[Spambayes-checkins] spambayes/spambayes CorePlugin.py, 1.1, 1.2 CoreUI.py, 1.1, 1.2 XMLRPCPlugin.py, 1.1, 1.2 Options.py, 1.141, 1.142 ProxyUI.py, 1.64, 1.65 UserInterface.py, 1.61, 1.62 dnscache.py, 1.3, 1.4 storage.py, 1.63, 1.64

Skip Montanaro montanaro at users.sourceforge.net
Sun Jun 10 17:27:39 CEST 2007


Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs8.sourceforge.net:/tmp/cvs-serv7512/spambayes

Modified Files:
	Options.py ProxyUI.py UserInterface.py dnscache.py storage.py 
Added Files:
	CorePlugin.py CoreUI.py XMLRPCPlugin.py 
Log Message:

Merge CORESVR branch to HEAD.  This adds a new app, scripts/core_server.py
and attendant bits, such as an XML-RPC plugin.  The web interface is
straight from the POP3 proxy server.






Index: Options.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v
retrieving revision 1.141
retrieving revision 1.142
diff -C2 -d -r1.141 -r1.142
*** Options.py	26 Mar 2007 07:57:13 -0000	1.141
--- Options.py	10 Jun 2007 15:27:36 -0000	1.142
***************
*** 653,656 ****
--- 653,671 ----
       PATH, DO_NOT_RESTORE),
  
