Проверить инфраструктуры для объектов Python


Фон

Я являюсь автором модуля, который основывается на аналогичные классы, некоторые из которых наследуют от других. Пользователь создает экземпляр класса и предлагает параметры для создания сложного объекта. Эти параметры должны соответствовать определенным критериям, чтобы быть действительным; в противном случае будут ошибки, дальше по дороге. Просмотрев некоторые из этих критериев занимает значительное количество времени или по иным причинам нецелесообразно. Следовательно, некоторые проверки должны выполняться, только если пользователь желает, он, как правило, после того, как они произошла ошибка.

Я реализовал инфраструктура, которая позволяет мне определить индивидуальные чеки, как методы классов, зарегистрировать их в качестве проверки, и имеют центральную функцию, которая проходит все проверки, применяя для определенного класса.

Фактический Код

# This provides an infrastructure for checks that works as follows:
# • Each check is registered as such by being decorated with `@Check`.
# • Each class that needs checks inherits from CheckEnvironment.
# • All checks are run with the method `run_checks`.

# This class exist just to mark functions by decoration
class Check(object):
    def __init__(self,function):
        self.function = function

    def __call__(self,*args):
        self.function(*args)

class CheckEnvironment(object):
    def run_checks(self, fail_fast=True):
        """
        Performs a series of checks that may not be feasible at runtime.

        Parameters
        ----------
        fail_fast : boolean
            whether to abort on the first failure.
            If false, an error is raised only
            after all problems are printed.
        """

        self.failed_check = False
        self.fail_checks_fast = fail_fast

        # execute all methods decorated with checker:
        visited = set()
        for cls in [self.__class__] + self.__class__.mro():
            for name,member in cls.__dict__.items():
                if name not in visited and isinstance(member,Check):
                    member(self)
                visited.add(name)

        if self.failed_check:
            raise ValueError("Check failed.")

    def _check_assert(self,condition,message):
        """
        Assert `condition` as part of a check. Raise `message` if it fails.
        """

        if not condition:
            self.failed_check = True
            if self.fail_checks_fast:
                raise ValueError(message)
            else:
                print(message)

Пример использования

from check import CheckEnvironment, Check

class House(CheckEnvironment):
    def __init__(self):
        self.doors = []
        self.windows = []

    def add_door(self,door):
        self.doors.append(door)

    def add_window(self,window):
        self.windows.append(window)

    @Check
    def check_doors(self):
        self._check_assert(
                self.doors,
                "Must have at least one door."
            )

    @Check
    def check_windows(self):
        self._check_assert(
                len(self.windows) > len(self.doors),
                "Must have more doors than windows."
            )

my_house = House()
my_house.run_checks() # fails
my_house.add_door("front door")
my_house.run_checks() # fails differently
my_house.add_window("kitchen window")
my_house.add_window("toilet window")
my_house.run_checks() # succeeds

Тест

import unittest
from check import CheckEnvironment, Check

class SomeChecks(CheckEnvironment):
    def __init__(self,fail=True):
        self.invoked = []
        self.fail = fail

    @Check
    def A(self):
        self.invoked.append(1)

    @Check
    def B(self):
        self.invoked.append(2)
        self._check_assert( not self.fail, "Check A failed." )

    @Check
    def C(self):
        self.invoked.append(3)

    @Check
    def D(self):
        self.invoked.append(4)
        self._check_assert( not self.fail, "Check D failed." )

    def E(self):
        raise AssertionError("This method should not be run")

class DifferentChecks(SomeChecks):
    def A(self):
        raise AssertionError("This method should not be run")

    @Check
    def B(self):
        self.invoked.append(5)

    @Check
    def C(self):
        self.invoked.append(6)
        self._check_assert( not self.fail, "Check C failed." )

class TestChecks(unittest.TestCase):
    def test_default(self):
        X = SomeChecks(fail=True)
        with self.assertRaises(ValueError):
            X.run_checks()
        assert (2 in X.invoked) ^ (4 in X.invoked)

    def test_fail_slow(self):
        X = SomeChecks(fail=True)
        with self.assertRaises(ValueError):
            X.run_checks(fail_fast=False)
        self.assertListEqual( sorted(X.invoked), [1,2,3,4] )

    def test_success(self):
        X = SomeChecks(fail=False)
        X.run_checks()
        self.assertListEqual( sorted(X.invoked), [1,2,3,4] )

class TestInheritance(unittest.TestCase):
    def test_default(self):
        X = DifferentChecks(fail=True)
        with self.assertRaises(ValueError):
            X.run_checks()
        assert (4 in X.invoked) ^ (6 in X.invoked)
        assert all( i not in X.invoked for i in [1,2,3] )

    def test_fail_slow(self):
        X = DifferentChecks(fail=True)
        with self.assertRaises(ValueError):
            X.run_checks(fail_fast=False)
        self.assertListEqual( sorted(X.invoked), [4,5,6] )

    def test_success(self):
        X = DifferentChecks(fail=False)
        X.run_checks()
        self.assertListEqual( sorted(X.invoked), [4,5,6] )

if __name__ == "__main__":
    unittest.main(buffer=True)

Какие отзывы я ищу

Я особенно заинтересован в обратной связи на моем пути, чтобы отметить чеки (т. е. с декоратором изменение класса объекта) и параметры, которые сделают его более элегантным. Я также заинтересован в обратной связи на что-либо другое, кроме использования пробелов.



132
2
задан 16 марта 2018 в 12:03 Источник Поделиться
Комментарии