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_configurable__no_config(logger_mock, warnings_mock): """ Test `@configurable` if there is a warning for a missing configuration. """ @configurable def configured(): pass configured() assert logger_mock.warning.call_count == 0 assert warnings_mock.warn.call_count == 1
def test_configurable__unused_hparam(): """ Test if `@configurable` errors if `HParam` is unused. """ @configurable def configured(arg=HParam()): return arg with pytest.raises(ValueError): configured() # Ensure it continues to throw this error with pytest.raises(ValueError): configured()
def test_configurable__empty_configuration_warnings(logger_mock, warnings_mock): """ Test if `@configurable` throws warnings for an empty configuration. """ @configurable def configured(): pass add_config({configured: HParams()}) logger_mock.reset_mock() warnings_mock.reset_mock() configured() assert logger_mock.warning.call_count == 0 assert warnings_mock.warn.call_count == 0
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_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_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_configurable__double_invoke(): """ Test if `@configurable` can be called with no arguments and `@configurable` can be called. """ @configurable() def configured(arg): return arg expected = '' add_config({configured: HParams(arg=expected)}) assert configured() == expected