1
+ import logging
1
2
from pathlib import Path
2
3
from typing import Any , Dict , List , Optional , Set
3
4
4
5
import pkg_resources
5
6
import yaml
7
+ from truss .constants import CONFIG_FILE
6
8
from truss .patch .hash import file_content_hash_str
7
9
from truss .patch .types import TrussSignature
8
10
from truss .templates .control .control .helpers .types import (
16
18
from truss .truss_config import TrussConfig
17
19
from truss .truss_spec import TrussSpec
18
20
21
+ logger : logging .Logger = logging .getLogger (__name__ )
19
22
PYCACHE_IGNORE_PATTERNS = [
20
23
"**/__pycache__/**/*" ,
21
24
"**/__pycache__/**" ,
@@ -31,10 +34,18 @@ def calc_truss_patch(
31
34
Calculate patch for a truss from a previous state.
32
35
33
36
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.
37
44
"""
45
+
46
+ def _relative_to_root (path : Path ) -> str :
47
+ return str (path .relative_to (truss_dir ))
48
+
38
49
if ignore_patterns is None :
39
50
ignore_patterns = PYCACHE_IGNORE_PATTERNS
40
51
@@ -45,68 +56,72 @@ def calc_truss_patch(
45
56
)
46
57
# TODO(pankaj) Calculate model code patches only for now, add config changes
47
58
# later.
59
+
48
60
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 ])
51
73
52
74
patches = []
53
75
for path in changed_paths ["removed" ]:
54
76
if path .startswith (model_module_path ):
55
- relative_to_model_module_path = str (
56
- Path (path ).relative_to (model_module_path )
57
- )
58
77
patches .append (
59
78
Patch (
60
79
type = PatchType .MODEL_CODE ,
61
80
body = ModelCodePatch (
62
81
action = Action .REMOVE ,
63
- path = relative_to_model_module_path ,
82
+ path = _relative_to ( path , model_module_path ) ,
64
83
),
65
84
)
66
85
)
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 } " )
71
92
return None
72
93
73
94
for path in changed_paths ["added" ] + changed_paths ["updated" ]:
74
95
if path .startswith (model_module_path ):
75
96
full_path = truss_dir / path
76
- relative_to_model_module_path = str (
77
- Path (path ).relative_to (model_module_path )
78
- )
79
97
80
98
# TODO(pankaj) Add support for empty directories, skip them for now.
81
99
if not full_path .is_file ():
82
100
continue
83
101
84
- with full_path .open () as file :
85
- content = file .read ()
86
102
action = Action .ADD if path in changed_paths ["added" ] else Action .UPDATE
87
103
patches .append (
88
104
Patch (
89
105
type = PatchType .MODEL_CODE ,
90
106
body = ModelCodePatch (
91
107
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 ) ,
94
110
),
95
111
)
96
112
)
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 )
102
115
prev_config = TrussConfig .from_dict (
103
116
yaml .safe_load (previous_truss_signature .config )
104
117
)
105
118
config_patches = _calc_config_patches (prev_config , new_config )
106
119
if config_patches is None :
120
+ logger .info (f"Unable to patch update to { path } " )
107
121
return None
108
122
patches .extend (config_patches )
109
- else :
123
+ elif _under_unsupported_patch_dir (path ):
124
+ logger .info (f"Patching not supported for updating { path } " )
110
125
return None
111
126
return patches
112
127
@@ -280,3 +295,23 @@ def _mk_system_package_patch(action: Action, package: str) -> Patch:
280
295
package = package ,
281
296
),
282
297
)
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 ()
0 commit comments