Skip to content

Commit

Permalink
Changed rupdate to merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Joel Collins committed Apr 24, 2020
1 parent 696d5aa commit 76966a0
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 27 deletions.
12 changes: 7 additions & 5 deletions labthings/core/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import operator
import sys
import os
import copy
from functools import reduce

PY3 = sys.version_info > (3,)
Expand Down Expand Up @@ -39,20 +40,21 @@ def get_summary(obj):
return get_docstring(obj, remove_newlines=False).partition("\n")[0].strip()


def rupdate(destination_dict, update_dict):
def merge(first: dict, second: dict):
"""Recursively update a dictionary
This will take an "update_dictionary",
and recursively merge it with "destination_dict".
Args:
destination_dict (dict): Original dictionary
update_dict (dict): New data dictionary
first (dict): Original dictionary
second (dict): New data dictionary
Returns:
dict: Merged dictionary
"""
for k, v in update_dict.items():
destination_dict = copy.deepcopy(first)
for k, v in second.items():
# Merge lists if they're present in both objects
if isinstance(v, list):
# If key is missing from destination, create the list
Expand All @@ -68,7 +70,7 @@ def rupdate(destination_dict, update_dict):
elif isinstance(v, collections.abc.Mapping):
if k not in destination_dict:
destination_dict[k] = {}
destination_dict[k] = rupdate(destination_dict.get(k, {}), v)
destination_dict[k] = merge(destination_dict.get(k, {}), v)
# If not a list or dictionary, overwrite old value with new value
else:
destination_dict[k] = v
Expand Down
4 changes: 2 additions & 2 deletions labthings/server/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .utilities import unpack

from labthings.core.tasks.pool import TaskThread
from labthings.core.utilities import rupdate
from labthings.core.utilities import merge

import logging

Expand Down Expand Up @@ -304,7 +304,7 @@ def __init__(self, code, description=None, mimetype=None, **kwargs):
}

if self.mimetype:
rupdate(
self.response_dict = merge(
self.response_dict,
{
"responses": {self.code: {"content": {self.mimetype: {}}}},
Expand Down
18 changes: 10 additions & 8 deletions labthings/server/spec/apispec.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ..view import View
from apispec import APISpec

from ...core.utilities import get_docstring, get_summary, rupdate
from ...core.utilities import get_docstring, get_summary, merge
from .paths import rule_to_path, rule_to_params
from .utilities import convert_schema, update_spec

Expand Down Expand Up @@ -38,7 +38,7 @@ def rule_to_apispec_path(rule: Rule, view: View, spec: APISpec):

# Add extra parameters
# build_spec(view) guarantees view.__apispec__ exists
rupdate(params, view.__apispec__)
params = merge(params, view.__apispec__)

return params

Expand All @@ -64,7 +64,7 @@ def view_to_apispec_operations(view: View, spec: APISpec):
# Populate missing spec parameters
build_spec(method_function, inherit_from=view)

rupdate(
ops[method] = merge(
ops[method],
{
"description": getattr(method_function, "__apispec__").get(
Expand All @@ -75,7 +75,9 @@ def view_to_apispec_operations(view: View, spec: APISpec):
},
)

rupdate(ops[method], method_to_apispec_operation(method_function, spec))
ops[method] = merge(
ops[method], method_to_apispec_operation(method_function, spec)
)

return ops

Expand All @@ -97,7 +99,7 @@ def method_to_apispec_operation(method: callable, spec: APISpec):

op = {}
if "_params" in apispec:
rupdate(
op = merge(
op,
{
"requestBody": {
Expand All @@ -112,7 +114,7 @@ def method_to_apispec_operation(method: callable, spec: APISpec):

if "_schema" in apispec:
for code, schema in apispec.get("_schema", {}).items():
rupdate(
op = merge(
op,
{
"responses": {
Expand All @@ -129,7 +131,7 @@ def method_to_apispec_operation(method: callable, spec: APISpec):
)
else:
# If no explicit responses are known, populate with defaults
rupdate(
op = merge(
op,
{
"responses": {
Expand All @@ -141,7 +143,7 @@ def method_to_apispec_operation(method: callable, spec: APISpec):
# Bung in any extra swagger fields supplied
for key, val in apispec.items():
if key not in ["_params", "_schema"]:
rupdate(op, {key: val})
op = merge(op, {key: val})

return op

Expand Down
4 changes: 2 additions & 2 deletions labthings/server/spec/utilities.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin

from ...core.utilities import rupdate
from ...core.utilities import merge

from ..fields import Field
from marshmallow import Schema as BaseSchema
Expand All @@ -17,7 +17,7 @@ def update_spec(obj, spec: dict):
spec (dict): Dictionary of API spec data to add
"""
obj.__apispec__ = obj.__dict__.get("__apispec__", {})
rupdate(obj.__apispec__, spec)
obj.__apispec__ = merge(obj.__apispec__, spec)
return obj.__apispec__ or {}


Expand Down
20 changes: 10 additions & 10 deletions tests/test_core_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,51 +47,51 @@ def test_get_summary(example_class):
assert utilities.get_summary(example_class.class_method_no_docstring) == ""


def test_rupdate_granular():
def test_merge_granular():
# Update string value
s1 = {"a": "String"}
s2 = {"a": "String 2"}
assert utilities.rupdate(s1, s2) == s2
assert utilities.merge(s1, s2) == s2

# Update int value
i1 = {"b": 5}
i2 = {"b": 50}
assert utilities.rupdate(i1, i2) == i2
assert utilities.merge(i1, i2) == i2

# Update list elements
l1 = {"c": []}
l2 = {"c": [1, 2, 3, 4]}
assert utilities.rupdate(l1, l2) == l2
assert utilities.merge(l1, l2) == l2

# Extend list elements
l1 = {"c": [1, 2, 3]}
l2 = {"c": [4, 5, 6]}
assert utilities.rupdate(l1, l2)["c"] == [1, 2, 3, 4, 5, 6]
assert utilities.merge(l1, l2)["c"] == [1, 2, 3, 4, 5, 6]

# Merge dictionaries
d1 = {"d": {"a": "String", "b": 5, "c": []}}
d2 = {"d": {"a": "String 2", "b": 50, "c": [1, 2, 3, 4, 5]}}
assert utilities.rupdate(d1, d2) == d2
assert utilities.merge(d1, d2) == d2

# Replace value with list
ml1 = {"k": True}
ml2 = {"k": [1, 2, 3]}
assert utilities.rupdate(ml1, ml2) == ml2
assert utilities.merge(ml1, ml2) == ml2

# Create missing value
ms1 = {}
ms2 = {"k": "v"}
assert utilities.rupdate(ms1, ms2) == ms2
assert utilities.merge(ms1, ms2) == ms2

# Create missing list
ml1 = {}
ml2 = {"k": [1, 2, 3]}
assert utilities.rupdate(ml1, ml2) == ml2
assert utilities.merge(ml1, ml2) == ml2

# Create missing dictionary
md1 = {}
md2 = {"d": {"a": "String 2", "b": 50, "c": [1, 2, 3, 4, 5]}}
assert utilities.rupdate(md1, md2) == md2
assert utilities.merge(md1, md2) == md2


def test_rapply():
Expand Down

0 comments on commit 76966a0

Please sign in to comment.