Commits

James Mills committed 9c27ee4 Merge

Merged with aspidites - Fixing issue with WIndows and unix related improts

Comments (0)

Files changed (112)

 holger@merlinux.eu = Holger Krekel
 toni@playsign.net = Toni Alatalo
 root@localhost = James Mills
+aspidites = Edwin Marshall
 dsuch = Dariusz Suchojad
 jamesmills = James Mills
 jaemsmills = James Mills
 f572915d5b78ea570c3008588bbc006cda8963a2 1.5
 f572915d5b78ea570c3008588bbc006cda8963a2 1.5
 2c8f6603dccf38e3e052db4675056d17089f37df 1.5
+85fd4941cc0369eef29134b4c606a9441e4b607d 1.6
+85fd4941cc0369eef29134b4c606a9441e4b607d 1.6
+ac4e2474236e59d5d73c5ad7b9f4da25cd2701f9 1.6
+ac4e2474236e59d5d73c5ad7b9f4da25cd2701f9 1.6
+bdaa579d1f825b835055ebc4669272f79f4b32b1 1.6
+18f0db0026e726e55af8fc62752569a5d9e6bde3 1.6
+circuits-1.6.1 YYYYMMDD
+-----------------------
+
+- Fixed missing import of sys in ``circuits.io``
 ................
 
 This is the third attempt at getting Python 3 support for circuits working
-while still maintaining Python 2 compatibility. These release finally adds
+while still maintaining Python 2 compatibility. This release finally adds
 full support for Python 3 as well as maintaining compatibility with Python
-2.6 and 2.7 with the same codebase.
+2.6 and 2.7 with the same code-base.
 
 .. note::
    Python 2.5 support has been dropped as of this release and will no
    specifically for Python 2.5 if required.
 
 
+Greenlet Support
+................
+
+circuits now includes two new primitives which work by integrating
+`greenlet <http://pypi.python.org/pypi/greenlet>`_.
+
+- ``.waitEvent(...)`` -- Wait for the given event to complete.
+- ``.callEvent(...)`` -- Call event handlers for the given event.
+
+.. note:: These new primitives add (*as long as greenlet is installed*) the ability to write semi-synchronous code in your event handlers while still taking full advantage of the asynchronous and component framework that circuits has to offer.
+
 Code Coverage
 .............
 
 Features
 ........
 
-...
+- Added an example WebSockets server using circuits.web
+- Added support for specifying a ``Poll`` instance to use when using the
+  ``@future`` decorator to create "future" event handlers.
+- Added ``add_section``, ``has_section`` and ``set`` methods to
+  ``app.config.Config`` Component.
+- Added support for running test suite with distutils ``python setup.py
+  test``.
+- Added a ``_on_signal`` event handler on the ``BaseEnvironment`` Component
+  so that environments can be reloaded by listening to ``SIGHUP`` signals.
+- Added support for using absolute paths in ``app.env.Environment``.
+- Added support in circuits.web ``HTTP`` protocol to limit the no. of
+  header fragments. This prevents OOM exploits.
+- Added a ticks limit to waitEvent
+- Added deprecation warnings for .push .add and .remove methods
+- NEW ``Loader`` Component in ``circuits.core`` for simple plugin support.
+- NEW ``app.env`` and ``app.config`` modules including a new ``app.startup``
+  modules integrating a common startup for applications.
+- NEW ``KQueue`` poller
 
 
 Bug Fixes
 .........
 
-...
+- Fixed Issue #17
+- Renamed ``circuits.web.main`` module to ``circuits.web.__main__`` so that
+  ``python -m circuits.web`` just works.
+- Fixed ``Server.host`` and ``Server.port`` properties in
+  ``circuits.net.sockets``.
+- Fixed Issue #19
+- Fixed ``app.Daemon`` Component to correctly open the stderr file.
+- Fixed triggering of ``Success`` events.
+- Fixed duplicate broadcast handler in ``UDPServer``
+- Fixed duplicate ``Disconnect`` event from being triggered twice on
+  ``Client`` socket components.
+- Removed dynamic timeout code from ``Select`` poller.
+- Fixed a bug in the circuits.web ``HTTP`` protocol where headers were
+  not being buffered per client.
+- Fixes a missing Event ``Closed()`` not being triggered for ``UDPServer``.
+- Make underlying ``UDPServer`` socket reusable by setting ``SO_REUSEADDR``
+- Fixes Server socket being discarded twice on close + disconnect
+- Socket.write now expects bytes (bytes for python3 and str for python2)
+- Better handling of encoding in HTTP Component (allow non utf-8 encoding)
+- Always encode http headers in utf-8
+- Fixes error after getting socket.ERRCONNREFUSED
+- Allows TCPClient to bind to a specific port
+- Improved docs
+- Handles closing of udpserver socket when no client is connected
+- Adds an unregister handler for components
+- Allows utils.kill to work from a different thread
+- Fixes bug when handling "*" in channels and targets
+- Fixes a bug that could occur when unregistering components
+- Fixes for CPU usage problems when using circuits with no I/O pollers
+  and using a Timer for timed events

circuits/app/daemon.py

 """Daemon Component
 
 Component to daemonizae a system into the background and detach it from its
-controlling PTY. Supports pid file writing, logging stdin, stdout and stderr
+controlling PTY. Supports PID file writing, logging stdin, stdout and stderr
 and changing the current working directory.
 """
 
         os.dup2(so.fileno(), sys.stdout.fileno())
         os.dup2(se.fileno(), sys.stderr.fileno())
 
-        self.push(WritePID())
+        self.fire(WritePID())
 
     @handler("started", filter=True, priority=100.0, target="*")
     def _on_started(self, manager, mode):
         if not manager == self and mode is None:
-            self.push(Daemonize())
+            self.fire(Daemonize())
 
     @handler("registered")
     def _on_registered(self, component, manager):
         if component == self and manager == self and manager.root.running:
-            self.push(Daemonize())
+            self.fire(Daemonize())

circuits/app/env.py

 
 
 class Verify(EnvironmentEvent):
-    """Verify EEnvironment Event"""
+    """Verify Environment Event"""
 
     success = "verify_success", EnvironmentEvent._target
     failure = "verify_failure", EnvironmentEvent._target
     @handler("load", priority=1.0)
     def load(self, verify=False):
         if verify:
-            return self.push(Verify())
+            return self.fire(Verify())
         else:
             return self._load()
 
                         "path": self.path,
                     }
                 self.config.set(section, option, value)
-        return self.push(config.Save(), target=self.config)
+        return self.fire(config.Save(), target=self.config)
 
     def _load(self):
         # Create Config Component
         configfile = joinpath(self.path, "conf", "%s.ini" % self.envname)
         self.config = Config(configfile).register(self)
-        self.push(config.Load(), target=self.config)
+        self.fire(config.Load(), target=self.config)
         return True

circuits/app/startup.py

     @handler("signal", target="*")
     def _on_signal(self, signal, track):
         if signal in (SIGINT, SIGTERM):
-            self.push(Terminate())
+            self.fire(Terminate())
 
     @handler("environment_loaded", target="env")
     def _on_environment_loaded(self, *args):
-        self.push(Command(), self.command, self)
+        self.fire(Command(), self.command, self)
 
     @handler("started")
     def _on_started(self, component, mode):
             if not os.path.exists(self.env.path):
                 raise Error("Environment does not exist!")
             else:
