Context
Во-первых, спасибо за гипотезу. Это очень мощный и очень полезный!
Я написал стратегию гипотез для создания монотонных (ANDS и ORs) выражений политики в форме:
(A and (B or C))
Это можно рассматривать как древовидную структуру, где A, B и C являются атрибутами в конечных узлах, тогда как 'и' и 'или' являются неконцевыми узлами.
Стратегия, кажется, генерирует выражения по желанию.
>>> find(policy_expressions(), lambda x: len(x.split()) > 3)
'(A or (A or A))'
(Возможно, статистическое разнообразие примеров можно улучшить, но это не суть этого вопроса).
Неравенства также действительны. Например:
(N or (WlIorO and (nX <= 55516 and e)))
Я хочу ограничить или отфильтровать примеры, чтобы я мог генерировать выражения политики с указанным числом конечных узлов (то есть атрибутов).
Для теста производительности я попытался использовать data.draw()
с filter
примерно так:
@given(data=data())
def test_keygen_encrypt_proxy_decrypt_decrypt_execution_time(data, n):
"""
:param n: the input size n. Number of attributes or leaf nodes in policy tree.
"""
policy_str = data.draw(strategy=policy_expressions().filter(lambda x: len(extract_attributes(group, x)) == n),
label="policy string")
Где extract_attributes()
подсчитывает количество вершин листьев в выражении, а n
- желаемое количество листьев.
Проблема с этим решением состоит в том, что когда n
> 16, гипотеза выдает:
hypothesis.errors.Unsatisfiable: Unable to satisfy assumptions of hypothesis test_keygen_encrypt_proxy_decrypt_decrypt_execution_time.
Я хочу создать допустимые выражения политики с сотнями конечных узлов.
Другим недостатком этого подхода было то, что гипотеза сообщала HealthCheck.filter_too_much
и HealthCheck.too_slow
, а @settings
становился безобразным.
Я бы предпочел параметр, чтобы сказать policy_expressions(leaf_nodes=4)
, чтобы получить пример, подобный этому:
(N or (WlIorO and (nX <= 55516 and e)))
Сначала я этого избегал, потому что не могу понять, как это сделать с помощью рекурсивного кода стратегии.
Вопрос
Можете ли вы предложить способ рефакторинга этой стратегии, чтобы ее можно было параметрировать для числа конечных узлов?
Вот код стратегии (в любом случае его открытый источник в Charm Crypto)
from hypothesis.strategies import text, composite, sampled_from, characters, one_of, integers
def policy_expressions():
return one_of(attributes(), inequalities(), policy_expression())
@composite
def policy_expression(draw):
left = draw(policy_expressions())
right = draw(policy_expressions())
gate = draw(gates())
return u'(' + u' '.join((left, gate, right)) + u')'
def attributes():
return text(min_size=1, alphabet=characters(whitelist_categories='L', max_codepoint=0x7e))
@composite
def inequalities(draw):
attr = draw(attributes())
oper = draw(inequality_operators())
numb = draw(integers(min_value=1))
return u' '.join((attr, oper, str(numb)))
def inequality_operators():
return sampled_from((u'<', u'>', u'<=', u'>='))
def gates():
return sampled_from((u'or', u'and'))
def assert_valid(policy_expression):
assert policy_expression # not empty
assert policy_expression.count(u'(') == policy_expression.count(u')')
https://github.com/JHUISI/charm/blob/dev/charm/toolbox/policy_expression_spec.py