[Pymoul-svn] SF.net SVN: pymoul: [296] pymoul/trunk/src/moul
Status: Alpha
Brought to you by:
tiran
|
From: <ti...@us...> - 2007-05-29 13:49:35
|
Revision: 296
http://pymoul.svn.sourceforge.net/pymoul/?rev=296&view=rev
Author: tiran
Date: 2007-05-29 06:44:29 -0700 (Tue, 29 May 2007)
Log Message:
-----------
More work on the IRC chat relay system
Modified Paths:
--------------
pymoul/trunk/src/moul/chatrelay/filter.py
pymoul/trunk/src/moul/chatrelay/formatter.py
pymoul/trunk/src/moul/chatrelay/interfaces.py
pymoul/trunk/src/moul/file/chatlog.py
pymoul/trunk/src/moul/file/tests/test_chatlog.py
Added Paths:
-----------
pymoul/trunk/src/moul/chatrelay/handler.py
Removed Paths:
-------------
pymoul/trunk/src/moul/chatrelay/parser.py
Modified: pymoul/trunk/src/moul/chatrelay/filter.py
===================================================================
--- pymoul/trunk/src/moul/chatrelay/filter.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/chatrelay/filter.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -2,6 +2,7 @@
from moul.chatrelay.interfaces import ILineFilter
from moul.chatrelay.interfaces import ISnoopyLineFilter
+from moul.chatrelay.interfaces import IConfigureableFilter
from moul.file.chatlog import (CHAT_UNKNOWN, CHAT_PRIVMSG, CHAT_PRIVMSGTO,
CHAT_PRIVMSGFROM, CHAT_ACTION, CHAT_ERROR,
CHAT_MSG)
@@ -9,42 +10,43 @@
__all__ = [obj for obj in globals() if ILineFilter.isImplementedBy(obj)]
class NullFilter(object):
+ """Filter nothing
+ """
implements(ILineFilter)
- name = 'Null filter'
def filter(self, line, **kwargs):
return line, kwargs
class HightlightImportantFilter(object):
+ """Highlight important people
+ """
implements(ILineFilter)
- name = 'Highlight important people'
def filter(self, line, **kwargs):
if kwargs.get('important', False):
kwargs['highlight'] = True
return line, kwargs
-class AbstractMessageFilter(object):
+class _AbstractMessageFilter(object):
implements(ILineFilter)
- name = None
- msg_type = None
+ msg_flags = None
def __init__(self):
- assert self.msg_type, "No message type applied"
+ assert self.msg_flags, "No message type applied"
def filter(self, line, **kwargs):
typ = kwargs.get('type', CHAT_UNKNOWN)
- if typ & self.msg_type:
- return None, None
+ if typ & self.msg_flags:
+ kwargs['skip'] = True
return line, kwargs
-class PrivateMessageFilter(AbstractMessageFilter):
+class PrivateMessageFilter(_AbstractMessageFilter):
name = 'Private message filter'
- msg_type = CHAT_PRIVMSG
+ msg_flags = CHAT_PRIVMSG
-class ErrorMessageFilter(AbstractMessageFilter):
+class ErrorMessageFilter(_AbstractMessageFilter):
name = 'Error message filter'
- msg_type = CHAT_ERROR
+ msg_flags = CHAT_ERROR
class NonImportantFilter(object):
implements(ILineFilter)
@@ -52,5 +54,6 @@
def filter(self, line, **kwargs):
if not kwargs.get('important', False):
- return None, None
+ kwargs['skip'] = True
return line, kwargs
+
Modified: pymoul/trunk/src/moul/chatrelay/formatter.py
===================================================================
--- pymoul/trunk/src/moul/chatrelay/formatter.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/chatrelay/formatter.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -8,25 +8,41 @@
__all__ = [obj for obj in globals() if IOutputFormatter.isImplementedBy(obj)]
class NullFormatter(object):
+ """Null formatter
+
+ The null formatter returns the line without changing it.
+ """
implements(IOutputFormatter)
- name = 'Null formatter'
def format(self, line, **kwargs):
+ if kwargs.get('skip', False):
+ return None
return line
class MOULFormatter(object):
+ """MOUL formatter
+
+ The MOUL formatter formats the text based on its type. It also understands
+ some additional keyword arguments:
+
+ * highlight: text is highlighted with bold characters
+ * skip: text is skipped (no output)
+ """
implements(IOutputFormatter)
name = 'MOUL formatter'
def format(self, line, **kwargs):
+ if not line:
+ return
typ = kwargs.get('type', CHAT_UNKNOWN)
- highlight = kwargs.get('highlight', False)
if typ == CHAT_PRIVMSGTO:
line = "-> %s" % line
elif typ == CHAT_PRIVMSGFROM:
line = "<- %s" % line
elif typ == CHAT_ACTION:
line = "* %s" % line
- if highlight:
+ if kwargs.get('highlight', False):
line = "%B%s" % line
+ if kwargs.get('skip', False):
+ return None
return line
Copied: pymoul/trunk/src/moul/chatrelay/handler.py (from rev 295, pymoul/trunk/src/moul/chatrelay/parser.py)
===================================================================
--- pymoul/trunk/src/moul/chatrelay/handler.py (rev 0)
+++ pymoul/trunk/src/moul/chatrelay/handler.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -0,0 +1,37 @@
+import os
+from zope.interface import implements
+from moul.file.chatlog import ChatlogParser
+from moul.chatrelay.interfaces import IInputHandler
+
+__all__ = [obj for obj in globals() if IInputHandler.isImplementedBy(obj)]
+
+class NullHandler(object):
+ implements(IInputHandler)
+ name = 'Null parser'
+ delimiter = '\n'
+
+ def __init__(self, delimiter=None, **kwargs):
+ if delimiter:
+ self.delimiter = delimiter
+ self._buffer = ''
+
+ def handle(self, raw):
+ data = self._buffer + raw
+ lines = data.split(self.delimiter)
+ self._buffer = lines.pop(-1)
+ for line in lines:
+ yield self.parse(line)
+
+ def parse(self, line):
+ return line, {}
+
+class MOULChatlogHandler(NullHandler):
+ name = 'MOUL chatlog parser'
+ delimiter = os.linesep
+
+ def __init__(self):
+ super(MOULChatlogHandler).__init__()
+ self._parser = ChatlogParser([])
+
+ def parse(self, line):
+ return self._parser.parse(line)
Modified: pymoul/trunk/src/moul/chatrelay/interfaces.py
===================================================================
--- pymoul/trunk/src/moul/chatrelay/interfaces.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/chatrelay/interfaces.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -4,8 +4,9 @@
class IFileAppendWatchService(Interface):
"""A service which monitors text files and calls the callback for new lines
"""
+ default_interval = Attribute("Default interval length in seconds")
- def monitorFile(path, callback, errback=None):
+ def monitorFile(path, callback, errback=None, interval=None):
"""Monitor a file
@param path: path to a file
@@ -14,8 +15,26 @@
@type callback: callable
@param errback: errback function(path, error)
@type errback: callable
+ @param interval: interval in seconds (None for default interval
+ @type interval: int or None
"""
+
+ def pauseFile(path):
+ """Pause monitoring the file (closes fd)
+ @param path: path to a registered file
+ @type path: string
+ """
+
+ def resumeFile(path, fromEnd=True):
+ """Pause monitoring the file (closes fd)
+
+ @param path: path to a registered file
+ @type path: string
+ @param fromEnd: start from EOF or last position
+ @type fromEnd: bool
+ """
+
def unmonitorFile(path):
"""Stop monitoring files
@@ -30,10 +49,19 @@
@rtype: list
"""
-class IInputParser(Interface):
- """Parse input data
+class IInputHandler(Interface):
+ """Handle raw input and parse it
"""
- name = Attribute("name of the parser")
+ delimiter = Attribute("line delimiter (default: '\n')")
+
+ def handle(raw):
+ """Handle raw data
+
+ @param raw: raw data
+ @type raw: string
+ @return: parsed data as an iterable
+ @rtype: generator yielding (str, dict)
+ """
def parse(line):
"""Parse a single line
@@ -46,17 +74,19 @@
class ILineFilter(Interface):
"""A line filter
+
+ In order to suppress the output of a line the filter may set
+ kwargs['skip'] to True.
"""
- name = Attribute("name of the filter")
def filter(line, **kwargs):
- """
+ """Filter a line
@param line: the line to filter
@type line: string
@param kwargs: additional information
- @return: (filtered line, kwargs) or None, None
- @rtype: (string, dict) / None, None
+ @return: (filtered line, kwargs)
+ @rtype: (string, dict)
"""
class ISnoopyLineFilter(ILineFilter):
@@ -75,10 +105,16 @@
@return: None
"""
+class IConfigureableFilter(ILineFilter):
+ """A filter which may be configured using commands
+ """
+ def listCommands():
+ """List commands XXX: finish me
+ """
+
class IOutputFormatter(Interface):
"""A line formatter
"""
- name = Attribute("name of the output formatter")
def format(line, **kwargs):
"""Format a line
@@ -86,8 +122,8 @@
@param line: the line to filter
@type line: string
@param kwargs: additional information
- @return: a formatted line
- @rtype: string
+ @return: a formatted line or None
+ @rtype: string, None
"""
class IChatRelay(Interface):
@@ -121,3 +157,7 @@
@param name: name of the filter
@type name: string
"""
+
+ def listFilters():
+ """Return a list of filters
+ """
Deleted: pymoul/trunk/src/moul/chatrelay/parser.py
===================================================================
--- pymoul/trunk/src/moul/chatrelay/parser.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/chatrelay/parser.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -1,27 +0,0 @@
-from zope.interface import implements
-
-from moul.file.chatlog import ChatlogParser
-
-from moul.chatrelay.interfaces import IInputParser
-
-__all__ = [obj for obj in globals() if IInputParser.isImplementedBy(obj)]
-
-class NullParser(object):
- implements(IInputParser)
- name = 'Null parser'
-
- def __init__(self, *args, **kwargs):
- pass
-
- def parse(self, line):
- return line, {}
-
-class MOULChatlogParser(ChatlogParser):
- implements(IInputParser)
- name = 'MOUL chatlog parser'
-
- def __init__(self, year):
- self.setYear(year)
-
- def setYear(self, year):
- self._year = int(year)
Modified: pymoul/trunk/src/moul/file/chatlog.py
===================================================================
--- pymoul/trunk/src/moul/file/chatlog.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/file/chatlog.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -349,17 +349,17 @@
New idea, new design
"""
- def __init__(self, iterable=[], year=2007):
+ def __init__(self, iterable):
self._iterable = iterable
- self._year = year # XXX
def parse(self, line):
mo = CHAT_RE.match(line)
if mo is None:
- return ChatLine(line, (1970, 1, 1, 0, 0, 0), CHAT_UNKNOWN)
+ return line, {'type' : CHAT_UNKNOWN}
d = mo.groupdict()
- dt = (self._year, int(d['M']), int(d['D']),
+ dt = (0, int(d['M']), int(d['D']),
int(d['h']), int(d['m']), int(d['s']))
+ dt = self.estimateDT(dt)
msg = d['msg']
info = {'type' : CHAT_UNKNOWN, 'datetime' : dt, 'important' : False}
if len(d['space']) == 2:
@@ -393,6 +393,13 @@
if user in USER_IMPORTANT:
info['important'] = True
return msg, info
+
+ def estimateDT(self, lst):
+ lt = localtime()
+ year = lt[0]
+ if lst[1:2] == (12, 31) and lt[1:2] == (1, 1):
+ year = year - 1
+ return (year,) + lst[1:]
def __iter__(self):
for line in self._iterable:
Modified: pymoul/trunk/src/moul/file/tests/test_chatlog.py
===================================================================
--- pymoul/trunk/src/moul/file/tests/test_chatlog.py 2007-05-28 16:53:00 UTC (rev 295)
+++ pymoul/trunk/src/moul/file/tests/test_chatlog.py 2007-05-29 13:44:29 UTC (rev 296)
@@ -41,17 +41,14 @@
parser = ChatlogParser(TEST_LOG)
results = list(iter(parser))
fue = self.failUnlessEqual
- for result in results:
- fue(result.datetime, (2007, 1, 2, 3, 4, 5))
- fue(results[0].typ, CHAT_START)
- fue(results[1].typ, CHAT_STOP)
- fue(results[2].typ, CHAT_PRIVMSGFROM)
- fue(results[3].typ, CHAT_ERROR)
- fue(results[4].typ, CHAT_MSG)
- fue(results[5].typ, CHAT_PRIVMSGTO)
- fue(results[6].typ, CHAT_ACTION)
+ for msg, info in results:
+ fue(info['datetime'], (2007, 1, 2, 3, 4, 5))
+ for i, t in enumerate((CHAT_START, CHAT_STOP, CHAT_PRIVMSGFROM,
+ CHAT_ERROR, CHAT_MSG, CHAT_PRIVMSGTO)):
+ msg, info = results[i]
+ fue(info['type'], t)
for i in (2, 4, 5):
- fue(results[i].info['user'], 'USER')
+ fue(results[i][1]['user'], 'USER')
def test_suite():
return unittest.TestSuite((
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|