Skip to content

Commit

Permalink
Fixed reload code to be Python 3 compatible.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jan 14, 2014
1 parent 282897b commit c03e6ac
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 187 deletions.
15 changes: 13 additions & 2 deletions plugins/org.python.pydev/pysrc/pydevd_custom_frames.py
Expand Up @@ -84,11 +84,22 @@ def updateCustomFrame(frame_id, frame, thread_id, name=None):
CustomFramesContainer.custom_frames_lock.release()


def getCustomFrame(frame_id):
def getCustomFrame(thread_id, frame_id):
'''
:param thread_id: This should actually be the frame_id which is returned by addCustomFrame.
:param frame_id: This is the actual id() of the frame
'''

CustomFramesContainer.custom_frames_lock.acquire()
try:
return CustomFramesContainer.custom_frames[frame_id].frame
frame_id = int(frame_id)
f = CustomFramesContainer.custom_frames[thread_id].frame
while f is not None:
if id(f) == frame_id:
return f
f = f.f_back
finally:
f = None
CustomFramesContainer.custom_frames_lock.release()


Expand Down
97 changes: 50 additions & 47 deletions plugins/org.python.pydev/pysrc/pydevd_reload.py
Expand Up @@ -71,7 +71,7 @@
class Helper:

DEBUG = NO_DEBUG

def write(*args):
new_lst = []
for a in args:
Expand All @@ -80,12 +80,12 @@ def write(*args):
msg = ' '.join(new_lst)
sys.stdout.write('%s\n' % (msg,))
write = staticmethod(write)

def info(*args):
if Helper.DEBUG >= LEVEL1:
Helper.write(*args)
info = staticmethod(info)

def info2(*args):
if Helper.DEBUG >= LEVEL2:
Helper.write(*args)
Expand Down Expand Up @@ -117,16 +117,16 @@ def xreload(mod):
r.apply()
r = None
pydevd_dont_trace.clear_trace_filter_cache()


#=======================================================================================================================
# Reload
#=======================================================================================================================
class Reload:

def __init__(self, mod):
self.mod = mod

def apply(self):
mod = self.mod
self._on_finish_callbacks = []
Expand Down Expand Up @@ -181,16 +181,16 @@ def apply(self):
# Now we get to the hard part
oldnames = set(modns)
newnames = set(new_namespace)

# Update in-place what we can
for name in oldnames & newnames:
self._update(modns[name], new_namespace[name])

# Create new tokens (note: not deleting existing)
for name in newnames - oldnames:
Helper.info('Created:', new_namespace[name])
modns[name] = new_namespace[name]

for c in self._on_finish_callbacks:
c()
del self._on_finish_callbacks[:]
Expand All @@ -200,9 +200,9 @@ def apply(self):

