4545# models
4646#
4747
48- class Container (object ):
49- """Contains a tree of messages.
48+ class Container (dict ):
49+ """Contains a tree of objects. Each container is a subclassed dict
50+ where the contents are stored.
5051
5152 Attributes:
52- message (Message): Message corresponding to this tree node.
53- This can be None, if a Message-Id is referenced but no
54- message with the ID is included.
5553 children ([Container]): Possibly-empty list of child containers
5654 parent (Container): Parent container, if any
5755 """
58- def __init__ (self ):
59- self .message = self .parent = None
56+ def __init__ (self , ** args ):
57+ dict .__init__ (self , ** args )
58+ self .parent = None
6059 self .children = []
6160
6261 def __repr__ (self ):
6362 return '<%s %x: %r>' % (self .__class__ .__name__ , id (self ),
64- self .message )
63+ dict .__repr__ (self ))
64+ def __hash__ (self ):
65+ """ Make the container hashable. Care must be taken though not to change
66+ the container contents after the initialization as otherwise the hash
67+ value will change
68+ """
69+ return hash (tuple (sorted (self .items ())) + (self .parent ,))
70+
6571
6672 def is_dummy (self ):
67- """Check if Container has a message ."""
68- return self .message is None
73+ """Check if Container has some contents ."""
74+ return not len ( self .keys ())
6975
7076 def add_child (self , child ):
7177 """Add a child to `self`.
@@ -132,7 +138,7 @@ def depth(self):
132138 return 1 + self .parent .depth
133139
134140 def flatten (self ):
135- """ Return a flatten version of the thread
141+ """ Return a flatten version of the tree
136142
137143 Returns
138144 list [Containers]: a list of messages
@@ -160,6 +166,8 @@ def root(self):
160166 def collapse_empty (self , inplace = True ):
161167 """ Collapse empty top level containers.
162168
169+ This only applies to the JWZ algorithm
170+
163171 If multiple messages reference a non existing top level message,
164172 by default JWZ threading algorithm will create a en empty top level
165173 container to be used as the root node.
@@ -178,12 +186,15 @@ def collapse_empty(self, inplace=True):
178186 if not inplace :
179187 raise NotImplementedError
180188
189+ if not 'message' in self :
190+ raise ValueError ('This method is only valid when used for email threading' )
191+
181192
182- if self . message is not None :
193+ if self [ ' message' ] is not None :
183194 # nothing to be done
184195 return self
185196
186- if any ([el . message is None for el in self .children ]):
197+ if any ([el [ ' message' ] is None for el in self .children ]):
187198 raise ValueError ('Children containers cannot be empty!' )
188199
189200 # In the following, self.message is None
@@ -203,20 +214,24 @@ def collapse_empty(self, inplace=True):
203214
204215
205216 def to_dict (self , include = []):
206- """ Convert a Container tree to a nested dict """
207- if self .message is None :
208- raise ValueError ('Containers with None messages are not supported!' )
217+ """ Convert a Container tree to a nested dict
218+ """
219+ if 'message' not in self :
220+ raise ValueError ('This method is currently valid with email threading, '
221+ 'please overwrite it for other applications' )
222+
223+ if self ['message' ] is None :
209224 raise ValueError ('Containers with None messages are not supported:!\n ' \
210225 ' this: {}' .format (self ))
211226
212- res = {'id' : self . message .message_idx }
227+ res = {'id' : self [ ' message' ] .message_idx }
213228
214229 for key in include :
215- res [key ] = getattr (self . message , key )
230+ res [key ] = getattr (self [ ' message' ] , key )
216231
217232 if self .parent is not None :
218- if self .parent . message is not None :
219- res ['parent' ] = self .parent . message .message_idx
233+ if self .parent [ ' message' ] is not None :
234+ res ['parent' ] = self .parent [ ' message' ] .message_idx
220235 else :
221236 raise ValueError ('Containers with None messages are not supported:!\n ' \
222237 ' this: {}\n parent: {}' .format (self , self .parent ))
@@ -324,10 +339,10 @@ def prune_container(container):
324339 for child in new_children :
325340 container .add_child (child )
326341
327- if container .message is None and not len (container .children ):
342+ if container .get ( ' message' ) is None and not len (container .children ):
328343 # step 4 (a) - nuke empty containers
329344 return []
330- elif container .message is None and (
345+ elif container .get ( ' message' ) is None and (
331346 len (container .children ) == 1 or container .parent is not None ):
332347 # step 4 (b) - promote children
333348 children = container .children [:]
@@ -356,10 +371,10 @@ def sort_threads(threads, key='message_idx', missing=-1, reverse=False):
356371
357372 def _sort_func (el ):
358373
359- if el .message is None :
374+ if el .get ( ' message' ) is None :
360375 val = missing
361376 else :
362- val = getattr (el .message , key )
377+ val = getattr (el .get ( ' message' ) , key )
363378 if val is None :
364379 val = missing
365380 return val
@@ -398,10 +413,10 @@ def thread(messages, group_by_subject=True):
398413 # step one (a)
399414 this_container = id_table .get (msg .message_id , None )
400415 if this_container is not None :
401- this_container . message = msg
416+ this_container [ ' message' ] = msg
402417 else :
403- this_container = Container ()
404- this_container . message = msg
418+ this_container = Container (message = None )
419+ this_container [ ' message' ] = msg
405420 id_table [msg .message_id ] = this_container
406421
407422 # step one (b)
@@ -410,7 +425,7 @@ def thread(messages, group_by_subject=True):
410425 ## print "Processing reference for "+repr(msg.message_id)+": "+repr(ref)
411426 container = id_table .get (ref , None )
412427 if container is None :
413- container = Container ()
428+ container = Container (message = None )
414429 id_table [ref ] = container
415430
416431 if prev is not None :
@@ -466,10 +481,10 @@ def thread(messages, group_by_subject=True):
466481 # step five - group root set by subject
467482 subject_table = OrderedDict ()
468483 for container in root_set :
469- if container . message :
470- subj = container . message .subject
484+ if container [ ' message' ] :
485+ subj = container [ ' message' ] .subject
471486 else :
472- subj = container .children [0 ]. message .subject
487+ subj = container .children [0 ][ ' message' ] .subject
473488
474489 subj = SUBJECT_RE .sub ('' , subj )
475490 if subj == '' :
@@ -478,19 +493,19 @@ def thread(messages, group_by_subject=True):
478493 existing = subject_table .get (subj , None )
479494 if (existing is None or
480495 (existing .message is not None and
481- container .message is None ) or
496+ container .get ( ' message' ) is None ) or
482497 (existing .message is not None and
483- container .message is not None and
484- len (existing .message .subject ) > len (container . message .subject ))):
498+ container .get ( ' message' ) is not None and
499+ len (existing .message .subject ) > len (container [ ' message' ] .subject ))):
485500 subject_table [subj ] = container
486501
487502
488503 # step five (c)
489504 for container in root_set :
490- if container . message :
491- subj = container . message .subject
505+ if container [ ' message' ] :
506+ subj = container [ ' message' ] .subject
492507 else :
493- subj = container .children [0 ]. message .subject
508+ subj = container .children [0 ][ ' message' ] .subject
494509
495510 subj = SUBJECT_RE .sub ('' , subj )
496511 ctr = subject_table .get (subj )
@@ -506,14 +521,14 @@ def thread(messages, group_by_subject=True):
506521 ctr .add_child (container )
507522 else :
508523 container .add_child (ctr )
509- elif len (ctr .message .subject ) < len (container . message .subject ):
524+ elif len (ctr .message .subject ) < len (container [ ' message' ] .subject ):
510525 # ctr has fewer levels of 're:' headers
511526 ctr .add_child (container )
512- elif len (ctr .message .subject ) > len (container . message .subject ):
527+ elif len (ctr .message .subject ) > len (container [ ' message' ] .subject ):
513528 # container has fewer levels of 're:' headers
514529 container .add_child (ctr )
515530 else :
516- new = Container ()
531+ new = Container (message = None )
517532 new .add_child (ctr )
518533 new .add_child (container )
519534 subject_table [subj ] = new
0 commit comments