-                self.push(LoadEnvironment(), target=self.env)
+                self.fire(LoadEnvironment(), target=self.env)
         else:
             if os.path.exists(self.env.path):
                 raise Error("Environment already exists!")
             else:
-                self.push(Command(), self.command, self)
+                self.fire(Command(), self.command, self)
 
     @handler("start")
     def _on_start(self):
         if self.opts.daemon:
-            pidfile = self.env.config.get("general", "pidfile", "dap.pid")
+            pidfile = self.env.config.get("general", "pidfile", "app.pid")
             Daemon(pidfile, self.env.path).register(self)
 
     @handler("stop")
 
     @handler("restart")
     def _on_restart(self):
-        self.push(Command(), "stop", self.channel)
+        self.fire(Command(), "stop", self.channel)
         sleep(1)
-        self.push(Command(), "start", self.channel)
+        self.fire(Command(), "start", self.channel)
 
     @handler("rehash")
     def _on_rehash(self):
 
     @handler("init")
     def _on_init(self):
-        self.push(CreateEnvironment(), target=self.env)
+        self.fire(CreateEnvironment(), target=self.env)
 
     @handler("upgrade")
     def _on_upgrade(self):
-        self.push(UpgradeEnvironment(), target=self.env)
+        self.fire(UpgradeEnvironment(), target=self.env)

circuits/core/bridge.py

         try:
             eid = self._values[value]
             s = dumps((eid, value), -1)
-            self.push(Write(s), target=self._socket)
+            self.fire(Write(s), target=self._socket)
         except:
             return
 
     def _process(self, id, obj):
         if isinstance(obj, Event):
             obj.remote = True
-            value = self._manager.push(obj)
+            value = self._manager.fire(obj)
             self._values[value] = id
             value.manager = self._manager
             value.onSet = "value",
             eid = id(event)
             self._values[eid] = event.value
             s = dumps((eid, event))
-            self.push(Write(s), target=self._socket)
+            self.fire(Write(s), target=self._socket)
         except:
             return
 

circuits/core/components.py

 
 """Components
 
-This module definse the BaseComponent and the subclassed Component
+This module defines the BaseComponent and the subclass Component
 """
 
 from itertools import chain
 
 from .utils import findroot
 from .manager import Manager
-from .handlers import HandlerMetaClass
+from .handlers import HandlerMetaClass, handler
 from .events import Registered, Unregistered
 import collections
 
             handlers = [v for k, v in getmembers(self, p)]
             for handler in handlers:
                 target = handler.target or getattr(self, "channel", "*")
-                self.add(handler, target=target)
+                self.addHandler(handler, target=target)
         else:
             for handler in chain(self._globals, self._handlers):
                 kwargs = {}
                     del kwargs["channels"]
                 else:
                     channels = ()
-                manager.add(handler, *channels, **kwargs)
+                manager.addHandler(handler, *channels, **kwargs)
 
     def _unregisterHandlers(self, manager):
         for handler in self._handlers.copy():
-            manager.remove(handler)
+            manager.removeHandler(handler)
 
     def register(self, manager):
         """Register all Event Handlers with the given Manager
         given Manager. By default, every Component (Base Component) is
         registered with itself.
         
-        Iif the Component or Manager being registered
+        If the Component or Manager being registered
         with is not the current Component, then any Hidden Components
-        in registered to this Component will also be regsitered with the
+        in registered to this Component will also be registered with the
         given Manager. A Registered Event will also be sent.
         """
 
 
         return self
 
+    @handler('unregister')
+    def on_unregister(self, component=None):
+        if component == self or component == None:
+            self.unregister()
+
     def unregister(self):
         """Unregister all registered Event Handlers
         
 
         @note: It's possible to unregister a Component from itself!
         """
-
         def _unregister(c, m, r):
             c._unregisterHandlers(m)
             c.root = self

circuits/core/debugger.py

 
 """
 Debugger component used to debug each event in a system by printing
-each event to sys.stderr or to a Logger Component instnace.
+each event to sys.stderr or to a Logger Component instance.
 """
 
 import os
         if self.logger is not None:
             self.logger.error("".join(s))
         else:
-            self.file.write("".join(s))
-            # Bugged on py2
-            #self.file.flush()
+            try:
+                self.file.write("".join(s))
+                self.file.flush()
+            except IOError:
+                pass
 
     @handler(priority=100.0)
     def _on_event(self, event, *args, **kwargs):
             if self.logger is not None:
                 self.logger.debug(s)
             else:
-                self.file.write(s)
-                self.file.write("\n")
-                self.file.flush()
+                try:
+                    self.file.write(s)
+                    self.file.write("\n")
+                    self.file.flush()
+                except IOError:
+                    pass

circuits/core/events.py

 
 """Events
 
-This module define the basic Event object and commmon events.
+This module define the basic Event object and common events.
 """
 
 class Event(object):
     :param args: list of arguments
     :type  args: tuple
 
-    :param kwargs: dct of keyword arguments
+    :param kwargs: dict of keyword arguments
     :type  kwargs: dict
     """
 
 class Failure(Event):
     """Failure Event
 
-    This Event is sent when an error has occured with the execution of an
+    This Event is sent when an error has occurred with the execution of an
     Event Handlers.
 
-    :param evt: The event that failued
+    :param evt: The event that failed
     :type  evt: Event
 
     :param handler: The handler that failed
     :type  handler: @handler
 
-    :param error: A tuple containing the exception that occured
+    :param error: A tuple containing the exception that occurred
     :type  error: (etype, evalue, traceback)
     """
 
 
         super(Registered, self).__init__(component, manager)
 
+
+class Unregister(Event):
+    """Unregister Event
+
+    This Event ask for a Component to unregister from its
+    Component or Manager.
+    """
+
+    def __init__(self, component=None):
+        "x.__init__(...) initializes x; see x.__class__.__doc__ for signature"
+
+        super(Unregister, self).__init__(component)
+
+
 class Unregistered(Event):
     """Unregistered Event
 

circuits/core/futures.py

 
 """Futures
 
-...
+circuits supports the concept of "future" events. In circuits futures are
+event handlers that are specially designed to be run in the background
+either in a Thread or a Process. If you have event handlers that may
+potentially "block" then wrapping them by the @future decorator unblocks
+the bottle-neck caused by the "blocking" event handler(s).
+
+Support for using a Thread or Process pool is also supported by specifying
+an optional `pool` keyword argument and supplying an instance to a
+``circuits.core.pool.Pool``.
 """
 
 from uuid import uuid4 as uuid
                 p = findcmp(self.root, Pool)
             if p is not None:
                 setattr(self, "_pool", p)
-                return self.push(Task(f, self, *args, **kwargs), target=p)
+                return self.fire(Task(f, self, *args, **kwargs), target=p)
             else:
-                return Worker(channel=str(uuid())).push(
+                return Worker(channel=str(uuid())).fire(
                         Task(f, self, *args, **kwargs))
         wrapper.event = True
         return update_wrapper(wrapper, f)

circuits/core/manager.py

            Use :py:meth:`addHandler` instead.
         """
 
-        warn(DeprecationWarning("Use .addHandler(...) instead"))
+        warn(DeprecationWarning("Use .addHandler(...) instead"), DeprecationWarning, 2)
 
         return self.addHandler(*args, **kwargs)
 
 
             (target, channel) = channel
 
-            if target in self._tmap and handler in self._tmap:
+            if target in self._tmap and handler in self._tmap[target]:
                 self._tmap[target].remove(handler)
                 if not self._tmap[target]:
                     del self._tmap[target]
 
-            if channel in self._cmap and handler in self._cmap:
+            if channel in self._cmap and handler in self._cmap[channel]:
                 self._cmap[channel].remove(handler)
                 if not self._cmap[channel]:
                     del self._cmap[channel]
            Use :py:meth:`fire` instead.
         """
 
-        warn(DeprecationWarning("Use .fire(...) instead"))
+        warn(DeprecationWarning("Use .fire(...) instead"), DeprecationWarning, 2)
 
         return self.fire(*args, **kwargs)
 
     def callEvent(self, event, channel=None, target=None):
         self.fire(event, channel, target)
         e = self.waitEvent(event)
-        return e.value.value
+        return e.value
 
     call = callEvent
 
         retval = None
         handler = None
 
-        for handler in self._getHandlers(channel):
+        handlers = self._getHandlers(channel)
+        handlerattrs = self._handlerattrs.copy()
+
+        for handler in handlers[:]:
             error = None
-
-            attrs = self._handlerattrs[handler]
             event.handler = handler
+            attrs = handlerattrs[handler]
 
             try:
                 if attrs["event"]:
         self.tick()
 
     def tick(self):
-        if self._ticks:
+        for f in self._ticks.copy():
             try:
-                [f() for f in self._ticks.copy()]
+                f()
             except (KeyboardInterrupt, SystemExit):
                 raise
             except:
                 etype, evalue, etraceback = _exc_info()
                 self.fire(Error(etype, evalue, format_tb(etraceback)))
+
+        if self:
+            self.flush()
         else:
-            sleep(TIMEOUT)  # Nothing to do - Let's not tie up the CUP
-
-        self._flush()
+            sleep(TIMEOUT)
 
     def run(self, *args, **kwargs):
         log = kwargs.get("log", True)

circuits/core/pollers.py

 
         for sock in w:
             if self.isWriting(sock):
-                self.push(Write(sock), "_write", self.getTarget(sock))
+                self.fire(Write(sock), "_write", self.getTarget(sock))
 
         for sock in r:
             if self.isReading(sock):
-                self.push(Read(sock), "_read", self.getTarget(sock))
+                self.fire(Read(sock), "_read", self.getTarget(sock))
 
 
 class Poll(BasePoller):
         fd = self._map[fileno]
 
         if event & self._disconnected_flag and not (event & select.POLLIN):
-            self.push(Disconnect(fd), "_disconnect", self.getTarget(fd))
+            self.fire(Disconnect(fd), "_disconnect", self.getTarget(fd))
             self._poller.unregister(fileno)
             super(Poll, self).discard(fd)
             del self._map[fileno]
         else:
             try:
                 if event & select.POLLIN:
-                    self.push(Read(fd), "_read", self.getTarget(fd))
+                    self.fire(Read(fd), "_read", self.getTarget(fd))
                 if event & select.POLLOUT:
-                    self.push(Write(fd), "_write", self.getTarget(fd))
+                    self.fire(Write(fd), "_write", self.getTarget(fd))
             except Exception as e:
-                self.push(Error(fd, e), "_error", self.getTarget(fd))
-                self.push(Disconnect(fd), "_disconnect", self.getTarget(fd))
+                self.fire(Error(fd, e), "_error", self.getTarget(fd))
+                self.fire(Disconnect(fd), "_disconnect", self.getTarget(fd))
                 self._poller.unregister(fileno)
                 super(Poll, self).discard(fd)
                 del self._map[fileno]
         fd = self._map[fileno]
 
         if event & self._disconnected_flag and not (event & select.POLLIN):
-            self.push(Disconnect(fd), "_disconnect", self.getTarget(fd))
+            self.fire(Disconnect(fd), "_disconnect", self.getTarget(fd))
             self._poller.unregister(fileno)
             super(EPoll, self).discard(fd)
             del self._map[fileno]
         else:
             try:
                 if event & select.EPOLLIN:
-                    self.push(Read(fd), "_read", self.getTarget(fd))
+                    self.fire(Read(fd), "_read", self.getTarget(fd))
                 if event & select.EPOLLOUT:
-                    self.push(Write(fd), "_write", self.getTarget(fd))
+                    self.fire(Write(fd), "_write", self.getTarget(fd))
             except Exception as e:
-                self.push(Error(fd, e), "_error", self.getTarget(fd))
-                self.push(Disconnect(fd), "_disconnect", self.getTarget(fd))
+                self.fire(Error(fd, e), "_error", self.getTarget(fd))
+                self.fire(Disconnect(fd), "_disconnect", self.getTarget(fd))
                 self._poller.unregister(fileno)
                 super(EPoll, self).discard(fd)
                 del self._map[fileno]
         sock = self._map[event.ident]
 
         if event.flags & select.KQ_EV_ERROR:
-            self.push(Error(sock, "error"), "_error", self.getTarget(sock))
+            self.fire(Error(sock, "error"), "_error", self.getTarget(sock))
         elif event.flags & select.KQ_EV_EOF:
-            self.push(Disconnect(sock), "_disconnect", self.getTarget(sock))
+            self.fire(Disconnect(sock), "_disconnect", self.getTarget(sock))
         elif event.filter == select.KQ_FILTER_WRITE:
-            self.push(Write(sock), "_write", self.getTarget(sock))
+            self.fire(Write(sock), "_write", self.getTarget(sock))
         elif event.filter == select.KQ_FILTER_READ:
-            self.push(Read(sock), "_read", self.getTarget(sock))
+            self.fire(Read(sock), "_read", self.getTarget(sock))
 
 Poller = Select
 

circuits/core/pools.py

         if not assigned:
             worker = choice(self._workers)
             assigned = worker.channel
-            return worker.push(Task(f, *args, **kwargs), target=worker)
+            return worker.fire(Task(f, *args, **kwargs), target=worker)

circuits/core/timers.py

 
     def __tick__(self):
         if time() > self._eTime:
-            self.push(self.e, self.c, self.t)
+            self.fire(self.e, self.c, self.t)
 
             if self.persist:
                 self.reset()

circuits/io/__init__.py

 # Date:     4th August 2004
 # Author:   James Mills <prologic@shortcircuit.net.au>
 
-"""I/O Components
+"""I/O Support
 
 This package contains various I/O Components. Provided are a a generic File
 Component, StdIn, StdOut and StdErr components. Instances of StdIn, StdOUt
 and StdErr are also created by importing this package.
 """
 
-import os
 import sys
-import errno
-import select
-from collections import deque
 
-from circuits.tools import tryimport
-from circuits.core import Event, Component
+from .file import File
+from .serial import Serial
+from .events import Close, Seek, Write
 
-fcntl = tryimport("fcntl")
-serial = tryimport("serial")
-
-TIMEOUT = 0.2
-BUFSIZE = 4096
-
-
-class EOF(Event):
-    """EOF Event"""
-
-
-class Seek(Event):
-    """Seek Event"""
-
-
-class Read(Event):
-    """Read Event"""
-
-
-class Close(Event):
-    """Close Event"""
-
-
-class Write(Event):
-    """Write Event"""
-
-
-class Error(Event):
-    """Error Event"""
-
-
-class Opened(Event):
-    """Opened Event"""
-
-
-class Closed(Event):
-    """Closed Event"""
-
-
-class File(Component):
-
-    channel = "file"
-
-    def __init__(self, filename=None, mode="r", fd=None, autoclose=True,
-            bufsize=BUFSIZE, encoding="utf-8", channel=channel):
-        super(File, self).__init__(channel=channel)
-
-        self.bufsize = bufsize
-        self.encoding = encoding
-        self.autoclose = autoclose
-
-        if filename is not None:
-            self.mode = mode
-            self.filename = filename
-            self._fd = open(filename, mode)
-        else:
-            self._fd = fd
-            self.mode = fd.mode
-            self.filename = fd.name
-
-        if fcntl is not None:
-            # Set non-blocking file descriptor (non-portable)
-            flag = fcntl.fcntl(self._fd, fcntl.F_GETFL)
-            flag = flag | os.O_NONBLOCK
-            fcntl.fcntl(self._fd, fcntl.F_SETFL, flag)
-
-        self._read = []
-        self._write = []
-        self._buffer = deque()
-
-        if any([m for m in "r+" if m in self._fd.mode]):
-            self._read.append(self._fd)
-
-        self.push(Opened(self.filename), "opened")
-
-    @property
-    def closed(self):
-        return self._fd.closed if hasattr(self, "_fd") else None
-
-    def __tick__(self, wait=TIMEOUT):
-        if not self.closed:
-            try:
-                r, w, e = select.select(self._read, self._write, [], wait)
-            except select.error as error:
-                if not error[0] == errno.EINTR:
-                    self.push(Error(error), "error")
-                return
-
-            if w and self._buffer:
-                data = self._buffer.popleft()
-                try:
-                    if isinstance(data, str):
-                        data = data.encode(self.encoding)
-                    bytes = os.write(self._fd.fileno(), data)
-                    if bytes < len(data):
-                        self._buffer.append(data[bytes:])
-                    elif not self._buffer:
-                        self._write.remove(self._fd)
-                except OSError as error:
-                    self.push(Error(error), "error")
-
-            if r:
-                try:
-                    data = os.read(self._fd.fileno(), self.bufsize)
-                except IOError as e:
-                    if e[0] == errno.EBADF:
-                        data = None
-
-                if data:
-                    self.push(Read(data), "read")
-                elif self.autoclose:
-                    self.push(EOF())
-                    self.close()
-
-    def write(self, data):
-        if self._fd not in self._write:
-            self._write.append(self._fd)
-        self._buffer.append(data)
-
-    def close(self):
-        self._fd.close()
-        self._read = []
-        self._write = []
-        self.push(Closed(self.filename), "closed")
-
-    def seek(self, offset, whence=0):
-        self._fd.seek(offset, whence)
-
-
-class Serial(Component):
-
-    channel = "serial"
-
-    def __init__(self, port, baudrate=115200, bufsize=BUFSIZE,
-            timeout=TIMEOUT, channel=channel):
-        super(Serial, self).__init__(channel=channel)
-
-        if serial is None:
-            raise RuntimeError("No serial support available")
-
-        self.port = port
-        self.baudrate = baudrate
-
-        self._serial = serial.Serial()
-        self._serial.port = port
-        self._serial.baudrate = baudrate
-
-        if os.name == "posix":
-            self._serial.timeout = 0  # non-blocking (POSIX)
-        else:
-            self._serial.timeout = timeout
-
-        self._buffer = deque()
-        self._bufsize = bufsize
-        self._timeout = timeout
-
-        self._read = []
-        self._write = []
-
-        self._serial.open()
-        self._fd = self._serial.fileno()
-
-        self._read.append(self._fd)
-
-        self.push(Opened(self.port))
-
-    def __tick__(self):
-        r, w, e = select.select(self._read, self._write, [], self._timeout)
-
-        if w and self._buffer:
-            data = self._buffer.popleft()
-            try:
-                bytes = os.write(self._fd, data)
-                if bytes < len(data):
-                    self._buffer.append(data[bytes:])
-                else:
-                    if not self._buffer and self._fd in self._write:
-                        self._write.remove(self._fd)
-            except OSError as error:
-                self.push(Error(error))
-
-        if r:
-            data = os.read(self._fd, self._bufsize)
-            if data:
-                self.push(Read(data))
-
-    def write(self, data):
-        if self._fd not in self._write:
-            self._write.append(self._fd)
-        self._buffer.append(data)
-
-    def close(self):
-        self._fd = None
-        self._read = []
-        self._write = []
-        self._serial.close()
-        self.push(Closed(self.port))
+try:
+    from .notify import Notify
+except:
+    pass
 
 try:
     stdin = File(fd=sys.stdin, mode="r", channel="stdin")

circuits/io/events.py

+# Module:   events
+# Date:     10th June 2011
+# Author:   James Mills <prologic@shortcircuit.net.au>
+
+"""I/O Events
+
+This module implements commonly used I/O events used by other I/O modules.
+"""
+
+from circuits.core import Event
+
+
+class EOF(Event):
+    """EOF Event"""
+
+
+class Seek(Event):
+    """Seek Event"""
+
+
+class Read(Event):
+    """Read Event"""
+
+
+class Close(Event):
+    """Close Event"""
+
+
+class Write(Event):
+    """Write Event"""
+
+
+class Error(Event):
+    """Error Event"""
+
+
+class Opened(Event):
+    """Opened Event"""
+
+
+class Closed(Event):
+    """Closed Event"""

circuits/io/file.py

+# Module:   file
+# Date:     4th August 2004
+# Author:   James Mills <prologic@shortcircuit.net.au>
+
+"""File I/O
+
+This module implements a wrapper for basic File I/O.
+"""
+
+import os
+import errno
+import select
+from collections import deque
+
+from circuits.core import Component
+from circuits.tools import tryimport
+
+from .events import Closed, EOF, Error, Opened, Read
+
+fcntl = tryimport("fcntl")
+
+TIMEOUT = 0.2
+BUFSIZE = 4096
+
+
+class File(Component):
+
+    channel = "file"
+
+    def __init__(self, filename=None, mode="r", fd=None, autoclose=True,
+            bufsize=BUFSIZE, encoding="utf-8", channel=channel):
+        super(File, self).__init__(channel=channel)
+
+        self.bufsize = bufsize
+        self.encoding = encoding
+        self.autoclose = autoclose
+
+        if filename is not None:
+            self.mode = mode
+            self.filename = filename
+            self._fd = open(filename, mode)
+        else:
+            self._fd = fd
+            self.mode = fd.mode
+            self.filename = fd.name
+
+        if fcntl is not None:
+            # Set non-blocking file descriptor (non-portable)
+            flag = fcntl.fcntl(self._fd, fcntl.F_GETFL)
+            flag = flag | os.O_NONBLOCK
+            fcntl.fcntl(self._fd, fcntl.F_SETFL, flag)
+
+        self._read = []
+        self._write = []
+        self._buffer = deque()
+
+        if any([m for m in "r+" if m in self._fd.mode]):
+            self._read.append(self._fd)
+
+        self.fire(Opened(self.filename), "opened")
+
+    @property
+    def closed(self):
+        return self._fd.closed if hasattr(self, "_fd") else None
+
+    def __tick__(self, wait=TIMEOUT):
+        if not self.closed:
+            try:
+                r, w, e = select.select(self._read, self._write, [], wait)
+            except select.error as error:
+                if not error[0] == errno.EINTR:
+                    self.fire(Error(error), "error")
+                return
+
+            if w and self._buffer:
+                data = self._buffer.popleft()
+                try:
+                    if isinstance(data, str):
+                        data = data.encode(self.encoding)
+                    bytes = os.write(self._fd.fileno(), data)
+                    if bytes < len(data):
+                        self._buffer.append(data[bytes:])
+                    elif not self._buffer:
+                        self._write.remove(self._fd)
+                except OSError as error:
+                    self.fire(Error(error), "error")
+
+            if r:
+                try:
+                    data = os.read(self._fd.fileno(), self.bufsize)
+                except IOError as e:
+                    if e[0] == errno.EBADF:
+                        data = None
+
+                if data:
+                    self.fire(Read(data), "read")
+                elif self.autoclose:
+                    self.fire(EOF())
+                    self.close()
+
+    def write(self, data):
+        if self._fd not in self._write:
+            self._write.append(self._fd)
+        self._buffer.append(data)
+
+    def close(self):
+        self._fd.close()
+        self._read = []
+        self._write = []
+        self.fire(Closed(self.filename), "closed")
+
+    def seek(self, offset, whence=0):
+        self._fd.seek(offset, whence)

circuits/io/notify.py

 except ImportError:
     raise Exception("No pyinotify support available. Is pyinotify installed?")
 
-from circuits.core import Event, BaseComponent
+from circuits.core import Event, Component
 
 MASK = ALL_EVENTS
 
+class AddPath(Event):
+    """Add path to watch"""
+    channel = 'add_path'
+
+class RemovePath(Event):
+    """Remove path from watch"""
+    channel = 'remove_path'
 
 class Moved(Event):
     """Moved Event"""
 }
 
 
-class Notify(BaseComponent):
+class Notify(Component):
 
     channel = "notify"
 
             if mask & k:
                 e = v(name, path, pathname, dir)
                 c = e.name.lower()
-                self.push(e, c)
+                self.fire(e, c)
 
-    def add(self, path, mask=None, recursive=False):
+    def add_path(self, path, mask=None, recursive=False):
         mask = mask or MASK
         self._wm.add_watch(path, mask, rec=recursive)
 
-    def remove(self, path, recursive=False):
+    def remove_path(self, path, recursive=False):
         wd = self._wm.get_wd(path)
         if wd:
             self._wm.rm_watch(wd, rec=recursive)

circuits/io/serial.py

+# Module:   serial
+# Date:     4th August 2004
+# Author:   James Mills <prologic@shortcircuit.net.au>
+
+"""Serial I/O
+
+This module implements basic Serial (RS232) I/O.
+"""
+
+import os
+import select
+from collections import deque
+
+from circuits.core import Component
+from circuits.tools import tryimport
+
+from .events import Closed, Error, Opened, Read
+
+serial = tryimport("serial")
+
+TIMEOUT = 0.2
+BUFSIZE = 4096
+
+
+class Serial(Component):
+
+    channel = "serial"
+
+    def __init__(self, port, baudrate=115200, bufsize=BUFSIZE,
+            timeout=TIMEOUT, channel=channel):
+        super(Serial, self).__init__(channel=channel)
+
+        if serial is None:
+            raise RuntimeError("No serial support available")
+
+        self.port = port
+        self.baudrate = baudrate
+
+        self._serial = serial.Serial()
+        self._serial.port = port
+        self._serial.baudrate = baudrate
+
+        if os.name == "posix":
+            self._serial.timeout = 0  # non-blocking (POSIX)
+        else:
+            self._serial.timeout = timeout
+
+        self._buffer = deque()
+        self._bufsize = bufsize
+        self._timeout = timeout
+
+        self._read = []
+        self._write = []
+
+        self._serial.open()
+        self._fd = self._serial.fileno()
+
+        self._read.append(self._fd)
+
+        self.fire(Opened(self.port))
+
+    def __tick__(self):
+        r, w, e = select.select(self._read, self._write, [], self._timeout)
+
+        if w and self._buffer:
+            data = self._buffer.popleft()
+            try:
+                bytes = os.write(self._fd, data)
+                if bytes < len(data):
+                    self._buffer.append(data[bytes:])
+                else:
+                    if not self._buffer and self._fd in self._write:
+                        self._write.remove(self._fd)
+            except OSError as error:
+                self.fire(Error(error))
+
+        if r:
+            data = os.read(self._fd, self._bufsize)
+            if data:
+                self.fire(Read(data))
+
+    def write(self, data):
+        if self._fd not in self._write:
+            self._write.append(self._fd)
+        self._buffer.append(data)
+
+    def close(self):
+        self._fd = None
+        self._read = []
+        self._write = []
+        self._serial.close()
+        self.fire(Closed(self.port))

circuits/net/protocols/http.py

             cLen = int(self._response.headers.get("Content-Length", "0"))
             if cLen and self._response._body.tell() == cLen:
                 self._response._body.seek(0)
-                self.push(Response(self._response))
+                self.fire(Response(self._response))
                 self._response = None
         else:
             statusline, data = data.split(b"\r\n", 1)
                 return
 
             response._body.seek(0)
-            self.push(Response(response))
+            self.fire(Response(response))

circuits/net/protocols/irc.py

     ###
 
     def RAW(self, data):
-        self.push(Write("%s\r\n" % data))
+        self.fire(Write("%s\r\n" % data))
 
     def PASS(self, password):
-        self.push(RAW("PASS %s" % password))
+        self.fire(RAW("PASS %s" % password))
 
     def USER(self, ident, host, server, name):
-        self.push(RAW("USER %s \"%s\" \"%s\" :%s" % (
+        self.fire(RAW("USER %s \"%s\" \"%s\" :%s" % (
             ident, host, server, name)))
 
     def NICK(self, nick):
-        self.push(RAW("NICK %s" % nick))
+        self.fire(RAW("NICK %s" % nick))
 
     def PING(self, server):
-        self.push(RAW("PING :%s" % server))
+        self.fire(RAW("PING :%s" % server))
 
     def PONG(self, server):
-        self.push(RAW("PONG :%s" % server))
+        self.fire(RAW("PONG :%s" % server))
 
     def QUIT(self, message="Leaving"):
-        self.push(RAW("QUIT :%s" % message))
+        self.fire(RAW("QUIT :%s" % message))
 
     def JOIN(self, channel, key=None):
         if key is None:
-            self.push(RAW("JOIN %s" % channel))
+            self.fire(RAW("JOIN %s" % channel))
         else:
-            self.push(RAW("JOIN %s %s" % (channel, key)))
+            self.fire(RAW("JOIN %s %s" % (channel, key)))
 
     def PART(self, channel, message="Leaving"):
-        self.push(RAW("PART %s :%s" % (channel, message)))
+        self.fire(RAW("PART %s :%s" % (channel, message)))
 
     def PRIVMSG(self, target, message):
-        self.push(RAW("PRIVMSG %s :%s" % (target, message)))
+        self.fire(RAW("PRIVMSG %s :%s" % (target, message)))
 
     def NOTICE(self, target, message):
-        self.push(RAW("NOTICE %s :%s" % (target, message)))
+        self.fire(RAW("NOTICE %s :%s" % (target, message)))
 
     def CTCP(self, target, type, message):
-        self.push(PRIVMSG(target, "%s %s" % (type, message)))
+        self.fire(PRIVMSG(target, "%s %s" % (type, message)))
 
     def CTCPREPLY(self, target, type, message):
-        self.push(NOTICE(target, "%s %s" % (type, message)))
+        self.fire(NOTICE(target, "%s %s" % (type, message)))
 
     def KICK(self, channel, target, message=""):
-        self.push(RAW("KICK %s %s :%s" % (channel, target, message)))
+        self.fire(RAW("KICK %s %s :%s" % (channel, target, message)))
 
     def TOPIC(self, channel, topic):
-        self.push(RAW("TOPIC %s :%s" % (channel, topic)))
+        self.fire(RAW("TOPIC %s :%s" % (channel, topic)))
 
     def MODE(self, modes, channel=None):
         if channel is None:
-            self.push(RAW("MODE :%s" % modes))
+            self.fire(RAW("MODE :%s" % modes))
         else:
-            self.push(RAW("MODE %s :%s" % (channel, modes)))
+            self.fire(RAW("MODE %s :%s" % (channel, modes)))
 
     def INVITE(self, target, channel):
-        self.push(RAW("INVITE %s %s" % (target, channel)))
+        self.fire(RAW("INVITE %s %s" % (target, channel)))
 
     def NAMES(self, channel=None):
         if channel:
-            self.push(RAW("NAMES %s" % channel))
+            self.fire(RAW("NAMES %s" % channel))
         else:
-            self.push(RAW("NAMES"))
+            self.fire(RAW("NAMES"))
 
     ###
     ### Event Processing
         tokens = line.split(" ")
 
         if tokens[0] == "PING":
-            self.push(Ping(strip(tokens[1])))
+            self.fire(Ping(strip(tokens[1])))
 
         elif re.match("[0-9]+", tokens[1]):
             source = strip(tokens[0])
                 arg = tokens[3]
                 message = strip(" ".join(tokens[4:]))
 
-            self.push(Numeric(source, target, numeric, arg, message))
+            self.fire(Numeric(source, target, numeric, arg, message))
 
         elif tokens[1] == "PRIVMSG":
             source = sourceSplit(strip(tokens[0]))
                 tokens = strip(message, color=True).split(" ")
                 type = tokens[0]
                 message = " ".join(tokens[1:])
-                self.push(Ctcp(source, target, type, message))
+                self.fire(Ctcp(source, target, type, message))
             else:
-                self.push(Message(source, target, message))
+                self.fire(Message(source, target, message))
 
         elif tokens[1] == "NOTICE":
             source = sourceSplit(strip(tokens[0]))
             target = tokens[2]
             message = strip(" ".join(tokens[3:]))
-            self.push(Notice(source, target, message))
+            self.fire(Notice(source, target, message))
 
         elif tokens[1] == "JOIN":
             source = sourceSplit(strip(tokens[0]))
             channel = strip(tokens[2])
-            self.push(Join(source, channel))
+            self.fire(Join(source, channel))
 
         elif tokens[1] == "PART":
             source = sourceSplit(strip(tokens[0]))
             channel = strip(tokens[2])
             message = strip(" ".join(tokens[3:]))
-            self.push(Part(source, channel, message))
+            self.fire(Part(source, channel, message))
 
         elif tokens[1] == "QUIT":
             source = sourceSplit(strip(tokens[0]))
             message = strip(" ".join(tokens[2:]))
-            self.push(Quit(source, message))
+            self.fire(Quit(source, message))
 
         elif tokens[1] == "NICK":
             source = sourceSplit(strip(tokens[0]))
             newNick = strip(tokens[2])
 
-            self.push(Nick(source, newNick))
+            self.fire(Nick(source, newNick))
 
         elif tokens[1] == "MODE":
             source = sourceSplit(strip(tokens[0]))
             target = tokens[2]
             modes = strip(" ".join(tokens[3:]))
-            self.push(Mode(source, target, modes))
+            self.fire(Mode(source, target, modes))
 
     ###
     ### Default Events
         or sending your own Pong reponse.
         """
 
-        self.push(PONG(server))
+        self.fire(PONG(server))
 
 ###
 ### Errors and Numeric Replies

circuits/net/protocols/line.py

             data, = args
             lines, self.buffer = self.splitter(data, self.buffer)
             for line in lines:
-                self.push(Line(line.decode(self.encoding, "replace")))
+                self.fire(Line(line.decode(self.encoding, "replace")))
         else:
             # Server read
             sock, data = args
             lines, buffer = self.splitter(data, self.getBuffer(sock))
             self.updateBuffer(sock, buffer)
             for line in lines:
-                self.push(Line(sock,
+                self.fire(Line(sock,
                     line.decode(self.encoding, "replace")))

circuits/net/sockets.py

 from socket import gaierror, error as SocketError
 from socket import gethostname, gethostbyname, socket
 
+from socket import AF_INET, IPPROTO_TCP, SOCK_STREAM, SOCK_DGRAM
 from socket import SOL_SOCKET, SO_BROADCAST, SO_REUSEADDR, TCP_NODELAY
-from socket import AF_INET, IPPROTO_TCP, SOCK_STREAM, SOCK_DGRAM
-try:
-    from socket import AF_UNIX
-except:
-    # not running on Unix
-    pass
 
 try:
     from ssl import wrap_socket as ssl_socket
         if self._poller is None:
             if isinstance(component, BasePoller):
                 self._poller = component
-                self.push(Ready(self), "ready", self.channel)
+                self.fire(Ready(self), "ready", self.channel)
             else:
                 component = findcmp(self.root, BasePoller, subclass=False)
                 if component is not None:
                     self._poller = component
-                    self.push(Ready(self), "ready", self.channel)
+                    self.fire(Ready(self), "ready", self.channel)
                 else:
                     self._poller = Poller().register(self)
-                    self.push(Ready(self), "ready", self.channel)
+                    self.fire(Ready(self), "ready", self.channel)
 
     @handler("started", filter=True, target="*")
     def _on_started(self, component, mode):
         if self._poller is None:
             self._poller = Poller().register(self)
-            self.push(Ready(self), "ready", self.channel)
+            self.fire(Ready(self), "ready", self.channel)
             return True
 
     @handler("stopped", target="*")
     def _on_stopped(self, component):
-        self.push(Close(), "close", self.channel)
+        self.fire(Close(), "close", self.channel)
 
     def _close(self):
         if not self._connected:
         except SocketError:
             pass
 
-        self.push(Disconnected(), "disconnected", self.channel)
+        self.fire(Disconnected(), "disconnected", self.channel)
 
     def close(self):
         if not self._buffer:
                 data = self._sock.recv(self._bufsize)
 
             if data:
-                self.push(Read(data), "read", self.channel)
+                self.fire(Read(data), "read", self.channel)
             else:
                 self.close()
         except SocketError as e:
             if e.args[0] == EWOULDBLOCK:
                 return
             else:
-                self.push(Error(e), "error", self.channel)
+                self.fire(Error(e), "error", self.channel)
                 self._close()
 
     def _write(self, data):
             if e.args[0] in (EPIPE, ENOTCONN):
                 self._close()
             else:
-                self.push(Error(e), "error", self.channel)
+                self.fire(Error(e), "error", self.channel)
 
     def write(self, data):
         if not self._poller.isWriting(self._sock):
             if r in (EISCONN, EWOULDBLOCK, EINPROGRESS, EALREADY):
                 self._connected = True
             else:
-                self.push(Error(r), "error", self.channel)
+                self.fire(Error(r), "error", self.channel)
                 return
 
         self._connected = True
         if self.secure:
             self._ssock = ssl_socket(self._sock, self.keyfile, self.certfile)
 
-        self.push(Connected(host, port), "connected", self.channel)
+        self.fire(Connected(host, port), "connected", self.channel)
 
 
 class UNIXClient(Client):
 
     def _create_socket(self):
+        from socket import AF_UNIX
+
         sock = socket(AF_UNIX, SOCK_STREAM)
         if self._bind is not None:
             sock.bind(self._bind)
             if r in (EISCONN, EWOULDBLOCK, EINPROGRESS, EALREADY):
                 self._connected = True
             else:
-                self.push(Error(r), "error", self.channel)
+                self.fire(Error(r), "error", self.channel)
                 return
 
         self._connected = True
         if self.secure:
             self._ssock = ssl_socket(self._sock, self.keyfile, self.certfile)
 
-        self.push(Connected(gethostname(), path), "connected", self.channel)
+        self.fire(Connected(gethostname(), path), "connected", self.channel)
 
 
 class Server(Component):
             if isinstance(component, BasePoller):
                 self._poller = component
                 self._poller.addReader(self, self._sock)
-                self.push(Ready(self), "ready", self.channel)
+                self.fire(Ready(self), "ready", self.channel)
             else:
                 component = findcmp(self.root, BasePoller, subclass=False)
                 if component is not None:
                     self._poller = component
                     self._poller.addReader(self, self._sock)
-                    self.push(Ready(self), "ready", self.channel)
+                    self.fire(Ready(self), "ready", self.channel)
                 else:
                     self._poller = Poller().register(self)
                     self._poller.addReader(self, self._sock)
-                    self.push(Ready(self), "ready", self.channel)
+                    self.fire(Ready(self), "ready", self.channel)
 
     @handler("started", filter=True, target="*")
     def _on_started(self, component, mode):
         if self._poller is None:
             self._poller = Poller().register(self)
             self._poller.addReader(self, self._sock)
-            self.push(Ready(self), "ready", self.channel)
+            self.fire(Ready(self), "ready", self.channel)
             return True
 
     @handler("stopped", target="*")
     def _on_stopped(self, component):
-        self.push(Close(), "close", self.channel)
+        self.fire(Close(), "close", self.channel)
 
     def _close(self, sock):
         if sock is None:
         except SocketError:
             pass
 
-        self.push(Disconnect(sock), "disconnect", self.channel)
+        self.fire(Disconnect(sock), "disconnect", self.channel)
 
     def close(self, sock=None):
         closed = sock is None
                 self._closeq.append(sock)
 
         if closed:
-            self.push(Closed(), "closed", self.channel)
+            self.fire(Closed(), "closed", self.channel)
 
     def _read(self, sock):
         if sock not in self._clients:
         try:
             data = sock.recv(self._bufsize)
             if data:
-                self.push(Read(sock, data), "read", self.channel)
+                self.fire(Read(sock, data), "read", self.channel)
             else:
                 self.close(sock)
         except SocketError as e:
             if e.args[0] == EWOULDBLOCK:
                 return
             else:
-                self.push(Error(sock, e), "error", self.channel)
+                self.fire(Error(sock, e), "error", self.channel)
                 self._close(sock)
 
     def _write(self, sock, data):
                 self._buffers[sock].appendleft(data[nbytes:])
         except SocketError as e:
             if e.args[0] not in (EINTR, EWOULDBLOCK, ENOBUFS):
-                self.push(Error(sock, e), "error", self.channel)
+                self.fire(Error(sock, e), "error", self.channel)
                 self._close(sock)
             else:
                 self._buffers[sock].appendleft(data)
         newsock.setblocking(False)
         self._poller.addReader(self, newsock)
         self._clients.append(newsock)
-        self.push(Connect(newsock, *host), "connect", self.channel)
+        self.fire(Connect(newsock, *host), "connect", self.channel)
 
     @handler("_disconnect", filter=True)
     def _on_disconnect(self, sock):
 class UNIXServer(Server):
 
     def _create_socket(self):
+        from socket import AF_UNIX
+
         if os.path.exists(self._bind):
             os.unlink(self._bind)
 
 
         try:
             sock.shutdown(2)
+        except SocketError:
+            pass
+        try:
             sock.close()
         except SocketError:
             pass
 
-        self.push(Disconnect(sock), "disconnect", self.channel)
+        self.fire(Disconnect(sock), "disconnect", self.channel)
 
     @handler("close", override=True)
     def close(self):
-        self.push(Closed(), "closed", self.channel)
+        self.fire(Closed(), "closed", self.channel)
 
         if self._buffers[self._sock] and self._sock not in self._closeq:
             self._closeq.append(self._sock)
         try:
             data, address = self._sock.recvfrom(self._bufsize)
             if data:
-                self.push(Read(address, data), "read", self.channel)
+                self.fire(Read(address, data), "read", self.channel)
         except SocketError as e:
             if e.args[0] in (EWOULDBLOCK, EAGAIN):
                 return
-            self.push(Error(self._sock, e), "error", self.channel)
-            print "Closing socket... (1)"
+            self.fire(Error(self._sock, e), "error", self.channel)
             self._close(self._sock)
 
     def _write(self, address, data):
                 self._buffers[self._sock].appendleft(data[bytes:])
         except SocketError as e:
             if e.args[0] in (EPIPE, ENOTCONN):
-                print "Closing socket... (2)"
                 self._close(self._sock)
             else:
-                self.push(Error(self._sock, e), "error", self.channel)
+                self.fire(Error(self._sock, e), "error", self.channel)
 
     @handler("write", override=True)
     def write(self, address, data):
 
     @handler("_disconnect", filter=True, override=True)
     def _on_disconnect(self, sock):
-        print "Closing socket... (3)"
         self._close(sock)
 
     @handler("_read", filter=True, override=True)

circuits/tools/__init__.py

 from hashlib import md5
 from warnings import warn
 
+from circuits.core.events import Unregister
 
 def tryimport(modules, message=None):
     if isinstance(modules, str):

circuits/web/client.py

     @handler("write")
     def write(self, data):
         if self._transport.connected:
-            self.push(Write(data), target=self._transport)
+            self.fire(Write(data), target=self._transport)
 
     @handler("close")
     def close(self):
         if self._transport.connected:
-            self.push(Close(), target=self._transport)
+            self.fire(Close(), target=self._transport)
 
     @handler("connect")
     def connect(self):
         if not self._transport.connected:
-            self.push(Connect(self._host, self._port, self._secure),
+            self.fire(Connect(self._host, self._port, self._secure),
                     target=self._transport)
 
     @handler("request")
             headers = Headers([(k, v) for k, v in headers.items()])
             command = "%s %s HTTP/1.1" % (method, path)
             message = "%s\r\n%s" % (command, headers)
-            self.push(Write(message.encode('utf-8')), target=self._transport)
+            self.fire(Write(message.encode('utf-8')), target=self._transport)
             if body:
-                self.push(Write(body), target=self._transport)
+                self.fire(Write(body), target=self._transport)
         else:
             raise NotConnected()
 
     def _on_response(self, response):
         self._response = response
         if response.headers.get("Connection") == "Close":
-            self.push(Close(), target=self._transport)
+            self.fire(Close(), target=self._transport)
 
     @property
     def connected(self):

circuits/web/dispatchers/dispatcher.py

             if vpath:
                 req.args += tuple(vpath)
 
-            return self.push(req, channel, target)
+            return self.fire(req, channel, target)

circuits/web/dispatchers/jsonrpc.py

         id = value.id
         response = value.response
         response.body = self._response(id, value.value)
-        self.push(Response(response), target=self.channel)
+        self.fire(Response(response), target=self.channel)
         value.handled = True
 
     @handler("request", filter=True, priority=0.1)
                 t, c = self.target, method
 
             if type(params) is dict:
-                value = self.push(RPC(**params), c, t)
+                value = self.fire(RPC(**params), c, t)
             else:
-                value = self.push(RPC(*params), c, t)
+                value = self.fire(RPC(*params), c, t)
 
             value.id = id
             value.response = response

circuits/web/dispatchers/routes.py

             if vpath:
                 req.args += tuple(vpath)
 
-            return self.push(req, channel, target=target)
+            return self.fire(req, channel, target=target)
 
     @handler("registered", target="*")
     def _on_registered(self, event, component, manager):

circuits/web/dispatchers/websockets.py

     @handler("write", target=WebSocketEvent._target)
     def _on_write(self, sock, data):
         payload = b'\x00' + data.encode("utf-8") + b'\xff'
-        self.push(Write(sock, payload))
+        self.fire(Write(sock, payload))
 
     @handler("value_changed", target=WebSocketEvent._target)
     def _on_value_changed(self, value):
         sock, message = value.event.args
         result, data = value.result, value.value
         if result and isinstance(data, basestring):
-                self.push(Write(sock, data),
+                self.fire(Write(sock, data),
                         target=WebSocketEvent._target)
 
     @handler("read", filter=True)
             self._buffers[sock] += data
             messages = self._parse_messages(sock)
             for message in messages:
-                value = self.push(Message(sock, message))
+                value = self.fire(Message(sock, message))
                 value.onSet = "value_changed", WebSocketEvent._target
             return True
 

circuits/web/dispatchers/xmlrpc.py

     def _on_value_changed(self, value):
         response = value.response
         response.body = self._response(value.value)
-        self.push(Response(response), target=self.channel)
+        self.fire(Response(response), target=self.channel)
         value.handled = True
 
     @handler("request", filter=True, priority=0.1)
             else:
                 t, c = self.target, method
 
-            value = self.push(RPC(*params), c, t)
+            value = self.fire(RPC(*params), c, t)
             value.response = response
             value.onSet = ("value_changed", self)
         except Exception as e:

circuits/web/events.py

 
     _target = "web"
 
+    def __init__(self, *args, **kwargs):
+        if 'target' in kwargs:
+            self._target = kwargs.pop('target')
+        super(WebEvent, self).__init__(*args, **kwargs)
+
+
 class Request(WebEvent):
     """Request(WebEvent) -> Request WebEvent
 
     args: request, response
     """
-
-    success = "request_success", WebEvent._target
-    failure = "request_failure", WebEvent._target
-    filter = "request_filtered", WebEvent._target
-    start = "request_started", WebEvent._target
-    end = "request_completed", WebEvent._target
+    def __init__(self, *args, **kwargs):
+        super(Request, self).__init__(*args, **kwargs)
+        self.success = "request_success", self._target
+        self.failure = "request_failure", self._target
+        self.filter = "request_filtered", self._target
+        self.start = "request_started", self._target
+        self.end = "request_completed", self._target
 
 class Response(WebEvent):
     """Response(WebEvent) -> Response WebEvent
 
     args: request, response
     """
-
-    success = "response_success", WebEvent._target
-    failure = "response_failure", WebEvent._target
-    filter = "response_filtered", WebEvent._target
-    start = "response_started", WebEvent._target
-    end = "response_completed", WebEvent._target
+    def __init__(self, *args, **kwargs):
+        super(Response, self).__init__(*args, **kwargs)
+        self.success = "response_success", self._target
+        self.failure = "response_failure", self._target
+        self.filter = "response_filtered", self._target
+        self.start = "response_started", self._target
+        self.end = "response_completed", self._target
 
 class Stream(WebEvent):
     """Stream(WebEvent) -> Stream WebEvent
 
     args: request, response
     """
-
-    success = "stream_success", WebEvent._target
-    failure = "stream_failure", WebEvent._target
-    filter = "stream_filtered", WebEvent._target
-    start = "stream_started", WebEvent._target
-    end = "stream_completed", WebEvent._target
+    def __init__(self, *args, **kwargs):
+        super(Stream, self).__init__(*args, **kwargs)
+        self.success = "stream_success", self._target
+        self.failure = "stream_failure", self._target
+        self.filter = "stream_filtered", self._target
+        self.start = "stream_started", self._target
+        self.end = "stream_completed", self._target

circuits/web/http.py

                 if response.chunked:
                     buf = [hex(len(data))[2:], b"\r\n", data, b"\r\n"]
                     data = b"".join(buf)
-                self.push(Write(response.request.sock, data))
+                self.fire(Write(response.request.sock, data))
             if response.body and not response.done:
                 try:
                     data = next(response.body)
                 except StopIteration:
                     data = None
-                self.push(Stream(response, data))
+                self.fire(Stream(response, data))
         else:
             if response.body:
                 response.body.close()
             if response.chunked:
-                self.push(Write(response.request.sock, b"0\r\n\r\n"))
+                self.fire(Write(response.request.sock, b"0\r\n\r\n"))
             if response.close:
-                self.push(Close(response.request.sock))
+                self.fire(Close(response.request.sock))
             response.done = True
 
     @handler("response")
     def _on_response(self, response):
-        self.push(
+        self.fire(
                 Write(response.request.sock,
                     str(response).encode(HTTP_ENCODING)))
 
                 data = next(response.body)
             except StopIteration:
                 data = None
-            self.push(Stream(response, data))
+            self.fire(Stream(response, data))
         else:
             if isinstance(response.body, bytes):
                 body = response.body
                 if response.chunked:
                     buf = [hex(len(body))[2:].encode(), b"\r\n", body, b"\r\n"]
                     body = b"".join(buf)
-                self.push(Write(response.request.sock, body))
+                self.fire(Write(response.request.sock, body))
 
                 if response.chunked:
-                    self.push(Write(response.request.sock, b"0\r\n\r\n"))
+                    self.fire(Write(response.request.sock, b"0\r\n\r\n"))
 
             if not response.stream:
                 if response.close:
-                    self.push(Close(response.request.sock))
+                    self.fire(Close(response.request.sock))
                 response.done = True
 
     @handler("disconnect")