+     ("core_spam_cache", _("Spam cache directory"), "core-spam-cache",
+      _("""Directory that SpamBayes should cache spam in.  If this does
+      not exist, it will be created."""),
+      PATH, DO_NOT_RESTORE),
+ 
+     ("core_ham_cache", _("Ham cache directory"), "core-ham-cache",
+      _("""Directory that SpamBayes should cache ham in.  If this does
+      not exist, it will be created."""),
+      PATH, DO_NOT_RESTORE),
+ 
+     ("core_unknown_cache", _("Unknown cache directory"), "core-unknown-cache",
+      _("""Directory that SpamBayes should cache unclassified messages in.
+      If this does not exist, it will be created."""),
+      PATH, DO_NOT_RESTORE),
+ 
      ("cache_messages", _("Cache messages"), True,
       _("""You can disable the pop3proxy caching of messages.  This
***************
*** 1280,1283 ****
--- 1295,1309 ----
       r"\w\w(?:_\w\w)?", RESTORE),
    ),
+   "Plugin": (
+     ("xmlrpc_path", _("XML-RPC path"), "/sbrpc",
+      _("""The path to respond to."""),
+      r"[\w]+", RESTORE),
+     ("xmlrpc_host", _("XML-RPC host"), "localhost",
+      _("""The host to listen on."""),
+      SERVER, RESTORE),
+     ("xmlrpc_port", _("XML-RPC port"), 8001,
+      _("""The port to listen on."""),
+      r"[\d]+", RESTORE),
+     ),
  }
  

Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.64
retrieving revision 1.65
diff -C2 -d -r1.64 -r1.65
*** ProxyUI.py	28 Nov 2005 10:54:18 -0000	1.64
--- ProxyUI.py	10 Jun 2007 15:27:36 -0000	1.65
***************
*** 54,58 ****
      True, False = 1, 0
  
- import re
  import cgi
  import time
--- 54,57 ----
***************
*** 72,81 ****
          from spambayes.compatsets import Set
  
- import tokenizer
  import UserInterface
  from spambayes.Options import options, _
- from email.Iterators import typed_subpart_iterator
  
! global state
  
  # These are the options that will be offered on the configuration page.
--- 71,78 ----
          from spambayes.compatsets import Set
  
  import UserInterface
  from spambayes.Options import options, _
  
! state = None
  
  # These are the options that will be offered on the configuration page.
***************
*** 111,115 ****
  )
  
! # Like the above, but hese are the options that will be offered on the
  # advanced configuration page.
  adv_map = (
--- 108,112 ----
  )
  
! # Like the above, but these are the options that will be offered on the
  # advanced configuration page.
  adv_map = (
***************
*** 174,178 ****
          self.state_recreator = state_recreator # ugly
          self.app_for_version = "SpamBayes Proxy"
-         self.previous_sort = None
          if not proxy_state.can_stop:
              self.html._readonly = False
--- 171,174 ----
***************
*** 221,241 ****
          self.write(_("<p>OK. Return <a href='home'>Home</a>.</p>"))
  
-     def _keyToTimestamp(self, key):
-         """Given a message key (as seen in a Corpus), returns the timestamp
-         for that message.  This is the time that the message was received,
-         not the Date header."""
-         return long(key[:10])
- 
-     def _getTimeRange(self, timestamp):
-         """Given a unix timestamp, returns a 3-tuple: the start timestamp
-         of the given day, the end timestamp of the given day, and the
-         formatted date of the given day."""
-         this = time.localtime(timestamp)
-         start = (this[0], this[1], this[2], 0, 0, 0, this[6], this[7], this[8])
-         end = time.localtime(time.mktime(start) + 36*60*60)
-         end = (end[0], end[1], end[2], 0, 0, 0, end[6], end[7], end[8])
-         date = time.strftime("%A, %B %d, %Y", start)
-         return time.mktime(start), time.mktime(end), date
- 
      def _buildReviewKeys(self, timestamp):
          """Builds an ordered list of untrained message keys, ready for output
--- 217,220 ----
***************
*** 278,382 ****
          return keys, date, prior, start, end
  
-     def _sortMessages(self, messages, sort_order, reverse=False):
-         """Sorts the message by the appropriate attribute.  If this was the
-         previous sort order, then reverse it."""
-         if sort_order is None or sort_order == "received":
-             # Default sorting, which is in reverse order of appearance.
-             # This is complicated because the 'received' info is the key.
-             messages.sort()
-             if self.previous_sort == sort_order:
-                 messages.reverse()
-                 self.previous_sort = None
-             else:
-                 self.previous_sort = 'received'
-             return messages
-         tmplist = [(getattr(x[1], sort_order), x) for x in messages]
-         tmplist.sort()
-         if reverse:
-             tmplist.reverse()
-         return [x for (key, x) in tmplist]
- 
-     def _appendMessages(self, table, keyedMessageInfo, label, sort_order,
-                         reverse=False):
-         """Appends the rows of a table of messages to 'table'."""
-         stripe = 0
- 
-         keyedMessageInfo = self._sortMessages(keyedMessageInfo, sort_order,
-                                               reverse)
-         nrows = options["html_ui", "rows_per_section"]
-         for key, messageInfo in keyedMessageInfo[:nrows]:
-             unused, unused, messageInfo.received = \
-                     self._getTimeRange(self._keyToTimestamp(key))
-             row = self.html.reviewRow.clone()
-             try:
-                 score = messageInfo.score
-             except ValueError:
-                 score = None
-             if label == _('Spam'):
-                 if score is not None \
-                    and score > options["html_ui", "spam_discard_level"]:
-                     r_att = getattr(row, 'discard')
-                 else:
-                     r_att = getattr(row, options["html_ui",
-                                            "default_spam_action"])
-             elif label == _('Ham'):
-                 if score is not None \
-                    and score < options["html_ui", "ham_discard_level"]:
-                     r_att = getattr(row, 'discard')
-                 else:
-                     r_att = getattr(row, options["html_ui",
-                                            "default_ham_action"])
-             else:
-                 r_att = getattr(row, options["html_ui",
-                                            "default_unsure_action"])
-             setattr(r_att, "checked", 1)
- 
-             row.optionalHeadersValues = '' # make way for real list
-             for header in options["html_ui", "display_headers"]:
-                 header = header.lower()
-                 text = getattr(messageInfo, "%sHeader" % (header,))
-                 if header == "subject":
-                     # Subject is special, because it links to the body.
-                     # If the user doesn't display the subject, then there
-                     # is no link to the body.
-                     h = self.html.reviewRow.linkedHeaderValue.clone()
-                     h.text.title = messageInfo.bodySummary
-                     h.text.href = "view?key=%s&corpus=%s" % (key, label)
-                 else:
-                     h = self.html.reviewRow.headerValue.clone()
-                 h.text = text
-                 row.optionalHeadersValues += h
- 
-             # Apart from any message headers, we may also wish to display
-             # the message score, and the time the message was received.
-             if options["html_ui", "display_score"]:
-                 if isinstance(messageInfo.score, types.StringTypes):
-                     # Presumably either "?" or "Err".
-                     row.score_ = messageInfo.score
-                 else:
-                     row.score_ = "%.2f%%" % (messageInfo.score,)
-             else:
-                 del row.score_
-             if options["html_ui", "display_received_time"]:
-                 row.received_ = messageInfo.received
-             else:
-                 del row.received_
- 
-             # Many characters can't go in the URL or they cause problems
-             # (&, ;, ?, etc).  So we use the hex values for them all.
-             subj_list = []
-             for c in messageInfo.subjectHeader:
-                 subj_list.append("%%%s" % (hex(ord(c))[2:],))
-             subj = "".join(subj_list)
-             row.classify.href="showclues?key=%s&subject=%s" % (key, subj)
-             row.tokens.href="showclues?key=%s&subject=%s&tokens=1" % (key, subj)
-             setattr(row, 'class', ['stripe_on', 'stripe_off'][stripe]) # Grr!
-             setattr(row, 'onMouseOut',
-                     ["this.className='stripe_on';",
-                      "this.className='stripe_off';"][stripe])
-             row = str(row).replace('TYPE', label).replace('KEY', key)
-             table += row
-             stripe = stripe ^ 1
- 
      def onReview(self, **params):
          """Present a list of message for (re)training."""
--- 257,260 ----
***************
*** 619,626 ****
                      sh.optionalHeaders = ''
                      h = self.html.headerHeader.clone()
!                     for header in options["html_ui", "display_headers"]:
                          h.headerLink.href = 'review?sort=%sHeader' % \
!                                             (header.lower(),)
!                         h.headerName = header.title()
                          sh.optionalHeaders += h
                      if not options["html_ui", "display_score"]:
--- 497,504 ----
                      sh.optionalHeaders = ''
                      h = self.html.headerHeader.clone()
!                     for disp_header in options["html_ui", "display_headers"]:
                          h.headerLink.href = 'review?sort=%sHeader' % \
!                                             (disp_header.lower(),)
!                         h.headerName = disp_header.title()
                          sh.optionalHeaders += h
                      if not options["html_ui", "display_score"]:
***************
*** 649,661 ****
          self._writePostamble(help_topic="review")
  
-     def _contains(self, a, b, ignore_case=False):
-         """Return true if substring b is part of string a."""
-         assert isinstance(a, types.StringTypes)
-         assert isinstance(b, types.StringTypes)
-         if ignore_case:
-             a = a.lower()
-             b = b.lower()
-         return a.find(b) >= 0
- 
      def onView(self, key, corpus):
          """View a message - linked from the Review page."""
--- 527,530 ----
***************
*** 702,769 ****
          self._writePostamble()
  
-     def _makeMessageInfo(self, message):
-         """Given an email.Message, return an object with subjectHeader,
-         bodySummary and other header (as needed) attributes.  These objects
-         are passed into appendMessages by onReview - passing email.Message
-         objects directly uses too much memory.
-         """
-         # Remove notations before displaying - see:
-         # [ 848365 ] Remove subject annotations from message review page
-         message.delNotations()
-         subjectHeader = message["Subject"] or "(none)"
-         headers = {"subject" : subjectHeader}
-         for header in options["html_ui", "display_headers"]:
-             headers[header.lower()] = (message[header] or "(none)")
-         score = message[options["Headers", "score_header_name"]]
-         if score:
-             # the score might have the log info at the end
-             op = score.find('(')
-             if op >= 0:
-                 score = score[:op]
-             try:
-                 score = float(score) * 100
-             except ValueError:
-                 # Hmm.  The score header should only contain a floating
-                 # point number.  What's going on here, then?
-                 score = "Err"  # Let the user know something is wrong.
-         else:
-             # If the lookup fails, this means that the "include_score"
-             # option isn't activated. We have the choice here to either
-             # calculate it now, which is pretty inefficient, since we have
-             # already done so, or to admit that we don't know what it is.
-             # We'll go with the latter.
-             score = "?"
-         try:
-             part = typed_subpart_iterator(message, 'text', 'plain').next()
-             text = part.get_payload()
-         except StopIteration:
-             try:
-                 part = typed_subpart_iterator(message, 'text', 'html').next()
-                 text = part.get_payload()
-                 text, unused = tokenizer.crack_html_style(text)
-                 text, unused = tokenizer.crack_html_comment(text)
-                 text = tokenizer.html_re.sub(' ', text)
-                 text = _('(this message only has an HTML body)\n') + text
-             except StopIteration:
-                 text = _('(this message has no text body)')
-         if type(text) == type([]):  # gotta be a 'right' way to do this
-             text = _("(this message is a digest of %s messages)") % (len(text))
-         elif text is None:
-             text = _("(this message has no body)")
-         else:
-             text = text.replace('&nbsp;', ' ')      # Else they'll be quoted
-             text = re.sub(r'(\s)\s+', r'\1', text)  # Eg. multiple blank lines
-             text = text.strip()
- 
-         class _MessageInfo:
-             pass
-         messageInfo = _MessageInfo()
-         for headerName, headerValue in headers.items():
-             headerValue = self._trimHeader(headerValue, 45, True)
-             setattr(messageInfo, "%sHeader" % (headerName,), headerValue)
-         messageInfo.score = score
-         messageInfo.bodySummary = self._trimHeader(text, 200)
-         return messageInfo
- 
      def close_database(self):
          state.close()
--- 571,574 ----

Index: UserInterface.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v
retrieving revision 1.61
retrieving revision 1.62
diff -C2 -d -r1.61 -r1.62
*** UserInterface.py	28 Nov 2005 10:53:12 -0000	1.61
--- UserInterface.py	10 Jun 2007 15:27:36 -0000	1.62
***************
*** 80,83 ****
--- 80,84 ----
  import types
  import StringIO
+ from email.Iterators import typed_subpart_iterator
  
  import oe_mailbox
***************
*** 277,280 ****
--- 278,282 ----
          self.stats = stats
          self.app_for_version = None # subclasses must fill this in
+         self.previous_sort = None
  
      def onClassify(self, file, text, which):
***************
*** 820,823 ****
--- 822,827 ----
              elif parms["how"] == _("Save experimental options"):
                  pmap = experimental_ini_map
+             elif parms["how"] == _("Save plugin options"):
+                 pmap = self.plugin_ini_map
              del parms["how"]
          html = self._getHTMLClone()
***************
*** 1251,1252 ****
--- 1255,1448 ----
                  lines.append(''.join(cur_line))
          return lines
+ 
+     def _keyToTimestamp(self, key):
+         """Given a message key (as seen in a Corpus), returns the timestamp
+         for that message.  This is the time that the message was received,
+         not the Date header."""
+         return long(key[:10])
+ 
+     def _getTimeRange(self, timestamp):
+         """Given a unix timestamp, returns a 3-tuple: the start timestamp
+         of the given day, the end timestamp of the given day, and the
+         formatted date of the given day."""
+         this = time.localtime(timestamp)
+         start = (this[0], this[1], this[2], 0, 0, 0, this[6], this[7], this[8])
+         end = time.localtime(time.mktime(start) + 36*60*60)
+         end = (end[0], end[1], end[2], 0, 0, 0, end[6], end[7], end[8])
+         date = time.strftime("%A, %B %d, %Y", start)
+         return time.mktime(start), time.mktime(end), date
+ 
+     def _sortMessages(self, messages, sort_order, reverse=False):
+         """Sorts the message by the appropriate attribute.  If this was the
+         previous sort order, then reverse it."""
+         if sort_order is None or sort_order == "received":
+             # Default sorting, which is in reverse order of appearance.
+             # This is complicated because the 'received' info is the key.
+             messages.sort()
+             if self.previous_sort == sort_order:
+                 messages.reverse()
+                 self.previous_sort = None
+             else:
+                 self.previous_sort = 'received'
+             return messages
+         tmplist = [(getattr(x[1], sort_order), x) for x in messages]
+         tmplist.sort()
+         if reverse:
+             tmplist.reverse()
+         return [x for (key, x) in tmplist]
+ 
+     def _appendMessages(self, table, keyedMessageInfo, label, sort_order,
+                         reverse=False):
+         """Appends the rows of a table of messages to 'table'."""
+         stripe = 0
+ 
+         keyedMessageInfo = self._sortMessages(keyedMessageInfo, sort_order,
+                                               reverse)
+         nrows = options["html_ui", "rows_per_section"]
+         for key, messageInfo in keyedMessageInfo[:nrows]:
+             unused, unused, messageInfo.received = \
+                     self._getTimeRange(self._keyToTimestamp(key))
+             row = self.html.reviewRow.clone()
+             try:
+                 score = messageInfo.score
+             except ValueError:
+                 score = None
+             if label == _('Spam'):
+                 if score is not None \
+                    and score > options["html_ui", "spam_discard_level"]:
+                     r_att = getattr(row, 'discard')
+                 else:
+                     r_att = getattr(row, options["html_ui",
+                                            "default_spam_action"])
+             elif label == _('Ham'):
+                 if score is not None \
+                    and score < options["html_ui", "ham_discard_level"]:
+                     r_att = getattr(row, 'discard')
+                 else:
+                     r_att = getattr(row, options["html_ui",
+                                            "default_ham_action"])
+             else:
+                 r_att = getattr(row, options["html_ui",
+                                            "default_unsure_action"])
+             setattr(r_att, "checked", 1)
+ 
+             row.optionalHeadersValues = '' # make way for real list
+             for header in options["html_ui", "display_headers"]:
+                 header = header.lower()
+                 text = getattr(messageInfo, "%sHeader" % (header,))
+                 if header == "subject":
+                     # Subject is special, because it links to the body.
+                     # If the user doesn't display the subject, then there
+                     # is no link to the body.
+                     h = self.html.reviewRow.linkedHeaderValue.clone()
+                     h.text.title = messageInfo.bodySummary
+                     h.text.href = "view?key=%s&corpus=%s" % (key, label)
+                 else:
+                     h = self.html.reviewRow.headerValue.clone()
+                 h.text = text
+                 row.optionalHeadersValues += h
+ 
+             # Apart from any message headers, we may also wish to display
+             # the message score, and the time the message was received.
+             if options["html_ui", "display_score"]:
+                 if isinstance(messageInfo.score, types.StringTypes):
+                     # Presumably either "?" or "Err".
+                     row.score_ = messageInfo.score
+                 else:
+                     row.score_ = "%.2f%%" % (messageInfo.score,)
+             else:
+                 del row.score_
+             if options["html_ui", "display_received_time"]:
+                 row.received_ = messageInfo.received
+             else:
+                 del row.received_
+ 
+             # Many characters can't go in the URL or they cause problems
+             # (&, ;, ?, etc).  So we use the hex values for them all.
+             subj_list = []
+             for c in messageInfo.subjectHeader:
+                 subj_list.append("%%%s" % (hex(ord(c))[2:],))
+             subj = "".join(subj_list)
+             row.classify.href = "showclues?key=%s&subject=%s" % (key, subj)
+             row.tokens.href = ("showclues?key=%s&subject=%s&tokens=1" %
+                                (key, subj))
+             setattr(row, 'class', ['stripe_on', 'stripe_off'][stripe]) # Grr!
+             setattr(row, 'onMouseOut',
+                     ["this.className='stripe_on';",
+                      "this.className='stripe_off';"][stripe])
+             row = str(row).replace('TYPE', label).replace('KEY', key)
+             table += row
+             stripe = stripe ^ 1
+ 
+     def _contains(self, a, b, ignore_case=False):
+         """Return true if substring b is part of string a."""
+         assert isinstance(a, types.StringTypes)
+         assert isinstance(b, types.StringTypes)
+         if ignore_case:
+             a = a.lower()
+             b = b.lower()
+         return a.find(b) >= 0
+ 
+     def _makeMessageInfo(self, message):
+         """Given an email.Message, return an object with subjectHeader,
+         bodySummary and other header (as needed) attributes.  These objects
+         are passed into appendMessages by onReview - passing email.Message
+         objects directly uses too much memory.
+         """
+         # Remove notations before displaying - see:
+         # [ 848365 ] Remove subject annotations from message review page
+         message.delNotations()
+         subjectHeader = message["Subject"] or "(none)"
+         headers = {"subject" : subjectHeader}
+         for header in options["html_ui", "display_headers"]:
+             headers[header.lower()] = (message[header] or "(none)")
+         score = message[options["Headers", "score_header_name"]]
+         if score:
+             # the score might have the log info at the end
+             op = score.find('(')
+             if op >= 0:
+                 score = score[:op]
+             try:
+                 score = float(score) * 100
+             except ValueError:
+                 # Hmm.  The score header should only contain a floating
+                 # point number.  What's going on here, then?
+                 score = "Err"  # Let the user know something is wrong.
+         else:
+             # If the lookup fails, this means that the "include_score"
+             # option isn't activated. We have the choice here to either
+             # calculate it now, which is pretty inefficient, since we have
+             # already done so, or to admit that we don't know what it is.
+             # We'll go with the latter.
+             score = "?"
+         try:
+             part = typed_subpart_iterator(message, 'text', 'plain').next()
+             text = part.get_payload()
+         except StopIteration:
+             try:
+                 part = typed_subpart_iterator(message, 'text', 'html').next()
+                 text = part.get_payload()
+                 text, unused = tokenizer.crack_html_style(text)
+                 text, unused = tokenizer.crack_html_comment(text)
+                 text = tokenizer.html_re.sub(' ', text)
+                 text = _('(this message only has an HTML body)\n') + text
+             except StopIteration:
+                 text = _('(this message has no text body)')
+         if type(text) == type([]):  # gotta be a 'right' way to do this
+             text = _("(this message is a digest of %s messages)") % (len(text))
+         elif text is None:
+             text = _("(this message has no body)")
+         else:
+             text = text.replace('&nbsp;', ' ')      # Else they'll be quoted
+             text = re.sub(r'(\s)\s+', r'\1', text)  # Eg. multiple blank lines
+             text = text.strip()
+ 
+         class _MessageInfo:
+             pass
+         messageInfo = _MessageInfo()
+         for headerName, headerValue in headers.items():
+             headerValue = self._trimHeader(headerValue, 45, True)
+             setattr(messageInfo, "%sHeader" % (headerName,), headerValue)
+         messageInfo.score = score
+         messageInfo.bodySummary = self._trimHeader(text, 200)
+         return messageInfo

Index: dnscache.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/dnscache.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** dnscache.py	13 Aug 2006 02:05:43 -0000	1.3
--- dnscache.py	10 Jun 2007 15:27:36 -0000	1.4
***************
*** 23,29 ****
  
  kCheckForPruneEvery=20
! kMaxTTL=60 * 60 * 24 * 7 # One week
! kPruneThreshold=1500 # May go over slightly; numbers chosen at random
! kPruneDownTo=1000
  
  
--- 23,32 ----
  
  kCheckForPruneEvery=20
! kMaxTTL=60 * 60 * 24 * 7                # One week
! # Some servers always return a TTL of zero.  We'll hold onto data a bit
! # longer.
! kMinTTL=24 * 60 * 60 * 1                # one day
! kPruneThreshold=5000 # May go over slightly; numbers chosen at random
! kPruneDownTo=2500
  
  
***************
*** 89,97 ****
          self.dnsTimeout=10
  
-         # Some servers always return a TTL of zero.
-         # In those cases, turning this up a bit is
-         # probably reasonable.
-         self.minTTL=0
- 
          # end of user-settable attributes
  
--- 92,95 ----
***************
*** 160,164 ****
              c=self.caches[answer.qType]
              c[answer.question].remove(answer)
!             if len(c[answer.question])==0:
                  del c[answer.question]
  
--- 158,162 ----
              c=self.caches[answer.qType]
              c[answer.question].remove(answer)
!             if  not c[answer.question]:
                  del c[answer.question]
  
***************
*** 180,184 ****
              c=self.caches[answer.qType]
              c[answer.question].remove(answer)
!             if len(c[answer.question])==0:
                  del c[answer.question]
  
--- 178,182 ----
              c=self.caches[answer.qType]
              c[answer.question].remove(answer)
!             if not c[answer.question]:
                  del c[answer.question]
  
***************
*** 218,233 ****
              pass
          else:
!             assert len(answers)>0
!             ind=0
!             # No guarantee that expire has already been done
!             while ind<len(answers):
!                 thisAnswer=answers[ind]
!                 if thisAnswer.expiresAt<now:
!                     del answers[ind]
!                 else:
!                     thisAnswer.lastUsed=now
!                     ind+=1
  
!             if len(answers)==0:
                  del cacheToLookIn[question]
              else:
--- 216,233 ----
              pass
          else:
!             if answers:
!                 ind=0
!                 # No guarantee that expire has already been done
!                 while ind<len(answers):
!                     thisAnswer=answers[ind]
!                     if thisAnswer.expiresAt<now:
!                         del answers[ind]
!                     else:
!                         thisAnswer.lastUsed=now
!                         ind+=1
!             else:
!                 print >> sys.stderr, "lookup failure:", question
  
!             if not answers:
                  del cacheToLookIn[question]
              else:
***************
*** 250,275 ****
          except DNS.Base.DNSError,detail:
              if detail.args[0]<>"Timeout":
!                 print "Error, fixme",detail
!                 print "Question was",queryQuestion
!                 print "Origianal question was",question
!                 print "Type was",qType
              objs=[ lookupResult(qType,None,question,self.cacheErrorSecs+now,now) ]
              cacheToLookIn[question]=objs # Add to format for return?
              return self.formatForReturn(objs)
          except socket.gaierror,detail:
!             print "DNS connection failure:", self.queryObj.ns, detail
!             print "Defaults:", DNS.defaults
  
          objs=[]
          for answer in reply.answers:
              if answer["typename"]==qType:
!                 # PyDNS returns TTLs as longs but RFC 1035 says that the
!                 # TTL value is a signed 32-bit value and must be positive,
!                 # so it should be safe to coerce it to a Python integer.
!                 # And anyone who sets a time to live of more than 2^31-1
!                 # seconds (68 years and change) is drunk.
!                 # Arguably, I ought to impose a maximum rather than continuing
!                 # with longs (int(long) returns long in recent versions of Python).
!                 ttl=max(min(int(answer["ttl"]),kMaxTTL),self.minTTL)
                  # RFC 2308 says that you should cache an NXDOMAIN for the
                  # minimum of the minimum field of the SOA record and the TTL
--- 250,275 ----
          except DNS.Base.DNSError,detail:
              if detail.args[0]<>"Timeout":
!                 print >> sys.stderr, "Error, fixme", detail
!                 print >> sys.stderr, "Question was", queryQuestion
!                 print >> sys.stderr, "Original question was", question
!                 print >> sys.stderr, "Type was", qType
              objs=[ lookupResult(qType,None,question,self.cacheErrorSecs+now,now) ]
              cacheToLookIn[question]=objs # Add to format for return?
              return self.formatForReturn(objs)
          except socket.gaierror,detail:
!             print >> sys.stderr, "DNS connection failure:", self.queryObj.ns, detail
!             print >> sys.stderr, "Defaults:", DNS.defaults
  
          objs=[]
          for answer in reply.answers:
              if answer["typename"]==qType:
!                 # PyDNS returns TTLs as longs but RFC 1035 says that the TTL
!                 # value is a signed 32-bit value and must be positive, so it
!                 # should be safe to coerce it to a Python integer.  And
!                 # anyone who sets a time to live of more than 2^31-1 seconds
!                 # (68 years and change) is drunk.  Arguably, I ought to
!                 # impose a maximum rather than continuing with longs
!                 # (int(long) returns long in recent versions of Python).
!                 ttl=max(min(int(answer["ttl"]),kMaxTTL),kMinTTL)
                  # RFC 2308 says that you should cache an NXDOMAIN for the
                  # minimum of the minimum field of the SOA record and the TTL
***************
*** 279,288 ****
                      objs.append(item)
  
!         if len(objs)>0:
              cacheToLookIn[question]=objs
              return self.formatForReturn(objs)
  
          # Probably SERVFAIL or the like
!         if len(reply.authority)==0:
              objs=[ lookupResult(qType,None,question,self.cacheErrorSecs+now,now) ]
              cacheToLookIn[question]=objs
--- 279,288 ----
                      objs.append(item)
  
!         if objs:
              cacheToLookIn[question]=objs
              return self.formatForReturn(objs)
  
          # Probably SERVFAIL or the like
!         if not reply.authority:
              objs=[ lookupResult(qType,None,question,self.cacheErrorSecs+now,now) ]
              cacheToLookIn[question]=objs
***************
*** 319,329 ****
                   "www.seeputofor.com", "www.completegarbage.tv",
                   "www.tradelinkllc.com"]:
!         print "checking", host
          now=time.time()
          ips=c.lookup(host)
!         print ips,time.time()-now
          now=time.time()
          ips=c.lookup(host)
!         print ips,time.time()-now
  
          if ips:
--- 319,329 ----
                   "www.seeputofor.com", "www.completegarbage.tv",
                   "www.tradelinkllc.com"]:
