def test__resolve_configuration__multiple_configurations(): """ Test resolution for with multiple configurations. """ @configurable def configured(arg): pass class Configured: @configurable def __init__(self): pass parsed = _parse_configuration({ 'tests.test_hparams.test__resolve_configuration__multiple_configurations': { '<locals>.Configured.__init__': HParams(), '<locals>.configured': HParams() } }) resolved = _resolve_configuration(parsed) assert isinstance( resolved[_get_function_signature(Configured.__init__.__wrapped__)], HParams) assert isinstance( resolved[_get_function_signature(configured.__wrapped__)], HParams) assert len(resolved) == 2
def test_set_lazy_resolution(logger_mock): """ Test if `@configurable` can resolve it's configuration lazily. Also, test if `get_config` only lazily returns the configuration. This test combines multiple tests because Python does not support unloading a module: https://stackoverflow.com/questions/8781257/remove-an-imported-python-module TODO: This doesn't set `set_lazy_resolution(False)` which forces a resolution for any unresolved modules. In order to add more tests like so, we'd need to create more modules. """ set_lazy_resolution(True) expected = '' add_config({'_tests.other_module.configured': HParams(arg=expected)}) assert {} == get_config() assert logger_mock.warning.call_count == 1 assert len(_get_skip_resolution()) == 1 logger_mock.reset_mock() # NOTE: Test multiple similar config calls add_config({'_tests.other_module.configured': HParams(arg=expected)}) assert len(_get_skip_resolution()) == 1 from _tests.other_module import configured assert configured() == expected assert len(get_config()) == 1 assert logger_mock.warning.call_count == 0 assert len(_get_skip_resolution()) == 0
def test__resolve_configuration__duplicate(): """ Test resolution for multiple sys path allowing for duplicate configuration. """ sys.path = [os.path.dirname(__file__)] + sys.path parsed = _parse_configuration({ 'test_hparams._test__resolve_configuration__duplicate': HParams(), 'tests.test_hparams._test__resolve_configuration__duplicate': HParams(), }) with pytest.raises(TypeError): _resolve_configuration(parsed) sys.path.pop(0)
def test__resolve_configuration__external_internal_library(): """ Test resolution for an internal api of an external library. """ _pytest.python_api.approx = configurable(_pytest.python_api.approx) parsed = _parse_configuration({'pytest.approx': HParams(expected='')}) function_signature = _get_function_signature(pytest.approx.__wrapped__) assert isinstance( _resolve_configuration(parsed)[function_signature], HParams)
def test__resolve_configuration__external_library(): """ Test resolution for an external library and with a none-empty `HParams`. """ pytest.approx = configurable(pytest.approx) parsed = _parse_configuration({'pytest.approx': HParams(expected='')}) function_signature = _get_function_signature(pytest.approx.__wrapped__) assert isinstance( _resolve_configuration(parsed)[function_signature], HParams)
def test__resolve_configuration__built_in_function(): """ Test resolution for an built in function. """ builtins.open = configurable(builtins.open) function_name = 'builtins.open' parsed = _parse_configuration({function_name: HParams()}) function_signature = _get_function_signature(builtins.open.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams)
def test_configurable__benchmark(): """ Test if `@configurable` is within the ballpark of a native decorator in performance. """ def baseline(function): @wraps(function) def decorator(*args, **kwargs): return function(*args, **kwargs) return decorator lambda_ = lambda arg: arg other_lambda_ = lambda arg: arg native = baseline(lambda_) configured = configurable(other_lambda_) add_config({configured: HParams(arg='')}) samples = 10000 # NOTE: The timer decisions are inspired by the native `timeit` module. gc.disable() iterator = itertools.repeat(None, samples) start = time.perf_counter() for _ in iterator: native('') native_elapsed = time.perf_counter() - start iterator = itertools.repeat(None, samples) start = time.perf_counter() for _ in iterator: configured() configured_elapsed = time.perf_counter() - start gc.enable() assert (configured_elapsed / samples) - (native_elapsed / samples) < 3e-05
def test__resolve_configuration__configured_hparam(): """ Test resolution for a configured `HParam`. """ parsed = _parse_configuration( {_test__resolve_configuration__configured_hparam: HParams(arg=None)}) function_signature = _get_function_signature( _test__resolve_configuration__configured_hparam.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams)
def test__resolve_configuration__lambda_function(): """ Test resolution for an lambda function. """ configured = configurable(lambda: None) function_name = ('tests.test_hparams.test__resolve_configuration__lambda_function' '.<locals>.<lambda>') parsed = _parse_configuration({function_name: HParams()}) function_signature = _get_function_signature(configured.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams)
def test_configurable__double_invoke(): """ Test if `@configurable` can be called with no arguments. """ @configurable() def configured(arg): return arg expected = '' add_config({configured: HParams(arg=expected)}) assert configured() == expected
def test_configurable__unwrap(): """ Test if `@configurable` works with the original `configured` path. """ @configurable() def configured(arg): return arg expected = '' add_config({configured.__wrapped__: HParams(arg=expected)}) assert configured() == expected
def test__resolve_configuration__multiple_sys_path(): """ Test resolution for multiple `sys` path. """ sys.path = [os.path.dirname(__file__)] + sys.path function_name = 'test_hparams._test__resolve_configuration__multiple_sys_path' parsed = _parse_configuration({function_name: HParams()}) function_signature = _get_function_signature( _test__resolve_configuration__multiple_sys_path.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams) sys.path.pop(0)
def test__resolve_configuration__no_sys_path(): """ Test resolution with no `sys` path. """ original = sys.path sys.path = [] parsed = _parse_configuration( {_test__resolve_configuration__no_sys_path.__wrapped__: HParams()}) function_signature = _get_function_signature( _test__resolve_configuration__no_sys_path.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams) sys.path = original
def test__configurable__regression(): """ Test if `@configurable` fails with a none-overridden `__init__` function for a global class. TODO: Support this use case. Curiously, this works for none-global classes though. """ _test__resolve_configuration__super_class.__init__ = configurable( _test__resolve_configuration__super_class.__init__) with pytest.raises(TypeError): add_config({_test__resolve_configuration__super_class.__init__: HParams()})
def test__configurable__regression(warnings_mock): """ Test if `@configurable` fails with a none-overridden `__init__` function for a global class. TODO: Support this use case. Curiously, this works for none-global classes though. """ _test__resolve_configuration__super_class.__init__ = configurable( _test__resolve_configuration__super_class.__init__) add_config({_test__resolve_configuration__super_class.__init__: HParams()}) assert warnings_mock.warn.call_count == 1
def test_configurable__empty_configuration_warnings(logger_mock): """ Test if `@configurable` throws warnings for an empty configuration. """ @configurable def configured(): pass add_config({configured: HParams()}) logger_mock.reset_mock() configured() assert logger_mock.warning.call_count == 0
def test_configurable__get_partial(): """ Test if `@configurable#get_configured_partial` is able to export a partial with expected configuration. """ @configurable def configured(arg): return arg expected = '' add_config({configured: HParams(arg=expected)}) partial = configured.get_configured_partial() assert expected == partial()
def test__resolve_configuration__internal_function(): """ Test resolution for an internal function. """ @configurable def configured(arg): pass function_name = ('tests.test_hparams.test__resolve_configuration__internal_function' '.<locals>.configured') parsed = _parse_configuration({function_name: HParams()}) function_signature = _get_function_signature(configured.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams)
def test_merge_configs(): """ Test the merging of two configurations via `add_config`. """ @configurable def configured(arg, arg_two): pass @configurable def other_configured(arg): pass clear_config() add_config({configured: HParams(arg='arg', arg_two='arg_two')}) add_config({other_configured: HParams()}) assert len(get_config()) == 2 assert get_config()[_get_function_signature(configured.__wrapped__)]['arg'] == 'arg' add_config({configured: HParams(arg='gra')}) assert len(get_config()) == 2 assert get_config()[_get_function_signature(configured.__wrapped__)]['arg'] == 'gra' assert get_config()[_get_function_signature(configured.__wrapped__)]['arg_two'] == 'arg_two' clear_config()
def test_config_operators(): """ Test the `log_config`, `clear_config`, `add_config` and `get_config` together. It's difficult to test them alone. """ @configurable def configured(arg): pass assert len(get_config()) == 0 add_config({configured: HParams()}) log_config() # Smoke test assert len(get_config()) == 1 clear_config() assert len(get_config()) == 0
def test__resolve_configuration__internal_class(): """ Test resolution for an internal class. """ class Configured: @configurable def __init__(self): pass function_name = ('tests.test_hparams.test__resolve_configuration__internal_class.' '<locals>.Configured.__init__') parsed = _parse_configuration({function_name: HParams()}) function_signature = _get_function_signature(Configured.__init__.__wrapped__) assert isinstance(_resolve_configuration(parsed)[function_signature], HParams)
def test_configurable__override_with_kwarg(logger_mock, warnings_mock): """ Test if `@configurable` throws an error on a override of an `HParam` argument that's not configured. """ @configurable def configured(arg=HParam()): return arg add_config({configured: HParams()}) logger_mock.reset_mock() warnings_mock.reset_mock() configured(arg='a') assert logger_mock.warning.call_count == 0 assert warnings_mock.warn.call_count == 1
def test_configurable__exposed_function(logger_mock, warnings_mock): """ Test if `@configurable` throws warnings if a configured function is run without its decorator. """ @configurable def configured(): pass add_config({configured: HParams()}) logger_mock.reset_mock() warnings_mock.reset_mock() configured.__wrapped__() assert logger_mock.warning.call_count == 0 assert warnings_mock.warn.call_count == 1
def test_configurable__override(logger_mock): """ Test if `@configurable` throws an error on a override of an `HParam` argument that's not configured. """ @configurable def configured(arg=HParam()): return arg add_config({configured: HParams()}) logger_mock.reset_mock() configured('a') assert logger_mock.warning.call_count == 1 # It shouldn't throw a second warning configured('a') assert logger_mock.warning.call_count == 1
def test_configurable__regression_overriding(logger_mock, warnings_mock): """ Test `@configurable` that there are no warnings with a valid config. """ add_config({_test_configurable__valid_config: HParams(hparam='')}) _test_configurable__valid_config('arg', kwarg='') assert logger_mock.warning.call_count == 0 assert warnings_mock.warn.call_count == 0
def test_parse_hparam_args__list(): hparam_args = ['--foo', 'HParams(boo=[1,2])'] assert parse_hparam_args(hparam_args) == {'foo': HParams(boo=[1, 2])}
def test_parse_hparam_args__exponent(): hparam_args = ['--foo', 'HParams(boo=10**-6)'] assert parse_hparam_args(hparam_args) == {'foo': HParams(boo=10**-6)}
def test_parse_hparam_args__nesting(): hparam_args = ['--moo.foo', 'HParams(boo=1)'] assert parse_hparam_args(hparam_args) == {'moo.foo': HParams(boo=1)}
def test_parse_hparam_args__equals(): hparam_args = ['--foo=HParams(boo=1)'] assert parse_hparam_args(hparam_args) == {'foo': HParams(boo=1)}
def test_parse_hparam_args__string(): hparam_args = ['--foo', 'HParams(boo="WaveNet")'] assert parse_hparam_args(hparam_args) == {'foo': HParams(boo='WaveNet')}