--- a/iro/config.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/config.py Fri Mar 30 11:23:22 2012 +0200
@@ -8,11 +8,16 @@
from error import NeededOption
class MyConfigParser(ConfigParser):
+ """Configparser that also validate configfile.
+
+ It is possile to restiger function, that are called, when config file is reloaded
+ """
def __init__(self):
ConfigParser.__init__(self)
self.reloadList=[]
def read(self,files):
+ """reads an validate configuration file"""
from offer import getProvider
r = ConfigParser.read(self, files)
for s in self.sections():
@@ -23,14 +28,27 @@
return r
def reload_(self):
+ """run all registered function."""
for f in self.reloadList:
f()
def registerReload(self, func):
+ """adds **func** to reloadList.
+
+ func ist called with no arguments.
+ """
self.reloadList.append(func)
class Option():
+ """One Option in the configuration file"""
def __init__(self, validate, long="", help="", must=False, default=None):
+ """
+ :param func validate: a validate function, it has to return the value, if valid and raise an error if not.
+ :param string long: long description
+ :param string help: the help text
+ :param boolean must: Is this option nessasary
+ :param default: default value
+ """
self.validate = validate
self.long=long
self.help = help
@@ -38,17 +56,45 @@
self.default = default
class Config:
+ """Base class for all classes, that uses option from configfile.
+
+ If one option is valid, the attribute is created with the value of the validate function.
+ """
def __init__(self, name):
+ """
+ :param string name: section name.
+ """
self.name = name
self.options={
"port":Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True),
"dburl":Option(lambda x,y:x,long="Connection URL to database",must=True),
}
+ """Options dict for Options used in configuration file (see :class:`iro.config.Option`). Ordering of configuration fields are done by :attr:`order`.
+
+ Sample::
+
+ {"port":Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True),
+ "dburl":Option(lambda x,y:x,long="Connection URL to database",must=True)
+ }
+
+ A child class typically use update to add more options.
+ """
+
self.order = ["dburl","port"]
+ """ A list for ordering the options dict (:attr:`options`). """
+
self._init = True
+ """indecates, if the config is loaded with config values."""
def _read(self, cfg, write=False):
+ """Test or set configuration options.
+
+ :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
+ :param boolean write: test or set the option to actual object.
+
+ :raises: :exc:`iro.error.NeededOption`
+ """
c = dict(cfg)
for o in self.options:
@@ -65,12 +111,21 @@
setattr(self, o, option.default)
def validate(self, cfg):
+ """Validate configuration.
+
+ :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
+ """
self._read(cfg, False)
def load(self, cfg):
+ """Loads configuration into object.
+
+ :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
+ """
self._read(cfg, True)
def same(self, other):
+ """returns ``True``, if the options of other object are the same"""
for o in self.options:
if getattr(self,o) != getattr(other,o):
return False
@@ -78,6 +133,12 @@
return True
def sampleConf(self):
+ """returns a sample Configuration section.
+
+ This function also adds the long help text to the sample section.
+
+ :return: a list of lines
+ """
ret=[]
for o in self.order:
opt=self.options[o]
@@ -95,6 +156,7 @@
return ["[%s]"%self.name,]+ret
def readConfig():
+ """Read the configuration and update all registered object (see :meth:`MyConfigParser.reload_`)."""
log.msg("Reading configs.")
configParser.read(confFiles)
configParser.reload_()
@@ -107,6 +169,7 @@
raise Exception("Main options can't be reloaded, please restart your Application.")
def init():
+ """Load the main options."""
configParser.read(confFiles)
main.load(configParser.items("main"))
@@ -118,6 +181,10 @@
signal.signal(signal.SIGUSR2,rC)
configParser = MyConfigParser()
+"""configParser to get configuration."""
+
confFiles=["iro.conf", "~/iro.conf","/etc/iro/iro.conf"]
+"""Configfile list """
main = Config("main")
+"""Main config options"""
--- a/iro/error.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/error.py Fri Mar 30 11:23:22 2012 +0200
@@ -1,30 +1,49 @@
# -*- coding: utf-8 -*-
class InterfaceException(Exception):
- def __init__(self, code=999, msg="Unbekannter Fehler."):
+ """Exception, that should be reported to external client
+
+ 999 -- unknown error
+ """
+ def __init__(self, code=999, msg="Unknown error."):
+ """
+ :param integer code: the error code
+ :param string msg: the error message
+ """
self.code=code
self.msg=msg
def dict(self):
+ """dict representation of the error"""
return {"code":self.code,
"msg":self.msg,
}
+
def __str__(self):
return "%i: %s"%(self.code,self.msg)
class UserNotFound(InterfaceException):
+ """ 901 -- apikey is unknown -- user not found"""
def __init__(self):
- InterfaceException.__init__(self, 901, "API-Key is unknown.")
+ InterfaceException.__init__(self, 901, "Apikey is unknown.")
class JobNotFound(InterfaceException):
+ """902 -- jobid is unknown"""
def __init__(self):
InterfaceException.__init__(self, 902, "Jobid is unknown.")
class ExternalException(InterfaceException):
+ """950 -- error in external api"""
def __init__(self):
InterfaceException.__init__(self, 950, "Error in external API.")
class ValidateException(Exception):
+ """700 -- validation failed."""
def __init__(self, code=700, field=None, msg=None):
+ """
+ :param integer code: the error code
+ :param string field: the field, that is not valid
+ :param string msg: the error message
+ """
self.code=code
self.field=field
self.msg = msg
@@ -34,41 +53,50 @@
self.msg="Validation of '%s' failed."%field
def dict(self):
+ """dict representation of the error"""
return {"code":self.code,
"msg":self.msg,
+ "field":self.field,
}
def __str__(self):
return "%i: %s"%(self.code,self.msg)
class InvalidTel(ValidateException):
+ """701 -- invalid telnumber"""
def __init__(self, number,field=None):
self.number = number
msg = "No valid telnumber: '%s'"%(number)
ValidateException.__init__(self, 701, field, msg)
class InvalidMail(ValidateException):
+ """702 -- invalid mailaddress"""
def __init__(self, number,field=None):
self.number = number
msg = "No valid email: '%s'"%(number)
ValidateException.__init__(self, 702, field, msg)
class OfferException(Exception):
+ """an Exception in Offer handling"""
def __init__(self, name):
self.name = name
class NoRoute(OfferException):
+ """no valid route found"""
def __str__(self):
return "Not a valid route: %s"%self.name
class NoProvider(OfferException):
+ """no provider found"""
def __str__(self):
return "Not a valid provider: %s"%self.name
class NoTyp(OfferException):
+ """no typ found."""
def __str__(self):
return "Not a valid Typ: %s"%self.name
class RejectRecipient(Exception):
+ """can't handle the recipient in a route"""
def __init__(self,recipient, status=None):
self.recipient = recipient
self.status = status
@@ -77,17 +105,21 @@
return "Reject recipient(%s): %s"%(str(self.recipient),str(self.status))
class ConfigException(Exception):
+ """Exception while loading configuration."""
def __init__(self,section, name):
self.section = section
self.name = name
class UnknownOption(ConfigException):
+ """Option is unknown"""
def __str__(self):
return "Unknown option '%s' in section '%s'."%(self.name, self.section)
class NeededOption(ConfigException):
+ """Option is missing, but needed."""
def __str__(self):
return "Option '%s' in section '%s' is missing."%(self.name, self.section)
class NoRouteForTask(Exception):
+ """Can't send message to recipient with given offers"""
pass
--- a/iro/install.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/install.py Fri Mar 30 11:23:22 2012 +0200
@@ -17,6 +17,11 @@
from . import config
def checkConfig():
+ """check configuration file syntax.
+
+ :return: boolena value.
+ """
+
try:
l = configParser.read(confFiles)
if len(l) > 0:
@@ -27,6 +32,10 @@
return False
def checkDatabase():
+ """Checks, if all tables are created.
+
+ :return: boolean value
+ """
engine = create_engine(config.main.dburl)
for t in Base.metadata.sorted_tables:
if not t.exists(engine):
@@ -34,6 +43,10 @@
return True
def checkDatabaseConnection():
+ """Checks, if database can be connected.
+
+ :return: boolean value
+ """
try:
engine = create_engine(config.main.dburl)
con = engine.connect()
@@ -44,10 +57,12 @@
return False
def createDatabase():
+ """Create all database tables or only missing."""
engine = create_engine(config.main.dburl)
Base.metadata.create_all(engine)
def createSampleConfig():
+ """create a sample configuration file 'iro.conf' with all possible provider sections."""
if not os.path.exists("iro.conf"):
with open("iro.conf",'w') as fp:
fp.write("\n".join(main.sampleConf()))
@@ -61,6 +76,14 @@
log.msg("iro.conf exists and will not be overwritten.")
def getAllRoutes(providers,write=False):
+ """Checks and update offer list.
+
+ :param boolean write: check or update list
+ :return dict:
+ - **"orphand"** (Set) -- a set of orphand offers
+ - **"added"** (Set) -- a set of new offers. The new name have a schema provider_typ_route
+
+ """
engine = create_engine(config.main.dburl)
ret={"orphand":Set(),"added":Set()}
with WithSession(engine,write) as session:
--- a/iro/main.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/main.py Fri Mar 30 11:23:22 2012 +0200
@@ -10,6 +10,13 @@
from . import config
def runReactor(reactor, engine, port, root):
+ """start reactor.
+
+ :param reactor: twisted reactor
+ :param engine: sqlalchemy engine
+ :param integer port: port to listen to
+ :param `twisted.web.resource.Resource` root: resource to share
+ """
setEngine(engine)
startPool(reactor)
setPool(dbPool)
--- a/iro/telnumber.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/telnumber.py Fri Mar 30 11:23:22 2012 +0200
@@ -16,22 +16,52 @@
from .error import InvalidTel
class Telnumber:
+ """A telefonnumer, with splitted country part"""
re_telnum=re.compile(r'^\s*(\+)?([0-9\s\-/\(\)]){5,}\s*$')
+ """Regex for a complete telefon number"""
+
re_land=re.compile(r'^\s*(\+|00)(?P<land>[1-9]{2})')
+ """Regex for country part"""
+
re_number=re.compile(r'[^0-9]')
+ """Regex for numbers"""
+
std_land="49"
+ """Standard country part"""
def __init__(self,number=None):
+ """
+ :param string number: a telefonnumber
+
+ .. automethod:: __eq__
+ .. automethod:: __neq__
+ .. automethod:: __hash__
+ .. automethod:: __str__
+ .. automethod:: __repr__
+
+ """
+ self.land = None
+ """Country part of a telefonnumber"""
+
+ self.number = None
+ """Localpart of the telefonnumber"""
+
if not(number is None):
self.createNumber(number)
def createNumber(self, number):
+ """Split string into two parts: one country part and the rest.
+ For a local number :attr:`std_land` will be used for country part.
+
+ :param string number: a telefonnumber
+ """
if not self.re_telnum.match(number):
raise InvalidTel(number)
self.land=self.std_land
+
land=self.re_land.match(number)
if land:
@@ -46,16 +76,30 @@
self.number = number
def __eq__(self, y):
+ """Return ``True``, if y hase the same telefonnumber"""
return ((self.number == y.number) and ( self.land == y.land))
- def __ne__(self, y):
+ def __neq__(self, y):
+ """Return ``True``, if y is not equal (see :meth:`__eq__`)"""
return not self.__eq__(y)
def __hash__(self):
+ """Return the Hash for telefonnumbers.
+
+ Yust use the hash of the string representation of the Number
+ """
return str(self).__hash__()
def __str__(self):
+ """String representation of the telefonnumber.
+
+ :return: the international telefonnumber with leading zeros
+ """
return "00%s%s"%(self.land,self.number)
def __repr__(self):
+ """debug representation of the class.
+
+ :return: `<Telnumber 0012345667>`
+ """
return "<Telnumber %s>"%str(self)
--- a/iro/validate.py Fri Mar 30 02:25:20 2012 +0200
+++ b/iro/validate.py Fri Mar 30 11:23:22 2012 +0200
@@ -9,7 +9,11 @@
from .telnumber import Telnumber
def vBool(value, field):
- '''Validator for boolean values'''
+ '''Validate function for boolean values
+
+ :return: **value**
+ :raises: :exc:`iro.error.ValidateException`
+ '''
t=[True, 1, "true", "True", "TRUE"]
f=[False, 0, "false", "False", "FALSE"]
if value in t:
@@ -21,7 +25,14 @@
def vInteger(value, field, minv=None, maxv=None, none_allowed=False):
+ """validate function for integer values.
+ :param integer minv: minimum value
+ :param integer maxv: maximum value
+ :param boolean none_allowed: is None or empty string allowed
+ :return: **value**
+ :raises: :exc:`iro.error.ValidateException`
+ """
if none_allowed and value in [None,'']:
return None
@@ -41,7 +52,13 @@
return ret
def vHash(value,field,minlength=None,maxlength=None):
- '''Validator for hash values'''
+ '''Validate function for hash values
+
+ :param integer minlength: minimum length of value string
+ :param integer maxlength: maximum length of value string
+ :return: **value**
+ :raises: :exc:`iro.error.ValidateException`
+ '''
if not re.match(r'^[a-f0-9]*$', value.lower()):
raise ValidateException(field=field)
if minlength and len(value)<minlength:
@@ -51,7 +68,11 @@
return value.lower()
def vTel(value,field):
- '''Validator for Telefonnumbers'''
+ '''Validator for telefon numbers
+ :return: **value**
+ :raises: :exc:`iro.error.InvalidTel`
+ '''
+
ret = []
for v in value:
try:
@@ -65,16 +86,24 @@
def vEmail(value, field, allowString=True, allowList=True):
'''validator for emailadresses (see wikipeda for strange mailadresses and RFC3696)
+
valid:
- "very.(),:;<>[]\\".VERY.\\"very@\\\ \\"very\\".unusual"@strange.example.com
- ""@example.org
- "very.unusual.@.unusual.com"@example.com'
+
+ - "very.(),:;<>[]\\".VERY.\\"very@\\\ \\"very\\".unusual"@strange.example.com
+ - ""@example.org
+ - "very.unusual.@.unusual.com"@example.com'
not valid:
- Abc.@example.com
- Abc..123@example.com
- thisis."notallowed@example.com
- this\\ still\\"not\\allowed@example.com
+
+ - Abc.@example.com
+ - Abc..123@example.com
+ - thisis."notallowed@example.com
+ - this\\ still\\"not\\allowed@example.com
+
+ :param boolean allowString: value can be a string -> a string is returned
+ :param boolean allowList: value is a a list -> a list is returned
+ :return: **value**
+ :raises: :exc:`iro.error.ValidateException`, :exc:`iro.error.InvalidMail`
'''
ret = []
str_=False
@@ -132,14 +161,26 @@
return ret
def validate(kwd,func, need=True,*args,**kargs):
- '''validate decorator
-use it like this:
- @validate(kwd=userhash, func=vuserhash)
- f(userhash)
-that will validate usrhash with the function vuserhash.
-Every validate function should raise an Exception, if the the value is not valid.
-All args and kargs are used to call the validate function.
-if need is True, the kwd can't be None.'''
+ '''validate decorator.
+
+ :param string kwd: keyword to validate
+ :param func func: validate function
+ :param boolean need: ``False`` -- ``None`` is a valid value for kwd
+ :params args: arguments for validate function
+ :params kargs: keyword arguments for validate function
+
+ .. note:: this decorator can handle function that returns a defer object.
+
+ use it like this::
+
+ @validate(kwd=userhash, func=vuserhash)
+ f(userhash)
+
+ that will validate ``userhash`` with the function **vuserhash**.
+ Every validate function should raise an Exception, if the the value is not valid.
+ All **args** and **kargs** are used to call the validate function.
+ if **need** is True, the kwd can't be `None`.
+ '''
@decorator
def v(f,*a,**k):
kp=getcallargs(f,*a,**k)