在覈心,我想要做的是採取一些看起來像這樣未經修飾的驗證功能功能:處理函數參數與裝飾
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
在所有的執行功能的描述相同的功能。
具體而言,這些函數都返回「驗證」功能,以便與voluptuous validation framework一起使用。所以f()
類型的所有函數都返回一個函數,稍後由Schema()
執行。 k
實際上是allow_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))
功能來自這兩個返回的應該是等價的。
什麼我試着寫是這樣的,利用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
我試過調試這個,但是無法讓調試器實際進入裝飾。我懷疑由於命名問題(如this (very lengthy) blog post series中提出的問題),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
需要一個參數,因此邏輯上需要括號。替換它們會導致原始錯誤。
裝飾師是微妙的,我很明顯在這裏失去了一些東西。這與currying一個函數類似,但它不是很有效。我錯過了應該如何實施?
哇!這工作。我想我甚至明白了爲什麼這是有效的。謝謝您的幫助! –