Skip to content

Commit f6bd78a

Browse files
committed
feat: port util.copy_task from gecko_taskgraph
And use it in `util.templates.merge`. This is a port of the following patch in `gecko_taskgraph`: https://hg.mozilla.org/mozilla-central/rev/fa2cdac6989ac78d606d8f8adb10d826a6e50429
1 parent deaeb37 commit f6bd78a

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

src/taskgraph/util/copy.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import Any
2+
3+
from taskgraph.task import Task
4+
from taskgraph.util.readonlydict import ReadOnlyDict
5+
6+
immutable_types = {int, float, bool, str, type(None), ReadOnlyDict}
7+
8+
9+
def deepcopy(obj: Any) -> Any:
10+
"""Perform a deep copy of an object with a tree like structure.
11+
12+
This is a re-implementation of Python's `copy.deepcopy` function with a few key differences:
13+
14+
1. Unlike the stdlib, this does *not* support copying graph-like structure,
15+
which allows it to be more efficient than deepcopy on tree-like structures
16+
(such as Tasks).
17+
2. This special cases support for `taskgraph.task.Task` objects.
18+
19+
Args:
20+
obj: The object to deep copy.
21+
22+
Returns:
23+
A deep copy of the object.
24+
"""
25+
ty = type(obj)
26+
if ty in immutable_types:
27+
return obj
28+
if ty is dict:
29+
return {k: deepcopy(v) for k, v in obj.items()}
30+
if ty is list:
31+
return [deepcopy(elt) for elt in obj]
32+
if ty is Task:
33+
task = Task(
34+
kind=deepcopy(obj.kind),
35+
label=deepcopy(obj.label),
36+
attributes=deepcopy(obj.attributes),
37+
task=deepcopy(obj.task),
38+
description=deepcopy(obj.description),
39+
optimization=deepcopy(obj.optimization),
40+
dependencies=deepcopy(obj.dependencies),
41+
soft_dependencies=deepcopy(obj.soft_dependencies),
42+
if_dependencies=deepcopy(obj.if_dependencies),
43+
)
44+
if obj.task_id:
45+
task.task_id = obj.task_id
46+
return task
47+
raise NotImplementedError(f"copying '{ty}' from '{obj}'")

src/taskgraph/util/templates.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

5-
import copy
5+
from taskgraph.util.copy import deepcopy
66

77

88
def merge_to(source, dest):
@@ -55,7 +55,7 @@ def merge(*objects):
5555
Returns the result without modifying any arguments.
5656
"""
5757
if len(objects) == 1:
58-
return copy.deepcopy(objects[0])
58+
return deepcopy(objects[0])
5959
return merge_to(objects[-1], merge(*objects[:-1]))
6060

6161

test/test_util_copy.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import pytest
2+
3+
from taskgraph.task import Task
4+
from taskgraph.util.copy import deepcopy, immutable_types
5+
from taskgraph.util.readonlydict import ReadOnlyDict
6+
7+
8+
@pytest.mark.parametrize(
9+
"input",
10+
(
11+
1,
12+
False,
13+
"foo",
14+
ReadOnlyDict(a=1, b="foo"),
15+
["foo", "bar"],
16+
{
17+
"foo": Task(
18+
label="abc",
19+
kind="kind",
20+
attributes={"bar": "baz"},
21+
dependencies={"dep": "bar"},
22+
task={"payload": {"command": ["echo hello"]}},
23+
)
24+
},
25+
),
26+
)
27+
def test_deepcopy(input):
28+
result = deepcopy(input)
29+
assert result == input
30+
31+
if type(result) in immutable_types:
32+
assert id(result) == id(input)
33+
else:
34+
assert id(result) != id(input)

0 commit comments

Comments
 (0)