[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Base class for events
commit cac0c434ca9a4ed1f9bb6aa940dba1e006b92471
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Nov 4 18:38:28 2012 -0800
Base class for events
Adding a base class for tor events which does the initial parsing of positional
and keyword arguments. This is using a similar design to the TorCtl patch I
wrote for #3679 (but a little better).
---
stem/control.py | 4 +-
stem/response/__init__.py | 3 ++
stem/response/events.py | 65 ++++++++++++++++++++++++++++++++++++++
test/integ/control/controller.py | 8 +++--
4 files changed, 75 insertions(+), 5 deletions(-)
diff --git a/stem/control.py b/stem/control.py
index ca3730b..fdcf972 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1410,11 +1410,11 @@ class Controller(BaseController):
def _handle_event(self, event_message):
# TODO: parse the event_message into a stem.response.events.Event class
- event_message_type = str(event_message).split()[0]
+ stem.response.convert("EVENT", event_message, arrived_at=time.time())
with self._event_listeners_lock:
for event_type, event_listeners in self._event_listeners.items():
- if event_type == event_message_type:
+ if event_type == event_message.type:
for listener in event_listeners:
listener(event_message)
diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index 050ba02..2e9296d 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -26,6 +26,7 @@ Parses replies from the control socket.
from __future__ import with_statement
__all__ = [
+ "events",
"getinfo",
"getconf",
"protocolinfo",
@@ -85,6 +86,7 @@ def convert(response_type, message, **kwargs):
or response_type isn't supported
"""
+ import stem.response.events
import stem.response.getinfo
import stem.response.getconf
import stem.response.protocolinfo
@@ -95,6 +97,7 @@ def convert(response_type, message, **kwargs):
raise TypeError("Only able to convert stem.response.ControlMessage instances")
response_types = {
+ "EVENT": stem.response.events.Event,
"GETINFO": stem.response.getinfo.GetInfoResponse,
"GETCONF": stem.response.getconf.GetConfResponse,
"MAPADDRESS": stem.response.mapaddress.MapAddressResponse,
diff --git a/stem/response/events.py b/stem/response/events.py
new file mode 100644
index 0000000..32dd655
--- /dev/null
+++ b/stem/response/events.py
@@ -0,0 +1,65 @@
+import re
+
+import stem.response
+
+# Matches keyword=value arguments. This can't be a simple "(.*)=(.*)" pattern
+# because some positional arguments, like circuit paths, can have an equal
+# sign.
+
+KW_ARG = re.compile("([A-Za-z0-9_]+)=(.*)")
+
+class Event(stem.response.ControlMessage):
+ """
+ Base for events we receive asynchronously, as described in section 4.1 of the
+ `control-spec
+ <https://gitweb.torproject.org/torspec.git/blob/HEAD:/control-spec.txt>`_.
+ """
+
+ _POSITIONAL_ARGS = ()
+ _KEYWORD_ARGS = {}
+
+ def _parse_message(self, arrived_at):
+ fields = str(self).split()
+
+ if not fields:
+ raise stem.socket.ProtocolError("Received a blank tor event. Events must at the very least have a type.")
+
+ self.type = fields.pop(0)
+ self.arrived_at = arrived_at
+
+ # Tor events contain some number of positional arguments followed by
+ # key/value mappings. Parsing keyword arguments from the end until we hit
+ # something that isn't a key/value mapping. The rest are positional.
+
+ self.positional_args = []
+ self.keyword_args = {}
+
+ while fields:
+ kw_match = KW_ARG.match(fields[-1])
+
+ if kw_match:
+ k, v = kw_match.groups()
+ self.keyword_args[k] = v
+ fields.pop() # remove the field
+ else:
+ # not a key/value mapping, the remaining fields are positional
+ self.positional_args = fields
+ break
+
+ # Setting attributes for the fields that we recognize. Unrecognized fields
+ # only appear in our 'positional_args' and 'keyword_args' attributes.
+
+ for i in xrange(len(self._POSITIONAL_ARGS)):
+ attr_name = self._POSITIONAL_ARGS[i]
+ attr_value = self.positional_args[i] if i < len(self.positional_args) else None
+
+ setattr(self, attr_name, attr_value)
+
+ for controller_attr_name, attr_name in self._KEYWORD_ARGS.items():
+ setattr(self, attr_name, self.keyword_args.get(controller_attr_name))
+
+ # if we're a recognized event type then translate ourselves into that subclass
+
+ #self.__class__ = response_class
+ #self._parse_message()
+
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index e5d7782..8f93d53 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -74,9 +74,6 @@ class TestController(unittest.TestCase):
# BW events occure at the rate of one per second, so wait a bit to let
# some accumulate.
- # TODO: check that the type of events in event_buffer1 are BandwidthEvent
- # instances when we have proper event types
-
time.sleep(3)
self.assertTrue(len(event_buffer1) >= 2)
@@ -91,6 +88,11 @@ class TestController(unittest.TestCase):
self.assertTrue(len(event_buffer1) >= 4)
self.assertEqual(buffer2_size, len(event_buffer2))
+
+ for event in event_buffer1:
+ self.assertTrue(isinstance(event, stem.response.events.Event))
+ self.assertEqual(2, len(event.positional_args))
+ self.assertEqual({}, event.keyword_args)
def test_getinfo(self):
"""
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits