|
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