Skip to content
Browse files

tidy up for packaging

  • Loading branch information...
1 parent 88f4997 commit 7b9849d8fca0eafb207fa713609b4116bd18823f Jonthan Moss committed
View
85 amity/.ropeproject/config.py
@@ -0,0 +1,85 @@
+# The default ``config.py``
+
+
+def set_prefs(prefs):
+ """This function is called before opening the project"""
+
+ # Specify which files and folders to ignore in the project.
+ # Changes to ignored resources are not added to the history and
+ # VCSs. Also they are not returned in `Project.get_files()`.
+ # Note that ``?`` and ``*`` match all characters but slashes.
+ # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
+ # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
+ # '.svn': matches 'pkg/.svn' and all of its children
+ # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
+ # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
+ prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject',
+ '.hg', '.svn', '_svn', '.git']
+
+ # Specifies which files should be considered python files. It is
+ # useful when you have scripts inside your project. Only files
+ # ending with ``.py`` are considered to be python files by
+ # default.
+ #prefs['python_files'] = ['*.py']
+
+ # Custom source folders: By default rope searches the project
+ # for finding source folders (folders that should be searched
+ # for finding modules). You can add paths to that list. Note
+ # that rope guesses project source folders correctly most of the
+ # time; use this if you have any problems.
+ # The folders should be relative to project root and use '/' for
+ # separating folders regardless of the platform rope is running on.
+ # 'src/my_source_folder' for instance.
+ #prefs.add('source_folders', 'src')
+
+ # You can extend python path for looking up modules
+ #prefs.add('python_path', '~/python/')
+
+ # Should rope save object information or not.
+ prefs['save_objectdb'] = True
+ prefs['compress_objectdb'] = False
+
+ # If `True`, rope analyzes each module when it is being saved.
+ prefs['automatic_soa'] = True
+ # The depth of calls to follow in static object analysis
+ prefs['soa_followed_calls'] = 0
+
+ # If `False` when running modules or unit tests "dynamic object
+ # analysis" is turned off. This makes them much faster.
+ prefs['perform_doa'] = True
+
+ # Rope can check the validity of its object DB when running.
+ prefs['validate_objectdb'] = True
+
+ # How many undos to hold?
+ prefs['max_history_items'] = 32
+
+ # Shows whether to save history across sessions.
+ prefs['save_history'] = True
+ prefs['compress_history'] = False
+
+ # Set the number spaces used for indenting. According to
+ # :PEP:`8`, it is best to use 4 spaces. Since most of rope's
+ # unit-tests use 4 spaces it is more reliable, too.
+ prefs['indent_size'] = 4
+
+ # Builtin and c-extension modules that are allowed to be imported
+ # and inspected by rope.
+ prefs['extension_modules'] = []
+
+ # Add all standard c-extensions to extension_modules list.
+ prefs['import_dynload_stdmods'] = True
+
+ # If `True` modules with syntax errors are considered to be empty.
+ # The default value is `False`; When `False` syntax errors raise
+ # `rope.base.exceptions.ModuleSyntaxError` exception.
+ prefs['ignore_syntax_errors'] = False
+
+ # If `True`, rope ignores unresolvable imports. Otherwise, they
+ # appear in the importing namespace.
+ prefs['ignore_bad_imports'] = False
+
+
+def project_opened(project):
+ """This function is called after opening the project"""
+ # Do whatever you like here!
View
0 amity/__init__.py
No changes.
View
101 amity/entity.py
@@ -0,0 +1,101 @@
+import socket
+
+#Get my IP for events I am sending
+SOCK = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+SOCK.connect(('google.com', 0))
+IP = SOCK.getsockname()[0]
+
+
+class Event(object):
+ """
+ Base event object
+ """
+ _data = {
+ 'values': {},
+ 'headers': {}
+ }
+
+ def __init__(self, values=None, headers=None):
+ """
+ Arguments:
+
+ values
+ A dictionary of values
+
+ headers
+ A dictionary of headers
+ """
+ self.set_header('from', IP)
+ if headers:
+ for header, value in headers.items():
+ self.set_header(header, value)
+ if values:
+ for key, value in values.items():
+ self.set_value(key, value)
+
+ def set_header(self, header, value):
+ """
+ Sets a header within the event
+
+ Arguments:
+
+ header
+ The name of the header to set
+
+ value
+ The value of the header
+ """
+ self._data['headers'][header] = value
+ return self
+
+ def get_header(self, header):
+ """
+ Gets the value of the specified header
+ """
+ value = None
+ if header in self._data['headers']:
+ value = self._data['headers'][header]
+ return value
+
+ def get_headers(self):
+ """
+ Returns a dictionary of all headers
+ """
+ return self._data['headers']
+
+ def set_value(self, key, value):
+ """
+ Sets a value within the event
+
+ Arguments:
+
+ key
+ The name of the key to set
+
+ value
+ The value of the key
+ """
+ self._data['values'][key] = value
+ return self
+
+ def get_value(self, key):
+ """
+ Gets the value of the specified key
+ """
+ value = None
+ if key in self._data['values']:
+ value = self._data['values'][key]
+ return value
+
+ def get_values(self):
+ """
+ Returns a dictionary of all values
+ """
+ return self._data['values']
+
+ @property
+ def data(self):
+ """
+ Returns the entire data dictionary for this event
+ """
+ return self._data
View
240 amity/messaging.py
@@ -0,0 +1,240 @@
+from amity.entity import Event
+from kombu.connection import BrokerConnection
+from kombu.messaging import Exchange, Queue, Producer, Consumer
+from os import getpid
+from socket import timeout as socket_timeout
+import uuid
+
+
+class Emitter(object):
+ """
+ Base class for all amity Event Emitters
+ """
+ connection = None
+ exchange = None
+ producer = None
+
+ def __init__(self, connection):
+ """
+ Arguments:
+
+ connection
+ The instance of kombu.connection.BrokerConnection to use
+ """
+ self._set_connection(connection)
+ self.exchange = Exchange('event', 'topic')
+
+ def _set_connection(self, connection):
+ """
+ Arguments:
+
+ connection
+ The instance of kombu.connection.BrokerConnection to use
+ """
+ if isinstance(connection, BrokerConnection):
+ self.connection = connection
+ return self
+ else:
+ raise Exception("not a kombu.connection.BrokerConnection")
+
+ def get_connection(self):
+ """
+ Returns:
+ kombu.connection.BrokerConnection
+ """
+ return self.connection
+
+ def _get_producer(self):
+ """
+ Returns a producer that can be used to publish a message
+ """
+ if self.producer is None:
+ channel = self.connection.channel()
+ self.producer = Producer(channel,
+ exchange=self.exchange,
+ serializer="json")
+ return self.producer
+
+ def emit(self, event_name, event):
+ """
+ Emits the event
+ """
+ event.set_header('event_name', event_name)
+ self._get_producer().publish(event.data, routing_key=event_name)
+
+
+class Listener(object):
+ """
+ A Base class for amity event listeners
+ """
+ connection = None
+ exchange = None
+ consumers = []
+
+ def __init__(self, connection):
+ """
+ Arguments:
+
+ connection
+ The instance of kombu.connection.BrokerConnection to use
+ """
+ self._set_connection(connection)
+ self.exchange = Exchange('event', 'topic')
+
+ def _set_connection(self, connection):
+ """
+ Arguments:
+
+ connection
+ The instance of kombu.connection.BrokerConnection to use
+ """
+ if isinstance(connection, BrokerConnection):
+ self.connection = connection
+ return self
+ else:
+ raise Exception("not a kombu.connection.BrokerConnection")
+
+ def get_connection(self):
+ """
+ Returns:
+
+ kombu.connection.Broker
+ """
+ return self.connection
+
+ def _marshal(self, func):
+ """
+ Wraps up the pass in func so it receive a nice tasty
+ Event object
+ """
+ def marshalled_func(body, message):
+ """
+ Converts the kombu body into an Event object
+ and passes that to the handler function
+ """
+ event = Event()
+ for key, value in body['values'].items():
+ event.set_value(key, value)
+ for header, value in body['headers'].items():
+ event.set_header(header, value)
+ func(event)
+ return marshalled_func
+
+ def register_callback(self, event_name, callback):
+ """
+ Registers a callable against an event name
+
+ Arguments:
+
+ event_name
+ The name of the event you want to listen for
+
+ callback
+ The callable to call when the event is received
+ """
+ channel = self.get_connection().channel()
+ queue = Queue(str(getpid()),
+ exchange=self.exchange,
+ routing_key=event_name,
+ durable=False)
+ consumer = Consumer(channel, queue)
+ consumer.register_callback(self._marshal(callback))
+ consumer.consume()
+ self.consumers.append(consumer)
+
+ def listen_once(self, timeout=10):
+ """
+ Waits for 1 event to be received and processed and the returns
+
+ Arguments:
+
+ timeout
+ The length of time in seconds to wait for an event
+ """
+ self.connection.drain_events(timeout=timeout)
+
+ def listen(self):
+ """
+ Sit and processes events forever
+ """
+ while True:
+ try:
+ self.connection.drain_events(timeout=10)
+ except socket_timeout:
+ pass
+
+class Responder(Listener):
+ """
+ Base class for a listener that responds
+ """
+ _emitter = None
+
+ def __init__(self, connection):
+ Listener.__init__(self, connection)
+ self._emitter = Emitter(self.connection)
+
+ def _marshal(self, func):
+ """
+ Wraps the passed in function with the functionality to parse kombu body
+ and message objects
+
+ The passed in function should return an Event object
+ """
+ def marshalled_func(body, message):
+ """
+ Converts the kombu body into an Event object
+ and passes that to the handler function
+ """
+ event = Event()
+ for key, value in body['values'].items():
+ event.set_value(key, value)
+ for header, value in body['headers'].items():
+ event.set_header(header, value)
+ self._emitter.emit(event.get_header('reply-to'), func(event))
+ return marshalled_func
+
+
+class Requester(object):
+ """
+ Base class for an emitter that waits for a response
+ """
+ connection = None
+ _response = None
+ _emitter = None
+
+ def __init__(self, connection):
+ """
+ Arguments:
+
+ connection
+ The instance of kombu.connection.BrokerConnection to use
+ """
+ self.connection = connection
+ self._emitter = Emitter(self.connection)
+
+ def _on_response(self, event):
+ """
+ Simple handler to store the response
+ """
+ self._response = event
+
+ def call(self, event_name, event, timeout=10):
+ """
+ Emits the specified event and returns the response
+
+ Arguments:
+
+ event
+ The event to emit
+
+ timeout
+ The time in seconds to wait for a response
+ """
+ self._response = None
+ reply_to = str(uuid.uuid1())
+ event.set_header('reply-to', reply_to)
+ listen = Listener(self.connection)
+ listen.register_callback(reply_to, self._on_response)
+ self._emitter.emit(event_name, event)
+ listen.listen_once(timeout)
+ return self._response
View
3 requirements.txt
@@ -0,0 +1,3 @@
+amqplib==0.6.1
+anyjson==0.3.1
+kombu==1.1.6
View
85 scripts/.ropeproject/config.py
@@ -0,0 +1,85 @@
+# The default ``config.py``
+
+
+def set_prefs(prefs):
+ """This function is called before opening the project"""
+
+ # Specify which files and folders to ignore in the project.
+ # Changes to ignored resources are not added to the history and
+ # VCSs. Also they are not returned in `Project.get_files()`.
+ # Note that ``?`` and ``*`` match all characters but slashes.
+ # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
+ # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
+ # '.svn': matches 'pkg/.svn' and all of its children
+ # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
+ # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
+ prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject',
+ '.hg', '.svn', '_svn', '.git']
+
+ # Specifies which files should be considered python files. It is
+ # useful when you have scripts inside your project. Only files
+ # ending with ``.py`` are considered to be python files by
+ # default.
+ #prefs['python_files'] = ['*.py']
+
+ # Custom source folders: By default rope searches the project
+ # for finding source folders (folders that should be searched
+ # for finding modules). You can add paths to that list. Note
+ # that rope guesses project source folders correctly most of the
+ # time; use this if you have any problems.
+ # The folders should be relative to project root and use '/' for
+ # separating folders regardless of the platform rope is running on.
+ # 'src/my_source_folder' for instance.
+ #prefs.add('source_folders', 'src')
+
+ # You can extend python path for looking up modules
+ #prefs.add('python_path', '~/python/')
+
+ # Should rope save object information or not.
+ prefs['save_objectdb'] = True
+ prefs['compress_objectdb'] = False
+
+ # If `True`, rope analyzes each module when it is being saved.
+ prefs['automatic_soa'] = True
+ # The depth of calls to follow in static object analysis
+ prefs['soa_followed_calls'] = 0
+
+ # If `False` when running modules or unit tests "dynamic object
+ # analysis" is turned off. This makes them much faster.
+ prefs['perform_doa'] = True
+
+ # Rope can check the validity of its object DB when running.
+ prefs['validate_objectdb'] = True
+
+ # How many undos to hold?
+ prefs['max_history_items'] = 32
+
+ # Shows whether to save history across sessions.
+ prefs['save_history'] = True
+ prefs['compress_history'] = False
+
+ # Set the number spaces used for indenting. According to
+ # :PEP:`8`, it is best to use 4 spaces. Since most of rope's
+ # unit-tests use 4 spaces it is more reliable, too.
+ prefs['indent_size'] = 4
+
+ # Builtin and c-extension modules that are allowed to be imported
+ # and inspected by rope.
+ prefs['extension_modules'] = []
+
+ # Add all standard c-extensions to extension_modules list.
+ prefs['import_dynload_stdmods'] = True
+
+ # If `True` modules with syntax errors are considered to be empty.
+ # The default value is `False`; When `False` syntax errors raise
+ # `rope.base.exceptions.ModuleSyntaxError` exception.
+ prefs['ignore_syntax_errors'] = False
+
+ # If `True`, rope ignores unresolvable imports. Otherwise, they
+ # appear in the importing namespace.
+ prefs['ignore_bad_imports'] = False
+
+
+def project_opened(project):
+ """This function is called after opening the project"""
+ # Do whatever you like here!
View
23 scripts/consume.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+'''
+Created on Jun 19, 2011
+
+@author: mossj
+'''
+import os
+import sys
+ROOT_PATH = os.path.dirname(os.path.realpath(__file__ + "/../"))
+sys.path.append(ROOT_PATH)
+from amity.messaging import Listener
+from kombu.connection import BrokerConnection
+
+connection = BrokerConnection('localhost', 'guest', 'guest', '/')
+
+
+def lineItemLogger(event):
+ print "Message: '%s' FROM %s" % (event.get_value("msg"),
+ event.get_header('from'))
+
+c = Listener(connection)
+c.register_callback("lineitem.*", lineItemLogger)
+c.listen()
View
19 scripts/produce.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+'''
+Created on Jun 19, 2011
+
+@author: mossj
+'''
+import os
+import sys
+ROOT_PATH = os.path.dirname(os.path.realpath(__file__ + "/../"))
+sys.path.append(ROOT_PATH)
+from amity.entity import Event
+from amity.messaging import Emitter
+from kombu.connection import BrokerConnection
+connection = BrokerConnection('localhost', 'guest', 'guest', '/')
+
+emitter = Emitter(connection)
+
+emitter.emit("lineitem.new", Event({'msg': 'you mum'}))
+emitter.emit("lineitem.status_change", Event({'msg': 'and chips'}))
View
22 scripts/requester.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+'''
+Created on Jun 19, 2011
+
+@author: mossj
+'''
+import os
+import sys
+ROOT_PATH = os.path.dirname(os.path.realpath(__file__ + "/../"))
+sys.path.append(ROOT_PATH)
+from amity.entity import Event
+from amity.messaging import Requester
+from kombu.connection import BrokerConnection
+#import uuid
+
+connection = BrokerConnection('localhost', 'guest', 'guest', '/')
+
+#event = Emitter(connection, "event.reverse")
+
+r = Requester(connection)
+response = r.call('event.reverse', Event({'msg': 'jonathan'}))
+print "Message: %s" % response.get_value("msg")
View
25 scripts/responser.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+'''
+Created on Jun 19, 2011
+
+@author: mossj
+'''
+import os
+import sys
+ROOT_PATH = os.path.dirname(os.path.realpath(__file__ + "/../"))
+sys.path.append(ROOT_PATH)
+from amity.entity import Event
+from amity.messaging import Responder
+from kombu.connection import BrokerConnection
+connection = BrokerConnection('localhost', 'guest', 'guest', '/')
+
+
+def responser(event):
+ print "replying to %s on %s" % (event.get_value('msg'),
+ event.get_header("reply-to"))
+ response = event.get_value('msg')[::-1]
+ return Event({"msg": response})
+
+c = Responder(connection)
+c.register_callback("event.reverse", responser)
+c.listen()
View
13 setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+setup(name='amity',
+ version='0.1.0',
+ url='https://github.com/a-musing-moose/amity',
+ description="Event driven framework",
+ author="Jonathan Moss",
+ author_email="jonathan.moss@tangentone.com.au",
+ package_dir={'': '.'},
+ install_requires=['amqplib>=0.6.1'
+ 'anyjson>=0.3.1'
+ 'kombu>=1.1.6'],
+ )
View
0 tests/__init__.py
No changes.
View
12 tests/amity_tests.py
@@ -0,0 +1,12 @@
+'''
+Created on Jun 22, 2011
+
+@author: mossj
+'''
+from kombu.connection import BrokerConnection
+
+
+def test_can_connect():
+ b = BrokerConnection('localhost', 'guest', 'guest', '/')
+ b.connect()
+ assert True

0 comments on commit 7b9849d

Please sign in to comment.
Something went wrong with that request. Please try again.