Skip to content

Commit f476fe1

Browse files
authored
Ignore irrelevant changes for live-reload (#266)
1 parent 2adbc39 commit f476fe1

File tree

3 files changed

+101
-30
lines changed

3 files changed

+101
-30
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "truss"
3-
version = "0.4.2"
3+
version = "0.4.3"
44
description = "A seamless bridge from model development to model delivery"
55
license = "MIT"
66
readme = "README.md"

truss/patch/calc_patch.py

+61-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import logging
12
from pathlib import Path
23
from typing import Any, Dict, List, Optional, Set
34

45
import pkg_resources
56
import yaml
7+
from truss.constants import CONFIG_FILE
68
from truss.patch.hash import file_content_hash_str
79
from truss.patch.types import TrussSignature
810
from truss.templates.control.control.helpers.types import (
@@ -16,6 +18,7 @@
1618
from truss.truss_config import TrussConfig
1719
from truss.truss_spec import TrussSpec
1820

21+
logger: logging.Logger = logging.getLogger(__name__)
1922
PYCACHE_IGNORE_PATTERNS = [
2023
"**/__pycache__/**/*",
2124
"**/__pycache__/**",
@@ -31,10 +34,18 @@ def calc_truss_patch(
3134
Calculate patch for a truss from a previous state.
3235
3336
Returns: None if patch cannot be calculated, otherwise a list of patches.
34-
Note that the none return value is pretty important, patch coverage
35-
is limited and this usually indicates that the identified change cannot
36-
be expressed with currently supported patches.
37+
Note that the none return value is pretty important, patch coverage is
38+
limited and this usually indicates that the identified change cannot be
39+
expressed with currently supported patches.
40+
41+
Only standard and relevant truss paths are scanned for changes, rest
42+
ignored. E.g. at the root level, only changes to config.yaml are
43+
checked, any other changes are ignored.
3744
"""
45+
46+
def _relative_to_root(path: Path) -> str:
47+
return str(path.relative_to(truss_dir))
48+
3849
if ignore_patterns is None:
3950
ignore_patterns = PYCACHE_IGNORE_PATTERNS
4051

@@ -45,68 +56,72 @@ def calc_truss_patch(
4556
)
4657
# TODO(pankaj) Calculate model code patches only for now, add config changes
4758
# later.
59+
4860
truss_spec = TrussSpec(truss_dir)
49-
model_module_path = str(truss_spec.model_module_dir.relative_to(truss_dir))
50-
training_module_path = str(truss_spec.training_module_dir.relative_to(truss_dir))
61+
model_module_path = _relative_to_root(truss_spec.model_module_dir)
62+
data_dir_path = _relative_to_root(truss_spec.data_dir)
63+
bundled_packages_path = _relative_to_root(truss_spec.bundled_packages_dir)
64+
65+
def _under_unsupported_patch_dir(path: str) -> bool:
66+
"""
67+
Checks if the given path is under one of the directories that don't
68+
support patching. Note that if path `is` one of those directories that's
69+
ok, because those empty directories can be ignored from patching point
70+
of view.
71+
"""
72+
return _strictly_under(path, [data_dir_path, bundled_packages_path])
5173

5274
patches = []
5375
for path in changed_paths["removed"]:
5476
if path.startswith(model_module_path):
55-
relative_to_model_module_path = str(
56-
Path(path).relative_to(model_module_path)
57-
)
5877
patches.append(
5978
Patch(
6079
type=PatchType.MODEL_CODE,
6180
body=ModelCodePatch(
6281
action=Action.REMOVE,
63-
path=relative_to_model_module_path,
82+
path=_relative_to(path, model_module_path),
6483
),
6584
)
6685
)
67-
elif path.startswith(training_module_path):
68-
# Ignore training changes from patch
69-
continue
70-
else:
86+
elif path == CONFIG_FILE:
87+
# Don't support removal of config file
88+
logger.info(f"Patching not supported for removing {path}")
89+
return None
90+
elif _under_unsupported_patch_dir(path):
91+
logger.info(f"Patching not supported for removing {path}")
7192
return None
7293

7394
for path in changed_paths["added"] + changed_paths["updated"]:
7495
if path.startswith(model_module_path):
7596
full_path = truss_dir / path
76-
relative_to_model_module_path = str(
77-
Path(path).relative_to(model_module_path)
78-
)
7997

8098
# TODO(pankaj) Add support for empty directories, skip them for now.
8199
if not full_path.is_file():
82100
continue
83101

84-
with full_path.open() as file:
85-
content = file.read()
86102
action = Action.ADD if path in changed_paths["added"] else Action.UPDATE
87103
patches.append(
88104
Patch(
89105
type=PatchType.MODEL_CODE,
90106
body=ModelCodePatch(
91107
action=action,
92-
path=relative_to_model_module_path,
93-
content=content,
108+
path=_relative_to(path, model_module_path),
109+
content=_file_content(full_path),
94110
),
95111
)
96112
)
97-
elif path.startswith(training_module_path):
98-
# Ignore training changes from patch
99-
continue
100-
elif str(path) == "config.yaml":
101-
new_config = TrussConfig.from_yaml(truss_dir / "config.yaml")
113+
elif path == CONFIG_FILE:
114+
new_config = TrussConfig.from_yaml(truss_dir / CONFIG_FILE)
102115
prev_config = TrussConfig.from_dict(
103116
yaml.safe_load(previous_truss_signature.config)
104117
)
105118
config_patches = _calc_config_patches(prev_config, new_config)
106119
if config_patches is None:
120+
logger.info(f"Unable to patch update to {path}")
107121
return None
108122
patches.extend(config_patches)
109-
else:
123+
elif _under_unsupported_patch_dir(path):
124+
logger.info(f"Patching not supported for updating {path}")
110125
return None
111126
return patches
112127

@@ -280,3 +295,23 @@ def _mk_system_package_patch(action: Action, package: str) -> Patch:
280295
package=package,
281296
),
282297
)
298+
299+
300+
def _relative_to(path: str, relative_to_path: str):
301+
return str(Path(path).relative_to(relative_to_path))
302+
303+
304+
def _strictly_under(path: str, parent_paths: List[str]) -> bool:
305+
"""
306+
Checks if given path is under one of the given paths, but not the same as
307+
them. Assumes that parent paths themselves are not under each other.
308+
"""
309+
for dir_path in parent_paths:
310+
if path.startswith(dir_path) and not path == dir_path:
311+
return True
312+
return False
313+
314+
315+
def _file_content(path: Path) -> str:
316+
with path.open() as file:
317+
return file.read()

truss/tests/patch/test_patch.py truss/tests/patch/test_calc_patch.py

+39-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,23 @@
1616

1717
def test_calc_truss_patch_unsupported(custom_model_truss_dir: Path):
1818
prev_sign = calc_truss_signature(custom_model_truss_dir)
19-
(custom_model_truss_dir / "dummy").touch()
19+
20+
# Unsupported directory should result in no patches
21+
(custom_model_truss_dir / "data").touch()
22+
patches = calc_truss_patch(custom_model_truss_dir, prev_sign)
23+
assert len(patches) == 0
24+
25+
# Changes under unsupported directory should return None to reflect
26+
# inability to calculate patch.
27+
(custom_model_truss_dir / "data" / "dummy").touch()
2028
patches = calc_truss_patch(custom_model_truss_dir, prev_sign)
2129
assert patches is None
2230

2331

2432
def test_calc_truss_patch_add_file(custom_model_truss_dir: Path):
2533
prev_sign = calc_truss_signature(custom_model_truss_dir)
26-
(custom_model_truss_dir / "model" / "dummy").touch()
34+
with (custom_model_truss_dir / "model" / "dummy").open("w") as file:
35+
file.write("content")
2736
patches = calc_truss_patch(custom_model_truss_dir, prev_sign)
2837

2938
assert len(patches) == 1
@@ -33,7 +42,7 @@ def test_calc_truss_patch_add_file(custom_model_truss_dir: Path):
3342
body=ModelCodePatch(
3443
action=Action.ADD,
3544
path="dummy",
36-
content="",
45+
content="content",
3746
),
3847
)
3948

@@ -110,6 +119,33 @@ def test_calc_truss_ignore_pycache(custom_model_truss_dir: Path):
110119
assert len(patches) == 0
111120

112121

122+
def test_calc_truss_ignore_changes_outside_patch_relevant_dirs(
123+
custom_model_truss_dir: Path,
124+
):
125+
prev_sign = calc_truss_signature(custom_model_truss_dir)
126+
top_pycache_path = custom_model_truss_dir / "__pycache__"
127+
top_pycache_path.mkdir()
128+
(top_pycache_path / "README.md").touch()
129+
git_dir = custom_model_truss_dir / ".git"
130+
git_dir.mkdir()
131+
(git_dir / "dummy").touch()
132+
133+
patches = calc_truss_patch(
134+
custom_model_truss_dir,
135+
prev_sign,
136+
)
137+
assert len(patches) == 0
138+
139+
# Removing should also be ignored
140+
new_sign = calc_truss_signature(custom_model_truss_dir)
141+
(git_dir / "dummy").unlink()
142+
patches = calc_truss_patch(
143+
custom_model_truss_dir,
144+
new_sign,
145+
)
146+
assert len(patches) == 0
147+
148+
113149
def test_calc_config_patches_add_python_requirement(custom_model_truss_dir: Path):
114150
patches = _apply_config_change_and_calc_patches(
115151
custom_model_truss_dir,

0 commit comments

Comments
 (0)