|
1 | 1 | """ |
2 | 2 | Module for executing git commands, sending results back to the handlers |
3 | 3 | """ |
| 4 | +import datetime |
4 | 5 | import os |
| 6 | +import pathlib |
5 | 7 | import re |
6 | 8 | import shlex |
7 | 9 | import subprocess |
8 | 10 | from urllib.parse import unquote |
9 | 11 |
|
10 | | -import pathlib |
| 12 | +import nbformat |
11 | 13 | import pexpect |
12 | 14 | import tornado |
13 | 15 | import tornado.locks |
14 | | -import datetime |
| 16 | +from nbdime import diff_notebooks |
15 | 17 |
|
16 | 18 | from .log import get_logger |
17 | 19 |
|
18 | | - |
19 | 20 | # Regex pattern to capture (key, value) of Git configuration options. |
20 | 21 | # See https://git-scm.com/docs/git-config#_syntax for git var syntax |
21 | 22 | CONFIG_PATTERN = re.compile(r"(?:^|\n)([\w\-\.]+)\=") |
@@ -322,6 +323,40 @@ async def fetch(self, current_path): |
322 | 323 |
|
323 | 324 | return result |
324 | 325 |
|
| 326 | + async def get_nbdiff(self, prev_content: str, curr_content: str) -> dict: |
| 327 | + """Compute the diff between two notebooks. |
| 328 | +
|
| 329 | + Args: |
| 330 | + prev_content: Notebook previous content |
| 331 | + curr_content: Notebook current content |
| 332 | + Returns: |
| 333 | + {"base": Dict, "diff": Dict} |
| 334 | + """ |
| 335 | + |
| 336 | + def read_notebook(content): |
| 337 | + if not content: |
| 338 | + return nbformat.versions[nbformat.current_nbformat].new_notebook() |
| 339 | + if isinstance(content, dict): |
| 340 | + # Content may come from model as a dict directly |
| 341 | + return ( |
| 342 | + nbformat.versions[ |
| 343 | + content.get("nbformat", nbformat.current_nbformat) |
| 344 | + ] |
| 345 | + .nbjson.JSONReader() |
| 346 | + .to_notebook(content) |
| 347 | + ) |
| 348 | + else: |
| 349 | + return nbformat.reads(content, as_version=4) |
| 350 | + |
| 351 | + current_loop = tornado.ioloop.IOLoop.current() |
| 352 | + prev_nb = await current_loop.run_in_executor(None, read_notebook, prev_content) |
| 353 | + curr_nb = await current_loop.run_in_executor(None, read_notebook, curr_content) |
| 354 | + thediff = await current_loop.run_in_executor( |
| 355 | + None, diff_notebooks, prev_nb, curr_nb |
| 356 | + ) |
| 357 | + |
| 358 | + return {"base": prev_nb, "diff": thediff} |
| 359 | + |
325 | 360 | async def status(self, current_path): |
326 | 361 | """ |
327 | 362 | Execute git status command & return the result. |
@@ -1156,6 +1191,7 @@ async def show(self, filename, ref, top_repo_path): |
1156 | 1191 | filename |
1157 | 1192 | ), |
1158 | 1193 | "fatal: Path '{}' does not exist in '{}'".format(filename, ref), |
| 1194 | + "fatal: Invalid object name 'HEAD'", |
1159 | 1195 | ], |
1160 | 1196 | ) |
1161 | 1197 | lower_error = error.lower() |
@@ -1188,48 +1224,39 @@ def get_content(self, filename, top_repo_path): |
1188 | 1224 | raise error |
1189 | 1225 | return model["content"] |
1190 | 1226 |
|
1191 | | - async def diff_content(self, filename, prev_ref, curr_ref, top_repo_path): |
| 1227 | + async def get_content_at_reference(self, filename, reference, top_repo_path): |
1192 | 1228 | """ |
1193 | | - Collect get content of prev and curr and return. |
| 1229 | + Collect get content of the file at the git reference. |
1194 | 1230 | """ |
1195 | | - if prev_ref["git"]: |
1196 | | - is_binary = await self._is_binary(filename, prev_ref["git"], top_repo_path) |
1197 | | - if is_binary: |
1198 | | - raise tornado.web.HTTPError( |
1199 | | - log_message="Error occurred while executing command to retrieve plaintext diff as file is not UTF-8." |
1200 | | - ) |
1201 | | - |
1202 | | - prev_content = await self.show(filename, prev_ref["git"], top_repo_path) |
1203 | | - else: |
1204 | | - prev_content = "" |
1205 | | - |
1206 | | - if "special" in curr_ref: |
1207 | | - if curr_ref["special"] == "WORKING": |
1208 | | - curr_content = self.get_content(filename, top_repo_path) |
1209 | | - elif curr_ref["special"] == "INDEX": |
| 1231 | + if "special" in reference: |
| 1232 | + if reference["special"] == "WORKING": |
| 1233 | + content = self.get_content(filename, top_repo_path) |
| 1234 | + elif reference["special"] == "INDEX": |
1210 | 1235 | is_binary = await self._is_binary(filename, "INDEX", top_repo_path) |
1211 | 1236 | if is_binary: |
1212 | 1237 | raise tornado.web.HTTPError( |
1213 | | - log_message="Error occurred while executing command to retrieve plaintext diff as file is not UTF-8." |
| 1238 | + log_message="Error occurred while executing command to retrieve plaintext content as file is not UTF-8." |
1214 | 1239 | ) |
1215 | 1240 |
|
1216 | | - curr_content = await self.show(filename, "", top_repo_path) |
| 1241 | + content = await self.show(filename, "", top_repo_path) |
1217 | 1242 | else: |
1218 | 1243 | raise tornado.web.HTTPError( |
1219 | | - log_message="Error while retrieving plaintext diff, unknown special ref '{}'.".format( |
1220 | | - curr_ref["special"] |
| 1244 | + log_message="Error while retrieving plaintext content, unknown special ref '{}'.".format( |
| 1245 | + reference["special"] |
1221 | 1246 | ) |
1222 | 1247 | ) |
1223 | | - else: |
1224 | | - is_binary = await self._is_binary(filename, curr_ref["git"], top_repo_path) |
| 1248 | + elif reference["git"]: |
| 1249 | + is_binary = await self._is_binary(filename, reference["git"], top_repo_path) |
1225 | 1250 | if is_binary: |
1226 | 1251 | raise tornado.web.HTTPError( |
1227 | | - log_message="Error occurred while executing command to retrieve plaintext diff as file is not UTF-8." |
| 1252 | + log_message="Error occurred while executing command to retrieve plaintext content as file is not UTF-8." |
1228 | 1253 | ) |
1229 | 1254 |
|
1230 | | - curr_content = await self.show(filename, curr_ref["git"], top_repo_path) |
| 1255 | + content = await self.show(filename, reference["git"], top_repo_path) |
| 1256 | + else: |
| 1257 | + content = "" |
1231 | 1258 |
|
1232 | | - return {"prev_content": prev_content, "curr_content": curr_content} |
| 1259 | + return {"content": content} |
1233 | 1260 |
|
1234 | 1261 | async def _is_binary(self, filename, ref, top_repo_path): |
1235 | 1262 | """ |
@@ -1275,10 +1302,17 @@ async def _is_binary(self, filename, ref, top_repo_path): |
1275 | 1302 | code, output, error = await execute(command, cwd=top_repo_path) |
1276 | 1303 |
|
1277 | 1304 | if code != 0: |
1278 | | - err_msg = "fatal: Path '{}' does not exist (neither on disk nor in the index)".format( |
1279 | | - filename |
1280 | | - ).lower() |
1281 | | - if err_msg in error.lower(): |
| 1305 | + error_messages = map( |
| 1306 | + lambda n: n.lower(), |
| 1307 | + [ |
| 1308 | + "fatal: Path '{}' does not exist (neither on disk nor in the index)".format( |
| 1309 | + filename |
| 1310 | + ), |
| 1311 | + "fatal: bad revision 'HEAD'", |
| 1312 | + ], |
| 1313 | + ) |
| 1314 | + lower_error = error.lower() |
| 1315 | + if any([msg in lower_error for msg in error_messages]): |
1282 | 1316 | return False |
1283 | 1317 |
|
1284 | 1318 | raise tornado.web.HTTPError( |
|
0 commit comments