... |
... |
@@ -52,72 +52,20 @@ class YamlCache(): |
52
|
52
|
def __init__(self, context):
|
53
|
53
|
self._project_caches = {}
|
54
|
54
|
self._context = context
|
|
55
|
+ ##################
|
|
56
|
+ # Public Methods #
|
|
57
|
+ ##################
|
55
|
58
|
|
56
|
|
- # Writes the yaml cache to the specified path.
|
|
59
|
+ # is_cached():
|
57
|
60
|
#
|
58
|
|
- # Args:
|
59
|
|
- # path (str): The path to the cache file.
|
60
|
|
- def write(self, path):
|
61
|
|
- parent_dir = os.path.dirname(path)
|
62
|
|
- os.makedirs(parent_dir, exist_ok=True)
|
63
|
|
- with open(path, "wb") as f:
|
64
|
|
- BstPickler(f).dump(self)
|
65
|
|
-
|
66
|
|
- # Gets a parsed file from the cache.
|
|
61
|
+ # Checks whether a file is cached.
|
67
|
62
|
#
|
68
|
63
|
# Args:
|
69
|
64
|
# project (Project): The project this file is in.
|
70
|
|
- # filepath (str): The path to the file.
|
71
|
|
- # key (str): The key to the file within the cache. Typically, this is the
|
72
|
|
- # value of `calculate_key()` with the file's unparsed contents
|
73
|
|
- # and any relevant metadata passed in.
|
74
|
|
- #
|
75
|
|
- # Returns:
|
76
|
|
- # (decorated dict): The parsed yaml from the cache, or None if the file isn't in the cache.
|
77
|
|
- def get(self, project, filepath, key):
|
78
|
|
- cache_path = self._get_filepath(project, filepath)
|
79
|
|
- project_name = project.name if project else ""
|
80
|
|
- try:
|
81
|
|
- project_cache = self._project_caches[project_name]
|
82
|
|
- try:
|
83
|
|
- cachedyaml = project_cache.elements[cache_path]
|
84
|
|
- if cachedyaml._key == key:
|
85
|
|
- # We've unpickled the YamlCache, but not the specific file
|
86
|
|
- if cachedyaml._contents is None:
|
87
|
|
- cachedyaml._contents = BstUnpickler.loads(cachedyaml._pickled_contents, self._context)
|
88
|
|
- return cachedyaml._contents
|
89
|
|
- except KeyError:
|
90
|
|
- pass
|
91
|
|
- except KeyError:
|
92
|
|
- pass
|
93
|
|
- return None
|
94
|
|
-
|
95
|
|
- # Put a parsed file into the cache.
|
96
|
|
- #
|
97
|
|
- # Args:
|
98
|
|
- # project (Project): The project this file is in.
|
99
|
|
- # filepath (str): The path to the file.
|
100
|
|
- # key (str): The key to the file within the cache. Typically, this is the
|
101
|
|
- # value of `calculate_key()` with the file's unparsed contents
|
102
|
|
- # and any relevant metadata passed in.
|
103
|
|
- # value (decorated dict): The data to put into the cache.
|
104
|
|
- def put(self, project, filepath, key, value):
|
105
|
|
- cache_path = self._get_filepath(project, filepath)
|
106
|
|
- project_name = project.name if project else ""
|
107
|
|
- try:
|
108
|
|
- project_cache = self._project_caches[project_name]
|
109
|
|
- except KeyError:
|
110
|
|
- project_cache = self._project_caches[project_name] = CachedProject({})
|
111
|
|
-
|
112
|
|
- project_cache.elements[cache_path] = CachedYaml(key, value)
|
113
|
|
-
|
114
|
|
- # Checks whether a file is cached
|
115
|
|
- # Args:
|
116
|
|
- # project (Project): The project this file is in.
|
117
|
65
|
# filepath (str): The path to the file, *relative to the project's directory*.
|
118
|
66
|
#
|
119
|
67
|
# Returns:
|
120
|
|
- # (bool): Whether the file is cached
|
|
68
|
+ # (bool): Whether the file is cached.
|
121
|
69
|
def is_cached(self, project, filepath):
|
122
|
70
|
cache_path = self._get_filepath(project, filepath)
|
123
|
71
|
project_name = project.name if project else ""
|
... |
... |
@@ -129,14 +77,8 @@ class YamlCache(): |
129
|
77
|
pass
|
130
|
78
|
return False
|
131
|
79
|
|
132
|
|
- def _get_filepath(self, project, full_path):
|
133
|
|
- if project:
|
134
|
|
- assert full_path.startswith(project.directory)
|
135
|
|
- filepath = os.path.relpath(full_path, project.directory)
|
136
|
|
- else:
|
137
|
|
- filepath = full_path
|
138
|
|
- return full_path
|
139
|
|
-
|
|
80
|
+ # open():
|
|
81
|
+ #
|
140
|
82
|
# Return an instance of the YamlCache which writes to disk when it leaves scope.
|
141
|
83
|
#
|
142
|
84
|
# Args:
|
... |
... |
@@ -163,18 +105,151 @@ class YamlCache(): |
163
|
105
|
|
164
|
106
|
yield cache
|
165
|
107
|
|
166
|
|
- cache.write(cachefile)
|
|
108
|
+ cache._write(cachefile)
|
|
109
|
+
|
|
110
|
+ # get_cache_file():
|
|
111
|
+ #
|
|
112
|
+ # Retrieves a path to the yaml cache file.
|
|
113
|
+ #
|
|
114
|
+ # Returns:
|
|
115
|
+ # (str): The path to the cache file
|
|
116
|
+ @staticmethod
|
|
117
|
+ def get_cache_file(top_dir):
|
|
118
|
+ return os.path.join(top_dir, ".bst", YAML_CACHE_FILENAME)
|
|
119
|
+
|
|
120
|
+ # get():
|
|
121
|
+ #
|
|
122
|
+ # Gets a parsed file from the cache.
|
|
123
|
+ #
|
|
124
|
+ # Args:
|
|
125
|
+ # project (Project) or None: The project this file is in, if it exists.
|
|
126
|
+ # filepath (str): The absolute path to the file.
|
|
127
|
+ # contents (str): The contents of the file to be cached
|
|
128
|
+ # copy_tree (bool): Whether the data should make a copy when it's being generated
|
|
129
|
+ # (i.e. exactly as when called in yaml)
|
|
130
|
+ #
|
|
131
|
+ # Returns:
|
|
132
|
+ # (decorated dict): The parsed yaml from the cache, or None if the file isn't in the cache.
|
|
133
|
+ # (str): The key used to look up the parsed yaml in the cache
|
|
134
|
+ def get(self, project, filepath, contents, copy_tree):
|
|
135
|
+ key = self._calculate_key(contents, copy_tree)
|
|
136
|
+ data = self._get(project, filepath, key)
|
|
137
|
+ return data, key
|
|
138
|
+
|
|
139
|
+ # put():
|
|
140
|
+ #
|
|
141
|
+ # Puts a parsed file into the cache.
|
|
142
|
+ #
|
|
143
|
+ # Args:
|
|
144
|
+ # project (Project): The project this file is in.
|
|
145
|
+ # filepath (str): The path to the file.
|
|
146
|
+ # contents (str): The contents of the file that has been cached
|
|
147
|
+ # copy_tree (bool): Whether the data should make a copy when it's being generated
|
|
148
|
+ # (i.e. exactly as when called in yaml)
|
|
149
|
+ # value (decorated dict): The data to put into the cache.
|
|
150
|
+ def put(self, project, filepath, contents, copy_tree, value):
|
|
151
|
+ key = self._calculate_key(contents, copy_tree)
|
|
152
|
+ self.put_from_key(project, filepath, key, value)
|
|
153
|
+
|
|
154
|
+ # put_from_key():
|
|
155
|
+ #
|
|
156
|
+ # Put a parsed file into the cache when given a key.
|
|
157
|
+ #
|
|
158
|
+ # Args:
|
|
159
|
+ # project (Project): The project this file is in.
|
|
160
|
+ # filepath (str): The path to the file.
|
|
161
|
+ # key (str): The key to the file within the cache. Typically, this is the
|
|
162
|
+ # value of `calculate_key()` with the file's unparsed contents
|
|
163
|
+ # and any relevant metadata passed in.
|
|
164
|
+ # value (decorated dict): The data to put into the cache.
|
|
165
|
+ def put_from_key(self, project, filepath, key, value):
|
|
166
|
+ cache_path = self._get_filepath(project, filepath)
|
|
167
|
+ project_name = project.name if project else ""
|
|
168
|
+ try:
|
|
169
|
+ project_cache = self._project_caches[project_name]
|
|
170
|
+ except KeyError:
|
|
171
|
+ project_cache = self._project_caches[project_name] = CachedProject({})
|
|
172
|
+
|
|
173
|
+ project_cache.elements[cache_path] = CachedYaml(key, value)
|
167
|
174
|
|
|
175
|
+
|
|
176
|
+ ###################
|
|
177
|
+ # Private Methods #
|
|
178
|
+ ###################
|
|
179
|
+
|
|
180
|
+ # Writes the yaml cache to the specified path.
|
|
181
|
+ #
|
|
182
|
+ # Args:
|
|
183
|
+ # path (str): The path to the cache file.
|
|
184
|
+ def _write(self, path):
|
|
185
|
+ parent_dir = os.path.dirname(path)
|
|
186
|
+ os.makedirs(parent_dir, exist_ok=True)
|
|
187
|
+ with open(path, "wb") as f:
|
|
188
|
+ BstPickler(f).dump(self)
|
|
189
|
+
|
|
190
|
+ # _get_filepath():
|
|
191
|
+ #
|
|
192
|
+ # Returns a file path relative to a project if passed, or the original path if
|
|
193
|
+ # the project is None
|
|
194
|
+ #
|
|
195
|
+ # Args:
|
|
196
|
+ # project (Project) or None: The project the filepath exists within
|
|
197
|
+ # full_path (str): The path that the returned path is based on
|
|
198
|
+ #
|
|
199
|
+ # Returns:
|
|
200
|
+ # (str): The path to the file, relative to a project if it exists
|
|
201
|
+ def _get_filepath(self, project, full_path):
|
|
202
|
+ if project:
|
|
203
|
+ assert full_path.startswith(project.directory)
|
|
204
|
+ filepath = os.path.relpath(full_path, project.directory)
|
|
205
|
+ else:
|
|
206
|
+ filepath = full_path
|
|
207
|
+ return full_path
|
|
208
|
+
|
|
209
|
+ # _calculate_key():
|
|
210
|
+ #
|
168
|
211
|
# Calculates a key for putting into the cache.
|
|
212
|
+ #
|
|
213
|
+ # Args:
|
|
214
|
+ # (basic object)... : Any number of strictly-ordered basic objects
|
|
215
|
+ #
|
|
216
|
+ # Returns:
|
|
217
|
+ # (str): A key made out of every arg passed in
|
169
|
218
|
@staticmethod
|
170
|
|
- def calculate_key(*args):
|
|
219
|
+ def _calculate_key(*args):
|
171
|
220
|
string = pickle.dumps(args)
|
172
|
221
|
return hashlib.sha1(string).hexdigest()
|
173
|
222
|
|
174
|
|
- # Retrieves a path to the yaml cache file.
|
175
|
|
- @staticmethod
|
176
|
|
- def get_cache_file(top_dir):
|
177
|
|
- return os.path.join(top_dir, ".bst", YAML_CACHE_FILENAME)
|
|
223
|
+ # _get():
|
|
224
|
+ #
|
|
225
|
+ # Gets a parsed file from the cache when given a key.
|
|
226
|
+ #
|
|
227
|
+ # Args:
|
|
228
|
+ # project (Project): The project this file is in.
|
|
229
|
+ # filepath (str): The path to the file.
|
|
230
|
+ # key (str): The key to the file within the cache. Typically, this is the
|
|
231
|
+ # value of `calculate_key()` with the file's unparsed contents
|
|
232
|
+ # and any relevant metadata passed in.
|
|
233
|
+ #
|
|
234
|
+ # Returns:
|
|
235
|
+ # (decorated dict): The parsed yaml from the cache, or None if the file isn't in the cache.
|
|
236
|
+ def _get(self, project, filepath, key):
|
|
237
|
+ cache_path = self._get_filepath(project, filepath)
|
|
238
|
+ project_name = project.name if project else ""
|
|
239
|
+ try:
|
|
240
|
+ project_cache = self._project_caches[project_name]
|
|
241
|
+ try:
|
|
242
|
+ cachedyaml = project_cache.elements[cache_path]
|
|
243
|
+ if cachedyaml._key == key:
|
|
244
|
+ # We've unpickled the YamlCache, but not the specific file
|
|
245
|
+ if cachedyaml._contents is None:
|
|
246
|
+ cachedyaml._contents = BstUnpickler.loads(cachedyaml._pickled_contents, self._context)
|
|
247
|
+ return cachedyaml._contents
|
|
248
|
+ except KeyError:
|
|
249
|
+ pass
|
|
250
|
+ except KeyError:
|
|
251
|
+ pass
|
|
252
|
+ return None
|
178
|
253
|
|
179
|
254
|
|
180
|
255
|
CachedProject = namedtuple('CachedProject', ['elements'])
|