Skip to content

Commit 543cfaf

Browse files
committed
Improve deep_merge performance
1 parent 64fc737 commit 543cfaf

File tree

1 file changed

+25
-21
lines changed

1 file changed

+25
-21
lines changed

paradox/lib/utils.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
# -*- coding: utf-8 -*-
21
import asyncio
2+
from collections.abc import Hashable
3+
from copy import deepcopy
34
import functools
45
import json
56
import re
67
import threading
78
import typing
8-
from collections.abc import Hashable
9-
10-
from slugify import slugify
11-
12-
from copy import deepcopy
139

1410
from construct import Container, ListContainer
11+
from slugify import slugify
1512

1613
main_thread_loop = asyncio.get_event_loop()
1714

@@ -38,7 +35,7 @@ def default(self, o):
3835
if isinstance(o, bytes):
3936
return o.decode("utf-8")
4037

41-
return super(JSONByteEncoder, self).default(o)
38+
return super().default(o)
4239

4340

4441
class SerializableToJSONEncoder(json.JSONEncoder):
@@ -66,15 +63,22 @@ def deep_merge(*dicts, extend_lists=False, initializer=None):
6663
def merge_into(d1, d2):
6764
if d1 is None:
6865
return d2
69-
for key in d2:
66+
for key, value in d2.items():
7067
if key not in d1: # key is missing
71-
d1[key] = deepcopy(d2[key])
68+
d1[key] = deepcopy(value)
7269
elif extend_lists and isinstance(d1[key], list):
73-
d1[key].extend(deepcopy(d2[key]))
70+
if isinstance(value, list):
71+
# Use list concatenation instead of extend for better performance with large lists
72+
d1[key] = d1[key] + deepcopy(value)
73+
else:
74+
# Keep original behavior for non-list values
75+
d1[key].extend(deepcopy(value))
7476
elif not isinstance(d1[key], dict):
75-
d1[key] = deepcopy(d2[key])
76-
else:
77-
d1[key] = merge_into(d1[key], d2[key])
77+
d1[key] = deepcopy(value)
78+
elif isinstance(value, dict): # Ensure both are dicts before recursion
79+
d1[key] = merge_into(d1[key], value)
80+
else: # d1[key] is dict but value is not
81+
d1[key] = deepcopy(value)
7882
return d1
7983

8084
return functools.reduce(merge_into, dicts, initializer)
@@ -92,23 +96,23 @@ def sanitize_key(key):
9296

9397
def construct_free(container: Container):
9498
if isinstance(container, (Container, typing.Mapping)):
95-
return dict(
96-
(k, construct_free(v))
99+
return {
100+
k: construct_free(v)
97101
for k, v in container.items()
98102
if not (isinstance(k, str) and k.startswith("_"))
99-
)
103+
}
100104
elif isinstance(container, (ListContainer, typing.List)):
101105
return list(construct_free(v) for v in container)
102106
else:
103107
return container
104108

105109

106-
class memoized(object):
110+
class memoized:
107111
"""From: https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
108-
Decorator. Caches a function's return value each time it is called.
109-
If called later with the same arguments, the cached value is returned
110-
(not reevaluated).
111-
"""
112+
Decorator. Caches a function's return value each time it is called.
113+
If called later with the same arguments, the cached value is returned
114+
(not reevaluated).
115+
"""
112116

113117
def __init__(self, func):
114118
self.func = func

0 commit comments

Comments
 (0)