adding docstring: iro devel
authorSandro Knauß <knauss@netzguerilla.net>
Fri, 30 Mar 2012 11:23:22 +0200
branchdevel
changeset 267 ef2df3f23cb1
parent 266 a0adcb788fec
child 268 d0fcb1cde990
adding docstring: iro
iro/config.py
iro/error.py
iro/install.py
iro/main.py
iro/telnumber.py
iro/validate.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"""
--- 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)