核心で、私がやろうとしているのは、この 装飾されていない検証関数のように見えるいくつかの関数を取ることです:
def f(k: bool):
def g(n):
# check that n is valid
return n
return g
そして、それらを次の装飾された検証関数のようにします:
@k
def f():
def g(n):
# check that n is valid
return n
return g
ここでの考え方k
は、すべての実装関数で同じ機能を記述しているということです。
具体的には、これらの関数はすべて官能的な検証フレームワークで使用するための「検証」関数を返します。したがって、 type のすべての関数は、f()
後で によって実行される関数を返しSchema()
ます。k
は実際には です。つまり、値が OKallow_none
かどうかを判断するフラグです。None
非常に単純な例は、次のサンプル使用コードです。
x = "Some input value."
y = None
input_validator = Schema(f(allow_none=True))
x = input_validator(x) # succeeds, returning x
y = input_validator(y) # succeeds, returning None
input_validator_no_none = Schema(f(allow_none=False))
x = input_validator(x) # succeeds, returning x
y = input_validator(y) # raises an Invalid
サンプルの使用コードを変更せずに、装飾されていない検証関数を装飾された検証関数に変更することで、同じ結果を達成しようとしています。具体例を挙げると、これを次のように変更します。
def valid_identifier(allow_none: bool=True):
min_range = Range(min=1)
validator = Any(All(int, min_range), All(Coerce(int), min_range))
return Any(validator, None) if allow_none else validator
これに:
@allow_none(default=True)
def valid_identifier():
min_range = Range(min=1)
return Any(All(int, min_range), All(Coerce(int), min_range))
これら 2 つの関数から返される関数は同等である必要があります。
私が書こうとしたのは、decorator
ライブラリを利用してこれです:
from decorator import decorator
@decorator
def allow_none(default: bool=True):
def decorate_validator(wrapped_validator, allow_none: bool=default):
@wraps(wrapped_validator)
def validator_allowing_none(*args, **kwargs):
if allow_none:
return Any(None, wrapped_validator)
else:
return wrapped_validator(*args, **kwargs)
return validator_allowing_none
return decorate_validator
そしてunittest.TestCase
、これが期待どおりに機能するかどうかをテストするために、次のものがあります。
@allow_none()
def test_wrapped_func():
return Schema(str)
class TestAllowNone(unittest.TestCase):
def test_allow_none__success(self):
test_string = "blah"
validation_function = test_wrapped_func(allow_none=False)
self.assertEqual(test_string, validation_function(test_string))
self.assertEqual(None, validation_function(None))
しかし、私のテストは次の失敗を返します:
def validate_callable(path, data):
try:
> return schema(data)
E TypeError: test_wrapped_func() takes 0 positional arguments but 1 was given
これをデバッグしようとしましたが、デバッガーが実際に装飾に入ることができませんでした。この (非常に長い) ブログ投稿シリーズで提起されたような命名の問題が原因で、test_wrapped_func
引数リストが適切に設定されていないため、デコレータが実行されることさえありませんが、完全に別のものである可能性もあります。
他のバリエーションも試してみました。から関数の括弧を削除することにより@allow_none
:
@allow_none
def test_wrapped_func():
return Schema(str)
別のエラーが発生します。
> validation_function = test_wrapped_func(allow_none=False)
E TypeError: test_wrapped_func() got an unexpected keyword argument 'allow_none'
@decorator
失敗をドロップすると:
> validation_function = test_wrapped_func(allow_none=False)
E TypeError: decorate_validator() missing 1 required positional argument: 'wrapped_validator'
@allow_none
引数を取るため、これは理にかなっています。そのため、論理的に括弧が必要になります。それらを置き換えると、元のエラーが発生します。
デコレータは微妙で、明らかに何かが欠けています。これは関数のカリー化に似ていますが、あまり機能していません。これをどのように実装する必要があるかについて、何が欠けていますか?