mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Update debugger to latest Pydev.Debugger snapshot.
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
|
||||
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
|
||||
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and documentation
|
||||
distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and are
|
||||
distributed by that particular Contributor. A Contribution 'originates'
|
||||
from a Contributor if it was added to the Program by such Contributor
|
||||
itself or anyone acting on such Contributor's behalf. Contributions do not
|
||||
include additions to the Program which: (i) are separate modules of
|
||||
software distributed in conjunction with the Program under their own
|
||||
license agreement, and (ii) are not derivative works of the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are
|
||||
necessarily infringed by the use or sale of its Contribution alone or when
|
||||
combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this
|
||||
Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free copyright license to
|
||||
reproduce, prepare derivative works of, publicly display, publicly
|
||||
perform, distribute and sublicense the Contribution of such Contributor,
|
||||
if any, and such derivative works, in source code and object code form.
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free patent license under
|
||||
Licensed Patents to make, use, sell, offer to sell, import and otherwise
|
||||
transfer the Contribution of such Contributor, if any, in source code and
|
||||
object code form. This patent license shall apply to the combination of
|
||||
the Contribution and the Program if, at the time the Contribution is
|
||||
added by the Contributor, such addition of the Contribution causes such
|
||||
combination to be covered by the Licensed Patents. The patent license
|
||||
shall not apply to any other combinations which include the Contribution.
|
||||
No hardware per se is licensed hereunder.
|
||||
c) Recipient understands that although each Contributor grants the licenses
|
||||
to its Contributions set forth herein, no assurances are provided by any
|
||||
Contributor that the Program does not infringe the patent or other
|
||||
intellectual property rights of any other entity. Each Contributor
|
||||
disclaims any liability to Recipient for claims brought by any other
|
||||
entity based on infringement of intellectual property rights or
|
||||
otherwise. As a condition to exercising the rights and licenses granted
|
||||
hereunder, each Recipient hereby assumes sole responsibility to secure
|
||||
any other intellectual property rights needed, if any. For example, if a
|
||||
third party patent license is required to allow Recipient to distribute
|
||||
the Program, it is Recipient's responsibility to acquire that license
|
||||
before distributing the Program.
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright
|
||||
license set forth in this Agreement.
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under
|
||||
its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
b) its license agreement:
|
||||
i) effectively disclaims on behalf of all Contributors all warranties
|
||||
and conditions, express and implied, including warranties or
|
||||
conditions of title and non-infringement, and implied warranties or
|
||||
conditions of merchantability and fitness for a particular purpose;
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and
|
||||
consequential damages, such as lost profits;
|
||||
iii) states that any provisions which differ from this Agreement are
|
||||
offered by that Contributor alone and not by any other party; and
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable
|
||||
manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
Contributors may not remove or alter any copyright notices contained
|
||||
within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution,
|
||||
if
|
||||
any, in a manner that reasonably allows subsequent Recipients to identify the
|
||||
originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with
|
||||
respect to end users, business partners and the like. While this license is
|
||||
intended to facilitate the commercial use of the Program, the Contributor who
|
||||
includes the Program in a commercial product offering should do so in a manner
|
||||
which does not create potential liability for other Contributors. Therefore,
|
||||
if a Contributor includes the Program in a commercial product offering, such
|
||||
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
|
||||
every other Contributor ("Indemnified Contributor") against any losses,
|
||||
damages and costs (collectively "Losses") arising from claims, lawsuits and
|
||||
other legal actions brought by a third party against the Indemnified
|
||||
Contributor to the extent caused by the acts or omissions of such Commercial
|
||||
Contributor in connection with its distribution of the Program in a commercial
|
||||
product offering. The obligations in this section do not apply to any claims
|
||||
or Losses relating to any actual or alleged intellectual property
|
||||
infringement. In order to qualify, an Indemnified Contributor must:
|
||||
a) promptly notify the Commercial Contributor in writing of such claim, and
|
||||
b) allow the Commercial Contributor to control, and cooperate with the
|
||||
Commercial Contributor in, the defense and any related settlement
|
||||
negotiations. The Indemnified Contributor may participate in any such claim at
|
||||
its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product
|
||||
offering, Product X. That Contributor is then a Commercial Contributor. If
|
||||
that Commercial Contributor then makes performance claims, or offers
|
||||
warranties related to Product X, those performance claims and warranties are
|
||||
such Commercial Contributor's responsibility alone. Under this section, the
|
||||
Commercial Contributor would have to defend claims against the other
|
||||
Contributors related to those performance claims and warranties, and if a
|
||||
court requires any other Contributor to pay any damages as a result, the
|
||||
Commercial Contributor must pay those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
|
||||
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
|
||||
Recipient is solely responsible for determining the appropriateness of using
|
||||
and distributing the Program and assumes all risks associated with its
|
||||
exercise of rights under this Agreement , including but not limited to the
|
||||
risks and costs of program errors, compliance with applicable laws, damage to
|
||||
or loss of data, programs or equipment, and unavailability or interruption of
|
||||
operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
|
||||
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
|
||||
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of the
|
||||
remainder of the terms of this Agreement, and without further action by the
|
||||
parties hereto, such provision shall be reformed to the minimum extent
|
||||
necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
|
||||
(excluding combinations of the Program with other software or hardware)
|
||||
infringes such Recipient's patent(s), then such Recipient's rights granted
|
||||
under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to
|
||||
comply with any of the material terms or conditions of this Agreement and does
|
||||
not cure such failure in a reasonable period of time after becoming aware of
|
||||
such noncompliance. If all Recipient's rights under this Agreement terminate,
|
||||
Recipient agrees to cease use and distribution of the Program as soon as
|
||||
reasonably practicable. However, Recipient's obligations under this Agreement
|
||||
and any licenses granted by Recipient relating to the Program shall continue
|
||||
and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in
|
||||
order to avoid inconsistency the Agreement is copyrighted and may only be
|
||||
modified in the following manner. The Agreement Steward reserves the right to
|
||||
publish new versions (including revisions) of this Agreement from time to
|
||||
time. No one other than the Agreement Steward has the right to modify this
|
||||
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
|
||||
Eclipse Foundation may assign the responsibility to serve as the Agreement
|
||||
Steward to a suitable separate entity. Each new version of the Agreement will
|
||||
be given a distinguishing version number. The Program (including
|
||||
Contributions) may always be distributed subject to the version of the
|
||||
Agreement under which it was received. In addition, after a new version of the
|
||||
Agreement is published, Contributor may elect to distribute the Program
|
||||
(including its Contributions) under the new version. Except as expressly
|
||||
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
|
||||
licenses to the intellectual property of any Contributor under this Agreement,
|
||||
whether expressly, by implication, estoppel or otherwise. All rights in the
|
||||
Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to this
|
||||
Agreement will bring a legal action under this Agreement more than one year
|
||||
after the cause of action arose. Each party waives its rights to a jury trial in
|
||||
any resulting litigation.
|
||||
@@ -0,0 +1,2 @@
|
||||
PyDev.Debugger
|
||||
==============
|
||||
@@ -1,38 +0,0 @@
|
||||
#We must redefine it in Py3k if it's not already there
|
||||
def execfile(file, glob=None, loc=None):
|
||||
if glob is None:
|
||||
import sys
|
||||
glob = sys._getframe().f_back.f_globals
|
||||
if loc is None:
|
||||
loc = glob
|
||||
stream = open(file, 'rb')
|
||||
try:
|
||||
encoding = None
|
||||
#Get encoding!
|
||||
for _i in range(2):
|
||||
line = stream.readline() #Should not raise an exception even if there are no more contents
|
||||
#Must be a comment line
|
||||
if line.strip().startswith(b'#'):
|
||||
#Don't import re if there's no chance that there's an encoding in the line
|
||||
if b'coding' in line:
|
||||
import re
|
||||
p = re.search(br"coding[:=]\s*([-\w.]+)", line)
|
||||
if p:
|
||||
try:
|
||||
encoding = p.group(1).decode('ascii')
|
||||
break
|
||||
except:
|
||||
encoding = None
|
||||
finally:
|
||||
stream.close()
|
||||
|
||||
if encoding:
|
||||
stream = open(file, encoding=encoding)
|
||||
else:
|
||||
stream = open(file)
|
||||
try:
|
||||
contents = stream.read()
|
||||
finally:
|
||||
stream.close()
|
||||
|
||||
exec(compile(contents+"\n", file, 'exec'), glob, loc) #execute the script
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
#=======================================================================================================================
|
||||
# getopt code copied since gnu_getopt is not available on jython 2.1
|
||||
#=======================================================================================================================
|
||||
class GetoptError(Exception):
|
||||
opt = ''
|
||||
msg = ''
|
||||
def __init__(self, msg, opt=''):
|
||||
self.msg = msg
|
||||
self.opt = opt
|
||||
Exception.__init__(self, msg, opt)
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
|
||||
def gnu_getopt(args, shortopts, longopts=[]):
|
||||
"""getopt(args, options[, long_options]) -> opts, args
|
||||
|
||||
This function works like getopt(), except that GNU style scanning
|
||||
mode is used by default. This means that option and non-option
|
||||
arguments may be intermixed. The getopt() function stops
|
||||
processing options as soon as a non-option argument is
|
||||
encountered.
|
||||
|
||||
If the first character of the option string is `+', or if the
|
||||
environment variable POSIXLY_CORRECT is set, then option
|
||||
processing stops as soon as a non-option argument is encountered.
|
||||
"""
|
||||
|
||||
opts = []
|
||||
prog_args = []
|
||||
if type('') == type(longopts):
|
||||
longopts = [longopts]
|
||||
else:
|
||||
longopts = list(longopts)
|
||||
|
||||
# Allow options after non-option arguments?
|
||||
all_options_first = False
|
||||
if shortopts.startswith('+'):
|
||||
shortopts = shortopts[1:]
|
||||
all_options_first = True
|
||||
|
||||
while args:
|
||||
if args[0] == '--':
|
||||
prog_args += args[1:]
|
||||
break
|
||||
|
||||
if args[0][:2] == '--':
|
||||
opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
|
||||
elif args[0][:1] == '-':
|
||||
opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
|
||||
else:
|
||||
if all_options_first:
|
||||
prog_args += args
|
||||
break
|
||||
else:
|
||||
prog_args.append(args[0])
|
||||
args = args[1:]
|
||||
|
||||
return opts, prog_args
|
||||
|
||||
def do_longs(opts, opt, longopts, args):
|
||||
try:
|
||||
i = opt.index('=')
|
||||
except ValueError:
|
||||
optarg = None
|
||||
else:
|
||||
opt, optarg = opt[:i], opt[i + 1:]
|
||||
|
||||
has_arg, opt = long_has_args(opt, longopts)
|
||||
if has_arg:
|
||||
if optarg is None:
|
||||
if not args:
|
||||
raise GetoptError('option --%s requires argument' % opt, opt)
|
||||
optarg, args = args[0], args[1:]
|
||||
elif optarg:
|
||||
raise GetoptError('option --%s must not have an argument' % opt, opt)
|
||||
opts.append(('--' + opt, optarg or ''))
|
||||
return opts, args
|
||||
|
||||
# Return:
|
||||
# has_arg?
|
||||
# full option name
|
||||
def long_has_args(opt, longopts):
|
||||
possibilities = [o for o in longopts if o.startswith(opt)]
|
||||
if not possibilities:
|
||||
raise GetoptError('option --%s not recognized' % opt, opt)
|
||||
# Is there an exact match?
|
||||
if opt in possibilities:
|
||||
return False, opt
|
||||
elif opt + '=' in possibilities:
|
||||
return True, opt
|
||||
# No exact match, so better be unique.
|
||||
if len(possibilities) > 1:
|
||||
# XXX since possibilities contains all valid continuations, might be
|
||||
# nice to work them into the error msg
|
||||
raise GetoptError('option --%s not a unique prefix' % opt, opt)
|
||||
assert len(possibilities) == 1
|
||||
unique_match = possibilities[0]
|
||||
has_arg = unique_match.endswith('=')
|
||||
if has_arg:
|
||||
unique_match = unique_match[:-1]
|
||||
return has_arg, unique_match
|
||||
|
||||
def do_shorts(opts, optstring, shortopts, args):
|
||||
while optstring != '':
|
||||
opt, optstring = optstring[0], optstring[1:]
|
||||
if short_has_arg(opt, shortopts):
|
||||
if optstring == '':
|
||||
if not args:
|
||||
raise GetoptError('option -%s requires argument' % opt,
|
||||
opt)
|
||||
optstring, args = args[0], args[1:]
|
||||
optarg, optstring = optstring, ''
|
||||
else:
|
||||
optarg = ''
|
||||
opts.append(('-' + opt, optarg))
|
||||
return opts, args
|
||||
|
||||
def short_has_arg(opt, shortopts):
|
||||
for i in range(len(shortopts)):
|
||||
if opt == shortopts[i] != ':':
|
||||
return shortopts.startswith(':', i + 1)
|
||||
raise GetoptError('option -%s not recognized' % opt, opt)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# End getopt code
|
||||
#=======================================================================================================================
|
||||
@@ -4,6 +4,10 @@ import sys
|
||||
|
||||
from _pydev_tipper_common import DoFind
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
#completion types.
|
||||
TYPE_IMPORT = '0'
|
||||
@@ -19,20 +23,20 @@ def _imp(name, log=None):
|
||||
except:
|
||||
if '.' in name:
|
||||
sub = name[0:name.rfind('.')]
|
||||
|
||||
|
||||
if log is not None:
|
||||
log.AddContent('Unable to import', name, 'trying with', sub)
|
||||
log.AddException()
|
||||
|
||||
|
||||
return _imp(sub, log)
|
||||
else:
|
||||
s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path)
|
||||
if log is not None:
|
||||
log.AddContent(s)
|
||||
log.AddException()
|
||||
|
||||
|
||||
raise ImportError(s)
|
||||
|
||||
|
||||
|
||||
IS_IPY = False
|
||||
if sys.platform == 'cli':
|
||||
@@ -53,9 +57,9 @@ if sys.platform == 'cli':
|
||||
clr.AddReference(name)
|
||||
except:
|
||||
pass #That's OK (not dot net module).
|
||||
|
||||
|
||||
return _old_imp(initial_name, log)
|
||||
|
||||
|
||||
|
||||
|
||||
def GetFile(mod):
|
||||
@@ -69,19 +73,19 @@ def GetFile(mod):
|
||||
filename = f[:-4] + '.py'
|
||||
if os.path.exists(filename):
|
||||
f = filename
|
||||
|
||||
|
||||
return f
|
||||
|
||||
def Find(name, log=None):
|
||||
f = None
|
||||
|
||||
|
||||
mod = _imp(name, log)
|
||||
parent = mod
|
||||
foundAs = ''
|
||||
|
||||
|
||||
if inspect.ismodule(mod):
|
||||
f = GetFile(mod)
|
||||
|
||||
|
||||
components = name.split('.')
|
||||
|
||||
old_comp = None
|
||||
@@ -94,22 +98,22 @@ def Find(name, log=None):
|
||||
except AttributeError:
|
||||
if old_comp != comp:
|
||||
raise
|
||||
|
||||
|
||||
if inspect.ismodule(mod):
|
||||
f = GetFile(mod)
|
||||
else:
|
||||
if len(foundAs) > 0:
|
||||
foundAs = foundAs + '.'
|
||||
foundAs = foundAs + comp
|
||||
|
||||
|
||||
old_comp = comp
|
||||
|
||||
|
||||
return f, mod, parent, foundAs
|
||||
|
||||
def Search(data):
|
||||
'''@return file, line, col
|
||||
'''
|
||||
|
||||
|
||||
data = data.replace('\n', '')
|
||||
if data.endswith('.'):
|
||||
data = data.rstrip('.')
|
||||
@@ -118,19 +122,19 @@ def Search(data):
|
||||
return DoFind(f, mod), foundAs
|
||||
except:
|
||||
return DoFind(f, parent), foundAs
|
||||
|
||||
|
||||
|
||||
|
||||
def GenerateTip(data, log=None):
|
||||
data = data.replace('\n', '')
|
||||
if data.endswith('.'):
|
||||
data = data.rstrip('.')
|
||||
|
||||
|
||||
f, mod, parent, foundAs = Find(data, log)
|
||||
#print_ >> open('temp.txt', 'w'), f
|
||||
tips = GenerateImportsTipForModule(mod)
|
||||
return f, tips
|
||||
|
||||
|
||||
|
||||
|
||||
def CheckChar(c):
|
||||
if c == '-' or c == '.':
|
||||
return '_'
|
||||
@@ -146,7 +150,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
name, doc, args, type (from the TYPE_* constants)
|
||||
'''
|
||||
ret = []
|
||||
|
||||
|
||||
if dirComps is None:
|
||||
dirComps = dir(obj_to_complete)
|
||||
if hasattr(obj_to_complete, '__dict__'):
|
||||
@@ -155,22 +159,22 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
dirComps.append('__class__')
|
||||
|
||||
getCompleteInfo = True
|
||||
|
||||
|
||||
if len(dirComps) > 1000:
|
||||
#ok, we don't want to let our users wait forever...
|
||||
#ok, we don't want to let our users wait forever...
|
||||
#no complete info for you...
|
||||
|
||||
|
||||
getCompleteInfo = False
|
||||
|
||||
|
||||
dontGetDocsOn = (float, int, str, tuple, list)
|
||||
for d in dirComps:
|
||||
|
||||
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
|
||||
if not filter(d):
|
||||
continue
|
||||
|
||||
|
||||
args = ''
|
||||
|
||||
try:
|
||||
@@ -182,18 +186,18 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
if getCompleteInfo:
|
||||
try:
|
||||
retType = TYPE_BUILTIN
|
||||
|
||||
|
||||
#check if we have to get docs
|
||||
getDoc = True
|
||||
for class_ in dontGetDocsOn:
|
||||
|
||||
|
||||
if isinstance(obj, class_):
|
||||
getDoc = False
|
||||
break
|
||||
|
||||
|
||||
doc = ''
|
||||
if getDoc:
|
||||
#no need to get this info... too many constants are defined and
|
||||
#no need to get this info... too many constants are defined and
|
||||
#makes things much slower (passing all that through sockets takes quite some time)
|
||||
try:
|
||||
doc = inspect.getdoc(obj)
|
||||
@@ -201,12 +205,12 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
doc = ''
|
||||
except: #may happen on jython when checking java classes (so, just ignore it)
|
||||
doc = ''
|
||||
|
||||
|
||||
|
||||
|
||||
if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
|
||||
try:
|
||||
args, vargs, kwargs, defaults = inspect.getargspec(obj)
|
||||
|
||||
|
||||
r = ''
|
||||
for a in (args):
|
||||
if len(r) > 0:
|
||||
@@ -250,7 +254,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
if major:
|
||||
args = major[major.index('('):]
|
||||
found = True
|
||||
|
||||
|
||||
|
||||
if not found:
|
||||
i = doc.find('->')
|
||||
@@ -260,12 +264,12 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
i = doc.find('\n')
|
||||
if i < 0:
|
||||
i = doc.find('\r')
|
||||
|
||||
|
||||
|
||||
|
||||
if i > 0:
|
||||
s = doc[0:i]
|
||||
s = s.strip()
|
||||
|
||||
|
||||
#let's see if we have a docstring in the first line
|
||||
if s[-1] == ')':
|
||||
start = s.find('(')
|
||||
@@ -275,21 +279,21 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
end = s.find(')')
|
||||
if end <= 0:
|
||||
end = len(s)
|
||||
|
||||
|
||||
args = s[start:end]
|
||||
if not args[-1] == ')':
|
||||
args = args + ')'
|
||||
|
||||
|
||||
|
||||
|
||||
#now, get rid of unwanted chars
|
||||
l = len(args) - 1
|
||||
r = []
|
||||
for i in range(len(args)):
|
||||
for i in xrange(len(args)):
|
||||
if i == 0 or i == l:
|
||||
r.append(args[i])
|
||||
else:
|
||||
r.append(CheckChar(args[i]))
|
||||
|
||||
|
||||
args = ''.join(r)
|
||||
|
||||
if IS_IPY:
|
||||
@@ -305,43 +309,43 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
retType = TYPE_FUNCTION
|
||||
|
||||
|
||||
elif inspect.isclass(obj):
|
||||
retType = TYPE_CLASS
|
||||
|
||||
|
||||
elif inspect.ismodule(obj):
|
||||
retType = TYPE_IMPORT
|
||||
|
||||
|
||||
else:
|
||||
retType = TYPE_ATTR
|
||||
|
||||
|
||||
|
||||
|
||||
#add token and doc to return - assure only strings.
|
||||
ret.append((d, doc, args, retType))
|
||||
|
||||
|
||||
except: #just ignore and get it without aditional info
|
||||
ret.append((d, '', args, TYPE_BUILTIN))
|
||||
|
||||
|
||||
else: #getCompleteInfo == False
|
||||
if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
|
||||
retType = TYPE_FUNCTION
|
||||
|
||||
|
||||
elif inspect.isclass(obj):
|
||||
retType = TYPE_CLASS
|
||||
|
||||
|
||||
elif inspect.ismodule(obj):
|
||||
retType = TYPE_IMPORT
|
||||
|
||||
|
||||
else:
|
||||
retType = TYPE_ATTR
|
||||
#ok, no complete info, let's try to do this as fast and clean as possible
|
||||
#so, no docs for this kind of information, only the signatures
|
||||
ret.append((d, '', str(args), retType))
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,604 @@
|
||||
"""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
|
||||
from _pydev_imps import _pydev_time as time
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
from warnings import filterwarnings, catch_warnings
|
||||
with catch_warnings():
|
||||
if sys.py3kwarning:
|
||||
filterwarnings("ignore", ".*mimetools has been removed",
|
||||
DeprecationWarning)
|
||||
import mimetools
|
||||
|
||||
from _pydev_imps import _pydev_SocketServer as SocketServer
|
||||
|
||||
# Default error message template
|
||||
DEFAULT_ERROR_MESSAGE = """\
|
||||
<head>
|
||||
<title>Error response</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Error response</h1>
|
||||
<p>Error code %(code)d.
|
||||
<p>Message: %(message)s.
|
||||
<p>Error code explanation: %(code)s = %(explain)s.
|
||||
</body>
|
||||
"""
|
||||
|
||||
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
|
||||
|
||||
def _quote_html(html):
|
||||
return html.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
|
||||
class HTTPServer(SocketServer.TCPServer):
|
||||
|
||||
allow_reuse_address = 1 # Seems to make sense in testing environment
|
||||
|
||||
def server_bind(self):
|
||||
"""Override server_bind to store the server name."""
|
||||
SocketServer.TCPServer.server_bind(self)
|
||||
host, port = self.socket.getsockname()[:2]
|
||||
self.server_name = socket.getfqdn(host)
|
||||
self.server_port = port
|
||||
|
||||
|
||||
class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
|
||||
|
||||
"""HTTP request handler base class.
|
||||
|
||||
The following explanation of HTTP serves to guide you through the
|
||||
code as well as to expose any misunderstandings I may have about
|
||||
HTTP (so you don't need to read the code to figure out I'm wrong
|
||||
:-).
|
||||
|
||||
HTTP (HyperText Transfer Protocol) is an extensible protocol on
|
||||
top of a reliable stream transport (e.g. TCP/IP). The protocol
|
||||
recognizes three parts to a request:
|
||||
|
||||
1. One line identifying the request type and path
|
||||
2. An optional set of RFC-822-style headers
|
||||
3. An optional data part
|
||||
|
||||
The headers and data are separated by a blank line.
|
||||
|
||||
The first line of the request has the form
|
||||
|
||||
<command> <path> <version>
|
||||
|
||||
where <command> is a (case-sensitive) keyword such as GET or POST,
|
||||
<path> is a string containing path information for the request,
|
||||
and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
|
||||
<path> is encoded using the URL encoding scheme (using %xx to signify
|
||||
the ASCII character with hex code xx).
|
||||
|
||||
The specification specifies that lines are separated by CRLF but
|
||||
for compatibility with the widest range of clients recommends
|
||||
servers also handle LF. Similarly, whitespace in the request line
|
||||
is treated sensibly (allowing multiple spaces between components
|
||||
and allowing trailing whitespace).
|
||||
|
||||
Similarly, for output, lines ought to be separated by CRLF pairs
|
||||
but most clients grok LF characters just fine.
|
||||
|
||||
If the first line of the request has the form
|
||||
|
||||
<command> <path>
|
||||
|
||||
(i.e. <version> is left out) then this is assumed to be an HTTP
|
||||
0.9 request; this form has no optional headers and data part and
|
||||
the reply consists of just the data.
|
||||
|
||||
The reply form of the HTTP 1.x protocol again has three parts:
|
||||
|
||||
1. One line giving the response code
|
||||
2. An optional set of RFC-822-style headers
|
||||
3. The data
|
||||
|
||||
Again, the headers and data are separated by a blank line.
|
||||
|
||||
The response code line has the form
|
||||
|
||||
<version> <responsecode> <responsestring>
|
||||
|
||||
where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
|
||||
<responsecode> is a 3-digit response code indicating success or
|
||||
failure of the request, and <responsestring> is an optional
|
||||
human-readable string explaining what the response code means.
|
||||
|
||||
This server parses the request and the headers, and then calls a
|
||||
function specific to the request type (<command>). Specifically,
|
||||
a request SPAM will be handled by a method do_SPAM(). If no
|
||||
such method exists the server sends an error response to the
|
||||
client. If it exists, it is called with no arguments:
|
||||
|
||||
do_SPAM()
|
||||
|
||||
Note that the request name is case sensitive (i.e. SPAM and spam
|
||||
are different requests).
|
||||
|
||||
The various request details are stored in instance variables:
|
||||
|
||||
- client_address is the client IP address in the form (host,
|
||||
port);
|
||||
|
||||
- command, path and version are the broken-down request line;
|
||||
|
||||
- headers is an instance of mimetools.Message (or a derived
|
||||
class) containing the header information;
|
||||
|
||||
- rfile is a file object open for reading positioned at the
|
||||
start of the optional input data part;
|
||||
|
||||
- wfile is a file object open for writing.
|
||||
|
||||
IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
|
||||
|
||||
The first thing to be written must be the response line. Then
|
||||
follow 0 or more header lines, then a blank line, and then the
|
||||
actual data (if any). The meaning of the header lines depends on
|
||||
the command executed by the server; in most cases, when data is
|
||||
returned, there should be at least one header line of the form
|
||||
|
||||
Content-type: <type>/<subtype>
|
||||
|
||||
where <type> and <subtype> should be registered MIME types,
|
||||
e.g. "text/html" or "text/plain".
|
||||
|
||||
"""
|
||||
|
||||
# The Python system version, truncated to its first component.
|
||||
sys_version = "Python/" + sys.version.split()[0]
|
||||
|
||||
# The server software version. You may want to override this.
|
||||
# The format is multiple whitespace-separated strings,
|
||||
# where each string is of the form name[/version].
|
||||
server_version = "BaseHTTP/" + __version__
|
||||
|
||||
# The default request version. This only affects responses up until
|
||||
# the point where the request line is parsed, so it mainly decides what
|
||||
# the client gets back when sending a malformed request line.
|
||||
# Most web servers default to HTTP 0.9, i.e. don't send a status line.
|
||||
default_request_version = "HTTP/0.9"
|
||||
|
||||
def parse_request(self):
|
||||
"""Parse a request (internal).
|
||||
|
||||
The request should be stored in self.raw_requestline; the results
|
||||
are in self.command, self.path, self.request_version and
|
||||
self.headers.
|
||||
|
||||
Return True for success, False for failure; on failure, an
|
||||
error is sent back.
|
||||
|
||||
"""
|
||||
self.command = None # set in case of error on the first line
|
||||
self.request_version = version = self.default_request_version
|
||||
self.close_connection = 1
|
||||
requestline = self.raw_requestline
|
||||
requestline = requestline.rstrip('\r\n')
|
||||
self.requestline = requestline
|
||||
words = requestline.split()
|
||||
if len(words) == 3:
|
||||
command, path, version = words
|
||||
if version[:5] != 'HTTP/':
|
||||
self.send_error(400, "Bad request version (%r)" % version)
|
||||
return False
|
||||
try:
|
||||
base_version_number = version.split('/', 1)[1]
|
||||
version_number = base_version_number.split(".")
|
||||
# RFC 2145 section 3.1 says there can be only one "." and
|
||||
# - major and minor numbers MUST be treated as
|
||||
# separate integers;
|
||||
# - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
||||
# turn is lower than HTTP/12.3;
|
||||
# - Leading zeros MUST be ignored by recipients.
|
||||
if len(version_number) != 2:
|
||||
raise ValueError
|
||||
version_number = int(version_number[0]), int(version_number[1])
|
||||
except (ValueError, IndexError):
|
||||
self.send_error(400, "Bad request version (%r)" % version)
|
||||
return False
|
||||
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
|
||||
self.close_connection = 0
|
||||
if version_number >= (2, 0):
|
||||
self.send_error(505,
|
||||
"Invalid HTTP Version (%s)" % base_version_number)
|
||||
return False
|
||||
elif len(words) == 2:
|
||||
command, path = words
|
||||
self.close_connection = 1
|
||||
if command != 'GET':
|
||||
self.send_error(400,
|
||||
"Bad HTTP/0.9 request type (%r)" % command)
|
||||
return False
|
||||
elif not words:
|
||||
return False
|
||||
else:
|
||||
self.send_error(400, "Bad request syntax (%r)" % requestline)
|
||||
return False
|
||||
self.command, self.path, self.request_version = command, path, version
|
||||
|
||||
# Examine the headers and look for a Connection directive
|
||||
self.headers = self.MessageClass(self.rfile, 0)
|
||||
|
||||
conntype = self.headers.get('Connection', "")
|
||||
if conntype.lower() == 'close':
|
||||
self.close_connection = 1
|
||||
elif (conntype.lower() == 'keep-alive' and
|
||||
self.protocol_version >= "HTTP/1.1"):
|
||||
self.close_connection = 0
|
||||
return True
|
||||
|
||||
def handle_one_request(self):
|
||||
"""Handle a single HTTP request.
|
||||
|
||||
You normally don't need to override this method; see the class
|
||||
__doc__ string for information on how to handle specific HTTP
|
||||
commands such as GET and POST.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.raw_requestline = self.rfile.readline(65537)
|
||||
if len(self.raw_requestline) > 65536:
|
||||
self.requestline = ''
|
||||
self.request_version = ''
|
||||
self.command = ''
|
||||
self.send_error(414)
|
||||
return
|
||||
if not self.raw_requestline:
|
||||
self.close_connection = 1
|
||||
return
|
||||
if not self.parse_request():
|
||||
# An error code has been sent, just exit
|
||||
return
|
||||
mname = 'do_' + self.command
|
||||
if not hasattr(self, mname):
|
||||
self.send_error(501, "Unsupported method (%r)" % self.command)
|
||||
return
|
||||
method = getattr(self, mname)
|
||||
method()
|
||||
self.wfile.flush() #actually send the response if not already done.
|
||||
except socket.timeout:
|
||||
#a read or a write timed out. Discard this connection
|
||||
self.log_error("Request timed out: %r", sys.exc_info()[1])
|
||||
self.close_connection = 1
|
||||
return
|
||||
|
||||
def handle(self):
|
||||
"""Handle multiple requests if necessary."""
|
||||
self.close_connection = 1
|
||||
|
||||
self.handle_one_request()
|
||||
while not self.close_connection:
|
||||
self.handle_one_request()
|
||||
|
||||
def send_error(self, code, message=None):
|
||||
"""Send and log an error reply.
|
||||
|
||||
Arguments are the error code, and a detailed message.
|
||||
The detailed message defaults to the short entry matching the
|
||||
response code.
|
||||
|
||||
This sends an error response (so it must be called before any
|
||||
output has been generated), logs the error, and finally sends
|
||||
a piece of HTML explaining the error to the user.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
short, long = self.responses[code]
|
||||
except KeyError:
|
||||
short, long = '???', '???'
|
||||
if message is None:
|
||||
message = short
|
||||
explain = long
|
||||
self.log_error("code %d, message %s", code, message)
|
||||
# using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
|
||||
content = (self.error_message_format %
|
||||
{'code': code, 'message': _quote_html(message), 'explain': explain})
|
||||
self.send_response(code, message)
|
||||
self.send_header("Content-Type", self.error_content_type)
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
|
||||
self.wfile.write(content)
|
||||
|
||||
error_message_format = DEFAULT_ERROR_MESSAGE
|
||||
error_content_type = DEFAULT_ERROR_CONTENT_TYPE
|
||||
|
||||
def send_response(self, code, message=None):
|
||||
"""Send the response header and log the response code.
|
||||
|
||||
Also send two standard headers with the server software
|
||||
version and the current date.
|
||||
|
||||
"""
|
||||
self.log_request(code)
|
||||
if message is None:
|
||||
if code in self.responses:
|
||||
message = self.responses[code][0]
|
||||
else:
|
||||
message = ''
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("%s %d %s\r\n" %
|
||||
(self.protocol_version, code, message))
|
||||
# print (self.protocol_version, code, message)
|
||||
self.send_header('Server', self.version_string())
|
||||
self.send_header('Date', self.date_time_string())
|
||||
|
||||
def send_header(self, keyword, value):
|
||||
"""Send a MIME header."""
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("%s: %s\r\n" % (keyword, value))
|
||||
|
||||
if keyword.lower() == 'connection':
|
||||
if value.lower() == 'close':
|
||||
self.close_connection = 1
|
||||
elif value.lower() == 'keep-alive':
|
||||
self.close_connection = 0
|
||||
|
||||
def end_headers(self):
|
||||
"""Send the blank line ending the MIME headers."""
|
||||
if self.request_version != 'HTTP/0.9':
|
||||
self.wfile.write("\r\n")
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
"""Log an accepted request.
|
||||
|
||||
This is called by send_response().
|
||||
|
||||
"""
|
||||
|
||||
self.log_message('"%s" %s %s',
|
||||
self.requestline, str(code), str(size))
|
||||
|
||||
def log_error(self, format, *args):
|
||||
"""Log an error.
|
||||
|
||||
This is called when a request cannot be fulfilled. By
|
||||
default it passes the message on to log_message().
|
||||
|
||||
Arguments are the same as for log_message().
|
||||
|
||||
XXX This should go to the separate error log.
|
||||
|
||||
"""
|
||||
|
||||
self.log_message(format, *args)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
"""Log an arbitrary message.
|
||||
|
||||
This is used by all other logging functions. Override
|
||||
it if you have specific logging wishes.
|
||||
|
||||
The first argument, FORMAT, is a format string for the
|
||||
message to be logged. If the format string contains
|
||||
any % escapes requiring parameters, they should be
|
||||
specified as subsequent arguments (it's just like
|
||||
printf!).
|
||||
|
||||
The client host and current date/time are prefixed to
|
||||
every message.
|
||||
|
||||
"""
|
||||
|
||||
sys.stderr.write("%s - - [%s] %s\n" %
|
||||
(self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format%args))
|
||||
|
||||
def version_string(self):
|
||||
"""Return the server software version string."""
|
||||
return self.server_version + ' ' + self.sys_version
|
||||
|
||||
def date_time_string(self, timestamp=None):
|
||||
"""Return the current date and time formatted for a message header."""
|
||||
if timestamp is None:
|
||||
timestamp = time.time()
|
||||
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
|
||||
s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
|
||||
self.weekdayname[wd],
|
||||
day, self.monthname[month], year,
|
||||
hh, mm, ss)
|
||||
return s
|
||||
|
||||
def log_date_time_string(self):
|
||||
"""Return the current time formatted for logging."""
|
||||
now = time.time()
|
||||
year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
|
||||
s = "%02d/%3s/%04d %02d:%02d:%02d" % (
|
||||
day, self.monthname[month], year, hh, mm, ss)
|
||||
return s
|
||||
|
||||
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
|
||||
monthname = [None,
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
|
||||
def address_string(self):
|
||||
"""Return the client address formatted for logging.
|
||||
|
||||
This version looks up the full hostname using gethostbyaddr(),
|
||||
and tries to find a name that contains at least one dot.
|
||||
|
||||
"""
|
||||
|
||||
host, port = self.client_address[:2]
|
||||
return socket.getfqdn(host)
|
||||
|
||||
# Essentially static class variables
|
||||
|
||||
# The version of the HTTP protocol we support.
|
||||
# Set this to HTTP/1.1 to enable automatic keepalive
|
||||
protocol_version = "HTTP/1.0"
|
||||
|
||||
# The Message-like class used to parse headers
|
||||
MessageClass = mimetools.Message
|
||||
|
||||
# Table mapping response codes to messages; entries have the
|
||||
# form {code: (shortmessage, longmessage)}.
|
||||
# See RFC 2616.
|
||||
responses = {
|
||||
100: ('Continue', 'Request received, please continue'),
|
||||
101: ('Switching Protocols',
|
||||
'Switching to new protocol; obey Upgrade header'),
|
||||
|
||||
200: ('OK', 'Request fulfilled, document follows'),
|
||||
201: ('Created', 'Document created, URL follows'),
|
||||
202: ('Accepted',
|
||||
'Request accepted, processing continues off-line'),
|
||||
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
|
||||
204: ('No Content', 'Request fulfilled, nothing follows'),
|
||||
205: ('Reset Content', 'Clear input form for further input.'),
|
||||
206: ('Partial Content', 'Partial content follows.'),
|
||||
|
||||
300: ('Multiple Choices',
|
||||
'Object has several resources -- see URI list'),
|
||||
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
|
||||
302: ('Found', 'Object moved temporarily -- see URI list'),
|
||||
303: ('See Other', 'Object moved -- see Method and URL list'),
|
||||
304: ('Not Modified',
|
||||
'Document has not changed since given time'),
|
||||
305: ('Use Proxy',
|
||||
'You must use proxy specified in Location to access this '
|
||||
'resource.'),
|
||||
307: ('Temporary Redirect',
|
||||
'Object moved temporarily -- see URI list'),
|
||||
|
||||
400: ('Bad Request',
|
||||
'Bad request syntax or unsupported method'),
|
||||
401: ('Unauthorized',
|
||||
'No permission -- see authorization schemes'),
|
||||
402: ('Payment Required',
|
||||
'No payment -- see charging schemes'),
|
||||
403: ('Forbidden',
|
||||
'Request forbidden -- authorization will not help'),
|
||||
404: ('Not Found', 'Nothing matches the given URI'),
|
||||
405: ('Method Not Allowed',
|
||||
'Specified method is invalid for this resource.'),
|
||||
406: ('Not Acceptable', 'URI not available in preferred format.'),
|
||||
407: ('Proxy Authentication Required', 'You must authenticate with '
|
||||
'this proxy before proceeding.'),
|
||||
408: ('Request Timeout', 'Request timed out; try again later.'),
|
||||
409: ('Conflict', 'Request conflict.'),
|
||||
410: ('Gone',
|
||||
'URI no longer exists and has been permanently removed.'),
|
||||
411: ('Length Required', 'Client must specify Content-Length.'),
|
||||
412: ('Precondition Failed', 'Precondition in headers is false.'),
|
||||
413: ('Request Entity Too Large', 'Entity is too large.'),
|
||||
414: ('Request-URI Too Long', 'URI is too long.'),
|
||||
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
|
||||
416: ('Requested Range Not Satisfiable',
|
||||
'Cannot satisfy request range.'),
|
||||
417: ('Expectation Failed',
|
||||
'Expect condition could not be satisfied.'),
|
||||
|
||||
500: ('Internal Server Error', 'Server got itself in trouble'),
|
||||
501: ('Not Implemented',
|
||||
'Server does not support this operation'),
|
||||
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
|
||||
503: ('Service Unavailable',
|
||||
'The server cannot process the request due to a high load'),
|
||||
504: ('Gateway Timeout',
|
||||
'The gateway server did not receive a timely response'),
|
||||
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
|
||||
}
|
||||
|
||||
|
||||
def test(HandlerClass = BaseHTTPRequestHandler,
|
||||
ServerClass = HTTPServer, protocol="HTTP/1.0"):
|
||||
"""Test the HTTP request handler class.
|
||||
|
||||
This runs an HTTP server on port 8000 (or the first command line
|
||||
argument).
|
||||
|
||||
"""
|
||||
|
||||
if sys.argv[1:]:
|
||||
port = int(sys.argv[1])
|
||||
else:
|
||||
port = 8000
|
||||
server_address = ('', port)
|
||||
|
||||
HandlerClass.protocol_version = protocol
|
||||
httpd = ServerClass(server_address, HandlerClass)
|
||||
|
||||
sa = httpd.socket.getsockname()
|
||||
print ("Serving HTTP on", sa[0], "port", sa[1], "...")
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
@@ -0,0 +1,244 @@
|
||||
"""A multi-producer, multi-consumer queue."""
|
||||
|
||||
from _pydev_imps._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()
|
||||
@@ -0,0 +1,610 @@
|
||||
#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)
|
||||
|
||||
|
||||
from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
|
||||
from _pydev_imps._pydev_xmlrpclib import Fault
|
||||
from _pydev_imps import _pydev_SocketServer as SocketServer
|
||||
from _pydev_imps 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()
|
||||
@@ -0,0 +1,715 @@
|
||||
"""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"
|
||||
|
||||
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
from _pydev_imps 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)
|
||||
@@ -0,0 +1,18 @@
|
||||
#We must redefine it in Py3k if it's not already there
|
||||
def execfile(file, glob=None, loc=None):
|
||||
if glob is None:
|
||||
import sys
|
||||
glob = sys._getframe().f_back.f_globals
|
||||
if loc is None:
|
||||
loc = glob
|
||||
|
||||
# It seems that the best way is using tokenize.open(): http://code.activestate.com/lists/python-dev/131251/
|
||||
import tokenize
|
||||
stream = tokenize.open(file)
|
||||
try:
|
||||
contents = stream.read()
|
||||
finally:
|
||||
stream.close()
|
||||
|
||||
#execute the script (note: it's important to compile first to have the filename set in debug mode)
|
||||
exec(compile(contents+"\n", file, 'exec'), glob, loc)
|
||||
@@ -0,0 +1,794 @@
|
||||
"""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
|
||||
import os
|
||||
import types
|
||||
import string
|
||||
import re
|
||||
import imp
|
||||
import 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
|
||||
@@ -0,0 +1 @@
|
||||
from select import *
|
||||
@@ -0,0 +1 @@
|
||||
from socket import *
|
||||
@@ -0,0 +1,4 @@
|
||||
try:
|
||||
from thread import *
|
||||
except:
|
||||
from _thread import * #Py3k
|
||||
@@ -0,0 +1 @@
|
||||
from time import *
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
import StringIO
|
||||
import traceback
|
||||
from java.lang import StringBuffer #@UnresolvedImport
|
||||
from java.lang import String #@UnresolvedImport
|
||||
import java.lang #@UnresolvedImport
|
||||
import sys
|
||||
from _pydev_tipper_common import DoFind
|
||||
@@ -14,12 +12,16 @@ except NameError: # version < 2.3 -- didn't have the True/False builtins
|
||||
import __builtin__
|
||||
setattr(__builtin__, 'True', 1)
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
|
||||
|
||||
|
||||
from org.python.core import PyReflectedFunction #@UnresolvedImport
|
||||
|
||||
from org.python import core #@UnresolvedImport
|
||||
from org.python.core import PyClass #@UnresolvedImport
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
|
||||
#completion types.
|
||||
@@ -48,11 +50,11 @@ def Find(name):
|
||||
name = 'org.python.core.PyString'
|
||||
elif name == '__builtin__.dict':
|
||||
name = 'org.python.core.PyDictionary'
|
||||
|
||||
|
||||
mod = _imp(name)
|
||||
parent = mod
|
||||
foundAs = ''
|
||||
|
||||
|
||||
if hasattr(mod, '__file__'):
|
||||
f = mod.__file__
|
||||
|
||||
@@ -68,29 +70,29 @@ def Find(name):
|
||||
except AttributeError:
|
||||
if old_comp != comp:
|
||||
raise
|
||||
|
||||
|
||||
if hasattr(mod, '__file__'):
|
||||
f = mod.__file__
|
||||
else:
|
||||
if len(foundAs) > 0:
|
||||
foundAs = foundAs + '.'
|
||||
foundAs = foundAs + comp
|
||||
|
||||
|
||||
old_comp = comp
|
||||
|
||||
|
||||
return f, mod, parent, foundAs
|
||||
|
||||
def formatParamClassName(paramClassName):
|
||||
if paramClassName.startswith('['):
|
||||
if paramClassName == '[C':
|
||||
paramClassName = 'char[]'
|
||||
|
||||
|
||||
elif paramClassName == '[B':
|
||||
paramClassName = 'byte[]'
|
||||
|
||||
|
||||
elif paramClassName == '[I':
|
||||
paramClassName = 'int[]'
|
||||
|
||||
|
||||
elif paramClassName.startswith('[L') and paramClassName.endswith(';'):
|
||||
paramClassName = paramClassName[2:-1]
|
||||
paramClassName += '[]'
|
||||
@@ -101,17 +103,17 @@ def GenerateTip(data, log=None):
|
||||
data = data.replace('\n', '')
|
||||
if data.endswith('.'):
|
||||
data = data.rstrip('.')
|
||||
|
||||
|
||||
f, mod, parent, foundAs = Find(data)
|
||||
tips = GenerateImportsTipForModule(mod)
|
||||
return f, tips
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# Info
|
||||
#=======================================================================================================================
|
||||
class Info:
|
||||
|
||||
|
||||
def __init__(self, name, **kwargs):
|
||||
self.name = name
|
||||
self.doc = kwargs.get('doc', None)
|
||||
@@ -119,47 +121,47 @@ class Info:
|
||||
self.varargs = kwargs.get('varargs', None) #string
|
||||
self.kwargs = kwargs.get('kwargs', None) #string
|
||||
self.ret = kwargs.get('ret', None) #string
|
||||
|
||||
|
||||
def basicAsStr(self):
|
||||
'''@returns this class information as a string (just basic format)
|
||||
'''
|
||||
|
||||
|
||||
s = 'function:%s args=%s, varargs=%s, kwargs=%s, docs:%s' % \
|
||||
(str(self.name), str(self.args), str(self.varargs), str(self.kwargs), str(self.doc))
|
||||
return s
|
||||
|
||||
|
||||
|
||||
def getAsDoc(self):
|
||||
s = str(self.name)
|
||||
if self.doc:
|
||||
s += '\n@doc %s\n' % str(self.doc)
|
||||
|
||||
|
||||
if self.args:
|
||||
s += '\n@params '
|
||||
for arg in self.args:
|
||||
s += str(formatParamClassName(arg))
|
||||
s += ' '
|
||||
|
||||
|
||||
if self.varargs:
|
||||
s += '\n@varargs '
|
||||
s += str(self.varargs)
|
||||
|
||||
|
||||
if self.kwargs:
|
||||
s += '\n@kwargs '
|
||||
s += str(self.kwargs)
|
||||
|
||||
|
||||
if self.ret:
|
||||
s += '\n@return '
|
||||
s += str(formatParamClassName(str(self.ret)))
|
||||
|
||||
|
||||
return str(s)
|
||||
|
||||
|
||||
def isclass(cls):
|
||||
return isinstance(cls, core.PyClass)
|
||||
|
||||
def ismethod(func):
|
||||
'''this function should return the information gathered on a function
|
||||
|
||||
|
||||
@param func: this is the function we want to get info on
|
||||
@return a tuple where:
|
||||
0 = indicates whether the parameter passed is a method or not
|
||||
@@ -167,24 +169,24 @@ def ismethod(func):
|
||||
this is a list because when we have methods from java with the same name and different signatures,
|
||||
we actually have many methods, each with its own set of arguments
|
||||
'''
|
||||
|
||||
|
||||
try:
|
||||
if isinstance(func, core.PyFunction):
|
||||
#ok, this is from python, created by jython
|
||||
#print_ ' PyFunction'
|
||||
|
||||
|
||||
def getargs(func_code):
|
||||
"""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."""
|
||||
|
||||
|
||||
nargs = func_code.co_argcount
|
||||
names = func_code.co_varnames
|
||||
args = list(names[:nargs])
|
||||
step = 0
|
||||
|
||||
|
||||
varargs = None
|
||||
if func_code.co_flags & func_code.CO_VARARGS:
|
||||
varargs = func_code.co_varnames[nargs]
|
||||
@@ -193,35 +195,35 @@ def ismethod(func):
|
||||
if func_code.co_flags & func_code.CO_VARKEYWORDS:
|
||||
varkw = func_code.co_varnames[nargs]
|
||||
return args, varargs, varkw
|
||||
|
||||
|
||||
args = getargs(func.func_code)
|
||||
return 1, [Info(func.func_name, args=args[0], varargs=args[1], kwargs=args[2], doc=func.func_doc)]
|
||||
|
||||
|
||||
if isinstance(func, core.PyMethod):
|
||||
#this is something from java itself, and jython just wrapped it...
|
||||
|
||||
|
||||
#things to play in func:
|
||||
#['__call__', '__class__', '__cmp__', '__delattr__', '__dir__', '__doc__', '__findattr__', '__name__', '_doget', 'im_class',
|
||||
#'im_func', 'im_self', 'toString']
|
||||
#print_ ' PyMethod'
|
||||
#that's the PyReflectedFunction... keep going to get it
|
||||
func = func.im_func
|
||||
|
||||
|
||||
if isinstance(func, PyReflectedFunction):
|
||||
#this is something from java itself, and jython just wrapped it...
|
||||
|
||||
|
||||
#print_ ' PyReflectedFunction'
|
||||
|
||||
|
||||
infos = []
|
||||
for i in range(len(func.argslist)):
|
||||
for i in xrange(len(func.argslist)):
|
||||
#things to play in func.argslist[i]:
|
||||
|
||||
|
||||
#'PyArgsCall', 'PyArgsKeywordsCall', 'REPLACE', 'StandardCall', 'args', 'compare', 'compareTo', 'data', 'declaringClass'
|
||||
#'flags', 'isStatic', 'matches', 'precedence']
|
||||
|
||||
|
||||
#print_ ' ', func.argslist[i].data.__class__
|
||||
#func.argslist[i].data.__class__ == java.lang.reflect.Method
|
||||
|
||||
|
||||
if func.argslist[i]:
|
||||
met = func.argslist[i].data
|
||||
name = met.getName()
|
||||
@@ -230,9 +232,9 @@ def ismethod(func):
|
||||
except AttributeError:
|
||||
ret = ''
|
||||
parameterTypes = met.getParameterTypes()
|
||||
|
||||
|
||||
args = []
|
||||
for j in range(len(parameterTypes)):
|
||||
for j in xrange(len(parameterTypes)):
|
||||
paramTypesClass = parameterTypes[j]
|
||||
try:
|
||||
try:
|
||||
@@ -246,7 +248,7 @@ def ismethod(func):
|
||||
except:
|
||||
paramClassName = repr(paramTypesClass) #just in case something else happens... it will at least be visible
|
||||
#if the parameter equals [C, it means it it a char array, so, let's change it
|
||||
|
||||
|
||||
a = formatParamClassName(paramClassName)
|
||||
#a = a.replace('[]','Array')
|
||||
#a = a.replace('Object', 'obj')
|
||||
@@ -255,18 +257,18 @@ def ismethod(func):
|
||||
#a = a.replace('Char', 'c')
|
||||
#a = a.replace('Double', 'd')
|
||||
args.append(a) #so we don't leave invalid code
|
||||
|
||||
|
||||
|
||||
|
||||
info = Info(name, args=args, ret=ret)
|
||||
#print_ info.basicAsStr()
|
||||
infos.append(info)
|
||||
|
||||
|
||||
return 1, infos
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
s = StringIO.StringIO()
|
||||
traceback.print_exc(file=s)
|
||||
return 1, [Info(str('ERROR'), doc=s.getvalue())]
|
||||
|
||||
|
||||
return 0, None
|
||||
|
||||
def ismodule(mod):
|
||||
@@ -274,7 +276,7 @@ def ismodule(mod):
|
||||
if not hasattr(mod, 'getClass') and not hasattr(mod, '__class__') \
|
||||
and hasattr(mod, '__name__'):
|
||||
return 1
|
||||
|
||||
|
||||
return isinstance(mod, core.PyModule)
|
||||
|
||||
|
||||
@@ -293,11 +295,11 @@ def dirObj(obj):
|
||||
except TypeError:
|
||||
#may happen on jython when getting the java.lang.Class class
|
||||
c = obj.getSuperclass(obj)
|
||||
|
||||
|
||||
while c != None:
|
||||
classes.append(c)
|
||||
c = c.getSuperclass()
|
||||
|
||||
|
||||
#get info about interfaces
|
||||
interfs = []
|
||||
for obj in classes:
|
||||
@@ -306,57 +308,57 @@ def dirObj(obj):
|
||||
except TypeError:
|
||||
interfs.extend(obj.getInterfaces(obj))
|
||||
classes.extend(interfs)
|
||||
|
||||
|
||||
#now is the time when we actually get info on the declared methods and fields
|
||||
for obj in classes:
|
||||
try:
|
||||
declaredMethods = obj.getDeclaredMethods()
|
||||
except TypeError:
|
||||
declaredMethods = obj.getDeclaredMethods(obj)
|
||||
|
||||
|
||||
try:
|
||||
declaredFields = obj.getDeclaredFields()
|
||||
except TypeError:
|
||||
declaredFields = obj.getDeclaredFields(obj)
|
||||
|
||||
for i in range(len(declaredMethods)):
|
||||
|
||||
for i in xrange(len(declaredMethods)):
|
||||
name = declaredMethods[i].getName()
|
||||
ret.append(name)
|
||||
found.put(name, 1)
|
||||
|
||||
for i in range(len(declaredFields)):
|
||||
|
||||
for i in xrange(len(declaredFields)):
|
||||
name = declaredFields[i].getName()
|
||||
ret.append(name)
|
||||
found.put(name, 1)
|
||||
|
||||
|
||||
elif isclass(obj.__class__):
|
||||
|
||||
|
||||
elif isclass(obj.__class__):
|
||||
d = dir(obj.__class__)
|
||||
for name in d:
|
||||
ret.append(name)
|
||||
found.put(name, 1)
|
||||
|
||||
|
||||
|
||||
#this simple dir does not always get all the info, that's why we have the part before
|
||||
#(e.g.: if we do a dir on String, some methods that are from other interfaces such as
|
||||
#(e.g.: if we do a dir on String, some methods that are from other interfaces such as
|
||||
#charAt don't appear)
|
||||
d = dir(original)
|
||||
for name in d:
|
||||
if found.get(name) != 1:
|
||||
ret.append(name)
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def formatArg(arg):
|
||||
'''formats an argument to be shown
|
||||
'''
|
||||
|
||||
|
||||
s = str(arg)
|
||||
dot = s.rfind('.')
|
||||
if dot >= 0:
|
||||
s = s[dot + 1:]
|
||||
|
||||
|
||||
s = s.replace(';', '')
|
||||
s = s.replace('[]', 'Array')
|
||||
if len(s) > 0:
|
||||
@@ -364,13 +366,13 @@ def formatArg(arg):
|
||||
s = c + s[1:]
|
||||
|
||||
return s
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def Search(data):
|
||||
'''@return file, line, col
|
||||
'''
|
||||
|
||||
|
||||
data = data.replace('\n', '')
|
||||
if data.endswith('.'):
|
||||
data = data.rstrip('.')
|
||||
@@ -379,8 +381,8 @@ def Search(data):
|
||||
return DoFind(f, mod), foundAs
|
||||
except:
|
||||
return DoFind(f, parent), foundAs
|
||||
|
||||
|
||||
|
||||
|
||||
def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True):
|
||||
'''
|
||||
@param obj_to_complete: the object from where we should get the completions
|
||||
@@ -391,18 +393,18 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
name, doc, args, type (from the TYPE_* constants)
|
||||
'''
|
||||
ret = []
|
||||
|
||||
|
||||
if dirComps is None:
|
||||
dirComps = dirObj(obj_to_complete)
|
||||
|
||||
|
||||
for d in dirComps:
|
||||
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
|
||||
if not filter(d):
|
||||
continue
|
||||
|
||||
|
||||
args = ''
|
||||
doc = ''
|
||||
retType = TYPE_BUILTIN
|
||||
@@ -421,7 +423,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
#note: this only happens when we add things to the sys.path at runtime, if they are added to the classpath
|
||||
#before the run, everything goes fine.
|
||||
#
|
||||
#The code below ilustrates what I mean...
|
||||
#The code below ilustrates what I mean...
|
||||
#
|
||||
#import sys
|
||||
#sys.path.insert(1, r"C:\bin\eclipse310\plugins\org.junit_3.8.1\junit.jar" )
|
||||
@@ -429,7 +431,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
#import junit.framework
|
||||
#print_ dir(junit.framework) #shows the TestCase class here
|
||||
#
|
||||
#import junit.framework.TestCase
|
||||
#import junit.framework.TestCase
|
||||
#
|
||||
#raises the error:
|
||||
#Traceback (innermost last):
|
||||
@@ -458,19 +460,19 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr,
|
||||
except TypeError:
|
||||
traceback.print_exc()
|
||||
args = '()'
|
||||
|
||||
|
||||
retType = TYPE_FUNCTION
|
||||
|
||||
|
||||
elif isclass(obj):
|
||||
retType = TYPE_CLASS
|
||||
|
||||
|
||||
elif ismodule(obj):
|
||||
retType = TYPE_IMPORT
|
||||
|
||||
|
||||
#add token and doc to return - assure only strings.
|
||||
ret.append((d, doc, args, retType))
|
||||
|
||||
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from thread import *
|
||||
@@ -2,14 +2,10 @@
|
||||
|
||||
import sys as _sys
|
||||
|
||||
try:
|
||||
import _pydev_thread as thread
|
||||
except ImportError:
|
||||
import thread
|
||||
|
||||
from _pydev_imps import _pydev_thread as thread
|
||||
import warnings
|
||||
|
||||
from _pydev_time import time as _time, sleep as _sleep
|
||||
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
|
||||
@@ -854,7 +850,7 @@ _shutdown = _MainThread()._exitfunc
|
||||
# module, or from the python fallback
|
||||
|
||||
try:
|
||||
from _pydev_thread import _local as local
|
||||
from _pydev_imps._pydev_thread import _local as local
|
||||
except ImportError:
|
||||
from _threading_local import local
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ try:
|
||||
import inspect
|
||||
except:
|
||||
try:
|
||||
import _pydev_inspect as inspect # for older versions
|
||||
from _pydev_imps import _pydev_inspect as inspect
|
||||
except:
|
||||
import traceback;traceback.print_exc() #Ok, no inspect available (search will not work)
|
||||
|
||||
@@ -10,57 +10,58 @@ try:
|
||||
import re
|
||||
except:
|
||||
try:
|
||||
import _pydev_re as re # for older versions @UnresolvedImport
|
||||
import sre as re # for older versions
|
||||
except:
|
||||
import traceback;traceback.print_exc() #Ok, no inspect available (search will not work)
|
||||
|
||||
|
||||
from pydevd_constants import xrange
|
||||
|
||||
def DoFind(f, mod):
|
||||
import linecache
|
||||
if inspect.ismodule(mod):
|
||||
return f, 0, 0
|
||||
|
||||
|
||||
lines = linecache.getlines(f)
|
||||
|
||||
|
||||
if inspect.isclass(mod):
|
||||
name = mod.__name__
|
||||
pat = re.compile(r'^\s*class\s*' + name + r'\b')
|
||||
for i in range(len(lines)):
|
||||
if pat.match(lines[i]):
|
||||
for i in xrange(len(lines)):
|
||||
if pat.match(lines[i]):
|
||||
return f, i, 0
|
||||
|
||||
|
||||
return f, 0, 0
|
||||
|
||||
if inspect.ismethod(mod):
|
||||
mod = mod.im_func
|
||||
|
||||
|
||||
if inspect.isfunction(mod):
|
||||
try:
|
||||
mod = mod.func_code
|
||||
except AttributeError:
|
||||
mod = mod.__code__ #python 3k
|
||||
|
||||
|
||||
if inspect.istraceback(mod):
|
||||
mod = mod.tb_frame
|
||||
|
||||
|
||||
if inspect.isframe(mod):
|
||||
mod = mod.f_code
|
||||
|
||||
if inspect.iscode(mod):
|
||||
if not hasattr(mod, 'co_filename'):
|
||||
return None, 0, 0
|
||||
|
||||
|
||||
if not hasattr(mod, 'co_firstlineno'):
|
||||
return mod.co_filename, 0, 0
|
||||
|
||||
|
||||
lnum = mod.co_firstlineno
|
||||
pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
|
||||
while lnum > 0:
|
||||
if pat.match(lines[lnum]):
|
||||
if pat.match(lines[lnum]):
|
||||
break
|
||||
lnum -= 1
|
||||
|
||||
|
||||
return f, lnum, 0
|
||||
|
||||
raise RuntimeError('Do not know about: ' + f + ' ' + str(mod))
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
from pydev_imports import SimpleXMLRPCServer
|
||||
from pydev_ipython.inputhook import get_inputhook, set_return_control_callback
|
||||
import select
|
||||
import sys
|
||||
|
||||
select_fn = select.select
|
||||
if sys.platform.startswith('java'):
|
||||
select_fn = select.cpython_compatible_select
|
||||
|
||||
class InputHookedXMLRPCServer(SimpleXMLRPCServer):
|
||||
''' An XML-RPC Server that can run hooks while polling for new requests.
|
||||
|
||||
This code was designed to work with IPython's inputhook methods and
|
||||
to allow Debug framework to have a place to run commands during idle
|
||||
too.
|
||||
'''
|
||||
def __init__(self, *args, **kwargs):
|
||||
SimpleXMLRPCServer.__init__(self, *args, **kwargs)
|
||||
# Tell the inputhook mechanisms when control should be returned
|
||||
set_return_control_callback(self.return_control)
|
||||
self.debug_hook = None
|
||||
self.return_control_osc = False
|
||||
|
||||
def return_control(self):
|
||||
''' A function that the inputhooks can call (via inputhook.stdin_ready()) to find
|
||||
out if they should cede control and return '''
|
||||
if self.debug_hook:
|
||||
# Some of the input hooks check return control without doing
|
||||
# a single operation, so we don't return True on every
|
||||
# call when the debug hook is in place to allow the GUI to run
|
||||
# XXX: Eventually the inputhook code will have diverged enough
|
||||
# from the IPython source that it will be worthwhile rewriting
|
||||
# it rather than pretending to maintain the old API
|
||||
self.return_control_osc = not self.return_control_osc
|
||||
if self.return_control_osc:
|
||||
return True
|
||||
r, unused_w, unused_e = select_fn([self], [], [], 0)
|
||||
return bool(r)
|
||||
|
||||
def setDebugHook(self, debug_hook):
|
||||
self.debug_hook = debug_hook
|
||||
|
||||
def serve_forever(self):
|
||||
''' Serve forever, running defined hooks regularly and when idle.
|
||||
Does not support shutdown '''
|
||||
inputhook = get_inputhook()
|
||||
while True:
|
||||
# Block for default 1/2 second when no GUI is in progress
|
||||
timeout = 0.5
|
||||
if self.debug_hook:
|
||||
self.debug_hook()
|
||||
timeout = 0.1
|
||||
if inputhook:
|
||||
try:
|
||||
inputhook()
|
||||
# The GUI has given us an opportunity to try receiving, normally
|
||||
# this happens because the input hook has already polled the
|
||||
# server has knows something is waiting
|
||||
timeout = 0.020
|
||||
except:
|
||||
inputhook = None
|
||||
r, unused_w, unused_e = select_fn([self], [], [], timeout)
|
||||
if self in r:
|
||||
try:
|
||||
self._handle_request_noblock()
|
||||
except AttributeError:
|
||||
# Older libraries do not support _handle_request_noblock, so fall
|
||||
# back to the handle_request version
|
||||
self.handle_request()
|
||||
# Running the request may have changed the inputhook in use
|
||||
inputhook = get_inputhook()
|
||||
|
||||
def shutdown(self):
|
||||
raise NotImplementedError('InputHookedXMLRPCServer does not support shutdown')
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
__all__ = [ "match", "search", "sub", "subn", "split", "findall",
|
||||
"compile", "purge", "template", "escape", "I", "L", "M", "S", "X",
|
||||
"U", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
|
||||
"UNICODE", "error" ]
|
||||
|
||||
import sre, sys
|
||||
module = sys.modules['re']
|
||||
for name in __all__:
|
||||
setattr(module, name, getattr(sre, name))
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
import inspect
|
||||
from django_frame import DjangoTemplateFrame, get_template_file_name, get_template_line
|
||||
from django_frame import DjangoTemplateFrame
|
||||
from pydevd_comm import CMD_SET_BREAK
|
||||
from pydevd_constants import DJANGO_SUSPEND, GetThreadId
|
||||
from pydevd_file_utils import NormFileToServer
|
||||
from runfiles import DictContains
|
||||
from pydevd_constants import DJANGO_SUSPEND, GetThreadId, DictContains
|
||||
from pydevd_breakpoints import LineBreakpoint
|
||||
import pydevd_vars
|
||||
import traceback
|
||||
|
||||
class DjangoLineBreakpoint(LineBreakpoint):
|
||||
def __init__(self, type, file, line, flag, condition, func_name, expression):
|
||||
|
||||
def __init__(self, file, line, condition, func_name, expression):
|
||||
self.file = file
|
||||
self.line = line
|
||||
LineBreakpoint.__init__(self, type, flag, condition, func_name, expression)
|
||||
LineBreakpoint.__init__(self, line, condition, func_name, expression)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, DjangoLineBreakpoint):
|
||||
return False
|
||||
return self.file == other.file and self.line == other.line
|
||||
|
||||
def is_triggered(self, frame):
|
||||
file = get_template_file_name(frame)
|
||||
line = get_template_line(frame)
|
||||
return self.file == file and self.line == line
|
||||
def is_triggered(self, template_frame_file, template_frame_line):
|
||||
return self.file == template_frame_file and self.line == template_frame_line
|
||||
|
||||
def __str__(self):
|
||||
return "DjangoLineBreakpoint: %s-%d" %(self.file, self.line)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
from pydevd_file_utils import GetFileNameAndBaseFromFile
|
||||
import pydev_log
|
||||
import traceback
|
||||
from pydevd_constants import DictContains
|
||||
|
||||
def read_file(filename):
|
||||
f = open(filename, "r")
|
||||
s = f.read()
|
||||
f.close()
|
||||
try:
|
||||
s = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
return s
|
||||
|
||||
|
||||
@@ -34,7 +37,9 @@ def get_source(frame):
|
||||
if hasattr(node, 'source'):
|
||||
return node.source
|
||||
else:
|
||||
pydev_log.error_once("WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True in your settings.py to make django template breakpoints working")
|
||||
pydev_log.error_once(
|
||||
"WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True "
|
||||
"in your settings.py to make django template breakpoints working")
|
||||
return None
|
||||
|
||||
except:
|
||||
@@ -61,41 +66,50 @@ def get_template_file_name(frame):
|
||||
return None
|
||||
|
||||
|
||||
def get_template_line(frame):
|
||||
def get_template_line(frame, template_frame_file):
|
||||
source = get_source(frame)
|
||||
file_name = get_template_file_name(frame)
|
||||
try:
|
||||
return offset_to_line_number(read_file(file_name), source[1][0])
|
||||
return offset_to_line_number(read_file(template_frame_file), source[1][0])
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
class DjangoTemplateFrame:
|
||||
def __init__(self, frame):
|
||||
file_name = get_template_file_name(frame)
|
||||
def __init__(
|
||||
self,
|
||||
frame,
|
||||
template_frame_file=None,
|
||||
template_frame_line=None):
|
||||
|
||||
if template_frame_file is None:
|
||||
template_frame_file = get_template_file_name(frame)
|
||||
|
||||
self.back_context = frame.f_locals['context']
|
||||
self.f_code = FCode('Django Template', file_name)
|
||||
self.f_lineno = get_template_line(frame)
|
||||
self.f_code = FCode('Django Template', template_frame_file)
|
||||
|
||||
if template_frame_line is None:
|
||||
template_frame_line = get_template_line(frame, template_frame_file)
|
||||
self.f_lineno = template_frame_line
|
||||
|
||||
self.f_back = frame
|
||||
self.f_globals = {}
|
||||
self.f_locals = self.collect_context(self.back_context)
|
||||
self.f_locals = self.collect_context()
|
||||
self.f_trace = None
|
||||
|
||||
def collect_context(self, context):
|
||||
def collect_context(self):
|
||||
res = {}
|
||||
try:
|
||||
for d in context.dicts:
|
||||
for k, v in d.items():
|
||||
res[k] = v
|
||||
except AttributeError:
|
||||
for d in self.back_context.dicts:
|
||||
res.update(d)
|
||||
except AttributeError:
|
||||
pass
|
||||
return res
|
||||
|
||||
def changeVariable(self, name, value):
|
||||
for d in self.back_context.dicts:
|
||||
for k, v in d.items():
|
||||
if k == name:
|
||||
d[k] = value
|
||||
if DictContains(d, name):
|
||||
d[name] = value
|
||||
self.f_locals[name] = value
|
||||
|
||||
|
||||
class FCode:
|
||||
@@ -106,10 +120,9 @@ class FCode:
|
||||
|
||||
def is_django_exception_break_context(frame):
|
||||
try:
|
||||
name = frame.f_code.co_name
|
||||
return frame.f_code.co_name in ['_resolve_lookup', 'find_template']
|
||||
except:
|
||||
name = None
|
||||
return name in ['_resolve_lookup', 'find_template']
|
||||
return False
|
||||
|
||||
|
||||
def just_raised(trace):
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
Done in the merge (started from the PyCharm version and bringing in things from PyDev):
|
||||
|
||||
- Added modules which were unused in PyCharm but are used in PyDev.
|
||||
|
||||
- execfile was upgraded to the PyDev version (it had errors with BOM in utf-8).
|
||||
|
||||
- pydevd_file_utils: automatically doing normcase.
|
||||
|
||||
- pyded: multiprocessing supporting 2 approaches (use new connection/use same connection).
|
||||
|
||||
- pydev_monkey: fixes from PyDev to properly deal with windows command lines.
|
||||
|
||||
- Private variables (name-mangled) are now evaluated (so they can be hovered).
|
||||
|
||||
- Exceptions raised from lines with a #@IgnoreException are ignored.
|
||||
|
||||
- Fixed exception changing variable in django debugging.
|
||||
|
||||
- Made debugging with Django breakpoints a bit faster.
|
||||
|
||||
- Exceptions separated by caught/uncaught, so, it's no longer needed to check
|
||||
an additional attribute to check it.
|
||||
|
||||
- When exception is thrown evaluating breakpoint condition, the debugger will stop
|
||||
(can be configured in main_debugger.suspend_on_breakpoint_exception).
|
||||
|
||||
- #@DontTrace comments can be used on methods so that they are ignored when stepping
|
||||
in (needs UI for CMD_ENABLE_DONT_TRACE).
|
||||
|
||||
- Code which stops tracing inside python properties integrated (CMD_SET_PROPERTY_TRACE).
|
||||
|
||||
- Find Referrers integrated.
|
||||
|
||||
- Using same facade for IPython integration.
|
||||
|
||||
- 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Things to be fixed in PyCharm:
|
||||
--------------------------------
|
||||
|
||||
1. CMD_VERSION now should receive that it wants breakpoints by ID (and it
|
||||
should give a breakpoint id which should be used to remove it later
|
||||
when setting a breakpoint).
|
||||
|
||||
2. Setting breakpoint: the func_name is not being properly passed from PyCharm
|
||||
(and as such, PyCharm debugging is slower than it should be).
|
||||
|
||||
Note that it works passing 'None', but the func_name should be given when possible.
|
||||
|
||||
I.e.:
|
||||
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
print('here') # Break here: '__init__' as func_name
|
||||
print(a)
|
||||
|
||||
def bar(self):
|
||||
print('bar') # Break here: 'bar' as func_name
|
||||
|
||||
3. Note (may not need to change anything):
|
||||
Removed support for removing breakpoint without knowing its type (i.e.:
|
||||
remove breakpoint tried to remove a breakpoint if the type wasn't
|
||||
python-line nor django-line, now, it'll give an exception).
|
||||
|
||||
4. break_on_exceptions_thrown_in_same_context / ignore_exceptions_thrown_in_lines_with_ignore_exception
|
||||
|
||||
These are currently set in the bulk operation to add exceptions, but
|
||||
it does make sense to create a separate command for that (but it's only
|
||||
worth doing it when/if PyCharm gets an UI to add it).
|
||||
|
||||
5. UI to ignore exception from additional places (not only from #@IgnoreException code-comments)
|
||||
i.e.: UI for CMD_IGNORE_THROWN_EXCEPTION_AT.
|
||||
|
||||
6. UI to show the current exception (i.e.: deal with CMD_SEND_CURR_EXCEPTION_TRACE and
|
||||
CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED in the client side).
|
||||
|
||||
7. When an exception is detected on a breakpoint condition evaluation, we'll send
|
||||
a CMD_GET_BREAKPOINT_EXCEPTION (which should be handled by PyCharm to show some
|
||||
UI notification).
|
||||
|
||||
8. The CMD_ENABLE_DONT_TRACE must be sent from the UI to skip methods which have
|
||||
an #@DontTrace above it.
|
||||
|
||||
9. The CMD_SET_PROPERTY_TRACE must be sent from the UI to skip setter/getter/deleter
|
||||
python properties.
|
||||
|
||||
10. Integrate find referrers UI in PyCharm. In the backend it uses a CMD_RUN_CUSTOM_OPERATION with:
|
||||
from pydevd_referrers import get_referrer_info
|
||||
get_referrer_info
|
||||
|
||||
11. CMD_RELOAD_CODE has to be integrated (when a file changes it should be issued
|
||||
for 'hot' auto-reload of the code -- note that it's not needed if the
|
||||
user already has some sort of auto-reload builtin -- i.e.: django without the noreload option).
|
||||
|
||||
12. Console Completions: See: pydev_ipython_console_011.PyDevFrontEnd.getCompletions
|
||||
Now we're completing as they come from the IPython backend (i.e.: not stripping %
|
||||
for magic commands).
|
||||
|
||||
13. In PyDev, interrupt can be used to clear the current buffer (whereas in PyCharm it's only
|
||||
possible to activate it to stop the execution of a command) -- note that this is only a
|
||||
client-side limitation.
|
||||
|
||||
14. Console GUI event loop can have some UI integrated.
|
||||
Note that the user can enable it manually (i.e.: by writing something as "%gui qt"
|
||||
the qt backend is integrated, but it's possible to call 'enableGui' with the
|
||||
backend to use from PyCharm too -- in PyDev this is an option with the possible backends).
|
||||
|
||||
|
||||
Things to be fixed in PyDev:
|
||||
--------------------------------
|
||||
|
||||
.Provide UI for 'smart step into' (later)
|
||||
|
||||
. Check what to do with 'message' from xml (later)
|
||||
|
||||
. Deal with sendSignatureCallTrace (later)
|
||||
|
||||
. Set IPYTHONENABLE to False/True to use IPython console (later)
|
||||
|
||||
|
||||
Manual test:
|
||||
---------------
|
||||
|
||||
* Support for IPython GUI event loop in console
|
||||
* Django template debugging
|
||||
* Gevent debugging
|
||||
* Smart step into
|
||||
* Collection of type information of arguments in debug mode
|
||||
* Ability to stop tracing
|
||||
* Ability to run debugger and console on remote interpreter
|
||||
@@ -2,12 +2,10 @@
|
||||
'''
|
||||
@author Radim Kubacki
|
||||
'''
|
||||
import __builtin__
|
||||
import _pydev_imports_tipper
|
||||
import traceback
|
||||
import StringIO
|
||||
import sys
|
||||
import time
|
||||
import urllib
|
||||
import pycompletionserver
|
||||
|
||||
@@ -24,7 +22,7 @@ def GetImports(module_name):
|
||||
except:
|
||||
s = StringIO.StringIO()
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
|
||||
traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
|
||||
err = s.getvalue()
|
||||
pycompletionserver.dbg('Received error: ' + str(err), pycompletionserver.ERROR)
|
||||
@@ -38,4 +36,4 @@ if __name__ == '__main__':
|
||||
mod_name = sys.argv[1]
|
||||
|
||||
print(GetImports(mod_name))
|
||||
|
||||
|
||||
|
||||
@@ -37,10 +37,7 @@ except ImportError:
|
||||
import _pydev_imports_tipper
|
||||
|
||||
|
||||
if pydevd_constants.USE_LIB_COPY:
|
||||
import _pydev_socket as socket
|
||||
else:
|
||||
import socket
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
|
||||
import sys
|
||||
if sys.platform == "darwin":
|
||||
@@ -65,10 +62,7 @@ for name, mod in sys.modules.items():
|
||||
|
||||
import traceback
|
||||
|
||||
if pydevd_constants.USE_LIB_COPY:
|
||||
import _pydev_time as time
|
||||
else:
|
||||
import time
|
||||
from _pydev_imps import _pydev_time as time
|
||||
|
||||
try:
|
||||
import StringIO
|
||||
@@ -93,7 +87,7 @@ def dbg(s, prior):
|
||||
# f = open('c:/temp/test.txt', 'a')
|
||||
# print_ >> f, s
|
||||
# f.close()
|
||||
|
||||
|
||||
import pydev_localhost
|
||||
HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
|
||||
|
||||
@@ -203,7 +197,7 @@ class T(Thread):
|
||||
|
||||
|
||||
def connectToServer(self):
|
||||
import socket
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
|
||||
self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
from pydev_imports import xmlrpclib
|
||||
from pydev_imports import xmlrpclib, _queue, Exec
|
||||
import sys
|
||||
|
||||
import traceback
|
||||
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
from pydevd_constants import IS_JYTHON
|
||||
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_Queue as _queue
|
||||
else:
|
||||
import Queue as _queue
|
||||
except:
|
||||
import queue as _queue
|
||||
|
||||
try:
|
||||
from pydevd_exec import Exec
|
||||
except:
|
||||
from pydevd_exec2 import Exec
|
||||
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_thread as thread
|
||||
else:
|
||||
import thread
|
||||
except:
|
||||
import _thread as thread
|
||||
|
||||
from _pydev_imps import _pydev_thread as thread
|
||||
import pydevd_xml
|
||||
import pydevd_vars
|
||||
|
||||
from pydevd_utils import *
|
||||
from pydevd_utils import * # @UnusedWildImport
|
||||
import traceback
|
||||
|
||||
#=======================================================================================================================
|
||||
# Null
|
||||
@@ -137,7 +113,7 @@ class CodeFragment:
|
||||
def __init__(self, text, is_single_line=True):
|
||||
self.text = text
|
||||
self.is_single_line = is_single_line
|
||||
|
||||
|
||||
def append(self, code_fragment):
|
||||
self.text = self.text + "\n" + code_fragment.text
|
||||
if not code_fragment.is_single_line:
|
||||
@@ -173,9 +149,12 @@ class BaseInterpreterInterface:
|
||||
self.buffer = code_fragment
|
||||
else:
|
||||
self.buffer.append(code_fragment)
|
||||
|
||||
|
||||
return self.needMoreForCode(self.buffer.text)
|
||||
|
||||
def createStdIn(self):
|
||||
return StdIn(self, self.host, self.client_port)
|
||||
|
||||
def addExec(self, code_fragment):
|
||||
original_in = sys.stdin
|
||||
try:
|
||||
@@ -194,7 +173,7 @@ class BaseInterpreterInterface:
|
||||
|
||||
more = False
|
||||
try:
|
||||
sys.stdin = StdIn(self, self.host, self.client_port)
|
||||
sys.stdin = self.createStdIn()
|
||||
try:
|
||||
if help is not None:
|
||||
#This will enable the help() function to work.
|
||||
@@ -209,8 +188,6 @@ class BaseInterpreterInterface:
|
||||
self._input_error_printed = True
|
||||
sys.stderr.write('\nError when trying to update pydoc.help.input\n')
|
||||
sys.stderr.write('(help() may not work -- please report this as a bug in the pydev bugtracker).\n\n')
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
try:
|
||||
@@ -241,8 +218,6 @@ class BaseInterpreterInterface:
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
import traceback;
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
return more
|
||||
@@ -251,7 +226,7 @@ class BaseInterpreterInterface:
|
||||
def doAddExec(self, codeFragment):
|
||||
'''
|
||||
Subclasses should override.
|
||||
|
||||
|
||||
@return: more (True if more input is needed to complete the statement and False if the statement is complete).
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
@@ -260,7 +235,7 @@ class BaseInterpreterInterface:
|
||||
def getNamespace(self):
|
||||
'''
|
||||
Subclasses should override.
|
||||
|
||||
|
||||
@return: dict with namespace.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
@@ -312,14 +287,14 @@ class BaseInterpreterInterface:
|
||||
pass
|
||||
|
||||
try:
|
||||
#if no attempt succeeded, try to return repr()...
|
||||
#if no attempt succeeded, try to return repr()...
|
||||
return repr(obj)
|
||||
except:
|
||||
try:
|
||||
#otherwise the class
|
||||
#otherwise the class
|
||||
return str(obj.__class__)
|
||||
except:
|
||||
#if all fails, go to an empty string
|
||||
#if all fails, go to an empty string
|
||||
return ''
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@@ -353,6 +328,7 @@ class BaseInterpreterInterface:
|
||||
|
||||
|
||||
def interrupt(self):
|
||||
self.buffer = None # Also clear the buffer when it's interrupted.
|
||||
try:
|
||||
if self.interruptable:
|
||||
if hasattr(thread, 'interrupt_main'): #Jython doesn't have it
|
||||
@@ -371,11 +347,13 @@ class BaseInterpreterInterface:
|
||||
self.interruptable = True
|
||||
|
||||
def get_server(self):
|
||||
if self.host is not None:
|
||||
if getattr(self, 'host', None) is not None:
|
||||
return xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port))
|
||||
else:
|
||||
return None
|
||||
|
||||
server = property(get_server)
|
||||
|
||||
def finishExec(self, more):
|
||||
self.interruptable = False
|
||||
|
||||
@@ -409,7 +387,12 @@ class BaseInterpreterInterface:
|
||||
return xml
|
||||
|
||||
def changeVariable(self, attr, value):
|
||||
Exec('%s=%s' % (attr, value), self.getNamespace(), self.getNamespace())
|
||||
def do_change_variable():
|
||||
Exec('%s=%s' % (attr, value), self.getNamespace(), self.getNamespace())
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_change_variable)
|
||||
|
||||
def _findFrame(self, thread_id, frame_id):
|
||||
'''
|
||||
@@ -431,39 +414,48 @@ class BaseInterpreterInterface:
|
||||
Used to show console with variables connection.
|
||||
Mainly, monkey-patches things in the debugger structure so that the debugger protocol works.
|
||||
'''
|
||||
try:
|
||||
# Try to import the packages needed to attach the debugger
|
||||
import pydevd
|
||||
import pydevd_vars
|
||||
import threading
|
||||
except:
|
||||
# This happens on Jython embedded in host eclipse
|
||||
import traceback;traceback.print_exc()
|
||||
return ('pydevd is not available, cannot connect',)
|
||||
def do_connect_to_debugger():
|
||||
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_localhost
|
||||
threading.currentThread().__pydevd_id__ = "console_main"
|
||||
except:
|
||||
# This happens on Jython embedded in host eclipse
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('pydevd is not available, cannot connect\n',)
|
||||
|
||||
self.orig_findFrame = pydevd_vars.findFrame
|
||||
pydevd_vars.findFrame = self._findFrame
|
||||
import pydev_localhost
|
||||
threading.currentThread().__pydevd_id__ = "console_main"
|
||||
|
||||
self.debugger = pydevd.PyDB()
|
||||
try:
|
||||
self.debugger.connect(pydev_localhost.get_localhost(), debuggerPort)
|
||||
self.debugger.prepareToRun()
|
||||
import pydevd_tracing
|
||||
pydevd_tracing.SetTrace(None)
|
||||
except:
|
||||
import traceback;traceback.print_exc()
|
||||
return ('Failed to connect to target debugger.')
|
||||
self.orig_findFrame = pydevd_vars.findFrame
|
||||
pydevd_vars.findFrame = self._findFrame
|
||||
|
||||
# Register to process commands when idle
|
||||
self.debugrunning = False
|
||||
try:
|
||||
self.server.setDebugHook(self.debugger.processInternalCommands)
|
||||
except:
|
||||
import traceback;traceback.print_exc()
|
||||
return ('Version of Python does not support debuggable Interactive Console.')
|
||||
self.debugger = pydevd.PyDB()
|
||||
try:
|
||||
self.debugger.connect(pydev_localhost.get_localhost(), debuggerPort)
|
||||
self.debugger.prepareToRun()
|
||||
import pydevd_tracing
|
||||
pydevd_tracing.SetTrace(None)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Failed to connect to target debugger.\n')
|
||||
|
||||
# Register to process commands when idle
|
||||
self.debugrunning = False
|
||||
try:
|
||||
import pydevconsole
|
||||
pydevconsole.set_debug_hook(self.debugger.processInternalCommands)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
sys.stderr.write('Version of Python does not support debuggable Interactive Console.\n')
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_connect_to_debugger)
|
||||
|
||||
return ('connect complete',)
|
||||
|
||||
@@ -476,19 +468,24 @@ class BaseInterpreterInterface:
|
||||
As with IPython, enabling multiple GUIs isn't an error, but
|
||||
only the last one's main loop runs and it may not work
|
||||
'''
|
||||
from pydev_versioncheck import versionok_for_gui
|
||||
if versionok_for_gui():
|
||||
try:
|
||||
from pydev_ipython.inputhook import enable_gui
|
||||
enable_gui(guiname)
|
||||
except:
|
||||
sys.stderr.write("Failed to enable GUI event loop integration for '%s'\n" % guiname)
|
||||
import traceback;traceback.print_exc()
|
||||
elif guiname not in ['none', '', None]:
|
||||
# Only print a warning if the guiname was going to do something
|
||||
sys.stderr.write("PyDev console: Python version does not support GUI event loop integration for '%s'\n" % guiname)
|
||||
# Return value does not matter, so return back what was sent
|
||||
return guiname
|
||||
def do_enable_gui():
|
||||
from pydev_versioncheck import versionok_for_gui
|
||||
if versionok_for_gui():
|
||||
try:
|
||||
from pydev_ipython.inputhook import enable_gui
|
||||
enable_gui(guiname)
|
||||
except:
|
||||
sys.stderr.write("Failed to enable GUI event loop integration for '%s'\n" % guiname)
|
||||
traceback.print_exc()
|
||||
elif guiname not in ['none', '', None]:
|
||||
# Only print a warning if the guiname was going to do something
|
||||
sys.stderr.write("PyDev console: Python version does not support GUI event loop integration for '%s'\n" % guiname)
|
||||
# Return value does not matter, so return back what was sent
|
||||
return guiname
|
||||
|
||||
# Important: it has to be really enabled in the main thread, so, schedule
|
||||
# it to run in the main thread.
|
||||
self.exec_queue.put(do_enable_gui)
|
||||
|
||||
#=======================================================================================================================
|
||||
# FakeFrame
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
def execute():
|
||||
import os
|
||||
import sys
|
||||
|
||||
files = None
|
||||
if 'combine' not in sys.argv:
|
||||
|
||||
if '--pydev-analyze' in sys.argv:
|
||||
|
||||
#Ok, what we want here is having the files passed through stdin (because
|
||||
#there may be too many files for passing in the command line -- we could
|
||||
#just pass a dir and make the find files here, but as that's already
|
||||
#given in the java side, let's just gather that info here).
|
||||
sys.argv.remove('--pydev-analyze')
|
||||
try:
|
||||
s = raw_input()
|
||||
except:
|
||||
s = input()
|
||||
s = s.replace('\r', '')
|
||||
s = s.replace('\n', '')
|
||||
files = s.split('|')
|
||||
files = [v for v in files if len(v) > 0]
|
||||
|
||||
#Note that in this case we'll already be in the working dir with the coverage files, so, the
|
||||
#coverage file location is not passed.
|
||||
|
||||
else:
|
||||
#For all commands, the coverage file is configured in pydev, and passed as the first argument
|
||||
#in the command line, so, let's make sure this gets to the coverage module.
|
||||
os.environ['COVERAGE_FILE'] = sys.argv[1]
|
||||
del sys.argv[1]
|
||||
|
||||
try:
|
||||
import coverage #@UnresolvedImport
|
||||
except:
|
||||
sys.stderr.write('Error: coverage module could not be imported\n')
|
||||
sys.stderr.write('Please make sure that the coverage module (http://nedbatchelder.com/code/coverage/)\n')
|
||||
sys.stderr.write('is properly installed in your interpreter: %s\n' % (sys.executable,))
|
||||
|
||||
import traceback;traceback.print_exc()
|
||||
return
|
||||
|
||||
#print(coverage.__version__) TODO: Check if the version is a version we support (should be at least 3.4) -- note that maybe the attr is not there.
|
||||
from coverage.cmdline import main #@UnresolvedImport
|
||||
|
||||
if files is not None:
|
||||
sys.argv.append('-r')
|
||||
sys.argv.append('-m')
|
||||
sys.argv += files
|
||||
|
||||
main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
execute()
|
||||
@@ -1,40 +1,52 @@
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
from pydevd_constants import USE_LIB_COPY, izip
|
||||
|
||||
|
||||
try:
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_xmlrpclib as xmlrpclib
|
||||
from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
|
||||
else:
|
||||
import xmlrpclib
|
||||
except ImportError:
|
||||
import xmlrpc.client as xmlrpclib
|
||||
except ImportError:
|
||||
import _pydev_xmlrpclib as xmlrpclib
|
||||
from _pydev_imps import _pydev_xmlrpclib as xmlrpclib
|
||||
|
||||
|
||||
try:
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
else:
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
except ImportError:
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
except ImportError:
|
||||
from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
|
||||
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
|
||||
try:
|
||||
execfile=execfile #Not in Py3k
|
||||
except NameError:
|
||||
from _pydev_execfile import execfile
|
||||
from _pydev_imps._pydev_execfile import execfile
|
||||
|
||||
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_Queue as _queue
|
||||
from _pydev_imps import _pydev_Queue as _queue
|
||||
else:
|
||||
import Queue as _queue
|
||||
except:
|
||||
import queue as _queue #@UnresolvedImport
|
||||
|
||||
|
||||
try:
|
||||
from pydevd_exec import Exec
|
||||
except:
|
||||
@@ -80,7 +92,7 @@ except:
|
||||
return start
|
||||
|
||||
i = 0
|
||||
for start_seg, dest_seg in zip(orig_list, dest_list):
|
||||
for start_seg, dest_seg in izip(orig_list, dest_list):
|
||||
if start_seg != os.path.normcase(dest_seg):
|
||||
break
|
||||
i += 1
|
||||
|
||||
@@ -29,9 +29,8 @@ GLUT Inputhook support functions
|
||||
#-----------------------------------------------------------------------------
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from _pydev_imps import _pydev_time as time
|
||||
import signal
|
||||
import OpenGL.GLUT as glut
|
||||
import OpenGL.platform as platform
|
||||
|
||||
@@ -20,9 +20,8 @@ Authors
|
||||
# Imports
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from _pydev_imps import _pydev_time as time
|
||||
from timeit import default_timer as clock
|
||||
import pyglet
|
||||
from pydev_ipython.inputhook import stdin_ready
|
||||
|
||||
@@ -18,7 +18,13 @@ Author: Christian Boos
|
||||
|
||||
import os
|
||||
import signal
|
||||
import threading
|
||||
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
|
||||
|
||||
from pydev_ipython.qt_for_kernel import QtCore, QtGui
|
||||
from pydev_ipython.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
|
||||
|
||||
@@ -19,7 +19,7 @@ Authors: Robin Dunn, Brian Granger, Ondrej Certik
|
||||
|
||||
import sys
|
||||
import signal
|
||||
import time
|
||||
from _pydev_imps import _pydev_time as time
|
||||
from timeit import default_timer as clock
|
||||
import wx
|
||||
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import sys
|
||||
from pydev_console_utils import BaseInterpreterInterface
|
||||
import re
|
||||
|
||||
import os
|
||||
|
||||
os.environ['TERM'] = 'emacs' #to use proper page_more() for paging
|
||||
|
||||
|
||||
#Uncomment to force PyDev standard shell.
|
||||
#raise ImportError()
|
||||
# Uncomment to force PyDev standard shell.
|
||||
# raise ImportError()
|
||||
|
||||
try:
|
||||
#IPython 0.11 broke compatibility...
|
||||
from pydev_ipython_console_011 import PyDevFrontEnd
|
||||
except:
|
||||
from pydev_ipython_console_010 import PyDevFrontEnd
|
||||
from pydev_ipython_console_011 import PyDevFrontEnd
|
||||
|
||||
#=======================================================================================================================
|
||||
# InterpreterInterface
|
||||
@@ -28,7 +23,7 @@ class InterpreterInterface(BaseInterpreterInterface):
|
||||
BaseInterpreterInterface.__init__(self, mainThread)
|
||||
self.client_port = client_port
|
||||
self.host = host
|
||||
self.interpreter = PyDevFrontEnd()
|
||||
self.interpreter = PyDevFrontEnd(host, client_port)
|
||||
self._input_error_printed = False
|
||||
self.notification_succeeded = False
|
||||
self.notification_tries = 0
|
||||
@@ -57,58 +52,11 @@ class InterpreterInterface(BaseInterpreterInterface):
|
||||
|
||||
|
||||
def getCompletions(self, text, act_tok):
|
||||
try:
|
||||
ipython_completion = text.startswith('%')
|
||||
if not ipython_completion:
|
||||
s = re.search(r'\bcd\b', text)
|
||||
if s is not None and s.start() == 0:
|
||||
ipython_completion = True
|
||||
|
||||
if text is None:
|
||||
text = ""
|
||||
|
||||
TYPE_LOCAL = '9'
|
||||
_line, completions = self.interpreter.complete(text)
|
||||
|
||||
ret = []
|
||||
append = ret.append
|
||||
for completion in completions:
|
||||
if completion.startswith('%'):
|
||||
append((completion[1:], '', '%', TYPE_LOCAL))
|
||||
else:
|
||||
append((completion, '', '', TYPE_LOCAL))
|
||||
|
||||
if ipython_completion:
|
||||
return ret
|
||||
|
||||
#Otherwise, use the default PyDev completer (to get nice icons)
|
||||
from _pydev_completer import Completer
|
||||
|
||||
completer = Completer(self.getNamespace(), None)
|
||||
completions = completer.complete(act_tok)
|
||||
cset = set()
|
||||
for c in completions:
|
||||
cset.add(c[0])
|
||||
for c in ret:
|
||||
if c[0] not in cset:
|
||||
completions.append(c)
|
||||
|
||||
return completions
|
||||
|
||||
except:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
return []
|
||||
return self.interpreter.getCompletions(text, act_tok)
|
||||
|
||||
def close(self):
|
||||
sys.exit(0)
|
||||
|
||||
def ipython_editor(self, file, line):
|
||||
server = self.get_server()
|
||||
|
||||
if server is not None:
|
||||
return server.IPythonEditor(os.path.realpath(file), line)
|
||||
|
||||
def notify_about_magic(self):
|
||||
if not self.notification_succeeded:
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
|
||||
from pydev_console_utils import Null
|
||||
import sys
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDevFrontEnd
|
||||
#=======================================================================================================================
|
||||
class PyDevFrontEnd(PrefilterFrontEnd):
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
PrefilterFrontEnd.__init__(self, *args, **kwargs)
|
||||
#Disable the output trap: we want all that happens to go to the output directly
|
||||
self.shell.output_trap = Null()
|
||||
self._curr_exec_lines = []
|
||||
self._continuation_prompt = ''
|
||||
|
||||
|
||||
def capture_output(self):
|
||||
pass
|
||||
|
||||
|
||||
def release_output(self):
|
||||
pass
|
||||
|
||||
|
||||
def continuation_prompt(self):
|
||||
return self._continuation_prompt
|
||||
|
||||
|
||||
def write(self, txt, refresh=True):
|
||||
original_stdout.write(txt)
|
||||
|
||||
|
||||
def new_prompt(self, prompt):
|
||||
self.input_buffer = ''
|
||||
#The java side takes care of this part.
|
||||
#self.write(prompt)
|
||||
|
||||
|
||||
def show_traceback(self):
|
||||
import traceback;traceback.print_exc()
|
||||
|
||||
|
||||
def write_out(self, txt, *args, **kwargs):
|
||||
original_stdout.write(txt)
|
||||
|
||||
|
||||
def write_err(self, txt, *args, **kwargs):
|
||||
original_stderr.write(txt)
|
||||
|
||||
|
||||
def getNamespace(self):
|
||||
return self.shell.user_ns
|
||||
|
||||
|
||||
def is_complete(self, string):
|
||||
#Based on IPython 0.10.1
|
||||
|
||||
if string in ('', '\n'):
|
||||
# Prefiltering, eg through ipython0, may return an empty
|
||||
# string although some operations have been accomplished. We
|
||||
# thus want to consider an empty string as a complete
|
||||
# statement.
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
# Add line returns here, to make sure that the statement is
|
||||
# complete (except if '\' was used).
|
||||
# This should probably be done in a different place (like
|
||||
# maybe 'prefilter_input' method? For now, this works.
|
||||
clean_string = string.rstrip('\n')
|
||||
if not clean_string.endswith('\\'): clean_string += '\n\n'
|
||||
is_complete = codeop.compile_command(clean_string,
|
||||
"<string>", "exec")
|
||||
except Exception:
|
||||
# XXX: Hack: return True so that the
|
||||
# code gets executed and the error captured.
|
||||
is_complete = True
|
||||
return is_complete
|
||||
|
||||
|
||||
def addExec(self, line):
|
||||
if self._curr_exec_lines:
|
||||
if not line:
|
||||
self._curr_exec_lines.append(line)
|
||||
|
||||
#Would be the line below, but we've set the continuation_prompt to ''.
|
||||
#buf = self.continuation_prompt() + ('\n' + self.continuation_prompt()).join(self._curr_exec_lines)
|
||||
buf = '\n'.join(self._curr_exec_lines)
|
||||
|
||||
self.input_buffer = buf + '\n'
|
||||
if self._on_enter():
|
||||
del self._curr_exec_lines[:]
|
||||
return False #execute complete (no more)
|
||||
|
||||
return True #needs more
|
||||
else:
|
||||
self._curr_exec_lines.append(line)
|
||||
return True #needs more
|
||||
|
||||
else:
|
||||
|
||||
self.input_buffer = line
|
||||
if not self._on_enter():
|
||||
#Did not execute
|
||||
self._curr_exec_lines.append(line)
|
||||
return True #needs more
|
||||
|
||||
return False #execute complete (no more)
|
||||
|
||||
def update(self, globals, locals):
|
||||
locals['_oh'] = self.shell.user_ns['_oh']
|
||||
locals['_ip'] = self.shell.user_ns['_ip']
|
||||
self.shell.user_global_ns = globals
|
||||
self.shell.user_ns = locals
|
||||
|
||||
def is_automagic(self):
|
||||
if self.ipython0.rc.automagic:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_greeting_msg(self):
|
||||
return 'PyDev console: using IPython 0.10\n'
|
||||
|
||||
@@ -1,23 +1,295 @@
|
||||
# TODO that would make IPython integration better
|
||||
# - show output other times then when enter was pressed
|
||||
# - support proper exit to allow IPython to cleanup (e.g. temp files created with %edit)
|
||||
# - support Ctrl-D (Ctrl-Z on Windows)
|
||||
# - use IPython (numbered) prompts in PyDev
|
||||
# - better integration of IPython and PyDev completions
|
||||
# - some of the semantics on handling the code completion are not correct:
|
||||
# eg: Start a line with % and then type c should give %cd as a completion by it doesn't
|
||||
# however type %c and request completions and %cd is given as an option
|
||||
# eg: Completing a magic when user typed it without the leading % causes the % to be inserted
|
||||
# to the left of what should be the first colon.
|
||||
"""Interface to TerminalInteractiveShell for PyDev Interactive Console frontend
|
||||
for IPython 0.11 to 1.0+.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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
|
||||
from IPython.utils.strdispatch import StrDispatch
|
||||
import IPython.core.release as IPythonRelease
|
||||
try:
|
||||
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
||||
except ImportError:
|
||||
# Versions of IPython [0.11,1.0) had an extra hierarchy level
|
||||
from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
|
||||
from IPython.utils import io
|
||||
import sys
|
||||
import codeop, re
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
from IPython.utils.traitlets import CBool, Unicode
|
||||
from IPython.core import release
|
||||
|
||||
from pydev_imports import xmlrpclib
|
||||
|
||||
pydev_banner_parts = [
|
||||
'\n',
|
||||
'PyDev -- Python IDE for Eclipse\n', # TODO can we get a version number in here?
|
||||
'For help on using PyDev\'s Console see http://pydev.org/manual_adv_interactive_console.html\n',
|
||||
]
|
||||
|
||||
default_pydev_banner_parts = default_banner_parts + pydev_banner_parts
|
||||
|
||||
default_pydev_banner = ''.join(default_pydev_banner_parts)
|
||||
|
||||
def show_in_pager(self, strng):
|
||||
""" Run a string through pager """
|
||||
# On PyDev we just output the string, there are scroll bars in the console
|
||||
# to handle "paging". This is the same behaviour as when TERM==dump (see
|
||||
# page.py)
|
||||
print(strng)
|
||||
|
||||
def create_editor_hook(pydev_host, pydev_client_port):
|
||||
def call_editor(self, filename, line=0, wait=True):
|
||||
""" Open an editor in PyDev """
|
||||
if line is None:
|
||||
line = 0
|
||||
|
||||
# Make sure to send an absolution path because unlike most editor hooks
|
||||
# we don't launch a process. This is more like what happens in the zmqshell
|
||||
filename = os.path.abspath(filename)
|
||||
|
||||
# Tell PyDev to open the editor
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_host, pydev_client_port))
|
||||
server.IPythonEditor(filename, str(line))
|
||||
|
||||
if wait:
|
||||
try:
|
||||
raw_input("Press Enter when done editing:")
|
||||
except NameError:
|
||||
input("Press Enter when done editing:")
|
||||
return call_editor
|
||||
|
||||
|
||||
|
||||
class PyDevIPCompleter(IPCompleter):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
""" Create a Completer that reuses the advanced completion support of PyDev
|
||||
in addition to the completion support provided by IPython """
|
||||
IPCompleter.__init__(self, *args, **kwargs)
|
||||
# Use PyDev for python matches, see getCompletions below
|
||||
self.matchers.remove(self.python_matches)
|
||||
|
||||
class PyDevTerminalInteractiveShell(TerminalInteractiveShell):
|
||||
banner1 = Unicode(default_pydev_banner, config=True,
|
||||
help="""The part of the banner to be printed before the profile"""
|
||||
)
|
||||
|
||||
# TODO term_title: (can PyDev's title be changed???, see terminal.py for where to inject code, in particular set_term_title as used by %cd)
|
||||
# for now, just disable term_title
|
||||
term_title = CBool(False)
|
||||
|
||||
# Note in version 0.11 there is no guard in the IPython code about displaying a
|
||||
# warning, so with 0.11 you get:
|
||||
# WARNING: Readline services not available or not loaded.
|
||||
# WARNING: The auto-indent feature requires the readline library
|
||||
# Disable readline, readline type code is all handled by PyDev (on Java side)
|
||||
readline_use = CBool(False)
|
||||
# autoindent has no meaning in PyDev (PyDev always handles that on the Java side),
|
||||
# and attempting to enable it will print a warning in the absence of readline.
|
||||
autoindent = CBool(False)
|
||||
# Force console to not give warning about color scheme choice and default to NoColor.
|
||||
# TODO It would be nice to enable colors in PyDev but:
|
||||
# - The PyDev Console (Eclipse Console) does not support the full range of colors, so the
|
||||
# effect isn't as nice anyway at the command line
|
||||
# - If done, the color scheme should default to LightBG, but actually be dependent on
|
||||
# any settings the user has (such as if a dark theme is in use, then Linux is probably
|
||||
# a better theme).
|
||||
colors_force = CBool(True)
|
||||
colors = Unicode("NoColor")
|
||||
|
||||
# In the PyDev Console, GUI control is done via hookable XML-RPC server
|
||||
@staticmethod
|
||||
def enable_gui(gui=None, app=None):
|
||||
"""Switch amongst GUI input hooks by name.
|
||||
"""
|
||||
# Deferred import
|
||||
from pydev_ipython.inputhook import enable_gui as real_enable_gui
|
||||
try:
|
||||
return real_enable_gui(gui, app)
|
||||
except ValueError as e:
|
||||
raise UsageError("%s" % e)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to hooks
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def init_hooks(self):
|
||||
super(PyDevTerminalInteractiveShell, self).init_hooks()
|
||||
self.set_hook('show_in_pager', show_in_pager)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to exceptions
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None,
|
||||
exception_only=False):
|
||||
# IPython does a lot of clever stuff with Exceptions. However mostly
|
||||
# it is related to IPython running in a terminal instead of an IDE.
|
||||
# (e.g. it prints out snippets of code around the stack trace)
|
||||
# PyDev does a lot of clever stuff too, so leave exception handling
|
||||
# with default print_exc that PyDev can parse and do its clever stuff
|
||||
# with (e.g. it puts links back to the original source code)
|
||||
import traceback;traceback.print_exc()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to text completion
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
# The way to construct an IPCompleter changed in most versions,
|
||||
# so we have a custom, per version implementation of the construction
|
||||
|
||||
def _new_completer_011(self):
|
||||
return PyDevIPCompleter(self,
|
||||
self.user_ns,
|
||||
self.user_global_ns,
|
||||
self.readline_omit__names,
|
||||
self.alias_manager.alias_table,
|
||||
self.has_readline)
|
||||
|
||||
|
||||
def _new_completer_012(self):
|
||||
completer = PyDevIPCompleter(shell=self,
|
||||
namespace=self.user_ns,
|
||||
global_namespace=self.user_global_ns,
|
||||
alias_table=self.alias_manager.alias_table,
|
||||
use_readline=self.has_readline,
|
||||
config=self.config,
|
||||
)
|
||||
self.configurables.append(completer)
|
||||
return completer
|
||||
|
||||
|
||||
def _new_completer_100(self):
|
||||
completer = PyDevIPCompleter(shell=self,
|
||||
namespace=self.user_ns,
|
||||
global_namespace=self.user_global_ns,
|
||||
alias_table=self.alias_manager.alias_table,
|
||||
use_readline=self.has_readline,
|
||||
parent=self,
|
||||
)
|
||||
self.configurables.append(completer)
|
||||
return completer
|
||||
|
||||
def _new_completer_200(self):
|
||||
# As of writing this, IPython 2.0.0 is in dev mode so subject to change
|
||||
completer = PyDevIPCompleter(shell=self,
|
||||
namespace=self.user_ns,
|
||||
global_namespace=self.user_global_ns,
|
||||
use_readline=self.has_readline,
|
||||
parent=self,
|
||||
)
|
||||
self.configurables.append(completer)
|
||||
return completer
|
||||
|
||||
|
||||
|
||||
def init_completer(self):
|
||||
"""Initialize the completion machinery.
|
||||
|
||||
This creates a completer that provides the completions that are
|
||||
IPython specific. We use this to supplement PyDev's core code
|
||||
completions.
|
||||
"""
|
||||
# PyDev uses its own completer and custom hooks so that it uses
|
||||
# most completions from PyDev's core completer which provides
|
||||
# extra information.
|
||||
# See getCompletions for where the two sets of results are merged
|
||||
|
||||
from IPython.core.completerlib import magic_run_completer, cd_completer
|
||||
try:
|
||||
from IPython.core.completerlib import reset_completer
|
||||
except ImportError:
|
||||
# reset_completer was added for rel-0.13
|
||||
reset_completer = None
|
||||
|
||||
if IPythonRelease._version_major >= 2:
|
||||
self.Completer = self._new_completer_200()
|
||||
elif IPythonRelease._version_major >= 1:
|
||||
self.Completer = self._new_completer_100()
|
||||
elif IPythonRelease._version_minor >= 12:
|
||||
self.Completer = self._new_completer_012()
|
||||
else:
|
||||
self.Completer = self._new_completer_011()
|
||||
|
||||
# Add custom completers to the basic ones built into IPCompleter
|
||||
sdisp = self.strdispatchers.get('complete_command', StrDispatch())
|
||||
self.strdispatchers['complete_command'] = sdisp
|
||||
self.Completer.custom_completers = sdisp
|
||||
|
||||
self.set_hook('complete_command', magic_run_completer, str_key='%run')
|
||||
self.set_hook('complete_command', cd_completer, str_key='%cd')
|
||||
if reset_completer:
|
||||
self.set_hook('complete_command', reset_completer, str_key='%reset')
|
||||
|
||||
# Only configure readline if we truly are using readline. IPython can
|
||||
# do tab-completion over the network, in GUIs, etc, where readline
|
||||
# itself may be absent
|
||||
if self.has_readline:
|
||||
self.set_readline_completer()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to aliases
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def init_alias(self):
|
||||
# InteractiveShell defines alias's we want, but TerminalInteractiveShell defines
|
||||
# ones we don't. So don't use super and instead go right to InteractiveShell
|
||||
InteractiveShell.init_alias(self)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to exiting
|
||||
#-------------------------------------------------------------------------
|
||||
def ask_exit(self):
|
||||
""" Ask the shell to exit. Can be overiden and used as a callback. """
|
||||
# TODO PyDev's console does not have support from the Python side to exit
|
||||
# the console. If user forces the exit (with sys.exit()) then the console
|
||||
# simply reports errors. e.g.:
|
||||
# >>> import sys
|
||||
# >>> sys.exit()
|
||||
# Failed to create input stream: Connection refused
|
||||
# >>>
|
||||
# Console already exited with value: 0 while waiting for an answer.
|
||||
# Error stream:
|
||||
# Output stream:
|
||||
# >>>
|
||||
#
|
||||
# Alternatively if you use the non-IPython shell this is what happens
|
||||
# >>> exit()
|
||||
# <type 'exceptions.SystemExit'>:None
|
||||
# >>>
|
||||
# <type 'exceptions.SystemExit'>:None
|
||||
# >>>
|
||||
#
|
||||
super(PyDevTerminalInteractiveShell, self).ask_exit()
|
||||
print('To exit the PyDev Console, terminate the console within Eclipse.')
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Things related to magics
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def init_magics(self):
|
||||
super(PyDevTerminalInteractiveShell, self).init_magics()
|
||||
# TODO Any additional magics for PyDev?
|
||||
|
||||
InteractiveShellABC.register(PyDevTerminalInteractiveShell) # @UndefinedVariable
|
||||
|
||||
#=======================================================================================================================
|
||||
# _showtraceback
|
||||
#=======================================================================================================================
|
||||
def _showtraceback(*args, **kwargs):
|
||||
import traceback;traceback.print_exc()
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDevFrontEnd
|
||||
#=======================================================================================================================
|
||||
@@ -25,49 +297,23 @@ class PyDevFrontEnd:
|
||||
|
||||
version = release.__version__
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
#Initialization based on: from IPython.testing.globalipapp import start_ipython
|
||||
|
||||
self._curr_exec_line = 0
|
||||
# Store certain global objects that IPython modifies
|
||||
_displayhook = sys.displayhook
|
||||
_excepthook = sys.excepthook
|
||||
|
||||
class ClosablePyDevTerminalInteractiveShell(TerminalInteractiveShell):
|
||||
'''Override ask_exit() method for correct exit, exit(), etc. handling.'''
|
||||
def ask_exit(self):
|
||||
sys.exit()
|
||||
def __init__(self, pydev_host, pydev_client_port, *args, **kwarg):
|
||||
|
||||
# Create and initialize our IPython instance.
|
||||
shell = ClosablePyDevTerminalInteractiveShell.instance()
|
||||
self.ipython = PyDevTerminalInteractiveShell.instance()
|
||||
|
||||
shell.showtraceback = _showtraceback
|
||||
# IPython is ready, now clean up some global state...
|
||||
|
||||
# Deactivate the various python system hooks added by ipython for
|
||||
# interactive convenience so we don't confuse the doctest system
|
||||
sys.displayhook = _displayhook
|
||||
sys.excepthook = _excepthook
|
||||
|
||||
# So that ipython magics and aliases can be doctested (they work by making
|
||||
# a call into a global _ip object). Also make the top-level get_ipython
|
||||
# now return this without recursively calling here again.
|
||||
get_ipython = shell.get_ipython
|
||||
try:
|
||||
import __builtin__
|
||||
except:
|
||||
import builtins as __builtin__
|
||||
__builtin__._ip = shell
|
||||
__builtin__.get_ipython = get_ipython
|
||||
# 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))
|
||||
|
||||
# We want to print to stdout/err as usual.
|
||||
io.stdout = original_stdout
|
||||
io.stderr = original_stderr
|
||||
# Display the IPython banner, this has version info and
|
||||
# help info
|
||||
self.ipython.show_banner()
|
||||
|
||||
|
||||
self._curr_exec_line = 0
|
||||
self._curr_exec_lines = []
|
||||
self.ipython = shell
|
||||
|
||||
|
||||
def update(self, globals, locals):
|
||||
@@ -95,7 +341,7 @@ class PyDevFrontEnd:
|
||||
|
||||
def is_complete(self, string):
|
||||
#Based on IPython 0.10.1
|
||||
|
||||
|
||||
if string in ('', '\n'):
|
||||
# Prefiltering, eg through ipython0, may return an empty
|
||||
# string although some operations have been accomplished. We
|
||||
@@ -109,20 +355,64 @@ class PyDevFrontEnd:
|
||||
# This should probably be done in a different place (like
|
||||
# maybe 'prefilter_input' method? For now, this works.
|
||||
clean_string = string.rstrip('\n')
|
||||
if not clean_string.endswith('\\'): clean_string += '\n\n'
|
||||
is_complete = codeop.compile_command(clean_string,
|
||||
"<string>", "exec")
|
||||
if not clean_string.endswith('\\'):
|
||||
clean_string += '\n\n'
|
||||
|
||||
is_complete = codeop.compile_command(
|
||||
clean_string,
|
||||
"<string>",
|
||||
"exec"
|
||||
)
|
||||
except Exception:
|
||||
# XXX: Hack: return True so that the
|
||||
# code gets executed and the error captured.
|
||||
is_complete = True
|
||||
return is_complete
|
||||
|
||||
|
||||
|
||||
|
||||
def getCompletions(self, text, act_tok):
|
||||
# Get completions from IPython and from PyDev and merge the results
|
||||
# IPython only gives context free list of completions, while PyDev
|
||||
# gives detailed information about completions.
|
||||
try:
|
||||
TYPE_IPYTHON = '11'
|
||||
TYPE_IPYTHON_MAGIC = '12'
|
||||
_line, ipython_completions = self.complete(text)
|
||||
|
||||
from _pydev_completer import Completer
|
||||
completer = Completer(self.getNamespace(), None)
|
||||
ret = completer.complete(act_tok)
|
||||
append = ret.append
|
||||
ip = self.ipython
|
||||
pydev_completions = set([f[0] for f in ret])
|
||||
for ipython_completion in ipython_completions:
|
||||
|
||||
#PyCharm was not expecting completions with '%'...
|
||||
#Could be fixed in the backend, but it's probably better
|
||||
#fixing it at PyCharm.
|
||||
#if ipython_completion.startswith('%'):
|
||||
# ipython_completion = ipython_completion[1:]
|
||||
|
||||
if ipython_completion not in pydev_completions:
|
||||
pydev_completions.add(ipython_completion)
|
||||
inf = ip.object_inspect(ipython_completion)
|
||||
if inf['type_name'] == 'Magic function':
|
||||
pydev_type = TYPE_IPYTHON_MAGIC
|
||||
else:
|
||||
pydev_type = TYPE_IPYTHON
|
||||
pydev_doc = inf['docstring']
|
||||
if pydev_doc is None:
|
||||
pydev_doc = ''
|
||||
append((ipython_completion, pydev_doc, '', pydev_type))
|
||||
return ret
|
||||
except:
|
||||
import traceback;traceback.print_exc()
|
||||
return []
|
||||
|
||||
|
||||
def getNamespace(self):
|
||||
return self.ipython.user_ns
|
||||
|
||||
|
||||
def addExec(self, line):
|
||||
if self._curr_exec_lines:
|
||||
self._curr_exec_lines.append(line)
|
||||
@@ -155,5 +445,21 @@ class PyDevFrontEnd:
|
||||
return self.ipython.automagic
|
||||
|
||||
def get_greeting_msg(self):
|
||||
return 'PyDev console: using IPython %s' % self.version
|
||||
return 'PyDev console: using IPython %s\n' % self.version
|
||||
|
||||
|
||||
# If we have succeeded in importing this module, then monkey patch inputhook
|
||||
# in IPython to redirect to PyDev's version. This is essential to make
|
||||
# %gui in 0.11 work (0.12+ fixes it by calling self.enable_gui, which is implemented
|
||||
# above, instead of inputhook.enable_gui).
|
||||
# See testGui (test_pydev_ipython_011.TestRunningCode) which fails on 0.11 without
|
||||
# this patch
|
||||
import IPython.lib.inputhook
|
||||
import pydev_ipython.inputhook
|
||||
IPython.lib.inputhook.enable_gui = pydev_ipython.inputhook.enable_gui
|
||||
# In addition to enable_gui, make all publics in pydev_ipython.inputhook replace
|
||||
# the IPython versions. This enables the examples in IPython's examples/lib/gui-*
|
||||
# to operate properly because those examples don't use %gui magic and instead
|
||||
# rely on using the inputhooks directly.
|
||||
for name in pydev_ipython.inputhook.__all__:
|
||||
setattr(IPython.lib.inputhook, name, getattr(pydev_ipython.inputhook, name))
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import pydevd_constants
|
||||
if pydevd_constants.USE_LIB_COPY:
|
||||
import _pydev_socket as socket
|
||||
else:
|
||||
import socket
|
||||
|
||||
from _pydev_imps import _pydev_socket as socket
|
||||
|
||||
_cache = None
|
||||
def get_localhost():
|
||||
'''
|
||||
Should return 127.0.0.1 in ipv4 and ::1 in ipv6
|
||||
|
||||
|
||||
localhost is not used because on windows vista/windows 7, there can be issues where the resolving doesn't work
|
||||
properly and takes a lot of time (had this issue on the pyunit server).
|
||||
|
||||
properly and takes a lot of time (had this issue on the pyunit server).
|
||||
|
||||
Using the IP directly solves the problem.
|
||||
'''
|
||||
#TODO: Needs better investigation!
|
||||
|
||||
|
||||
global _cache
|
||||
if _cache is None:
|
||||
try:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
import pydev_log
|
||||
import traceback
|
||||
|
||||
helpers = os.path.dirname(__file__).replace('\\', '/')
|
||||
pydev_src_dir = os.path.dirname(__file__)
|
||||
|
||||
from pydevd_constants import xrange
|
||||
|
||||
def is_python(path):
|
||||
if path.endswith("'") or path.endswith('"'):
|
||||
@@ -38,7 +39,9 @@ def patch_args(args):
|
||||
|
||||
if port is not None:
|
||||
new_args.extend(args)
|
||||
new_args[indC + 1] = "import sys; sys.path.append('%s'); import pydevd; pydevd.settrace(host='%s', port=%s, suspend=False); %s"%(helpers, host, port, args[indC + 1])
|
||||
new_args[indC + 1] = ("import sys; sys.path.append(r'%s'); import pydevd; "
|
||||
"pydevd.settrace(host='%s', port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); %s") % (
|
||||
pydev_src_dir, host, port, args[indC + 1])
|
||||
return new_args
|
||||
else:
|
||||
new_args.append(args[0])
|
||||
@@ -52,14 +55,14 @@ def patch_args(args):
|
||||
new_args.append(args[i])
|
||||
else:
|
||||
break
|
||||
i+=1
|
||||
i += 1
|
||||
|
||||
if args[i].endswith('pydevd.py'): #no need to add pydevd twice
|
||||
return args
|
||||
|
||||
for x in sys.original_argv:
|
||||
if sys.platform == "win32" and not x.endswith('"'):
|
||||
arg = '"%s"'%x
|
||||
arg = '"%s"' % x
|
||||
else:
|
||||
arg = x
|
||||
new_args.append(arg)
|
||||
@@ -68,7 +71,7 @@ def patch_args(args):
|
||||
|
||||
while i < len(args):
|
||||
new_args.append(args[i])
|
||||
i+=1
|
||||
i += 1
|
||||
|
||||
return new_args
|
||||
except:
|
||||
@@ -82,26 +85,101 @@ def args_to_str(args):
|
||||
if x.startswith('"') and x.endswith('"'):
|
||||
quoted_args.append(x)
|
||||
else:
|
||||
x = x.replace('"', '\\"')
|
||||
quoted_args.append('"%s"' % x)
|
||||
|
||||
return ' '.join(quoted_args)
|
||||
|
||||
def remove_quotes(str):
|
||||
if str.startswith('"') and str.endswith('"'):
|
||||
return str[1:-1]
|
||||
else:
|
||||
return str
|
||||
|
||||
def str_to_args(str):
|
||||
return [remove_quotes(x) for x in shlex.split(str)]
|
||||
def str_to_args_windows(args):
|
||||
# see http:#msdn.microsoft.com/en-us/library/a1y7w461.aspx
|
||||
result = []
|
||||
|
||||
DEFAULT = 0
|
||||
ARG = 1
|
||||
IN_DOUBLE_QUOTE = 2
|
||||
|
||||
state = DEFAULT
|
||||
backslashes = 0
|
||||
buf = ''
|
||||
|
||||
args_len = len(args)
|
||||
for i in xrange(args_len):
|
||||
ch = args[i]
|
||||
if (ch == '\\'):
|
||||
backslashes+=1
|
||||
continue
|
||||
elif (backslashes != 0):
|
||||
if ch == '"':
|
||||
while backslashes >= 2:
|
||||
backslashes -= 2
|
||||
buf += '\\'
|
||||
if (backslashes == 1):
|
||||
if (state == DEFAULT):
|
||||
state = ARG
|
||||
|
||||
buf += '"'
|
||||
backslashes = 0
|
||||
continue
|
||||
# else fall through to switch
|
||||
else:
|
||||
# false alarm, treat passed backslashes literally...
|
||||
if (state == DEFAULT):
|
||||
state = ARG
|
||||
|
||||
while backslashes > 0:
|
||||
backslashes-=1
|
||||
buf += '\\'
|
||||
# fall through to switch
|
||||
if ch in (' ', '\t'):
|
||||
if (state == DEFAULT):
|
||||
# skip
|
||||
continue
|
||||
elif (state == ARG):
|
||||
state = DEFAULT
|
||||
result.append(buf)
|
||||
buf = ''
|
||||
continue
|
||||
|
||||
if state in (DEFAULT, ARG):
|
||||
if ch == '"':
|
||||
state = IN_DOUBLE_QUOTE
|
||||
else:
|
||||
state = ARG
|
||||
buf += ch
|
||||
|
||||
elif state == IN_DOUBLE_QUOTE:
|
||||
if ch == '"':
|
||||
if (i + 1 < args_len and args[i + 1] == '"'):
|
||||
# Undocumented feature in Windows:
|
||||
# Two consecutive double quotes inside a double-quoted argument are interpreted as
|
||||
# a single double quote.
|
||||
buf += '"'
|
||||
i+=1
|
||||
elif len(buf) == 0:
|
||||
# empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl.
|
||||
result.append("\"\"")
|
||||
state = DEFAULT
|
||||
else:
|
||||
state = ARG
|
||||
else:
|
||||
buf += ch
|
||||
|
||||
else:
|
||||
raise RuntimeError('Illegal condition')
|
||||
|
||||
if len(buf) > 0 or state != DEFAULT:
|
||||
result.append(buf)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def patch_arg_str_win(arg_str):
|
||||
new_arg_str = arg_str.replace('\\', '/')
|
||||
args = str_to_args(new_arg_str)
|
||||
args = str_to_args_windows(arg_str)
|
||||
if not is_python(args[0]):
|
||||
return arg_str
|
||||
arg_str = args_to_str(patch_args(args))
|
||||
pydev_log.debug("New args: %s"% arg_str)
|
||||
pydev_log.debug("New args: %s" % arg_str)
|
||||
return arg_str
|
||||
|
||||
def monkey_patch_module(module, funcname, create_func):
|
||||
@@ -120,7 +198,8 @@ def warn_multiproc():
|
||||
import pydev_log
|
||||
|
||||
pydev_log.error_once(
|
||||
"New process is launching. Breakpoints won't work.\n To debug that process please enable 'Attach to subprocess automatically while debugging' option in the debugger settings.\n")
|
||||
"pydev debugger: New process is launching (breakpoints won't work in the new process).\n"
|
||||
"pydev debugger: To debug that process please enable 'Attach to subprocess automatically while debugging?' option in the debugger settings.\n")
|
||||
|
||||
|
||||
def create_warn_multiproc(original_name):
|
||||
@@ -308,3 +387,110 @@ def patch_new_process_functions_with_warning():
|
||||
except ImportError:
|
||||
import _winapi as _subprocess
|
||||
monkey_patch_module(_subprocess, 'CreateProcess', create_CreateProcessWarnMultiproc)
|
||||
|
||||
|
||||
|
||||
class _NewThreadStartupWithTrace:
|
||||
|
||||
def __init__(self, original_func):
|
||||
self.original_func = original_func
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
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)
|
||||
|
||||
class _NewThreadStartupWithoutTrace:
|
||||
|
||||
def __init__(self, original_func):
|
||||
self.original_func = original_func
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.original_func(*args, **kwargs)
|
||||
|
||||
_UseNewThreadStartup = _NewThreadStartupWithTrace
|
||||
|
||||
def _get_threading_modules():
|
||||
threading_modules = []
|
||||
from _pydev_imps import _pydev_thread
|
||||
threading_modules.append(_pydev_thread)
|
||||
try:
|
||||
import thread as _thread
|
||||
threading_modules.append(_thread)
|
||||
except:
|
||||
import _thread
|
||||
threading_modules.append(_thread)
|
||||
return threading_modules
|
||||
|
||||
threading_modules = _get_threading_modules()
|
||||
|
||||
|
||||
|
||||
def patch_thread_module(thread):
|
||||
|
||||
if getattr(thread, '_original_start_new_thread', None) is None:
|
||||
_original_start_new_thread = thread._original_start_new_thread = thread.start_new_thread
|
||||
else:
|
||||
_original_start_new_thread = thread._original_start_new_thread
|
||||
|
||||
|
||||
class ClassWithPydevStartNewThread:
|
||||
|
||||
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)
|
||||
|
||||
# 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):
|
||||
# start_new_thread = thread.start_new_thread
|
||||
#
|
||||
# def start_it(self):
|
||||
# self.start_new_thread(self.function, args, kwargs)
|
||||
# So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it
|
||||
# does work in the default case because in builtins self isn't passed either.
|
||||
pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread
|
||||
|
||||
try:
|
||||
# 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.
|
||||
thread.start_new_thread = pydev_start_new_thread
|
||||
thread.start_new = pydev_start_new_thread
|
||||
except:
|
||||
pass
|
||||
|
||||
def patch_thread_modules():
|
||||
for t in threading_modules:
|
||||
patch_thread_module(t)
|
||||
|
||||
def undo_patch_thread_modules():
|
||||
for t in threading_modules:
|
||||
try:
|
||||
t.start_new_thread = t._original_start_new_thread
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
t.start_new = t._original_start_new_thread
|
||||
except:
|
||||
pass
|
||||
|
||||
def disable_trace_thread_modules():
|
||||
'''
|
||||
Can be used to temporarily stop tracing threads created with thread.start_new_thread.
|
||||
'''
|
||||
global _UseNewThreadStartup
|
||||
_UseNewThreadStartup = _NewThreadStartupWithoutTrace
|
||||
|
||||
|
||||
def enable_trace_thread_modules():
|
||||
'''
|
||||
Can be used to start tracing threads created with thread.start_new_thread again.
|
||||
'''
|
||||
global _UseNewThreadStartup
|
||||
_UseNewThreadStartup = _NewThreadStartupWithTrace
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
def overrides(method):
|
||||
'''
|
||||
Initially meant to be used as
|
||||
|
||||
class B:
|
||||
@overrides(A.m1)
|
||||
def m1(self):
|
||||
pass
|
||||
|
||||
but as we want to be compatible with Jython 2.1 where decorators have an uglier syntax (needing an assign
|
||||
after the method), it should now be used without being a decorator as below (in which case we don't even check
|
||||
for anything, just that the parent name was actually properly loaded).
|
||||
|
||||
i.e.:
|
||||
|
||||
class B:
|
||||
overrides(A.m1)
|
||||
def m1(self):
|
||||
pass
|
||||
'''
|
||||
return
|
||||
|
||||
# def wrapper(func):
|
||||
# if func.__name__ != method.__name__:
|
||||
# msg = "Wrong @override: %r expected, but overwriting %r."
|
||||
# msg = msg % (func.__name__, method.__name__)
|
||||
# raise AssertionError(msg)
|
||||
#
|
||||
# if func.__doc__ is None:
|
||||
# func.__doc__ = method.__doc__
|
||||
#
|
||||
# return func
|
||||
#
|
||||
# return wrapper
|
||||
|
||||
def implements(method):
|
||||
return
|
||||
# def wrapper(func):
|
||||
# if func.__name__ != method.__name__:
|
||||
# msg = "Wrong @implements: %r expected, but implementing %r."
|
||||
# msg = msg % (func.__name__, method.__name__)
|
||||
# raise AssertionError(msg)
|
||||
#
|
||||
# if func.__doc__ is None:
|
||||
# func.__doc__ = method.__doc__
|
||||
#
|
||||
# return func
|
||||
#
|
||||
# return wrapper
|
||||
@@ -0,0 +1 @@
|
||||
'''An empty file in pysrc that can be imported (from sitecustomize) to find the location of pysrc'''
|
||||
@@ -0,0 +1,831 @@
|
||||
from __future__ import nested_scopes
|
||||
|
||||
import fnmatch
|
||||
import os.path
|
||||
from pydev_runfiles_coverage import StartCoverageSupport
|
||||
import pydev_runfiles_unittest
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
import re
|
||||
import time
|
||||
import unittest
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# Configuration
|
||||
#=======================================================================================================================
|
||||
class Configuration:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
files_or_dirs='',
|
||||
verbosity=2,
|
||||
include_tests=None,
|
||||
tests=None,
|
||||
port=None,
|
||||
files_to_tests=None,
|
||||
jobs=1,
|
||||
split_jobs='tests',
|
||||
coverage_output_dir=None,
|
||||
coverage_include=None,
|
||||
coverage_output_file=None,
|
||||
exclude_files=None,
|
||||
exclude_tests=None,
|
||||
include_files=None,
|
||||
django=False,
|
||||
):
|
||||
self.files_or_dirs = files_or_dirs
|
||||
self.verbosity = verbosity
|
||||
self.include_tests = include_tests
|
||||
self.tests = tests
|
||||
self.port = port
|
||||
self.files_to_tests = files_to_tests
|
||||
self.jobs = jobs
|
||||
self.split_jobs = split_jobs
|
||||
self.django = django
|
||||
|
||||
if include_tests:
|
||||
assert isinstance(include_tests, (list, tuple))
|
||||
|
||||
if exclude_files:
|
||||
assert isinstance(exclude_files, (list, tuple))
|
||||
|
||||
if exclude_tests:
|
||||
assert isinstance(exclude_tests, (list, tuple))
|
||||
|
||||
self.exclude_files = exclude_files
|
||||
self.include_files = include_files
|
||||
self.exclude_tests = exclude_tests
|
||||
|
||||
self.coverage_output_dir = coverage_output_dir
|
||||
self.coverage_include = coverage_include
|
||||
self.coverage_output_file = coverage_output_file
|
||||
|
||||
def __str__(self):
|
||||
return '''Configuration
|
||||
- files_or_dirs: %s
|
||||
- verbosity: %s
|
||||
- tests: %s
|
||||
- port: %s
|
||||
- files_to_tests: %s
|
||||
- jobs: %s
|
||||
- split_jobs: %s
|
||||
|
||||
- include_files: %s
|
||||
- include_tests: %s
|
||||
|
||||
- exclude_files: %s
|
||||
- exclude_tests: %s
|
||||
|
||||
- coverage_output_dir: %s
|
||||
- coverage_include_dir: %s
|
||||
- coverage_output_file: %s
|
||||
|
||||
- django: %s
|
||||
''' % (
|
||||
self.files_or_dirs,
|
||||
self.verbosity,
|
||||
self.tests,
|
||||
self.port,
|
||||
self.files_to_tests,
|
||||
self.jobs,
|
||||
self.split_jobs,
|
||||
|
||||
self.include_files,
|
||||
self.include_tests,
|
||||
|
||||
self.exclude_files,
|
||||
self.exclude_tests,
|
||||
|
||||
self.coverage_output_dir,
|
||||
self.coverage_include,
|
||||
self.coverage_output_file,
|
||||
|
||||
self.django,
|
||||
)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# parse_cmdline
|
||||
#=======================================================================================================================
|
||||
def parse_cmdline(argv=None):
|
||||
"""
|
||||
Parses command line and returns test directories, verbosity, test filter and test suites
|
||||
|
||||
usage:
|
||||
runfiles.py -v|--verbosity <level> -t|--tests <Test.test1,Test2> dirs|files
|
||||
|
||||
Multiprocessing options:
|
||||
jobs=number (with the number of jobs to be used to run the tests)
|
||||
split_jobs='module'|'tests'
|
||||
if == module, a given job will always receive all the tests from a module
|
||||
if == tests, the tests will be split independently of their originating module (default)
|
||||
|
||||
--exclude_files = comma-separated list of patterns with files to exclude (fnmatch style)
|
||||
--include_files = comma-separated list of patterns with files to include (fnmatch style)
|
||||
--exclude_tests = comma-separated list of patterns with test names to exclude (fnmatch style)
|
||||
|
||||
Note: if --tests is given, --exclude_files, --include_files and --exclude_tests are ignored!
|
||||
"""
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
verbosity = 2
|
||||
include_tests = None
|
||||
tests = None
|
||||
port = None
|
||||
jobs = 1
|
||||
split_jobs = 'tests'
|
||||
files_to_tests = {}
|
||||
coverage_output_dir = None
|
||||
coverage_include = None
|
||||
exclude_files = None
|
||||
exclude_tests = None
|
||||
include_files = None
|
||||
django = False
|
||||
|
||||
from _pydev_getopt import gnu_getopt
|
||||
optlist, dirs = gnu_getopt(
|
||||
argv[1:], "",
|
||||
[
|
||||
"verbosity=",
|
||||
"tests=",
|
||||
|
||||
"port=",
|
||||
"config_file=",
|
||||
|
||||
"jobs=",
|
||||
"split_jobs=",
|
||||
|
||||
"include_tests=",
|
||||
"include_files=",
|
||||
|
||||
"exclude_files=",
|
||||
"exclude_tests=",
|
||||
|
||||
"coverage_output_dir=",
|
||||
"coverage_include=",
|
||||
|
||||
"django="
|
||||
]
|
||||
)
|
||||
|
||||
for opt, value in optlist:
|
||||
if opt in ("-v", "--verbosity"):
|
||||
verbosity = value
|
||||
|
||||
elif opt in ("-p", "--port"):
|
||||
port = int(value)
|
||||
|
||||
elif opt in ("-j", "--jobs"):
|
||||
jobs = int(value)
|
||||
|
||||
elif opt in ("-s", "--split_jobs"):
|
||||
split_jobs = value
|
||||
if split_jobs not in ('module', 'tests'):
|
||||
raise AssertionError('Expected split to be either "module" or "tests". Was :%s' % (split_jobs,))
|
||||
|
||||
elif opt in ("-d", "--coverage_output_dir",):
|
||||
coverage_output_dir = value.strip()
|
||||
|
||||
elif opt in ("-i", "--coverage_include",):
|
||||
coverage_include = value.strip()
|
||||
|
||||
elif opt in ("-I", "--include_tests"):
|
||||
include_tests = value.split(',')
|
||||
|
||||
elif opt in ("-E", "--exclude_files"):
|
||||
exclude_files = value.split(',')
|
||||
|
||||
elif opt in ("-F", "--include_files"):
|
||||
include_files = value.split(',')
|
||||
|
||||
elif opt in ("-e", "--exclude_tests"):
|
||||
exclude_tests = value.split(',')
|
||||
|
||||
elif opt in ("-t", "--tests"):
|
||||
tests = value.split(',')
|
||||
|
||||
elif opt in ("--django",):
|
||||
django = value.strip() in ['true', 'True', '1']
|
||||
|
||||
elif opt in ("-c", "--config_file"):
|
||||
config_file = value.strip()
|
||||
if os.path.exists(config_file):
|
||||
f = open(config_file, 'rU')
|
||||
try:
|
||||
config_file_contents = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
if config_file_contents:
|
||||
config_file_contents = config_file_contents.strip()
|
||||
|
||||
if config_file_contents:
|
||||
for line in config_file_contents.splitlines():
|
||||
file_and_test = line.split('|')
|
||||
if len(file_and_test) == 2:
|
||||
file, test = file_and_test
|
||||
if DictContains(files_to_tests, file):
|
||||
files_to_tests[file].append(test)
|
||||
else:
|
||||
files_to_tests[file] = [test]
|
||||
|
||||
else:
|
||||
sys.stderr.write('Could not find config file: %s\n' % (config_file,))
|
||||
|
||||
if type([]) != type(dirs):
|
||||
dirs = [dirs]
|
||||
|
||||
ret_dirs = []
|
||||
for d in dirs:
|
||||
if '|' in d:
|
||||
#paths may come from the ide separated by |
|
||||
ret_dirs.extend(d.split('|'))
|
||||
else:
|
||||
ret_dirs.append(d)
|
||||
|
||||
verbosity = int(verbosity)
|
||||
|
||||
if tests:
|
||||
if verbosity > 4:
|
||||
sys.stdout.write('--tests provided. Ignoring --exclude_files, --exclude_tests and --include_files\n')
|
||||
exclude_files = exclude_tests = include_files = None
|
||||
|
||||
config = Configuration(
|
||||
ret_dirs,
|
||||
verbosity,
|
||||
include_tests,
|
||||
tests,
|
||||
port,
|
||||
files_to_tests,
|
||||
jobs,
|
||||
split_jobs,
|
||||
coverage_output_dir,
|
||||
coverage_include,
|
||||
exclude_files=exclude_files,
|
||||
exclude_tests=exclude_tests,
|
||||
include_files=include_files,
|
||||
django=django,
|
||||
)
|
||||
|
||||
if verbosity > 5:
|
||||
sys.stdout.write(str(config) + '\n')
|
||||
return config
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PydevTestRunner
|
||||
#=======================================================================================================================
|
||||
class PydevTestRunner(object):
|
||||
""" finds and runs a file or directory of files as a unit test """
|
||||
|
||||
__py_extensions = ["*.py", "*.pyw"]
|
||||
__exclude_files = ["__init__.*"]
|
||||
|
||||
#Just to check that only this attributes will be written to this file
|
||||
__slots__ = [
|
||||
'verbosity', #Always used
|
||||
|
||||
'files_to_tests', #If this one is given, the ones below are not used
|
||||
|
||||
'files_or_dirs', #Files or directories received in the command line
|
||||
'include_tests', #The filter used to collect the tests
|
||||
'tests', #Strings with the tests to be run
|
||||
|
||||
'jobs', #Integer with the number of jobs that should be used to run the test cases
|
||||
'split_jobs', #String with 'tests' or 'module' (how should the jobs be split)
|
||||
|
||||
'configuration',
|
||||
'coverage',
|
||||
]
|
||||
|
||||
def __init__(self, configuration):
|
||||
self.verbosity = configuration.verbosity
|
||||
|
||||
self.jobs = configuration.jobs
|
||||
self.split_jobs = configuration.split_jobs
|
||||
|
||||
files_to_tests = configuration.files_to_tests
|
||||
if files_to_tests:
|
||||
self.files_to_tests = files_to_tests
|
||||
self.files_or_dirs = list(files_to_tests.keys())
|
||||
self.tests = None
|
||||
else:
|
||||
self.files_to_tests = {}
|
||||
self.files_or_dirs = configuration.files_or_dirs
|
||||
self.tests = configuration.tests
|
||||
|
||||
self.configuration = configuration
|
||||
self.__adjust_path()
|
||||
|
||||
|
||||
def __adjust_path(self):
|
||||
""" add the current file or directory to the python path """
|
||||
path_to_append = None
|
||||
for n in xrange(len(self.files_or_dirs)):
|
||||
dir_name = self.__unixify(self.files_or_dirs[n])
|
||||
if os.path.isdir(dir_name):
|
||||
if not dir_name.endswith("/"):
|
||||
self.files_or_dirs[n] = dir_name + "/"
|
||||
path_to_append = os.path.normpath(dir_name)
|
||||
elif os.path.isfile(dir_name):
|
||||
path_to_append = os.path.dirname(dir_name)
|
||||
else:
|
||||
if not os.path.exists(dir_name):
|
||||
block_line = '*' * 120
|
||||
sys.stderr.write('\n%s\n* PyDev test runner error: %s does not exist.\n%s\n' % (block_line, dir_name, block_line))
|
||||
return
|
||||
msg = ("unknown type. \n%s\nshould be file or a directory.\n" % (dir_name))
|
||||
raise RuntimeError(msg)
|
||||
if path_to_append is not None:
|
||||
#Add it as the last one (so, first things are resolved against the default dirs and
|
||||
#if none resolves, then we try a relative import).
|
||||
sys.path.append(path_to_append)
|
||||
|
||||
def __is_valid_py_file(self, fname):
|
||||
""" tests that a particular file contains the proper file extension
|
||||
and is not in the list of files to exclude """
|
||||
is_valid_fname = 0
|
||||
for invalid_fname in self.__class__.__exclude_files:
|
||||
is_valid_fname += int(not fnmatch.fnmatch(fname, invalid_fname))
|
||||
if_valid_ext = 0
|
||||
for ext in self.__class__.__py_extensions:
|
||||
if_valid_ext += int(fnmatch.fnmatch(fname, ext))
|
||||
return is_valid_fname > 0 and if_valid_ext > 0
|
||||
|
||||
def __unixify(self, s):
|
||||
""" stupid windows. converts the backslash to forwardslash for consistency """
|
||||
return os.path.normpath(s).replace(os.sep, "/")
|
||||
|
||||
def __importify(self, s, dir=False):
|
||||
""" turns directory separators into dots and removes the ".py*" extension
|
||||
so the string can be used as import statement """
|
||||
if not dir:
|
||||
dirname, fname = os.path.split(s)
|
||||
|
||||
if fname.count('.') > 1:
|
||||
#if there's a file named xxx.xx.py, it is not a valid module, so, let's not load it...
|
||||
return
|
||||
|
||||
imp_stmt_pieces = [dirname.replace("\\", "/").replace("/", "."), os.path.splitext(fname)[0]]
|
||||
|
||||
if len(imp_stmt_pieces[0]) == 0:
|
||||
imp_stmt_pieces = imp_stmt_pieces[1:]
|
||||
|
||||
return ".".join(imp_stmt_pieces)
|
||||
|
||||
else: #handle dir
|
||||
return s.replace("\\", "/").replace("/", ".")
|
||||
|
||||
def __add_files(self, pyfiles, root, files):
|
||||
""" if files match, appends them to pyfiles. used by os.path.walk fcn """
|
||||
for fname in files:
|
||||
if self.__is_valid_py_file(fname):
|
||||
name_without_base_dir = self.__unixify(os.path.join(root, fname))
|
||||
pyfiles.append(name_without_base_dir)
|
||||
|
||||
|
||||
def find_import_files(self):
|
||||
""" return a list of files to import """
|
||||
if self.files_to_tests:
|
||||
pyfiles = self.files_to_tests.keys()
|
||||
else:
|
||||
pyfiles = []
|
||||
|
||||
for base_dir in self.files_or_dirs:
|
||||
if os.path.isdir(base_dir):
|
||||
if hasattr(os, 'walk'):
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
|
||||
#Note: handling directories that should be excluded from the search because
|
||||
#they don't have __init__.py
|
||||
exclude = {}
|
||||
for d in dirs:
|
||||
for init in ['__init__.py', '__init__.pyo', '__init__.pyc', '__init__.pyw']:
|
||||
if os.path.exists(os.path.join(root, d, init).replace('\\', '/')):
|
||||
break
|
||||
else:
|
||||
exclude[d] = 1
|
||||
|
||||
if exclude:
|
||||
new = []
|
||||
for d in dirs:
|
||||
if d not in exclude:
|
||||
new.append(d)
|
||||
|
||||
dirs[:] = new
|
||||
|
||||
self.__add_files(pyfiles, root, files)
|
||||
else:
|
||||
# jython2.1 is too old for os.walk!
|
||||
os.path.walk(base_dir, self.__add_files, pyfiles)
|
||||
|
||||
elif os.path.isfile(base_dir):
|
||||
pyfiles.append(base_dir)
|
||||
|
||||
if self.configuration.exclude_files or self.configuration.include_files:
|
||||
ret = []
|
||||
for f in pyfiles:
|
||||
add = True
|
||||
basename = os.path.basename(f)
|
||||
if self.configuration.include_files:
|
||||
add = False
|
||||
|
||||
for pat in self.configuration.include_files:
|
||||
if fnmatch.fnmatchcase(basename, pat):
|
||||
add = True
|
||||
break
|
||||
|
||||
if not add:
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('Skipped file: %s (did not match any include_files pattern: %s)\n' % (f, self.configuration.include_files))
|
||||
|
||||
elif self.configuration.exclude_files:
|
||||
for pat in self.configuration.exclude_files:
|
||||
if fnmatch.fnmatchcase(basename, pat):
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('Skipped file: %s (matched exclude_files pattern: %s)\n' % (f, pat))
|
||||
|
||||
elif self.verbosity > 2:
|
||||
sys.stdout.write('Skipped file: %s\n' % (f,))
|
||||
|
||||
add = False
|
||||
break
|
||||
|
||||
if add:
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('Adding file: %s for test discovery.\n' % (f,))
|
||||
ret.append(f)
|
||||
|
||||
pyfiles = ret
|
||||
|
||||
|
||||
return pyfiles
|
||||
|
||||
def __get_module_from_str(self, modname, print_exception, pyfile):
|
||||
""" Import the module in the given import path.
|
||||
* Returns the "final" module, so importing "coilib40.subject.visu"
|
||||
returns the "visu" module, not the "coilib40" as returned by __import__ """
|
||||
try:
|
||||
mod = __import__(modname)
|
||||
for part in modname.split('.')[1:]:
|
||||
mod = getattr(mod, part)
|
||||
return mod
|
||||
except:
|
||||
if print_exception:
|
||||
import pydev_runfiles_xml_rpc
|
||||
import pydevd_io
|
||||
buf_err = pydevd_io.StartRedirect(keep_original_redirection=True, std='stderr')
|
||||
buf_out = pydevd_io.StartRedirect(keep_original_redirection=True, std='stdout')
|
||||
try:
|
||||
import traceback;traceback.print_exc()
|
||||
sys.stderr.write('ERROR: Module: %s could not be imported (file: %s).\n' % (modname, pyfile))
|
||||
finally:
|
||||
pydevd_io.EndRedirect('stderr')
|
||||
pydevd_io.EndRedirect('stdout')
|
||||
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'error', buf_out.getvalue(), buf_err.getvalue(), pyfile, modname, 0)
|
||||
|
||||
return None
|
||||
|
||||
def find_modules_from_files(self, pyfiles):
|
||||
""" returns a list of modules given a list of files """
|
||||
#let's make sure that the paths we want are in the pythonpath...
|
||||
imports = [(s, self.__importify(s)) for s in pyfiles]
|
||||
|
||||
system_paths = []
|
||||
for s in sys.path:
|
||||
system_paths.append(self.__importify(s, True))
|
||||
|
||||
|
||||
ret = []
|
||||
for pyfile, imp in imports:
|
||||
if imp is None:
|
||||
continue #can happen if a file is not a valid module
|
||||
choices = []
|
||||
for s in system_paths:
|
||||
if imp.startswith(s):
|
||||
add = imp[len(s) + 1:]
|
||||
if add:
|
||||
choices.append(add)
|
||||
#sys.stdout.write(' ' + add + ' ')
|
||||
|
||||
if not choices:
|
||||
sys.stdout.write('PYTHONPATH not found for file: %s\n' % imp)
|
||||
else:
|
||||
for i, import_str in enumerate(choices):
|
||||
print_exception = i == len(choices) - 1
|
||||
mod = self.__get_module_from_str(import_str, print_exception, pyfile)
|
||||
if mod is not None:
|
||||
ret.append((pyfile, mod, import_str))
|
||||
break
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
#===================================================================================================================
|
||||
# GetTestCaseNames
|
||||
#===================================================================================================================
|
||||
class GetTestCaseNames:
|
||||
"""Yes, we need a class for that (cannot use outer context on jython 2.1)"""
|
||||
|
||||
def __init__(self, accepted_classes, accepted_methods):
|
||||
self.accepted_classes = accepted_classes
|
||||
self.accepted_methods = accepted_methods
|
||||
|
||||
def __call__(self, testCaseClass):
|
||||
"""Return a sorted sequence of method names found within testCaseClass"""
|
||||
testFnNames = []
|
||||
className = testCaseClass.__name__
|
||||
|
||||
if DictContains(self.accepted_classes, className):
|
||||
for attrname in dir(testCaseClass):
|
||||
#If a class is chosen, we select all the 'test' methods'
|
||||
if attrname.startswith('test') and hasattr(getattr(testCaseClass, attrname), '__call__'):
|
||||
testFnNames.append(attrname)
|
||||
|
||||
else:
|
||||
for attrname in dir(testCaseClass):
|
||||
#If we have the class+method name, we must do a full check and have an exact match.
|
||||
if DictContains(self.accepted_methods, className + '.' + attrname):
|
||||
if hasattr(getattr(testCaseClass, attrname), '__call__'):
|
||||
testFnNames.append(attrname)
|
||||
|
||||
#sorted() is not available in jython 2.1
|
||||
testFnNames.sort()
|
||||
return testFnNames
|
||||
|
||||
|
||||
def _decorate_test_suite(self, suite, pyfile, module_name):
|
||||
if isinstance(suite, unittest.TestSuite):
|
||||
add = False
|
||||
suite.__pydev_pyfile__ = pyfile
|
||||
suite.__pydev_module_name__ = module_name
|
||||
|
||||
for t in suite._tests:
|
||||
t.__pydev_pyfile__ = pyfile
|
||||
t.__pydev_module_name__ = module_name
|
||||
if self._decorate_test_suite(t, pyfile, module_name):
|
||||
add = True
|
||||
|
||||
return add
|
||||
|
||||
elif isinstance(suite, unittest.TestCase):
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def find_tests_from_modules(self, file_and_modules_and_module_name):
|
||||
""" returns the unittests given a list of modules """
|
||||
#Use our own suite!
|
||||
unittest.TestLoader.suiteClass = pydev_runfiles_unittest.PydevTestSuite
|
||||
loader = unittest.TestLoader()
|
||||
|
||||
ret = []
|
||||
if self.files_to_tests:
|
||||
for pyfile, m, module_name in file_and_modules_and_module_name:
|
||||
accepted_classes = {}
|
||||
accepted_methods = {}
|
||||
tests = self.files_to_tests[pyfile]
|
||||
for t in tests:
|
||||
accepted_methods[t] = t
|
||||
|
||||
loader.getTestCaseNames = self.GetTestCaseNames(accepted_classes, accepted_methods)
|
||||
|
||||
suite = loader.loadTestsFromModule(m)
|
||||
if self._decorate_test_suite(suite, pyfile, module_name):
|
||||
ret.append(suite)
|
||||
return ret
|
||||
|
||||
|
||||
if self.tests:
|
||||
accepted_classes = {}
|
||||
accepted_methods = {}
|
||||
|
||||
for t in self.tests:
|
||||
splitted = t.split('.')
|
||||
if len(splitted) == 1:
|
||||
accepted_classes[t] = t
|
||||
|
||||
elif len(splitted) == 2:
|
||||
accepted_methods[t] = t
|
||||
|
||||
loader.getTestCaseNames = self.GetTestCaseNames(accepted_classes, accepted_methods)
|
||||
|
||||
|
||||
for pyfile, m, module_name in file_and_modules_and_module_name:
|
||||
suite = loader.loadTestsFromModule(m)
|
||||
if self._decorate_test_suite(suite, pyfile, module_name):
|
||||
ret.append(suite)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def filter_tests(self, test_objs, internal_call=False):
|
||||
""" based on a filter name, only return those tests that have
|
||||
the test case names that match """
|
||||
if not internal_call:
|
||||
if not self.configuration.include_tests and not self.tests and not self.configuration.exclude_tests:
|
||||
#No need to filter if we have nothing to filter!
|
||||
return test_objs
|
||||
|
||||
if self.verbosity > 1:
|
||||
if self.configuration.include_tests:
|
||||
sys.stdout.write('Tests to include: %s\n' % (self.configuration.include_tests,))
|
||||
|
||||
if self.tests:
|
||||
sys.stdout.write('Tests to run: %s\n' % (self.tests,))
|
||||
|
||||
if self.configuration.exclude_tests:
|
||||
sys.stdout.write('Tests to exclude: %s\n' % (self.configuration.exclude_tests,))
|
||||
|
||||
test_suite = []
|
||||
for test_obj in test_objs:
|
||||
|
||||
if isinstance(test_obj, unittest.TestSuite):
|
||||
#Note: keep the suites as they are and just 'fix' the tests (so, don't use the iter_tests).
|
||||
if test_obj._tests:
|
||||
test_obj._tests = self.filter_tests(test_obj._tests, True)
|
||||
if test_obj._tests: #Only add the suite if we still have tests there.
|
||||
test_suite.append(test_obj)
|
||||
|
||||
elif isinstance(test_obj, unittest.TestCase):
|
||||
try:
|
||||
testMethodName = test_obj._TestCase__testMethodName
|
||||
except AttributeError:
|
||||
#changed in python 2.5
|
||||
testMethodName = test_obj._testMethodName
|
||||
|
||||
add = True
|
||||
if self.configuration.exclude_tests:
|
||||
for pat in self.configuration.exclude_tests:
|
||||
if fnmatch.fnmatchcase(testMethodName, pat):
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('Skipped test: %s (matched exclude_tests pattern: %s)\n' % (testMethodName, pat))
|
||||
|
||||
elif self.verbosity > 2:
|
||||
sys.stdout.write('Skipped test: %s\n' % (testMethodName,))
|
||||
|
||||
add = False
|
||||
break
|
||||
|
||||
if add:
|
||||
if self.__match_tests(self.tests, test_obj, testMethodName):
|
||||
include = True
|
||||
if self.configuration.include_tests:
|
||||
include = False
|
||||
for pat in self.configuration.include_tests:
|
||||
if fnmatch.fnmatchcase(testMethodName, pat):
|
||||
include = True
|
||||
break
|
||||
if include:
|
||||
test_suite.append(test_obj)
|
||||
else:
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('Skipped test: %s (did not match any include_tests pattern %s)\n' % (self.configuration.include_tests,))
|
||||
return test_suite
|
||||
|
||||
|
||||
def iter_tests(self, test_objs):
|
||||
#Note: not using yield because of Jython 2.1.
|
||||
tests = []
|
||||
for test_obj in test_objs:
|
||||
if isinstance(test_obj, unittest.TestSuite):
|
||||
tests.extend(self.iter_tests(test_obj._tests))
|
||||
|
||||
elif isinstance(test_obj, unittest.TestCase):
|
||||
tests.append(test_obj)
|
||||
return tests
|
||||
|
||||
|
||||
def list_test_names(self, test_objs):
|
||||
names = []
|
||||
for tc in self.iter_tests(test_objs):
|
||||
try:
|
||||
testMethodName = tc._TestCase__testMethodName
|
||||
except AttributeError:
|
||||
#changed in python 2.5
|
||||
testMethodName = tc._testMethodName
|
||||
names.append(testMethodName)
|
||||
return names
|
||||
|
||||
|
||||
def __match_tests(self, tests, test_case, test_method_name):
|
||||
if not tests:
|
||||
return 1
|
||||
|
||||
for t in tests:
|
||||
class_and_method = t.split('.')
|
||||
if len(class_and_method) == 1:
|
||||
#only class name
|
||||
if class_and_method[0] == test_case.__class__.__name__:
|
||||
return 1
|
||||
|
||||
elif len(class_and_method) == 2:
|
||||
if class_and_method[0] == test_case.__class__.__name__ and class_and_method[1] == test_method_name:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def __match(self, filter_list, name):
|
||||
""" returns whether a test name matches the test filter """
|
||||
if filter_list is None:
|
||||
return 1
|
||||
for f in filter_list:
|
||||
if re.match(f, name):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def run_tests(self, handle_coverage=True):
|
||||
""" runs all tests """
|
||||
sys.stdout.write("Finding files... ")
|
||||
files = self.find_import_files()
|
||||
if self.verbosity > 3:
|
||||
sys.stdout.write('%s ... done.\n' % (self.files_or_dirs))
|
||||
else:
|
||||
sys.stdout.write('done.\n')
|
||||
sys.stdout.write("Importing test modules ... ")
|
||||
|
||||
|
||||
if handle_coverage:
|
||||
coverage_files, coverage = StartCoverageSupport(self.configuration)
|
||||
|
||||
file_and_modules_and_module_name = self.find_modules_from_files(files)
|
||||
sys.stdout.write("done.\n")
|
||||
|
||||
all_tests = self.find_tests_from_modules(file_and_modules_and_module_name)
|
||||
all_tests = self.filter_tests(all_tests)
|
||||
|
||||
test_suite = pydev_runfiles_unittest.PydevTestSuite(all_tests)
|
||||
import pydev_runfiles_xml_rpc
|
||||
pydev_runfiles_xml_rpc.notifyTestsCollected(test_suite.countTestCases())
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
def run_tests():
|
||||
executed_in_parallel = False
|
||||
if self.jobs > 1:
|
||||
import pydev_runfiles_parallel
|
||||
|
||||
#What may happen is that the number of jobs needed is lower than the number of jobs requested
|
||||
#(e.g.: 2 jobs were requested for running 1 test) -- in which case ExecuteTestsInParallel will
|
||||
#return False and won't run any tests.
|
||||
executed_in_parallel = pydev_runfiles_parallel.ExecuteTestsInParallel(
|
||||
all_tests, self.jobs, self.split_jobs, self.verbosity, coverage_files, self.configuration.coverage_include)
|
||||
|
||||
if not executed_in_parallel:
|
||||
#If in coverage, we don't need to pass anything here (coverage is already enabled for this execution).
|
||||
runner = pydev_runfiles_unittest.PydevTextTestRunner(stream=sys.stdout, descriptions=1, verbosity=self.verbosity)
|
||||
sys.stdout.write('\n')
|
||||
runner.run(test_suite)
|
||||
|
||||
if self.configuration.django:
|
||||
MyDjangoTestSuiteRunner(run_tests).run_tests([])
|
||||
else:
|
||||
run_tests()
|
||||
|
||||
if handle_coverage:
|
||||
coverage.stop()
|
||||
coverage.save()
|
||||
|
||||
total_time = 'Finished in: %.2f secs.' % (time.time() - start_time,)
|
||||
pydev_runfiles_xml_rpc.notifyTestRunFinished(total_time)
|
||||
|
||||
|
||||
try:
|
||||
from django.test.simple import DjangoTestSuiteRunner
|
||||
except:
|
||||
class DjangoTestSuiteRunner:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def run_tests(self, *args, **kwargs):
|
||||
raise AssertionError("Unable to run suite with DjangoTestSuiteRunner because it couldn't be imported.")
|
||||
|
||||
class MyDjangoTestSuiteRunner(DjangoTestSuiteRunner):
|
||||
|
||||
def __init__(self, on_run_suite):
|
||||
DjangoTestSuiteRunner.__init__(self)
|
||||
self.on_run_suite = on_run_suite
|
||||
|
||||
def build_suite(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def suite_result(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def run_suite(self, *args, **kwargs):
|
||||
self.on_run_suite()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
def main(configuration):
|
||||
PydevTestRunner(configuration).run_tests()
|
||||
@@ -0,0 +1,76 @@
|
||||
import os.path
|
||||
import sys
|
||||
from pydevd_constants import Null
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# GetCoverageFiles
|
||||
#=======================================================================================================================
|
||||
def GetCoverageFiles(coverage_output_dir, number_of_files):
|
||||
base_dir = coverage_output_dir
|
||||
ret = []
|
||||
i = 0
|
||||
while len(ret) < number_of_files:
|
||||
while True:
|
||||
f = os.path.join(base_dir, '.coverage.%s' % i)
|
||||
i += 1
|
||||
if not os.path.exists(f):
|
||||
ret.append(f)
|
||||
break #Break only inner for.
|
||||
return ret
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# StartCoverageSupport
|
||||
#=======================================================================================================================
|
||||
def StartCoverageSupport(configuration):
|
||||
return StartCoverageSupportFromParams(
|
||||
configuration.coverage_output_dir,
|
||||
configuration.coverage_output_file,
|
||||
configuration.jobs,
|
||||
configuration.coverage_include,
|
||||
)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# StartCoverageSupportFromParams
|
||||
#=======================================================================================================================
|
||||
def StartCoverageSupportFromParams(coverage_output_dir, coverage_output_file, jobs, coverage_include):
|
||||
coverage_files = []
|
||||
coverage_instance = Null()
|
||||
if coverage_output_dir or coverage_output_file:
|
||||
try:
|
||||
import coverage #@UnresolvedImport
|
||||
except:
|
||||
sys.stderr.write('Error: coverage module could not be imported\n')
|
||||
sys.stderr.write('Please make sure that the coverage module (http://nedbatchelder.com/code/coverage/)\n')
|
||||
sys.stderr.write('is properly installed in your interpreter: %s\n' % (sys.executable,))
|
||||
|
||||
import traceback;traceback.print_exc()
|
||||
else:
|
||||
if coverage_output_dir:
|
||||
if not os.path.exists(coverage_output_dir):
|
||||
sys.stderr.write('Error: directory for coverage output (%s) does not exist.\n' % (coverage_output_dir,))
|
||||
|
||||
elif not os.path.isdir(coverage_output_dir):
|
||||
sys.stderr.write('Error: expected (%s) to be a directory.\n' % (coverage_output_dir,))
|
||||
|
||||
else:
|
||||
n = jobs
|
||||
if n <= 0:
|
||||
n += 1
|
||||
n += 1 #Add 1 more for the current process (which will do the initial import).
|
||||
coverage_files = GetCoverageFiles(coverage_output_dir, n)
|
||||
os.environ['COVERAGE_FILE'] = coverage_files.pop(0)
|
||||
|
||||
coverage_instance = coverage.coverage(source=[coverage_include])
|
||||
coverage_instance.start()
|
||||
|
||||
elif coverage_output_file:
|
||||
#Client of parallel run.
|
||||
os.environ['COVERAGE_FILE'] = coverage_output_file
|
||||
coverage_instance = coverage.coverage(source=[coverage_include])
|
||||
coverage_instance.start()
|
||||
|
||||
return coverage_files, coverage_instance
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
from nose.plugins.multiprocess import MultiProcessTestRunner # @UnresolvedImport
|
||||
from nose.plugins.base import Plugin # @UnresolvedImport
|
||||
import sys
|
||||
import pydev_runfiles_xml_rpc
|
||||
import time
|
||||
from pydev_runfiles_coverage import StartCoverageSupport
|
||||
|
||||
#=======================================================================================================================
|
||||
# PydevPlugin
|
||||
#=======================================================================================================================
|
||||
class PydevPlugin(Plugin):
|
||||
|
||||
def __init__(self, configuration):
|
||||
self.configuration = configuration
|
||||
Plugin.__init__(self)
|
||||
|
||||
|
||||
def begin(self):
|
||||
# Called before any test is run (it's always called, with multiprocess or not)
|
||||
self.start_time = time.time()
|
||||
self.coverage_files, self.coverage = StartCoverageSupport(self.configuration)
|
||||
|
||||
|
||||
def finalize(self, result):
|
||||
# Called after all tests are run (it's always called, with multiprocess or not)
|
||||
self.coverage.stop()
|
||||
self.coverage.save()
|
||||
|
||||
pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - self.start_time,))
|
||||
|
||||
|
||||
|
||||
#===================================================================================================================
|
||||
# Methods below are not called with multiprocess (so, we monkey-patch MultiProcessTestRunner.consolidate
|
||||
# so that they're called, but unfortunately we loose some info -- i.e.: the time for each test in this
|
||||
# process).
|
||||
#===================================================================================================================
|
||||
|
||||
|
||||
def reportCond(self, cond, test, captured_output, error=''):
|
||||
'''
|
||||
@param cond: fail, error, ok
|
||||
'''
|
||||
|
||||
# test.address() is something as:
|
||||
# ('D:\\workspaces\\temp\\test_workspace\\pytesting1\\src\\mod1\\hello.py', 'mod1.hello', 'TestCase.testMet1')
|
||||
#
|
||||
# and we must pass: location, test
|
||||
# E.g.: ['D:\\src\\mod1\\hello.py', 'TestCase.testMet1']
|
||||
try:
|
||||
if hasattr(test, 'address'):
|
||||
address = test.address()
|
||||
address = address[0], address[2]
|
||||
else:
|
||||
# multiprocess
|
||||
try:
|
||||
address = test[0], test[1]
|
||||
except TypeError:
|
||||
# It may be an error at setup, in which case it's not really a test, but a Context object.
|
||||
f = test.context.__file__
|
||||
if f.endswith('.pyc'):
|
||||
f = f[:-1]
|
||||
address = f, '?'
|
||||
except:
|
||||
sys.stderr.write("PyDev: Internal pydev error getting test address. Please report at the pydev bug tracker\n")
|
||||
import traceback;traceback.print_exc()
|
||||
sys.stderr.write("\n\n\n")
|
||||
address = '?', '?'
|
||||
|
||||
error_contents = self.getIoFromError(error)
|
||||
try:
|
||||
time_str = '%.2f' % (time.time() - test._pydev_start_time)
|
||||
except:
|
||||
time_str = '?'
|
||||
|
||||
pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, address[0], address[1], time_str)
|
||||
|
||||
|
||||
def startTest(self, test):
|
||||
test._pydev_start_time = time.time()
|
||||
if hasattr(test, 'address'):
|
||||
address = test.address()
|
||||
file, test = address[0], address[2]
|
||||
else:
|
||||
# multiprocess
|
||||
file, test = test
|
||||
pydev_runfiles_xml_rpc.notifyStartTest(file, test)
|
||||
|
||||
|
||||
def getIoFromError(self, err):
|
||||
if type(err) == type(()):
|
||||
if len(err) != 3:
|
||||
if len(err) == 2:
|
||||
return err[1] # multiprocess
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except:
|
||||
from io import StringIO
|
||||
s = StringIO()
|
||||
etype, value, tb = err
|
||||
import traceback;traceback.print_exception(etype, value, tb, file=s)
|
||||
return s.getvalue()
|
||||
return err
|
||||
|
||||
|
||||
def getCapturedOutput(self, test):
|
||||
if hasattr(test, 'capturedOutput') and test.capturedOutput:
|
||||
return test.capturedOutput
|
||||
return ''
|
||||
|
||||
|
||||
def addError(self, test, err):
|
||||
self.reportCond(
|
||||
'error',
|
||||
test,
|
||||
self.getCapturedOutput(test),
|
||||
err,
|
||||
)
|
||||
|
||||
|
||||
def addFailure(self, test, err):
|
||||
self.reportCond(
|
||||
'fail',
|
||||
test,
|
||||
self.getCapturedOutput(test),
|
||||
err,
|
||||
)
|
||||
|
||||
|
||||
def addSuccess(self, test):
|
||||
self.reportCond(
|
||||
'ok',
|
||||
test,
|
||||
self.getCapturedOutput(test),
|
||||
'',
|
||||
)
|
||||
|
||||
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON = None
|
||||
def StartPydevNosePluginSingleton(configuration):
|
||||
global PYDEV_NOSE_PLUGIN_SINGLETON
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON = PydevPlugin(configuration)
|
||||
return PYDEV_NOSE_PLUGIN_SINGLETON
|
||||
|
||||
|
||||
|
||||
|
||||
original = MultiProcessTestRunner.consolidate
|
||||
#=======================================================================================================================
|
||||
# NewConsolidate
|
||||
#=======================================================================================================================
|
||||
def NewConsolidate(self, result, batch_result):
|
||||
'''
|
||||
Used so that it can work with the multiprocess plugin.
|
||||
Monkeypatched because nose seems a bit unsupported at this time (ideally
|
||||
the plugin would have this support by default).
|
||||
'''
|
||||
ret = original(self, result, batch_result)
|
||||
|
||||
parent_frame = sys._getframe().f_back
|
||||
# addr is something as D:\pytesting1\src\mod1\hello.py:TestCase.testMet4
|
||||
# so, convert it to what reportCond expects
|
||||
addr = parent_frame.f_locals['addr']
|
||||
i = addr.rindex(':')
|
||||
addr = [addr[:i], addr[i + 1:]]
|
||||
|
||||
output, testsRun, failures, errors, errorClasses = batch_result
|
||||
if failures or errors:
|
||||
for failure in failures:
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('fail', addr, output, failure)
|
||||
|
||||
for error in errors:
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('error', addr, output, error)
|
||||
else:
|
||||
PYDEV_NOSE_PLUGIN_SINGLETON.reportCond('ok', addr, output)
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
MultiProcessTestRunner.consolidate = NewConsolidate
|
||||
@@ -0,0 +1,298 @@
|
||||
import unittest
|
||||
try:
|
||||
import Queue
|
||||
except:
|
||||
import queue as Queue #@UnresolvedImport
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
import pydev_runfiles_xml_rpc
|
||||
import time
|
||||
import os
|
||||
|
||||
#=======================================================================================================================
|
||||
# FlattenTestSuite
|
||||
#=======================================================================================================================
|
||||
def FlattenTestSuite(test_suite, ret):
|
||||
if isinstance(test_suite, unittest.TestSuite):
|
||||
for t in test_suite._tests:
|
||||
FlattenTestSuite(t, ret)
|
||||
|
||||
elif isinstance(test_suite, unittest.TestCase):
|
||||
ret.append(test_suite)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ExecuteTestsInParallel
|
||||
#=======================================================================================================================
|
||||
def ExecuteTestsInParallel(tests, jobs, split, verbosity, coverage_files, coverage_include):
|
||||
'''
|
||||
@param tests: list(PydevTestSuite)
|
||||
A list with the suites to be run
|
||||
|
||||
@param split: str
|
||||
Either 'module' or the number of tests that should be run in each batch
|
||||
|
||||
@param coverage_files: list(file)
|
||||
A list with the files that should be used for giving coverage information (if empty, coverage information
|
||||
should not be gathered).
|
||||
|
||||
@param coverage_include: str
|
||||
The pattern that should be included in the coverage.
|
||||
|
||||
@return: bool
|
||||
Returns True if the tests were actually executed in parallel. If the tests were not executed because only 1
|
||||
should be used (e.g.: 2 jobs were requested for running 1 test), False will be returned and no tests will be
|
||||
run.
|
||||
|
||||
It may also return False if in debug mode (in which case, multi-processes are not accepted)
|
||||
'''
|
||||
try:
|
||||
from pydevd_comm import GetGlobalDebugger
|
||||
if GetGlobalDebugger() is not None:
|
||||
return False
|
||||
except:
|
||||
pass #Ignore any error here.
|
||||
|
||||
#This queue will receive the tests to be run. Each entry in a queue is a list with the tests to be run together When
|
||||
#split == 'tests', each list will have a single element, when split == 'module', each list will have all the tests
|
||||
#from a given module.
|
||||
tests_queue = []
|
||||
|
||||
queue_elements = []
|
||||
if split == 'module':
|
||||
module_to_tests = {}
|
||||
for test in tests:
|
||||
lst = []
|
||||
FlattenTestSuite(test, lst)
|
||||
for test in lst:
|
||||
key = (test.__pydev_pyfile__, test.__pydev_module_name__)
|
||||
module_to_tests.setdefault(key, []).append(test)
|
||||
|
||||
for key, tests in module_to_tests.items():
|
||||
queue_elements.append(tests)
|
||||
|
||||
if len(queue_elements) < jobs:
|
||||
#Don't create jobs we will never use.
|
||||
jobs = len(queue_elements)
|
||||
|
||||
elif split == 'tests':
|
||||
for test in tests:
|
||||
lst = []
|
||||
FlattenTestSuite(test, lst)
|
||||
for test in lst:
|
||||
queue_elements.append([test])
|
||||
|
||||
if len(queue_elements) < jobs:
|
||||
#Don't create jobs we will never use.
|
||||
jobs = len(queue_elements)
|
||||
|
||||
else:
|
||||
raise AssertionError('Do not know how to handle: %s' % (split,))
|
||||
|
||||
for test_cases in queue_elements:
|
||||
test_queue_elements = []
|
||||
for test_case in test_cases:
|
||||
try:
|
||||
test_name = test_case.__class__.__name__+"."+test_case._testMethodName
|
||||
except AttributeError:
|
||||
#Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
|
||||
test_name = test_case.__class__.__name__+"."+test_case._TestCase__testMethodName
|
||||
|
||||
test_queue_elements.append(test_case.__pydev_pyfile__+'|'+test_name)
|
||||
|
||||
tests_queue.append(test_queue_elements)
|
||||
|
||||
if jobs < 2:
|
||||
return False
|
||||
|
||||
sys.stdout.write('Running tests in parallel with: %s jobs.\n' %(jobs,))
|
||||
|
||||
|
||||
queue = Queue.Queue()
|
||||
for item in tests_queue:
|
||||
queue.put(item, block=False)
|
||||
|
||||
|
||||
providers = []
|
||||
clients = []
|
||||
for i in range(jobs):
|
||||
test_cases_provider = CommunicationThread(queue)
|
||||
providers.append(test_cases_provider)
|
||||
|
||||
test_cases_provider.start()
|
||||
port = test_cases_provider.port
|
||||
|
||||
if coverage_files:
|
||||
clients.append(ClientThread(i, port, verbosity, coverage_files.pop(0), coverage_include))
|
||||
else:
|
||||
clients.append(ClientThread(i, port, verbosity))
|
||||
|
||||
for client in clients:
|
||||
client.start()
|
||||
|
||||
client_alive = True
|
||||
while client_alive:
|
||||
client_alive = False
|
||||
for client in clients:
|
||||
#Wait for all the clients to exit.
|
||||
if not client.finished:
|
||||
client_alive = True
|
||||
time.sleep(.2)
|
||||
break
|
||||
|
||||
for provider in providers:
|
||||
provider.shutdown()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# CommunicationThread
|
||||
#=======================================================================================================================
|
||||
class CommunicationThread(threading.Thread):
|
||||
|
||||
def __init__(self, tests_queue):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.queue = tests_queue
|
||||
self.finished = False
|
||||
from pydev_imports import SimpleXMLRPCServer
|
||||
|
||||
|
||||
# This is a hack to patch slow socket.getfqdn calls that
|
||||
# BaseHTTPServer (and its subclasses) make.
|
||||
# See: http://bugs.python.org/issue6085
|
||||
# See: http://www.answermysearches.com/xmlrpc-server-slow-in-python-how-to-fix/2140/
|
||||
try:
|
||||
import BaseHTTPServer
|
||||
def _bare_address_string(self):
|
||||
host, port = self.client_address[:2]
|
||||
return '%s' % host
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.address_string = _bare_address_string
|
||||
|
||||
except:
|
||||
pass
|
||||
# End hack.
|
||||
|
||||
|
||||
# Create server
|
||||
|
||||
import pydev_localhost
|
||||
server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), 0), logRequests=False)
|
||||
server.register_function(self.GetTestsToRun)
|
||||
server.register_function(self.notifyStartTest)
|
||||
server.register_function(self.notifyTest)
|
||||
server.register_function(self.notifyCommands)
|
||||
self.port = server.socket.getsockname()[1]
|
||||
self.server = server
|
||||
|
||||
|
||||
def GetTestsToRun(self, job_id):
|
||||
'''
|
||||
@param job_id:
|
||||
|
||||
@return: list(str)
|
||||
Each entry is a string in the format: filename|Test.testName
|
||||
'''
|
||||
try:
|
||||
ret = self.queue.get(block=False)
|
||||
return ret
|
||||
except: #Any exception getting from the queue (empty or not) means we finished our work on providing the tests.
|
||||
self.finished = True
|
||||
return []
|
||||
|
||||
|
||||
def notifyCommands(self, job_id, commands):
|
||||
#Batch notification.
|
||||
for command in commands:
|
||||
getattr(self, command[0])(job_id, *command[1], **command[2])
|
||||
|
||||
return True
|
||||
|
||||
def notifyStartTest(self, job_id, *args, **kwargs):
|
||||
pydev_runfiles_xml_rpc.notifyStartTest(*args, **kwargs)
|
||||
return True
|
||||
|
||||
|
||||
def notifyTest(self, job_id, *args, **kwargs):
|
||||
pydev_runfiles_xml_rpc.notifyTest(*args, **kwargs)
|
||||
return True
|
||||
|
||||
def shutdown(self):
|
||||
if hasattr(self.server, 'shutdown'):
|
||||
self.server.shutdown()
|
||||
else:
|
||||
self._shutdown = True
|
||||
|
||||
def run(self):
|
||||
if hasattr(self.server, 'shutdown'):
|
||||
self.server.serve_forever()
|
||||
else:
|
||||
self._shutdown = False
|
||||
while not self._shutdown:
|
||||
self.server.handle_request()
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# Client
|
||||
#=======================================================================================================================
|
||||
class ClientThread(threading.Thread):
|
||||
|
||||
def __init__(self, job_id, port, verbosity, coverage_output_file=None, coverage_include=None):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.port = port
|
||||
self.job_id = job_id
|
||||
self.verbosity = verbosity
|
||||
self.finished = False
|
||||
self.coverage_output_file = coverage_output_file
|
||||
self.coverage_include = coverage_include
|
||||
|
||||
|
||||
def _reader_thread(self, pipe, target):
|
||||
while True:
|
||||
target.write(pipe.read(1))
|
||||
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
import pydev_runfiles_parallel_client
|
||||
#TODO: Support Jython:
|
||||
#
|
||||
#For jython, instead of using sys.executable, we should use:
|
||||
#r'D:\bin\jdk_1_5_09\bin\java.exe',
|
||||
#'-classpath',
|
||||
#'D:/bin/jython-2.2.1/jython.jar',
|
||||
#'org.python.util.jython',
|
||||
|
||||
args = [
|
||||
sys.executable,
|
||||
pydev_runfiles_parallel_client.__file__,
|
||||
str(self.job_id),
|
||||
str(self.port),
|
||||
str(self.verbosity),
|
||||
]
|
||||
|
||||
if self.coverage_output_file and self.coverage_include:
|
||||
args.append(self.coverage_output_file)
|
||||
args.append(self.coverage_include)
|
||||
|
||||
import subprocess
|
||||
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()
|
||||
|
||||
stderr_thread = threading.Thread(target=self._reader_thread,args=(proc.stderr, sys.stderr))
|
||||
stderr_thread.setDaemon(True)
|
||||
stderr_thread.start()
|
||||
else:
|
||||
proc = subprocess.Popen(args, env=os.environ, shell=False)
|
||||
proc.wait()
|
||||
|
||||
finally:
|
||||
self.finished = True
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
from pydev_imports import xmlrpclib, _queue
|
||||
Queue = _queue.Queue
|
||||
import traceback
|
||||
from pydev_runfiles_coverage import StartCoverageSupportFromParams
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ParallelNotification
|
||||
#=======================================================================================================================
|
||||
class ParallelNotification(object):
|
||||
|
||||
def __init__(self, method, args, kwargs):
|
||||
self.method = method
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def ToTuple(self):
|
||||
return self.method, self.args, self.kwargs
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# KillServer
|
||||
#=======================================================================================================================
|
||||
class KillServer(object):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ServerComm
|
||||
#=======================================================================================================================
|
||||
class ServerComm(threading.Thread):
|
||||
|
||||
|
||||
|
||||
def __init__(self, job_id, server):
|
||||
self.notifications_queue = Queue()
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(False) #Wait for all the notifications to be passed before exiting!
|
||||
assert job_id is not None
|
||||
assert port is not None
|
||||
self.job_id = job_id
|
||||
|
||||
self.finished = False
|
||||
self.server = server
|
||||
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
kill_found = False
|
||||
commands = []
|
||||
command = self.notifications_queue.get(block=True)
|
||||
if isinstance(command, KillServer):
|
||||
kill_found = True
|
||||
else:
|
||||
assert isinstance(command, ParallelNotification)
|
||||
commands.append(command.ToTuple())
|
||||
|
||||
try:
|
||||
while True:
|
||||
command = self.notifications_queue.get(block=False) #No block to create a batch.
|
||||
if isinstance(command, KillServer):
|
||||
kill_found = True
|
||||
else:
|
||||
assert isinstance(command, ParallelNotification)
|
||||
commands.append(command.ToTuple())
|
||||
except:
|
||||
pass #That's OK, we're getting it until it becomes empty so that we notify multiple at once.
|
||||
|
||||
|
||||
if commands:
|
||||
try:
|
||||
#Batch notification.
|
||||
self.server.lock.acquire()
|
||||
try:
|
||||
self.server.notifyCommands(self.job_id, commands)
|
||||
finally:
|
||||
self.server.lock.release()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if kill_found:
|
||||
self.finished = True
|
||||
return
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ServerFacade
|
||||
#=======================================================================================================================
|
||||
class ServerFacade(object):
|
||||
|
||||
|
||||
def __init__(self, notifications_queue):
|
||||
self.notifications_queue = notifications_queue
|
||||
|
||||
|
||||
def notifyTestsCollected(self, *args, **kwargs):
|
||||
pass #This notification won't be passed
|
||||
|
||||
|
||||
def notifyTestRunFinished(self, *args, **kwargs):
|
||||
pass #This notification won't be passed
|
||||
|
||||
|
||||
def notifyStartTest(self, *args, **kwargs):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyStartTest', args, kwargs))
|
||||
|
||||
|
||||
def notifyTest(self, *args, **kwargs):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyTest', args, kwargs))
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# run_client
|
||||
#=======================================================================================================================
|
||||
def run_client(job_id, port, verbosity, coverage_output_file, coverage_include):
|
||||
job_id = int(job_id)
|
||||
|
||||
import pydev_localhost
|
||||
server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), port))
|
||||
server.lock = threading.Lock()
|
||||
|
||||
|
||||
server_comm = ServerComm(job_id, server)
|
||||
server_comm.start()
|
||||
|
||||
try:
|
||||
server_facade = ServerFacade(server_comm.notifications_queue)
|
||||
import pydev_runfiles
|
||||
import pydev_runfiles_xml_rpc
|
||||
pydev_runfiles_xml_rpc.SetServer(server_facade)
|
||||
|
||||
#Starts None and when the 1st test is gotten, it's started (because a server may be initiated and terminated
|
||||
#before receiving any test -- which would mean a different process got all the tests to run).
|
||||
coverage = None
|
||||
|
||||
try:
|
||||
tests_to_run = [1]
|
||||
while tests_to_run:
|
||||
#Investigate: is it dangerous to use the same xmlrpclib server from different threads?
|
||||
#It seems it should be, as it creates a new connection for each request...
|
||||
server.lock.acquire()
|
||||
try:
|
||||
tests_to_run = server.GetTestsToRun(job_id)
|
||||
finally:
|
||||
server.lock.release()
|
||||
|
||||
if not tests_to_run:
|
||||
break
|
||||
|
||||
if coverage is None:
|
||||
_coverage_files, coverage = StartCoverageSupportFromParams(
|
||||
None, coverage_output_file, 1, coverage_include)
|
||||
|
||||
|
||||
files_to_tests = {}
|
||||
for test in tests_to_run:
|
||||
filename_and_test = test.split('|')
|
||||
if len(filename_and_test) == 2:
|
||||
files_to_tests.setdefault(filename_and_test[0], []).append(filename_and_test[1])
|
||||
|
||||
configuration = pydev_runfiles.Configuration(
|
||||
'',
|
||||
verbosity,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
files_to_tests,
|
||||
1, #Always single job here
|
||||
None,
|
||||
|
||||
#The coverage is handled in this loop.
|
||||
coverage_output_file=None,
|
||||
coverage_include=None,
|
||||
)
|
||||
test_runner = pydev_runfiles.PydevTestRunner(configuration)
|
||||
sys.stdout.flush()
|
||||
test_runner.run_tests(handle_coverage=False)
|
||||
finally:
|
||||
if coverage is not None:
|
||||
coverage.stop()
|
||||
coverage.save()
|
||||
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
server_comm.notifications_queue.put_nowait(KillServer())
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) -1 == 3:
|
||||
job_id, port, verbosity = sys.argv[1:]
|
||||
coverage_output_file, coverage_include = None, None
|
||||
|
||||
elif len(sys.argv) -1 == 5:
|
||||
job_id, port, verbosity, coverage_output_file, coverage_include = sys.argv[1:]
|
||||
|
||||
else:
|
||||
raise AssertionError('Could not find out how to handle the parameters: '+sys.argv[1:])
|
||||
|
||||
job_id = int(job_id)
|
||||
port = int(port)
|
||||
verbosity = int(verbosity)
|
||||
run_client(job_id, port, verbosity, coverage_output_file, coverage_include)
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
import pickle
|
||||
import zlib
|
||||
import base64
|
||||
import os
|
||||
import py
|
||||
from py._code import code # @UnresolvedImport
|
||||
import pydev_runfiles_xml_rpc
|
||||
from pydevd_file_utils import _NormFile
|
||||
import pytest
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
|
||||
#===================================================================================================
|
||||
# Load filters with tests we should skip
|
||||
#===================================================================================================
|
||||
py_test_accept_filter = None
|
||||
|
||||
def _load_filters():
|
||||
global py_test_accept_filter
|
||||
if py_test_accept_filter is None:
|
||||
py_test_accept_filter = os.environ.get('PYDEV_PYTEST_SKIP')
|
||||
if py_test_accept_filter:
|
||||
py_test_accept_filter = pickle.loads(zlib.decompress(base64.b64decode(py_test_accept_filter)))
|
||||
else:
|
||||
py_test_accept_filter = {}
|
||||
|
||||
|
||||
def connect_to_server_for_communication_to_xml_rpc_on_xdist():
|
||||
main_pid = os.environ.get('PYDEV_MAIN_PID')
|
||||
if main_pid and main_pid != str(os.getpid()):
|
||||
port = os.environ.get('PYDEV_PYTEST_SERVER')
|
||||
if not port:
|
||||
sys.stderr.write('Error: no PYDEV_PYTEST_SERVER environment variable defined.\n')
|
||||
else:
|
||||
pydev_runfiles_xml_rpc.InitializeServer(int(port), daemon=True)
|
||||
|
||||
|
||||
#===================================================================================================
|
||||
# Mocking to get clickable file representations
|
||||
#===================================================================================================
|
||||
def _MockFileRepresentation():
|
||||
code.ReprFileLocation._original_toterminal = code.ReprFileLocation.toterminal
|
||||
|
||||
def toterminal(self, tw):
|
||||
# filename and lineno output for each entry,
|
||||
# using an output format that most editors understand
|
||||
msg = self.message
|
||||
i = msg.find("\n")
|
||||
if i != -1:
|
||||
msg = msg[:i]
|
||||
|
||||
tw.line('File "%s", line %s\n%s' %(os.path.abspath(self.path), self.lineno, msg))
|
||||
|
||||
code.ReprFileLocation.toterminal = toterminal
|
||||
|
||||
|
||||
def _UninstallMockFileRepresentation():
|
||||
code.ReprFileLocation.toterminal = code.ReprFileLocation._original_toterminal #@UndefinedVariable
|
||||
|
||||
|
||||
class State:
|
||||
numcollected = 0
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
def pytest_configure(*args, **kwargs):
|
||||
_MockFileRepresentation()
|
||||
|
||||
|
||||
def pytest_collectreport(report):
|
||||
|
||||
i = 0
|
||||
for x in report.result:
|
||||
if isinstance(x, pytest.Item):
|
||||
try:
|
||||
# Call our setup (which may do a skip, in which
|
||||
# case we won't count it).
|
||||
pytest_runtest_setup(x)
|
||||
i += 1
|
||||
except:
|
||||
continue
|
||||
State.numcollected += i
|
||||
|
||||
|
||||
def pytest_collection_modifyitems():
|
||||
connect_to_server_for_communication_to_xml_rpc_on_xdist()
|
||||
pydev_runfiles_xml_rpc.notifyTestsCollected(State.numcollected)
|
||||
State.numcollected = 0
|
||||
|
||||
|
||||
def pytest_unconfigure(*args, **kwargs):
|
||||
_UninstallMockFileRepresentation()
|
||||
pydev_runfiles_xml_rpc.notifyTestRunFinished('Finished in: %.2f secs.' % (time.time() - State.start_time,))
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
filename = item.fspath.strpath
|
||||
test = item.location[2]
|
||||
State.start_test_time = time.time()
|
||||
|
||||
pydev_runfiles_xml_rpc.notifyStartTest(filename, test)
|
||||
|
||||
|
||||
def report_test(cond, filename, test, captured_output, error_contents, delta):
|
||||
'''
|
||||
@param filename: 'D:\\src\\mod1\\hello.py'
|
||||
@param test: 'TestCase.testMet1'
|
||||
@param cond: fail, error, ok
|
||||
'''
|
||||
time_str = '%.2f' % (delta,)
|
||||
pydev_runfiles_xml_rpc.notifyTest(cond, captured_output, error_contents, filename, test, time_str)
|
||||
|
||||
|
||||
def pytest_runtest_makereport(item, call):
|
||||
report_when = call.when
|
||||
report_duration = call.stop-call.start
|
||||
excinfo = call.excinfo
|
||||
|
||||
if not call.excinfo:
|
||||
report_outcome = "passed"
|
||||
report_longrepr = None
|
||||
else:
|
||||
excinfo = call.excinfo
|
||||
if not isinstance(excinfo, py.code.ExceptionInfo):
|
||||
report_outcome = "failed"
|
||||
report_longrepr = excinfo
|
||||
|
||||
elif excinfo.errisinstance(py.test.skip.Exception):
|
||||
report_outcome = "skipped"
|
||||
r = excinfo._getreprcrash()
|
||||
report_longrepr = None #(str(r.path), r.lineno, r.message)
|
||||
|
||||
else:
|
||||
report_outcome = "failed"
|
||||
if call.when == "call":
|
||||
report_longrepr = item.repr_failure(excinfo)
|
||||
|
||||
else: # exception in setup or teardown
|
||||
report_longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle)
|
||||
|
||||
filename = item.fspath.strpath
|
||||
test = item.location[2]
|
||||
|
||||
status = 'ok'
|
||||
captured_output = ''
|
||||
error_contents = ''
|
||||
|
||||
if report_outcome in ('passed', 'skipped'):
|
||||
#passed or skipped: no need to report if in setup or teardown (only on the actual test if it passed).
|
||||
if report_when in ('setup', 'teardown'):
|
||||
return
|
||||
|
||||
else:
|
||||
#It has only passed, skipped and failed (no error), so, let's consider error if not on call.
|
||||
if report_when == 'setup':
|
||||
if status == 'ok':
|
||||
status = 'error'
|
||||
|
||||
elif report_when == 'teardown':
|
||||
if status == 'ok':
|
||||
status = 'error'
|
||||
|
||||
else:
|
||||
#any error in the call (not in setup or teardown) is considered a regular failure.
|
||||
status = 'fail'
|
||||
|
||||
|
||||
if call.excinfo:
|
||||
rep = report_longrepr
|
||||
if hasattr(rep, 'reprcrash'):
|
||||
reprcrash = rep.reprcrash
|
||||
error_contents += str(reprcrash)
|
||||
error_contents += '\n'
|
||||
|
||||
if hasattr(rep, 'reprtraceback'):
|
||||
error_contents += str(rep.reprtraceback)
|
||||
|
||||
if hasattr(rep, 'sections'):
|
||||
for name, content, sep in rep.sections:
|
||||
error_contents += sep * 40
|
||||
error_contents += name
|
||||
error_contents += sep * 40
|
||||
error_contents += '\n'
|
||||
error_contents += content
|
||||
error_contents += '\n'
|
||||
|
||||
if status != 'skip': #I.e.: don't event report skips...
|
||||
report_test(status, filename, test, captured_output, error_contents, report_duration)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_runtest_setup(item):
|
||||
'''
|
||||
Skips tests. With xdist will be on a secondary process.
|
||||
'''
|
||||
_load_filters()
|
||||
if not py_test_accept_filter:
|
||||
return #Keep on going (nothing to filter)
|
||||
|
||||
f = _NormFile(str(item.parent.fspath))
|
||||
name = item.name
|
||||
|
||||
if f not in py_test_accept_filter:
|
||||
pytest.skip() # Skip the file
|
||||
|
||||
accept_tests = py_test_accept_filter[f]
|
||||
|
||||
if item.cls is not None:
|
||||
class_name = item.cls.__name__
|
||||
else:
|
||||
class_name = None
|
||||
for test in accept_tests:
|
||||
if test == name:
|
||||
#Direct match of the test (just go on with the default loading)
|
||||
return
|
||||
|
||||
if class_name is not None:
|
||||
if test == class_name + '.' + name:
|
||||
return
|
||||
|
||||
if class_name == test:
|
||||
return
|
||||
|
||||
# If we had a match it'd have returned already.
|
||||
pytest.skip() # Skip the test
|
||||
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
try:
|
||||
import unittest2 as python_unittest
|
||||
except:
|
||||
import unittest as python_unittest
|
||||
|
||||
import pydev_runfiles_xml_rpc
|
||||
import time
|
||||
import pydevd_io
|
||||
import traceback
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PydevTextTestRunner
|
||||
#=======================================================================================================================
|
||||
class PydevTextTestRunner(python_unittest.TextTestRunner):
|
||||
|
||||
def _makeResult(self):
|
||||
return PydevTestResult(self.stream, self.descriptions, self.verbosity)
|
||||
|
||||
|
||||
_PythonTextTestResult = python_unittest.TextTestRunner()._makeResult().__class__
|
||||
|
||||
#=======================================================================================================================
|
||||
# PydevTestResult
|
||||
#=======================================================================================================================
|
||||
class PydevTestResult(_PythonTextTestResult):
|
||||
|
||||
|
||||
def startTest(self, test):
|
||||
_PythonTextTestResult.startTest(self, test)
|
||||
self.buf = pydevd_io.StartRedirect(keep_original_redirection=True, std='both')
|
||||
self.start_time = time.time()
|
||||
self._current_errors_stack = []
|
||||
self._current_failures_stack = []
|
||||
|
||||
try:
|
||||
test_name = test.__class__.__name__+"."+test._testMethodName
|
||||
except AttributeError:
|
||||
#Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
|
||||
test_name = test.__class__.__name__+"."+test._TestCase__testMethodName
|
||||
|
||||
pydev_runfiles_xml_rpc.notifyStartTest(
|
||||
test.__pydev_pyfile__, test_name)
|
||||
|
||||
|
||||
|
||||
|
||||
def getTestName(self, test):
|
||||
try:
|
||||
try:
|
||||
test_name = test.__class__.__name__ + "." + test._testMethodName
|
||||
except AttributeError:
|
||||
#Support for jython 2.1 (__testMethodName is pseudo-private in the test case)
|
||||
try:
|
||||
test_name = test.__class__.__name__ + "." + test._TestCase__testMethodName
|
||||
#Support for class/module exceptions (test is instance of _ErrorHolder)
|
||||
except:
|
||||
test_name = test.description.split()[1][1:-1] + ' <' + test.description.split()[0] + '>'
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return '<unable to get test name>'
|
||||
return test_name
|
||||
|
||||
|
||||
def stopTest(self, test):
|
||||
end_time = time.time()
|
||||
pydevd_io.EndRedirect(std='both')
|
||||
|
||||
_PythonTextTestResult.stopTest(self, test)
|
||||
|
||||
captured_output = self.buf.getvalue()
|
||||
del self.buf
|
||||
error_contents = ''
|
||||
test_name = self.getTestName(test)
|
||||
|
||||
|
||||
diff_time = '%.2f' % (end_time - self.start_time)
|
||||
if not self._current_errors_stack and not self._current_failures_stack:
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'ok', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
|
||||
else:
|
||||
self._reportErrors(self._current_errors_stack, self._current_failures_stack, captured_output, test_name)
|
||||
|
||||
|
||||
def _reportErrors(self, errors, failures, captured_output, test_name, diff_time=''):
|
||||
error_contents = []
|
||||
for test, s in errors+failures:
|
||||
if type(s) == type((1,)): #If it's a tuple (for jython 2.1)
|
||||
sio = StringIO()
|
||||
traceback.print_exception(s[0], s[1], s[2], file=sio)
|
||||
s = sio.getvalue()
|
||||
error_contents.append(s)
|
||||
|
||||
sep = '\n'+self.separator1
|
||||
error_contents = sep.join(error_contents)
|
||||
|
||||
if errors and not failures:
|
||||
try:
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'error', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
|
||||
except:
|
||||
file_start = error_contents.find('File "')
|
||||
file_end = error_contents.find('", ', file_start)
|
||||
if file_start != -1 and file_end != -1:
|
||||
file = error_contents[file_start+6:file_end]
|
||||
else:
|
||||
file = '<unable to get file>'
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'error', captured_output, error_contents, file, test_name, diff_time)
|
||||
|
||||
elif failures and not errors:
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'fail', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
|
||||
|
||||
else: #Ok, we got both, errors and failures. Let's mark it as an error in the end.
|
||||
pydev_runfiles_xml_rpc.notifyTest(
|
||||
'error', captured_output, error_contents, test.__pydev_pyfile__, test_name, diff_time)
|
||||
|
||||
|
||||
|
||||
def addError(self, test, err):
|
||||
_PythonTextTestResult.addError(self, test, err)
|
||||
#Support for class/module exceptions (test is instance of _ErrorHolder)
|
||||
if not hasattr(self, '_current_errors_stack') or test.__class__.__name__ == '_ErrorHolder':
|
||||
#Not in start...end, so, report error now (i.e.: django pre/post-setup)
|
||||
self._reportErrors([self.errors[-1]], [], '', self.getTestName(test))
|
||||
else:
|
||||
self._current_errors_stack.append(self.errors[-1])
|
||||
|
||||
|
||||
def addFailure(self, test, err):
|
||||
_PythonTextTestResult.addFailure(self, test, err)
|
||||
if not hasattr(self, '_current_failures_stack'):
|
||||
#Not in start...end, so, report error now (i.e.: django pre/post-setup)
|
||||
self._reportErrors([], [self.failures[-1]], '', self.getTestName(test))
|
||||
else:
|
||||
self._current_failures_stack.append(self.failures[-1])
|
||||
|
||||
|
||||
try:
|
||||
#Version 2.7 onwards has a different structure... Let's not make any changes in it for now
|
||||
#(waiting for bug: http://bugs.python.org/issue11798)
|
||||
try:
|
||||
from unittest2 import suite
|
||||
except ImportError:
|
||||
from unittest import suite
|
||||
#===================================================================================================================
|
||||
# PydevTestSuite
|
||||
#===================================================================================================================
|
||||
class PydevTestSuite(python_unittest.TestSuite):
|
||||
pass
|
||||
|
||||
|
||||
except ImportError:
|
||||
|
||||
#===================================================================================================================
|
||||
# PydevTestSuite
|
||||
#===================================================================================================================
|
||||
class PydevTestSuite(python_unittest.TestSuite):
|
||||
|
||||
|
||||
def run(self, result):
|
||||
for index, test in enumerate(self._tests):
|
||||
if result.shouldStop:
|
||||
break
|
||||
test(result)
|
||||
|
||||
# Let the memory be released!
|
||||
self._tests[index] = None
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import traceback
|
||||
import warnings
|
||||
|
||||
from _pydev_filesystem_encoding import getfilesystemencoding
|
||||
from pydev_imports import xmlrpclib, _queue
|
||||
Queue = _queue.Queue
|
||||
from pydevd_constants import *
|
||||
|
||||
#This may happen in IronPython (in Python it shouldn't happen as there are
|
||||
#'fast' replacements that are used in xmlrpclib.py)
|
||||
warnings.filterwarnings(
|
||||
'ignore', 'The xmllib module is obsolete.*', DeprecationWarning)
|
||||
|
||||
|
||||
file_system_encoding = getfilesystemencoding()
|
||||
|
||||
#=======================================================================================================================
|
||||
# _ServerHolder
|
||||
#=======================================================================================================================
|
||||
class _ServerHolder:
|
||||
'''
|
||||
Helper so that we don't have to use a global here.
|
||||
'''
|
||||
SERVER = None
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# SetServer
|
||||
#=======================================================================================================================
|
||||
def SetServer(server):
|
||||
_ServerHolder.SERVER = server
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ParallelNotification
|
||||
#=======================================================================================================================
|
||||
class ParallelNotification(object):
|
||||
|
||||
def __init__(self, method, args):
|
||||
self.method = method
|
||||
self.args = args
|
||||
|
||||
def ToTuple(self):
|
||||
return self.method, self.args
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# KillServer
|
||||
#=======================================================================================================================
|
||||
class KillServer(object):
|
||||
pass
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ServerFacade
|
||||
#=======================================================================================================================
|
||||
class ServerFacade(object):
|
||||
|
||||
|
||||
def __init__(self, notifications_queue):
|
||||
self.notifications_queue = notifications_queue
|
||||
|
||||
|
||||
def notifyTestsCollected(self, *args):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyTestsCollected', args))
|
||||
|
||||
def notifyConnected(self, *args):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyConnected', args))
|
||||
|
||||
|
||||
def notifyTestRunFinished(self, *args):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyTestRunFinished', args))
|
||||
|
||||
|
||||
def notifyStartTest(self, *args):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyStartTest', args))
|
||||
|
||||
|
||||
def notifyTest(self, *args):
|
||||
self.notifications_queue.put_nowait(ParallelNotification('notifyTest', args))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ServerComm
|
||||
#=======================================================================================================================
|
||||
class ServerComm(threading.Thread):
|
||||
|
||||
|
||||
|
||||
def __init__(self, notifications_queue, port, daemon=False):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(daemon) # If False, wait for all the notifications to be passed before exiting!
|
||||
self.finished = False
|
||||
self.notifications_queue = notifications_queue
|
||||
|
||||
import pydev_localhost
|
||||
|
||||
# It is necessary to specify an encoding, that matches
|
||||
# the encoding of all bytes-strings passed into an
|
||||
# XMLRPC call: "All 8-bit strings in the data structure are assumed to use the
|
||||
# packet encoding. Unicode strings are automatically converted,
|
||||
# where necessary."
|
||||
# Byte strings most likely come from file names.
|
||||
encoding = file_system_encoding
|
||||
if encoding == "mbcs":
|
||||
# Windos symbolic name for the system encoding CP_ACP.
|
||||
# We need to convert it into a encoding that is recognized by Java.
|
||||
# Unfortunately this is not always possible. You could use
|
||||
# GetCPInfoEx and get a name similar to "windows-1251". Then
|
||||
# you need a table to translate on a best effort basis. Much to complicated.
|
||||
# ISO-8859-1 is good enough.
|
||||
encoding = "ISO-8859-1"
|
||||
|
||||
self.server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), port),
|
||||
encoding=encoding)
|
||||
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
kill_found = False
|
||||
commands = []
|
||||
command = self.notifications_queue.get(block=True)
|
||||
if isinstance(command, KillServer):
|
||||
kill_found = True
|
||||
else:
|
||||
assert isinstance(command, ParallelNotification)
|
||||
commands.append(command.ToTuple())
|
||||
|
||||
try:
|
||||
while True:
|
||||
command = self.notifications_queue.get(block=False) #No block to create a batch.
|
||||
if isinstance(command, KillServer):
|
||||
kill_found = True
|
||||
else:
|
||||
assert isinstance(command, ParallelNotification)
|
||||
commands.append(command.ToTuple())
|
||||
except:
|
||||
pass #That's OK, we're getting it until it becomes empty so that we notify multiple at once.
|
||||
|
||||
|
||||
if commands:
|
||||
try:
|
||||
self.server.notifyCommands(commands)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if kill_found:
|
||||
self.finished = True
|
||||
return
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InitializeServer
|
||||
#=======================================================================================================================
|
||||
def InitializeServer(port, daemon=False):
|
||||
if _ServerHolder.SERVER is None:
|
||||
if port is not None:
|
||||
notifications_queue = Queue()
|
||||
_ServerHolder.SERVER = ServerFacade(notifications_queue)
|
||||
_ServerHolder.SERVER_COMM = ServerComm(notifications_queue, port, daemon)
|
||||
_ServerHolder.SERVER_COMM.start()
|
||||
else:
|
||||
#Create a null server, so that we keep the interface even without any connection.
|
||||
_ServerHolder.SERVER = Null()
|
||||
_ServerHolder.SERVER_COMM = Null()
|
||||
|
||||
try:
|
||||
_ServerHolder.SERVER.notifyConnected()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# notifyTest
|
||||
#=======================================================================================================================
|
||||
def notifyTestsCollected(tests_count):
|
||||
assert tests_count is not None
|
||||
try:
|
||||
_ServerHolder.SERVER.notifyTestsCollected(tests_count)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# notifyStartTest
|
||||
#=======================================================================================================================
|
||||
def notifyStartTest(file, test):
|
||||
'''
|
||||
@param file: the tests file (c:/temp/test.py)
|
||||
@param test: the test ran (i.e.: TestCase.test1)
|
||||
'''
|
||||
assert file is not None
|
||||
if test is None:
|
||||
test = '' #Could happen if we have an import error importing module.
|
||||
|
||||
try:
|
||||
_ServerHolder.SERVER.notifyStartTest(file, test)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def _encode_if_needed(obj):
|
||||
if not IS_PY3K:
|
||||
if isinstance(obj, str):
|
||||
try:
|
||||
return xmlrpclib.Binary(obj.encode('ISO-8859-1', 'xmlcharrefreplace'))
|
||||
except:
|
||||
return xmlrpclib.Binary(obj)
|
||||
|
||||
elif isinstance(obj, unicode):
|
||||
return xmlrpclib.Binary(obj.encode('ISO-8859-1', 'xmlcharrefreplace'))
|
||||
|
||||
else:
|
||||
if isinstance(obj, str):
|
||||
return obj.encode('ISO-8859-1', 'xmlcharrefreplace')
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# notifyTest
|
||||
#=======================================================================================================================
|
||||
def notifyTest(cond, captured_output, error_contents, file, test, time):
|
||||
'''
|
||||
@param cond: ok, fail, error
|
||||
@param captured_output: output captured from stdout
|
||||
@param captured_output: output captured from stderr
|
||||
@param file: the tests file (c:/temp/test.py)
|
||||
@param test: the test ran (i.e.: TestCase.test1)
|
||||
@param time: float with the number of seconds elapsed
|
||||
'''
|
||||
assert cond is not None
|
||||
assert captured_output is not None
|
||||
assert error_contents is not None
|
||||
assert file is not None
|
||||
if test is None:
|
||||
test = '' #Could happen if we have an import error importing module.
|
||||
assert time is not None
|
||||
try:
|
||||
captured_output = _encode_if_needed(captured_output)
|
||||
error_contents = _encode_if_needed(error_contents)
|
||||
|
||||
_ServerHolder.SERVER.notifyTest(cond, captured_output, error_contents, file, test, time)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
#=======================================================================================================================
|
||||
# notifyTestRunFinished
|
||||
#=======================================================================================================================
|
||||
def notifyTestRunFinished(total_time):
|
||||
assert total_time is not None
|
||||
try:
|
||||
_ServerHolder.SERVER.notifyTestRunFinished(total_time)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# forceServerKill
|
||||
#=======================================================================================================================
|
||||
def forceServerKill():
|
||||
_ServerHolder.SERVER_COMM.notifications_queue.put_nowait(KillServer())
|
||||
@@ -0,0 +1 @@
|
||||
(no __init__.py file)
|
||||
@@ -0,0 +1,192 @@
|
||||
'''
|
||||
This module will:
|
||||
- change the input() and raw_input() commands to change \r\n or \r into \n
|
||||
- execute the user site customize -- if available
|
||||
- change raw_input() and input() to also remove any trailing \r
|
||||
|
||||
Up to PyDev 3.4 it also was setting the default encoding, but it was removed because of differences when
|
||||
running from a shell (i.e.: now we just set the PYTHONIOENCODING related to that -- which is properly
|
||||
treated on Py 2.7 onwards).
|
||||
'''
|
||||
DEBUG = 0 #0 or 1 because of jython
|
||||
|
||||
import sys
|
||||
encoding = None
|
||||
|
||||
IS_PYTHON_3K = 0
|
||||
|
||||
try:
|
||||
if sys.version_info[0] == 3:
|
||||
IS_PYTHON_3K = 1
|
||||
|
||||
except:
|
||||
#That's OK, not all versions of python have sys.version_info
|
||||
if DEBUG:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
#Line buffering
|
||||
if IS_PYTHON_3K:
|
||||
#Python 3 has a bug (http://bugs.python.org/issue4705) in which -u doesn't properly make output/input unbuffered
|
||||
#so, we need to enable that ourselves here.
|
||||
try:
|
||||
sys.stdout._line_buffering = True
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
sys.stderr._line_buffering = True
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
sys.stdin._line_buffering = True
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
|
||||
def DictContains(d, key):
|
||||
return d.has_key(key)
|
||||
except:
|
||||
try:
|
||||
#Py3k does not have has_key anymore, and older versions don't have __contains__
|
||||
DictContains = dict.__contains__
|
||||
except:
|
||||
try:
|
||||
DictContains = dict.has_key
|
||||
except NameError:
|
||||
def DictContains(d, key):
|
||||
return d.has_key(key)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
#now that we've finished the needed pydev sitecustomize, let's run the default one (if available)
|
||||
|
||||
#Ok, some weirdness going on in Python 3k: when removing this module from the sys.module to import the 'real'
|
||||
#sitecustomize, all the variables in this scope become None (as if it was garbage-collected), so, the the reference
|
||||
#below is now being kept to create a cyclic reference so that it neven dies)
|
||||
__pydev_sitecustomize_module__ = sys.modules.get('sitecustomize') #A ref to this module
|
||||
|
||||
|
||||
#remove the pydev site customize (and the pythonpath for it)
|
||||
paths_removed = []
|
||||
try:
|
||||
for c in sys.path[:]:
|
||||
#Pydev controls the whole classpath in Jython already, so, we don't want a a duplicate for
|
||||
#what we've already added there (this is needed to support Jython 2.5b1 onwards -- otherwise, as
|
||||
#we added the sitecustomize to the pythonpath and to the classpath, we'd have to remove it from the
|
||||
#classpath too -- and I don't think there's a way to do that... or not?)
|
||||
if c.find('pydev_sitecustomize') != -1 or c == '__classpath__' or c == '__pyclasspath__' or \
|
||||
c == '__classpath__/' or c == '__pyclasspath__/' or c == '__classpath__\\' or c == '__pyclasspath__\\':
|
||||
sys.path.remove(c)
|
||||
if c.find('pydev_sitecustomize') == -1:
|
||||
#We'll re-add any paths removed but the pydev_sitecustomize we added from pydev.
|
||||
paths_removed.append(c)
|
||||
|
||||
if DictContains(sys.modules, 'sitecustomize'):
|
||||
del sys.modules['sitecustomize'] #this module
|
||||
except:
|
||||
#print the error... should never happen (so, always show, and not only on debug)!
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
else:
|
||||
#Now, execute the default sitecustomize
|
||||
try:
|
||||
import sitecustomize #@UnusedImport
|
||||
sitecustomize.__pydev_sitecustomize_module__ = __pydev_sitecustomize_module__
|
||||
except:
|
||||
pass
|
||||
|
||||
if not DictContains(sys.modules, 'sitecustomize'):
|
||||
#If there was no sitecustomize, re-add the pydev sitecustomize (pypy gives a KeyError if it's not there)
|
||||
sys.modules['sitecustomize'] = __pydev_sitecustomize_module__
|
||||
|
||||
try:
|
||||
if paths_removed:
|
||||
if sys is None:
|
||||
import sys
|
||||
if sys is not None:
|
||||
#And after executing the default sitecustomize, restore the paths (if we didn't remove it before,
|
||||
#the import sitecustomize would recurse).
|
||||
sys.path.extend(paths_removed)
|
||||
except:
|
||||
#print the error... should never happen (so, always show, and not only on debug)!
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
|
||||
|
||||
|
||||
|
||||
if not IS_PYTHON_3K:
|
||||
try:
|
||||
#Redefine input and raw_input only after the original sitecustomize was executed
|
||||
#(because otherwise, the original raw_input and input would still not be defined)
|
||||
import __builtin__
|
||||
original_raw_input = __builtin__.raw_input
|
||||
original_input = __builtin__.input
|
||||
|
||||
|
||||
def raw_input(prompt=''):
|
||||
#the original raw_input would only remove a trailing \n, so, at
|
||||
#this point if we had a \r\n the \r would remain (which is valid for eclipse)
|
||||
#so, let's remove the remaining \r which python didn't expect.
|
||||
ret = original_raw_input(prompt)
|
||||
|
||||
if ret.endswith('\r'):
|
||||
return ret[:-1]
|
||||
|
||||
return ret
|
||||
raw_input.__doc__ = original_raw_input.__doc__
|
||||
|
||||
def input(prompt=''):
|
||||
#input must also be rebinded for using the new raw_input defined
|
||||
return eval(raw_input(prompt))
|
||||
input.__doc__ = original_input.__doc__
|
||||
|
||||
|
||||
__builtin__.raw_input = raw_input
|
||||
__builtin__.input = input
|
||||
|
||||
except:
|
||||
#Don't report errors at this stage
|
||||
if DEBUG:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
|
||||
else:
|
||||
try:
|
||||
import builtins #Python 3.0 does not have the __builtin__ module @UnresolvedImport
|
||||
original_input = builtins.input
|
||||
def input(prompt=''):
|
||||
#the original input would only remove a trailing \n, so, at
|
||||
#this point if we had a \r\n the \r would remain (which is valid for eclipse)
|
||||
#so, let's remove the remaining \r which python didn't expect.
|
||||
ret = original_input(prompt)
|
||||
|
||||
if ret.endswith('\r'):
|
||||
return ret[:-1]
|
||||
|
||||
return ret
|
||||
input.__doc__ = original_input.__doc__
|
||||
builtins.input = input
|
||||
except:
|
||||
#Don't report errors at this stage
|
||||
if DEBUG:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
|
||||
|
||||
|
||||
try:
|
||||
#The original getpass doesn't work from the eclipse console, so, let's put a replacement
|
||||
#here (note that it'll not go into echo mode in the console, so, what' the user writes
|
||||
#will actually be seen)
|
||||
import getpass #@UnresolvedImport
|
||||
if IS_PYTHON_3K:
|
||||
def pydev_getpass(msg='Password: '):
|
||||
return input(msg)
|
||||
else:
|
||||
def pydev_getpass(msg='Password: '):
|
||||
return raw_input(msg)
|
||||
|
||||
getpass.getpass = pydev_getpass
|
||||
except:
|
||||
#Don't report errors at this stage
|
||||
if DEBUG:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
The UserModuleDeleter and runfile methods are copied from
|
||||
Spyder and carry their own license agreement.
|
||||
http://code.google.com/p/spyderlib/source/browse/spyderlib/widgets/externalshell/sitecustomize.py
|
||||
|
||||
Spyder License Agreement (MIT License)
|
||||
--------------------------------------
|
||||
|
||||
Copyright (c) 2009-2012 Pierre Raybaut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# The following classes and functions are mainly intended to be used from
|
||||
# an interactive Python session
|
||||
class UserModuleDeleter:
|
||||
"""
|
||||
User Module Deleter (UMD) aims at deleting user modules
|
||||
to force Python to deeply reload them during import
|
||||
|
||||
pathlist [list]: blacklist in terms of module path
|
||||
namelist [list]: blacklist in terms of module name
|
||||
"""
|
||||
def __init__(self, namelist=None, pathlist=None):
|
||||
if namelist is None:
|
||||
namelist = []
|
||||
self.namelist = namelist
|
||||
if pathlist is None:
|
||||
pathlist = []
|
||||
self.pathlist = pathlist
|
||||
try:
|
||||
# blacklist all files in org.python.pydev/pysrc
|
||||
import pydev_pysrc, inspect
|
||||
self.pathlist.append(os.path.dirname(pydev_pysrc.__file__))
|
||||
except:
|
||||
pass
|
||||
self.previous_modules = list(sys.modules.keys())
|
||||
|
||||
def is_module_blacklisted(self, modname, modpath):
|
||||
for path in [sys.prefix] + self.pathlist:
|
||||
if modpath.startswith(path):
|
||||
return True
|
||||
else:
|
||||
return set(modname.split('.')) & set(self.namelist)
|
||||
|
||||
def run(self, verbose=False):
|
||||
"""
|
||||
Del user modules to force Python to deeply reload them
|
||||
|
||||
Do not del modules which are considered as system modules, i.e.
|
||||
modules installed in subdirectories of Python interpreter's binary
|
||||
Do not del C modules
|
||||
"""
|
||||
log = []
|
||||
modules_copy = dict(sys.modules)
|
||||
for modname, module in modules_copy.items():
|
||||
if modname == 'aaaaa':
|
||||
print(modname, module)
|
||||
print(self.previous_modules)
|
||||
if modname not in self.previous_modules:
|
||||
modpath = getattr(module, '__file__', None)
|
||||
if modpath is None:
|
||||
# *module* is a C module that is statically linked into the
|
||||
# interpreter. There is no way to know its path, so we
|
||||
# choose to ignore it.
|
||||
continue
|
||||
if not self.is_module_blacklisted(modname, modpath):
|
||||
log.append(modname)
|
||||
del sys.modules[modname]
|
||||
if verbose and log:
|
||||
print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" % ("UMD has deleted",
|
||||
": " + ", ".join(log)))
|
||||
|
||||
__umd__ = None
|
||||
|
||||
_get_globals_callback = None
|
||||
def _set_globals_function(get_globals):
|
||||
global _get_globals_callback
|
||||
_get_globals_callback = get_globals
|
||||
def _get_globals():
|
||||
"""Return current Python interpreter globals namespace"""
|
||||
if _get_globals_callback is not None:
|
||||
return _get_globals_callback()
|
||||
else:
|
||||
try:
|
||||
from __main__ import __dict__ as namespace
|
||||
except ImportError:
|
||||
try:
|
||||
# The import fails on IronPython
|
||||
import __main__
|
||||
namespace = __main__.__dict__
|
||||
except:
|
||||
namespace
|
||||
shell = namespace.get('__ipythonshell__')
|
||||
if shell is not None and hasattr(shell, 'user_ns'):
|
||||
# IPython 0.12+ kernel
|
||||
return shell.user_ns
|
||||
else:
|
||||
# Python interpreter
|
||||
return namespace
|
||||
return namespace
|
||||
|
||||
|
||||
def runfile(filename, args=None, wdir=None, namespace=None):
|
||||
"""
|
||||
Run filename
|
||||
args: command line arguments (string)
|
||||
wdir: working directory
|
||||
"""
|
||||
try:
|
||||
if hasattr(filename, 'decode'):
|
||||
filename = filename.decode('utf-8')
|
||||
except (UnicodeError, TypeError):
|
||||
pass
|
||||
global __umd__
|
||||
if os.environ.get("PYDEV_UMD_ENABLED", "").lower() == "true":
|
||||
if __umd__ is None:
|
||||
namelist = os.environ.get("PYDEV_UMD_NAMELIST", None)
|
||||
if namelist is not None:
|
||||
namelist = namelist.split(',')
|
||||
__umd__ = UserModuleDeleter(namelist=namelist)
|
||||
else:
|
||||
verbose = os.environ.get("PYDEV_UMD_VERBOSE", "").lower() == "true"
|
||||
__umd__.run(verbose=verbose)
|
||||
if args is not None and not isinstance(args, basestring):
|
||||
raise TypeError("expected a character buffer object")
|
||||
if namespace is None:
|
||||
namespace = _get_globals()
|
||||
if '__file__' in namespace:
|
||||
old_file = namespace['__file__']
|
||||
else:
|
||||
old_file = None
|
||||
namespace['__file__'] = filename
|
||||
sys.argv = [filename]
|
||||
if args is not None:
|
||||
for arg in args.split():
|
||||
sys.argv.append(arg)
|
||||
if wdir is not None:
|
||||
try:
|
||||
if hasattr(wdir, 'decode'):
|
||||
wdir = wdir.decode('utf-8')
|
||||
except (UnicodeError, TypeError):
|
||||
pass
|
||||
os.chdir(wdir)
|
||||
execfile(filename, namespace)
|
||||
sys.argv = ['']
|
||||
if old_file is None:
|
||||
del namespace['__file__']
|
||||
else:
|
||||
namespace['__file__'] = old_file
|
||||
@@ -10,7 +10,6 @@ import os
|
||||
import sys
|
||||
|
||||
from pydevd_constants import USE_LIB_COPY
|
||||
from pydevd_constants import IS_JYTHON
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
@@ -23,15 +22,7 @@ fix_getpass.fixGetpass()
|
||||
|
||||
import pydevd_vars
|
||||
|
||||
from pydev_imports import Exec
|
||||
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_Queue as _queue
|
||||
else:
|
||||
import Queue as _queue
|
||||
except:
|
||||
import queue as _queue
|
||||
from pydev_imports import Exec, _queue
|
||||
|
||||
try:
|
||||
import __builtin__
|
||||
@@ -47,7 +38,7 @@ except NameError: # version < 2.3 -- didn't have the True/False builtins
|
||||
setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
from pydev_console_utils import BaseInterpreterInterface
|
||||
from pydev_console_utils import BaseInterpreterInterface, BaseStdIn
|
||||
from pydev_console_utils import CodeFragment
|
||||
|
||||
IS_PYTHON_3K = False
|
||||
@@ -59,17 +50,6 @@ except:
|
||||
#That's OK, not all versions of python have sys.version_info
|
||||
pass
|
||||
|
||||
try:
|
||||
try:
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_xmlrpclib as xmlrpclib
|
||||
else:
|
||||
import xmlrpclib
|
||||
except ImportError:
|
||||
import xmlrpc.client as xmlrpclib
|
||||
except ImportError:
|
||||
import _pydev_xmlrpclib as xmlrpclib
|
||||
|
||||
|
||||
class Command:
|
||||
def __init__(self, interpreter, code_fragment):
|
||||
@@ -145,23 +125,71 @@ class InterpreterInterface(BaseInterpreterInterface):
|
||||
|
||||
traceback.print_exc()
|
||||
return []
|
||||
|
||||
|
||||
def close(self):
|
||||
sys.exit(0)
|
||||
|
||||
def get_greeting_msg(self):
|
||||
return 'PyDev console: starting.'
|
||||
return 'PyDev console: starting.\n'
|
||||
|
||||
|
||||
class _ProcessExecQueueHelper:
|
||||
_debug_hook = None
|
||||
_return_control_osc = False
|
||||
|
||||
def set_debug_hook(debug_hook):
|
||||
_ProcessExecQueueHelper._debug_hook = debug_hook
|
||||
|
||||
|
||||
def process_exec_queue(interpreter):
|
||||
|
||||
from pydev_ipython.inputhook import get_inputhook, set_return_control_callback
|
||||
|
||||
def return_control():
|
||||
''' A function that the inputhooks can call (via inputhook.stdin_ready()) to find
|
||||
out if they should cede control and return '''
|
||||
if _ProcessExecQueueHelper._debug_hook:
|
||||
# Some of the input hooks check return control without doing
|
||||
# a single operation, so we don't return True on every
|
||||
# call when the debug hook is in place to allow the GUI to run
|
||||
# XXX: Eventually the inputhook code will have diverged enough
|
||||
# from the IPython source that it will be worthwhile rewriting
|
||||
# it rather than pretending to maintain the old API
|
||||
_ProcessExecQueueHelper._return_control_osc = not _ProcessExecQueueHelper._return_control_osc
|
||||
if _ProcessExecQueueHelper._return_control_osc:
|
||||
return True
|
||||
|
||||
if not interpreter.exec_queue.empty():
|
||||
return True
|
||||
return False
|
||||
|
||||
set_return_control_callback(return_control)
|
||||
|
||||
while 1:
|
||||
# Running the request may have changed the inputhook in use
|
||||
inputhook = get_inputhook()
|
||||
|
||||
if _ProcessExecQueueHelper._debug_hook:
|
||||
_ProcessExecQueueHelper._debug_hook()
|
||||
|
||||
if inputhook:
|
||||
try:
|
||||
# Note: it'll block here until return_control returns True.
|
||||
inputhook()
|
||||
except:
|
||||
import traceback;traceback.print_exc()
|
||||
try:
|
||||
try:
|
||||
codeFragment = interpreter.exec_queue.get(block=True, timeout=0.05)
|
||||
code_fragment = interpreter.exec_queue.get(block=True, timeout=1/20.) # 20 calls/second
|
||||
except _queue.Empty:
|
||||
continue
|
||||
|
||||
more = interpreter.addExec(codeFragment)
|
||||
if callable(code_fragment):
|
||||
# It can be a callable (i.e.: something that must run in the main
|
||||
# thread can be put in the queue for later execution).
|
||||
code_fragment()
|
||||
else:
|
||||
more = interpreter.addExec(code_fragment)
|
||||
except KeyboardInterrupt:
|
||||
interpreter.buffer = None
|
||||
continue
|
||||
@@ -221,16 +249,6 @@ def handshake():
|
||||
return "PyCharm"
|
||||
|
||||
|
||||
def ipython_editor(interpreter):
|
||||
def editor(file, line):
|
||||
if file is None:
|
||||
file = ""
|
||||
if line is None:
|
||||
line = "-1"
|
||||
interpreter.ipython_editor(file, line)
|
||||
|
||||
return editor
|
||||
|
||||
#=======================================================================================================================
|
||||
# StartServer
|
||||
#=======================================================================================================================
|
||||
@@ -238,11 +256,8 @@ def start_server(host, port, interpreter):
|
||||
if port == 0:
|
||||
host = ''
|
||||
|
||||
try:
|
||||
from _pydev_xmlrpc_hook import InputHookedXMLRPCServer as XMLRPCServer #@UnusedImport
|
||||
except:
|
||||
#I.e.: supporting the internal Jython version in PyDev to create a Jython interactive console inside Eclipse.
|
||||
from pydev_imports import SimpleXMLRPCServer as XMLRPCServer #@Reimport
|
||||
#I.e.: supporting the internal Jython version in PyDev to create a Jython interactive console inside Eclipse.
|
||||
from pydev_imports import SimpleXMLRPCServer as XMLRPCServer #@Reimport
|
||||
|
||||
try:
|
||||
server = XMLRPCServer((host, port), logRequests=False, allow_none=True)
|
||||
@@ -262,12 +277,10 @@ def start_server(host, port, interpreter):
|
||||
server.register_function(interpreter.interrupt)
|
||||
server.register_function(handshake)
|
||||
server.register_function(interpreter.connectToDebugger)
|
||||
server.register_function(interpreter.hello)
|
||||
|
||||
if IPYTHON:
|
||||
try:
|
||||
interpreter.interpreter.ipython.hooks.editor = ipython_editor(interpreter)
|
||||
except:
|
||||
pass
|
||||
# Functions for GUI main loop integration
|
||||
server.register_function(interpreter.enableGui)
|
||||
|
||||
if port == 0:
|
||||
(h, port) = server.socket.getsockname()
|
||||
@@ -279,7 +292,6 @@ def start_server(host, port, interpreter):
|
||||
sys.stderr.write(interpreter.get_greeting_msg())
|
||||
sys.stderr.flush()
|
||||
|
||||
interpreter.server = server
|
||||
server.serve_forever()
|
||||
|
||||
return server
|
||||
@@ -318,10 +330,9 @@ def get_completions(text, token, globals, locals):
|
||||
|
||||
return interpreterInterface.getCompletions(text, token)
|
||||
|
||||
def get_frame():
|
||||
interpreterInterface = get_interpreter()
|
||||
|
||||
return interpreterInterface.getFrame()
|
||||
#===============================================================================
|
||||
# Debugger integration
|
||||
#===============================================================================
|
||||
|
||||
def exec_code(code, globals, locals):
|
||||
interpreterInterface = get_interpreter()
|
||||
@@ -338,20 +349,6 @@ def exec_code(code, globals, locals):
|
||||
return False
|
||||
|
||||
|
||||
def read_line(s):
|
||||
ret = ''
|
||||
|
||||
while True:
|
||||
c = s.recv(1)
|
||||
|
||||
if c == '\n' or c == '':
|
||||
break
|
||||
else:
|
||||
ret += c
|
||||
|
||||
return ret
|
||||
|
||||
# Debugger integration
|
||||
|
||||
class ConsoleWriter(InteractiveInterpreter):
|
||||
skip = 0
|
||||
@@ -408,7 +405,7 @@ class ConsoleWriter(InteractiveInterpreter):
|
||||
sys.stderr.write(''.join(lines))
|
||||
|
||||
def consoleExec(thread_id, frame_id, expression):
|
||||
"""returns 'False' in case expression is partialy correct
|
||||
"""returns 'False' in case expression is partially correct
|
||||
"""
|
||||
frame = pydevd_vars.findFrame(thread_id, frame_id)
|
||||
|
||||
@@ -452,9 +449,8 @@ def consoleExec(thread_id, frame_id, expression):
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.stdin = BaseStdIn()
|
||||
port, client_port = sys.argv[1:3]
|
||||
import pydev_localhost
|
||||
|
||||
|
||||
+583
-255
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ import weakref
|
||||
#=======================================================================================================================
|
||||
class AbstractPyDBAdditionalThreadInfo:
|
||||
def __init__(self):
|
||||
self.pydev_state = STATE_RUN
|
||||
self.pydev_state = STATE_RUN
|
||||
self.pydev_step_stop = None
|
||||
self.pydev_step_cmd = None
|
||||
self.pydev_notify_kill = False
|
||||
@@ -20,51 +20,52 @@ class AbstractPyDBAdditionalThreadInfo:
|
||||
self.pydev_smart_step_stop = None
|
||||
self.pydev_django_resolve_frame = None
|
||||
self.is_tracing = False
|
||||
self.conditional_breakpoint_exception = None
|
||||
|
||||
|
||||
|
||||
def IterFrames(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def CreateDbFrame(self, args):
|
||||
#args = mainDebugger, filename, base, additionalInfo, t, frame
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'State:%s Stop:%s Cmd: %s Kill:%s' % (self.pydev_state, self.pydev_step_stop, self.pydev_step_cmd, self.pydev_notify_kill)
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDBAdditionalThreadInfoWithCurrentFramesSupport
|
||||
#=======================================================================================================================
|
||||
class PyDBAdditionalThreadInfoWithCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
|
||||
|
||||
|
||||
def IterFrames(self):
|
||||
#sys._current_frames(): dictionary with thread id -> topmost frame
|
||||
return sys._current_frames().values() #return a copy... don't know if it's changed if we did get an iterator
|
||||
|
||||
#just create the db frame directly
|
||||
CreateDbFrame = PyDBFrame
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDBAdditionalThreadInfoWithoutCurrentFramesSupport
|
||||
#=======================================================================================================================
|
||||
class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditionalThreadInfo):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
AbstractPyDBAdditionalThreadInfo.__init__(self)
|
||||
#That's where the last frame entered is kept. That's needed so that we're able to
|
||||
#That's where the last frame entered is kept. That's needed so that we're able to
|
||||
#trace contexts that were previously untraced and are currently active. So, the bad thing
|
||||
#is that the frame may be kept alive longer than it would if we go up on the frame stack,
|
||||
#and is only disposed when some other frame is removed.
|
||||
#A better way would be if we could get the topmost frame for each thread, but that's
|
||||
#A better way would be if we could get the topmost frame for each thread, but that's
|
||||
#not possible (until python 2.5 -- which is the PyDBAdditionalThreadInfoWithCurrentFramesSupport version)
|
||||
#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._acquire_lock = self.lock.acquire
|
||||
self._release_lock = self.lock.release
|
||||
|
||||
|
||||
#collection with the refs
|
||||
d = {}
|
||||
self.pydev_existing_frames = d
|
||||
@@ -72,8 +73,8 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional
|
||||
self._iter_frames = d.iterkeys
|
||||
except AttributeError:
|
||||
self._iter_frames = d.keys
|
||||
|
||||
|
||||
|
||||
|
||||
def _OnDbFrameCollected(self, ref):
|
||||
'''
|
||||
Callback to be called when a given reference is garbage-collected.
|
||||
@@ -83,8 +84,8 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional
|
||||
del self.pydev_existing_frames[ref]
|
||||
finally:
|
||||
self._release_lock()
|
||||
|
||||
|
||||
|
||||
|
||||
def _AddDbFrame(self, db_frame):
|
||||
self._acquire_lock()
|
||||
try:
|
||||
@@ -94,8 +95,8 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional
|
||||
self.pydev_existing_frames[r] = r
|
||||
finally:
|
||||
self._release_lock()
|
||||
|
||||
|
||||
|
||||
|
||||
def CreateDbFrame(self, args):
|
||||
#the frame must be cached as a weak-ref (we return the actual db frame -- which will be kept
|
||||
#alive until its trace_dispatch method is not referenced anymore).
|
||||
@@ -106,14 +107,14 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional
|
||||
db_frame.frame = args[-1]
|
||||
self._AddDbFrame(db_frame)
|
||||
return db_frame
|
||||
|
||||
|
||||
|
||||
|
||||
def IterFrames(self):
|
||||
#We cannot use yield (because of the lock)
|
||||
self._acquire_lock()
|
||||
try:
|
||||
ret = []
|
||||
|
||||
|
||||
for weak_db_frame in self._iter_frames():
|
||||
try:
|
||||
ret.append(weak_db_frame().frame)
|
||||
|
||||
@@ -2,14 +2,12 @@ from pydevd_constants import *
|
||||
import pydevd_tracing
|
||||
import sys
|
||||
import pydev_log
|
||||
import pydevd_import_class
|
||||
|
||||
_original_excepthook = None
|
||||
_handle_exceptions = None
|
||||
|
||||
|
||||
NOTIFY_ALWAYS="NOTIFY_ALWAYS"
|
||||
NOTIFY_ON_TERMINATE="NOTIFY_ON_TERMINATE"
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
@@ -20,52 +18,39 @@ threadingCurrentThread = threading.currentThread
|
||||
from pydevd_comm import GetGlobalDebugger
|
||||
|
||||
class ExceptionBreakpoint:
|
||||
def __init__(self, qname, notify_always, notify_on_terminate):
|
||||
exctype = get_class(qname)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
qname,
|
||||
notify_always,
|
||||
notify_on_terminate,
|
||||
notify_on_first_raise_only,
|
||||
):
|
||||
exctype = _get_class(qname)
|
||||
self.qname = qname
|
||||
if exctype is not None:
|
||||
self.name = exctype.__name__
|
||||
else:
|
||||
self.name = None
|
||||
|
||||
self.notify_on_terminate = int(notify_on_terminate) == 1
|
||||
self.notify_always = int(notify_always) > 0
|
||||
self.notify_on_first_raise_only = int(notify_always) == 2
|
||||
self.notify_on_terminate = notify_on_terminate
|
||||
self.notify_always = notify_always
|
||||
self.notify_on_first_raise_only = notify_on_first_raise_only
|
||||
|
||||
self.type = exctype
|
||||
self.notify = {NOTIFY_ALWAYS: self.notify_always, NOTIFY_ON_TERMINATE: self.notify_on_terminate}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.qname
|
||||
|
||||
class LineBreakpoint:
|
||||
def __init__(self, type, flag, condition, func_name, expression):
|
||||
self.type = type
|
||||
|
||||
def __init__(self, line, condition, func_name, expression):
|
||||
self.line = line
|
||||
self.condition = condition
|
||||
self.func_name = func_name
|
||||
self.expression = expression
|
||||
|
||||
def get_break_dict(self, breakpoints, file):
|
||||
if DictContains(breakpoints, file):
|
||||
breakDict = breakpoints[file]
|
||||
else:
|
||||
breakDict = {}
|
||||
breakpoints[file] = breakDict
|
||||
return breakDict
|
||||
|
||||
def trace(self, file, line, func_name):
|
||||
if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
|
||||
pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
|
||||
sys.stderr.flush()
|
||||
|
||||
def add(self, breakpoints, file, line, func_name):
|
||||
self.trace(file, line, func_name)
|
||||
|
||||
breakDict = self.get_break_dict(breakpoints, file)
|
||||
|
||||
breakDict[line] = self
|
||||
|
||||
def get_exception_full_qname(exctype):
|
||||
if not exctype:
|
||||
return None
|
||||
@@ -77,41 +62,41 @@ def get_exception_name(exctype):
|
||||
return exctype.__name__
|
||||
|
||||
|
||||
def get_exception_breakpoint(exctype, exceptions, notify_class):
|
||||
name = get_exception_full_qname(exctype)
|
||||
def get_exception_breakpoint(exctype, exceptions):
|
||||
exception_full_qname = get_exception_full_qname(exctype)
|
||||
|
||||
exc = None
|
||||
if exceptions is not None:
|
||||
for k, e in exceptions.items():
|
||||
if e.notify[notify_class]:
|
||||
if name == k:
|
||||
return e
|
||||
if (e.type is not None and issubclass(exctype, e.type)):
|
||||
if exc is None or issubclass(e.type, exc.type):
|
||||
exc = e
|
||||
try:
|
||||
return exceptions[exception_full_qname]
|
||||
except KeyError:
|
||||
for exception_breakpoint in DictIterValues(exceptions):
|
||||
if exception_breakpoint.type is not None and issubclass(exctype, exception_breakpoint.type):
|
||||
if exc is None or issubclass(exception_breakpoint.type, exc.type):
|
||||
exc = exception_breakpoint
|
||||
return exc
|
||||
|
||||
#=======================================================================================================================
|
||||
# excepthook
|
||||
# _excepthook
|
||||
#=======================================================================================================================
|
||||
def excepthook(exctype, value, tb):
|
||||
def _excepthook(exctype, value, tb):
|
||||
global _handle_exceptions
|
||||
if _handle_exceptions is not None:
|
||||
exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions, NOTIFY_ON_TERMINATE)
|
||||
if _handle_exceptions:
|
||||
exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions)
|
||||
else:
|
||||
exception_breakpoint = None
|
||||
|
||||
if exception_breakpoint is None:
|
||||
return _original_excepthook(exctype, value, tb)
|
||||
|
||||
#Always call the original excepthook before going on to call the debugger post mortem to show it.
|
||||
_original_excepthook(exctype, value, tb)
|
||||
|
||||
if not exception_breakpoint:
|
||||
return
|
||||
|
||||
if tb is None: #sometimes it can be None, e.g. with GTK
|
||||
return
|
||||
return
|
||||
|
||||
frames = []
|
||||
|
||||
traceback = tb
|
||||
while tb:
|
||||
frames.append(tb.tb_frame)
|
||||
tb = tb.tb_next
|
||||
@@ -122,9 +107,7 @@ def excepthook(exctype, value, tb):
|
||||
thread.additionalInfo.exception = (exctype, value, tb)
|
||||
thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid)
|
||||
thread.additionalInfo.message = exception_breakpoint.qname
|
||||
#sys.exc_info = lambda : (exctype, value, traceback)
|
||||
debugger = GetGlobalDebugger()
|
||||
debugger.force_post_mortem_stop += 1
|
||||
|
||||
pydevd_tracing.SetTrace(None) #no tracing from here
|
||||
|
||||
@@ -133,38 +116,27 @@ def excepthook(exctype, value, tb):
|
||||
debugger.handle_post_mortem_stop(thread.additionalInfo, thread)
|
||||
|
||||
#=======================================================================================================================
|
||||
# set_pm_excepthook
|
||||
# _set_pm_excepthook
|
||||
#=======================================================================================================================
|
||||
def set_pm_excepthook(handle_exceptions_arg=None):
|
||||
def _set_pm_excepthook(handle_exceptions_dict=None):
|
||||
'''
|
||||
Should be called to register the excepthook to be used.
|
||||
|
||||
It's only useful for uncaucht exceptions. I.e.: exceptions that go up to the excepthook.
|
||||
It's only useful for uncaught exceptions. I.e.: exceptions that go up to the excepthook.
|
||||
|
||||
Can receive a parameter to stop only on some exceptions.
|
||||
|
||||
E.g.:
|
||||
register_excepthook((IndexError, ValueError))
|
||||
|
||||
or
|
||||
|
||||
register_excepthook(IndexError)
|
||||
|
||||
if passed without a parameter, will break on any exception
|
||||
|
||||
@param handle_exceptions: exception or tuple(exceptions)
|
||||
@param handle_exceptions: dict(exception -> ExceptionBreakpoint)
|
||||
The exceptions that should be handled.
|
||||
'''
|
||||
global _handle_exceptions
|
||||
global _original_excepthook
|
||||
if sys.excepthook != excepthook:
|
||||
#Only keep the original if it's not our own excepthook (if called many times).
|
||||
if sys.excepthook != _excepthook:
|
||||
#Only keep the original if it's not our own _excepthook (if called many times).
|
||||
_original_excepthook = sys.excepthook
|
||||
|
||||
_handle_exceptions = handle_exceptions_arg
|
||||
sys.excepthook = excepthook
|
||||
_handle_exceptions = handle_exceptions_dict
|
||||
sys.excepthook = _excepthook
|
||||
|
||||
def restore_pm_excepthook():
|
||||
def _restore_pm_excepthook():
|
||||
global _original_excepthook
|
||||
if _original_excepthook:
|
||||
sys.excepthook = _original_excepthook
|
||||
@@ -172,27 +144,16 @@ def restore_pm_excepthook():
|
||||
|
||||
|
||||
def update_exception_hook(dbg):
|
||||
if dbg.exception_set:
|
||||
set_pm_excepthook(dict(dbg.exception_set))
|
||||
if dbg.break_on_uncaught_exceptions:
|
||||
_set_pm_excepthook(dbg.break_on_uncaught_exceptions)
|
||||
else:
|
||||
restore_pm_excepthook()
|
||||
_restore_pm_excepthook()
|
||||
|
||||
def get_class( kls ):
|
||||
def _get_class( kls ):
|
||||
if IS_PY24 and "BaseException" == kls:
|
||||
kls = "Exception"
|
||||
parts = kls.split('.')
|
||||
module = ".".join(parts[:-1])
|
||||
if module == "":
|
||||
if IS_PY3K:
|
||||
module = "builtins"
|
||||
else:
|
||||
module = "__builtin__"
|
||||
|
||||
try:
|
||||
m = __import__( module )
|
||||
for comp in parts[-1:]:
|
||||
if m is None:
|
||||
return None
|
||||
m = getattr(m, comp, None)
|
||||
return m
|
||||
except ImportError:
|
||||
return None
|
||||
return eval(kls)
|
||||
except:
|
||||
return pydevd_import_class.ImportName(kls)
|
||||
|
||||
@@ -61,32 +61,14 @@ from pydevd_constants import * #@UnusedWildImport
|
||||
|
||||
import sys
|
||||
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_time as time
|
||||
import _pydev_threading as threading
|
||||
try:
|
||||
import _pydev_thread as thread
|
||||
except ImportError:
|
||||
import _thread as thread #Py3K changed it.
|
||||
import _pydev_Queue as _queue
|
||||
from _pydev_socket import socket
|
||||
from _pydev_socket import AF_INET, SOCK_STREAM
|
||||
from _pydev_socket import SHUT_RD, SHUT_WR
|
||||
else:
|
||||
import time
|
||||
import threading
|
||||
try:
|
||||
import thread
|
||||
except ImportError:
|
||||
import _thread as thread #Py3K changed it.
|
||||
from _pydev_imps import _pydev_time as time
|
||||
|
||||
try:
|
||||
import Queue as _queue
|
||||
except ImportError:
|
||||
import queue as _queue
|
||||
from socket import socket
|
||||
from socket import AF_INET, SOCK_STREAM
|
||||
from socket import SHUT_RD, SHUT_WR
|
||||
if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR
|
||||
from pydev_imports import _queue
|
||||
|
||||
try:
|
||||
from urllib import quote, quote_plus, unquote, unquote_plus
|
||||
@@ -103,6 +85,8 @@ import pydev_log
|
||||
import _pydev_completer
|
||||
|
||||
from pydevd_tracing import GetExceptionTracebackStr
|
||||
import pydevd_console
|
||||
from pydev_monkey import disable_trace_thread_modules, enable_trace_thread_modules
|
||||
|
||||
|
||||
|
||||
@@ -126,6 +110,8 @@ CMD_CHANGE_VARIABLE = 117
|
||||
CMD_RUN_TO_LINE = 118
|
||||
CMD_RELOAD_CODE = 119
|
||||
CMD_GET_COMPLETIONS = 120
|
||||
|
||||
# Note: renumbered (conflicted on merge)
|
||||
CMD_CONSOLE_EXEC = 121
|
||||
CMD_ADD_EXCEPTION_BREAK = 122
|
||||
CMD_REMOVE_EXCEPTION_BREAK = 123
|
||||
@@ -136,6 +122,24 @@ CMD_SET_NEXT_STATEMENT = 127
|
||||
CMD_SMART_STEP_INTO = 128
|
||||
CMD_EXIT = 129
|
||||
CMD_SIGNATURE_CALL_TRACE = 130
|
||||
|
||||
|
||||
|
||||
CMD_SET_PY_EXCEPTION = 131
|
||||
CMD_GET_FILE_CONTENTS = 132
|
||||
CMD_SET_PROPERTY_TRACE = 133
|
||||
# Pydev debug console commands
|
||||
CMD_EVALUATE_CONSOLE_EXPRESSION = 134
|
||||
CMD_RUN_CUSTOM_OPERATION = 135
|
||||
CMD_GET_BREAKPOINT_EXCEPTION = 136
|
||||
CMD_STEP_CAUGHT_EXCEPTION = 137
|
||||
CMD_SEND_CURR_EXCEPTION_TRACE = 138
|
||||
CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED = 139
|
||||
CMD_IGNORE_THROWN_EXCEPTION_AT = 140
|
||||
CMD_ENABLE_DONT_TRACE = 141
|
||||
|
||||
|
||||
|
||||
CMD_VERSION = 501
|
||||
CMD_RETURN = 502
|
||||
CMD_ERROR = 901
|
||||
@@ -171,6 +175,19 @@ ID_TO_MEANING = {
|
||||
'128':'CMD_SMART_STEP_INTO',
|
||||
'129': 'CMD_EXIT',
|
||||
'130': 'CMD_SIGNATURE_CALL_TRACE',
|
||||
|
||||
'131': 'CMD_SET_PY_EXCEPTION',
|
||||
'132': 'CMD_GET_FILE_CONTENTS',
|
||||
'133': 'CMD_SET_PROPERTY_TRACE',
|
||||
'134': 'CMD_EVALUATE_CONSOLE_EXPRESSION',
|
||||
'135': 'CMD_RUN_CUSTOM_OPERATION',
|
||||
'136': 'CMD_GET_BREAKPOINT_EXCEPTION',
|
||||
'137': 'CMD_STEP_CAUGHT_EXCEPTION',
|
||||
'138': 'CMD_SEND_CURR_EXCEPTION_TRACE',
|
||||
'139': 'CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED',
|
||||
'140': 'CMD_IGNORE_THROWN_EXCEPTION_AT',
|
||||
'141': 'CMD_ENABLE_DONT_TRACE',
|
||||
|
||||
'501':'CMD_VERSION',
|
||||
'502':'CMD_RETURN',
|
||||
'901':'CMD_ERROR',
|
||||
@@ -274,7 +291,7 @@ class ReaderThread(PyDBDaemonThread):
|
||||
#We must close the socket so that it doesn't stay halted there.
|
||||
self.killReceived = True
|
||||
try:
|
||||
self.sock.shutdown(SHUT_RD) #shotdown the socket for read
|
||||
self.sock.shutdown(SHUT_RD) #shutdown the socket for read
|
||||
except:
|
||||
#just ignore that
|
||||
pass
|
||||
@@ -564,68 +581,69 @@ class NetCommandFactory:
|
||||
except:
|
||||
return self.makeErrorMessage(0, GetExceptionTracebackStr())
|
||||
|
||||
def makeThreadSuspendMessage(self, thread_id, frame, stop_reason, message):
|
||||
|
||||
def makeThreadSuspendStr(self, thread_id, frame, stop_reason, message):
|
||||
""" <xml>
|
||||
<thread id="id" stop_reason="reason">
|
||||
<frame id="id" name="functionName " file="file" line="line">
|
||||
<var variable stuffff....
|
||||
</frame>
|
||||
</thread>
|
||||
"""
|
||||
"""
|
||||
cmdTextList = ["<xml>"]
|
||||
|
||||
if message:
|
||||
message = pydevd_vars.makeValidXmlValue(str(message))
|
||||
|
||||
cmdTextList.append('<thread id="%s" stop_reason="%s" message="%s">' % (thread_id, stop_reason, message))
|
||||
|
||||
curFrame = frame
|
||||
try:
|
||||
cmdTextList = ["<xml>"]
|
||||
while curFrame:
|
||||
#print cmdText
|
||||
myId = str(id(curFrame))
|
||||
#print "id is ", myId
|
||||
|
||||
if message:
|
||||
message = pydevd_vars.makeValidXmlValue(str(message))
|
||||
if curFrame.f_code is None:
|
||||
break #Iron Python sometimes does not have it!
|
||||
|
||||
cmdTextList.append('<thread id="%s" stop_reason="%s" message="%s">' % (thread_id, stop_reason, message))
|
||||
myName = curFrame.f_code.co_name #method name (if in method) or ? if global
|
||||
if myName is None:
|
||||
break #Iron Python sometimes does not have it!
|
||||
|
||||
curFrame = frame
|
||||
try:
|
||||
while curFrame:
|
||||
#print cmdText
|
||||
myId = str(id(curFrame))
|
||||
#print "id is ", myId
|
||||
#print "name is ", myName
|
||||
|
||||
if curFrame.f_code is None:
|
||||
break #Iron Python sometimes does not have it!
|
||||
filename, base = pydevd_file_utils.GetFilenameAndBase(curFrame)
|
||||
|
||||
myName = curFrame.f_code.co_name #method name (if in method) or ? if global
|
||||
if myName is None:
|
||||
break #Iron Python sometimes does not have it!
|
||||
myFile = pydevd_file_utils.NormFileToClient(filename)
|
||||
if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
|
||||
# myFile is a byte string encoded using the file system encoding
|
||||
# convert it to utf8
|
||||
myFile = myFile.decode(file_system_encoding).encode("utf-8")
|
||||
|
||||
#print "name is ", myName
|
||||
#print "file is ", myFile
|
||||
#myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
|
||||
|
||||
filename, base = pydevd_file_utils.GetFilenameAndBase(curFrame)
|
||||
myLine = str(curFrame.f_lineno)
|
||||
#print "line is ", myLine
|
||||
|
||||
myFile = pydevd_file_utils.NormFileToClient(filename)
|
||||
if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
|
||||
# myFile is a byte string encoded using the file system encoding
|
||||
# convert it to utf8
|
||||
myFile = myFile.decode(file_system_encoding).encode("utf-8")
|
||||
#the variables are all gotten 'on-demand'
|
||||
#variables = pydevd_vars.frameVarsToXML(curFrame.f_locals)
|
||||
|
||||
#print "file is ", myFile
|
||||
#myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
|
||||
variables = ''
|
||||
cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName)))
|
||||
cmdTextList.append('file="%s" line="%s">"' % (quote(myFile, '/>_= \t'), myLine))
|
||||
cmdTextList.append(variables)
|
||||
cmdTextList.append("</frame>")
|
||||
curFrame = curFrame.f_back
|
||||
except :
|
||||
traceback.print_exc()
|
||||
|
||||
myLine = str(curFrame.f_lineno)
|
||||
#print "line is ", myLine
|
||||
cmdTextList.append("</thread></xml>")
|
||||
return ''.join(cmdTextList)
|
||||
|
||||
#the variables are all gotten 'on-demand'
|
||||
#variables = pydevd_vars.frameVarsToXML(curFrame.f_locals)
|
||||
|
||||
variables = ''
|
||||
cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_vars.makeValidXmlValue(myName)))
|
||||
cmdTextList.append('file="%s" line="%s">"' % (quote(myFile, '/>_= \t'), myLine))
|
||||
cmdTextList.append(variables)
|
||||
cmdTextList.append("</frame>")
|
||||
curFrame = curFrame.f_back
|
||||
except :
|
||||
traceback.print_exc()
|
||||
|
||||
cmdTextList.append("</thread></xml>")
|
||||
cmdText = ''.join(cmdTextList)
|
||||
return NetCommand(CMD_THREAD_SUSPEND, 0, cmdText)
|
||||
def makeThreadSuspendMessage(self, thread_id, frame, stop_reason, message):
|
||||
try:
|
||||
return NetCommand(CMD_THREAD_SUSPEND, 0, self.makeThreadSuspendStr(thread_id, frame, stop_reason, message))
|
||||
except:
|
||||
return self.makeErrorMessage(0, GetExceptionTracebackStr())
|
||||
|
||||
@@ -660,6 +678,51 @@ class NetCommandFactory:
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeGetFileContents(self, seq, payload):
|
||||
try:
|
||||
return NetCommand(CMD_GET_FILE_CONTENTS, seq, payload)
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeSendBreakpointExceptionMessage(self, seq, payload):
|
||||
try:
|
||||
return NetCommand(CMD_GET_BREAKPOINT_EXCEPTION, seq, payload)
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeSendCurrExceptionTraceMessage(self, seq, thread_id, curr_frame_id, exc_type, exc_desc, trace_obj):
|
||||
try:
|
||||
while trace_obj.tb_next is not None:
|
||||
trace_obj = trace_obj.tb_next
|
||||
|
||||
exc_type = pydevd_vars.makeValidXmlValue(str(exc_type)).replace('\t', ' ') or 'exception: type unknown'
|
||||
exc_desc = pydevd_vars.makeValidXmlValue(str(exc_desc)).replace('\t', ' ') or 'exception: no description'
|
||||
|
||||
payload = str(curr_frame_id) + '\t' + exc_type + "\t" + exc_desc + "\t" + \
|
||||
self.makeThreadSuspendStr(thread_id, trace_obj.tb_frame, CMD_SEND_CURR_EXCEPTION_TRACE, '')
|
||||
|
||||
return NetCommand(CMD_SEND_CURR_EXCEPTION_TRACE, seq, payload)
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeSendCurrExceptionTraceProceededMessage(self, seq, thread_id):
|
||||
try:
|
||||
return NetCommand(CMD_SEND_CURR_EXCEPTION_TRACE_PROCEEDED, 0, str(thread_id))
|
||||
except:
|
||||
return self.makeErrorMessage(0, GetExceptionTracebackStr())
|
||||
|
||||
def makeSendConsoleMessage(self, seq, payload):
|
||||
try:
|
||||
return NetCommand(CMD_EVALUATE_CONSOLE_EXPRESSION, seq, payload)
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeCustomOperationMessage(self, seq, payload):
|
||||
try:
|
||||
return NetCommand(CMD_RUN_CUSTOM_OPERATION, seq, payload)
|
||||
except Exception:
|
||||
return self.makeErrorMessage(seq, GetExceptionTracebackStr())
|
||||
|
||||
def makeLoadSourceMessage(self, seq, source, dbg=None):
|
||||
try:
|
||||
net = NetCommand(CMD_LOAD_SOURCE, seq, '%s' % source)
|
||||
@@ -698,7 +761,7 @@ class InternalThreadCommand:
|
||||
def canBeExecutedBy(self, thread_id):
|
||||
'''By default, it must be in the same thread to be executed
|
||||
'''
|
||||
return self.thread_id == thread_id
|
||||
return self.thread_id == thread_id or self.thread_id.endswith('|' + thread_id)
|
||||
|
||||
def doIt(self, dbg):
|
||||
raise NotImplementedError("you have to override doIt")
|
||||
@@ -929,7 +992,7 @@ class InternalEvaluateExpression(InternalThreadCommand):
|
||||
try:
|
||||
result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec)
|
||||
xml = "<xml>"
|
||||
xml += pydevd_vars.varToXML(result, "", self.doTrim)
|
||||
xml += pydevd_vars.varToXML(result, self.expression, self.doTrim)
|
||||
xml += "</xml>"
|
||||
cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml)
|
||||
dbg.writer.addCommand(cmd)
|
||||
@@ -961,7 +1024,6 @@ class InternalGetCompletions(InternalThreadCommand):
|
||||
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
|
||||
if frame is not None:
|
||||
|
||||
|
||||
msg = _pydev_completer.GenerateCompletionsAsXML(frame, self.act_tok)
|
||||
|
||||
cmd = dbg.cmdFactory.makeGetCompletionsMessage(self.sequence, msg)
|
||||
@@ -981,6 +1043,182 @@ class InternalGetCompletions(InternalThreadCommand):
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalGetBreakpointException
|
||||
#=======================================================================================================================
|
||||
class InternalGetBreakpointException(InternalThreadCommand):
|
||||
""" Send details of exception raised while evaluating conditional breakpoint """
|
||||
def __init__(self, thread_id, exc_type, stacktrace):
|
||||
self.sequence = 0
|
||||
self.thread_id = thread_id
|
||||
self.stacktrace = stacktrace
|
||||
self.exc_type = exc_type
|
||||
|
||||
def doIt(self, dbg):
|
||||
try:
|
||||
callstack = "<xml>"
|
||||
|
||||
makeValid = pydevd_vars.makeValidXmlValue
|
||||
|
||||
for filename, line, methodname, methodobj in self.stacktrace:
|
||||
if file_system_encoding.lower() != "utf-8" and hasattr(filename, "decode"):
|
||||
# filename is a byte string encoded using the file system encoding
|
||||
# convert it to utf8
|
||||
filename = filename.decode(file_system_encoding).encode("utf-8")
|
||||
|
||||
callstack += '<frame thread_id = "%s" file="%s" line="%s" name="%s" obj="%s" />' \
|
||||
% (self.thread_id, makeValid(filename), line, makeValid(methodname), makeValid(methodobj))
|
||||
callstack += "</xml>"
|
||||
|
||||
cmd = dbg.cmdFactory.makeSendBreakpointExceptionMessage(self.sequence, self.exc_type + "\t" + callstack)
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
sys.stderr.write('%s\n' % (exc,))
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Exception: " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalSendCurrExceptionTrace
|
||||
#=======================================================================================================================
|
||||
class InternalSendCurrExceptionTrace(InternalThreadCommand):
|
||||
""" Send details of the exception that was caught and where we've broken in.
|
||||
"""
|
||||
def __init__(self, thread_id, arg, curr_frame_id):
|
||||
'''
|
||||
:param arg: exception type, description, traceback object
|
||||
'''
|
||||
self.sequence = 0
|
||||
self.thread_id = thread_id
|
||||
self.curr_frame_id = curr_frame_id
|
||||
self.arg = arg
|
||||
|
||||
def doIt(self, dbg):
|
||||
try:
|
||||
cmd = dbg.cmdFactory.makeSendCurrExceptionTraceMessage(self.sequence, self.thread_id, self.curr_frame_id, *self.arg)
|
||||
del self.arg
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
sys.stderr.write('%s\n' % (exc,))
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Current Exception Trace: " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalSendCurrExceptionTraceProceeded
|
||||
#=======================================================================================================================
|
||||
class InternalSendCurrExceptionTraceProceeded(InternalThreadCommand):
|
||||
""" Send details of the exception that was caught and where we've broken in.
|
||||
"""
|
||||
def __init__(self, thread_id):
|
||||
self.sequence = 0
|
||||
self.thread_id = thread_id
|
||||
|
||||
def doIt(self, dbg):
|
||||
try:
|
||||
cmd = dbg.cmdFactory.makeSendCurrExceptionTraceProceededMessage(self.sequence, self.thread_id)
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
sys.stderr.write('%s\n' % (exc,))
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error Sending Current Exception Trace Proceeded: " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalEvaluateConsoleExpression
|
||||
#=======================================================================================================================
|
||||
class InternalEvaluateConsoleExpression(InternalThreadCommand):
|
||||
""" Execute the given command in the debug console """
|
||||
|
||||
def __init__(self, seq, thread_id, frame_id, line):
|
||||
self.sequence = seq
|
||||
self.thread_id = thread_id
|
||||
self.frame_id = frame_id
|
||||
self.line = line
|
||||
|
||||
def doIt(self, dbg):
|
||||
""" Create an XML for console output, error and more (true/false)
|
||||
<xml>
|
||||
<output message=output_message></output>
|
||||
<error message=error_message></error>
|
||||
<more>true/false</more>
|
||||
</xml>
|
||||
"""
|
||||
try:
|
||||
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
|
||||
if frame is not None:
|
||||
console_message = pydevd_console.execute_console_command(frame, self.thread_id, self.frame_id, self.line)
|
||||
cmd = dbg.cmdFactory.makeSendConsoleMessage(self.sequence, console_message.toXML())
|
||||
else:
|
||||
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),
|
||||
)
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, console_message.toXML())
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalRunCustomOperation
|
||||
#=======================================================================================================================
|
||||
class InternalRunCustomOperation(InternalThreadCommand):
|
||||
""" Run a custom command on an expression
|
||||
"""
|
||||
def __init__(self, seq, thread_id, frame_id, scope, attrs, style, encoded_code_or_file, fnname):
|
||||
self.sequence = seq
|
||||
self.thread_id = thread_id
|
||||
self.frame_id = frame_id
|
||||
self.scope = scope
|
||||
self.attrs = attrs
|
||||
self.style = style
|
||||
self.code_or_file = unquote_plus(encoded_code_or_file)
|
||||
self.fnname = fnname
|
||||
|
||||
def doIt(self, dbg):
|
||||
try:
|
||||
res = pydevd_vars.customOperation(self.thread_id, self.frame_id, self.scope, self.attrs,
|
||||
self.style, self.code_or_file, self.fnname)
|
||||
resEncoded = quote_plus(res)
|
||||
cmd = dbg.cmdFactory.makeCustomOperationMessage(self.sequence, resEncoded)
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error in running custom operation" + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalConsoleGetCompletions
|
||||
#=======================================================================================================================
|
||||
class InternalConsoleGetCompletions(InternalThreadCommand):
|
||||
""" Fetch the completions in the debug console
|
||||
"""
|
||||
def __init__(self, seq, thread_id, frame_id, act_tok):
|
||||
self.sequence = seq
|
||||
self.thread_id = thread_id
|
||||
self.frame_id = frame_id
|
||||
self.act_tok = act_tok
|
||||
|
||||
def doIt(self, dbg):
|
||||
""" Get completions and write back to the client
|
||||
"""
|
||||
try:
|
||||
frame = pydevd_vars.findFrame(self.thread_id, self.frame_id)
|
||||
completions_xml = pydevd_console.get_completions(frame, self.act_tok)
|
||||
cmd = dbg.cmdFactory.makeSendConsoleMessage(self.sequence, completions_xml)
|
||||
dbg.writer.addCommand(cmd)
|
||||
except:
|
||||
exc = GetExceptionTracebackStr()
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error in fetching completions" + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InternalConsoleExec
|
||||
@@ -996,13 +1234,10 @@ class InternalConsoleExec(InternalThreadCommand):
|
||||
|
||||
def doIt(self, dbg):
|
||||
""" Converts request into python variable """
|
||||
pydev_start_new_thread = None
|
||||
try:
|
||||
try:
|
||||
pydev_start_new_thread = thread.start_new_thread
|
||||
|
||||
thread.start_new_thread = thread._original_start_new_thread #don't trace new threads created by console command
|
||||
thread.start_new = thread._original_start_new_thread
|
||||
#don't trace new threads created by console command
|
||||
disable_trace_thread_modules()
|
||||
|
||||
result = pydevconsole.consoleExec(self.thread_id, self.frame_id, self.expression)
|
||||
xml = "<xml>"
|
||||
@@ -1016,8 +1251,8 @@ class InternalConsoleExec(InternalThreadCommand):
|
||||
cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating console expression " + exc)
|
||||
dbg.writer.addCommand(cmd)
|
||||
finally:
|
||||
thread.start_new_thread = pydev_start_new_thread
|
||||
thread.start_new = pydev_start_new_thread
|
||||
enable_trace_thread_modules()
|
||||
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
'''An helper file for the pydev debugger (REPL) console
|
||||
'''
|
||||
from code import InteractiveConsole
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import _pydev_completer
|
||||
from pydevd_tracing import GetExceptionTracebackStr
|
||||
from pydevd_vars import makeValidXmlValue
|
||||
from pydev_imports import Exec
|
||||
from pydevd_io import IOBuf
|
||||
from pydev_console_utils import BaseInterpreterInterface, BaseStdIn
|
||||
from pydev_override import overrides
|
||||
import pydevd_save_locals
|
||||
|
||||
CONSOLE_OUTPUT = "output"
|
||||
CONSOLE_ERROR = "error"
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# ConsoleMessage
|
||||
#=======================================================================================================================
|
||||
class ConsoleMessage:
|
||||
"""Console Messages
|
||||
"""
|
||||
def __init__(self):
|
||||
self.more = False
|
||||
# List of tuple [('error', 'error_message'), ('message_list', 'output_message')]
|
||||
self.console_messages = []
|
||||
|
||||
def add_console_message(self, message_type, message):
|
||||
"""add messages in the console_messages list
|
||||
"""
|
||||
for m in message.split("\n"):
|
||||
if m.strip():
|
||||
self.console_messages.append((message_type, m))
|
||||
|
||||
def update_more(self, more):
|
||||
"""more is set to true if further input is required from the user
|
||||
else more is set to false
|
||||
"""
|
||||
self.more = more
|
||||
|
||||
def toXML(self):
|
||||
"""Create an XML for console message_list, error and more (true/false)
|
||||
<xml>
|
||||
<message_list>console message_list</message_list>
|
||||
<error>console error</error>
|
||||
<more>true/false</more>
|
||||
</xml>
|
||||
"""
|
||||
makeValid = makeValidXmlValue
|
||||
|
||||
xml = '<xml><more>%s</more>' % (self.more)
|
||||
|
||||
for message_type, message in self.console_messages:
|
||||
xml += '<%s message="%s"></%s>' % (message_type, makeValid(message), message_type)
|
||||
|
||||
xml += '</xml>'
|
||||
|
||||
return xml
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DebugConsoleStdIn
|
||||
#=======================================================================================================================
|
||||
class DebugConsoleStdIn(BaseStdIn):
|
||||
|
||||
overrides(BaseStdIn.readline)
|
||||
def readline(self, *args, **kwargs):
|
||||
sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n')
|
||||
return '\n'
|
||||
|
||||
#=======================================================================================================================
|
||||
# DebugConsole
|
||||
#=======================================================================================================================
|
||||
class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
|
||||
"""Wrapper around code.InteractiveConsole, in order to send
|
||||
errors and outputs to the debug console
|
||||
"""
|
||||
|
||||
overrides(BaseInterpreterInterface.createStdIn)
|
||||
def createStdIn(self):
|
||||
return DebugConsoleStdIn() #For now, raw_input is not supported in this console.
|
||||
|
||||
|
||||
overrides(InteractiveConsole.push)
|
||||
def push(self, line, frame):
|
||||
"""Change built-in stdout and stderr methods by the
|
||||
new custom StdMessage.
|
||||
execute the InteractiveConsole.push.
|
||||
Change the stdout and stderr back be the original built-ins
|
||||
|
||||
Return boolean (True if more input is required else False),
|
||||
output_messages and input_messages
|
||||
"""
|
||||
more = False
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
try:
|
||||
try:
|
||||
self.frame = frame
|
||||
out = sys.stdout = IOBuf()
|
||||
err = sys.stderr = IOBuf()
|
||||
more = self.addExec(line)
|
||||
except Exception:
|
||||
exc = GetExceptionTracebackStr()
|
||||
err.buflist.append("Internal Error: %s" % (exc,))
|
||||
finally:
|
||||
#Remove frame references.
|
||||
self.frame = None
|
||||
frame = None
|
||||
sys.stdout = original_stdout
|
||||
sys.stderr = original_stderr
|
||||
|
||||
return more, out.buflist, err.buflist
|
||||
|
||||
|
||||
overrides(BaseInterpreterInterface.doAddExec)
|
||||
def doAddExec(self, line):
|
||||
return InteractiveConsole.push(self, line)
|
||||
|
||||
|
||||
overrides(InteractiveConsole.runcode)
|
||||
def runcode(self, code):
|
||||
"""Execute a code object.
|
||||
|
||||
When an exception occurs, self.showtraceback() is called to
|
||||
display a traceback. All exceptions are caught except
|
||||
SystemExit, which is reraised.
|
||||
|
||||
A note about KeyboardInterrupt: this exception may occur
|
||||
elsewhere in this code, and may not always be caught. The
|
||||
caller should be prepared to deal with it.
|
||||
|
||||
"""
|
||||
try:
|
||||
Exec(code, self.frame.f_globals, self.frame.f_locals)
|
||||
pydevd_save_locals.save_locals(self.frame)
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
self.showtraceback()
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# InteractiveConsoleCache
|
||||
#=======================================================================================================================
|
||||
class InteractiveConsoleCache:
|
||||
|
||||
thread_id = None
|
||||
frame_id = None
|
||||
interactive_console_instance = None
|
||||
|
||||
|
||||
#Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions.
|
||||
def get_interactive_console(thread_id, frame_id, frame, console_message):
|
||||
"""returns the global interactive console.
|
||||
interactive console should have been initialized by this time
|
||||
"""
|
||||
if InteractiveConsoleCache.thread_id == thread_id and InteractiveConsoleCache.frame_id == frame_id:
|
||||
return InteractiveConsoleCache.interactive_console_instance
|
||||
|
||||
InteractiveConsoleCache.interactive_console_instance = DebugConsole()
|
||||
InteractiveConsoleCache.thread_id = thread_id
|
||||
InteractiveConsoleCache.frame_id = frame_id
|
||||
|
||||
console_stacktrace = traceback.extract_stack(frame, limit=1)
|
||||
if console_stacktrace:
|
||||
current_context = console_stacktrace[0] # top entry from stacktrace
|
||||
context_message = 'File "%s", line %s, in %s' % (current_context[0], current_context[1], current_context[2])
|
||||
console_message.add_console_message(CONSOLE_OUTPUT, "[Current context]: %s" % (context_message,))
|
||||
return InteractiveConsoleCache.interactive_console_instance
|
||||
|
||||
|
||||
def clear_interactive_console():
|
||||
InteractiveConsoleCache.thread_id = None
|
||||
InteractiveConsoleCache.frame_id = None
|
||||
InteractiveConsoleCache.interactive_console_instance = None
|
||||
|
||||
|
||||
def execute_console_command(frame, thread_id, frame_id, line):
|
||||
"""fetch an interactive console instance from the cache and
|
||||
push the received command to the console.
|
||||
|
||||
create and return an instance of console_message
|
||||
"""
|
||||
console_message = ConsoleMessage()
|
||||
|
||||
interpreter = get_interactive_console(thread_id, frame_id, frame, console_message)
|
||||
more, output_messages, error_messages = interpreter.push(line, frame)
|
||||
console_message.update_more(more)
|
||||
|
||||
for message in output_messages:
|
||||
console_message.add_console_message(CONSOLE_OUTPUT, message)
|
||||
|
||||
for message in error_messages:
|
||||
console_message.add_console_message(CONSOLE_ERROR, message)
|
||||
|
||||
return console_message
|
||||
|
||||
|
||||
def get_completions(frame, act_tok):
|
||||
""" fetch all completions, create xml for the same
|
||||
return the completions xml
|
||||
"""
|
||||
return _pydev_completer.GenerateCompletionsAsXML(frame, act_tok)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'''
|
||||
This module holds the constants used for specifying the states of the debugger.
|
||||
'''
|
||||
|
||||
STATE_RUN = 1
|
||||
STATE_SUSPEND = 2
|
||||
|
||||
@@ -17,13 +16,13 @@ except:
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
class DebugInfoHolder:
|
||||
#we have to put it here because it can be set through the command line (so, the
|
||||
#we have to put it here because it can be set through the command line (so, the
|
||||
#already imported references would not have it).
|
||||
DEBUG_RECORD_SOCKET_READS = False
|
||||
DEBUG_TRACE_LEVEL = -1
|
||||
DEBUG_TRACE_BREAKPOINTS = -1
|
||||
|
||||
#Optimize with psyco? This gave a 50% speedup in the debugger in tests
|
||||
#Optimize with psyco? This gave a 50% speedup in the debugger in tests
|
||||
USE_PSYCO_OPTIMIZATION = True
|
||||
|
||||
#Hold a reference to the original _getframe (because psyco will change that as soon as it's imported)
|
||||
@@ -111,11 +110,48 @@ except:
|
||||
return default
|
||||
|
||||
|
||||
if IS_PY3K:
|
||||
def DictKeys(d):
|
||||
return list(d.keys())
|
||||
|
||||
def DictValues(d):
|
||||
return list(d.values())
|
||||
|
||||
DictIterValues = dict.values
|
||||
|
||||
def DictIterItems(d):
|
||||
return d.items()
|
||||
|
||||
def DictItems(d):
|
||||
return list(d.items())
|
||||
|
||||
else:
|
||||
DictKeys = dict.keys
|
||||
try:
|
||||
DictIterValues = dict.itervalues
|
||||
except:
|
||||
DictIterValues = dict.values #Older versions don't have the itervalues
|
||||
|
||||
DictValues = dict.values
|
||||
|
||||
def DictIterItems(d):
|
||||
return d.iteritems()
|
||||
|
||||
def DictItems(d):
|
||||
return d.items()
|
||||
|
||||
|
||||
try:
|
||||
xrange
|
||||
xrange = xrange
|
||||
except:
|
||||
#Python 3k does not have it
|
||||
xrange = range
|
||||
|
||||
try:
|
||||
import itertools
|
||||
izip = itertools.izip
|
||||
except:
|
||||
izip = zip
|
||||
|
||||
try:
|
||||
object
|
||||
@@ -128,10 +164,10 @@ try:
|
||||
except:
|
||||
def enumerate(lst):
|
||||
ret = []
|
||||
i=0
|
||||
i = 0
|
||||
for element in lst:
|
||||
ret.append((i, element))
|
||||
i+=1
|
||||
i += 1
|
||||
return ret
|
||||
|
||||
#=======================================================================================================================
|
||||
@@ -174,8 +210,7 @@ def GetThreadId(thread):
|
||||
except AttributeError:
|
||||
try:
|
||||
#Jython does not have it!
|
||||
import java.lang.management.ManagementFactory #@UnresolvedImport -- just for jython
|
||||
|
||||
import java.lang.management.ManagementFactory #@UnresolvedImport -- just for jython
|
||||
pid = java.lang.management.ManagementFactory.getRuntimeMXBean().getName()
|
||||
pid = pid.replace('@', '_')
|
||||
except:
|
||||
@@ -262,4 +297,4 @@ def call_only_once(func):
|
||||
if __name__ == '__main__':
|
||||
if Null():
|
||||
sys.stdout.write('here\n')
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
'''
|
||||
Support for a tag that allows skipping over functions while debugging.
|
||||
'''
|
||||
import linecache
|
||||
import re
|
||||
from pydevd_constants import DictContains
|
||||
|
||||
# To suppress tracing a method, add the tag @DontTrace
|
||||
# to a comment either preceding or on the same line as
|
||||
# the method definition
|
||||
#
|
||||
# E.g.:
|
||||
# #@DontTrace
|
||||
# def test1():
|
||||
# pass
|
||||
#
|
||||
# ... or ...
|
||||
#
|
||||
# def test2(): #@DontTrace
|
||||
# pass
|
||||
DONT_TRACE_TAG = '@DontTrace'
|
||||
|
||||
# Regular expression to match a decorator (at the beginning
|
||||
# of a line).
|
||||
RE_DECORATOR = re.compile(r'^\s*@')
|
||||
|
||||
# Mapping from code object to bool.
|
||||
# If the key exists, the value is the cached result of should_trace_hook
|
||||
_filename_to_ignored_lines = {}
|
||||
|
||||
def default_should_trace_hook(frame, filename):
|
||||
'''
|
||||
Return True if this frame should be traced, False if tracing should be blocked.
|
||||
'''
|
||||
# First, check whether this code object has a cached value
|
||||
ignored_lines = _filename_to_ignored_lines.get(filename)
|
||||
if ignored_lines is None:
|
||||
# Now, look up that line of code and check for a @DontTrace
|
||||
# preceding or on the same line as the method.
|
||||
# E.g.:
|
||||
# #@DontTrace
|
||||
# def test():
|
||||
# pass
|
||||
# ... or ...
|
||||
# def test(): #@DontTrace
|
||||
# pass
|
||||
ignored_lines = {}
|
||||
lines = linecache.getlines(filename)
|
||||
i_line = 0 # Could use enumerate, but not there on all versions...
|
||||
for line in lines:
|
||||
j = line.find('#')
|
||||
if j >= 0:
|
||||
comment = line[j:]
|
||||
if DONT_TRACE_TAG in comment:
|
||||
ignored_lines[i_line] = 1
|
||||
|
||||
#Note: when it's found in the comment, mark it up and down for the decorator lines found.
|
||||
k = i_line - 1
|
||||
while k >= 0:
|
||||
if RE_DECORATOR.match(lines[k]):
|
||||
ignored_lines[k] = 1
|
||||
k -= 1
|
||||
else:
|
||||
break
|
||||
|
||||
k = i_line + 1
|
||||
while k <= len(lines):
|
||||
if RE_DECORATOR.match(lines[k]):
|
||||
ignored_lines[k] = 1
|
||||
k += 1
|
||||
else:
|
||||
break
|
||||
|
||||
i_line += 1
|
||||
|
||||
|
||||
_filename_to_ignored_lines[filename] = ignored_lines
|
||||
|
||||
func_line = frame.f_code.co_firstlineno - 1 # co_firstlineno is 1-based, so -1 is needed
|
||||
return not (
|
||||
DictContains(ignored_lines, func_line - 1) or #-1 to get line before method
|
||||
DictContains(ignored_lines, func_line)) #method line
|
||||
|
||||
|
||||
should_trace_hook = None
|
||||
|
||||
|
||||
def clear_trace_filter_cache():
|
||||
'''
|
||||
Clear the trace filter cache.
|
||||
Call this after reloading.
|
||||
'''
|
||||
global should_trace_hook
|
||||
try:
|
||||
# Need to temporarily disable a hook because otherwise
|
||||
# _filename_to_ignored_lines.clear() will never complete.
|
||||
old_hook = should_trace_hook
|
||||
should_trace_hook = None
|
||||
|
||||
# Clear the linecache
|
||||
linecache.clearcache()
|
||||
_filename_to_ignored_lines.clear()
|
||||
|
||||
finally:
|
||||
should_trace_hook = old_hook
|
||||
|
||||
|
||||
def trace_filter(mode):
|
||||
'''
|
||||
Set the trace filter mode.
|
||||
|
||||
mode: Whether to enable the trace hook.
|
||||
True: Trace filtering on (skipping methods tagged @DontTrace)
|
||||
False: Trace filtering off (trace methods tagged @DontTrace)
|
||||
None/default: Toggle trace filtering.
|
||||
'''
|
||||
global should_trace_hook
|
||||
if mode is None:
|
||||
mode = should_trace_hook is None
|
||||
|
||||
if mode:
|
||||
should_trace_hook = default_should_trace_hook
|
||||
else:
|
||||
should_trace_hook = None
|
||||
|
||||
return mode
|
||||
|
||||
@@ -3,72 +3,91 @@
|
||||
- The case of a file will match the actual file in the filesystem (otherwise breakpoints won't be hit).
|
||||
- Providing means for the user to make path conversions when doing a remote debugging session in
|
||||
one machine and debugging in another.
|
||||
|
||||
|
||||
To do that, the PATHS_FROM_ECLIPSE_TO_PYTHON constant must be filled with the appropriate paths.
|
||||
|
||||
@note:
|
||||
in this context, the server is where your python process is running
|
||||
|
||||
@note:
|
||||
in this context, the server is where your python process is running
|
||||
and the client is where eclipse is running.
|
||||
|
||||
E.g.:
|
||||
|
||||
E.g.:
|
||||
If the server (your python process) has the structure
|
||||
/user/projects/my_project/src/package/module1.py
|
||||
|
||||
and the client has:
|
||||
c:\my_project\src\package\module1.py
|
||||
|
||||
/user/projects/my_project/src/package/module1.py
|
||||
|
||||
and the client has:
|
||||
c:\my_project\src\package\module1.py
|
||||
|
||||
the PATHS_FROM_ECLIPSE_TO_PYTHON would have to be:
|
||||
PATHS_FROM_ECLIPSE_TO_PYTHON = [(r'c:\my_project\src', r'/user/projects/my_project/src')]
|
||||
|
||||
|
||||
@note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the result of those translations
|
||||
|
||||
|
||||
@note: the case of the paths is important! Note that this can be tricky to get right when one machine
|
||||
uses a case-independent filesystem and the other uses a case-dependent filesystem (if the system being
|
||||
debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON).
|
||||
|
||||
debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON).
|
||||
|
||||
@note: all the paths with breakpoints must be translated (otherwise they won't be found in the server)
|
||||
|
||||
|
||||
@note: to enable remote debugging in the target machine (pydev extensions in the eclipse installation)
|
||||
import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, port, suspend)
|
||||
|
||||
|
||||
see parameter docs on pydevd.py
|
||||
|
||||
@note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
|
||||
through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target
|
||||
|
||||
@note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
|
||||
through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target
|
||||
machine for the paths that'll actually have breakpoints).
|
||||
'''
|
||||
|
||||
|
||||
|
||||
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
import os.path
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
|
||||
os_normcase = os.path.normcase
|
||||
basename = os.path.basename
|
||||
exists = os.path.exists
|
||||
join = os.path.join
|
||||
|
||||
try:
|
||||
rPath = os.path.realpath #@UndefinedVariable
|
||||
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
|
||||
|
||||
rPath = os.path.abspath
|
||||
|
||||
#defined as a list of tuples where the 1st element of the tuple is the path in the client machine
|
||||
#and the 2nd element is the path in the server machine.
|
||||
#see module docstring for more details.
|
||||
PATHS_FROM_ECLIPSE_TO_PYTHON = []
|
||||
|
||||
|
||||
#example:
|
||||
#PATHS_FROM_ECLIPSE_TO_PYTHON = [
|
||||
#(normcase(r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy'),
|
||||
# normcase(r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx'))]
|
||||
# (r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy',
|
||||
# r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx')
|
||||
#]
|
||||
|
||||
|
||||
normcase = os_normcase # May be rebound on set_ide_os
|
||||
|
||||
def set_ide_os(os):
|
||||
'''
|
||||
We need to set the IDE os because the host where the code is running may be
|
||||
actually different from the client (and the point is that we want the proper
|
||||
paths to translate from the client to the server).
|
||||
'''
|
||||
global normcase
|
||||
if os == 'UNIX':
|
||||
normcase = lambda f:f #Change to no-op if the client side is on unix/mac.
|
||||
else:
|
||||
normcase = os_normcase
|
||||
|
||||
# After setting the ide OS, apply the normcase to the existing paths.
|
||||
|
||||
# Note: not using enumerate nor list comprehension because it may not be available in older python versions...
|
||||
i = 0
|
||||
for path in PATHS_FROM_ECLIPSE_TO_PYTHON[:]:
|
||||
PATHS_FROM_ECLIPSE_TO_PYTHON[i] = (normcase(path[0]), normcase(path[1]))
|
||||
i += 1
|
||||
|
||||
|
||||
DEBUG_CLIENT_SERVER_TRANSLATION = False
|
||||
|
||||
@@ -79,14 +98,6 @@ NORM_FILENAME_TO_SERVER_CONTAINER = {}
|
||||
NORM_FILENAME_TO_CLIENT_CONTAINER = {}
|
||||
|
||||
|
||||
pycharm_os = None
|
||||
|
||||
def normcase(file):
|
||||
global pycharm_os
|
||||
if pycharm_os == 'UNIX':
|
||||
return file
|
||||
else:
|
||||
return os.path.normcase(file)
|
||||
|
||||
|
||||
def _NormFile(filename):
|
||||
@@ -147,7 +158,7 @@ def exists(file):
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
|
||||
#Now, let's do a quick test to see if we're working with a version of python that has no problems
|
||||
#related to the names generated...
|
||||
try:
|
||||
@@ -162,11 +173,11 @@ try:
|
||||
sys.stderr.write('pydev debugger: Related bug: http://bugs.python.org/issue1666807\n')
|
||||
sys.stderr.write('-------------------------------------------------------------------------------\n')
|
||||
sys.stderr.flush()
|
||||
|
||||
|
||||
NORM_SEARCH_CACHE = {}
|
||||
|
||||
|
||||
initial_norm_file = _NormFile
|
||||
def _NormFile(filename): #Let's redefine _NormFile to work with paths that may be incorrect
|
||||
def _NormFile(filename): #Let's redefine _NormFile to work with paths that may be incorrect
|
||||
try:
|
||||
return NORM_SEARCH_CACHE[filename]
|
||||
except KeyError:
|
||||
@@ -180,7 +191,7 @@ try:
|
||||
else:
|
||||
sys.stderr.write('pydev debugger: Unable to find real location for: %s\n' % (filename,))
|
||||
ret = filename
|
||||
|
||||
|
||||
NORM_SEARCH_CACHE[filename] = ret
|
||||
return ret
|
||||
except:
|
||||
@@ -195,28 +206,28 @@ if PATHS_FROM_ECLIPSE_TO_PYTHON:
|
||||
for eclipse_prefix, server_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
|
||||
if eclipse_sep is not None and python_sep is not None:
|
||||
break
|
||||
|
||||
|
||||
if eclipse_sep is None:
|
||||
for c in eclipse_prefix:
|
||||
if c in ('/', '\\'):
|
||||
eclipse_sep = c
|
||||
break
|
||||
|
||||
|
||||
if python_sep is None:
|
||||
for c in server_prefix:
|
||||
if c in ('/', '\\'):
|
||||
python_sep = c
|
||||
break
|
||||
|
||||
|
||||
#If they're the same or one of them cannot be determined, just make it all None.
|
||||
if eclipse_sep == python_sep or eclipse_sep is None or python_sep is None:
|
||||
eclipse_sep = python_sep = None
|
||||
|
||||
|
||||
#only setup translation functions if absolutely needed!
|
||||
|
||||
|
||||
#only setup translation functions if absolutely needed!
|
||||
def NormFileToServer(filename):
|
||||
#Eclipse will send the passed filename to be translated to the python process
|
||||
#So, this would be 'NormFileFromEclipseToPython'
|
||||
#So, this would be 'NormFileFromEclipseToPython'
|
||||
try:
|
||||
return NORM_FILENAME_TO_SERVER_CONTAINER[filename]
|
||||
except KeyError:
|
||||
@@ -234,17 +245,17 @@ if PATHS_FROM_ECLIPSE_TO_PYTHON:
|
||||
if DEBUG_CLIENT_SERVER_TRANSLATION:
|
||||
sys.stderr.write('pydev debugger: to server: unable to find matching prefix for: %s in %s\n' % \
|
||||
(translated, [x[0] for x in PATHS_FROM_ECLIPSE_TO_PYTHON]))
|
||||
|
||||
|
||||
#Note that when going to the server, we do the replace first and only later do the norm file.
|
||||
if eclipse_sep is not None:
|
||||
translated = translated.replace(eclipse_sep, python_sep)
|
||||
translated = _NormFile(translated)
|
||||
|
||||
|
||||
NORM_FILENAME_TO_SERVER_CONTAINER[filename] = translated
|
||||
return translated
|
||||
|
||||
|
||||
def NormFileToClient(filename):
|
||||
|
||||
|
||||
def NormFileToClient(filename):
|
||||
#The result of this method will be passed to eclipse
|
||||
#So, this would be 'NormFileFromPythonToEclipse'
|
||||
try:
|
||||
@@ -264,15 +275,15 @@ if PATHS_FROM_ECLIPSE_TO_PYTHON:
|
||||
if DEBUG_CLIENT_SERVER_TRANSLATION:
|
||||
sys.stderr.write('pydev debugger: to client: unable to find matching prefix for: %s in %s\n' % \
|
||||
(translated, [x[1] for x in PATHS_FROM_ECLIPSE_TO_PYTHON]))
|
||||
|
||||
|
||||
if eclipse_sep is not None:
|
||||
translated = translated.replace(python_sep, eclipse_sep)
|
||||
|
||||
|
||||
#The resulting path is not in the python process, so, we cannot do a _NormFile here,
|
||||
#only at the beginning of this method.
|
||||
NORM_FILENAME_TO_CLIENT_CONTAINER[filename] = translated
|
||||
return translated
|
||||
|
||||
|
||||
else:
|
||||
#no translation step needed (just inline the calls)
|
||||
NormFileToClient = _NormFile
|
||||
@@ -299,6 +310,3 @@ def GetFilenameAndBase(frame):
|
||||
f = f[:-1]
|
||||
return GetFileNameAndBaseFromFile(f)
|
||||
|
||||
def set_pycharm_os(os):
|
||||
global pycharm_os
|
||||
pycharm_os = os
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
from django_debug import is_django_render_call, get_template_file_name, get_template_line, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
|
||||
from django_debug import find_django_render_frame
|
||||
from django_frame import just_raised
|
||||
from django_frame import is_django_exception_break_context
|
||||
from django_frame import DjangoTemplateFrame
|
||||
from pydevd_comm import * #@UnusedWildImport
|
||||
from pydevd_breakpoints import * #@UnusedWildImport
|
||||
import traceback #@Reimport
|
||||
import linecache
|
||||
import os.path
|
||||
import sys
|
||||
import re
|
||||
import traceback # @Reimport
|
||||
|
||||
from django_debug import find_django_render_frame
|
||||
from django_debug import is_django_render_call, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call
|
||||
from django_frame import DjangoTemplateFrame
|
||||
from django_frame import is_django_exception_break_context
|
||||
from django_frame import just_raised, get_template_file_name, get_template_line
|
||||
import pydev_log
|
||||
from pydevd_breakpoints import get_exception_breakpoint, get_exception_name
|
||||
from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \
|
||||
CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_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
|
||||
import pydevd_vars
|
||||
import pydevd_dont_trace
|
||||
|
||||
basename = os.path.basename
|
||||
|
||||
IGNORE_EXCEPTION_TAG = re.compile('[^#]*#.*@IgnoreException')
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PyDBFrame
|
||||
#=======================================================================================================================
|
||||
@@ -22,6 +33,13 @@ class PyDBFrame:
|
||||
is reused for the entire context.
|
||||
'''
|
||||
|
||||
#Note: class (and not instance) attributes.
|
||||
|
||||
#Same thing in the main debugger but only considering the file contents, while the one in the main debugger
|
||||
#considers the user input (so, the actual result must be a join of both).
|
||||
filename_to_lines_where_exceptions_are_ignored = {}
|
||||
filename_to_stat_info = {}
|
||||
|
||||
def __init__(self, args):
|
||||
#args = mainDebugger, filename, base, info, t, frame
|
||||
#yeap, much faster than putting in self and then getting it from self later on
|
||||
@@ -33,84 +51,234 @@ class PyDBFrame:
|
||||
def doWaitSuspend(self, *args, **kwargs):
|
||||
self._args[0].doWaitSuspend(*args, **kwargs)
|
||||
|
||||
def _is_django_render_call(self, frame):
|
||||
try:
|
||||
return self._cached_is_django_render_call
|
||||
except:
|
||||
# Calculate lazily: note that a PyDBFrame always deals with the same
|
||||
# frame over and over, so, we can cache this.
|
||||
# -- although we can't cache things which change over time (such as
|
||||
# the breakpoints for the file).
|
||||
ret = self._cached_is_django_render_call = is_django_render_call(frame)
|
||||
return ret
|
||||
|
||||
def trace_exception(self, frame, event, arg):
|
||||
if event == 'exception':
|
||||
(flag, frame) = self.shouldStopOnException(frame, event, arg)
|
||||
flag, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
|
||||
if flag:
|
||||
self.handle_exception(frame, event, arg)
|
||||
return self.trace_dispatch
|
||||
self.handle_exception(frame, event, arg)
|
||||
return self.trace_dispatch
|
||||
|
||||
return self.trace_exception
|
||||
|
||||
def shouldStopOnException(self, frame, event, arg):
|
||||
mainDebugger, filename, info, thread = self._args
|
||||
flag = False
|
||||
def should_stop_on_exception(self, frame, event, arg):
|
||||
mainDebugger, _filename, info, thread = self._args
|
||||
flag = False
|
||||
|
||||
if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None:
|
||||
(exception, value, trace) = arg
|
||||
if info.pydev_state != STATE_SUSPEND: #and breakpoint is not None:
|
||||
exception, value, trace = arg
|
||||
|
||||
if trace is not None: #on jython trace is None on the first event
|
||||
exception_breakpoint = get_exception_breakpoint(exception, dict(mainDebugger.exception_set), NOTIFY_ALWAYS)
|
||||
if exception_breakpoint is not None:
|
||||
if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
|
||||
curr_func_name = frame.f_code.co_name
|
||||
add_exception_to_frame(frame, (exception, value, trace))
|
||||
self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK)
|
||||
thread.additionalInfo.message = exception_breakpoint.qname
|
||||
flag = True
|
||||
else:
|
||||
flag = False
|
||||
else:
|
||||
try:
|
||||
if mainDebugger.django_exception_break and get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and just_raised(trace) and is_django_exception_break_context(frame):
|
||||
render_frame = find_django_render_frame(frame)
|
||||
if render_frame:
|
||||
suspend_frame = suspend_django(self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
|
||||
if trace is not None: #on jython trace is None on the first event
|
||||
exception_breakpoint = get_exception_breakpoint(
|
||||
exception, mainDebugger.break_on_caught_exceptions)
|
||||
|
||||
if suspend_frame:
|
||||
add_exception_to_frame(suspend_frame, (exception, value, trace))
|
||||
flag = True
|
||||
thread.additionalInfo.message = 'VariableDoesNotExist'
|
||||
suspend_frame.f_back = frame
|
||||
frame = suspend_frame
|
||||
except :
|
||||
flag = False
|
||||
if exception_breakpoint is not None:
|
||||
if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace):
|
||||
# print frame.f_code.co_name
|
||||
add_exception_to_frame(frame, (exception, value, trace))
|
||||
thread.additionalInfo.message = exception_breakpoint.qname
|
||||
flag = True
|
||||
else:
|
||||
flag = False
|
||||
else:
|
||||
try:
|
||||
if mainDebugger.django_exception_break and get_exception_name(exception) in [
|
||||
'VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] \
|
||||
and just_raised(trace) and is_django_exception_break_context(frame):
|
||||
|
||||
return (flag, frame)
|
||||
render_frame = find_django_render_frame(frame)
|
||||
if render_frame:
|
||||
suspend_frame = suspend_django(
|
||||
self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK)
|
||||
|
||||
if suspend_frame:
|
||||
add_exception_to_frame(suspend_frame, (exception, value, trace))
|
||||
flag = True
|
||||
thread.additionalInfo.message = 'VariableDoesNotExist'
|
||||
suspend_frame.f_back = frame
|
||||
frame = suspend_frame
|
||||
except :
|
||||
flag = False
|
||||
|
||||
return flag, frame
|
||||
|
||||
def handle_exception(self, frame, event, arg):
|
||||
mainDebugger = self._args[0]
|
||||
thread = self._args[3]
|
||||
self.doWaitSuspend(thread, frame, event, arg)
|
||||
mainDebugger.SetTraceForFrameAndParents(frame)
|
||||
try:
|
||||
# print 'handle_exception', frame.f_lineno, frame.f_code.co_name
|
||||
|
||||
# We have 3 things in arg: exception type, description, traceback object
|
||||
trace_obj = arg[2]
|
||||
mainDebugger = self._args[0]
|
||||
|
||||
if not hasattr(trace_obj, 'tb_next'):
|
||||
return #Not always there on Jython...
|
||||
|
||||
initial_trace_obj = trace_obj
|
||||
if trace_obj.tb_next is None and trace_obj.tb_frame is frame:
|
||||
#I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check).
|
||||
|
||||
if mainDebugger.break_on_exceptions_thrown_in_same_context:
|
||||
#Option: Don't break if an exception is caught in the same function from which it is thrown
|
||||
return
|
||||
else:
|
||||
#Get the trace_obj from where the exception was raised...
|
||||
while trace_obj.tb_next is not None:
|
||||
trace_obj = trace_obj.tb_next
|
||||
|
||||
|
||||
if mainDebugger.ignore_exceptions_thrown_in_lines_with_ignore_exception:
|
||||
for check_trace_obj in (initial_trace_obj, trace_obj):
|
||||
filename = GetFilenameAndBase(check_trace_obj.tb_frame)[0]
|
||||
|
||||
|
||||
filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored
|
||||
|
||||
|
||||
lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename)
|
||||
if lines_ignored is None:
|
||||
lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {}
|
||||
|
||||
try:
|
||||
curr_stat = os.stat(filename)
|
||||
curr_stat = (curr_stat.st_size, curr_stat.st_mtime)
|
||||
except:
|
||||
curr_stat = None
|
||||
|
||||
last_stat = self.filename_to_stat_info.get(filename)
|
||||
if last_stat != curr_stat:
|
||||
self.filename_to_stat_info[filename] = curr_stat
|
||||
lines_ignored.clear()
|
||||
try:
|
||||
linecache.checkcache(filename)
|
||||
except:
|
||||
#Jython 2.1
|
||||
linecache.checkcache()
|
||||
|
||||
from_user_input = mainDebugger.filename_to_lines_where_exceptions_are_ignored.get(filename)
|
||||
if from_user_input:
|
||||
merged = {}
|
||||
merged.update(lines_ignored)
|
||||
#Override what we have with the related entries that the user entered
|
||||
merged.update(from_user_input)
|
||||
else:
|
||||
merged = lines_ignored
|
||||
|
||||
exc_lineno = check_trace_obj.tb_lineno
|
||||
|
||||
# print ('lines ignored', lines_ignored)
|
||||
# print ('user input', from_user_input)
|
||||
# print ('merged', merged, 'curr', exc_lineno)
|
||||
|
||||
if not DictContains(merged, exc_lineno): #Note: check on merged but update lines_ignored.
|
||||
try:
|
||||
line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals)
|
||||
except:
|
||||
#Jython 2.1
|
||||
line = linecache.getline(filename, exc_lineno)
|
||||
|
||||
if IGNORE_EXCEPTION_TAG.match(line) is not None:
|
||||
lines_ignored[exc_lineno] = 1
|
||||
return
|
||||
else:
|
||||
#Put in the cache saying not to ignore
|
||||
lines_ignored[exc_lineno] = 0
|
||||
else:
|
||||
#Ok, dict has it already cached, so, let's check it...
|
||||
if merged.get(exc_lineno, 0):
|
||||
return
|
||||
|
||||
|
||||
thread = self._args[3]
|
||||
|
||||
try:
|
||||
frame_id_to_frame = {}
|
||||
frame_id_to_frame[id(frame)] = frame
|
||||
f = trace_obj.tb_frame
|
||||
while f is not None:
|
||||
frame_id_to_frame[id(f)] = f
|
||||
f = f.f_back
|
||||
f = None
|
||||
|
||||
thread_id = GetThreadId(thread)
|
||||
pydevd_vars.addAdditionalFrameById(thread_id, frame_id_to_frame)
|
||||
try:
|
||||
mainDebugger.sendCaughtExceptionStack(thread, arg, id(frame))
|
||||
self.setSuspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
|
||||
self.doWaitSuspend(thread, frame, event, arg)
|
||||
mainDebugger.sendCaughtExceptionStackProceeded(thread)
|
||||
|
||||
finally:
|
||||
pydevd_vars.removeAdditionalFrameById(thread_id)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
mainDebugger.SetTraceForFrameAndParents(frame)
|
||||
finally:
|
||||
#Clear some local variables...
|
||||
trace_obj = None
|
||||
initial_trace_obj = None
|
||||
check_trace_obj = None
|
||||
f = None
|
||||
frame_id_to_frame = None
|
||||
mainDebugger = None
|
||||
thread = None
|
||||
|
||||
def trace_dispatch(self, frame, event, arg):
|
||||
mainDebugger, filename, info, thread = self._args
|
||||
main_debugger, filename, info, thread = self._args
|
||||
try:
|
||||
info.is_tracing = True
|
||||
|
||||
if mainDebugger._finishDebuggingSession:
|
||||
if main_debugger._finishDebuggingSession:
|
||||
return None
|
||||
|
||||
if getattr(thread, 'pydev_do_not_trace', None):
|
||||
return None
|
||||
|
||||
if event == 'call':
|
||||
sendSignatureCallTrace(mainDebugger, frame, filename)
|
||||
if event == 'call' and main_debugger.signature_factory:
|
||||
sendSignatureCallTrace(main_debugger, frame, filename)
|
||||
|
||||
if event not in ('line', 'call', 'return'):
|
||||
if event == 'exception':
|
||||
(flag, frame) = self.shouldStopOnException(frame, event, arg)
|
||||
is_exception_event = event == 'exception'
|
||||
has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.django_exception_break
|
||||
|
||||
if is_exception_event:
|
||||
if has_exception_breakpoints:
|
||||
flag, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
if flag:
|
||||
self.handle_exception(frame, event, arg)
|
||||
return self.trace_dispatch
|
||||
else:
|
||||
#I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
|
||||
return None
|
||||
|
||||
if event is not 'exception':
|
||||
breakpoints_for_file = mainDebugger.breakpoints.get(filename)
|
||||
elif event not in ('line', 'call', 'return'):
|
||||
#I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
|
||||
return None
|
||||
|
||||
stop_frame = info.pydev_step_stop
|
||||
step_cmd = info.pydev_step_cmd
|
||||
|
||||
if is_exception_event:
|
||||
breakpoints_for_file = None
|
||||
else:
|
||||
# If we are in single step mode and something causes us to exit the current frame, we need to make sure we break
|
||||
# eventually. Force the step mode to step into and the step stop frame to None.
|
||||
# I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user
|
||||
# to make a step in or step over at that location).
|
||||
# Note: this is especially troublesome when we're skipping code with the
|
||||
# @DontTrace comment.
|
||||
if stop_frame is frame and event in ('return', 'exception') and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
|
||||
info.pydev_step_cmd = CMD_STEP_INTO
|
||||
info.pydev_step_stop = None
|
||||
|
||||
breakpoints_for_file = main_debugger.breakpoints.get(filename)
|
||||
|
||||
can_skip = False
|
||||
|
||||
@@ -118,10 +286,11 @@ class PyDBFrame:
|
||||
#we can skip if:
|
||||
#- we have no stop marked
|
||||
#- we should make a step return/step over and we're not in the current frame
|
||||
can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\
|
||||
or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame)
|
||||
can_skip = (step_cmd is None and stop_frame is None)\
|
||||
or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)
|
||||
|
||||
if mainDebugger.django_breakpoints:
|
||||
check_stop_on_django_render_call = main_debugger.django_breakpoints and self._is_django_render_call(frame)
|
||||
if check_stop_on_django_render_call:
|
||||
can_skip = False
|
||||
|
||||
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
|
||||
@@ -130,7 +299,7 @@ class PyDBFrame:
|
||||
#so, that's why the additional checks are there.
|
||||
if not breakpoints_for_file:
|
||||
if can_skip:
|
||||
if mainDebugger.always_exception_set or mainDebugger.django_exception_break:
|
||||
if has_exception_breakpoints:
|
||||
return self.trace_exception
|
||||
else:
|
||||
return None
|
||||
@@ -143,17 +312,18 @@ class PyDBFrame:
|
||||
if curr_func_name in ('?', '<module>'):
|
||||
curr_func_name = ''
|
||||
|
||||
for breakpoint in breakpoints_for_file.values(): #jython does not support itervalues()
|
||||
for breakpoint in DictIterValues(breakpoints_for_file): #jython does not support itervalues()
|
||||
#will match either global or some function
|
||||
if breakpoint.func_name in ('None', curr_func_name):
|
||||
break
|
||||
|
||||
else: # if we had some break, it won't get here (so, that's a context that we want to skip)
|
||||
if can_skip:
|
||||
#print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd
|
||||
return None
|
||||
else:
|
||||
breakpoints_for_file = None
|
||||
if has_exception_breakpoints:
|
||||
return self.trace_exception
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
#We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame
|
||||
#print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event
|
||||
@@ -163,33 +333,63 @@ class PyDBFrame:
|
||||
|
||||
|
||||
flag = False
|
||||
if event == 'call' and info.pydev_state != STATE_SUSPEND and mainDebugger.django_breakpoints \
|
||||
and is_django_render_call(frame):
|
||||
(flag, frame) = self.shouldStopOnDjangoBreak(frame, event, arg)
|
||||
if event == 'call' and info.pydev_state != STATE_SUSPEND and check_stop_on_django_render_call:
|
||||
flag, frame = self.should_stop_on_django_breakpoint(frame, event, arg)
|
||||
|
||||
#return is not taken into account for breakpoint hit because we'd have a double-hit in this case
|
||||
#(one for the line and the other for the return).
|
||||
|
||||
if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\
|
||||
and DictContains(breakpoints_for_file, line):
|
||||
and DictContains(breakpoints_for_file, line):
|
||||
#ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint
|
||||
# lets do the conditional stuff here
|
||||
breakpoint = breakpoints_for_file[line]
|
||||
|
||||
stop = True
|
||||
if info.pydev_step_cmd == CMD_STEP_OVER and info.pydev_step_stop is frame and event in ('line', 'return'):
|
||||
if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'):
|
||||
stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
|
||||
else:
|
||||
if breakpoint.condition is not None:
|
||||
condition = breakpoint.condition
|
||||
if condition is not None:
|
||||
try:
|
||||
val = eval(breakpoint.condition, frame.f_globals, frame.f_locals)
|
||||
val = eval(condition, frame.f_globals, frame.f_locals)
|
||||
if not val:
|
||||
return self.trace_dispatch
|
||||
|
||||
except:
|
||||
pydev_log.info('Error while evaluating condition \'%s\': %s\n' % (breakpoint.condition, sys.exc_info()[1]))
|
||||
if type(condition) != type(''):
|
||||
if hasattr(condition, 'encode'):
|
||||
condition = condition.encode('utf-8')
|
||||
|
||||
return self.trace_dispatch
|
||||
msg = 'Error while evaluating expression: %s\n' % (condition,)
|
||||
sys.stderr.write(msg)
|
||||
traceback.print_exc()
|
||||
if not main_debugger.suspend_on_breakpoint_exception:
|
||||
return self.trace_dispatch
|
||||
else:
|
||||
stop = True
|
||||
try:
|
||||
additional_info = None
|
||||
try:
|
||||
additional_info = thread.additionalInfo
|
||||
except AttributeError:
|
||||
pass #that's ok, no info currently set
|
||||
|
||||
if additional_info is not None:
|
||||
# add exception_type and stacktrace into thread additional info
|
||||
etype, value, tb = sys.exc_info()
|
||||
try:
|
||||
error = ''.join(traceback.format_exception_only(etype, value))
|
||||
stack = traceback.extract_stack(f=tb.tb_frame.f_back)
|
||||
|
||||
# On self.setSuspend(thread, CMD_SET_BREAK) this info will be
|
||||
# sent to the client.
|
||||
additional_info.conditional_breakpoint_exception = \
|
||||
('Condition:\n' + condition + '\n\nError:\n' + error, stack)
|
||||
finally:
|
||||
etype, value, tb = None, None, None
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if breakpoint.expression is not None:
|
||||
try:
|
||||
@@ -216,30 +416,45 @@ class PyDBFrame:
|
||||
#step handling. We stop when we hit the right frame
|
||||
try:
|
||||
django_stop = False
|
||||
if info.pydev_step_cmd == CMD_STEP_INTO:
|
||||
|
||||
should_skip = False
|
||||
if pydevd_dont_trace.should_trace_hook is not None:
|
||||
if not hasattr(self, 'should_skip'):
|
||||
# I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times).
|
||||
# Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code
|
||||
# Which will be handled by this frame is read-only, so, we can cache it safely.
|
||||
should_skip = self.should_skip = not pydevd_dont_trace.should_trace_hook(frame, filename)
|
||||
else:
|
||||
should_skip = self.should_skip
|
||||
|
||||
if should_skip:
|
||||
stop = False
|
||||
|
||||
elif step_cmd == CMD_STEP_INTO:
|
||||
stop = event in ('line', 'return')
|
||||
|
||||
if is_django_suspended(thread):
|
||||
#django_stop = event == 'call' and is_django_render_call(frame)
|
||||
stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame)
|
||||
if stop:
|
||||
info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame
|
||||
|
||||
elif info.pydev_step_cmd == CMD_STEP_OVER:
|
||||
elif step_cmd == CMD_STEP_OVER:
|
||||
if is_django_suspended(thread):
|
||||
django_stop = event == 'call' and is_django_render_call(frame)
|
||||
django_stop = event == 'call' and self._is_django_render_call(frame)
|
||||
|
||||
stop = False
|
||||
else:
|
||||
if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back):
|
||||
#we return to Django suspend mode and should not stop before django rendering frame
|
||||
info.pydev_step_stop = info.pydev_django_resolve_frame
|
||||
stop_frame = info.pydev_step_stop = info.pydev_django_resolve_frame
|
||||
info.pydev_django_resolve_frame = None
|
||||
thread.additionalInfo.suspend_type = DJANGO_SUSPEND
|
||||
|
||||
|
||||
stop = info.pydev_step_stop is frame and event in ('line', 'return')
|
||||
stop = stop_frame is frame and event in ('line', 'return')
|
||||
|
||||
elif info.pydev_step_cmd == CMD_SMART_STEP_INTO:
|
||||
elif step_cmd == CMD_SMART_STEP_INTO:
|
||||
stop = False
|
||||
if info.pydev_smart_step_stop is frame:
|
||||
info.pydev_func_name = None
|
||||
@@ -253,12 +468,12 @@ class PyDBFrame:
|
||||
curr_func_name = ''
|
||||
|
||||
if curr_func_name == info.pydev_func_name:
|
||||
stop = True
|
||||
stop = True
|
||||
|
||||
elif info.pydev_step_cmd == CMD_STEP_RETURN:
|
||||
stop = event == 'return' and info.pydev_step_stop is frame
|
||||
elif step_cmd == CMD_STEP_RETURN:
|
||||
stop = event == 'return' and stop_frame is frame
|
||||
|
||||
elif info.pydev_step_cmd == CMD_RUN_TO_LINE or info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT:
|
||||
elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
|
||||
stop = False
|
||||
|
||||
if event == 'line' or event == 'exception':
|
||||
@@ -286,13 +501,13 @@ class PyDBFrame:
|
||||
stop = False
|
||||
|
||||
if django_stop:
|
||||
frame = suspend_django(self, mainDebugger, thread, frame)
|
||||
frame = suspend_django(self, main_debugger, thread, frame)
|
||||
if frame:
|
||||
self.doWaitSuspend(thread, frame, event, arg)
|
||||
elif stop:
|
||||
#event is always == line or return at this point
|
||||
if event == 'line':
|
||||
self.setSuspend(thread, info.pydev_step_cmd)
|
||||
self.setSuspend(thread, step_cmd)
|
||||
self.doWaitSuspend(thread, frame, event, arg)
|
||||
else: #return event
|
||||
back = frame.f_back
|
||||
@@ -300,12 +515,18 @@ class PyDBFrame:
|
||||
#When we get to the pydevd run function, the debugging has actually finished for the main thread
|
||||
#(note that it can still go on for other threads, but for this one, we just make it finish)
|
||||
#So, just setting it to None should be OK
|
||||
if basename(back.f_code.co_filename) == 'pydevd.py' and back.f_code.co_name == 'run':
|
||||
base = basename(back.f_code.co_filename)
|
||||
if base == 'pydevd.py' and back.f_code.co_name == 'run':
|
||||
back = None
|
||||
|
||||
elif base == 'pydevd_traceproperty.py':
|
||||
# We dont want to trace the return event of pydevd_traceproperty (custom property for debugging)
|
||||
#if we're in a return, we want it to appear to the user in the previous frame!
|
||||
return None
|
||||
|
||||
if back is not None:
|
||||
#if we're in a return, we want it to appear to the user in the previous frame!
|
||||
self.setSuspend(thread, info.pydev_step_cmd)
|
||||
self.setSuspend(thread, step_cmd)
|
||||
self.doWaitSuspend(thread, back, event, arg)
|
||||
else:
|
||||
#in jython we may not have a back frame
|
||||
@@ -320,7 +541,7 @@ class PyDBFrame:
|
||||
|
||||
#if we are quitting, let's stop the tracing
|
||||
retVal = None
|
||||
if not mainDebugger.quitting:
|
||||
if not main_debugger.quitting:
|
||||
retVal = self.trace_dispatch
|
||||
|
||||
return retVal
|
||||
@@ -339,24 +560,36 @@ class PyDBFrame:
|
||||
sys.exc_clear() #don't keep the traceback
|
||||
pass #ok, psyco not available
|
||||
|
||||
def shouldStopOnDjangoBreak(self, frame, event, arg):
|
||||
mainDebugger, filename, info, thread = self._args
|
||||
def should_stop_on_django_breakpoint(self, frame, event, arg):
|
||||
mainDebugger = self._args[0]
|
||||
thread = self._args[3]
|
||||
flag = False
|
||||
filename = get_template_file_name(frame)
|
||||
pydev_log.debug("Django is rendering a template: %s\n" % filename)
|
||||
django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename)
|
||||
template_frame_file = get_template_file_name(frame)
|
||||
|
||||
#pydev_log.debug("Django is rendering a template: %s\n" % template_frame_file)
|
||||
|
||||
django_breakpoints_for_file = mainDebugger.django_breakpoints.get(template_frame_file)
|
||||
if django_breakpoints_for_file:
|
||||
pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
|
||||
template_line = get_template_line(frame)
|
||||
pydev_log.debug("Tracing template line: %d\n" % template_line)
|
||||
|
||||
if DictContains(django_breakpoints_for_file, template_line):
|
||||
django_breakpoint = django_breakpoints_for_file[template_line]
|
||||
#pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file)
|
||||
|
||||
template_frame_line = get_template_line(frame, template_frame_file)
|
||||
|
||||
#pydev_log.debug("Tracing template line: %d\n" % template_frame_line)
|
||||
|
||||
if DictContains(django_breakpoints_for_file, template_frame_line):
|
||||
django_breakpoint = django_breakpoints_for_file[template_frame_line]
|
||||
|
||||
if django_breakpoint.is_triggered(template_frame_file, template_frame_line):
|
||||
|
||||
#pydev_log.debug("Breakpoint is triggered.\n")
|
||||
|
||||
if django_breakpoint.is_triggered(frame):
|
||||
pydev_log.debug("Breakpoint is triggered.\n")
|
||||
flag = True
|
||||
new_frame = DjangoTemplateFrame(frame)
|
||||
new_frame = DjangoTemplateFrame(
|
||||
frame,
|
||||
template_frame_file=template_frame_file,
|
||||
template_frame_line=template_frame_line,
|
||||
)
|
||||
|
||||
if django_breakpoint.condition is not None:
|
||||
try:
|
||||
@@ -379,7 +612,7 @@ class PyDBFrame:
|
||||
thread.additionalInfo.message = val
|
||||
if flag:
|
||||
frame = suspend_django(self, mainDebugger, thread, frame)
|
||||
return (flag, frame)
|
||||
return flag, frame
|
||||
|
||||
def add_exception_to_frame(frame, exception_info):
|
||||
frame.f_locals['__exception__'] = exception_info
|
||||
@@ -24,10 +24,10 @@ class IORedirector:
|
||||
r.flush()
|
||||
|
||||
def __getattr__(self, name):
|
||||
for r in self._redirectTo:
|
||||
if hasattr(r, name):
|
||||
return r.__getattribute__(name)
|
||||
raise AttributeError(name)
|
||||
for r in self._redirectTo:
|
||||
if hasattr(r, name):
|
||||
return r.__getattribute__(name)
|
||||
raise AttributeError(name)
|
||||
|
||||
class IOBuf:
|
||||
'''This class works as a replacement for stdio and stderr.
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
from pydevd_constants import DictContains
|
||||
import sys
|
||||
import pydevd_vars
|
||||
from os.path import basename
|
||||
import traceback
|
||||
try:
|
||||
from urllib import quote, quote_plus, unquote, unquote_plus
|
||||
except:
|
||||
from urllib.parse import quote, quote_plus, unquote, unquote_plus #@Reimport @UnresolvedImport
|
||||
|
||||
#===================================================================================================
|
||||
# print_var_node
|
||||
#===================================================================================================
|
||||
def print_var_node(xml_node, stream):
|
||||
name = xml_node.getAttribute('name')
|
||||
value = xml_node.getAttribute('value')
|
||||
val_type = xml_node.getAttribute('type')
|
||||
|
||||
found_as = xml_node.getAttribute('found_as')
|
||||
stream.write('Name: ')
|
||||
stream.write(unquote_plus(name))
|
||||
stream.write(', Value: ')
|
||||
stream.write(unquote_plus(value))
|
||||
stream.write(', Type: ')
|
||||
stream.write(unquote_plus(val_type))
|
||||
if found_as:
|
||||
stream.write(', Found as: %s' % (unquote_plus(found_as),))
|
||||
stream.write('\n')
|
||||
|
||||
#===================================================================================================
|
||||
# print_referrers
|
||||
#===================================================================================================
|
||||
def print_referrers(obj, stream=None):
|
||||
if stream is None:
|
||||
stream = sys.stdout
|
||||
result = get_referrer_info(obj)
|
||||
from xml.dom.minidom import parseString
|
||||
dom = parseString(result)
|
||||
|
||||
xml = dom.getElementsByTagName('xml')[0]
|
||||
for node in xml.childNodes:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
continue
|
||||
|
||||
if node.localName == 'for':
|
||||
stream.write('Searching references for: ')
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.TEXT_NODE:
|
||||
continue
|
||||
print_var_node(child, stream)
|
||||
|
||||
elif node.localName == 'var':
|
||||
stream.write('Referrer found: ')
|
||||
print_var_node(node, stream)
|
||||
|
||||
else:
|
||||
sys.stderr.write('Unhandled node: %s\n' % (node,))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
#===================================================================================================
|
||||
# get_referrer_info
|
||||
#===================================================================================================
|
||||
def get_referrer_info(searched_obj):
|
||||
DEBUG = 0
|
||||
if DEBUG:
|
||||
sys.stderr.write('Getting referrers info.\n')
|
||||
try:
|
||||
try:
|
||||
if searched_obj is None:
|
||||
ret = ['<xml>\n']
|
||||
|
||||
ret.append('<for>\n')
|
||||
ret.append(pydevd_vars.varToXML(
|
||||
searched_obj,
|
||||
'Skipping getting referrers for None',
|
||||
additionalInXml=' id="%s"' % (id(searched_obj),)))
|
||||
ret.append('</for>\n')
|
||||
ret.append('</xml>')
|
||||
ret = ''.join(ret)
|
||||
return ret
|
||||
|
||||
obj_id = id(searched_obj)
|
||||
|
||||
try:
|
||||
if DEBUG:
|
||||
sys.stderr.write('Getting referrers...\n')
|
||||
import gc
|
||||
referrers = gc.get_referrers(searched_obj)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
ret = ['<xml>\n']
|
||||
|
||||
ret.append('<for>\n')
|
||||
ret.append(pydevd_vars.varToXML(
|
||||
searched_obj,
|
||||
'Exception raised while trying to get_referrers.',
|
||||
additionalInXml=' id="%s"' % (id(searched_obj),)))
|
||||
ret.append('</for>\n')
|
||||
ret.append('</xml>')
|
||||
ret = ''.join(ret)
|
||||
return ret
|
||||
|
||||
if DEBUG:
|
||||
sys.stderr.write('Found %s referrers.\n' % (len(referrers),))
|
||||
|
||||
curr_frame = sys._getframe()
|
||||
frame_type = type(curr_frame)
|
||||
|
||||
#Ignore this frame and any caller frame of this frame
|
||||
|
||||
ignore_frames = {} #Should be a set, but it's not available on all python versions.
|
||||
while curr_frame is not None:
|
||||
if basename(curr_frame.f_code.co_filename).startswith('pydev'):
|
||||
ignore_frames[curr_frame] = 1
|
||||
curr_frame = curr_frame.f_back
|
||||
|
||||
|
||||
ret = ['<xml>\n']
|
||||
|
||||
ret.append('<for>\n')
|
||||
if DEBUG:
|
||||
sys.stderr.write('Searching Referrers of obj with id="%s"\n' % (obj_id,))
|
||||
|
||||
ret.append(pydevd_vars.varToXML(
|
||||
searched_obj,
|
||||
'Referrers of obj with id="%s"' % (obj_id,)))
|
||||
ret.append('</for>\n')
|
||||
|
||||
all_objects = None
|
||||
|
||||
for r in referrers:
|
||||
try:
|
||||
if DictContains(ignore_frames, r):
|
||||
continue #Skip the references we may add ourselves
|
||||
except:
|
||||
pass #Ok: unhashable type checked...
|
||||
|
||||
if r is referrers:
|
||||
continue
|
||||
|
||||
r_type = type(r)
|
||||
r_id = str(id(r))
|
||||
|
||||
representation = str(r_type)
|
||||
|
||||
found_as = ''
|
||||
if r_type == frame_type:
|
||||
if DEBUG:
|
||||
sys.stderr.write('Found frame referrer: %r\n' % (r,))
|
||||
for key, val in r.f_locals.items():
|
||||
if val is searched_obj:
|
||||
found_as = key
|
||||
break
|
||||
|
||||
elif r_type == dict:
|
||||
if DEBUG:
|
||||
sys.stderr.write('Found dict referrer: %r\n' % (r,))
|
||||
|
||||
# Try to check if it's a value in the dict (and under which key it was found)
|
||||
for key, val in r.items():
|
||||
if val is searched_obj:
|
||||
found_as = key
|
||||
if DEBUG:
|
||||
sys.stderr.write(' Found as %r in dict\n' % (found_as,))
|
||||
break
|
||||
|
||||
#Ok, there's one annoying thing: many times we find it in a dict from an instance,
|
||||
#but with this we don't directly have the class, only the dict, so, to workaround that
|
||||
#we iterate over all reachable objects ad check if one of those has the given dict.
|
||||
if all_objects is None:
|
||||
all_objects = gc.get_objects()
|
||||
|
||||
for x in all_objects:
|
||||
try:
|
||||
if getattr(x, '__dict__', None) is r:
|
||||
r = x
|
||||
r_type = type(x)
|
||||
r_id = str(id(r))
|
||||
representation = str(r_type)
|
||||
break
|
||||
except:
|
||||
pass #Just ignore any error here (i.e.: ReferenceError, etc.)
|
||||
|
||||
elif r_type in (tuple, list):
|
||||
if DEBUG:
|
||||
sys.stderr.write('Found tuple referrer: %r\n' % (r,))
|
||||
|
||||
#Don't use enumerate() because not all Python versions have it.
|
||||
i = 0
|
||||
for x in r:
|
||||
if x is searched_obj:
|
||||
found_as = '%s[%s]' % (r_type.__name__, i)
|
||||
if DEBUG:
|
||||
sys.stderr.write(' Found as %s in tuple: \n' % (found_as,))
|
||||
break
|
||||
i += 1
|
||||
|
||||
if found_as:
|
||||
found_as = ' found_as="%s"' % (pydevd_vars.makeValidXmlValue(found_as),)
|
||||
|
||||
ret.append(pydevd_vars.varToXML(
|
||||
r,
|
||||
representation,
|
||||
additionalInXml=' id="%s"%s' % (r_id, found_as)))
|
||||
finally:
|
||||
if DEBUG:
|
||||
sys.stderr.write('Done searching for references.\n')
|
||||
|
||||
#If we have any exceptions, don't keep dangling references from this frame to any of our objects.
|
||||
all_objects = None
|
||||
referrers = None
|
||||
searched_obj = None
|
||||
r = None
|
||||
x = None
|
||||
key = None
|
||||
val = None
|
||||
curr_frame = None
|
||||
ignore_frames = None
|
||||
except:
|
||||
traceback.print_exc()
|
||||
ret = ['<xml>\n']
|
||||
|
||||
ret.append('<for>\n')
|
||||
ret.append(pydevd_vars.varToXML(
|
||||
searched_obj,
|
||||
'Error getting referrers for:',
|
||||
additionalInXml=' id="%s"' % (id(searched_obj),)))
|
||||
ret.append('</for>\n')
|
||||
ret.append('</xml>')
|
||||
ret = ''.join(ret)
|
||||
return ret
|
||||
|
||||
ret.append('</xml>')
|
||||
ret = ''.join(ret)
|
||||
return ret
|
||||
|
||||
@@ -13,6 +13,7 @@ except:
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
import pydevd_constants
|
||||
from pydevd_constants import DictIterItems, xrange, izip
|
||||
|
||||
|
||||
MAX_ITEMS_TO_HANDLE = 500
|
||||
@@ -58,7 +59,7 @@ except:
|
||||
class AbstractResolver:
|
||||
'''
|
||||
This class exists only for documentation purposes to explain how to create a resolver.
|
||||
|
||||
|
||||
Some examples on how to resolve things:
|
||||
- list: getDictionary could return a dict with index->item and use the index to resolve it later
|
||||
- set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
|
||||
@@ -69,7 +70,7 @@ class AbstractResolver:
|
||||
'''
|
||||
In this method, we'll resolve some child item given the string representation of the item in the key
|
||||
representing the previously asked dictionary.
|
||||
|
||||
|
||||
@param var: this is the actual variable to be resolved.
|
||||
@param attribute: this is the string representation of a key previously returned in getDictionary.
|
||||
'''
|
||||
@@ -78,7 +79,7 @@ class AbstractResolver:
|
||||
def getDictionary(self, var):
|
||||
'''
|
||||
@param var: this is the variable that should have its children gotten.
|
||||
|
||||
|
||||
@return: a dictionary where each pair key, value should be shown to the user as children items
|
||||
in the variables view for the given var.
|
||||
'''
|
||||
@@ -128,12 +129,12 @@ class DefaultResolver:
|
||||
|
||||
declaredMethods = obj.getDeclaredMethods()
|
||||
declaredFields = obj.getDeclaredFields()
|
||||
for i in range(len(declaredMethods)):
|
||||
for i in xrange(len(declaredMethods)):
|
||||
name = declaredMethods[i].getName()
|
||||
ret[name] = declaredMethods[i].toString()
|
||||
found.put(name, 1)
|
||||
|
||||
for i in range(len(declaredFields)):
|
||||
for i in xrange(len(declaredFields)):
|
||||
name = declaredFields[i].getName()
|
||||
found.put(name, 1)
|
||||
#if declaredFields[i].isAccessible():
|
||||
@@ -145,7 +146,7 @@ class DefaultResolver:
|
||||
ret[name] = declaredFields[i].toString()
|
||||
|
||||
#this simple dir does not always get all the info, that's why we have the part before
|
||||
#(e.g.: if we do a dir on String, some methods that are from other interfaces such as
|
||||
#(e.g.: if we do a dir on String, some methods that are from other interfaces such as
|
||||
#charAt don't appear)
|
||||
try:
|
||||
d = dir(original)
|
||||
@@ -169,8 +170,8 @@ class DefaultResolver:
|
||||
names = var.__members__
|
||||
d = {}
|
||||
|
||||
#Be aware that the order in which the filters are applied attempts to
|
||||
#optimize the operation by removing as many items as possible in the
|
||||
#Be aware that the order in which the filters are applied attempts to
|
||||
#optimize the operation by removing as many items as possible in the
|
||||
#first filters, leaving fewer items for later filters
|
||||
|
||||
if filterBuiltIn or filterFunction:
|
||||
@@ -212,18 +213,18 @@ class DefaultResolver:
|
||||
class DictResolver:
|
||||
|
||||
def resolve(self, dict, key):
|
||||
if key == '__len__':
|
||||
if key in ('__len__', TOO_LARGE_ATTR):
|
||||
return None
|
||||
|
||||
if '(' not in key:
|
||||
#we have to treat that because the dict resolver is also used to directly resolve the global and local
|
||||
#scopes (which already have the items directly)
|
||||
#scopes (which already have the items directly)
|
||||
return dict[key]
|
||||
|
||||
#ok, we have to iterate over the items to find the one that matches the id, because that's the only way
|
||||
#to actually find the reference from the string we have before.
|
||||
expected_id = int(key.split('(')[-1][:-1])
|
||||
for key, val in dict.items():
|
||||
for key, val in DictIterItems(dict):
|
||||
if id(key) == expected_id:
|
||||
return val
|
||||
|
||||
@@ -241,10 +242,15 @@ class DictResolver:
|
||||
def getDictionary(self, dict):
|
||||
ret = {}
|
||||
|
||||
for key, val in dict.items():
|
||||
i = 0
|
||||
for key, val in DictIterItems(dict):
|
||||
i += 1
|
||||
#we need to add the id because otherwise we cannot find the real object to get its contents later on.
|
||||
key = '%s (%s)' % (self.keyStr(key), id(key))
|
||||
ret[key] = val
|
||||
if i > MAX_ITEMS_TO_HANDLE:
|
||||
ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
ret['__len__'] = len(dict)
|
||||
return ret
|
||||
@@ -261,7 +267,7 @@ class TupleResolver: #to enumerate tuples and lists
|
||||
@param var: that's the original attribute
|
||||
@param attribute: that's the key passed in the dict (as a string)
|
||||
'''
|
||||
if attribute == '__len__' or attribute == TOO_LARGE_ATTR:
|
||||
if attribute in ('__len__', TOO_LARGE_ATTR):
|
||||
return None
|
||||
return var[int(attribute)]
|
||||
|
||||
@@ -270,12 +276,12 @@ class TupleResolver: #to enumerate tuples and lists
|
||||
# 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'
|
||||
|
||||
|
||||
for i, item in zip(range(l), var):
|
||||
|
||||
|
||||
for i, item in izip(xrange(l), var):
|
||||
d[ format % i ] = item
|
||||
else:
|
||||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
@@ -293,7 +299,7 @@ class SetResolver:
|
||||
'''
|
||||
|
||||
def resolve(self, var, attribute):
|
||||
if attribute == '__len__':
|
||||
if attribute in ('__len__', TOO_LARGE_ATTR):
|
||||
return None
|
||||
|
||||
attribute = int(attribute)
|
||||
@@ -305,8 +311,16 @@ class SetResolver:
|
||||
|
||||
def getDictionary(self, var):
|
||||
d = {}
|
||||
i = 0
|
||||
for item in var:
|
||||
d[ id(item) ] = item
|
||||
i+= 1
|
||||
d[id(item)] = item
|
||||
|
||||
if i > MAX_ITEMS_TO_HANDLE:
|
||||
d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
|
||||
break
|
||||
|
||||
|
||||
d['__len__'] = len(var)
|
||||
return d
|
||||
|
||||
@@ -325,7 +339,7 @@ class InstanceResolver:
|
||||
ret = {}
|
||||
|
||||
declaredFields = obj.__class__.getDeclaredFields()
|
||||
for i in range(len(declaredFields)):
|
||||
for i in xrange(len(declaredFields)):
|
||||
name = declaredFields[i].getName()
|
||||
try:
|
||||
declaredFields[i].setAccessible(True)
|
||||
@@ -352,7 +366,7 @@ class JyArrayResolver:
|
||||
def getDictionary(self, obj):
|
||||
ret = {}
|
||||
|
||||
for i in range(len(obj)):
|
||||
for i in xrange(len(obj)):
|
||||
ret[ i ] = obj[i]
|
||||
|
||||
ret['__len__'] = len(obj)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Utility for saving locals.
|
||||
"""
|
||||
import sys
|
||||
import pydevd_vars
|
||||
|
||||
def is_save_locals_available():
|
||||
try:
|
||||
@@ -12,7 +13,7 @@ def is_save_locals_available():
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
except:
|
||||
@@ -22,7 +23,7 @@ def is_save_locals_available():
|
||||
func = ctypes.pythonapi.PyFrame_LocalsToFast
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def save_locals(frame):
|
||||
@@ -32,6 +33,10 @@ def save_locals(frame):
|
||||
Note: the 'save_locals' branch had a different approach wrapping the frame (much more code, but it gives ideas
|
||||
on how to save things partially, not the 'whole' locals).
|
||||
"""
|
||||
if not isinstance(frame, pydevd_vars.frame_type):
|
||||
# Fix exception when changing Django variable (receiving DjangoTemplateFrame)
|
||||
return
|
||||
|
||||
try:
|
||||
if '__pypy__' in sys.builtin_module_names:
|
||||
import __pypy__
|
||||
@@ -40,7 +45,7 @@ def save_locals(frame):
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
|
||||
@@ -6,6 +6,7 @@ trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue
|
||||
import gc
|
||||
from pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand
|
||||
import pydevd_vars
|
||||
from pydevd_constants import xrange
|
||||
|
||||
class Signature(object):
|
||||
def __init__(self, file, name):
|
||||
@@ -43,7 +44,7 @@ class SignatureFactory(object):
|
||||
locals = frame.f_locals
|
||||
filename, modulename, funcname = self.file_module_function_of(frame)
|
||||
res = Signature(filename, funcname)
|
||||
for i in range(0, code.co_argcount):
|
||||
for i in xrange(0, code.co_argcount):
|
||||
name = code.co_varnames[i]
|
||||
tp = type(locals[name])
|
||||
class_name = tp.__name__
|
||||
@@ -123,9 +124,8 @@ def create_signature_message(signature):
|
||||
return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText)
|
||||
|
||||
def sendSignatureCallTrace(dbg, frame, filename):
|
||||
if dbg.signature_factory:
|
||||
if dbg.signature_factory.is_in_scope(filename):
|
||||
dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame)))
|
||||
if dbg.signature_factory.is_in_scope(filename):
|
||||
dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame)))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from pydevd_comm import GetGlobalDebugger
|
||||
import weakref
|
||||
from pydevd_file_utils import GetFilenameAndBase
|
||||
from pydevd import DONT_TRACE
|
||||
from pydevd_constants import DictItems
|
||||
|
||||
|
||||
# Used so that we don't loose the id (because we'll remove when it's not alive and would generate a new id for the
|
||||
@@ -195,7 +196,7 @@ def _schedule_callback(prev, next):
|
||||
register_tasklet_info(prev)
|
||||
|
||||
try:
|
||||
for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()): # Make sure it's a copy!
|
||||
for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info): # Make sure it's a copy!
|
||||
tasklet = tasklet_ref()
|
||||
if tasklet is None or not tasklet.alive:
|
||||
# Garbage-collected already!
|
||||
@@ -269,7 +270,7 @@ if not hasattr(stackless.tasklet, "trace_function"):
|
||||
register_tasklet_info(prev)
|
||||
|
||||
try:
|
||||
for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()): # Make sure it's a copy!
|
||||
for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info): # Make sure it's a copy!
|
||||
tasklet = tasklet_ref()
|
||||
if tasklet is None or not tasklet.alive:
|
||||
# Garbage-collected already!
|
||||
@@ -388,7 +389,7 @@ def patch_stackless():
|
||||
_application_set_schedule_callback = callable
|
||||
return old
|
||||
|
||||
def get_schedule_callback(callable):
|
||||
def get_schedule_callback():
|
||||
global _application_set_schedule_callback
|
||||
return _application_set_schedule_callback
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
'''For debug purpose we are replacing actual builtin property by the debug property
|
||||
'''
|
||||
from pydevd_comm import GetGlobalDebugger
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
import pydevd_tracing
|
||||
|
||||
#=======================================================================================================================
|
||||
# replace_builtin_property
|
||||
#=======================================================================================================================
|
||||
def replace_builtin_property(new_property=None):
|
||||
if new_property is None:
|
||||
new_property = DebugProperty
|
||||
original = property
|
||||
if not IS_PY3K:
|
||||
try:
|
||||
import __builtin__
|
||||
__builtin__.__dict__['property'] = new_property
|
||||
except:
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
else:
|
||||
try:
|
||||
import builtins #Python 3.0 does not have the __builtin__ module @UnresolvedImport
|
||||
builtins.__dict__['property'] = new_property
|
||||
except:
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL:
|
||||
import traceback;traceback.print_exc() #@Reimport
|
||||
return original
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DebugProperty
|
||||
#=======================================================================================================================
|
||||
class DebugProperty(object):
|
||||
"""A custom property which allows python property to get
|
||||
controlled by the debugger and selectively disable/re-enable
|
||||
the tracing.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
||||
self.fget = fget
|
||||
self.fset = fset
|
||||
self.fdel = fdel
|
||||
self.__doc__ = doc
|
||||
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
global_debugger = GetGlobalDebugger()
|
||||
try:
|
||||
if global_debugger is not None and global_debugger.disable_property_getter_trace:
|
||||
pydevd_tracing.SetTrace(None)
|
||||
if self.fget is None:
|
||||
raise AttributeError("unreadable attribute")
|
||||
return self.fget(obj)
|
||||
finally:
|
||||
if global_debugger is not None:
|
||||
pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
|
||||
|
||||
|
||||
def __set__(self, obj, value):
|
||||
global_debugger = GetGlobalDebugger()
|
||||
try:
|
||||
if global_debugger is not None and global_debugger.disable_property_setter_trace:
|
||||
pydevd_tracing.SetTrace(None)
|
||||
if self.fset is None:
|
||||
raise AttributeError("can't set attribute")
|
||||
self.fset(obj, value)
|
||||
finally:
|
||||
if global_debugger is not None:
|
||||
pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
|
||||
|
||||
|
||||
def __delete__(self, obj):
|
||||
global_debugger = GetGlobalDebugger()
|
||||
try:
|
||||
if global_debugger is not None and global_debugger.disable_property_deleter_trace:
|
||||
pydevd_tracing.SetTrace(None)
|
||||
if self.fdel is None:
|
||||
raise AttributeError("can't delete attribute")
|
||||
self.fdel(obj)
|
||||
finally:
|
||||
if global_debugger is not None:
|
||||
pydevd_tracing.SetTrace(global_debugger.trace_dispatch)
|
||||
|
||||
|
||||
def getter(self, fget):
|
||||
"""Overriding getter decorator for the property
|
||||
"""
|
||||
self.fget = fget
|
||||
return self
|
||||
|
||||
|
||||
def setter(self, fset):
|
||||
"""Overriding setter decorator for the property
|
||||
"""
|
||||
self.fset = fset
|
||||
return self
|
||||
|
||||
|
||||
def deleter(self, fdel):
|
||||
"""Overriding deleter decorator for the property
|
||||
"""
|
||||
self.fdel = fdel
|
||||
return self
|
||||
|
||||
@@ -65,7 +65,7 @@ def _InternalSetTrace(tracing_func):
|
||||
sys.stderr.flush()
|
||||
|
||||
if TracingFunctionHolder._original_tracing:
|
||||
TracingFunctionHolder._original_tracing(tracing_func)
|
||||
TracingFunctionHolder._original_tracing(tracing_func)
|
||||
|
||||
def SetTrace(tracing_func):
|
||||
if TracingFunctionHolder._original_tracing is None:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"""
|
||||
import pickle
|
||||
from django_frame import DjangoTemplateFrame
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
from types import * #@UnusedWildImport
|
||||
|
||||
from pydevd_custom_frames import getCustomFrame
|
||||
@@ -19,16 +18,15 @@ if USE_LIB_COPY:
|
||||
import _pydev_threading as threading
|
||||
else:
|
||||
import threading
|
||||
import pydevd_resolver
|
||||
import traceback
|
||||
import pydevd_save_locals
|
||||
from pydev_imports import Exec, quote, execfile
|
||||
from pydev_imports import Exec, execfile
|
||||
|
||||
try:
|
||||
import types
|
||||
frame_type = types.FrameType
|
||||
except:
|
||||
frame_type = None
|
||||
frame_type = type(sys._getframe())
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------- defining true and false for earlier versions
|
||||
@@ -37,25 +35,14 @@ try:
|
||||
__setFalse = False
|
||||
except:
|
||||
import __builtin__
|
||||
|
||||
setattr(__builtin__, 'True', 1)
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
#------------------------------------------------------------------------------------------------------ class for errors
|
||||
|
||||
class VariableError(RuntimeError): pass
|
||||
class VariableError(RuntimeError):pass
|
||||
|
||||
class FrameNotFoundError(RuntimeError): pass
|
||||
|
||||
|
||||
if USE_PSYCO_OPTIMIZATION:
|
||||
try:
|
||||
import psyco
|
||||
|
||||
varToXML = psyco.proxy(varToXML)
|
||||
except ImportError:
|
||||
if hasattr(sys, 'exc_clear'): #jython does not have it
|
||||
sys.exc_clear() #don't keep the traceback -- clients don't want to see it
|
||||
class FrameNotFoundError(RuntimeError):pass
|
||||
|
||||
def iterFrames(initialFrame):
|
||||
'''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
|
||||
@@ -166,50 +153,127 @@ def findFrame(thread_id, frame_id):
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
|
||||
""" returns the value of the compound variable as a dictionary"""
|
||||
def getVariable(thread_id, frame_id, scope, attrs):
|
||||
"""
|
||||
returns the value of a variable
|
||||
|
||||
:scope: can be BY_ID, EXPRESSION, GLOBAL, LOCAL, FRAME
|
||||
|
||||
BY_ID means we'll traverse the list of all objects alive to get the object.
|
||||
|
||||
:attrs: after reaching the proper scope, we have to get the attributes until we find
|
||||
the proper location (i.e.: obj\tattr1\tattr2)
|
||||
|
||||
:note: when BY_ID is used, the frame_id is considered the id of the object to find and
|
||||
not the frame (as we don't care about the frame in this case).
|
||||
"""
|
||||
if scope == 'BY_ID':
|
||||
if thread_id != GetThreadId(threading.currentThread()) :
|
||||
raise VariableError("getVariable: must execute on same thread")
|
||||
|
||||
try:
|
||||
import gc
|
||||
objects = gc.get_objects()
|
||||
except:
|
||||
pass #Not all python variants have it.
|
||||
else:
|
||||
frame_id = int(frame_id)
|
||||
for var in objects:
|
||||
if id(var) == frame_id:
|
||||
if attrs is not None:
|
||||
attrList = attrs.split('\t')
|
||||
for k in attrList:
|
||||
_type, _typeName, resolver = getType(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
return var
|
||||
|
||||
#If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
|
||||
sys.stderr.write('Unable to find object with id: %s\n' % (frame_id,))
|
||||
return None
|
||||
|
||||
frame = findFrame(thread_id, frame_id)
|
||||
if frame is None:
|
||||
return {}
|
||||
|
||||
attrList = attrs.split('\t')
|
||||
|
||||
if scope == "GLOBAL":
|
||||
var = frame.f_globals
|
||||
del attrList[0] # globals are special, and they get a single dummy unused attribute
|
||||
if attrs is not None:
|
||||
attrList = attrs.split('\t')
|
||||
else:
|
||||
var = frame.f_locals
|
||||
type, _typeName, resolver = getType(var)
|
||||
try:
|
||||
resolver.resolve(var, attrList[0])
|
||||
except:
|
||||
var = frame.f_globals
|
||||
attrList = []
|
||||
|
||||
for k in attrList:
|
||||
type, _typeName, resolver = getType(var)
|
||||
var = resolver.resolve(var, k)
|
||||
if scope == 'EXPRESSION':
|
||||
for count in xrange(len(attrList)):
|
||||
if count == 0:
|
||||
# An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression
|
||||
var = evaluateExpression(thread_id, frame_id, attrList[count], False)
|
||||
else:
|
||||
_type, _typeName, resolver = getType(var)
|
||||
var = resolver.resolve(var, attrList[count])
|
||||
else:
|
||||
if scope == "GLOBAL":
|
||||
var = frame.f_globals
|
||||
del attrList[0] # globals are special, and they get a single dummy unused attribute
|
||||
else:
|
||||
var = frame.f_locals
|
||||
|
||||
for k in attrList:
|
||||
_type, _typeName, resolver = getType(var)
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
return var
|
||||
|
||||
|
||||
def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
|
||||
""" returns the value of the compound variable as a dictionary"""
|
||||
|
||||
var = getVariable(thread_id, frame_id, scope, attrs)
|
||||
|
||||
try:
|
||||
type, _typeName, resolver = getType(var)
|
||||
_type, _typeName, resolver = getType(var)
|
||||
return resolver.getDictionary(var)
|
||||
except:
|
||||
sys.stderr.write('Error evaluating: thread_id: %s\nframe_id: %s\nscope: %s\nattrs: %s\n' % (
|
||||
thread_id, frame_id, scope, attrs,))
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
|
||||
|
||||
def resolveVar(var, attrs):
|
||||
attrList = attrs.split('\t')
|
||||
|
||||
|
||||
for k in attrList:
|
||||
type, _typeName, resolver = getType(var)
|
||||
|
||||
|
||||
var = resolver.resolve(var, k)
|
||||
|
||||
|
||||
try:
|
||||
type, _typeName, resolver = getType(var)
|
||||
return resolver.getDictionary(var)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
|
||||
def customOperation(thread_id, frame_id, scope, attrs, style, code_or_file, operation_fn_name):
|
||||
"""
|
||||
We'll execute the code_or_file and then search in the namespace the operation_fn_name to execute with the given var.
|
||||
|
||||
code_or_file: either some code (i.e.: from pprint import pprint) or a file to be executed.
|
||||
operation_fn_name: the name of the operation to execute after the exec (i.e.: pprint)
|
||||
"""
|
||||
expressionValue = getVariable(thread_id, frame_id, scope, attrs)
|
||||
|
||||
try:
|
||||
namespace = {'__name__': '<customOperation>'}
|
||||
if style == "EXECFILE":
|
||||
namespace['__file__'] = code_or_file
|
||||
execfile(code_or_file, namespace, namespace)
|
||||
else: # style == EXEC
|
||||
namespace['__file__'] = '<customOperationCode>'
|
||||
Exec(code_or_file, namespace, namespace)
|
||||
|
||||
return str(namespace[operation_fn_name](expressionValue))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
'''returns the result of the evaluated expression
|
||||
@@ -230,6 +294,7 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals
|
||||
|
||||
try:
|
||||
|
||||
if doExec:
|
||||
try:
|
||||
#try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
|
||||
@@ -240,7 +305,7 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
pydevd_save_locals.save_locals(frame)
|
||||
else:
|
||||
result = eval(compiled, updated_globals, frame.f_locals)
|
||||
if result is not None: #Only print if it's not None (as python does)
|
||||
if result is not None: #Only print if it's not None (as python does)
|
||||
sys.stdout.write('%s\n' % (result,))
|
||||
return
|
||||
|
||||
@@ -251,7 +316,6 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
except Exception:
|
||||
s = StringIO()
|
||||
traceback.print_exc(file=s)
|
||||
|
||||
result = s.getvalue()
|
||||
|
||||
try:
|
||||
@@ -265,6 +329,22 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
|
||||
result = ExceptionOnEvaluate(result)
|
||||
|
||||
# Ok, we have the initial error message, but let's see if we're dealing with a name mangling error...
|
||||
try:
|
||||
if '__' in expression:
|
||||
# Try to handle '__' name mangling...
|
||||
split = expression.split('.')
|
||||
curr = frame.f_locals.get(split[0])
|
||||
for entry in split[1:]:
|
||||
if entry.startswith('__') and not hasattr(curr, entry):
|
||||
entry = '_%s%s' % (curr.__class__.__name__, entry)
|
||||
curr = getattr(curr, entry)
|
||||
|
||||
result = curr
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
return result
|
||||
finally:
|
||||
#Should not be kept alive if an exception happens and this frame is kept in the stack.
|
||||
@@ -273,22 +353,18 @@ def evaluateExpression(thread_id, frame_id, expression, doExec):
|
||||
|
||||
def changeAttrExpression(thread_id, frame_id, attr, expression):
|
||||
'''Changes some attribute in a given frame.
|
||||
@note: it will not (currently) work if we're not in the topmost frame (that's a python
|
||||
deficiency -- and it appears that there is no way of making it currently work --
|
||||
will probably need some change to the python internals)
|
||||
'''
|
||||
frame = findFrame(thread_id, frame_id)
|
||||
if frame is None:
|
||||
return
|
||||
|
||||
if isinstance(frame, DjangoTemplateFrame):
|
||||
result = eval(expression, frame.f_globals, frame.f_locals)
|
||||
frame.changeVariable(attr, result)
|
||||
|
||||
try:
|
||||
expression = expression.replace('@LINE@', '\n')
|
||||
|
||||
|
||||
if isinstance(frame, DjangoTemplateFrame):
|
||||
result = eval(expression, frame.f_globals, frame.f_locals)
|
||||
frame.changeVariable(attr, result)
|
||||
return
|
||||
|
||||
if attr[:7] == "Globals":
|
||||
attr = attr[8:]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pydev_log
|
||||
import traceback
|
||||
import pydevd_resolver
|
||||
import sys
|
||||
from pydevd_constants import * #@UnusedWildImport
|
||||
|
||||
from pydev_imports import quote
|
||||
@@ -146,9 +147,9 @@ def frameVarsToXML(frame_f_locals):
|
||||
pydev_log.error("Unexpected error, recovered safely.\n")
|
||||
|
||||
return xml
|
||||
|
||||
|
||||
def varToXML(val, name, doTrim=True):
|
||||
|
||||
|
||||
def varToXML(val, name, doTrim=True, additionalInXml=''):
|
||||
""" single variable or dictionary to xml representation """
|
||||
|
||||
is_exception_on_eval = isinstance(val, ExceptionOnEvaluate)
|
||||
@@ -162,19 +163,22 @@ def varToXML(val, name, doTrim=True):
|
||||
|
||||
try:
|
||||
if hasattr(v, '__class__'):
|
||||
try:
|
||||
cName = str(v.__class__)
|
||||
if cName.find('.') != -1:
|
||||
cName = cName.split('.')[-1]
|
||||
if v.__class__ == frame_type:
|
||||
value = pydevd_resolver.frameResolver.getFrameName(v)
|
||||
else:
|
||||
try:
|
||||
cName = str(v.__class__)
|
||||
if cName.find('.') != -1:
|
||||
cName = cName.split('.')[-1]
|
||||
|
||||
elif cName.find("'") != -1: #does not have '.' (could be something like <type 'int'>)
|
||||
cName = cName[cName.index("'") + 1:]
|
||||
elif cName.find("'") != -1: #does not have '.' (could be something like <type 'int'>)
|
||||
cName = cName[cName.index("'") + 1:]
|
||||
|
||||
if cName.endswith("'>"):
|
||||
cName = cName[:-2]
|
||||
except:
|
||||
cName = str(v.__class__)
|
||||
value = '%s: %s' % (cName, v)
|
||||
if cName.endswith("'>"):
|
||||
cName = cName[:-2]
|
||||
except:
|
||||
cName = str(v.__class__)
|
||||
value = '%s: %s' % (cName, v)
|
||||
else:
|
||||
value = str(v)
|
||||
except:
|
||||
@@ -218,4 +222,13 @@ def varToXML(val, name, doTrim=True):
|
||||
else:
|
||||
xmlCont = ''
|
||||
|
||||
return ''.join((xml, xmlValue, xmlCont, ' />\n'))
|
||||
return ''.join((xml, xmlValue, xmlCont, additionalInXml, ' />\n'))
|
||||
|
||||
if USE_PSYCO_OPTIMIZATION:
|
||||
try:
|
||||
import psyco
|
||||
|
||||
varToXML = psyco.proxy(varToXML)
|
||||
except ImportError:
|
||||
if hasattr(sys, 'exc_clear'): #jython does not have it
|
||||
sys.exc_clear() #don't keep the traceback -- clients don't want to see it
|
||||
|
||||
+222
-503
@@ -1,530 +1,249 @@
|
||||
import fnmatch
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
import os
|
||||
|
||||
def main():
|
||||
import sys
|
||||
|
||||
#Separate the nose params and the pydev params.
|
||||
pydev_params = []
|
||||
other_test_framework_params = []
|
||||
found_other_test_framework_param = None
|
||||
|
||||
NOSE_PARAMS = '--nose-params'
|
||||
PY_TEST_PARAMS = '--py-test-params'
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if not found_other_test_framework_param and arg != NOSE_PARAMS and arg != PY_TEST_PARAMS:
|
||||
pydev_params.append(arg)
|
||||
|
||||
else:
|
||||
if not found_other_test_framework_param:
|
||||
found_other_test_framework_param = arg
|
||||
else:
|
||||
other_test_framework_params.append(arg)
|
||||
|
||||
|
||||
#Here we'll run either with nose or with the pydev_runfiles.
|
||||
import pydev_runfiles
|
||||
import pydev_runfiles_xml_rpc
|
||||
import pydevd_constants
|
||||
from pydevd_file_utils import _NormFile
|
||||
|
||||
DEBUG = 0
|
||||
if DEBUG:
|
||||
sys.stdout.write('Received parameters: %s\n' % (sys.argv,))
|
||||
sys.stdout.write('Params for pydev: %s\n' % (pydev_params,))
|
||||
if found_other_test_framework_param:
|
||||
sys.stdout.write('Params for test framework: %s, %s\n' % (found_other_test_framework_param, other_test_framework_params))
|
||||
|
||||
try:
|
||||
__setFalse = False
|
||||
except:
|
||||
import __builtin__
|
||||
setattr(__builtin__, 'True', 1)
|
||||
setattr(__builtin__, 'False', 0)
|
||||
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# Jython?
|
||||
#=======================================================================================================================
|
||||
try:
|
||||
import org.python.core.PyDictionary #@UnresolvedImport @UnusedImport -- just to check if it could be valid
|
||||
def DictContains(d, key):
|
||||
return d.has_key(key)
|
||||
except:
|
||||
try:
|
||||
#Py3k does not have has_key anymore, and older versions don't have __contains__
|
||||
DictContains = dict.__contains__
|
||||
configuration = pydev_runfiles.parse_cmdline([sys.argv[0]] + pydev_params)
|
||||
except:
|
||||
DictContains = dict.has_key
|
||||
sys.stderr.write('Command line received: %s\n' % (sys.argv,))
|
||||
raise
|
||||
pydev_runfiles_xml_rpc.InitializeServer(configuration.port) #Note that if the port is None, a Null server will be initialized.
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
#Python 3k does not have it
|
||||
xrange = range
|
||||
|
||||
try:
|
||||
enumerate
|
||||
except:
|
||||
def enumerate(lst):
|
||||
ret = []
|
||||
i=0
|
||||
for element in lst:
|
||||
ret.append((i, element))
|
||||
i+=1
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# getopt code copied since gnu_getopt is not available on jython 2.1
|
||||
#=======================================================================================================================
|
||||
class GetoptError(Exception):
|
||||
opt = ''
|
||||
msg = ''
|
||||
def __init__(self, msg, opt=''):
|
||||
self.msg = msg
|
||||
self.opt = opt
|
||||
Exception.__init__(self, msg, opt)
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
|
||||
def gnu_getopt(args, shortopts, longopts=[]):
|
||||
"""getopt(args, options[, long_options]) -> opts, args
|
||||
|
||||
This function works like getopt(), except that GNU style scanning
|
||||
mode is used by default. This means that option and non-option
|
||||
arguments may be intermixed. The getopt() function stops
|
||||
processing options as soon as a non-option argument is
|
||||
encountered.
|
||||
|
||||
If the first character of the option string is `+', or if the
|
||||
environment variable POSIXLY_CORRECT is set, then option
|
||||
processing stops as soon as a non-option argument is encountered.
|
||||
"""
|
||||
|
||||
opts = []
|
||||
prog_args = []
|
||||
if isinstance(longopts, ''.__class__):
|
||||
longopts = [longopts]
|
||||
else:
|
||||
longopts = list(longopts)
|
||||
|
||||
# Allow options after non-option arguments?
|
||||
if shortopts.startswith('+'):
|
||||
shortopts = shortopts[1:]
|
||||
all_options_first = True
|
||||
elif os.environ.get("POSIXLY_CORRECT"):
|
||||
all_options_first = True
|
||||
else:
|
||||
all_options_first = False
|
||||
|
||||
while args:
|
||||
if args[0] == '--':
|
||||
prog_args += args[1:]
|
||||
break
|
||||
|
||||
if args[0][:2] == '--':
|
||||
opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
|
||||
elif args[0][:1] == '-':
|
||||
opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
|
||||
else:
|
||||
if all_options_first:
|
||||
prog_args += args
|
||||
break
|
||||
else:
|
||||
prog_args.append(args[0])
|
||||
args = args[1:]
|
||||
|
||||
return opts, prog_args
|
||||
|
||||
def do_longs(opts, opt, longopts, args):
|
||||
NOSE_FRAMEWORK = 1
|
||||
PY_TEST_FRAMEWORK = 2
|
||||
try:
|
||||
i = opt.index('=')
|
||||
except ValueError:
|
||||
optarg = None
|
||||
if found_other_test_framework_param:
|
||||
test_framework = 0 #Default (pydev)
|
||||
if found_other_test_framework_param == NOSE_PARAMS:
|
||||
import nose
|
||||
test_framework = NOSE_FRAMEWORK
|
||||
|
||||
elif found_other_test_framework_param == PY_TEST_PARAMS:
|
||||
import pytest
|
||||
test_framework = PY_TEST_FRAMEWORK
|
||||
|
||||
else:
|
||||
raise ImportError()
|
||||
|
||||
else:
|
||||
raise ImportError()
|
||||
|
||||
except ImportError:
|
||||
if found_other_test_framework_param:
|
||||
sys.stderr.write('Warning: Could not import the test runner: %s. Running with the default pydev unittest runner instead.\n' % (
|
||||
found_other_test_framework_param,))
|
||||
|
||||
test_framework = 0
|
||||
|
||||
#Clear any exception that may be there so that clients don't see it.
|
||||
#See: https://sourceforge.net/tracker/?func=detail&aid=3408057&group_id=85796&atid=577329
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
sys.exc_clear()
|
||||
|
||||
if test_framework == 0:
|
||||
|
||||
pydev_runfiles.main(configuration)
|
||||
|
||||
else:
|
||||
opt, optarg = opt[:i], opt[i + 1:]
|
||||
#We'll convert the parameters to what nose or py.test expects.
|
||||
#The supported parameters are:
|
||||
#runfiles.py --config-file|-t|--tests <Test.test1,Test2> dirs|files --nose-params xxx yyy zzz
|
||||
#(all after --nose-params should be passed directly to nose)
|
||||
|
||||
has_arg, opt = long_has_args(opt, longopts)
|
||||
if has_arg:
|
||||
if optarg is None:
|
||||
if not args:
|
||||
raise GetoptError('option --%s requires argument' % opt, opt)
|
||||
optarg, args = args[0], args[1:]
|
||||
elif optarg:
|
||||
raise GetoptError('option --%s must not have an argument' % opt, opt)
|
||||
opts.append(('--' + opt, optarg or ''))
|
||||
return opts, args
|
||||
|
||||
# Return:
|
||||
# has_arg?
|
||||
# full option name
|
||||
def long_has_args(opt, longopts):
|
||||
possibilities = [o for o in longopts if o.startswith(opt)]
|
||||
if not possibilities:
|
||||
raise GetoptError('option --%s not recognized' % opt, opt)
|
||||
# Is there an exact match?
|
||||
if opt in possibilities:
|
||||
return False, opt
|
||||
elif opt + '=' in possibilities:
|
||||
return True, opt
|
||||
# No exact match, so better be unique.
|
||||
if len(possibilities) > 1:
|
||||
# XXX since possibilities contains all valid continuations, might be
|
||||
# nice to work them into the error msg
|
||||
raise GetoptError('option --%s not a unique prefix' % opt, opt)
|
||||
assert len(possibilities) == 1
|
||||
unique_match = possibilities[0]
|
||||
has_arg = unique_match.endswith('=')
|
||||
if has_arg:
|
||||
unique_match = unique_match[:-1]
|
||||
return has_arg, unique_match
|
||||
|
||||
def do_shorts(opts, optstring, shortopts, args):
|
||||
while optstring != '':
|
||||
opt, optstring = optstring[0], optstring[1:]
|
||||
if short_has_arg(opt, shortopts):
|
||||
if optstring == '':
|
||||
if not args:
|
||||
raise GetoptError('option -%s requires argument' % opt,
|
||||
opt)
|
||||
optstring, args = args[0], args[1:]
|
||||
optarg, optstring = optstring, ''
|
||||
else:
|
||||
optarg = ''
|
||||
opts.append(('-' + opt, optarg))
|
||||
return opts, args
|
||||
|
||||
def short_has_arg(opt, shortopts):
|
||||
for i in range(len(shortopts)):
|
||||
if opt == shortopts[i] != ':':
|
||||
return shortopts.startswith(':', i + 1)
|
||||
raise GetoptError('option -%s not recognized' % opt, opt)
|
||||
#In java:
|
||||
#--tests = Constants.ATTR_UNITTEST_TESTS
|
||||
#--config-file = Constants.ATTR_UNITTEST_CONFIGURATION_FILE
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# End getopt code
|
||||
#=======================================================================================================================
|
||||
#The only thing actually handled here are the tests that we want to run, which we'll
|
||||
#handle and pass as what the test framework expects.
|
||||
|
||||
py_test_accept_filter = {}
|
||||
files_to_tests = configuration.files_to_tests
|
||||
|
||||
if files_to_tests:
|
||||
#Handling through the file contents (file where each line is a test)
|
||||
files_or_dirs = []
|
||||
for file, tests in files_to_tests.items():
|
||||
if test_framework == NOSE_FRAMEWORK:
|
||||
for test in tests:
|
||||
files_or_dirs.append(file + ':' + test)
|
||||
|
||||
elif test_framework == PY_TEST_FRAMEWORK:
|
||||
file = _NormFile(file)
|
||||
py_test_accept_filter[file] = tests
|
||||
files_or_dirs.append(file)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# parse_cmdline
|
||||
#=======================================================================================================================
|
||||
def parse_cmdline():
|
||||
""" parses command line and returns test directories, verbosity, test filter and test suites
|
||||
usage:
|
||||
runfiles.py -v|--verbosity <level> -f|--filter <regex> -t|--tests <Test.test1,Test2> dirs|files
|
||||
"""
|
||||
verbosity = 2
|
||||
test_filter = None
|
||||
tests = None
|
||||
|
||||
optlist, dirs = gnu_getopt(sys.argv[1:], "v:f:t:", ["verbosity=", "filter=", "tests="])
|
||||
for opt, value in optlist:
|
||||
if opt in ("-v", "--verbosity"):
|
||||
verbosity = value
|
||||
|
||||
elif opt in ("-f", "--filter"):
|
||||
test_filter = value.split(',')
|
||||
|
||||
elif opt in ("-t", "--tests"):
|
||||
tests = value.split(',')
|
||||
|
||||
if type([]) != type(dirs):
|
||||
dirs = [dirs]
|
||||
|
||||
ret_dirs = []
|
||||
for d in dirs:
|
||||
if '|' in d:
|
||||
#paths may come from the ide separated by |
|
||||
ret_dirs.extend(d.split('|'))
|
||||
else:
|
||||
ret_dirs.append(d)
|
||||
|
||||
return ret_dirs, int(verbosity), test_filter, tests
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# PydevTestRunner
|
||||
#=======================================================================================================================
|
||||
class PydevTestRunner:
|
||||
""" finds and runs a file or directory of files as a unit test """
|
||||
|
||||
__py_extensions = ["*.py", "*.pyw"]
|
||||
__exclude_files = ["__init__.*"]
|
||||
|
||||
def __init__(self, test_dir, test_filter=None, verbosity=2, tests=None):
|
||||
self.test_dir = test_dir
|
||||
self.__adjust_path()
|
||||
self.test_filter = self.__setup_test_filter(test_filter)
|
||||
self.verbosity = verbosity
|
||||
self.tests = tests
|
||||
|
||||
|
||||
def __adjust_path(self):
|
||||
""" add the current file or directory to the python path """
|
||||
path_to_append = None
|
||||
for n in xrange(len(self.test_dir)):
|
||||
dir_name = self.__unixify(self.test_dir[n])
|
||||
if os.path.isdir(dir_name):
|
||||
if not dir_name.endswith("/"):
|
||||
self.test_dir[n] = dir_name + "/"
|
||||
path_to_append = os.path.normpath(dir_name)
|
||||
elif os.path.isfile(dir_name):
|
||||
path_to_append = os.path.dirname(dir_name)
|
||||
else:
|
||||
msg = ("unknown type. \n%s\nshould be file or a directory.\n" % (dir_name))
|
||||
raise RuntimeError(msg)
|
||||
if path_to_append is not None:
|
||||
#Add it as the last one (so, first things are resolved against the default dirs and
|
||||
#if none resolves, then we try a relative import).
|
||||
sys.path.append(path_to_append)
|
||||
return
|
||||
|
||||
def __setup_test_filter(self, test_filter):
|
||||
""" turn a filter string into a list of filter regexes """
|
||||
if test_filter is None or len(test_filter) == 0:
|
||||
return None
|
||||
return [re.compile("test%s" % f) for f in test_filter]
|
||||
|
||||
def __is_valid_py_file(self, fname):
|
||||
""" tests that a particular file contains the proper file extension
|
||||
and is not in the list of files to exclude """
|
||||
is_valid_fname = 0
|
||||
for invalid_fname in self.__class__.__exclude_files:
|
||||
is_valid_fname += int(not fnmatch.fnmatch(fname, invalid_fname))
|
||||
if_valid_ext = 0
|
||||
for ext in self.__class__.__py_extensions:
|
||||
if_valid_ext += int(fnmatch.fnmatch(fname, ext))
|
||||
return is_valid_fname > 0 and if_valid_ext > 0
|
||||
|
||||
def __unixify(self, s):
|
||||
""" stupid windows. converts the backslash to forwardslash for consistency """
|
||||
return os.path.normpath(s).replace(os.sep, "/")
|
||||
|
||||
def __importify(self, s, dir=False):
|
||||
""" turns directory separators into dots and removes the ".py*" extension
|
||||
so the string can be used as import statement """
|
||||
if not dir:
|
||||
dirname, fname = os.path.split(s)
|
||||
|
||||
if fname.count('.') > 1:
|
||||
#if there's a file named xxx.xx.py, it is not a valid module, so, let's not load it...
|
||||
return
|
||||
|
||||
imp_stmt_pieces = [dirname.replace("\\", "/").replace("/", "."), os.path.splitext(fname)[0]]
|
||||
|
||||
if len(imp_stmt_pieces[0]) == 0:
|
||||
imp_stmt_pieces = imp_stmt_pieces[1:]
|
||||
|
||||
return ".".join(imp_stmt_pieces)
|
||||
|
||||
else: #handle dir
|
||||
return s.replace("\\", "/").replace("/", ".")
|
||||
|
||||
def __add_files(self, pyfiles, root, files):
|
||||
""" if files match, appends them to pyfiles. used by os.path.walk fcn """
|
||||
for fname in files:
|
||||
if self.__is_valid_py_file(fname):
|
||||
name_without_base_dir = self.__unixify(os.path.join(root, fname))
|
||||
pyfiles.append(name_without_base_dir)
|
||||
return
|
||||
|
||||
|
||||
def find_import_files(self):
|
||||
""" return a list of files to import """
|
||||
pyfiles = []
|
||||
|
||||
for base_dir in self.test_dir:
|
||||
if os.path.isdir(base_dir):
|
||||
if hasattr(os, 'walk'):
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
self.__add_files(pyfiles, root, files)
|
||||
else:
|
||||
# jython2.1 is too old for os.walk!
|
||||
os.path.walk(base_dir, self.__add_files, pyfiles)
|
||||
raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
|
||||
|
||||
elif os.path.isfile(base_dir):
|
||||
pyfiles.append(base_dir)
|
||||
else:
|
||||
if configuration.tests:
|
||||
#Tests passed (works together with the files_or_dirs)
|
||||
files_or_dirs = []
|
||||
for file in configuration.files_or_dirs:
|
||||
if test_framework == NOSE_FRAMEWORK:
|
||||
for t in configuration.tests:
|
||||
files_or_dirs.append(file + ':' + t)
|
||||
|
||||
return pyfiles
|
||||
|
||||
def __get_module_from_str(self, modname, print_exception):
|
||||
""" Import the module in the given import path.
|
||||
* Returns the "final" module, so importing "coilib40.subject.visu"
|
||||
returns the "visu" module, not the "coilib40" as returned by __import__ """
|
||||
try:
|
||||
mod = __import__(modname)
|
||||
for part in modname.split('.')[1:]:
|
||||
mod = getattr(mod, part)
|
||||
return mod
|
||||
except:
|
||||
if print_exception:
|
||||
import traceback;traceback.print_exc()
|
||||
sys.stderr.write('ERROR: Module: %s could not be imported.\n' % (modname,))
|
||||
return None
|
||||
|
||||
def find_modules_from_files(self, pyfiles):
|
||||
""" returns a lisst of modules given a list of files """
|
||||
#let's make sure that the paths we want are in the pythonpath...
|
||||
imports = [self.__importify(s) for s in pyfiles]
|
||||
|
||||
system_paths = []
|
||||
for s in sys.path:
|
||||
system_paths.append(self.__importify(s, True))
|
||||
|
||||
|
||||
ret = []
|
||||
for imp in imports:
|
||||
if imp is None:
|
||||
continue #can happen if a file is not a valid module
|
||||
choices = []
|
||||
for s in system_paths:
|
||||
if imp.startswith(s):
|
||||
add = imp[len(s) + 1:]
|
||||
if add:
|
||||
choices.append(add)
|
||||
#sys.stdout.write(' ' + add + ' ')
|
||||
|
||||
if not choices:
|
||||
sys.stdout.write('PYTHONPATH not found for file: %s\n' % imp)
|
||||
else:
|
||||
for i, import_str in enumerate(choices):
|
||||
mod = self.__get_module_from_str(import_str, print_exception=i == len(choices) - 1)
|
||||
if mod is not None:
|
||||
ret.append(mod)
|
||||
break
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
def find_tests_from_modules(self, modules):
|
||||
""" returns the unittests given a list of modules """
|
||||
loader = unittest.TestLoader()
|
||||
|
||||
ret = []
|
||||
if self.tests:
|
||||
accepted_classes = {}
|
||||
accepted_methods = {}
|
||||
|
||||
for t in self.tests:
|
||||
splitted = t.split('.')
|
||||
if len(splitted) == 1:
|
||||
accepted_classes[t] = t
|
||||
|
||||
elif len(splitted) == 2:
|
||||
accepted_methods[t] = t
|
||||
|
||||
#===========================================================================================================
|
||||
# GetTestCaseNames
|
||||
#===========================================================================================================
|
||||
class GetTestCaseNames:
|
||||
"""Yes, we need a class for that (cannot use outer context on jython 2.1)"""
|
||||
|
||||
def __init__(self, accepted_classes, accepted_methods):
|
||||
self.accepted_classes = accepted_classes
|
||||
self.accepted_methods = accepted_methods
|
||||
|
||||
def __call__(self, testCaseClass):
|
||||
"""Return a sorted sequence of method names found within testCaseClass"""
|
||||
testFnNames = []
|
||||
className = testCaseClass.__name__
|
||||
|
||||
if DictContains(self.accepted_classes, className):
|
||||
for attrname in dir(testCaseClass):
|
||||
#If a class is chosen, we select all the 'test' methods'
|
||||
if attrname.startswith('test') and hasattr(getattr(testCaseClass, attrname), '__call__'):
|
||||
testFnNames.append(attrname)
|
||||
elif test_framework == PY_TEST_FRAMEWORK:
|
||||
file = _NormFile(file)
|
||||
py_test_accept_filter[file] = configuration.tests
|
||||
files_or_dirs.append(file)
|
||||
|
||||
else:
|
||||
for attrname in dir(testCaseClass):
|
||||
#If we have the class+method name, we must do a full check and have an exact match.
|
||||
if DictContains(self.accepted_methods, className + '.' + attrname):
|
||||
if hasattr(getattr(testCaseClass, attrname), '__call__'):
|
||||
testFnNames.append(attrname)
|
||||
raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
|
||||
else:
|
||||
#Only files or dirs passed (let it do the test-loading based on those paths)
|
||||
files_or_dirs = configuration.files_or_dirs
|
||||
|
||||
#sorted() is not available in jython 2.1
|
||||
testFnNames.sort()
|
||||
return testFnNames
|
||||
argv = other_test_framework_params + files_or_dirs
|
||||
|
||||
|
||||
loader.getTestCaseNames = GetTestCaseNames(accepted_classes, accepted_methods)
|
||||
if test_framework == NOSE_FRAMEWORK:
|
||||
#Nose usage: http://somethingaboutorange.com/mrl/projects/nose/0.11.2/usage.html
|
||||
#show_stdout_option = ['-s']
|
||||
#processes_option = ['--processes=2']
|
||||
argv.insert(0, sys.argv[0])
|
||||
if DEBUG:
|
||||
sys.stdout.write('Final test framework args: %s\n' % (argv[1:],))
|
||||
|
||||
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])
|
||||
|
||||
elif test_framework == PY_TEST_FRAMEWORK:
|
||||
if DEBUG:
|
||||
sys.stdout.write('Final test framework args: %s\n' % (argv,))
|
||||
sys.stdout.write('py_test_accept_filter: %s\n' % (py_test_accept_filter,))
|
||||
|
||||
import os
|
||||
|
||||
try:
|
||||
xrange
|
||||
except:
|
||||
xrange = range
|
||||
|
||||
for i in xrange(len(argv)):
|
||||
arg = argv[i]
|
||||
#Workaround bug in py.test: if we pass the full path it ends up importing conftest
|
||||
#more than once (so, always work with relative paths).
|
||||
if os.path.isfile(arg) or os.path.isdir(arg):
|
||||
from pydev_imports import relpath
|
||||
arg = relpath(arg)
|
||||
argv[i] = arg
|
||||
|
||||
d = os.path.dirname(__file__)
|
||||
if d not in sys.path:
|
||||
sys.path.insert(0, d)
|
||||
|
||||
import pickle, zlib, base64
|
||||
|
||||
# Update environment PYTHONPATH so that it finds our plugin if using xdist.
|
||||
os.environ['PYTHONPATH'] = os.pathsep.join(sys.path)
|
||||
|
||||
# Set what should be skipped in the plugin through an environment variable
|
||||
s = base64.b64encode(zlib.compress(pickle.dumps(py_test_accept_filter)))
|
||||
if pydevd_constants.IS_PY3K:
|
||||
s = s.decode('ascii') # Must be str in py3.
|
||||
os.environ['PYDEV_PYTEST_SKIP'] = s
|
||||
|
||||
# Identifies the main pid (i.e.: if it's not the main pid it has to connect back to the
|
||||
# main pid to give xml-rpc notifications).
|
||||
os.environ['PYDEV_MAIN_PID'] = str(os.getpid())
|
||||
os.environ['PYDEV_PYTEST_SERVER'] = str(configuration.port)
|
||||
|
||||
argv.append('-p')
|
||||
argv.append('pydev_runfiles_pytest2')
|
||||
pytest.main(argv)
|
||||
|
||||
else:
|
||||
raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,))
|
||||
|
||||
|
||||
ret.extend([loader.loadTestsFromModule(m) for m in modules])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def filter_tests(self, test_objs):
|
||||
""" based on a filter name, only return those tests that have
|
||||
the test case names that match """
|
||||
test_suite = []
|
||||
for test_obj in test_objs:
|
||||
|
||||
if isinstance(test_obj, unittest.TestSuite):
|
||||
if test_obj._tests:
|
||||
test_obj._tests = self.filter_tests(test_obj._tests)
|
||||
if test_obj._tests:
|
||||
test_suite.append(test_obj)
|
||||
|
||||
elif isinstance(test_obj, unittest.TestCase):
|
||||
test_cases = []
|
||||
for tc in test_objs:
|
||||
try:
|
||||
testMethodName = tc._TestCase__testMethodName
|
||||
except AttributeError:
|
||||
#changed in python 2.5
|
||||
testMethodName = tc._testMethodName
|
||||
|
||||
if self.__match(self.test_filter, testMethodName) and self.__match_tests(self.tests, tc, testMethodName):
|
||||
test_cases.append(tc)
|
||||
return test_cases
|
||||
return test_suite
|
||||
|
||||
|
||||
def __match_tests(self, tests, test_case, test_method_name):
|
||||
if not tests:
|
||||
return 1
|
||||
|
||||
for t in tests:
|
||||
class_and_method = t.split('.')
|
||||
if len(class_and_method) == 1:
|
||||
#only class name
|
||||
if class_and_method[0] == test_case.__class__.__name__:
|
||||
return 1
|
||||
|
||||
elif len(class_and_method) == 2:
|
||||
if class_and_method[0] == test_case.__class__.__name__ and class_and_method[1] == test_method_name:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
def __match(self, filter_list, name):
|
||||
""" returns whether a test name matches the test filter """
|
||||
if filter_list is None:
|
||||
return 1
|
||||
for f in filter_list:
|
||||
if re.match(f, name):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def run_tests(self):
|
||||
""" runs all tests """
|
||||
sys.stdout.write("Finding files...\n")
|
||||
files = self.find_import_files()
|
||||
sys.stdout.write('%s %s\n' % (self.test_dir, '... done'))
|
||||
sys.stdout.write("Importing test modules ... ")
|
||||
modules = self.find_modules_from_files(files)
|
||||
sys.stdout.write("done.\n")
|
||||
all_tests = self.find_tests_from_modules(modules)
|
||||
if self.test_filter or self.tests:
|
||||
|
||||
if self.test_filter:
|
||||
sys.stdout.write('Test Filter: %s' % ([p.pattern for p in self.test_filter],))
|
||||
|
||||
if self.tests:
|
||||
sys.stdout.write('Tests to run: %s' % (self.tests,))
|
||||
|
||||
all_tests = self.filter_tests(all_tests)
|
||||
|
||||
sys.stdout.write('\n')
|
||||
runner = unittest.TextTestRunner(stream=sys.stdout, descriptions=1, verbosity=verbosity)
|
||||
runner.run(unittest.TestSuite(all_tests))
|
||||
return
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
if __name__ == '__main__':
|
||||
dirs, verbosity, test_filter, tests = parse_cmdline()
|
||||
PydevTestRunner(dirs, test_filter, verbosity, tests).run_tests()
|
||||
try:
|
||||
main()
|
||||
finally:
|
||||
try:
|
||||
#The server is not a daemon thread, so, we have to ask for it to be killed!
|
||||
import pydev_runfiles_xml_rpc
|
||||
pydev_runfiles_xml_rpc.forceServerKill()
|
||||
except:
|
||||
pass #Ignore any errors here
|
||||
|
||||
import sys
|
||||
import threading
|
||||
if hasattr(sys, '_current_frames') and hasattr(threading, 'enumerate'):
|
||||
import time
|
||||
import traceback
|
||||
|
||||
class DumpThreads(threading.Thread):
|
||||
def run(self):
|
||||
time.sleep(10)
|
||||
|
||||
thread_id_to_name = {}
|
||||
try:
|
||||
for t in threading.enumerate():
|
||||
thread_id_to_name[t.ident] = '%s (daemon: %s)' % (t.name, t.daemon)
|
||||
except:
|
||||
pass
|
||||
|
||||
stack_trace = [
|
||||
'===============================================================================',
|
||||
'pydev pyunit runner: Threads still found running after tests finished',
|
||||
'================================= Thread Dump =================================']
|
||||
|
||||
for thread_id, stack in sys._current_frames().items():
|
||||
stack_trace.append('\n-------------------------------------------------------------------------------')
|
||||
stack_trace.append(" Thread %s" % thread_id_to_name.get(thread_id, thread_id))
|
||||
stack_trace.append('')
|
||||
|
||||
if 'self' in stack.f_locals:
|
||||
sys.stderr.write(str(stack.f_locals['self'])+'\n')
|
||||
|
||||
for filename, lineno, name, line in traceback.extract_stack(stack):
|
||||
stack_trace.append(' File "%s", line %d, in %s' % (filename, lineno, name))
|
||||
if line:
|
||||
stack_trace.append(" %s" % (line.strip()))
|
||||
stack_trace.append('\n=============================== END Thread Dump ===============================')
|
||||
sys.stderr.write('\n'.join(stack_trace))
|
||||
|
||||
|
||||
dump_current_frames_thread = DumpThreads()
|
||||
dump_current_frames_thread.setDaemon(True) # Daemon so that this thread doesn't halt it!
|
||||
dump_current_frames_thread.start()
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
# This is a dummy for code-completion purposes.
|
||||
|
||||
def __unicode__(self):
|
||||
"""
|
||||
Return "app_label.model_label.manager_name".
|
||||
"""
|
||||
|
||||
def _copy_to_model(self, model):
|
||||
"""
|
||||
Makes a copy of the manager and assigns it to 'model', which should be
|
||||
a child of the existing model (used when inheriting a manager from an
|
||||
abstract base class).
|
||||
"""
|
||||
|
||||
|
||||
def _db(self):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def _get_queryset_methods(cls, queryset_class):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def _hints(self):
|
||||
"""
|
||||
dict() -> new empty dictionary
|
||||
dict(mapping) -> new dictionary initialized from a mapping object's
|
||||
(key, value) pairs
|
||||
dict(iterable) -> new dictionary initialized as if via:
|
||||
d = {}
|
||||
for k, v in iterable:
|
||||
d[k] = v
|
||||
dict(**kwargs) -> new dictionary initialized with the name=value pairs
|
||||
in the keyword argument list. For example: dict(one=1, two=2)
|
||||
"""
|
||||
|
||||
|
||||
def _inherited(self):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def _insert(self, *args, **kwargs):
|
||||
"""
|
||||
Inserts a new record for the given model. This provides an interface to
|
||||
the InsertQuery class and is how Model.save() is implemented.
|
||||
"""
|
||||
|
||||
|
||||
def _queryset_class(self):
|
||||
"""
|
||||
Represents a lazy database lookup for a set of objects.
|
||||
"""
|
||||
|
||||
|
||||
def _set_creation_counter(self):
|
||||
"""
|
||||
Sets the creation counter value for this instance and increments the
|
||||
class-level copy.
|
||||
"""
|
||||
|
||||
|
||||
def _update(self, *args, **kwargs):
|
||||
"""
|
||||
A version of update that accepts field objects instead of field names.
|
||||
Used primarily for model saving and not intended for use by general
|
||||
code (it requires too much poking around at model internals to be
|
||||
useful at that level).
|
||||
"""
|
||||
|
||||
|
||||
def aggregate(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a dictionary containing the calculations (aggregation)
|
||||
over the current queryset
|
||||
|
||||
If args is present the expression is passed as a kwarg using
|
||||
the Aggregate object's default alias.
|
||||
"""
|
||||
|
||||
|
||||
def all(self):
|
||||
"""
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def annotate(self, *args, **kwargs):
|
||||
"""
|
||||
Return a query set in which the returned objects have been annotated
|
||||
with data aggregated from related fields.
|
||||
"""
|
||||
|
||||
|
||||
def bulk_create(self, *args, **kwargs):
|
||||
"""
|
||||
Inserts each of the instances into the database. This does *not* call
|
||||
save() on each of the instances, does not send any pre/post save
|
||||
signals, and does not set the primary key attribute if it is an
|
||||
autoincrement field.
|
||||
"""
|
||||
|
||||
|
||||
def check(self, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def complex_filter(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance with filter_obj added to the filters.
|
||||
|
||||
filter_obj can be a Q object (or anything with an add_to_query()
|
||||
method) or a dictionary of keyword lookup arguments.
|
||||
|
||||
This exists to support framework features such as 'limit_choices_to',
|
||||
and usually it will be more natural to use other methods.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def contribute_to_class(self, model, name):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def count(self, *args, **kwargs):
|
||||
"""
|
||||
Performs a SELECT COUNT() and returns the number of records as an
|
||||
integer.
|
||||
|
||||
If the QuerySet is already fully cached this simply returns the length
|
||||
of the cached results set to avoid multiple SELECT COUNT(*) calls.
|
||||
"""
|
||||
|
||||
|
||||
def create(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new object with the given kwargs, saving it to the database
|
||||
and returning the created object.
|
||||
"""
|
||||
|
||||
|
||||
def creation_counter(self):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def dates(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a list of date objects representing all available dates for
|
||||
the given field_name, scoped to 'kind'.
|
||||
"""
|
||||
|
||||
|
||||
def datetimes(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a list of datetime objects representing all available
|
||||
datetimes for the given field_name, scoped to 'kind'.
|
||||
"""
|
||||
|
||||
|
||||
def db(self):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def db_manager(self, using=None, hints=None):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def defer(self, *args, **kwargs):
|
||||
"""
|
||||
Defers the loading of data for certain fields until they are accessed.
|
||||
The set of fields to defer is added to any existing set of deferred
|
||||
fields. The only exception to this is if None is passed in as the only
|
||||
parameter, in which case all deferrals are removed (None acts as a
|
||||
reset option).
|
||||
"""
|
||||
|
||||
|
||||
def distinct(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance that will select only distinct results.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def earliest(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def exclude(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance with NOT (args) ANDed to the existing
|
||||
set.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def exists(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def extra(self, *args, **kwargs):
|
||||
"""
|
||||
Adds extra SQL fragments to the query.
|
||||
"""
|
||||
|
||||
|
||||
def filter(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance with the args ANDed to the existing
|
||||
set.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def first(self, *args, **kwargs):
|
||||
"""
|
||||
Returns the first object of a query, returns None if no match is found.
|
||||
"""
|
||||
|
||||
|
||||
def from_queryset(cls, queryset_class, class_name=None):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""
|
||||
Performs the query and returns a single object matching the given
|
||||
keyword arguments.
|
||||
"""
|
||||
|
||||
|
||||
def get_or_create(self, *args, **kwargs):
|
||||
"""
|
||||
Looks up an object with the given kwargs, creating one if necessary.
|
||||
Returns a tuple of (object, created), where created is a boolean
|
||||
specifying whether an object was created.
|
||||
"""
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Returns a new QuerySet object. Subclasses can override this method to
|
||||
easily customize the behavior of the Manager.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def in_bulk(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a dictionary mapping each of the given IDs to the object with
|
||||
that ID.
|
||||
"""
|
||||
|
||||
|
||||
def iterator(self, *args, **kwargs):
|
||||
"""
|
||||
An iterator over the results from applying this QuerySet to the
|
||||
database.
|
||||
"""
|
||||
|
||||
|
||||
def last(self, *args, **kwargs):
|
||||
"""
|
||||
Returns the last object of a query, returns None if no match is found.
|
||||
"""
|
||||
|
||||
|
||||
def latest(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def model(self):
|
||||
"""
|
||||
MyModel(id)
|
||||
"""
|
||||
|
||||
|
||||
def none(self, *args, **kwargs):
|
||||
"""
|
||||
Returns an empty QuerySet.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def only(self, *args, **kwargs):
|
||||
"""
|
||||
Essentially, the opposite of defer. Only the fields passed into this
|
||||
method and that are not already specified as deferred are loaded
|
||||
immediately when the queryset is evaluated.
|
||||
"""
|
||||
|
||||
|
||||
def order_by(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance with the ordering changed.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def prefetch_related(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance that will prefetch the specified
|
||||
Many-To-One and Many-To-Many related objects when the QuerySet is
|
||||
evaluated.
|
||||
|
||||
When prefetch_related() is called more than once, the list of lookups to
|
||||
prefetch is appended to. If prefetch_related(None) is called, the list
|
||||
is cleared.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def raw(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def reverse(self, *args, **kwargs):
|
||||
"""
|
||||
Reverses the ordering of the QuerySet.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def select_for_update(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance that will select objects with a
|
||||
FOR UPDATE lock.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def select_related(self, *args, **kwargs):
|
||||
"""
|
||||
Returns a new QuerySet instance that will select related objects.
|
||||
|
||||
If fields are specified, they must be ForeignKey fields and only those
|
||||
related objects are included in the selection.
|
||||
|
||||
If select_related(None) is called, the list is cleared.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
"""
|
||||
Updates all elements in the current QuerySet, setting all the given
|
||||
fields to the appropriate values.
|
||||
"""
|
||||
|
||||
|
||||
def update_or_create(self, *args, **kwargs):
|
||||
"""
|
||||
Looks up an object with the given kwargs, updating one with defaults
|
||||
if it exists, otherwise creates a new one.
|
||||
Returns a tuple (object, created), where created is a boolean
|
||||
specifying whether an object was created.
|
||||
"""
|
||||
|
||||
|
||||
def using(self, *args, **kwargs):
|
||||
"""
|
||||
Selects which database this QuerySet should execute its query against.
|
||||
|
||||
@rtype: django.db.models.query.QuerySet
|
||||
"""
|
||||
|
||||
|
||||
def values(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def values_list(self, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
import os.path
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
# completion types.
|
||||
TYPE_IMPORT = '0'
|
||||
TYPE_CLASS = '1'
|
||||
TYPE_FUNCTION = '2'
|
||||
TYPE_ATTR = '3'
|
||||
TYPE_BUILTIN = '4'
|
||||
TYPE_PARAM = '5'
|
||||
|
||||
def _imp(name, log=None):
|
||||
try:
|
||||
return __import__(name)
|
||||
except:
|
||||
if '.' in name:
|
||||
sub = name[0:name.rfind('.')]
|
||||
|
||||
if log is not None:
|
||||
log.AddContent('Unable to import', name, 'trying with', sub)
|
||||
# log.AddContent('PYTHONPATH:')
|
||||
# log.AddContent('\n'.join(sorted(sys.path)))
|
||||
log.AddException()
|
||||
|
||||
return _imp(sub, log)
|
||||
else:
|
||||
s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path)
|
||||
if log is not None:
|
||||
log.AddContent(s)
|
||||
log.AddException()
|
||||
|
||||
raise ImportError(s)
|
||||
|
||||
|
||||
IS_IPY = False
|
||||
if sys.platform == 'cli':
|
||||
IS_IPY = True
|
||||
_old_imp = _imp
|
||||
def _imp(name, log=None):
|
||||
# We must add a reference in clr for .Net
|
||||
import clr # @UnresolvedImport
|
||||
initial_name = name
|
||||
while '.' in name:
|
||||
try:
|
||||
clr.AddReference(name)
|
||||
break # If it worked, that's OK.
|
||||
except:
|
||||
name = name[0:name.rfind('.')]
|
||||
else:
|
||||
try:
|
||||
clr.AddReference(name)
|
||||
except:
|
||||
pass # That's OK (not dot net module).
|
||||
|
||||
return _old_imp(initial_name, log)
|
||||
|
||||
|
||||
|
||||
def GetFile(mod):
|
||||
f = None
|
||||
try:
|
||||
f = inspect.getsourcefile(mod) or inspect.getfile(mod)
|
||||
except:
|
||||
if hasattr(mod, '__file__'):
|
||||
f = mod.__file__
|
||||
if f.lower(f[-4:]) in ['.pyc', '.pyo']:
|
||||
filename = f[:-4] + '.py'
|
||||
if os.path.exists(filename):
|
||||
f = filename
|
||||
|
||||
return f
|
||||
|
||||
def Find(name, log=None):
|
||||
f = None
|
||||
|
||||
mod = _imp(name, log)
|
||||
parent = mod
|
||||
foundAs = ''
|
||||
|
||||
if inspect.ismodule(mod):
|
||||
f = GetFile(mod)
|
||||
|
||||
components = name.split('.')
|
||||
|
||||
old_comp = None
|
||||
for comp in components[1:]:
|
||||
try:
|
||||
# this happens in the following case:
|
||||
# we have mx.DateTime.mxDateTime.mxDateTime.pyd
|
||||
# but after importing it, mx.DateTime.mxDateTime shadows access to mxDateTime.pyd
|
||||
mod = getattr(mod, comp)
|
||||
except AttributeError:
|
||||
if old_comp != comp:
|
||||
raise
|
||||
|
||||
if inspect.ismodule(mod):
|
||||
f = GetFile(mod)
|
||||
else:
|
||||
if len(foundAs) > 0:
|
||||
foundAs = foundAs + '.'
|
||||
foundAs = foundAs + comp
|
||||
|
||||
old_comp = comp
|
||||
|
||||
return f, mod, parent, foundAs
|
||||
|
||||
|
||||
def GenerateTip(data, log=None):
|
||||
data = data.replace('\n', '')
|
||||
if data.endswith('.'):
|
||||
data = data.rstrip('.')
|
||||
|
||||
f, mod, parent, foundAs = Find(data, log)
|
||||
# print_ >> open('temp.txt', 'w'), f
|
||||
tips = GenerateImportsTipForModule(mod)
|
||||
return f, tips
|
||||
|
||||
|
||||
def CheckChar(c):
|
||||
if c == '-' or c == '.':
|
||||
return '_'
|
||||
return c
|
||||
|
||||
def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True):
|
||||
'''
|
||||
@param obj_to_complete: the object from where we should get the completions
|
||||
@param dirComps: if passed, we should not 'dir' the object and should just iterate those passed as a parameter
|
||||
@param getattr: the way to get a given object from the obj_to_complete (used for the completer)
|
||||
@param filter: a callable that receives the name and decides if it should be appended or not to the results
|
||||
@return: list of tuples, so that each tuple represents a completion with:
|
||||
name, doc, args, type (from the TYPE_* constants)
|
||||
'''
|
||||
ret = []
|
||||
|
||||
if dirComps is None:
|
||||
dirComps = dir(obj_to_complete)
|
||||
if hasattr(obj_to_complete, '__dict__'):
|
||||
dirComps.append('__dict__')
|
||||
if hasattr(obj_to_complete, '__class__'):
|
||||
dirComps.append('__class__')
|
||||
|
||||
getCompleteInfo = True
|
||||
|
||||
if len(dirComps) > 1000:
|
||||
# ok, we don't want to let our users wait forever...
|
||||
# no complete info for you...
|
||||
|
||||
getCompleteInfo = False
|
||||
|
||||
dontGetDocsOn = (float, int, str, tuple, list)
|
||||
for d in dirComps:
|
||||
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
if not filter(d):
|
||||
continue
|
||||
|
||||
args = ''
|
||||
|
||||
try:
|
||||
obj = getattr(obj_to_complete, d)
|
||||
except: # just ignore and get it without aditional info
|
||||
ret.append((d, '', args, TYPE_BUILTIN))
|
||||
else:
|
||||
|
||||
if getCompleteInfo:
|
||||
retType = TYPE_BUILTIN
|
||||
|
||||
# check if we have to get docs
|
||||
getDoc = True
|
||||
for class_ in dontGetDocsOn:
|
||||
|
||||
if isinstance(obj, class_):
|
||||
getDoc = False
|
||||
break
|
||||
|
||||
doc = ''
|
||||
if getDoc:
|
||||
# no need to get this info... too many constants are defined and
|
||||
# makes things much slower (passing all that through sockets takes quite some time)
|
||||
try:
|
||||
doc = inspect.getdoc(obj)
|
||||
if doc is None:
|
||||
doc = ''
|
||||
except: # may happen on jython when checking java classes (so, just ignore it)
|
||||
doc = ''
|
||||
|
||||
|
||||
if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
|
||||
try:
|
||||
args, vargs, kwargs, defaults = inspect.getargspec(obj)
|
||||
except:
|
||||
args, vargs, kwargs, defaults = (('self',), None, None, None)
|
||||
if defaults is not None:
|
||||
start_defaults_at = len(args) - len(defaults)
|
||||
|
||||
|
||||
r = ''
|
||||
for i, a in enumerate(args):
|
||||
|
||||
if len(r) > 0:
|
||||
r = r + ', '
|
||||
|
||||
r = r + str(a)
|
||||
|
||||
if defaults is not None and i >= start_defaults_at:
|
||||
default = defaults[i - start_defaults_at]
|
||||
r += '=' +str(default)
|
||||
|
||||
|
||||
others = ''
|
||||
if vargs:
|
||||
others += '*' + vargs
|
||||
|
||||
if kwargs:
|
||||
if others:
|
||||
others+= ', '
|
||||
others += '**' + kwargs
|
||||
|
||||
if others:
|
||||
r+= ', '
|
||||
|
||||
|
||||
args = '(%s%s)' % (r, others)
|
||||
retType = TYPE_FUNCTION
|
||||
|
||||
elif inspect.isclass(obj):
|
||||
retType = TYPE_CLASS
|
||||
|
||||
elif inspect.ismodule(obj):
|
||||
retType = TYPE_IMPORT
|
||||
|
||||
else:
|
||||
retType = TYPE_ATTR
|
||||
|
||||
|
||||
# add token and doc to return - assure only strings.
|
||||
ret.append((d, doc, args, retType))
|
||||
|
||||
|
||||
else: # getCompleteInfo == False
|
||||
if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj):
|
||||
retType = TYPE_FUNCTION
|
||||
|
||||
elif inspect.isclass(obj):
|
||||
retType = TYPE_CLASS
|
||||
|
||||
elif inspect.ismodule(obj):
|
||||
retType = TYPE_IMPORT
|
||||
|
||||
else:
|
||||
retType = TYPE_ATTR
|
||||
# ok, no complete info, let's try to do this as fast and clean as possible
|
||||
# so, no docs for this kind of information, only the signatures
|
||||
ret.append((d, '', str(args), retType))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# To use when we have some object: i.e.: obj_to_complete=MyModel.objects
|
||||
temp = '''
|
||||
def %(method_name)s%(args)s:
|
||||
"""
|
||||
%(doc)s
|
||||
"""
|
||||
'''
|
||||
|
||||
for entry in GenerateImportsTipForModule(obj_to_complete):
|
||||
import textwrap
|
||||
doc = textwrap.dedent(entry[1])
|
||||
lines = []
|
||||
for line in doc.splitlines():
|
||||
lines.append(' ' + line)
|
||||
doc = '\n'.join(lines)
|
||||
print temp % dict(method_name=entry[0], args=entry[2] or '(self)', doc=doc)
|
||||
@@ -0,0 +1,516 @@
|
||||
import os # @NoMove
|
||||
import sys # @NoMove
|
||||
sys.path.insert(0, os.path.realpath(os.path.abspath('..')))
|
||||
|
||||
import pydevd_reload
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
SAMPLE_CODE = """
|
||||
class C:
|
||||
def foo(self):
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def bar(cls):
|
||||
return (0, 0)
|
||||
|
||||
@staticmethod
|
||||
def stomp():
|
||||
return (0, 0, 0)
|
||||
|
||||
def unchanged(self):
|
||||
return 'unchanged'
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
self.tempdir = None
|
||||
self.save_path = None
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.save_path = list(sys.path)
|
||||
sys.path.append(self.tempdir)
|
||||
try:
|
||||
del sys.modules['x']
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
unittest.TestCase.tearDown(self)
|
||||
sys.path = self.save_path
|
||||
try:
|
||||
del sys.modules['x']
|
||||
except:
|
||||
pass
|
||||
|
||||
def make_mod(self, name="x", repl=None, subst=None, sample=SAMPLE_CODE):
|
||||
fn = os.path.join(self.tempdir, name + ".py")
|
||||
f = open(fn, "w")
|
||||
if repl is not None and subst is not None:
|
||||
sample = sample.replace(repl, subst)
|
||||
try:
|
||||
f.write(sample)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
def test_pydevd_reload(self):
|
||||
|
||||
self.make_mod()
|
||||
import x
|
||||
|
||||
C = x.C
|
||||
COut = C
|
||||
Cfoo = C.foo
|
||||
Cbar = C.bar
|
||||
Cstomp = C.stomp
|
||||
|
||||
def check2(expected):
|
||||
C = x.C
|
||||
Cfoo = C.foo
|
||||
Cbar = C.bar
|
||||
Cstomp = C.stomp
|
||||
b = C()
|
||||
bfoo = b.foo
|
||||
self.assertEqual(expected, b.foo())
|
||||
self.assertEqual(expected, bfoo())
|
||||
self.assertEqual(expected, Cfoo(b))
|
||||
|
||||
def check(expected):
|
||||
b = COut()
|
||||
bfoo = b.foo
|
||||
self.assertEqual(expected, b.foo())
|
||||
self.assertEqual(expected, bfoo())
|
||||
self.assertEqual(expected, Cfoo(b))
|
||||
self.assertEqual((expected, expected), Cbar())
|
||||
self.assertEqual((expected, expected, expected), Cstomp())
|
||||
check2(expected)
|
||||
|
||||
check(0)
|
||||
|
||||
# modify mod and reload
|
||||
count = 0
|
||||
while count < 1:
|
||||
count += 1
|
||||
self.make_mod(repl="0", subst=str(count))
|
||||
pydevd_reload.xreload(x)
|
||||
check(count)
|
||||
|
||||
|
||||
def test_pydevd_reload2(self):
|
||||
|
||||
self.make_mod()
|
||||
import x
|
||||
|
||||
c = x.C()
|
||||
cfoo = c.foo
|
||||
self.assertEqual(0, c.foo())
|
||||
self.assertEqual(0, cfoo())
|
||||
|
||||
self.make_mod(repl="0", subst='1')
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(1, c.foo())
|
||||
self.assertEqual(1, cfoo())
|
||||
|
||||
def test_pydevd_reload3(self):
|
||||
class F:
|
||||
def m1(self):
|
||||
return 1
|
||||
class G:
|
||||
def m1(self):
|
||||
return 2
|
||||
|
||||
self.assertEqual(F().m1(), 1)
|
||||
pydevd_reload.Reload(None)._update(None, None, F, G)
|
||||
self.assertEqual(F().m1(), 2)
|
||||
|
||||
|
||||
def test_pydevd_reload4(self):
|
||||
class F:
|
||||
pass
|
||||
F.m1 = lambda a:None
|
||||
class G:
|
||||
pass
|
||||
G.m1 = lambda a:10
|
||||
|
||||
self.assertEqual(F().m1(), None)
|
||||
pydevd_reload.Reload(None)._update(None, None, F, G)
|
||||
self.assertEqual(F().m1(), 10)
|
||||
|
||||
|
||||
|
||||
def test_if_code_obj_equals(self):
|
||||
class F:
|
||||
def m1(self):
|
||||
return 1
|
||||
class G:
|
||||
def m1(self):
|
||||
return 1
|
||||
class H:
|
||||
def m1(self):
|
||||
return 2
|
||||
|
||||
if hasattr(F.m1, 'func_code'):
|
||||
self.assertTrue(pydevd_reload.code_objects_equal(F.m1.func_code, G.m1.func_code))
|
||||
self.assertFalse(pydevd_reload.code_objects_equal(F.m1.func_code, H.m1.func_code))
|
||||
else:
|
||||
self.assertTrue(pydevd_reload.code_objects_equal(F.m1.__code__, G.m1.__code__))
|
||||
self.assertFalse(pydevd_reload.code_objects_equal(F.m1.__code__, H.m1.__code__))
|
||||
|
||||
|
||||
|
||||
def test_metaclass(self):
|
||||
|
||||
class Meta(type):
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(Meta, cls).__init__(name, bases, attrs)
|
||||
|
||||
class F:
|
||||
__metaclass__ = Meta
|
||||
|
||||
def m1(self):
|
||||
return 1
|
||||
|
||||
|
||||
class G:
|
||||
__metaclass__ = Meta
|
||||
|
||||
def m1(self):
|
||||
return 2
|
||||
|
||||
self.assertEqual(F().m1(), 1)
|
||||
pydevd_reload.Reload(None)._update(None, None, F, G)
|
||||
self.assertEqual(F().m1(), 2)
|
||||
|
||||
|
||||
|
||||
def test_change_hierarchy(self):
|
||||
|
||||
class F(object):
|
||||
|
||||
def m1(self):
|
||||
return 1
|
||||
|
||||
|
||||
class B(object):
|
||||
def super_call(self):
|
||||
return 2
|
||||
|
||||
class G(B):
|
||||
|
||||
def m1(self):
|
||||
return self.super_call()
|
||||
|
||||
self.assertEqual(F().m1(), 1)
|
||||
old = pydevd_reload.notify_error
|
||||
self._called = False
|
||||
def on_error(*args):
|
||||
self._called = True
|
||||
try:
|
||||
pydevd_reload.notify_error = on_error
|
||||
pydevd_reload.Reload(None)._update(None, None, F, G)
|
||||
self.assertTrue(self._called)
|
||||
finally:
|
||||
pydevd_reload.notify_error = old
|
||||
|
||||
|
||||
def test_change_hierarchy_old_style(self):
|
||||
|
||||
class F:
|
||||
|
||||
def m1(self):
|
||||
return 1
|
||||
|
||||
|
||||
class B:
|
||||
def super_call(self):
|
||||
return 2
|
||||
|
||||
class G(B):
|
||||
|
||||
def m1(self):
|
||||
return self.super_call()
|
||||
|
||||
|
||||
self.assertEqual(F().m1(), 1)
|
||||
old = pydevd_reload.notify_error
|
||||
self._called = False
|
||||
def on_error(*args):
|
||||
self._called = True
|
||||
try:
|
||||
pydevd_reload.notify_error = on_error
|
||||
pydevd_reload.Reload(None)._update(None, None, F, G)
|
||||
self.assertTrue(self._called)
|
||||
finally:
|
||||
pydevd_reload.notify_error = old
|
||||
|
||||
|
||||
def test_create_class(self):
|
||||
SAMPLE_CODE1 = """
|
||||
class C:
|
||||
def foo(self):
|
||||
return 0
|
||||
"""
|
||||
# Creating a new class and using it from old class
|
||||
SAMPLE_CODE2 = """
|
||||
class B:
|
||||
pass
|
||||
|
||||
class C:
|
||||
def foo(self):
|
||||
return B
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.C().foo
|
||||
self.assertEqual(foo(), 0)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo().__name__, 'B')
|
||||
|
||||
def test_create_class2(self):
|
||||
SAMPLE_CODE1 = """
|
||||
class C(object):
|
||||
def foo(self):
|
||||
return 0
|
||||
"""
|
||||
# Creating a new class and using it from old class
|
||||
SAMPLE_CODE2 = """
|
||||
class B(object):
|
||||
pass
|
||||
|
||||
class C(object):
|
||||
def foo(self):
|
||||
return B
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.C().foo
|
||||
self.assertEqual(foo(), 0)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo().__name__, 'B')
|
||||
|
||||
def test_parent_function(self):
|
||||
SAMPLE_CODE1 = """
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return 0
|
||||
|
||||
class C(B):
|
||||
def call(self):
|
||||
return self.foo()
|
||||
"""
|
||||
# Creating a new class and using it from old class
|
||||
SAMPLE_CODE2 = """
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return 0
|
||||
def bar(self):
|
||||
return 'bar'
|
||||
|
||||
class C(B):
|
||||
def call(self):
|
||||
return self.bar()
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
call = x.C().call
|
||||
self.assertEqual(call(), 0)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(call(), 'bar')
|
||||
|
||||
|
||||
def test_update_constant(self):
|
||||
SAMPLE_CODE1 = """
|
||||
CONSTANT = 1
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
CONSTANT = 2
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.B().foo
|
||||
self.assertEqual(foo(), 1)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo(), 1) #Just making it explicit we don't reload constants.
|
||||
|
||||
|
||||
def test_update_constant_with_custom_code(self):
|
||||
SAMPLE_CODE1 = """
|
||||
CONSTANT = 1
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
CONSTANT = 2
|
||||
|
||||
def __xreload_old_new__(namespace, name, old, new):
|
||||
if name == 'CONSTANT':
|
||||
namespace[name] = new
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.B().foo
|
||||
self.assertEqual(foo(), 1)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo(), 2) #Actually updated it now!
|
||||
|
||||
|
||||
def test_reload_custom_code_after_changes(self):
|
||||
SAMPLE_CODE1 = """
|
||||
CONSTANT = 1
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
CONSTANT = 1
|
||||
|
||||
def __xreload_after_reload_update__(namespace):
|
||||
namespace['CONSTANT'] = 2
|
||||
|
||||
class B(object):
|
||||
def foo(self):
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.B().foo
|
||||
self.assertEqual(foo(), 1)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo(), 2) #Actually updated it now!
|
||||
|
||||
|
||||
def test_reload_custom_code_after_changes_in_class(self):
|
||||
SAMPLE_CODE1 = """
|
||||
|
||||
class B(object):
|
||||
CONSTANT = 1
|
||||
|
||||
def foo(self):
|
||||
return self.CONSTANT
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
|
||||
|
||||
class B(object):
|
||||
CONSTANT = 1
|
||||
|
||||
@classmethod
|
||||
def __xreload_after_reload_update__(cls):
|
||||
cls.CONSTANT = 2
|
||||
|
||||
def foo(self):
|
||||
return self.CONSTANT
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.B().foo
|
||||
self.assertEqual(foo(), 1)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo(), 2) #Actually updated it now!
|
||||
|
||||
|
||||
def test_update_constant_with_custom_code(self):
|
||||
SAMPLE_CODE1 = """
|
||||
|
||||
class B(object):
|
||||
CONSTANT = 1
|
||||
|
||||
def foo(self):
|
||||
return self.CONSTANT
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
|
||||
|
||||
class B(object):
|
||||
|
||||
CONSTANT = 2
|
||||
|
||||
def __xreload_old_new__(cls, name, old, new):
|
||||
if name == 'CONSTANT':
|
||||
cls.CONSTANT = new
|
||||
__xreload_old_new__ = classmethod(__xreload_old_new__)
|
||||
|
||||
def foo(self):
|
||||
return self.CONSTANT
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
foo = x.B().foo
|
||||
self.assertEqual(foo(), 1)
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
self.assertEqual(foo(), 2) #Actually updated it now!
|
||||
|
||||
|
||||
def test_update_with_slots(self):
|
||||
SAMPLE_CODE1 = """
|
||||
class B(object):
|
||||
|
||||
__slots__ = ['bar']
|
||||
|
||||
"""
|
||||
SAMPLE_CODE2 = """
|
||||
class B(object):
|
||||
|
||||
__slots__ = ['bar', 'foo']
|
||||
|
||||
def m1(self):
|
||||
self.bar = 10
|
||||
return 1
|
||||
|
||||
"""
|
||||
|
||||
self.make_mod(sample=SAMPLE_CODE1)
|
||||
import x
|
||||
B = x.B
|
||||
self.make_mod(sample=SAMPLE_CODE2)
|
||||
pydevd_reload.xreload(x)
|
||||
b = B()
|
||||
self.assertEqual(1, b.m1())
|
||||
self.assertEqual(10, b.bar)
|
||||
self.assertRaises(Exception, setattr, b, 'foo', 20) #__slots__ can't be updated
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# import sys;sys.argv = ['', 'Test.test_reload_custom_code_after_changes_in_class']
|
||||
unittest.main()
|
||||
@@ -0,0 +1 @@
|
||||
(no __init__.py file)
|
||||
@@ -0,0 +1,105 @@
|
||||
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
|
||||
|
||||
try:
|
||||
raw_input
|
||||
raw_input_name = 'raw_input'
|
||||
except NameError:
|
||||
raw_input_name = 'input'
|
||||
|
||||
#=======================================================================================================================
|
||||
# Test
|
||||
#=======================================================================================================================
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def startClientThread(self, client_port):
|
||||
class ClientThread(threading.Thread):
|
||||
def __init__(self, client_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
|
||||
def run(self):
|
||||
class HandleRequestInput:
|
||||
def RequestInput(self):
|
||||
return 'RequestInput: OK'
|
||||
|
||||
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)
|
||||
client_server.register_function(handle_request_input.RequestInput)
|
||||
client_server.serve_forever()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
|
||||
def getFreeAddresses(self):
|
||||
import socket
|
||||
s = socket.socket()
|
||||
s.bind(('', 0))
|
||||
port0 = s.getsockname()[1]
|
||||
|
||||
s1 = socket.socket()
|
||||
s1.bind(('', 0))
|
||||
port1 = s1.getsockname()[1]
|
||||
s.close()
|
||||
s1.close()
|
||||
return port0, port1
|
||||
|
||||
|
||||
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
|
||||
print('Starting server with:', pydev_localhost.get_localhost(), self.server_port, self.client_port)
|
||||
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("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)')
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
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
|
||||
|
||||
#=======================================================================================================================
|
||||
# Test
|
||||
#=======================================================================================================================
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def testGetReferrers1(self):
|
||||
|
||||
container = []
|
||||
contained = [1, 2]
|
||||
container.append(0)
|
||||
container.append(contained)
|
||||
|
||||
# Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
|
||||
# we should skip temporary references inside the get_referrer_info.
|
||||
result = pydevd_referrers.get_referrer_info(contained)
|
||||
assert 'list[1]' in result
|
||||
pydevd_referrers.print_referrers(contained, stream=StringIO())
|
||||
|
||||
def testGetReferrers2(self):
|
||||
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
contained = [1, 2]
|
||||
obj = MyClass()
|
||||
obj.contained = contained
|
||||
del contained
|
||||
|
||||
# Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
|
||||
# we should skip temporary references inside the get_referrer_info.
|
||||
result = pydevd_referrers.get_referrer_info(obj.contained)
|
||||
assert 'found_as="contained"' in result
|
||||
assert 'MyClass' in result
|
||||
|
||||
|
||||
def testGetReferrers3(self):
|
||||
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
contained = [1, 2]
|
||||
obj = MyClass()
|
||||
obj.contained = contained
|
||||
del contained
|
||||
|
||||
# Ok, we have the contained in this frame and inside the given list (which on turn is in this frame too).
|
||||
# we should skip temporary references inside the get_referrer_info.
|
||||
result = pydevd_referrers.get_referrer_info(obj.contained)
|
||||
assert 'found_as="contained"' in result
|
||||
assert 'MyClass' in result
|
||||
|
||||
|
||||
def testGetReferrers4(self):
|
||||
|
||||
class MyClass(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
obj = MyClass()
|
||||
obj.me = obj
|
||||
|
||||
# Let's see if we detect the cycle...
|
||||
result = pydevd_referrers.get_referrer_info(obj)
|
||||
assert 'found_as="me"' in result #Cyclic ref
|
||||
|
||||
|
||||
def testGetReferrers5(self):
|
||||
container = dict(a=[1])
|
||||
|
||||
# Let's see if we detect the cycle...
|
||||
result = pydevd_referrers.get_referrer_info(container['a'])
|
||||
assert 'testGetReferrers5' not in result #I.e.: NOT in the current method
|
||||
assert 'found_as="a"' in result
|
||||
assert 'dict' in result
|
||||
assert str(id(container)) in result
|
||||
|
||||
|
||||
def testGetReferrers6(self):
|
||||
container = dict(a=[1])
|
||||
|
||||
def should_appear(obj):
|
||||
# Let's see if we detect the cycle...
|
||||
return pydevd_referrers.get_referrer_info(obj)
|
||||
|
||||
result = should_appear(container['a'])
|
||||
assert 'should_appear' in result
|
||||
|
||||
|
||||
def testGetReferrers7(self):
|
||||
|
||||
class MyThread(threading.Thread):
|
||||
def run(self):
|
||||
#Note: we do that because if we do
|
||||
self.frame = sys._getframe()
|
||||
|
||||
t = MyThread()
|
||||
t.start()
|
||||
while not hasattr(t, 'frame'):
|
||||
time.sleep(0.01)
|
||||
|
||||
result = pydevd_referrers.get_referrer_info(t.frame)
|
||||
assert 'MyThread' in result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#this is so that we can run it frem the jython tests -- because we don't actually have an __main__ module
|
||||
#(so, it won't try importing the __main__ module)
|
||||
try:
|
||||
import gc
|
||||
gc.get_referrers(unittest)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
unittest.TextTestRunner().run(unittest.makeSuite(Test))
|
||||
@@ -0,0 +1,165 @@
|
||||
'''
|
||||
@author Fabio Zadrozny
|
||||
'''
|
||||
import sys
|
||||
import unittest
|
||||
import socket
|
||||
import urllib
|
||||
|
||||
|
||||
IS_JYTHON = sys.platform.find('java') != -1
|
||||
|
||||
if IS_JYTHON:
|
||||
import os
|
||||
|
||||
#make it as if we were executing from the directory above this one (so that we can use jycompletionserver
|
||||
#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])))
|
||||
|
||||
import pycompletionserver as jycompletionserver
|
||||
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
def dbg(s):
|
||||
if DEBUG:
|
||||
sys.stdout.write('TEST %s\n' % s)
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
unittest.TestCase.tearDown(self)
|
||||
|
||||
def testIt(self):
|
||||
dbg('ok')
|
||||
|
||||
def testMessage(self):
|
||||
t = jycompletionserver.T(0)
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'description' , 'args'))
|
||||
l.append(('Def1', 'description1', 'args1'))
|
||||
l.append(('Def2', 'description2', 'args2'))
|
||||
|
||||
msg = t.processor.formatCompletionMessage('test_jyserver.py', l)
|
||||
|
||||
self.assertEquals('@@COMPLETIONS(test_jyserver.py,(Def,description,args),(Def1,description1,args1),(Def2,description2,args2))END@@', msg)
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'desc,,r,,i()ption', ''))
|
||||
l.append(('Def(1', 'descriptio(n1', ''))
|
||||
l.append(('De,f)2', 'de,s,c,ription2', ''))
|
||||
msg = t.processor.formatCompletionMessage(None, l)
|
||||
expected = '@@COMPLETIONS(None,(Def,desc%2C%2Cr%2C%2Ci%28%29ption, ),(Def%281,descriptio%28n1, ),(De%2Cf%292,de%2Cs%2Cc%2Cription2, ))END@@'
|
||||
|
||||
self.assertEquals(expected, msg)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def testCompletionSocketsAndMessages(self):
|
||||
dbg('testCompletionSocketsAndMessages')
|
||||
t, socket = self.createConnections()
|
||||
self.socket = socket
|
||||
dbg('connections created')
|
||||
|
||||
try:
|
||||
#now that we have the connections all set up, check the code completion messages.
|
||||
msg = urllib.quote_plus('math')
|
||||
|
||||
toWrite = '@@IMPORTS:%sEND@@' % msg
|
||||
dbg('writing' + str(toWrite))
|
||||
socket.send(toWrite) #math completions
|
||||
completions = self.readMsg()
|
||||
dbg(urllib.unquote_plus(completions))
|
||||
|
||||
start = '@@COMPLETIONS('
|
||||
self.assert_(completions.startswith(start), '%s DOESNT START WITH %s' % (completions, start))
|
||||
self.assert_(completions.find('@@COMPLETIONS') != -1)
|
||||
self.assert_(completions.find('END@@') != -1)
|
||||
|
||||
|
||||
msg = urllib.quote_plus('__builtin__.str')
|
||||
toWrite = '@@IMPORTS:%sEND@@' % msg
|
||||
dbg('writing' + str(toWrite))
|
||||
socket.send(toWrite) #math completions
|
||||
completions = self.readMsg()
|
||||
dbg(urllib.unquote_plus(completions))
|
||||
|
||||
start = '@@COMPLETIONS('
|
||||
self.assert_(completions.startswith(start), '%s DOESNT START WITH %s' % (completions, start))
|
||||
self.assert_(completions.find('@@COMPLETIONS') != -1)
|
||||
self.assert_(completions.find('END@@') != -1)
|
||||
|
||||
|
||||
|
||||
finally:
|
||||
try:
|
||||
self.sendKillMsg(socket)
|
||||
|
||||
|
||||
while not t.ended:
|
||||
pass #wait until it receives the message and quits.
|
||||
|
||||
|
||||
socket.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def createConnections(self, p1=50001):
|
||||
'''
|
||||
Creates the connections needed for testing.
|
||||
'''
|
||||
t = jycompletionserver.T(p1)
|
||||
|
||||
t.start()
|
||||
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.bind((jycompletionserver.HOST, p1))
|
||||
server.listen(1)
|
||||
|
||||
sock, _addr = server.accept()
|
||||
|
||||
return t, sock
|
||||
|
||||
|
||||
def readMsg(self):
|
||||
msg = '@@PROCESSING_END@@'
|
||||
while msg.startswith('@@PROCESSING'):
|
||||
msg = self.socket.recv(1024)
|
||||
if msg.startswith('@@PROCESSING:'):
|
||||
dbg('Status msg:' + str(msg))
|
||||
|
||||
while msg.find('END@@') == -1:
|
||||
msg += self.socket.recv(1024)
|
||||
|
||||
return msg
|
||||
|
||||
def sendKillMsg(self, socket):
|
||||
socket.send(jycompletionserver.MSG_KILL_SERVER)
|
||||
|
||||
|
||||
|
||||
|
||||
#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython D:\eclipse_workspace\org.python.pydev\pysrc\pycompletionserver.py 53795 58659
|
||||
#
|
||||
#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython D:\eclipse_workspace\org.python.pydev\pysrc\tests\test_jyserver.py
|
||||
#
|
||||
#"C:\Program Files\Java\jdk1.5.0_04\bin\java.exe" -Dpython.path="C:\bin\jython21\Lib";"C:\bin\jython21";"C:\Program Files\Java\jdk1.5.0_04\jre\lib\rt.jar" -classpath C:/bin/jython21/jython.jar org.python.util.jython d:\runtime-workbench-workspace\jython_test\src\test.py
|
||||
if __name__ == '__main__':
|
||||
if IS_JYTHON:
|
||||
suite = unittest.makeSuite(Test)
|
||||
unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
else:
|
||||
sys.stdout.write('Not running jython tests for non-java platform: %s' % sys.platform)
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#line to run:
|
||||
#java -classpath D:\bin\jython-2.1\jython.jar;D:\bin\eclipse331_1\plugins\org.junit_3.8.2.v200706111738\junit.jar;D:\bin\eclipse331_1\plugins\org.apache.ant_1.7.0.v200706080842\lib\ant.jar org.python.util.jython w:\org.python.pydev\pysrc\tests\test_jysimpleTipper.py
|
||||
|
||||
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
|
||||
|
||||
if sys.platform.find('java') != -1:
|
||||
from _pydev_jy_imports_tipper import ismethod
|
||||
from _pydev_jy_imports_tipper import isclass
|
||||
from _pydev_jy_imports_tipper import dirObj
|
||||
import _pydev_jy_imports_tipper
|
||||
from java.lang.reflect import Method #@UnresolvedImport
|
||||
from java.lang import System #@UnresolvedImport
|
||||
from java.lang import String #@UnresolvedImport
|
||||
from java.lang.System import arraycopy #@UnresolvedImport
|
||||
from java.lang.System import out #@UnresolvedImport
|
||||
import java.lang.String #@UnresolvedImport
|
||||
|
||||
__DBG = 0
|
||||
def dbg(s):
|
||||
if __DBG:
|
||||
sys.stdout.write('%s\n' % (s,))
|
||||
|
||||
|
||||
|
||||
class TestMod(unittest.TestCase):
|
||||
|
||||
def assertArgs(self, tok, args, tips):
|
||||
for a in tips:
|
||||
if tok == a[0]:
|
||||
self.assertEquals(args, a[2])
|
||||
return
|
||||
raise AssertionError('%s not in %s', tok, tips)
|
||||
|
||||
def assertIn(self, tok, tips):
|
||||
self.assertEquals(4, len(tips[0]))
|
||||
for a in tips:
|
||||
if tok == a[0]:
|
||||
return a
|
||||
s = ''
|
||||
for a in tips:
|
||||
s += str(a)
|
||||
s += '\n'
|
||||
raise AssertionError('%s not in %s' % (tok, s))
|
||||
|
||||
def testImports1a(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('java.util.HashMap')
|
||||
assert f.endswith('rt.jar')
|
||||
|
||||
def testImports1c(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.Class')
|
||||
assert f.endswith('rt.jar')
|
||||
|
||||
def testImports1b(self):
|
||||
try:
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.m')
|
||||
self.fail('err')
|
||||
except:
|
||||
pass
|
||||
|
||||
def testImports1(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('junit.framework.TestCase')
|
||||
assert f.endswith('junit.jar')
|
||||
ret = self.assertIn('assertEquals', tip)
|
||||
# self.assertEquals('', ret[2])
|
||||
|
||||
def testImports2(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('junit.framework')
|
||||
assert f.endswith('junit.jar')
|
||||
ret = self.assertIn('TestCase', tip)
|
||||
self.assertEquals('', ret[2])
|
||||
|
||||
def testImports2a(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('org.apache.tools.ant')
|
||||
assert f.endswith('ant.jar')
|
||||
ret = self.assertIn('Task', tip)
|
||||
self.assertEquals('', ret[2])
|
||||
|
||||
def testImports3(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('os')
|
||||
assert f.endswith('os.py')
|
||||
ret = self.assertIn('path', tip)
|
||||
self.assertEquals('', ret[2])
|
||||
|
||||
def testTipOnString(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('string')
|
||||
self.assertIn('join', tip)
|
||||
self.assertIn('uppercase', tip)
|
||||
|
||||
def testImports(self):
|
||||
tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__')[1]
|
||||
self.assertIn('tuple' , tip)
|
||||
self.assertIn('RuntimeError' , tip)
|
||||
self.assertIn('RuntimeWarning' , tip)
|
||||
|
||||
def testImports5(self):
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('java.lang')
|
||||
assert f.endswith('rt.jar')
|
||||
tup = self.assertIn('String' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_CLASS), tup[3])
|
||||
|
||||
tip = _pydev_jy_imports_tipper.GenerateTip('java')[1]
|
||||
tup = self.assertIn('lang' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_IMPORT), tup[3])
|
||||
|
||||
tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.String')[1]
|
||||
tup = self.assertIn('indexOf' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
|
||||
|
||||
tip = _pydev_jy_imports_tipper.GenerateTip('java.lang.String')[1]
|
||||
tup = self.assertIn('charAt' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
|
||||
self.assertEquals('(int)', tup[2])
|
||||
|
||||
tup = self.assertIn('format' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
|
||||
self.assertEquals('(string, objectArray)', tup[2])
|
||||
self.assert_(tup[1].find('[Ljava.lang.Object;') == -1)
|
||||
|
||||
tup = self.assertIn('getBytes' , tip)
|
||||
self.assertEquals(str(_pydev_jy_imports_tipper.TYPE_FUNCTION), tup[3])
|
||||
self.assert_(tup[1].find('[B') == -1)
|
||||
self.assert_(tup[1].find('byte[]') != -1)
|
||||
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.str')
|
||||
assert f.endswith('jython.jar')
|
||||
self.assertIn('find' , tip)
|
||||
|
||||
f, tip = _pydev_jy_imports_tipper.GenerateTip('__builtin__.dict')
|
||||
assert f.endswith('jython.jar')
|
||||
self.assertIn('get' , tip)
|
||||
|
||||
|
||||
class TestSearch(unittest.TestCase):
|
||||
|
||||
def testSearchOnJython(self):
|
||||
self.assertEqual('javaos.py', _pydev_jy_imports_tipper.Search('os')[0][0].split(os.sep)[-1])
|
||||
self.assertEqual(0, _pydev_jy_imports_tipper.Search('os')[0][1])
|
||||
|
||||
self.assertEqual('javaos.py', _pydev_jy_imports_tipper.Search('os.makedirs')[0][0].split(os.sep)[-1])
|
||||
self.assertNotEqual(0, _pydev_jy_imports_tipper.Search('os.makedirs')[0][1])
|
||||
|
||||
#print _pydev_jy_imports_tipper.Search('os.makedirs')
|
||||
|
||||
class TestCompl(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
unittest.TestCase.tearDown(self)
|
||||
|
||||
def testGettingInfoOnJython(self):
|
||||
|
||||
dbg('\n\n--------------------------- java')
|
||||
assert not ismethod(java)[0]
|
||||
assert not isclass(java)
|
||||
assert _pydev_jy_imports_tipper.ismodule(java)
|
||||
|
||||
dbg('\n\n--------------------------- java.lang')
|
||||
assert not ismethod(java.lang)[0]
|
||||
assert not isclass(java.lang)
|
||||
assert _pydev_jy_imports_tipper.ismodule(java.lang)
|
||||
|
||||
dbg('\n\n--------------------------- Method')
|
||||
assert not ismethod(Method)[0]
|
||||
assert isclass(Method)
|
||||
|
||||
dbg('\n\n--------------------------- System')
|
||||
assert not ismethod(System)[0]
|
||||
assert isclass(System)
|
||||
|
||||
dbg('\n\n--------------------------- String')
|
||||
assert not ismethod(System)[0]
|
||||
assert isclass(String)
|
||||
assert len(dirObj(String)) > 10
|
||||
|
||||
dbg('\n\n--------------------------- arraycopy')
|
||||
isMet = ismethod(arraycopy)
|
||||
assert isMet[0]
|
||||
assert isMet[1][0].basicAsStr() == "function:arraycopy args=['java.lang.Object', 'int', 'java.lang.Object', 'int', 'int'], varargs=None, kwargs=None, docs:None"
|
||||
assert not isclass(arraycopy)
|
||||
|
||||
dbg('\n\n--------------------------- out')
|
||||
isMet = ismethod(out)
|
||||
assert not isMet[0]
|
||||
assert not isclass(out)
|
||||
|
||||
dbg('\n\n--------------------------- out.println')
|
||||
isMet = ismethod(out.println) #@UndefinedVariable
|
||||
assert isMet[0]
|
||||
assert len(isMet[1]) == 10
|
||||
self.assertEquals(isMet[1][0].basicAsStr(), "function:println args=[], varargs=None, kwargs=None, docs:None")
|
||||
assert isMet[1][1].basicAsStr() == "function:println args=['long'], varargs=None, kwargs=None, docs:None"
|
||||
assert not isclass(out.println) #@UndefinedVariable
|
||||
|
||||
dbg('\n\n--------------------------- str')
|
||||
isMet = ismethod(str)
|
||||
#the code below should work, but is failing on jython 22a1
|
||||
#assert isMet[0]
|
||||
#assert isMet[1][0].basicAsStr() == "function:str args=['org.python.core.PyObject'], varargs=None, kwargs=None, docs:None"
|
||||
assert not isclass(str)
|
||||
|
||||
|
||||
def met1():
|
||||
a = 3
|
||||
return a
|
||||
|
||||
dbg('\n\n--------------------------- met1')
|
||||
isMet = ismethod(met1)
|
||||
assert isMet[0]
|
||||
assert isMet[1][0].basicAsStr() == "function:met1 args=[], varargs=None, kwargs=None, docs:None"
|
||||
assert not isclass(met1)
|
||||
|
||||
def met2(arg1, arg2, *vararg, **kwarg):
|
||||
'''docmet2'''
|
||||
|
||||
a = 1
|
||||
return a
|
||||
|
||||
dbg('\n\n--------------------------- met2')
|
||||
isMet = ismethod(met2)
|
||||
assert isMet[0]
|
||||
assert isMet[1][0].basicAsStr() == "function:met2 args=['arg1', 'arg2'], varargs=vararg, kwargs=kwarg, docs:docmet2"
|
||||
assert not isclass(met2)
|
||||
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,80 @@
|
||||
#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')
|
||||
@@ -0,0 +1,193 @@
|
||||
import sys
|
||||
import unittest
|
||||
import threading
|
||||
import os
|
||||
from nose.tools import eq_
|
||||
from pydev_imports import StringIO, SimpleXMLRPCServer
|
||||
from pydev_localhost import get_localhost
|
||||
from pydev_console_utils import StdIn
|
||||
import socket
|
||||
|
||||
# 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])))
|
||||
|
||||
# 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)
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestPyDevFrontEnd(TestBase):
|
||||
def testAddExec_1(self):
|
||||
addExec('if True:', True)
|
||||
def testAddExec_2(self):
|
||||
addExec('if True:\n testAddExec_a = 10\n', True)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def testComplete(self):
|
||||
unused_text, matches = 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_')
|
||||
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_')
|
||||
matches = [f[0] for f in res]
|
||||
assert len(matches) == 3
|
||||
eq_(set(['testComplete_a', 'testComplete_b', 'testComplete_c']), set(matches))
|
||||
|
||||
def testGetCompletions_2(self):
|
||||
# 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(', '')
|
||||
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')
|
||||
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')
|
||||
|
||||
def testQuestionMark_1(self):
|
||||
addExec('?')
|
||||
assert len(stdout.getvalue()) > 1000, 'IPython help should be pretty big'
|
||||
|
||||
def testQuestionMark_2(self):
|
||||
addExec('int?')
|
||||
assert stdout.getvalue().find('Convert') != -1
|
||||
|
||||
|
||||
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
|
||||
|
||||
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')
|
||||
|
||||
def testEdit(self):
|
||||
''' Make sure we can issue an edit command '''
|
||||
called_RequestInput = [False]
|
||||
called_IPythonEditor = [False]
|
||||
def startClientThread(client_port):
|
||||
class ClientThread(threading.Thread):
|
||||
def __init__(self, client_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
def run(self):
|
||||
class HandleRequestInput:
|
||||
def RequestInput(self):
|
||||
called_RequestInput[0] = True
|
||||
return '\n'
|
||||
def IPythonEditor(self, name, line):
|
||||
called_IPythonEditor[0] = (name, line)
|
||||
return True
|
||||
|
||||
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.IPythonEditor)
|
||||
client_server.serve_forever()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
startClientThread(client_port)
|
||||
orig_stdin = sys.stdin
|
||||
sys.stdin = StdIn(self, get_localhost(), client_port)
|
||||
try:
|
||||
filename = 'made_up_file.py'
|
||||
addExec('%edit ' + filename)
|
||||
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
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#Just doing: unittest.main() was not working for me when run directly (not sure why)
|
||||
#And doing it the way below the test with the import: from pydev_ipython.inputhook import get_inputhook, set_stdin_file
|
||||
#is failing (but if I do a Ctrl+F9 in PyDev to run it, it works properly, so, I'm a bit puzzled here).
|
||||
unittest.TextTestRunner(verbosity=1).run(unittest.makeSuite(TestRunningCode))
|
||||
unittest.TextTestRunner(verbosity=1).run(unittest.makeSuite(TestPyDevFrontEnd))
|
||||
@@ -0,0 +1,231 @@
|
||||
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
|
||||
|
||||
try:
|
||||
raw_input
|
||||
raw_input_name = 'raw_input'
|
||||
except NameError:
|
||||
raw_input_name = 'input'
|
||||
|
||||
#=======================================================================================================================
|
||||
# Test
|
||||
#=======================================================================================================================
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def setUp(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")
|
||||
|
||||
|
||||
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()
|
||||
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,))
|
||||
|
||||
|
||||
def startClientThread(self, client_port):
|
||||
class ClientThread(threading.Thread):
|
||||
def __init__(self, client_port):
|
||||
threading.Thread.__init__(self)
|
||||
self.client_port = client_port
|
||||
def run(self):
|
||||
class HandleRequestInput:
|
||||
def RequestInput(self):
|
||||
return 'input_request'
|
||||
|
||||
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.serve_forever()
|
||||
|
||||
client_thread = ClientThread(client_port)
|
||||
client_thread.setDaemon(True)
|
||||
client_thread.start()
|
||||
return client_thread
|
||||
|
||||
|
||||
def startDebuggerServerThread(self, debugger_port, socket_code):
|
||||
class DebuggerServerThread(threading.Thread):
|
||||
def __init__(self, debugger_port, socket_code):
|
||||
threading.Thread.__init__(self)
|
||||
self.debugger_port = debugger_port
|
||||
self.socket_code = socket_code
|
||||
def run(self):
|
||||
import socket
|
||||
s = socket.socket()
|
||||
s.bind(('', debugger_port))
|
||||
s.listen(1)
|
||||
socket, unused_addr = s.accept()
|
||||
socket_code(socket)
|
||||
|
||||
debugger_thread = DebuggerServerThread(debugger_port, socket_code)
|
||||
debugger_thread.setDaemon(True)
|
||||
debugger_thread.start()
|
||||
return debugger_thread
|
||||
|
||||
|
||||
def getFreeAddresses(self):
|
||||
import socket
|
||||
s = socket.socket()
|
||||
s.bind(('', 0))
|
||||
port0 = s.getsockname()[1]
|
||||
|
||||
s1 = socket.socket()
|
||||
s1.bind(('', 0))
|
||||
port1 = s1.getsockname()[1]
|
||||
s.close()
|
||||
s1.close()
|
||||
|
||||
if port0 <= 0 or port1 <= 0:
|
||||
#This happens in Jython...
|
||||
from java.net import ServerSocket
|
||||
s0 = ServerSocket(0)
|
||||
port0 = s0.getLocalPort()
|
||||
|
||||
s1 = ServerSocket(0)
|
||||
port1 = s1.getLocalPort()
|
||||
|
||||
s0.close()
|
||||
s1.close()
|
||||
|
||||
assert port0 != port1
|
||||
assert port0 > 0
|
||||
assert port1 > 0
|
||||
|
||||
return port0, port1
|
||||
|
||||
|
||||
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())
|
||||
|
||||
#=======================================================================================================================
|
||||
# main
|
||||
#=======================================================================================================================
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
'''
|
||||
@author Fabio Zadrozny
|
||||
'''
|
||||
import sys
|
||||
import os
|
||||
|
||||
#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])))
|
||||
|
||||
IS_PYTHON_3K = 0
|
||||
if sys.platform.find('java') == -1:
|
||||
|
||||
|
||||
try:
|
||||
import inspect
|
||||
import pycompletionserver
|
||||
import socket
|
||||
try:
|
||||
from urllib import quote_plus, unquote_plus
|
||||
def send(s, msg):
|
||||
s.send(msg)
|
||||
except ImportError:
|
||||
IS_PYTHON_3K = 1
|
||||
from urllib.parse import quote_plus, unquote_plus #Python 3.0
|
||||
def send(s, msg):
|
||||
s.send(bytearray(msg, 'utf-8'))
|
||||
except ImportError:
|
||||
pass #Not available in jython
|
||||
|
||||
import unittest
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
unittest.TestCase.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
unittest.TestCase.tearDown(self)
|
||||
|
||||
def testMessage(self):
|
||||
t = pycompletionserver.T(0)
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'description' , 'args'))
|
||||
l.append(('Def1', 'description1', 'args1'))
|
||||
l.append(('Def2', 'description2', 'args2'))
|
||||
|
||||
msg = t.processor.formatCompletionMessage(None, l)
|
||||
self.assertEquals('@@COMPLETIONS(None,(Def,description,args),(Def1,description1,args1),(Def2,description2,args2))END@@', msg)
|
||||
|
||||
l = []
|
||||
l.append(('Def', 'desc,,r,,i()ption', ''))
|
||||
l.append(('Def(1', 'descriptio(n1', ''))
|
||||
l.append(('De,f)2', 'de,s,c,ription2', ''))
|
||||
msg = t.processor.formatCompletionMessage(None, l)
|
||||
self.assertEquals('@@COMPLETIONS(None,(Def,desc%2C%2Cr%2C%2Ci%28%29ption, ),(Def%281,descriptio%28n1, ),(De%2Cf%292,de%2Cs%2Cc%2Cription2, ))END@@', msg)
|
||||
|
||||
def createConnections(self, p1=50002):
|
||||
'''
|
||||
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.
|
||||
|
||||
s, addr = server.accept()
|
||||
|
||||
return t, s
|
||||
|
||||
|
||||
def readMsg(self):
|
||||
finish = False
|
||||
msg = ''
|
||||
while finish == False:
|
||||
m = self.socket.recv(1024 * 4)
|
||||
if IS_PYTHON_3K:
|
||||
m = m.decode('utf-8')
|
||||
if m.startswith('@@PROCESSING'):
|
||||
sys.stdout.write('Status msg: %s\n' % (msg,))
|
||||
else:
|
||||
msg += m
|
||||
|
||||
if msg.find('END@@') != -1:
|
||||
finish = True
|
||||
|
||||
return msg
|
||||
|
||||
def testCompletionSocketsAndMessages(self):
|
||||
t, socket = self.createConnections()
|
||||
self.socket = socket
|
||||
|
||||
try:
|
||||
#now that we have the connections all set up, check the code completion messages.
|
||||
msg = quote_plus('math')
|
||||
send(socket, '@@IMPORTS:%sEND@@' % msg) #math completions
|
||||
completions = self.readMsg()
|
||||
#print_ unquote_plus(completions)
|
||||
|
||||
#math is a builtin and because of that, it starts with None as a file
|
||||
start = '@@COMPLETIONS(None,(__doc__,'
|
||||
start_2 = '@@COMPLETIONS(None,(__name__,'
|
||||
self.assert_(completions.startswith(start) or completions.startswith(start_2), '%s DOESNT START WITH %s' % (completions, (start, start_2)))
|
||||
|
||||
self.assert_('@@COMPLETIONS' in completions)
|
||||
self.assert_('END@@' in completions)
|
||||
|
||||
|
||||
#now, test i
|
||||
msg = quote_plus('__builtin__.list')
|
||||
send(socket, "@@IMPORTS:%s\nEND@@" % msg)
|
||||
found = self.readMsg()
|
||||
self.assert_('sort' in found, 'Could not find sort in: %s' % (found,))
|
||||
|
||||
#now, test search
|
||||
msg = quote_plus('inspect.ismodule')
|
||||
send(socket, '@@SEARCH%sEND@@' % msg) #math completions
|
||||
found = self.readMsg()
|
||||
self.assert_('inspect.py' in found)
|
||||
self.assert_('33' in found or '34' in found or '51' in found or '50' in found, 'Could not find 33, 34, 50 or 51 in %s' % found)
|
||||
|
||||
#now, test search
|
||||
msg = quote_plus('inspect.CO_NEWLOCALS')
|
||||
send(socket, '@@SEARCH%sEND@@' % msg) #math completions
|
||||
found = self.readMsg()
|
||||
self.assert_('inspect.py' in found)
|
||||
self.assert_('CO_NEWLOCALS' in found)
|
||||
|
||||
#now, test search
|
||||
msg = quote_plus('inspect.BlockFinder.tokeneater')
|
||||
send(socket, '@@SEARCH%sEND@@' % msg)
|
||||
found = self.readMsg()
|
||||
self.assert_('inspect.py' in found)
|
||||
# self.assert_('CO_NEWLOCALS' in found)
|
||||
|
||||
#reload modules test
|
||||
# send(socket, '@@RELOAD_MODULES_END@@')
|
||||
# ok = self.readMsg()
|
||||
# self.assertEquals('@@MSG_OK_END@@' , ok)
|
||||
# this test is not executed because it breaks our current enviroment.
|
||||
|
||||
|
||||
finally:
|
||||
try:
|
||||
sys.stdout.write('succedded...sending kill msg\n')
|
||||
self.sendKillMsg(socket)
|
||||
|
||||
|
||||
# while not hasattr(t, 'ended'):
|
||||
# pass #wait until it receives the message and quits.
|
||||
|
||||
|
||||
socket.close()
|
||||
self.socket.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def sendKillMsg(self, socket):
|
||||
socket.send(pycompletionserver.MSG_KILL_SERVER)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.platform.find('java') == -1:
|
||||
unittest.main()
|
||||
else:
|
||||
sys.stdout.write('Not running python tests in platform: %s\n' % (sys.platform,))
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
'''
|
||||
@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
|
||||
BUILTIN_MOD = '__builtin__'
|
||||
except ImportError:
|
||||
BUILTIN_MOD = 'builtins'
|
||||
|
||||
|
||||
if sys.platform.find('java') == -1:
|
||||
|
||||
HAS_WX = False
|
||||
|
||||
import unittest
|
||||
import _pydev_imports_tipper
|
||||
import inspect
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
def p(self, t):
|
||||
for a in t:
|
||||
sys.stdout.write('%s\n' % (a,))
|
||||
|
||||
def testImports3(self):
|
||||
tip = _pydev_imports_tipper.GenerateTip('os')
|
||||
ret = self.assertIn('path', tip)
|
||||
self.assertEquals('', ret[2])
|
||||
|
||||
def testImports2(self):
|
||||
try:
|
||||
tip = _pydev_imports_tipper.GenerateTip('OpenGL.GLUT')
|
||||
self.assertIn('glutDisplayFunc', tip)
|
||||
self.assertIn('glutInitDisplayMode', tip)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def testImports4(self):
|
||||
try:
|
||||
tip = _pydev_imports_tipper.GenerateTip('mx.DateTime.mxDateTime.mxDateTime')
|
||||
self.assertIn('now', tip)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def testImports5(self):
|
||||
tip = _pydev_imports_tipper.GenerateTip('__builtin__.list')
|
||||
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)'
|
||||
)
|
||||
|
||||
def testImports2a(self):
|
||||
tips = _pydev_imports_tipper.GenerateTip('%s.RuntimeError' % BUILTIN_MOD)
|
||||
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])
|
||||
|
||||
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])
|
||||
|
||||
def testImports(self):
|
||||
'''
|
||||
You can print_ the results to check...
|
||||
'''
|
||||
if HAS_WX:
|
||||
tip = _pydev_imports_tipper.GenerateTip('wxPython.wx')
|
||||
self.assertIn('wxApp' , tip)
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip('wxPython.wx.wxApp')
|
||||
|
||||
try:
|
||||
tip = _pydev_imports_tipper.GenerateTip('qt')
|
||||
self.assertIn('QWidget' , tip)
|
||||
self.assertIn('QDialog' , tip)
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip('qt.QWidget')
|
||||
self.assertIn('rect' , tip)
|
||||
self.assertIn('rect' , tip)
|
||||
self.assertIn('AltButton' , tip)
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip('qt.QWidget.AltButton')
|
||||
self.assertIn('__xor__' , tip)
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip('qt.QWidget.AltButton.__xor__')
|
||||
self.assertIn('__class__' , tip)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip(BUILTIN_MOD)
|
||||
# for t in tip[1]:
|
||||
# print_ t
|
||||
self.assertIn('object' , tip)
|
||||
self.assertIn('tuple' , tip)
|
||||
self.assertIn('list' , tip)
|
||||
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
|
||||
|
||||
t = self.assertIn('isinstance' , tip)
|
||||
self.CheckArgs(t, '(object, class_or_type_or_tuple)', '(object o, type typeinfo)', '(o: object, typeinfo: type)') #args
|
||||
|
||||
t = self.assertIn('compile' , tip)
|
||||
self.CheckArgs(t, '(source, filename, mode)', '()', '(o: object, name: str, val: object)') #args
|
||||
|
||||
t = self.assertIn('setattr' , tip)
|
||||
self.CheckArgs(t, '(object, name, value)', '(object o, str name, object val)', '(o: object, name: str, val: object)') #args
|
||||
|
||||
try:
|
||||
import compiler
|
||||
compiler_module = 'compiler'
|
||||
except ImportError:
|
||||
try:
|
||||
import ast
|
||||
compiler_module = 'ast'
|
||||
except ImportError:
|
||||
compiler_module = None
|
||||
|
||||
if compiler_module is not None: #Not available in iron python
|
||||
tip = _pydev_imports_tipper.GenerateTip(compiler_module)
|
||||
if compiler_module == 'compiler':
|
||||
self.assertArgs('parse', '(buf, mode)', tip)
|
||||
self.assertArgs('walk', '(tree, visitor, walker, verbose)', tip)
|
||||
self.assertIn('parseFile' , tip)
|
||||
else:
|
||||
self.assertArgs('parse', '(source, filename, mode)', tip)
|
||||
self.assertArgs('walk', '(node)', tip)
|
||||
self.assertIn('parse' , tip)
|
||||
|
||||
|
||||
def CheckArgs(self, t, *expected):
|
||||
for x in expected:
|
||||
if x == t[2]:
|
||||
return
|
||||
self.fail('Found: %s. Expected: %s' % (t[2], expected))
|
||||
|
||||
|
||||
def assertArgs(self, tok, args, tips):
|
||||
for a in tips[1]:
|
||||
if tok == a[0]:
|
||||
self.assertEquals(args, a[2])
|
||||
return
|
||||
raise AssertionError('%s not in %s', tok, tips)
|
||||
|
||||
def assertIn(self, tok, tips):
|
||||
for a in tips[1]:
|
||||
if tok == a[0]:
|
||||
return a
|
||||
raise AssertionError('%s not in %s' % (tok, tips))
|
||||
|
||||
|
||||
def testSearch(self):
|
||||
s = _pydev_imports_tipper.Search('inspect.ismodule')
|
||||
(f, line, col), foundAs = s
|
||||
self.assert_(line > 0)
|
||||
|
||||
|
||||
def testDotNetLibraries(self):
|
||||
if sys.platform == 'cli':
|
||||
tip = _pydev_imports_tipper.GenerateTip('System.Drawing')
|
||||
self.assertIn('Brushes' , tip)
|
||||
|
||||
tip = _pydev_imports_tipper.GenerateTip('System.Drawing.Brushes')
|
||||
self.assertIn('Aqua' , tip)
|
||||
|
||||
|
||||
def testInspect(self):
|
||||
|
||||
class C(object):
|
||||
def metA(self, a, b):
|
||||
pass
|
||||
|
||||
obj = C.metA
|
||||
if inspect.ismethod (obj):
|
||||
pass
|
||||
# print_ obj.im_func
|
||||
# print_ inspect.getargspec(obj.im_func)
|
||||
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(Test("testImports5"))
|
||||
unittest.TextTestRunner(verbosity=2).run(s)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.platform.find('java') == -1:
|
||||
# suite()
|
||||
unittest.main()
|
||||
else:
|
||||
sys.stdout.write('Not running python tests in platform: %s\n' % (sys.platform,))
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Parts of IPython, files from: https://github.com/ipython/ipython/tree/rel-1.0.0/examples/lib
|
||||
# The files in this folder are manual tests for main loop integration
|
||||
|
||||
# These tests have been modified to work in the PyDev Console context
|
||||
@@ -0,0 +1 @@
|
||||
(no __init__.py file)
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple GLUT example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for glut
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
interactive console
|
||||
4) run: gl.glClearColor(1,1,1,1)
|
||||
"""
|
||||
|
||||
#!/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()
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple GTK example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for gtk
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
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()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple Gtk example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for gtk3
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
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()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple pyglet example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for pyglet
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
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()
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple Qt4 example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for qt
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
interactive console
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
"""Simple Tk example to manually test event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for tk
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
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)
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
A Simple wx example to test PyDev's event loop integration.
|
||||
|
||||
To run this:
|
||||
1) Enable the PyDev GUI event loop integration for wx
|
||||
2) do an execfile on this script
|
||||
3) ensure you have a working GUI simultaneously with an
|
||||
interactive console
|
||||
|
||||
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)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
(no __init__.py file)
|
||||
@@ -0,0 +1,61 @@
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
def SetUp():
|
||||
observable = Observable()
|
||||
observer = Observer()
|
||||
observable.AddObserver(observer)
|
||||
return observable
|
||||
|
||||
|
||||
class Observable(object):
|
||||
def __init__(self):
|
||||
self.observers = []
|
||||
|
||||
def AddObserver(self, observer):
|
||||
sys.stdout.write( 'observer %s\n' % (observer,))
|
||||
ref = weakref.ref(observer)
|
||||
self.observers.append(ref)
|
||||
sys.stdout.write('weakref: %s\n' % (ref(),))
|
||||
|
||||
def Notify(self):
|
||||
for o in self.observers:
|
||||
o = o()
|
||||
|
||||
|
||||
try:
|
||||
import gc
|
||||
except ImportError:
|
||||
o = None #some jython does not have gc, so, there's no sense testing this in it
|
||||
else:
|
||||
try:
|
||||
gc.get_referrers(o)
|
||||
except:
|
||||
o = None #jython and ironpython do not have get_referrers
|
||||
|
||||
if o is not None:
|
||||
sys.stdout.write('still observing %s\n' % (o,))
|
||||
sys.stdout.write('number of referrers: %s\n' % len(gc.get_referrers(o)))
|
||||
frame = gc.get_referrers(o)[0]
|
||||
frame_referrers = gc.get_referrers(frame)
|
||||
sys.stdout.write('frame referrer %s\n' % (frame_referrers,))
|
||||
referrers1 = gc.get_referrers(frame_referrers[1])
|
||||
sys.stdout.write('%s\n' % (referrers1,))
|
||||
sys.stderr.write('TEST FAILED: The observer should have died, even when running in debug\n')
|
||||
else:
|
||||
sys.stdout.write('TEST SUCEEDED: observer died\n')
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
class Observer(object):
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
observable = SetUp()
|
||||
observable.Notify()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,18 @@
|
||||
def Method1():
|
||||
print('m1')
|
||||
print('m1')
|
||||
|
||||
def Method1a():
|
||||
print('m1a')
|
||||
print('m1a')
|
||||
|
||||
def Method2():
|
||||
print('m2 before')
|
||||
Method1()
|
||||
Method1a()
|
||||
print('m2 after')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Method2()
|
||||
print('TEST SUCEEDED!')
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
class TestProperty(object):
|
||||
def __init__(self, name = "Default"):
|
||||
self._x = None
|
||||
self.name = name
|
||||
|
||||
def get_name(self):
|
||||
return self.__name
|
||||
|
||||
|
||||
def set_name(self, value):
|
||||
self.__name = value
|
||||
|
||||
|
||||
def del_name(self):
|
||||
del self.__name
|
||||
name = property(get_name, set_name, del_name, "name's docstring")
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._x
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = value
|
||||
|
||||
@x.deleter
|
||||
def x(self):
|
||||
del self._x
|
||||
|
||||
def main():
|
||||
"""
|
||||
"""
|
||||
testObj = TestProperty()
|
||||
testObj.x = 10
|
||||
val = testObj.x
|
||||
|
||||
testObj.name = "Pydev"
|
||||
debugType = testObj.name
|
||||
print('TEST SUCEEDED!')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user