Permalink
Browse files

Moved code under pikacon namespace. Changed how configuration file is…

… parsed and fixed tests. Requires pika >= 0.9.8.
  • Loading branch information...
1 parent 2bb2eab commit 0efed1760aa6370ceeb96340283d9d8c39e60260 Jukka Ojaniemi committed Dec 2, 2012
View
@@ -1,18 +1,19 @@
-Introduction
-============
+=======
+Pikacon
+=======
Pikacon is a helper library which will reduce the amount of boilerplate your
software needs when it is using pika for creating connection to broker and
declaring exchanges and queues.
Requirements
-------------
+============
* python 2.7
-* pika
+* pika >= 0.9.8
Usage
------
+=====
Pikacon provides helper class which can be imported to your program. Class
takes a path to .ini-style config file as a parameter and creates connection,
@@ -28,35 +29,118 @@ Creation order is following:
5. Bindings
Config
-------
+======
Pikacon uses Pythons ConfigParser to get config for connection, exchanges and
queues.
-Below is an example of config file::
+Broker
+------
+
+Broker is configured as in above example. Section name is broker and options
+are regular pika parameters for broker. If you want to configure ssl_options
+create new section for those and refer that section name in broker options.
+Eg.::
+
+ [broker]
+ ...
+ ssl = True
+ ssl_options = my_ssl_options
+
+ [my_ssl_options]
+ ...
+
+Exchange
+--------
+
+Section name for exchange consists of two parts divided by ':'. First part is
+'exchange' and second part is the name of the exchange (eg.
+[exchange:myexchange]).
+
+The actual options below exchange section are normal key = value parameters
+which are used in pika.
+
+Queue
+-----
+
+The section for queue consits of two parts divided by ':'. First part is
+'queue' and second part is the name of the queue (eg. [queue:myqueue]).
+
+The options below queue section are::
+ durable = True|False
+ exclusive = True|False
+ arguments = queue:queuename:arguments
+
+Extra arguments for the queue are provided by another section. Pikacon assumes
+that the name of the arguments section follows following convention
+'queue:queuename:nameofargumentssection'.
+
+The actual options below queue section are normal key = value parameters which
+are used in pika.
+
+Binding
+-------
+
+The name of the binding section consists three parts divided by ':'. First
+part is always 'binding'. Second part is the name of the queue we're binding.
+Third part is name of the exchange where we're binding the queue. (eg.
+[binding:myqueue:myexchange]).
+
+The actual options below binding section are normal key = value parameters
+which are used in pika.
+Complete configuration example
+------------------------------
+::
[broker]
host = localhost
- port = 5432
+ port = 5672
username = guest
password = guest
+ virtual_host = /
+ heartbeat = 60
- [exchange1]
- config_for = exchange
- type = fanout
+ [exchange:exchangename]
+ type = direct
durable = False
auto_delete = True
- [queue1]
- config_for = queue
+ [queue:testqueue1]
+ durable = True
+ exclusive = False
+
+ [queue:testqueue2]
durable = False
- exclusive = True
+ exclusive = False
+
+ [queue:testqueue3]
+ durable = True
+ exclusive = False
+ arguments = queue:testqueue3:arguments
+
+ [queue:testqueue4]
+ durable = True
+ exclusive = False
+
+ [queue:testqueue3:arguments]
+ x-message-ttl = 1800000
+ x-dead-letter-exchange = exchangename
+ x-dead-letter-routing-key = key4
+
+ [binding:testqueue1:exchangename]
+ routing_key = key1
+
+ [binding:testqueue2:exchangename]
+ routing_key = key2
+
+ [binding:testqueue3:exchangename]
+ routing_key = key3
- [binding1]
- config_for = binding
- queue = queue1
- exchange = exchange1
- routing_key= routing
+ [binding:testqueue4:exchangename]
+ routing_key = key4
-In above example config_for tells the parser what kind of config this section
-contains. Rest of the parameters are regular pika parameters.
+Above example configures connection to broker at localhost. It defines one
+direct exchange called exchangename and four queues called testqueue1,
+testqueue2, testqueue3 and testqueue4. Testqueue3 has extra arguments which
+define dead letter exchange. All queues are bound to our only exchange with
+routingkeys key1, key2, key3 and key4.
View
@@ -0,0 +1,10 @@
+CHANGELOG
+=========
+
+0.3 - 2012-12-02
+----------------
+
+* Requires Pika >= 0.9.8
+* Supports same parameter set for connections as pika does.
+* Shortened configuration file structure. Configuration files made for older
+ versions of pikacon doesn't work anymore.
View
@@ -1,14 +1,12 @@
from setuptools import setup, find_packages
-version = "0.2"
+version = "0.3"
setup(name="pikacon",
version=version,
description="Helper library for using pika.",
long_description=open("README.rst").read() + "\n" +
open("docs/HISTORY.rst").read(),
- # Get more strings from
- # http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
@@ -18,17 +16,14 @@
author_email="jukka.ojaniemi@jyu.fi",
url="https://github.com/pingviini/pikacon",
license="GPL",
- py_modules=[
- 'pikacon',
- 'pikaconconfig'
- ],
packages=find_packages("src", exclude=["ez_setup"]),
package_dir={"": "src"},
+ namespace_packages=['pikacon'],
include_package_data=True,
zip_safe=False,
install_requires=[
"setuptools",
- "pika",
+ "pika>=0.9.8",
],
test_suite="tests",
)
File renamed without changes.
View
@@ -0,0 +1,191 @@
+import logging
+import ConfigParser
+
+
+logger = logging.getLogger("pikacon")
+
+
+class ConnectionConfig(ConfigParser.SafeConfigParser):
+ """
+ ConnectionConfig provides all the attributes pika needs for creating
+ connection, exchanges, queues and bindings.
+ """
+
+ @property
+ def broker_config(self):
+ config = {}
+ converted = []
+ convert_to_int = ["port", "heartbeat_interval", "channel_max",
+ "frame_max", "connection_attempts", "ssl_port"]
+ convert_to_float = ["retry_delay", "socket_timeout"]
+ convert_to_bool = ["ssl", "backpressure_detection"]
+
+ for option in convert_to_int:
+ try:
+ config[option] = self.getint("broker", option)
+ converted.append(option)
+ except ConfigParser.NoOptionError:
+ pass
+
+ for option in convert_to_float:
+ try:
+ config[option] = self.getfloat("broker", option)
+ converted.append(option)
+ except ConfigParser.NoOptionError:
+ pass
+
+ for option in convert_to_bool:
+ try:
+ config[option] = self.getfloat("broker", option)
+ converted.append(option)
+ except ConfigParser.NoOptionError:
+ pass
+
+ for option in self.options("broker"):
+ if not option in converted and\
+ option not in ["username", "password"]:
+ if option == "ssl_options":
+ ssl_options = dict(self.items(self.get('broker',
+ 'ssl_options')))
+ config[option] = ssl_options
+ else:
+ config[option] = self.get("broker", option)
+
+ return config
+
+ @property
+ def credentials(self):
+ """Return dict containing username and password."""
+ return {'username': self.username, 'password': self.password}
+
+ @property
+ def host(self):
+ return self.get("broker", "host")
+
+ @property
+ def port(self):
+ return self.getint("broker", "port")
+
+ @property
+ def username(self):
+ try:
+ return self.get("broker", "username")
+ except ConfigParser.NoOptionError:
+ return 'guest'
+
+ @property
+ def password(self):
+ try:
+ return self.get("broker", "password")
+ except ConfigParser.NoOptionError:
+ return 'guest'
+
+ @property
+ def virtual_host(self):
+ return self.get("broker", "virtual_host")
+
+ @property
+ def heartbeat(self):
+ return self.getint("broker", "heartbeat")
+
+ @property
+ def channel_max(self):
+ return self.getint("broker", "channel_max")
+
+ @property
+ def frame_max(self):
+ return self.getint("broker", "frame_max")
+
+ @property
+ def ssl(self):
+ return self.getbool("broker", "ssl")
+
+ @property
+ def ssl_options(self):
+ return self.getbool("broker", "ssl_options")
+
+ @property
+ def connection_attempts(self):
+ return self.getint("broker", "connection_attempts")
+
+ @property
+ def retry_delay(self):
+ return self.getint("broker", "retry_delay")
+
+ @property
+ def socket_timeout(self):
+ return self.getint("broker", "socket_timeout")
+
+ @property
+ def exchanges(self):
+ """Return list of exchanges"""
+ return self.get_config("exchange")
+
+ @property
+ def queues(self):
+ """Return list of queues"""
+ return self.get_config("queue")
+
+ @property
+ def bindings(self):
+ """Return list of bindings"""
+ return self.get_config("binding")
+
+ def get_config(self, section_name):
+ """Return list of sections which are for specified config"""
+
+ sections = {}
+
+ for section in self.sections():
+ try:
+ assert(section != "broker")
+ assert(section.split(':', 1)[0] == section_name)
+ if section_name == 'queue':
+ # skip arguments in here
+ assert(len(section.split(':')) < 3)
+ assert(section.split(':', 1)[0] == section_name)
+
+ options = self.options(section)
+ items = {}
+
+ if 'arguments' in options:
+ arguments_name = self.get(section, 'arguments')
+ arguments = self.get_arguments(arguments_name)
+ items['arguments'] = arguments
+ options.remove('arguments')
+
+ for option in options:
+ try:
+ items[option] = self.getboolean(section, option)
+ except ValueError:
+ items[option] = self.get(section, option)
+
+ sections[section] = items
+ except ConfigParser.NoOptionError:
+ # Config file has configuration which doesn't belong to
+ # pikacon so we ignore it.
+ pass
+ except AssertionError:
+ # We're parsing broker section which will be ignored too.
+ pass
+
+ return sections
+
+ @property
+ def get_exchanges(self):
+ """Returns list of Exchange objects."""
+
+
+ def get_arguments(self, name):
+ """Return dict of arguments for section"""
+
+ kw = {}
+ options = self.options(name)
+
+ for option in options:
+ try:
+ kw[option] = self.getint(name, option)
+ except ValueError:
+ kw[option] = self.get(name, option)
+
+ return kw
Oops, something went wrong.

0 comments on commit 0efed17

Please sign in to comment.