Skip to content

Commit 559063a

Browse files
committed
Fix circular reference bug
1 parent 0161489 commit 559063a

File tree

3 files changed

+75
-38
lines changed

3 files changed

+75
-38
lines changed

itchat/config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os, platform
22

3-
VERSION = '1.3.4'
3+
VERSION = '1.3.5'
44
BASE_URL = 'https://login.weixin.qq.com'
55
OS = platform.system() #Windows, Linux, Darwin
66
DIR = os.getcwd()

itchat/storage/messagequeue.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,29 @@
11
import logging
2-
32
try:
43
import Queue as queue
54
except ImportError:
65
import queue
76

7+
from .templates import AttributeDict
8+
89
logger = logging.getLogger('itchat')
910

1011
class Queue(queue.Queue):
1112
def put(self, message):
1213
queue.Queue.put(self, Message(message))
1314

14-
class Message(dict):
15+
class Message(AttributeDict):
1516
def download(self, fileName):
1617
if hasattr(self.text, '__call__'):
1718
return self.text(fileName)
1819
else:
1920
return b''
20-
def __getattr__(self, value):
21-
value = value[0].upper() + value[1:]
22-
return self[value]
2321
def __getitem__(self, value):
2422
if value in ('isAdmin', 'isAt'):
2523
v = value[0].upper() + value[1:] # ''[1:] == ''
2624
logger.debug('%s is expired in 1.3.0, use %s instead.' % (value, v))
2725
value = v
2826
return super(Message, self).__getitem__(value)
29-
def get(self, v, d=None):
30-
try:
31-
return self[v]
32-
except KeyError:
33-
return d
3427
def __str__(self):
3528
return '{%s}' % ', '.join(
3629
['%s: %s' % (repr(k),repr(v)) for k,v in self.items()])

itchat/storage/templates.py

+71-27
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,42 @@
11
import logging, copy, pickle
2+
from weakref import ref
23

34
from ..returnvalues import ReturnValue
45
from ..utils import update_info_dict
56

67
logger = logging.getLogger('itchat')
78

9+
class AttributeDict(dict):
10+
def __getattr__(self, value):
11+
keyName = value[0].upper() + value[1:]
12+
try:
13+
return self[keyName]
14+
except KeyError:
15+
raise AttributeError("'%s' object has no attribute '%s'" % (
16+
self.__class__.__name__.split('.')[-1], value))
17+
def get(self, v, d=None):
18+
try:
19+
return self[v]
20+
except KeyError:
21+
return d
22+
823
class UnInitializedItchat(object):
924
def _raise_error(self, *args, **kwargs):
1025
logger.warning('An itchat instance is called before initialized')
1126
def __getattr__(self, value):
1227
return self._raise_error
1328

14-
fakeItchat = UnInitializedItchat()
15-
1629
class ContactList(list):
17-
''' when a dict is append, init function will be called to format that dict
18-
'''
30+
''' when a dict is append, init function will be called to format that dict '''
1931
def __init__(self, *args, **kwargs):
2032
super(ContactList, self).__init__(*args, **kwargs)
2133
self.__setstate__(None)
34+
@property
35+
def core(self):
36+
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
37+
@core.setter
38+
def core(self, value):
39+
self._core = ref(value)
2240
def set_default_value(self, initFunction=None, contactClass=None):
2341
if hasattr(initFunction, '__call__'):
2442
self.contactInitFn = initFunction
@@ -28,7 +46,7 @@ def append(self, value):
2846
contact = self.contactClass(value)
2947
contact.core = self.core
3048
if self.contactInitFn is not None:
31-
contact = self.contactInitFn(contact) or contact
49+
contact = self.contactInitFn(self, contact) or contact
3250
super(ContactList, self).append(contact)
3351
def __deepcopy__(self, memo):
3452
r = self.__class__([copy.deepcopy(v) for v in self])
@@ -41,19 +59,21 @@ def __getstate__(self):
4159
def __setstate__(self, state):
4260
self.contactInitFn = None
4361
self.contactClass = User
44-
self.core = fakeItchat
4562
def __str__(self):
4663
return '[%s]' % ', '.join([repr(v) for v in self])
4764
def __repr__(self):
4865
return '<%s: %s>' % (self.__class__.__name__.split('.')[-1],
4966
self.__str__())
5067

