From 3500ac5c37ec0547ae6d293d51df53b0424c916d Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 02:24:00 +0100 Subject: [PATCH 1/8] Make qualname so names are valid identifiers. --- lib/iris/common/lenient.py | 2 +- .../tests/unit/common/lenient/test_lenient_client.py | 5 +++-- .../tests/unit/common/lenient/test_lenient_service.py | 3 ++- lib/iris/tests/unit/common/lenient/test_qualname.py | 9 +++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index 20e5634694..5e560d3689 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -214,7 +214,7 @@ def qualname(func): if callable(func): module = getmodule(func) result = f"{module.__name__}.{func.__qualname__}" - + result = result.replace(".", "_x_") return result diff --git a/lib/iris/tests/unit/common/lenient/test_lenient_client.py b/lib/iris/tests/unit/common/lenient/test_lenient_client.py index 45370a0dd7..e53588db96 100644 --- a/lib/iris/tests/unit/common/lenient/test_lenient_client.py +++ b/lib/iris/tests/unit/common/lenient/test_lenient_client.py @@ -21,8 +21,9 @@ class Test(tests.IrisTest): def setUp(self): module_name = getmodule(self).__name__ - self.client = f"{module_name}" + ".Test.{}..myclient" - self.service = f"{module_name}" + ".Test.{}..myservice" + module_name = module_name.replace(".", "_x_") + self.client = f"{module_name}" + "_x_Test_x_{}_x__x_myclient" + self.service = f"{module_name}" + "_x_Test_x_{}_x__x_myservice" self.active = "active" self.args_in = sentinel.arg1, sentinel.arg2 self.kwargs_in = dict(kwarg1=sentinel.kwarg1, kwarg2=sentinel.kwarg2) diff --git a/lib/iris/tests/unit/common/lenient/test_lenient_service.py b/lib/iris/tests/unit/common/lenient/test_lenient_service.py index 1124b0d01f..9d3ee493ff 100644 --- a/lib/iris/tests/unit/common/lenient/test_lenient_service.py +++ b/lib/iris/tests/unit/common/lenient/test_lenient_service.py @@ -21,7 +21,8 @@ class Test(tests.IrisTest): def setUp(self): module_name = getmodule(self).__name__ - self.service = f"{module_name}" + ".Test.{}..myservice" + module_name = module_name.replace(".", "_x_") + self.service = f"{module_name}" + "_x_Test_x_{}_x__x_myservice" self.args_in = sentinel.arg1, sentinel.arg2 self.kwargs_in = dict(kwarg1=sentinel.kwarg1, kwarg2=sentinel.kwarg2) diff --git a/lib/iris/tests/unit/common/lenient/test_qualname.py b/lib/iris/tests/unit/common/lenient/test_qualname.py index a5622873d0..5d8675574b 100644 --- a/lib/iris/tests/unit/common/lenient/test_qualname.py +++ b/lib/iris/tests/unit/common/lenient/test_qualname.py @@ -21,7 +21,8 @@ class Test(tests.IrisTest): def setUp(self): module_name = getmodule(self).__name__ - self.locals = f"{module_name}" + ".Test.{}..{}" + module_name = module_name.replace(".", "_x_") + self.locals = f"{module_name}" + "_x_Test_x_{}_x__x_{}" def test_pass_thru_non_callable(self): func = sentinel.func @@ -42,7 +43,7 @@ def test_callable_function(self): import iris result = qualname(iris.load) - self.assertEqual(result, "iris.load") + self.assertEqual(result, "iris_x_load") def test_callable_method_local(self): class MyClass: @@ -50,7 +51,7 @@ def mymethod(self): pass qualname_method = self.locals.format( - "test_callable_method_local", "MyClass.mymethod" + "test_callable_method_local", "MyClass_x_mymethod" ) result = qualname(MyClass.mymethod) self.assertEqual(result, qualname_method) @@ -59,7 +60,7 @@ def test_callable_method(self): import iris result = qualname(iris.cube.Cube.add_ancillary_variable) - self.assertEqual(result, "iris.cube.Cube.add_ancillary_variable") + self.assertEqual(result, "iris_x_cube_x_Cube_x_add_ancillary_variable") if __name__ == "__main__": From da4f737c5b47e66aa9d900f48617617d3bed8ef3 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 02:15:54 +0100 Subject: [PATCH 2/8] wkg with storage changes; almost no behaviour changes. --- lib/iris/common/lenient.py | 135 +++++++++++++++--- .../tests/unit/common/lenient/test_Lenient.py | 92 ++++++++---- .../common/lenient/test_lenient_client.py | 12 +- 3 files changed, 185 insertions(+), 54 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index 5e560d3689..8f61602f16 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -276,11 +276,8 @@ def __call__(self, func): active = self.__dict__["active"] if active is not None and active in self: services = self.__dict__[active] - if isinstance(services, str) or not isinstance( - services, Iterable - ): - services = (services,) - result = service in services + found = [val for svc, val in services if svc == service] + result = found[0] if found else False return result def __contains__(self, name): @@ -356,11 +353,18 @@ def context(self, *args, **kwargs): def update_client(client, services): if client in self.__dict__: - existing_services = self.__dict__[client] + # Convert existing set of pairs to dict + new_services = {svc: val for svc, val in self.__dict__[client]} else: - existing_services = () - - self.__dict__[client] = tuple(set(existing_services + services)) + new_services = {} + + # Update dict with new settings. + if not hasattr(services, "keys"): + services = {svc: True for svc in services} + new_services.update(services) + self.__dict__[client] = set( + (svc, val) for svc, val in new_services.items() + ) # Save the original state. original_state = deepcopy(self.__dict__) @@ -376,6 +380,83 @@ def update_client(client, services): # Update the client with the provided services. new_services = tuple([qualname(arg) for arg in args]) + if active is None: + # Ensure not to use "context" as the ephemeral name + # of the context manager runtime "active" lenient client, + # as this causes a namespace clash with this method + # i.e., Lenient.context, via Lenient.__getattr__ + active = "__context" + self.__dict__["active"] = active + # self.__dict__[active] = new_services + update_client(active, new_services) + + else: + # Append provided services to any pre-existing services of the active client. + update_client(active, new_services) + else: + # Append previous ephemeral services (for non-specific client) to the active client. + if ( + active is not None + and active != "__context" + and "__context" in self.__dict__ + ): + new_services = self.__dict__["__context"] + update_client(active, new_services) + + try: + yield + finally: + # Restore the original state. + self.__dict__.clear() + self.__dict__.update(original_state) + + @contextmanager + def context2( + self, active=None, services=None, enable=None, **service_values + ): + """ + Return a context manager which allows temporary modification of + the lenient option state for the active thread. + + For example:: + with iris.LENIENT.context(client, (srv1, srv2)): + # ... code that expects some lenient behaviour + + with iris.LENIENT.context(client, srv1=True, srv2=True): + # ... code that expects some lenient behaviour + + with iris.LENIENT.context(client, srv2=False, modify_existing=True): + # ... code that expects some non-lenient behaviour + + """ + + def update_client(client, services): + if client in self.__dict__: + existing_services = self.__dict__[client] + else: + existing_services = () + + self.__dict__[client] = tuple(set(existing_services + services)) + + # Save the original state. + original_state = deepcopy(self.__dict__) + + # First update the state with the fixed keyword controls. + if active is not None: + self.active = active + if enable is not None: + self.enable = enable + + # Get the active client. + active = self.__dict__["active"] + + if services or service_values: + # Update the client with the provided services. + new_services = {qualname(srv): True for srv in services} + new_services.update( + {qualname(srv): val for srv, val in service_values.items()} + ) + if active is None: # Ensure not to use "context" as the ephemeral name # of the context manager runtime "active" lenient client, @@ -429,14 +510,14 @@ def enable(self, state): raise ValueError(emsg) self.__dict__["enable"] = state - def register_client(self, func, services, append=False): + def register_client(self, client, services, append=False): """ Add the provided mapping of lenient client function/method to required lenient service function/methods. Args: - * func (callable or str): + * client (callable or str): A client function/method or fully qualified string name of the client function/method. @@ -451,26 +532,40 @@ def register_client(self, func, services, append=False): services for the provided lenient client. Default is False. """ - func = qualname(func) + client = qualname(client) cls = self.__class__.__name__ - if func in LENIENT_PROTECTED: - emsg = ( - f"Cannot register {cls!r} protected non-client, got {func!r}." - ) + if client in LENIENT_PROTECTED: + emsg = f"Cannot register {cls!r} protected non-client, got {client!r}." raise ValueError(emsg) + if isinstance(services, str) or not isinstance(services, Iterable): services = (services,) if not len(services): emsg = f"Require at least one {cls!r} lenient client service." raise ValueError(emsg) - services = tuple([qualname(service) for service in services]) + + if hasattr(services, "items"): + # Dictionary form contains setting values e.g. {'x':1, 'y':2}. + services = { + qualname(service): value for service, value in services.items() + } + else: + # List form (x, y) is equivalent to {x:True. y:True} + services = {qualname(service): True for service in services} + if append: # Service order is not significant, therefore there is no # requirement to preserve it. - existing = self.__dict__[func] if func in self else () - services = tuple(sorted(set(existing) | set(services))) - self.__dict__[func] = services + existing = self.__dict__[client] if client in self else () + updated = dict(existing) + updated.update(services) + services = updated + + # Convert dictionary to a set of pairs. + services = set((svc, services[svc]) for svc in sorted(services.keys())) + # N.B. must be in standard order?? + self.__dict__[client] = services def register_service(self, func): """ diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index 5c721dab51..24c6f02dd4 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -59,14 +59,17 @@ def service2(): def test_kwargs_client_str(self): client = dict(client1="service1") lenient = Lenient(**client) - self.expected.update(dict(client1=("service1",))) + self.expected.update(dict(client1=set([("service1", True)]))) self.assertEqual(self.expected, lenient.__dict__) def test_kwargs_clients_str(self): clients = dict(client1="service1", client2="service2") lenient = Lenient(**clients) self.expected.update( - dict(client1=("service1",), client2=("service2",)) + dict( + client1=set([("service1", True)]), + client2=set([("service2", True)]), + ) ) self.assertEqual(self.expected, lenient.__dict__) @@ -92,8 +95,10 @@ def service2(): lenient = Lenient(**clients) self.expected.update( { - qualname(client1): (qualname(service1),), - qualname(client2): (qualname(service1), qualname(service2)), + qualname(client1): set([(qualname(service1), True)]), + qualname(client2): set( + [(qualname(service1), True), (qualname(service2), True)] + ), } ) self.assertEqual(self.expected, lenient.__dict__) @@ -165,7 +170,9 @@ def test_service_str_with_active_client_with_unmatched_registered_services( service = "myservice" self.lenient.__dict__[service] = True self.lenient.__dict__["active"] = self.client - self.lenient.__dict__[self.client] = ("service1", "service2") + self.lenient.__dict__[self.client] = set( + [("service1", True), ("service2", True)] + ) self.assertFalse(self.lenient(service)) def test_service_callable_with_active_client_with_unmatched_registered_services( @@ -181,14 +188,18 @@ def myclient(): qualname_client = qualname(myclient) self.lenient.__dict__[qualname_service] = True self.lenient.__dict__["active"] = qualname_client - self.lenient.__dict__[qualname_client] = ("service1", "service2") + self.lenient.__dict__[qualname_client] = set( + [("service1", True), ("service2", True)] + ) self.assertFalse(self.lenient(myservice)) def test_service_str_with_active_client_with_registered_services(self): service = "myservice" self.lenient.__dict__[service] = True self.lenient.__dict__["active"] = self.client - self.lenient.__dict__[self.client] = ("service1", "service2", service) + self.lenient.__dict__[self.client] = set( + [("service1", True), ("service2", True), (service, True),] + ) self.assertTrue(self.lenient(service)) def test_service_callable_with_active_client_with_registered_services( @@ -204,10 +215,8 @@ def myclient(): qualname_client = qualname(myclient) self.lenient.__dict__[qualname_service] = True self.lenient.__dict__["active"] = qualname_client - self.lenient.__dict__[qualname_client] = ( - "service1", - "service2", - qualname_service, + self.lenient.__dict__[qualname_client] = set( + [(qualname_service, True), ("service1", True), ("service2", True),] ) self.assertTrue(self.lenient(myservice)) @@ -217,7 +226,7 @@ def test_service_str_with_active_client_with_unmatched_registered_service_str( service = "myservice" self.lenient.__dict__[service] = True self.lenient.__dict__["active"] = self.client - self.lenient.__dict__[self.client] = "serviceXXX" + self.lenient.__dict__[self.client] = set([("serviceXXX", True)]) self.assertFalse(self.lenient(service)) def test_service_callable_with_active_client_with_unmatched_registered_service_str( @@ -233,14 +242,16 @@ def myclient(): qualname_client = qualname(myclient) self.lenient.__dict__[qualname_service] = True self.lenient.__dict__["active"] = qualname_client - self.lenient.__dict__[qualname_client] = f"{qualname_service}XXX" + self.lenient.__dict__[qualname_client] = set( + [(f"{qualname_service}XXX", True)] + ) self.assertFalse(self.lenient(myservice)) def test_service_str_with_active_client_with_registered_service_str(self): service = "myservice" self.lenient.__dict__[service] = True self.lenient.__dict__["active"] = self.client - self.lenient.__dict__[self.client] = service + self.lenient.__dict__[self.client] = set([(service, True)]) self.assertTrue(self.lenient(service)) def test_service_callable_with_active_client_with_registered_service_str( @@ -256,14 +267,16 @@ def myclient(): qualname_client = qualname(myclient) self.lenient.__dict__[qualname_service] = True self.lenient.__dict__["active"] = qualname_client - self.lenient.__dict__[qualname_client] = qualname_service + self.lenient.__dict__[qualname_client] = set( + [(qualname_service, True)] + ) self.assertTrue(self.lenient(myservice)) def test_enable(self): service = "myservice" self.lenient.__dict__[service] = True self.lenient.__dict__["active"] = self.client - self.lenient.__dict__[self.client] = service + self.lenient.__dict__[self.client] = set([(service, True)]) self.assertTrue(self.lenient(service)) self.lenient.__dict__["enable"] = False self.assertFalse(self.lenient(service)) @@ -547,7 +560,9 @@ def test_args_str(self): post = self.copy() self.assertEqual(pre, self.default) expected = self.default.copy() - expected.update(dict(active=client, client=services)) + expected.update( + dict(active=client, client=set((svc, True) for svc in services)) + ) self.assertEqual(context["active"], expected["active"]) self.assertEqual(set(context["client"]), set(expected["client"])) self.assertEqual(post, self.default) @@ -568,7 +583,12 @@ def service2(): qualname_services = tuple([qualname(service) for service in services]) self.assertEqual(pre, self.default) expected = self.default.copy() - expected.update(dict(active=client, client=qualname_services)) + expected.update( + dict( + active=client, + client=set((svc, True) for svc in qualname_services), + ) + ) self.assertEqual(context["active"], expected["active"]) self.assertEqual(set(context["client"]), set(expected["client"])) self.assertEqual(post, self.default) @@ -581,7 +601,12 @@ def test_context_runtime(self): post = self.copy() self.assertEqual(pre, self.default) expected = self.default.copy() - expected.update(dict(active="__context", __context=services)) + expected.update( + dict( + active="__context", + __context=set([(srv, True) for srv in services]), + ) + ) self.assertEqual(context, expected) self.assertEqual(post, self.default) @@ -616,17 +641,19 @@ def test_not_protected(self): def test_str_service_str(self): client = "client" - services = "service" - self.lenient.register_client(client, services) + service = "service" + self.lenient.register_client(client, service) self.assertIn(client, self.lenient.__dict__) - self.assertEqual(self.lenient.__dict__[client], (services,)) + self.assertEqual(self.lenient.__dict__[client], set([(service, True)])) def test_str_services_str(self): client = "client" services = ("service1", "service2") self.lenient.register_client(client, services) self.assertIn(client, self.lenient.__dict__) - self.assertEqual(self.lenient.__dict__[client], services) + self.assertEqual( + self.lenient.__dict__[client], set((srv, True) for srv in services) + ) def test_callable_service_callable(self): def client(): @@ -640,7 +667,8 @@ def service(): self.lenient.register_client(client, service) self.assertIn(qualname_client, self.lenient.__dict__) self.assertEqual( - self.lenient.__dict__[qualname_client], (qualname_service,) + self.lenient.__dict__[qualname_client], + set([(qualname_service, True)]), ) def test_callable_services_callable(self): @@ -658,7 +686,8 @@ def service2(): self.lenient.register_client(client, (service1, service2)) self.assertIn(qualname_client, self.lenient.__dict__) self.assertEqual( - self.lenient.__dict__[qualname_client], qualname_services + self.lenient.__dict__[qualname_client], + set((srv, True) for srv in qualname_services), ) def test_services_empty(self): @@ -673,18 +702,21 @@ def test_services_overwrite(self): self.assertEqual(self.lenient[client], services) new_services = ("service3", "service4") self.lenient.register_client(client, services=new_services) - self.assertEqual(self.lenient[client], new_services) + self.assertEqual( + self.lenient[client], set((srv, True) for srv in new_services) + ) def test_services_append(self): client = "client" - services = ("service1", "service2") - self.lenient.__dict__[client] = services - self.assertEqual(self.lenient[client], services) + services = ("service1", "service2") # old style + services_set = set((srv, True) for srv in services) + self.lenient.__dict__[client] = services_set + self.assertEqual(self.lenient[client], services_set) new_services = ("service3", "service4") self.lenient.register_client( client, services=new_services, append=True ) - expected = set(services + new_services) + expected = set((srv, True) for srv in services + new_services) self.assertEqual(set(self.lenient[client]), expected) diff --git a/lib/iris/tests/unit/common/lenient/test_lenient_client.py b/lib/iris/tests/unit/common/lenient/test_lenient_client.py index e53588db96..9f76b437f3 100644 --- a/lib/iris/tests/unit/common/lenient/test_lenient_client.py +++ b/lib/iris/tests/unit/common/lenient/test_lenient_client.py @@ -129,7 +129,7 @@ def myclient(): qualname_client = self.client.format("test_call_kwargs_single") self.assertEqual(result[self.active], qualname_client) self.assertIn(qualname_client, result) - self.assertEqual(result[qualname_client], (service,)) + self.assertEqual(result[qualname_client], set([(service, True)])) def test_call_kwargs_single_callable(self): def myservice(): @@ -145,8 +145,10 @@ def myclient(): qualname_client = self.client.format(test_name) self.assertEqual(result[self.active], qualname_client) self.assertIn(qualname_client, result) - qualname_services = (self.service.format(test_name),) - self.assertEqual(result[qualname_client], qualname_services) + qualname_service = self.service.format(test_name) + self.assertEqual( + result[qualname_client], set([(qualname_service, True)]) + ) def test_call_kwargs_iterable(self): services = (sentinel.service1, sentinel.service2) @@ -160,7 +162,9 @@ def myclient(): qualname_client = self.client.format("test_call_kwargs_iterable") self.assertEqual(result[self.active], qualname_client) self.assertIn(qualname_client, result) - self.assertEqual(set(result[qualname_client]), set(services)) + self.assertEqual( + result[qualname_client], set((svc, True) for svc in services) + ) def test_call_client_args_kwargs(self): @lenient_client() From ed66460d33e62d08e4cda25ca7036cc3a19760a5 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 02:41:31 +0100 Subject: [PATCH 3/8] Small simplifies. --- lib/iris/common/lenient.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index 8f61602f16..522eacd8e4 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -374,7 +374,7 @@ def update_client(client, services): self[name] = value # Get the active client. - active = self.__dict__["active"] + active = self.active if args: # Update the client with the provided services. @@ -386,13 +386,10 @@ def update_client(client, services): # as this causes a namespace clash with this method # i.e., Lenient.context, via Lenient.__getattr__ active = "__context" - self.__dict__["active"] = active - # self.__dict__[active] = new_services - update_client(active, new_services) + self.active = active + + update_client(active, new_services) - else: - # Append provided services to any pre-existing services of the active client. - update_client(active, new_services) else: # Append previous ephemeral services (for non-specific client) to the active client. if ( @@ -400,7 +397,7 @@ def update_client(client, services): and active != "__context" and "__context" in self.__dict__ ): - new_services = self.__dict__["__context"] + new_services = self.__context update_client(active, new_services) try: From cc4c4985c0af375180855269f03894d6d241d7db Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 03:17:32 +0100 Subject: [PATCH 4/8] Remove superfluous use of 'set' in testing. --- lib/iris/tests/unit/common/lenient/test_Lenient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index 24c6f02dd4..4ad10dea4b 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -564,7 +564,7 @@ def test_args_str(self): dict(active=client, client=set((svc, True) for svc in services)) ) self.assertEqual(context["active"], expected["active"]) - self.assertEqual(set(context["client"]), set(expected["client"])) + self.assertEqual(context["client"], expected["client"]) self.assertEqual(post, self.default) def test_args_callable(self): @@ -590,7 +590,7 @@ def service2(): ) ) self.assertEqual(context["active"], expected["active"]) - self.assertEqual(set(context["client"]), set(expected["client"])) + self.assertEqual(context["client"], expected["client"]) self.assertEqual(post, self.default) def test_context_runtime(self): From 9efea78659a8faeea7f81d0b62b9c9478b705f73 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 03:27:16 +0100 Subject: [PATCH 5/8] Newstyle context2. --- lib/iris/common/lenient.py | 45 ++-- .../tests/unit/common/lenient/test_Lenient.py | 238 ++++++++++++++++++ 2 files changed, 269 insertions(+), 14 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index 522eacd8e4..946b3a3274 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -96,7 +96,7 @@ def lenient_client_inner_naked(*args, **kwargs): as active at runtime before executing it. """ - with LENIENT.context(active=qualname(func)): + with LENIENT.context2(active=qualname(func)): result = func(*args, **kwargs) return result @@ -117,7 +117,7 @@ def lenient_client_inner(*args, **kwargs): as active at runtime before executing it. """ - with LENIENT.context(*services, active=qualname(func)): + with LENIENT.context2(qualname(func), services=services): result = func(*args, **kwargs) return result @@ -409,7 +409,12 @@ def update_client(client, services): @contextmanager def context2( - self, active=None, services=None, enable=None, **service_values + self, + active=None, + services=None, + enable=None, + modify_existing=False, + **service_values, ): """ Return a context manager which allows temporary modification of @@ -423,24 +428,36 @@ def context2( # ... code that expects some lenient behaviour with iris.LENIENT.context(client, srv2=False, modify_existing=True): - # ... code that expects some non-lenient behaviour + # ... code that amends for some NON-lenient behaviour + + with iris.LENIENT.context(client, set1=3, set3='adjust'): + # ... code using non-binary settings. """ def update_client(client, services): if client in self.__dict__: - existing_services = self.__dict__[client] + # Convert existing set of pairs to dict + new_services = {svc: val for svc, val in self.__dict__[client]} else: - existing_services = () + new_services = {} + + # Update dict with new settings. + if not hasattr(services, "keys"): + services = {svc: True for svc in services} + new_services.update(services) - self.__dict__[client] = tuple(set(existing_services + services)) + # Save back, as a set-of-pairs. + self.__dict__[client] = set( + (svc, val) for svc, val in new_services.items() + ) # Save the original state. original_state = deepcopy(self.__dict__) # First update the state with the fixed keyword controls. if active is not None: - self.active = active + self.active = qualname(active) if enable is not None: self.enable = enable @@ -449,6 +466,7 @@ def update_client(client, services): if services or service_values: # Update the client with the provided services. + services = services or [] new_services = {qualname(srv): True for srv in services} new_services.update( {qualname(srv): val for srv, val in service_values.items()} @@ -460,11 +478,10 @@ def update_client(client, services): # as this causes a namespace clash with this method # i.e., Lenient.context, via Lenient.__getattr__ active = "__context" - self.__dict__["active"] = active - self.__dict__[active] = new_services - else: - # Append provided services to any pre-existing services of the active client. - update_client(active, new_services) + self.active = active + + update_client(active, new_services) + else: # Append previous ephemeral services (for non-specific client) to the active client. if ( @@ -472,7 +489,7 @@ def update_client(client, services): and active != "__context" and "__context" in self.__dict__ ): - new_services = self.__dict__["__context"] + new_services = self.__context update_client(active, new_services) try: diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index 4ad10dea4b..9d4b15e3b5 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -611,6 +611,244 @@ def test_context_runtime(self): self.assertEqual(post, self.default) +class Test_context2__oldstyles(tests.IrisTest): + def setUp(self): + self.lenient = Lenient() + self.default = dict(active=None, enable=LENIENT_ENABLE_DEFAULT) + + def copy(self): + return self.lenient.__dict__.copy() + + def test_nop(self): + pre = self.copy() + with self.lenient.context2(): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + self.assertEqual(context, self.default) + self.assertEqual(post, self.default) + + def test_active_str(self): + client = "client" + pre = self.copy() + with self.lenient.context2(active=client): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update(dict(active=client)) + self.assertEqual(context, expected) + self.assertEqual(post, self.default) + + def test_active_callable(self): + def client(): + pass + + pre = self.copy() + with self.lenient.context2(active=client): + context = self.copy() + post = self.copy() + qualname_client = qualname(client) + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update(dict(active=qualname_client)) + self.assertEqual(context, expected) + self.assertEqual(post, self.default) + + def test_args_str(self): + client = "client" + services = ("service1", "service2") + pre = self.copy() + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2(client, services=services): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict(active=client, client=set((svc, True) for svc in services)) + ) + self.assertEqual(context["active"], expected["active"]) + self.assertEqual(set(context["client"]), set(expected["client"])) + self.assertEqual(post, self.default) + + def test_args_callable(self): + def service1(): + pass + + def service2(): + pass + + client = "client" + services = (service1, service2) + pre = self.copy() + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2(client, services=services): + context = self.copy() + post = self.copy() + qualname_services = tuple([qualname(service) for service in services]) + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict( + active=client, + client=set((svc, True) for svc in qualname_services), + ) + ) + self.assertEqual(context["active"], expected["active"]) + self.assertEqual(context["client"], expected["client"]) + self.assertEqual(post, self.default) + + def test_context_runtime(self): + services = ("service1", "service2") + pre = self.copy() + # ref: self.lenient.context(*services): + with self.lenient.context2(services=services): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict( + active="__context", + __context=set([(srv, True) for srv in services]), + ) + ) + self.assertEqual(context, expected) + self.assertEqual(post, self.default) + + +class Test_context2__newstyles(tests.IrisTest): + def setUp(self): + self.lenient = Lenient() + self.default = dict(active=None, enable=LENIENT_ENABLE_DEFAULT) + + def copy(self): + return self.lenient.__dict__.copy() + + def test_args_str(self): + client = "client" + services = ("service1", "service2") + pre = self.copy() + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2(client, service1=True, service2=True): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict(active=client, client=set((svc, True) for svc in services)) + ) + self.assertEqual(context["active"], expected["active"]) + self.assertEqual(set(context["client"]), set(expected["client"])) + self.assertEqual(post, self.default) + + def test_args_callable(self): + def service1(): + pass + + def service2(): + pass + + client = "client" + service_qualnames = [qualname(svc) for svc in (service1, service2)] + pre = self.copy() + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2( + client, **{name: True for name in service_qualnames} + ): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict( + active=client, + client=set((svc, True) for svc in service_qualnames), + ) + ) + self.assertEqual(context["active"], expected["active"]) + self.assertEqual(context["client"], set(expected["client"])) + self.assertEqual(post, self.default) + + def test_context_runtime(self): + services = ("service1", "service2") + pre = self.copy() + # ref: self.lenient.context(*services): + with self.lenient.context2(services=services): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict( + active="__context", + __context=set([(srv, True) for srv in services]), + ) + ) + self.assertEqual(context, expected) + self.assertEqual(post, self.default) + + def test_args_switchoff(self): + def service1(): + pass + + def service2(): + pass + + qualname1, qualname2 = [qualname(svc) for svc in (service1, service2)] + pre = self.copy() + + # Note: make dict for keywords, as the names are really long ! + settings = {qualname1: True, qualname2: False} + + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2("client", **settings): + context = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected = self.default.copy() + expected.update( + dict( + active="client", + client=set((k, v) for k, v in settings.items()), + ) + ) + self.assertEqual(context["active"], expected["active"]) + self.assertEqual(context["client"], expected["client"]) + self.assertEqual(post, self.default) + + def test_args_modify(self): + pre = self.copy() + + # ref: self.lenient.context(*services, active=client): + with self.lenient.context2("client", set1=1, set2=2): + context1 = self.copy() + with self.lenient.context2( + "client", set2="not two", modify_existing=True + ): + context2 = self.copy() + post = self.copy() + self.assertEqual(pre, self.default) + expected1 = self.default.copy() + expected1.update( + dict(active="client", client=set([("set1", 1), ("set2", 2)]),) + ) + self.assertEqual(context1["active"], "client") + self.assertEqual(context1["client"], expected1["client"]) + self.assertEqual(post, self.default) + expected2 = self.default.copy() + expected2.update( + dict( + active="client", + client=set([("set1", 1), ("set2", "not two")]), + ) + ) + self.assertEqual(context2["active"], "client") + self.assertEqual(context2["client"], expected2["client"]) + self.assertEqual(post, self.default) + + class Test_enable(tests.IrisTest): def setUp(self): self.lenient = Lenient() From 0ee32a0a73bc8a6c21b2235fe7ac603ad2d0a4cb Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 03:36:37 +0100 Subject: [PATCH 6/8] Newstyle replaces old. --- lib/iris/common/lenient.py | 82 +--------- .../tests/unit/common/lenient/test_Lenient.py | 145 ++---------------- 2 files changed, 12 insertions(+), 215 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index 946b3a3274..aa060d9b01 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -96,7 +96,7 @@ def lenient_client_inner_naked(*args, **kwargs): as active at runtime before executing it. """ - with LENIENT.context2(active=qualname(func)): + with LENIENT.context(active=qualname(func)): result = func(*args, **kwargs) return result @@ -117,7 +117,7 @@ def lenient_client_inner(*args, **kwargs): as active at runtime before executing it. """ - with LENIENT.context2(qualname(func), services=services): + with LENIENT.context(qualname(func), services=services): result = func(*args, **kwargs) return result @@ -332,83 +332,7 @@ def __setitem__(self, name, value): self.__dict__[name] = value @contextmanager - def context(self, *args, **kwargs): - """ - Return a context manager which allows temporary modification of - the lenient option state for the active thread. - - On entry to the context manager, all provided keyword arguments are - applied. On exit from the context manager, the previous lenient option - state is restored. - - For example:: - with iris.LENIENT.context(example_lenient_flag=False): - # ... code that expects some non-lenient behaviour - - .. note:: - iris.LENIENT.example_lenient_flag does not exist and is - provided only as an example. - - """ - - def update_client(client, services): - if client in self.__dict__: - # Convert existing set of pairs to dict - new_services = {svc: val for svc, val in self.__dict__[client]} - else: - new_services = {} - - # Update dict with new settings. - if not hasattr(services, "keys"): - services = {svc: True for svc in services} - new_services.update(services) - self.__dict__[client] = set( - (svc, val) for svc, val in new_services.items() - ) - - # Save the original state. - original_state = deepcopy(self.__dict__) - - # Temporarily update the state with the kwargs first. - for name, value in kwargs.items(): - self[name] = value - - # Get the active client. - active = self.active - - if args: - # Update the client with the provided services. - new_services = tuple([qualname(arg) for arg in args]) - - if active is None: - # Ensure not to use "context" as the ephemeral name - # of the context manager runtime "active" lenient client, - # as this causes a namespace clash with this method - # i.e., Lenient.context, via Lenient.__getattr__ - active = "__context" - self.active = active - - update_client(active, new_services) - - else: - # Append previous ephemeral services (for non-specific client) to the active client. - if ( - active is not None - and active != "__context" - and "__context" in self.__dict__ - ): - new_services = self.__context - update_client(active, new_services) - - try: - yield - finally: - # Restore the original state. - self.__dict__.clear() - self.__dict__.update(original_state) - - @contextmanager - def context2( + def context( self, active=None, services=None, diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index 9d4b15e3b5..f8def5f59f 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -536,131 +536,11 @@ def client(): self.assertEqual(context, expected) self.assertEqual(post, self.default) - def test_kwargs(self): - client = "client" - self.lenient.__dict__["service1"] = False - self.lenient.__dict__["service2"] = False - pre = self.copy() - with self.lenient.context(active=client, service1=True, service2=True): - context = self.copy() - post = self.copy() - self.default.update(dict(service1=False, service2=False)) - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update(dict(active=client, service1=True, service2=True)) - self.assertEqual(context, expected) - self.assertEqual(post, self.default) - - def test_args_str(self): - client = "client" - services = ("service1", "service2") - pre = self.copy() - with self.lenient.context(*services, active=client): - context = self.copy() - post = self.copy() - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update( - dict(active=client, client=set((svc, True) for svc in services)) - ) - self.assertEqual(context["active"], expected["active"]) - self.assertEqual(context["client"], expected["client"]) - self.assertEqual(post, self.default) - - def test_args_callable(self): - def service1(): - pass - - def service2(): - pass - - client = "client" - services = (service1, service2) - pre = self.copy() - with self.lenient.context(*services, active=client): - context = self.copy() - post = self.copy() - qualname_services = tuple([qualname(service) for service in services]) - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update( - dict( - active=client, - client=set((svc, True) for svc in qualname_services), - ) - ) - self.assertEqual(context["active"], expected["active"]) - self.assertEqual(context["client"], expected["client"]) - self.assertEqual(post, self.default) - - def test_context_runtime(self): - services = ("service1", "service2") - pre = self.copy() - with self.lenient.context(*services): - context = self.copy() - post = self.copy() - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update( - dict( - active="__context", - __context=set([(srv, True) for srv in services]), - ) - ) - self.assertEqual(context, expected) - self.assertEqual(post, self.default) - - -class Test_context2__oldstyles(tests.IrisTest): - def setUp(self): - self.lenient = Lenient() - self.default = dict(active=None, enable=LENIENT_ENABLE_DEFAULT) - - def copy(self): - return self.lenient.__dict__.copy() - - def test_nop(self): - pre = self.copy() - with self.lenient.context2(): - context = self.copy() - post = self.copy() - self.assertEqual(pre, self.default) - self.assertEqual(context, self.default) - self.assertEqual(post, self.default) - - def test_active_str(self): - client = "client" - pre = self.copy() - with self.lenient.context2(active=client): - context = self.copy() - post = self.copy() - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update(dict(active=client)) - self.assertEqual(context, expected) - self.assertEqual(post, self.default) - - def test_active_callable(self): - def client(): - pass - - pre = self.copy() - with self.lenient.context2(active=client): - context = self.copy() - post = self.copy() - qualname_client = qualname(client) - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update(dict(active=qualname_client)) - self.assertEqual(context, expected) - self.assertEqual(post, self.default) - def test_args_str(self): client = "client" services = ("service1", "service2") pre = self.copy() - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2(client, services=services): + with self.lenient.context(client, services=services): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -682,8 +562,7 @@ def service2(): client = "client" services = (service1, service2) pre = self.copy() - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2(client, services=services): + with self.lenient.context(client, services=services): context = self.copy() post = self.copy() qualname_services = tuple([qualname(service) for service in services]) @@ -702,8 +581,7 @@ def service2(): def test_context_runtime(self): services = ("service1", "service2") pre = self.copy() - # ref: self.lenient.context(*services): - with self.lenient.context2(services=services): + with self.lenient.context(services=services): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -730,8 +608,7 @@ def test_args_str(self): client = "client" services = ("service1", "service2") pre = self.copy() - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2(client, service1=True, service2=True): + with self.lenient.context(client, service1=True, service2=True): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -753,8 +630,7 @@ def service2(): client = "client" service_qualnames = [qualname(svc) for svc in (service1, service2)] pre = self.copy() - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2( + with self.lenient.context( client, **{name: True for name in service_qualnames} ): context = self.copy() @@ -774,8 +650,7 @@ def service2(): def test_context_runtime(self): services = ("service1", "service2") pre = self.copy() - # ref: self.lenient.context(*services): - with self.lenient.context2(services=services): + with self.lenient.context(services=services): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -802,8 +677,7 @@ def service2(): # Note: make dict for keywords, as the names are really long ! settings = {qualname1: True, qualname2: False} - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2("client", **settings): + with self.lenient.context("client", **settings): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -821,10 +695,9 @@ def service2(): def test_args_modify(self): pre = self.copy() - # ref: self.lenient.context(*services, active=client): - with self.lenient.context2("client", set1=1, set2=2): + with self.lenient.context("client", set1=1, set2=2): context1 = self.copy() - with self.lenient.context2( + with self.lenient.context( "client", set2="not two", modify_existing=True ): context2 = self.copy() From b9428c0a6db0823d625b402885d37b502c0ae6cd Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 10:05:10 +0100 Subject: [PATCH 7/8] Fix usages. --- lib/iris/common/lenient.py | 47 ++++++---- .../tests/unit/common/lenient/test_Lenient.py | 92 ++++++++++--------- 2 files changed, 75 insertions(+), 64 deletions(-) diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index aa060d9b01..b12b1ebcdf 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -117,7 +117,7 @@ def lenient_client_inner(*args, **kwargs): as active at runtime before executing it. """ - with LENIENT.context(qualname(func), services=services): + with LENIENT.context(qualname(func), services): result = func(*args, **kwargs) return result @@ -331,6 +331,26 @@ def __setitem__(self, name, value): value = tuple([qualname(item) for item in value]) self.__dict__[name] = value + @staticmethod + def _services_asdict(services): + # Convert list of services, or dict(service:value) to dictionary form. + # For lists, also accepts callables, converting to qualnames. + if services is None: + services = {} + elif isinstance(services, str) or not isinstance(services, Iterable): + services = [services] + + if hasattr(services, "items"): + # Dictionary form contains setting values e.g. {'x':1, 'y':2}. + services = { + qualname(service): value for service, value in services.items() + } + else: + # List form (x, y) is equivalent to {x:True. y:True} + services = {qualname(service): True for service in services} + + return services + @contextmanager def context( self, @@ -338,7 +358,7 @@ def context( services=None, enable=None, modify_existing=False, - **service_values, + **service_values_dict, ): """ Return a context manager which allows temporary modification of @@ -388,13 +408,10 @@ def update_client(client, services): # Get the active client. active = self.__dict__["active"] - if services or service_values: + if services or service_values_dict: # Update the client with the provided services. - services = services or [] - new_services = {qualname(srv): True for srv in services} - new_services.update( - {qualname(srv): val for srv, val in service_values.items()} - ) + new_services = self._services_asdict(services) + new_services.update(self._services_asdict(service_values_dict)) if active is None: # Ensure not to use "context" as the ephemeral name @@ -477,21 +494,11 @@ def register_client(self, client, services, append=False): emsg = f"Cannot register {cls!r} protected non-client, got {client!r}." raise ValueError(emsg) - if isinstance(services, str) or not isinstance(services, Iterable): - services = (services,) - if not len(services): + services = self._services_asdict(services) + if not services: emsg = f"Require at least one {cls!r} lenient client service." raise ValueError(emsg) - if hasattr(services, "items"): - # Dictionary form contains setting values e.g. {'x':1, 'y':2}. - services = { - qualname(service): value for service, value in services.items() - } - else: - # List form (x, y) is equivalent to {x:True. y:True} - services = {qualname(service): True for service in services} - if append: # Service order is not significant, therefore there is no # requirement to preserve it. diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index f8def5f59f..8e631b932d 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -17,8 +17,10 @@ from iris.common.lenient import ( LENIENT_ENABLE_DEFAULT, LENIENT_PROTECTED, + LENIENT, Lenient, qualname, + lenient_client, ) @@ -512,7 +514,7 @@ def test_nop(self): def test_active_str(self): client = "client" pre = self.copy() - with self.lenient.context(active=client): + with self.lenient.context(client): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -526,7 +528,7 @@ def client(): pass pre = self.copy() - with self.lenient.context(active=client): + with self.lenient.context(client): context = self.copy() post = self.copy() qualname_client = qualname(client) @@ -540,7 +542,7 @@ def test_args_str(self): client = "client" services = ("service1", "service2") pre = self.copy() - with self.lenient.context(client, services=services): + with self.lenient.context(client, services): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -562,7 +564,7 @@ def service2(): client = "client" services = (service1, service2) pre = self.copy() - with self.lenient.context(client, services=services): + with self.lenient.context(client, services): context = self.copy() post = self.copy() qualname_services = tuple([qualname(service) for service in services]) @@ -581,7 +583,7 @@ def service2(): def test_context_runtime(self): services = ("service1", "service2") pre = self.copy() - with self.lenient.context(services=services): + with self.lenient.context(active=None, services=services): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) @@ -604,7 +606,7 @@ def setUp(self): def copy(self): return self.lenient.__dict__.copy() - def test_args_str(self): + def test_service_keys(self): client = "client" services = ("service1", "service2") pre = self.copy() @@ -620,79 +622,81 @@ def test_args_str(self): self.assertEqual(set(context["client"]), set(expected["client"])) self.assertEqual(post, self.default) - def test_args_callable(self): + def test_setting_false(self): def service1(): pass def service2(): pass - client = "client" - service_qualnames = [qualname(svc) for svc in (service1, service2)] + qualname1, qualname2 = [qualname(svc) for svc in (service1, service2)] pre = self.copy() - with self.lenient.context( - client, **{name: True for name in service_qualnames} - ): + + # Note: use dict for the keywords, as qualnames are really long ! + settings = {qualname1: True, qualname2: False} + + with self.lenient.context("client", **settings): context = self.copy() post = self.copy() self.assertEqual(pre, self.default) expected = self.default.copy() expected.update( dict( - active=client, - client=set((svc, True) for svc in service_qualnames), + active="client", + client=set([(qualname1, True), (qualname2, False)]), ) ) self.assertEqual(context["active"], expected["active"]) - self.assertEqual(context["client"], set(expected["client"])) + self.assertEqual(context["client"], expected["client"]) self.assertEqual(post, self.default) - def test_context_runtime(self): - services = ("service1", "service2") + def test_setting_nonbinary(self): + service_names = ("svc1", "svc2", "svc3", "svc4") + + def service_values(): + return [LENIENT("svc" + str(ix)) for ix in range(1, 5)] + pre = self.copy() - with self.lenient.context(services=services): - context = self.copy() - post = self.copy() - self.assertEqual(pre, self.default) - expected = self.default.copy() - expected.update( - dict( - active="__context", - __context=set([(srv, True) for srv in services]), - ) - ) - self.assertEqual(context, expected) - self.assertEqual(post, self.default) - def test_args_switchoff(self): - def service1(): - pass + client_settings = dict(svc1=True, svc2=False, svc3="seven", svc4=-5.3) - def service2(): - pass + @lenient_client(services=client_settings) + def client(): + return service_values() - qualname1, qualname2 = [qualname(svc) for svc in (service1, service2)] - pre = self.copy() + client_qualname = qualname(client) - # Note: make dict for keywords, as the names are really long ! - settings = {qualname1: True, qualname2: False} + for name in service_names: + LENIENT.register_service(name) - with self.lenient.context("client", **settings): + svcs_noclient = service_values() + svcs_inclient = client() + with self.lenient.context(client, **client_settings): context = self.copy() + post = self.copy() self.assertEqual(pre, self.default) expected = self.default.copy() expected.update( dict( - active="client", - client=set((k, v) for k, v in settings.items()), + active=client_qualname, + client=set( + [ + ("svc1", True), + ("svc2", False), + ("svc3", "seven"), + ("svc4", -5.3), + ] + ), ) ) self.assertEqual(context["active"], expected["active"]) - self.assertEqual(context["client"], expected["client"]) + self.assertEqual(context[client_qualname], expected["client"]) self.assertEqual(post, self.default) + self.assertEqual(svcs_noclient, [False, False, False, False]) + self.assertEqual(svcs_inclient, [True, False, "seven", -5.3]) - def test_args_modify(self): + def test_setting_modify(self): pre = self.copy() with self.lenient.context("client", set1=1, set2=2): From d67034ff071b1ee837ee48f5131ba5d8b8737a3f Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 1 Jul 2020 10:30:41 +0100 Subject: [PATCH 8/8] Reinstate test_kwargs just for easier change comparison. --- .../tests/unit/common/lenient/test_Lenient.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/iris/tests/unit/common/lenient/test_Lenient.py b/lib/iris/tests/unit/common/lenient/test_Lenient.py index 8e631b932d..31cd66c970 100644 --- a/lib/iris/tests/unit/common/lenient/test_Lenient.py +++ b/lib/iris/tests/unit/common/lenient/test_Lenient.py @@ -538,6 +538,21 @@ def client(): self.assertEqual(context, expected) self.assertEqual(post, self.default) + # def test_kwargs(self): + # client = "client" + # self.lenient.__dict__["service1"] = False + # self.lenient.__dict__["service2"] = False + # pre = self.copy() + # with self.lenient.context(active=client, service1=True, service2=True): + # context = self.copy() + # post = self.copy() + # self.default.update(dict(service1=False, service2=False)) + # self.assertEqual(pre, self.default) + # expected = self.default.copy() + # expected.update(dict(active=client, service1=True, service2=True)) + # self.assertEqual(context, expected) + # self.assertEqual(post, self.default) + def test_args_str(self): client = "client" services = ("service1", "service2")