def _update(self, oldobj, newobj):
"""Update oldobj, if possible in place, with newobj.
If oldobj is immutable, this simply returns newobj.
Args:
oldobj: the object to be updated
newobj: the object used as the source for the update
Expand All @@ -211,104 +211,107 @@ def _update(self, oldobj, newobj):
if oldobj is newobj:
# Probably something imported
return newobj

if type(oldobj) is not type(newobj):
# Cop-out: if the type changed, give up
return newobj

if hasattr(newobj, "__reload_update__"):
# Provide a hook for updating
return newobj.__reload_update__(oldobj)

if hasattr(types, 'ClassType'):
classtype = types.ClassType
else:
classtype = type

if isinstance(newobj, classtype):
return self._update_class(oldobj, newobj)

if isinstance(newobj, types.FunctionType):
return self._update_function(oldobj, newobj)

if isinstance(newobj, types.MethodType):
return self._update_method(oldobj, newobj)

if isinstance(newobj, classmethod):
return self._update_classmethod(oldobj, newobj)

if isinstance(newobj, staticmethod):
return self._update_staticmethod(oldobj, newobj)
#New: dealing with metaclasses.

# New: dealing with metaclasses.
if hasattr(newobj, '__metaclass__') and hasattr(newobj, '__class__') and newobj.__metaclass__ == newobj.__class__:
return self._update_class(oldobj, newobj)

# Not something we recognize, just give up
return newobj


# All of the following functions have the same signature as _update()


def _update_function(self, oldfunc, newfunc):
"""Update a function object."""
oldfunc.__doc__ = newfunc.__doc__
oldfunc.__dict__.update(newfunc.__dict__)

try:
newfunc.__code__
attr_name = '__code__'
except AttributeError:
newfunc.func_code
attr_name = 'func_code'

old_code = getattr(oldfunc, attr_name)
new_code = getattr(newfunc, attr_name)
if not code_objects_equal(old_code, new_code):
Helper.info('Update function:', oldfunc)
setattr(oldfunc, attr_name, new_code)

try:
oldfunc.__defaults__ = newfunc.__defaults__
except AttributeError:
oldfunc.func_defaults = newfunc.func_defaults

return oldfunc


def _update_method(self, oldmeth, newmeth):
"""Update a method object."""
# XXX What if im_func is not a function?
self._update(oldmeth.im_func, newmeth.im_func)
if hasattr(oldmeth, 'im_func') and hasattr(newmeth, 'im_func'):
self._update(oldmeth.im_func, newmeth.im_func)
elif hasattr(oldmeth, '__func__') and hasattr(newmeth, '__func__'):
self._update(oldmeth.__func__, newmeth.__func__)
return oldmeth


def _update_class(self, oldclass, newclass):
"""Update a class object."""
olddict = oldclass.__dict__
newdict = newclass.__dict__

oldnames = set(olddict)
newnames = set(newdict)

for name in newnames - oldnames:
Helper.info('Created:', newdict[name], 'in', oldclass)
setattr(oldclass, name, newdict[name])
#Note: not removing old things...
#for name in oldnames - newnames:

# Note: not removing old things...
# for name in oldnames - newnames:
# Helper.info('Removed:', name, 'from', oldclass)
# delattr(oldclass, name)

for name in oldnames & newnames - set(['__dict__', '__doc__']):
self._update(olddict[name], newdict[name])

if hasattr(oldclass, "__after_reload_update__"):
# If a client wants to know about it, give him a chance.
self._on_finish_callbacks.append(oldclass.__after_reload_update__)


def _update_classmethod(self, oldcm, newcm):
"""Update a classmethod update."""
# While we can't modify the classmethod object itself (it has no
Expand All @@ -317,8 +320,8 @@ def _update_classmethod(self, oldcm, newcm):
# it in-place. We don't have the class available to pass to
# __get__() but any object except None will do.
self._update(oldcm.__get__(0), newcm.__get__(0))


def _update_staticmethod(self, oldsm, newsm):
"""Update a staticmethod update."""
# While we can't modify the staticmethod object itself (it has no
Expand Down
2 changes: 1 addition & 1 deletion plugins/org.python.pydev/pysrc/pydevd_vars.py
Expand Up @@ -283,7 +283,7 @@ def findFrame(thread_id, frame_id):
curr_thread_id = GetThreadId(threading.currentThread())
if thread_id != curr_thread_id :
try:
return getCustomFrame(thread_id) #I.e.: thread_id could be a stackless frame id + thread_id.
return getCustomFrame(thread_id, frame_id) #I.e.: thread_id could be a stackless frame id + thread_id.
except:
pass

Expand Down
Expand Up @@ -94,7 +94,7 @@ def check(expected):

check(0)

#modify mod and reload
# modify mod and reload
count = 0
while count < 1:
count += 1
Expand Down Expand Up @@ -131,6 +131,19 @@ def m1(self):
self.assertEqual(F().m1(), 2)


def testPydevdReload4(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(F, G)
self.assertEqual(F().m1(), 10)



def testIfCodeObjEquals(self):
class F:
Expand All @@ -143,46 +156,50 @@ class H:
def m1(self):
return 2

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))
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 testMetaclass(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(F, G)
self.assertEqual(F().m1(), 2)


def testCreateClass(self):
SAMPLE_CODE1 = """
class C:
def foo(self):
return 0
"""
#Creating a new class and using it from old class
# Creating a new class and using it from old class
SAMPLE_CODE2 = """
class B:
pass
class C:
def foo(self):
return B
Expand All @@ -195,10 +212,10 @@ def foo(self):
self.make_mod(sample=SAMPLE_CODE2)
pydevd_reload.xreload(x)
self.assertEqual(foo().__name__, 'B')






if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testPydevdReload']
unittest.main()
# import sys;sys.argv = ['', 'Test.testPydevdReload']
unittest.main()

0 comments on commit c03e6ac

Please sign in to comment.