# HG changeset patch # User Sandro Knauß # Date 1333099402 -7200 # Node ID ef2df3f23cb1d9e348a60930aa589b3a4aa97cad # Parent a0adcb788fec3b6c036213021c3b5caab9873ea7 adding docstring: iro diff -r a0adcb788fec -r ef2df3f23cb1 iro/config.py --- 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""" diff -r a0adcb788fec -r ef2df3f23cb1 iro/error.py --- 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 diff -r a0adcb788fec -r ef2df3f23cb1 iro/install.py --- 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: diff -r a0adcb788fec -r ef2df3f23cb1 iro/main.py --- 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) diff -r a0adcb788fec -r ef2df3f23cb1 iro/telnumber.py --- 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[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: `` + """ return ""%str(self) diff -r a0adcb788fec -r ef2df3f23cb1 iro/validate.py --- 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)[]\\".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)