From bfc6fc4ff995faf003111066858648284fd87768 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 21 Jul 2021 17:58:43 +0100 Subject: [PATCH] Use deepcopy to avoid modifying responses Responses and parameters can be stored as dictionary/list properties on View subclasses, or on HTTP operation methods. The LabThings apispec plugin includes these dictionaries in the spec, and the Marshmallow plugin then modifies the spec to reference schemas. In unit testing, we build the API description multiple times per run of Python, which means that the second time it's built, the dictionaries have been modified and contain only {"$ref": "#/components/schema/name"} instead of a Schema object. This commit uses deepcopy so that the descriptions on the classes/methods are not modified, and unit testing works again. --- src/labthings/apispec/plugins.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/labthings/apispec/plugins.py b/src/labthings/apispec/plugins.py index f9900a3..6e24998 100644 --- a/src/labthings/apispec/plugins.py +++ b/src/labthings/apispec/plugins.py @@ -1,6 +1,7 @@ import re from apispec import BasePlugin +from copy import deepcopy from apispec.ext.marshmallow import ( MarshmallowPlugin as _MarshmallowPlugin, @@ -101,11 +102,11 @@ def spec_for_interaction(cls, interaction): "parameters": [], } # Allow custom responses from the class, overridden by the method - d[method]["responses"].update(getattr(interaction, "responses", {})) - d[method]["responses"].update(getattr(prop, "responses", {})) + d[method]["responses"].update(deepcopy(getattr(interaction, "responses", {}))) + d[method]["responses"].update(deepcopy(getattr(prop, "responses", {}))) # Allow custom parameters from the class & method - d[method]["parameters"].extend(getattr(interaction, "parameters", {})) - d[method]["parameters"].extend(getattr(prop, "parameters", {})) + d[method]["parameters"].extend(deepcopy(getattr(interaction, "parameters", {}))) + d[method]["parameters"].extend(deepcopy(getattr(prop, "parameters", {}))) return d @classmethod @@ -148,10 +149,6 @@ def spec_for_property(cls, prop): }, ) - # Enable custom responses from all methods - for method in d.keys(): - d[method]["responses"].update(prop.responses) - return d def spec_for_action(self, action): @@ -245,8 +242,6 @@ def spec_for_action(self, action): }, }, ) - # Enable custom responses from POST - d["post"]["responses"].update(action.responses) return d @classmethod