#!/usr/bin/env python # -*- coding: utf-8 -*- """ Copyright(C) 2007 INL Written by Damien Boucard <damien.boucard AT inl.fr> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. --- unicity.py is a module which checks a given attribute is unique in a sequence of objects. """ class UnicityError(LookupError): pass class IncreasingError(LookupError): pass def check_unicity(attribute_name, sequence, *args, **kw): """ This function checks if an attribute of sequence elements is unique. If more than 2 arguments are given, it will check recursively into the given sequence names which should be attributes of sub-elements. If None is given, the element itself is iterated. @type sequence: list or tupe or any iterable instance @param attribute_name: represents the name of the unique attribute of the sequence element. @type attribute_name: str @raise UnicityError: if unicity is not respected. @raise AttributeError: if attribute_name is not found in one of sequence elements. >>> class Test: ... def __init__(self, id): self.id = id >>> check_unicity("id", (Test(1), Test(2), Test(42), Test(33))) True >>> check_unicity("id", (Test(1), Test(2), Test(42), Test(1))) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): UnicityError: id=1 is found twice. Example of recursive lookup: >>> check_unicity("id", [(Test(1), Test(2)), (Test(42), Test(33))], None) True """ values = kw.get("values", []) for element in sequence: if len(args) > 0: if args[0] is None: subsequence = element else: subsequence = getattr(element, args[0]) check_unicity(attribute_name, subsequence, *args[1:], **{"values":values}) else: attribute = getattr(element, attribute_name) if attribute in values: raise UnicityError("%s=%s is found twice." %(attribute_name, attribute)) else: values.append(attribute) return True def check_increasing(element_name, attribute_name, sequence, *args, **kw): """ This function checks if sequence elements have an attribute which increments between one element to another. If more than 2 arguments are given, it will check recursively into the given sequence names which should be attributes of sub-elements. If None is given, the element itself is iterated. @type sequence: list or tupe or any iterable instance @param attribute_name: represents the name of the unique attribute of the sequence element. @type attribute_name: str @keyword minimal_value: indicates the first id value expected (default: 1) @type minimal_value: int @raise UnicityError: if unicity is not respected. @raise AttributeError: if attribute_name is not found in one of sequence elements. >>> class Test: ... def __init__(self, id): self.id = id #>>> check_increasing("test", "id", (Test(1), Test(2), Test(3), Test(4))) True #>>> check_increasing("test", "id", (Test(1), Test(2), Test(4), Test(5))) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): IncreasingError: id=3 of <test> is missing. #>>> check_increasing("test", "id", (Test(42), Test(43), Test(43), Test(44)), minimal_value=42) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): IncreasingError: id=43 of <test> is found twice. Example of recursive lookup: >>> check_increasing("test", "id", [(Test(1), Test(2)), (Test(3), Test(4))], None) True """ name = kw.get("name", attribute_name) minimal_value = kw.get("minimal_value", 1) expected = kw.get("expected", [minimal_value,]) for element in sequence: if len(args) > 0: if args[0] is None: subsequence = element else: subsequence = getattr(element, args[0]) check_increasing(element_name, attribute_name, subsequence, *args[1:], **{"minimal_value": minimal_value, "expected": expected}) else: attribute = getattr(element, attribute_name) if attribute < minimal_value: raise IncreasingError("%s of <%s> must be greater than %s; %s found." %(attribute_name, element_name, minimal_value-1, attribute)) elif attribute > expected[0]: raise IncreasingError("%s=%s of <%s> is missing." %(attribute_name, expected[0], element_name)) elif attribute < expected[0]: raise IncreasingError("%s=%s of <%s> is found twice." %(attribute_name, attribute, element_name)) else: expected[0] = expected[0] + 1 return True if __name__ == "__main__": from doctest import testmod from sys import exit failure, nb_test = testmod() if failure: exit(1)