diff --git a/.travis.yml b/.travis.yml index 2bc77802..0eb47468 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,10 +39,10 @@ before_script: - tools/kubernetes-client.sh script: - - pytest -v --cov=kopf --cov-branch + - pytest --cov=kopf --cov-branch - coveralls - codecov --flags unit - - pytest -v --only-e2e # NB: after the coverage uploads! + - pytest --only-e2e # NB: after the coverage uploads! - mypy kopf --strict --pretty deploy: diff --git a/examples/02-children/example.py b/examples/02-children/example.py index 52420072..f6be10a4 100644 --- a/examples/02-children/example.py +++ b/examples/02-children/example.py @@ -1,5 +1,5 @@ import kopf -import kubernetes.client +import pykube import yaml @@ -28,8 +28,10 @@ def create_fn(spec, **kwargs): kopf.adopt(doc) # Actually create an object by requesting the Kubernetes API. - api = kubernetes.client.CoreV1Api() - pod = api.create_namespaced_pod(namespace=doc['metadata']['namespace'], body=doc) + api = pykube.HTTPClient(pykube.KubeConfig.from_env()) + pod = pykube.Pod(api, doc) + pod.create() + api.session.close() # Update the parent's status. - return {'children': [pod.metadata.uid]} + return {'children': [pod.metadata['uid']]} diff --git a/examples/09-testing/test_example_09.py b/examples/09-testing/test_example_09.py index 1220c4c3..7dcbf572 100644 --- a/examples/09-testing/test_example_09.py +++ b/examples/09-testing/test_example_09.py @@ -35,9 +35,11 @@ def test_resource_lifecycle(mocker): # Run an operator and simulate some activity with the operated resource. with kopf.testing.KopfRunner(['run', '--verbose', '--standalone', example_py], timeout=60) as runner: - subprocess.run(f"kubectl create -f {obj_yaml}", shell=True, check=True) + subprocess.run(f"kubectl create -f {obj_yaml}", + shell=True, check=True, timeout=10, capture_output=True) time.sleep(5) # give it some time to react - subprocess.run(f"kubectl delete -f {obj_yaml}", shell=True, check=True) + subprocess.run(f"kubectl delete -f {obj_yaml}", + shell=True, check=True, timeout=10, capture_output=True) time.sleep(1) # give it some time to react # Ensure that the operator did not die on start, or during the operation. diff --git a/examples/10-builtins/example.py b/examples/10-builtins/example.py index 01d99b7a..6bd2576c 100644 --- a/examples/10-builtins/example.py +++ b/examples/10-builtins/example.py @@ -5,12 +5,6 @@ tasks = {} # dict{namespace: dict{name: asyncio.Task}} -try: - cfg = pykube.KubeConfig.from_service_account() -except FileNotFoundError: - cfg = pykube.KubeConfig.from_file() -api = pykube.HTTPClient(cfg) - @kopf.on.resume('', 'v1', 'pods') @kopf.on.create('', 'v1', 'pods') @@ -36,8 +30,10 @@ async def pod_killer(namespace, name, logger, timeout=30): await asyncio.sleep(timeout) logger.info(f"=== Pod killing happens NOW!") + api = pykube.HTTPClient(pykube.KubeConfig.from_env()) pod = pykube.Pod.objects(api, namespace=namespace).get_by_name(name) pod.delete() + api.session.close() except asyncio.CancelledError: logger.info(f"=== Pod killing is cancelled!") diff --git a/examples/11-filtering-handlers/test_example_11.py b/examples/11-filtering-handlers/test_example_11.py index 46b08a00..b2a63039 100644 --- a/examples/11-filtering-handlers/test_example_11.py +++ b/examples/11-filtering-handlers/test_example_11.py @@ -35,9 +35,11 @@ def test_handler_filtering(mocker): # Run an operator and simulate some activity with the operated resource. with kopf.testing.KopfRunner(['run', '--verbose', '--standalone', example_py]) as runner: - subprocess.run(f"kubectl create -f {obj_yaml}", shell=True, check=True) + subprocess.run(f"kubectl create -f {obj_yaml}", + shell=True, check=True, timeout=10, capture_output=True) time.sleep(5) # give it some time to react - subprocess.run(f"kubectl delete -f {obj_yaml}", shell=True, check=True) + subprocess.run(f"kubectl delete -f {obj_yaml}", + shell=True, check=True, timeout=10, capture_output=True) time.sleep(1) # give it some time to react # Ensure that the operator did not die on start, or during the operation. diff --git a/examples/12-embedded/example.py b/examples/12-embedded/example.py index 625b47aa..26a684c1 100644 --- a/examples/12-embedded/example.py +++ b/examples/12-embedded/example.py @@ -1,4 +1,5 @@ import asyncio +import contextlib import threading import time @@ -22,13 +23,14 @@ def kopf_thread( ): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) + with contextlib.closing(loop): - kopf.configure(verbose=True) # log formatting + kopf.configure(verbose=True) # log formatting - loop.run_until_complete(kopf.operator( - ready_flag=ready_flag, - stop_flag=stop_flag, - )) + loop.run_until_complete(kopf.operator( + ready_flag=ready_flag, + stop_flag=stop_flag, + )) def main(steps=3): diff --git a/examples/99-all-at-once/example.py b/examples/99-all-at-once/example.py index efe7664b..9bc43d9c 100644 --- a/examples/99-all-at-once/example.py +++ b/examples/99-all-at-once/example.py @@ -18,12 +18,6 @@ E2E_FAILURE_COUNTS = {} E2E_TRACEBACKS = True -try: - cfg = pykube.KubeConfig.from_service_account() -except FileNotFoundError: - cfg = pykube.KubeConfig.from_file() -api = pykube.HTTPClient(cfg) - @kopf.on.startup() async def startup_fn_simple(logger, **kwargs): @@ -112,8 +106,10 @@ def create_pod(**kwargs): kopf.label(pod_data, {'application': 'kopf-example-10'}) # Actually create an object by requesting the Kubernetes API. + api = pykube.HTTPClient(pykube.KubeConfig.from_env()) pod = pykube.Pod(api, pod_data) pod.create() + api.session.close() @kopf.on.event('', 'v1', 'pods', labels={'application': 'kopf-example-10'}) diff --git a/kopf/config.py b/kopf/config.py index c6ec29ef..e5a53250 100644 --- a/kopf/config.py +++ b/kopf/config.py @@ -48,9 +48,12 @@ def configure( del logger.handlers[1:] # everything except the default NullHandler # Prevent the low-level logging unless in the debug verbosity mode. Keep only the operator's messages. - logging.getLogger('urllib3').propagate = bool(debug) - logging.getLogger('asyncio').propagate = bool(debug) - logging.getLogger('kubernetes').propagate = bool(debug) + # For no-propagation loggers, add a dummy null handler to prevent printing the messages. + for name in ['urllib3', 'asyncio', 'kubernetes']: + logger = logging.getLogger(name) + logger.propagate = bool(debug) + if not debug: + logger.handlers[:] = [logging.NullHandler()] loop = asyncio.get_event_loop() loop.set_debug(bool(debug)) diff --git a/kopf/reactor/causation.py b/kopf/reactor/causation.py index 8c350572..9f82dae8 100644 --- a/kopf/reactor/causation.py +++ b/kopf/reactor/causation.py @@ -125,7 +125,7 @@ class ResourceChangingCause(ResourceCause): @property def event(self) -> Reason: - warnings.warn("`cause.event` is deprecated; use `cause.reason`.", DeprecationWarning) + warnings.warn("cause.event is deprecated; use cause.reason.", DeprecationWarning) return self.reason @property diff --git a/kopf/reactor/handlers.py b/kopf/reactor/handlers.py index 12f50307..6aba37a9 100644 --- a/kopf/reactor/handlers.py +++ b/kopf/reactor/handlers.py @@ -64,5 +64,5 @@ class ResourceHandler(BaseHandler): @property def event(self) -> Optional[causation.Reason]: - warnings.warn("`handler.event` is deprecated; use `handler.reason`.", DeprecationWarning) + warnings.warn("handler.event is deprecated; use handler.reason.", DeprecationWarning) return self.reason diff --git a/kopf/toolkits/runner.py b/kopf/toolkits/runner.py index e7c33204..06044720 100644 --- a/kopf/toolkits/runner.py +++ b/kopf/toolkits/runner.py @@ -67,7 +67,6 @@ def __init__( self.kwargs = kwargs self.reraise = reraise self.timeout = timeout - self._loop = asyncio.new_event_loop() self._stop = threading.Event() self._ready = threading.Event() # NB: not asyncio.Event! self._thread = threading.Thread(target=self._target) @@ -113,7 +112,8 @@ def _target(self) -> None: # Every thread must have its own loop. The parent thread (pytest) # needs to know when the loop is set up, to be able to shut it down. - asyncio.set_event_loop(self._loop) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) self._ready.set() # Execute the requested CLI command in the thread & thread's loop. @@ -126,6 +126,17 @@ def _target(self) -> None: self._future.set_exception(e) else: self._future.set_result(result) + finally: + + # Shut down the API-watching streams. + loop.run_until_complete(loop.shutdown_asyncgens()) + + # Shut down the transports and prevent ResourceWarning: unclosed transport. + # See: https://docs.aiohttp.org/en/stable/client_advanced.html#graceful-shutdown + # TODO: Try a hack: https://github.com/aio-libs/aiohttp/issues/1925#issuecomment-575754386 + loop.run_until_complete(asyncio.sleep(1.0)) + + loop.close() @property def future(self) -> ResultFuture: diff --git a/kopf/utilities/piggybacking.py b/kopf/utilities/piggybacking.py index f49ccb19..d4e93deb 100644 --- a/kopf/utilities/piggybacking.py +++ b/kopf/utilities/piggybacking.py @@ -11,6 +11,7 @@ :mod:`credentials` and :func:`authentication`. """ import logging +import warnings from typing import Any, Union, Optional, Sequence from kopf.clients import auth @@ -89,7 +90,9 @@ def login_via_pykube( # to inject custom authentication methods. Support these hacks if possible. config: pykube.KubeConfig try: - config = auth.get_pykube_cfg() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + config = auth.get_pykube_cfg() logger.debug("Pykube is configured via monkey-patched get_pykube_cfg().") except NotImplementedError: try: diff --git a/setup.py b/setup.py index 01dd8fa9..fdb94938 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,10 @@ from setuptools import setup, find_packages -LONG_DESCRIPTION = open(os.path.join(os.path.dirname(__file__), 'README.md')).read() -DESCRIPTION = LONG_DESCRIPTION.splitlines()[0].lstrip('#').strip() +with open(os.path.join(os.path.dirname(__file__), 'README.md')) as f: + LONG_DESCRIPTION = f.read() + DESCRIPTION = LONG_DESCRIPTION.splitlines()[0].lstrip('#').strip() + PROJECT_URLS = { 'Documentation': 'https://kopf.readthedocs.io', 'Bug Tracker': 'https://github.com/zalando-incubator/kopf/issues', diff --git a/tests/authentication/test_credentials.py b/tests/authentication/test_credentials.py index 3d10a855..4f34d064 100644 --- a/tests/authentication/test_credentials.py +++ b/tests/authentication/test_credentials.py @@ -141,9 +141,10 @@ async def test_basic_auth(vault): }) session = await fn() - assert session._default_auth.login == 'username' - assert session._default_auth.password == 'password' - assert 'Authorization' not in session._default_headers + async with session: + assert session._default_auth.login == 'username' + assert session._default_auth.password == 'password' + assert 'Authorization' not in session._default_headers async def test_header_with_token_only(vault): @@ -155,8 +156,9 @@ async def test_header_with_token_only(vault): }) session = await fn() - assert session._default_auth is None - assert session._default_headers['Authorization'] == 'Bearer token' + async with session: + assert session._default_auth is None + assert session._default_headers['Authorization'] == 'Bearer token' async def test_header_with_schema_only(vault): @@ -168,8 +170,9 @@ async def test_header_with_schema_only(vault): }) session = await fn() - assert session._default_auth is None - assert session._default_headers['Authorization'] == 'Digest xyz' + async with session: + assert session._default_auth is None + assert session._default_headers['Authorization'] == 'Digest xyz' async def test_header_with_schema_and_token(vault): @@ -182,8 +185,9 @@ async def test_header_with_schema_and_token(vault): }) session = await fn() - assert session._default_auth is None - assert session._default_headers['Authorization'] == 'Digest xyz' + async with session: + assert session._default_auth is None + assert session._default_headers['Authorization'] == 'Digest xyz' async def test_ca_insecure(vault, cafile): @@ -195,8 +199,9 @@ async def test_ca_insecure(vault, cafile): }) session = await fn() - ctx = session.connector._ssl - assert ctx.verify_mode == ssl.CERT_NONE + async with session: + ctx = session.connector._ssl + assert ctx.verify_mode == ssl.CERT_NONE async def test_ca_as_path(vault, cafile): @@ -208,10 +213,11 @@ async def test_ca_as_path(vault, cafile): }) session = await fn() - ctx = session.connector._ssl - assert len(ctx.get_ca_certs()) == 1 - assert ctx.cert_store_stats()['x509'] == 1 - assert ctx.cert_store_stats()['x509_ca'] == 1 + async with session: + ctx = session.connector._ssl + assert len(ctx.get_ca_certs()) == 1 + assert ctx.cert_store_stats()['x509'] == 1 + assert ctx.cert_store_stats()['x509_ca'] == 1 async def test_ca_as_data(vault, cabase64): @@ -223,10 +229,11 @@ async def test_ca_as_data(vault, cabase64): }) session = await fn() - ctx = session.connector._ssl - assert len(ctx.get_ca_certs()) == 1 - assert ctx.cert_store_stats()['x509'] == 1 - assert ctx.cert_store_stats()['x509_ca'] == 1 + async with session: + ctx = session.connector._ssl + assert len(ctx.get_ca_certs()) == 1 + assert ctx.cert_store_stats()['x509'] == 1 + assert ctx.cert_store_stats()['x509_ca'] == 1 # TODO: find a way to test that the client certificates/pkeys are indeed loaded. @@ -240,7 +247,10 @@ async def test_clientcert_as_path(vault, cafile, certfile, pkeyfile): private_key_path=pkeyfile, ), }) - await fn() + session = await fn() + + async with session: + pass async def test_clientcert_as_data(vault, cafile, certbase64, pkeybase64): @@ -252,4 +262,7 @@ async def test_clientcert_as_data(vault, cafile, certbase64, pkeybase64): private_key_data=pkeybase64, ), }) - await fn() + session = await fn() + + async with session: + pass diff --git a/tests/authentication/test_login.py b/tests/authentication/test_login.py index f176b0aa..e542c762 100644 --- a/tests/authentication/test_login.py +++ b/tests/authentication/test_login.py @@ -2,13 +2,16 @@ Remember: We do not test the clients, we assume they work when used properly. We test our own functions here, and check if the clients were called. """ +import pytest + import pykube from kopf import login def test_client_login_works_incluster(login_mocks, kubernetes): - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.client_in_cluster.called assert not login_mocks.client_from_file.called @@ -17,14 +20,16 @@ def test_client_login_works_incluster(login_mocks, kubernetes): def test_client_login_works_viaconfig(login_mocks, kubernetes): login_mocks.client_in_cluster.side_effect = kubernetes.config.ConfigException - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.client_in_cluster.called assert login_mocks.client_from_file.called def test_pykube_login_works_incluster(login_mocks, pykube): - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.pykube_in_cluster.called assert not login_mocks.pykube_from_file.called @@ -33,7 +38,8 @@ def test_pykube_login_works_incluster(login_mocks, pykube): def test_pykube_login_works_viaconfig(login_mocks, pykube): login_mocks.pykube_in_cluster.side_effect = FileNotFoundError - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.pykube_in_cluster.called assert login_mocks.pykube_from_file.called @@ -47,7 +53,8 @@ def test_monkeypatched_get_pykube_cfg_overrides_pykube(mocker, login_mocks): 'clusters': [{'name': 'self', 'cluster': {'server': 'https://localhost'}}], }) - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert get_pykube_cfg.called assert not login_mocks.pykube_in_cluster.called @@ -55,7 +62,8 @@ def test_monkeypatched_get_pykube_cfg_overrides_pykube(mocker, login_mocks): def test_pykube_is_independent_of_client_incluster(login_mocks, no_kubernetes, pykube): - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.pykube_in_cluster.called assert not login_mocks.pykube_from_file.called @@ -64,14 +72,16 @@ def test_pykube_is_independent_of_client_incluster(login_mocks, no_kubernetes, p def test_pykube_is_independent_of_client_viaconfig(login_mocks, no_kubernetes, pykube): login_mocks.pykube_in_cluster.side_effect = FileNotFoundError - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.pykube_in_cluster.called assert login_mocks.pykube_from_file.called def test_client_is_independent_of_pykube_incluster(login_mocks, no_pykube, kubernetes): - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.client_in_cluster.called assert not login_mocks.client_from_file.called @@ -80,7 +90,8 @@ def test_client_is_independent_of_pykube_incluster(login_mocks, no_pykube, kuber def test_client_is_independent_of_pykube_viaconfig(login_mocks, no_pykube, kubernetes): login_mocks.client_in_cluster.side_effect = kubernetes.config.ConfigException - login() + with pytest.deprecated_call(match=r"cease using kopf.login\(\)"): + login() assert login_mocks.client_in_cluster.called assert login_mocks.client_from_file.called diff --git a/tests/authentication/test_reauthentication.py b/tests/authentication/test_reauthentication.py index bae37e71..4a72382e 100644 --- a/tests/authentication/test_reauthentication.py +++ b/tests/authentication/test_reauthentication.py @@ -33,8 +33,9 @@ async def test_session_is_injected_to_request( context, result = await request_fn(1) - assert context is not None - assert result == 101 + async with context.session: + assert context is not None + assert result == 101 async def test_session_is_injected_to_stream( @@ -49,9 +50,10 @@ async def test_session_is_injected_to_stream( async for context, result in stream_fn(1): counter += 1 - assert context is not None - assert result == 101 - assert counter == 1 + async with context.session: + assert context is not None + assert result == 101 + assert counter == 1 async def test_session_is_passed_through_to_request( @@ -64,8 +66,9 @@ async def test_session_is_passed_through_to_request( explicit_context = APIContext(ConnectionInfo(server='http://irrelevant/')) context, result = await request_fn(1, context=explicit_context) - assert context is explicit_context - assert result == 101 + async with context.session: + assert context is explicit_context + assert result == 101 async def test_session_is_passed_through_to_stream( @@ -80,6 +83,7 @@ async def test_session_is_passed_through_to_stream( async for context, result in stream_fn(1, context=explicit_context): counter += 1 - assert context is explicit_context - assert result == 101 - assert counter == 1 + async with context.session: + assert context is explicit_context + assert result == 101 + assert counter == 1 diff --git a/tests/authentication/test_tempfiles.py b/tests/authentication/test_tempfiles.py index fc4d3317..a2d0599e 100644 --- a/tests/authentication/test_tempfiles.py +++ b/tests/authentication/test_tempfiles.py @@ -8,7 +8,8 @@ def test_created(): tempfiles = _TempFiles() path = tempfiles[b'hello'] assert os.path.isfile(path) - assert open(path, 'rb').read() == b'hello' + with open(path, 'rb') as f: + assert f.read() == b'hello' def test_reused(): diff --git a/tests/basic-structs/test_causes.py b/tests/basic-structs/test_causes.py index 6b057dbc..b8469e07 100644 --- a/tests/basic-structs/test_causes.py +++ b/tests/basic-structs/test_causes.py @@ -72,7 +72,6 @@ def test_resource_changing_cause_with_all_args(mocker): assert cause.resource is resource assert cause.logger is logger assert cause.reason is reason - assert cause.event is reason # deprecated assert cause.initial is initial assert cause.body is body assert cause.patch is patch @@ -81,6 +80,9 @@ def test_resource_changing_cause_with_all_args(mocker): assert cause.old is old assert cause.new is new + with pytest.deprecated_call(match=r"use cause.reason"): + assert cause.event is reason + def test_resource_changing_cause_with_only_required_args(mocker): logger = mocker.Mock() @@ -102,7 +104,6 @@ def test_resource_changing_cause_with_only_required_args(mocker): assert cause.resource is resource assert cause.logger is logger assert cause.reason is reason - assert cause.event is reason # deprecated assert cause.initial is initial assert cause.body is body assert cause.patch is patch @@ -111,3 +112,6 @@ def test_resource_changing_cause_with_only_required_args(mocker): assert not cause.diff assert cause.old is None assert cause.new is None + + with pytest.deprecated_call(match=r"use cause.reason"): + assert cause.event is reason diff --git a/tests/basic-structs/test_handlers.py b/tests/basic-structs/test_handlers.py index 2d38772b..56437c5c 100644 --- a/tests/basic-structs/test_handlers.py +++ b/tests/basic-structs/test_handlers.py @@ -33,7 +33,6 @@ def test_activity_handler_with_all_args(mocker): assert handler.timeout is timeout assert handler.retries is retries assert handler.backoff is backoff - assert handler.cooldown is backoff # deprecated alias assert handler.activity is activity @@ -68,14 +67,18 @@ def test_resource_handler_with_all_args(mocker): assert handler.fn is fn assert handler.id is id assert handler.reason is reason - assert handler.event is reason # deprecated assert handler.field is field assert handler.errors is errors assert handler.timeout is timeout assert handler.retries is retries assert handler.backoff is backoff - assert handler.cooldown is backoff # deprecated alias assert handler.initial is initial assert handler.labels is labels assert handler.annotations is annotations assert handler.requires_finalizer is requires_finalizer + + with pytest.deprecated_call(match=r"use handler.reason"): + assert handler.event is reason + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handler.cooldown is backoff diff --git a/tests/basic-structs/test_handlers_deprecated_cooldown.py b/tests/basic-structs/test_handlers_deprecated_cooldown.py index 3a54a67f..b9dc5a96 100644 --- a/tests/basic-structs/test_handlers_deprecated_cooldown.py +++ b/tests/basic-structs/test_handlers_deprecated_cooldown.py @@ -1,4 +1,6 @@ # Original test-file: tests/basic-structs/test_handlers.py +import pytest + from kopf.reactor.handlers import ActivityHandler, ResourceHandler @@ -10,25 +12,30 @@ def test_activity_handler_with_deprecated_cooldown_instead_of_backoff(mocker): retries = mocker.Mock() backoff = mocker.Mock() activity = mocker.Mock() - handler = ActivityHandler( - fn=fn, - id=id, - errors=errors, - timeout=timeout, - retries=retries, - backoff=None, - cooldown=backoff, # deprecated, but still required - activity=activity, - ) + + with pytest.deprecated_call(match=r"use backoff="): + handler = ActivityHandler( + fn=fn, + id=id, + errors=errors, + timeout=timeout, + retries=retries, + backoff=None, + cooldown=backoff, # deprecated, but still required + activity=activity, + ) + assert handler.fn is fn assert handler.id is id assert handler.errors is errors assert handler.timeout is timeout assert handler.retries is retries assert handler.backoff is backoff - assert handler.cooldown is backoff # deprecated alias assert handler.activity is activity + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handler.cooldown is backoff + def test_resource_handler_with_deprecated_cooldown_instead_of_backoff(mocker): fn = mocker.Mock() @@ -43,31 +50,32 @@ def test_resource_handler_with_deprecated_cooldown_instead_of_backoff(mocker): labels = mocker.Mock() annotations = mocker.Mock() requires_finalizer = mocker.Mock() - handler = ResourceHandler( - fn=fn, - id=id, - reason=reason, - field=field, - errors=errors, - timeout=timeout, - retries=retries, - backoff=None, - cooldown=backoff, # deprecated, but still required - initial=initial, - labels=labels, - annotations=annotations, - requires_finalizer=requires_finalizer, - ) + + with pytest.deprecated_call(match=r"use backoff="): + handler = ResourceHandler( + fn=fn, + id=id, + reason=reason, + field=field, + errors=errors, + timeout=timeout, + retries=retries, + backoff=None, + cooldown=backoff, # deprecated, but still required + initial=initial, + labels=labels, + annotations=annotations, + requires_finalizer=requires_finalizer, + ) + assert handler.fn is fn assert handler.id is id assert handler.reason is reason - assert handler.event is reason # deprecated assert handler.field is field assert handler.errors is errors assert handler.timeout is timeout assert handler.retries is retries assert handler.backoff is backoff - assert handler.cooldown is backoff # deprecated alias assert handler.initial is initial assert handler.labels is labels assert handler.annotations is annotations diff --git a/tests/conftest.py b/tests/conftest.py index ff7f521b..6e5fe5cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,6 +26,9 @@ def pytest_configure(config): config.addinivalue_line('markers', "e2e: end-to-end tests with real operators.") config.addinivalue_line('markers', "resource_clustered: (internal parameterizatiom mark).") + # Unexpected warnings should fail the tests. Use `-Wignore` to explicitly disable it. + config.addinivalue_line('filterwarnings', 'error') + def pytest_addoption(parser): parser.addoption("--only-e2e", action="store_true", help="Execute end-to-end tests only.") @@ -482,14 +485,21 @@ def logstream(caplog): logger = logging.getLogger() handlers = list(logger.handlers) + # Setup all log levels of sub-libraries. A sife-effect: the handlers are also added. configure(verbose=True) + # Remove any stream handlers added in the step above. But keep the caplog's handlers. + for handler in list(logger.handlers): + if isinstance(handler, logging.StreamHandler) and handler.stream is sys.stderr: + logger.removeHandler(handler) + + # Inject our stream-intercepting handler. stream = io.StringIO() handler = logging.StreamHandler(stream) formatter = ObjectPrefixingFormatter('prefix %(message)s') handler.setFormatter(formatter) - logger.addHandler(handler) + try: with caplog.at_level(logging.DEBUG): yield stream diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 6458bf1d..1d532f9d 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -18,19 +18,23 @@ def exampledir(request): @pytest.fixture() def with_crd(): - subprocess.run("kubectl apply -f examples/crd.yaml", shell=True, check=True) + subprocess.run("kubectl apply -f examples/crd.yaml", + shell=True, check=True, timeout=10, capture_output=True) @pytest.fixture() def with_peering(): - subprocess.run("kubectl apply -f peering.yaml", shell=True, check=True) + subprocess.run("kubectl apply -f peering.yaml", + shell=True, check=True, timeout=10, capture_output=True) @pytest.fixture() def no_crd(): - subprocess.run("kubectl delete customresourcedefinition kopfexamples.zalando.org", shell=True, check=True) + subprocess.run("kubectl delete customresourcedefinition kopfexamples.zalando.org", + shell=True, check=True, timeout=10, capture_output=True) @pytest.fixture() def no_peering(): - subprocess.run("kubectl delete customresourcedefinition kopfpeerings.zalando.org", shell=True, check=True) + subprocess.run("kubectl delete customresourcedefinition kopfpeerings.zalando.org", + shell=True, check=True, timeout=10, capture_output=True) diff --git a/tests/e2e/test_examples.py b/tests/e2e/test_examples.py index 68f0bd7e..d067a05c 100644 --- a/tests/e2e/test_examples.py +++ b/tests/e2e/test_examples.py @@ -56,13 +56,15 @@ def test_all_examples_are_runnable(mocker, with_crd, exampledir, caplog): patterns=e2e_startup_stop_words or ['Client is configured']) # Trigger the reaction. Give it some time to react and to sleep and to retry. - subprocess.run("kubectl apply -f examples/obj.yaml", shell=True, check=True) + subprocess.run("kubectl apply -f examples/obj.yaml", + shell=True, check=True, timeout=10, capture_output=True) _sleep_till_stopword(caplog=caplog, delay=e2e_creation_time_limit, patterns=e2e_creation_stop_words) # Trigger the reaction. Give it some time to react. - subprocess.run("kubectl delete -f examples/obj.yaml", shell=True, check=True) + subprocess.run("kubectl delete -f examples/obj.yaml", + shell=True, check=True, timeout=10, capture_output=True) _sleep_till_stopword(caplog=caplog, delay=e2e_deletion_time_limit, patterns=e2e_deletion_stop_words) diff --git a/tests/posting/test_threadsafety.py b/tests/posting/test_threadsafety.py index 968244b8..c2604396 100644 --- a/tests/posting/test_threadsafety.py +++ b/tests/posting/test_threadsafety.py @@ -52,7 +52,7 @@ def awakener(event_loop): handles = [] - async def noop(): + def noop(): pass def awaken_fn(delay, fn=noop): diff --git a/tests/primitives/test_toggles.py b/tests/primitives/test_toggles.py index 9516f9ef..8b0ba82b 100644 --- a/tests/primitives/test_toggles.py +++ b/tests/primitives/test_toggles.py @@ -1,4 +1,5 @@ import asyncio +import contextlib import pytest @@ -13,8 +14,9 @@ async def test_creation_with_default_loop(): async def test_creation_with_explicit_loop(): loop = asyncio.new_event_loop() - toggle = Toggle(loop=loop) - assert toggle.loop is loop + with contextlib.closing(loop): + toggle = Toggle(loop=loop) + assert toggle.loop is loop async def test_created_as_off(): diff --git a/tests/registries/legacy/test_legacy_decorators.py b/tests/registries/legacy/test_legacy_decorators.py index 2baa749a..590c55ca 100644 --- a/tests/registries/legacy/test_legacy_decorators.py +++ b/tests/registries/legacy/test_legacy_decorators.py @@ -16,7 +16,9 @@ def test_on_create_minimal(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.CREATE @@ -36,7 +38,9 @@ def test_on_update_minimal(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.UPDATE @@ -56,7 +60,9 @@ def test_on_delete_minimal(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.DELETE @@ -77,7 +83,9 @@ def test_on_field_minimal(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason is None @@ -111,7 +119,9 @@ def test_on_create_with_all_kwargs(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.CREATE @@ -138,7 +148,9 @@ def test_on_update_with_all_kwargs(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.UPDATE @@ -170,7 +182,9 @@ def test_on_delete_with_all_kwargs(mocker, optional): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason == Reason.DELETE @@ -199,7 +213,9 @@ def test_on_field_with_all_kwargs(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].reason is None @@ -221,7 +237,9 @@ def test_subhandler_declaratively(mocker): def fn(**_): pass - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use ResourceChangingRegistry.get_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn @@ -236,6 +254,8 @@ def fn(**_): pass kopf.register(fn) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use ResourceChangingRegistry.get_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) + assert len(handlers) == 1 assert handlers[0].fn is fn diff --git a/tests/registries/legacy/test_legacy_global_registry.py b/tests/registries/legacy/test_legacy_global_registry.py index 49e02180..39bd4f41 100644 --- a/tests/registries/legacy/test_legacy_global_registry.py +++ b/tests/registries/legacy/test_legacy_global_registry.py @@ -1,5 +1,7 @@ import collections +import pytest + from kopf import GlobalRegistry from kopf.structs.resources import Resource @@ -11,8 +13,11 @@ def some_fn(): def test_resources(): registry = GlobalRegistry() - registry.register_cause_handler('group1', 'version1', 'plural1', some_fn) - registry.register_cause_handler('group2', 'version2', 'plural2', some_fn) + + with pytest.deprecated_call(match=r"use OperatorRegistry.register_resource_changing_handler"): + registry.register_cause_handler('group1', 'version1', 'plural1', some_fn) + with pytest.deprecated_call(match=r"use OperatorRegistry.register_resource_changing_handler"): + registry.register_cause_handler('group2', 'version2', 'plural2', some_fn) resources = registry.resources diff --git a/tests/registries/legacy/test_legacy_handler_matching.py b/tests/registries/legacy/test_legacy_handler_matching.py index 9b0b4e5b..b7e9e93a 100644 --- a/tests/registries/legacy/test_legacy_handler_matching.py +++ b/tests/registries/legacy/test_legacy_handler_matching.py @@ -22,10 +22,12 @@ def registry(request): @pytest.fixture() def register_fn(registry, resource): if isinstance(registry, SimpleRegistry): - return registry.register - if isinstance(registry, GlobalRegistry): - return functools.partial(registry.register_cause_handler, resource.group, resource.version, resource.plural) - raise Exception(f"Unsupported registry type: {registry}") + yield registry.register + elif isinstance(registry, GlobalRegistry): + with pytest.deprecated_call(match=r"GlobalRegistry.register_cause_handler\(\) is deprecated"): + yield functools.partial(registry.register_cause_handler, resource.group, resource.version, resource.plural) + else: + raise Exception(f"Unsupported registry type: {registry}") @pytest.fixture(params=[ @@ -62,19 +64,22 @@ def cause_any_diff(resource, request): def test_catchall_handlers_without_field_found(cause_any_diff, registry, register_fn): register_fn(some_fn, reason=None, field=None) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert handlers def test_catchall_handlers_with_field_found(cause_with_diff, registry, register_fn): register_fn(some_fn, reason=None, field='some-field') - handlers = registry.get_cause_handlers(cause_with_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_with_diff) assert handlers def test_catchall_handlers_with_field_ignored(cause_no_diff, registry, register_fn): register_fn(some_fn, reason=None, field='some-field') - handlers = registry.get_cause_handlers(cause_no_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_no_diff) assert not handlers @@ -85,7 +90,8 @@ def test_catchall_handlers_with_field_ignored(cause_no_diff, registry, register_ def test_catchall_handlers_with_labels_satisfied(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -97,7 +103,8 @@ def test_catchall_handlers_with_labels_satisfied(registry, register_fn, resource def test_catchall_handlers_with_labels_not_satisfied(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert not handlers @@ -108,7 +115,8 @@ def test_catchall_handlers_with_labels_not_satisfied(registry, register_fn, reso def test_catchall_handlers_with_labels_exist(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': None}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -119,7 +127,8 @@ def test_catchall_handlers_with_labels_exist(registry, register_fn, resource, la def test_catchall_handlers_with_labels_not_exist(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': None}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert not handlers @@ -133,7 +142,8 @@ def test_catchall_handlers_with_labels_not_exist(registry, register_fn, resource def test_catchall_handlers_without_labels(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels=None) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -144,7 +154,8 @@ def test_catchall_handlers_without_labels(registry, register_fn, resource, label def test_catchall_handlers_with_annotations_satisfied(registry, register_fn, resource, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, annotations={'someannotation': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -156,7 +167,8 @@ def test_catchall_handlers_with_annotations_satisfied(registry, register_fn, res def test_catchall_handlers_with_annotations_not_satisfied(registry, register_fn, resource, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, annotations={'someannotation': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert not handlers @@ -167,7 +179,8 @@ def test_catchall_handlers_with_annotations_not_satisfied(registry, register_fn, def test_catchall_handlers_with_annotations_exist(registry, register_fn, resource, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, annotations={'someannotation': None}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -178,7 +191,8 @@ def test_catchall_handlers_with_annotations_exist(registry, register_fn, resourc def test_catchall_handlers_with_annotations_not_exist(registry, register_fn, resource, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, annotations={'someannotation': None}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert not handlers @@ -192,7 +206,8 @@ def test_catchall_handlers_with_annotations_not_exist(registry, register_fn, res def test_catchall_handlers_without_annotations(registry, register_fn, resource, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, annotations=None) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -205,7 +220,8 @@ def test_catchall_handlers_without_annotations(registry, register_fn, resource, def test_catchall_handlers_with_labels_and_annotations_satisfied(registry, register_fn, resource, labels, annotations): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels, 'annotations': annotations}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': 'somevalue'}, annotations={'someannotation': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert handlers @@ -219,7 +235,8 @@ def test_catchall_handlers_with_labels_and_annotations_satisfied(registry, regis def test_catchall_handlers_with_labels_and_annotations_not_satisfied(registry, register_fn, resource, labels): cause = Mock(resource=resource, reason='some-reason', diff=None, body={'metadata': {'labels': labels}}) register_fn(some_fn, reason=None, field=None, labels={'somelabel': 'somevalue'}, annotations={'someannotation': 'somevalue'}) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause) assert not handlers @@ -232,78 +249,91 @@ def test_catchall_handlers_with_labels_and_annotations_not_satisfied(registry, r def test_relevant_handlers_without_field_found(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='some-reason') - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert handlers def test_relevant_handlers_with_field_found(cause_with_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', field='some-field') - handlers = registry.get_cause_handlers(cause_with_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_with_diff) assert handlers def test_relevant_handlers_with_field_ignored(cause_no_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', field='some-field') - handlers = registry.get_cause_handlers(cause_no_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_no_diff) assert not handlers def test_relevant_handlers_with_labels_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', labels={'somelabel': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert handlers def test_relevant_handlers_with_labels_not_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', labels={'otherlabel': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_relevant_handlers_with_annotations_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', annotations={'someannotation': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert handlers def test_relevant_handlers_with_annotations_not_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='some-reason', annotations={'otherannotation': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_without_field_ignored(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason') - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_with_field_ignored(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason', field='another-field') - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_with_labels_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason', labels={'somelabel': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_with_labels_not_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason', labels={'otherlabel': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_with_annotations_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason', annotations={'someannotation': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers def test_irrelevant_handlers_with_annotations_not_satisfied(cause_any_diff, registry, register_fn): register_fn(some_fn, reason='another-reason', annotations={'otherannotation': None}) - handlers = registry.get_cause_handlers(cause_any_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_any_diff) assert not handlers @@ -319,7 +349,8 @@ def test_order_persisted_a(cause_with_diff, registry, register_fn): register_fn(functools.partial(some_fn, 4), reason=None, field='filtered-out-reason') register_fn(functools.partial(some_fn, 5), reason=None, field='some-field') - handlers = registry.get_cause_handlers(cause_with_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_with_diff) # Order must be preserved -- same as registered. assert len(handlers) == 3 @@ -338,7 +369,8 @@ def test_order_persisted_b(cause_with_diff, registry, register_fn): register_fn(functools.partial(some_fn, 4), reason='some-reason') register_fn(functools.partial(some_fn, 5), reason=None) - handlers = registry.get_cause_handlers(cause_with_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_with_diff) # Order must be preserved -- same as registered. assert len(handlers) == 3 @@ -358,7 +390,8 @@ def test_deduplicated(cause_with_diff, registry, register_fn): register_fn(some_fn, reason=None, id='a') register_fn(some_fn, reason=None, id='b') - handlers = registry.get_cause_handlers(cause_with_diff) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(cause_with_diff) assert len(handlers) == 1 assert handlers[0].id == 'a' # the first found one is returned diff --git a/tests/registries/legacy/test_legacy_id_detection.py b/tests/registries/legacy/test_legacy_id_detection.py index 2caf4ce4..2b49b9ea 100644 --- a/tests/registries/legacy/test_legacy_id_detection.py +++ b/tests/registries/legacy/test_legacy_id_detection.py @@ -76,7 +76,8 @@ def test_with_no_hints(mocker): registry = SimpleRegistry() registry.register(some_fn) - handlers = registry.get_cause_handlers(mocker.MagicMock()) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(mocker.MagicMock()) assert get_fn_id.called @@ -90,7 +91,8 @@ def test_with_prefix(mocker): registry = SimpleRegistry(prefix='some-prefix') registry.register(some_fn) - handlers = registry.get_cause_handlers(mocker.MagicMock()) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(mocker.MagicMock()) assert get_fn_id.called @@ -105,7 +107,8 @@ def test_with_suffix(mocker, field): registry = SimpleRegistry() registry.register(some_fn, field=field) - handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) assert get_fn_id.called @@ -120,7 +123,8 @@ def test_with_prefix_and_suffix(mocker, field): registry = SimpleRegistry(prefix='some-prefix') registry.register(some_fn, field=field) - handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) assert get_fn_id.called @@ -135,7 +139,8 @@ def test_with_explicit_id_and_prefix_and_suffix(mocker, field): registry = SimpleRegistry(prefix='some-prefix') registry.register(some_fn, id='explicit-id', field=field) - handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) + with pytest.deprecated_call(match=r"get_cause_handlers\(\) is deprecated"): + handlers = registry.get_cause_handlers(mocker.MagicMock(diff=diff)) assert not get_fn_id.called diff --git a/tests/registries/legacy/test_legacy_registering.py b/tests/registries/legacy/test_legacy_registering.py index 2d737a8a..d7810cef 100644 --- a/tests/registries/legacy/test_legacy_registering.py +++ b/tests/registries/legacy/test_legacy_registering.py @@ -1,5 +1,7 @@ import collections.abc +import pytest + from kopf import SimpleRegistry, GlobalRegistry @@ -19,7 +21,8 @@ def test_simple_registry_via_iter(mocker): assert not isinstance(iterator, collections.abc.Container) assert not isinstance(iterator, (list, tuple)) - handlers = list(iterator) + with pytest.deprecated_call(match=r"use ResourceChangingRegistry.iter_handlers\(\)"): + handlers = list(iterator) assert not handlers @@ -27,7 +30,8 @@ def test_simple_registry_via_list(mocker): cause = mocker.Mock(event=None, diff=None) registry = SimpleRegistry() - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use ResourceChangingRegistry.get_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) assert isinstance(handlers, collections.abc.Iterable) assert isinstance(handlers, collections.abc.Container) @@ -40,7 +44,8 @@ def test_simple_registry_with_minimal_signature(mocker): registry = SimpleRegistry() registry.register(some_fn) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use ResourceChangingRegistry.get_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is some_fn @@ -57,7 +62,8 @@ def test_global_registry_via_iter(mocker, resource): assert not isinstance(iterator, collections.abc.Container) assert not isinstance(iterator, (list, tuple)) - handlers = list(iterator) + with pytest.deprecated_call(match=r"use OperatorRegistry.iter_resource_changing_handlers\(\)"): + handlers = list(iterator) assert not handlers @@ -65,7 +71,8 @@ def test_global_registry_via_list(mocker, resource): cause = mocker.Mock(resource=resource, event=None, diff=None) registry = GlobalRegistry() - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) assert isinstance(handlers, collections.abc.Iterable) assert isinstance(handlers, collections.abc.Container) @@ -77,8 +84,10 @@ def test_global_registry_with_minimal_signature(mocker, resource): cause = mocker.Mock(resource=resource, event=None, diff=None) registry = GlobalRegistry() - registry.register_cause_handler(resource.group, resource.version, resource.plural, some_fn) - handlers = registry.get_cause_handlers(cause) + with pytest.deprecated_call(match=r"use OperatorRegistry.register_resource_changing_handler\(\)"): + registry.register_cause_handler(resource.group, resource.version, resource.plural, some_fn) + with pytest.deprecated_call(match=r"use OperatorRegistry.get_resource_changing_handlers\(\)"): + handlers = registry.get_cause_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is some_fn diff --git a/tests/registries/test_decorators.py b/tests/registries/test_decorators.py index 018f1cd7..4591300a 100644 --- a/tests/registries/test_decorators.py +++ b/tests/registries/test_decorators.py @@ -23,7 +23,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias def test_on_cleanup_minimal(): @@ -41,7 +40,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias def test_on_probe_minimal(): @@ -59,7 +57,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias # Resume handlers are mixed-in into all resource-changing reactions with initial listing. @@ -82,7 +79,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias assert handlers[0].labels is None assert handlers[0].annotations is None assert handlers[0].when is None @@ -106,7 +102,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias assert handlers[0].labels is None assert handlers[0].annotations is None assert handlers[0].when is None @@ -130,7 +125,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias assert handlers[0].labels is None assert handlers[0].annotations is None assert handlers[0].when is None @@ -154,7 +148,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias assert handlers[0].labels is None assert handlers[0].annotations is None assert handlers[0].when is None @@ -179,7 +172,6 @@ def fn(**_): assert handlers[0].timeout is None assert handlers[0].retries is None assert handlers[0].backoff is None - assert handlers[0].cooldown is None # deprecated alias assert handlers[0].labels is None assert handlers[0].annotations is None assert handlers[0].when is None @@ -210,7 +202,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias def test_on_cleanup_with_all_kwargs(mocker): @@ -231,7 +222,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias def test_on_probe_with_all_kwargs(mocker): @@ -252,7 +242,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias # Resume handlers are mixed-in into all resource-changing reactions with initial listing. @@ -285,7 +274,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias assert handlers[0].deleted == True assert handlers[0].labels == {'somelabel': 'somevalue'} assert handlers[0].annotations == {'someanno': 'somevalue'} @@ -319,7 +307,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias assert handlers[0].labels == {'somelabel': 'somevalue'} assert handlers[0].annotations == {'someanno': 'somevalue'} assert handlers[0].when == when @@ -352,7 +339,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias assert handlers[0].labels == {'somelabel': 'somevalue'} assert handlers[0].annotations == {'someanno': 'somevalue'} assert handlers[0].when == when @@ -390,7 +376,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias assert handlers[0].labels == {'somelabel': 'somevalue'} assert handlers[0].annotations == {'someanno': 'somevalue'} assert handlers[0].when == when @@ -424,7 +409,6 @@ def fn(**_): assert handlers[0].timeout == 123 assert handlers[0].retries == 456 assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias assert handlers[0].labels == {'somelabel': 'somevalue'} assert handlers[0].annotations == {'someanno': 'somevalue'} assert handlers[0].when == when diff --git a/tests/registries/test_decorators_deprecated_cooldown.py b/tests/registries/test_decorators_deprecated_cooldown.py index 116c3be1..6f921225 100644 --- a/tests/registries/test_decorators_deprecated_cooldown.py +++ b/tests/registries/test_decorators_deprecated_cooldown.py @@ -8,43 +8,52 @@ def test_on_startup_with_cooldown(): registry = kopf.get_default_registry() - @kopf.on.startup(cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.startup(cooldown=78) + def fn(**_): + pass handlers = registry.get_activity_handlers(activity=Activity.STARTUP) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 def test_on_cleanup_with_cooldown(): registry = kopf.get_default_registry() - @kopf.on.cleanup(cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.cleanup(cooldown=78) + def fn(**_): + pass handlers = registry.get_activity_handlers(activity=Activity.CLEANUP) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 def test_on_probe_with_cooldown(): registry = kopf.get_default_registry() - @kopf.on.probe(cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.probe(cooldown=78) + def fn(**_): + pass handlers = registry.get_activity_handlers(activity=Activity.PROBE) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 # Resume handlers are mixed-in into all resource-changing reactions with initial listing. @@ -55,15 +64,18 @@ def test_on_resume_with_cooldown(mocker, reason): cause = mocker.MagicMock(resource=resource, reason=reason, initial=True, deleted=False) mocker.patch('kopf.reactor.registries.match', return_value=True) - @kopf.on.resume('group', 'version', 'plural', cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.resume('group', 'version', 'plural', cooldown=78) + def fn(**_): + pass handlers = registry.get_resource_changing_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 def test_on_create_with_cooldown(mocker): @@ -72,15 +84,18 @@ def test_on_create_with_cooldown(mocker): cause = mocker.MagicMock(resource=resource, reason=Reason.CREATE) mocker.patch('kopf.reactor.registries.match', return_value=True) - @kopf.on.create('group', 'version', 'plural', cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.create('group', 'version', 'plural', cooldown=78) + def fn(**_): + pass handlers = registry.get_resource_changing_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 def test_on_update_with_cooldown(mocker): @@ -89,15 +104,18 @@ def test_on_update_with_cooldown(mocker): cause = mocker.MagicMock(resource=resource, reason=Reason.UPDATE) mocker.patch('kopf.reactor.registries.match', return_value=True) - @kopf.on.update('group', 'version', 'plural', cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.update('group', 'version', 'plural', cooldown=78) + def fn(**_): + pass handlers = registry.get_resource_changing_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 @pytest.mark.parametrize('optional', [ @@ -110,15 +128,18 @@ def test_on_delete_with_cooldown(mocker, optional): cause = mocker.MagicMock(resource=resource, reason=Reason.DELETE) mocker.patch('kopf.reactor.registries.match', return_value=True) - @kopf.on.delete('group', 'version', 'plural', cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.delete('group', 'version', 'plural', cooldown=78) + def fn(**_): + pass handlers = registry.get_resource_changing_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78 def test_on_field_with_cooldown(mocker): @@ -128,12 +149,15 @@ def test_on_field_with_cooldown(mocker): cause = mocker.MagicMock(resource=resource, reason=Reason.UPDATE, diff=diff) mocker.patch('kopf.reactor.registries.match', return_value=True) - @kopf.on.field('group', 'version', 'plural', 'field.subfield', cooldown=78) - def fn(**_): - pass + with pytest.deprecated_call(match=r"use backoff="): + @kopf.on.field('group', 'version', 'plural', 'field.subfield', cooldown=78) + def fn(**_): + pass handlers = registry.get_resource_changing_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn assert handlers[0].backoff == 78 - assert handlers[0].cooldown == 78 # deprecated alias + + with pytest.deprecated_call(match=r"use handler.backoff"): + assert handlers[0].cooldown == 78