51-
fakeContactList = ContactList
52-
53-
class AbstractUserDict(dict):
68+
class AbstractUserDict(AttributeDict):
5469
def __init__(self, *args, **kwargs):
5570
super(AbstractUserDict, self).__init__(*args, **kwargs)
56-
self.__setstate__(None)
71+
@property
72+
def core(self):
73+
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
74+
@core.setter
75+
def core(self, value):
76+
self._core = ref(value)
5777
def update(self):
5878
return ReturnValue({'BaseResponse': {
5979
'Ret': -1006,
@@ -104,9 +124,6 @@ def search_member(self, name=None, userName=None, remarkName=None, nickName=None
104124
'Ret': -1006,
105125
'ErrMsg': '%s do not have members' % \
106126
self.__class__.__name__, }, })
107-
def __getattr__(self, value):
108-
value = value[0].upper() + value[1:]
109-
return self.get(value, '')
110127
def __deepcopy__(self, memo):
111128
r = self.__class__()
112129
for k, v in self.items():
@@ -122,7 +139,7 @@ def __repr__(self):
122139
def __getstate__(self):
123140
return 1
124141
def __setstate__(self, state):
125-
self.core = fakeItchat
142+
pass
126143

127144
class User(AbstractUserDict):
128145
def __init__(self, *args, **kwargs):
@@ -146,32 +163,39 @@ def __deepcopy__(self, memo):
146163
def __setstate__(self, state):
147164
super(User, self).__setstate__(state)
148165
self.verifyDict = {}
149-
self.memberList = fakeContactList
166+
self['MemberList'] = fakeContactList
150167

151168
class MassivePlatform(AbstractUserDict):
152169
def __init__(self, *args, **kwargs):
153170
super(MassivePlatform, self).__init__(*args, **kwargs)
154171
self.__setstate__(None)
155172
def __setstate__(self, state):
156173
super(MassivePlatform, self).__setstate__(state)
157-
self.memberList = fakeContactList
174+
self['MemberList'] = fakeContactList
158175

159176
class Chatroom(AbstractUserDict):
160177
def __init__(self, *args, **kwargs):
161178
super(Chatroom, self).__init__(*args, **kwargs)
162179
memberList = ContactList()
163-
def init_fn(d):
164-
d.chatroom = self
180+
userName = self.get('UserName', '')
181+
refSelf = ref(self)
182+
def init_fn(parentList, d):
183+
d.chatroom = refSelf() or \
184+
parentList.core.search_chatrooms(userName=userName)
165185
memberList.set_default_value(init_fn, ChatroomMember)
166186
if 'MemberList' in self:
167-
if not isinstance(self.memberList, ContactList):
168-
for member in self.memberList:
169-
memberList.append(member)
170-
self['MemberList'] = memberList
171-
else:
172187
for member in self.memberList:
173188
memberList.append(member)
174-
self['MemberList'] = memberList
189+
self['MemberList'] = memberList
190+
@property
191+
def core(self):
192+
return getattr(self, '_core', lambda: fakeItchat)() or fakeItchat
193+
@core.setter
194+
def core(self, value):
195+
self._core = ref(value)
196+
self.memberList.core = value
197+
for member in self.memberList:
198+
member.core = value
175199
def update(self, detailedMember=False):
176200
r = self.core.update_chatroom(self.userName, detailedMember)
177201
if r:
@@ -218,11 +242,29 @@ def search_member(self, name=None, userName=None, remarkName=None, nickName=None
218242
return copy.deepcopy(friendList)
219243
else:
220244
return copy.deepcopy(contact)
245+
def __setstate__(self, state):
246+
super(Chatroom, self).__setstate__(state)
247+
if not 'MemberList' in self:
248+
self['MemberList'] = fakeContactList
221249

222250
class ChatroomMember(AbstractUserDict):
223251
def __init__(self, *args, **kwargs):
224252
super(AbstractUserDict, self).__init__(*args, **kwargs)
225253
self.__setstate__(None)
254+
@property
255+
def chatroom(self):
256+
r = getattr(self, '_chatroom', lambda: fakeChatroom)()
257+
if r is None:
258+
userName = getattr(self, '_chatroomUserName', '')
259+
r = self.core.search_chatrooms(userName=userName)
260+
if isinstance(r, dict):
261+
self.chatroom = r
262+
return r or fakeChatroom
263+
@chatroom.setter
264+
def chatroom(self, value):
265+
if isinstance(value, dict) and 'UserName' in value:
266+
self._chatroom = ref(value)
267+
self._chatroomUserName = value['UserName']
226268
def get_head_image(self, imageDir=None):
227269
return self.core.get_head_img(self.userName, self.chatroom.userName, picDir=imageDir)
228270
def delete_member(self, userName):
@@ -263,9 +305,7 @@ def __deepcopy__(self, memo):
263305
return r
264306
def __setstate__(self, state):
265307
super(ChatroomMember, self).__setstate__(state)
266-
self.chatroom = self.fakeChatroom
267-
268-
ChatroomMember.fakeChatroom = Chatroom()
308+
self['MemberList'] = fakeContactList
269309

270310
def wrap_user_dict(d):
271311
userName = d.get('UserName')
@@ -276,3 +316,7 @@ def wrap_user_dict(d):
276316
else:
277317
r = MassivePlatform(d)
278318
return r
319+
320+
fakeItchat = UnInitializedItchat()
321+
fakeContactList = ContactList()
322+
fakeChatroom = Chatroom()

0 commit comments

Comments
 (0)