!         print >> sys.stderr, "checking", host
          now=time.time()
          ips=c.lookup(host)
!         print >> sys.stderr, ips,time.time()-now
          now=time.time()
          ips=c.lookup(host)
!         print >> sys.stderr, ips,time.time()-now
  
          if ips:
***************
*** 331,340 ****
              now=time.time()
              name=c.lookup(ip,qType="PTR")
!             print name,time.time()-now
              now=time.time()
              name=c.lookup(ip,qType="PTR")
!             print name,time.time()-now
          else:
!             print "unknown"
  
      c.close()
--- 331,340 ----
              now=time.time()
              name=c.lookup(ip,qType="PTR")
!             print >> sys.stderr, name,time.time()-now
              now=time.time()
              name=c.lookup(ip,qType="PTR")
!             print >> sys.stderr, name,time.time()-now
          else:
!             print >> sys.stderr, "unknown"
  
      c.close()

Index: storage.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v
retrieving revision 1.63
retrieving revision 1.64
diff -C2 -d -r1.63 -r1.64
*** storage.py	11 May 2007 00:23:08 -0000	1.63
--- storage.py	10 Jun 2007 15:27:36 -0000	1.64
***************
*** 721,726 ****
          import ZODB
          from ZODB.FileStorage import FileStorage
!         self.storage = FileStorage(self.db_filename,
!                                    read_only=self.mode=='r')
  
      def load(self):
--- 721,731 ----
          import ZODB
          from ZODB.FileStorage import FileStorage
!         try:
!             self.storage = FileStorage(self.db_filename,
!                                        read_only=self.mode=='r')
!         except IOError, msg:
!             print >> sys.stderr, ("Could not create FileStorage from",
!                                   self.db_filename)
!             raise
  
      def load(self):
***************
*** 774,778 ****
          from ZODB.POSException import ReadOnlyError
  
!         assert self.closed == False, "Can't store a closed database"
  
          if options["globals", "verbose"]:
--- 779,783 ----
          from ZODB.POSException import ReadOnlyError
  
!         assert not self.closed, "Can't store a closed database"
  
          if options["globals", "verbose"]:



More information about the Spambayes-checkins mailing list