diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py index d82bba403..34a342462 100644 --- a/notebook/services/contents/filemanager.py +++ b/notebook/services/contents/filemanager.py @@ -25,6 +25,7 @@ from . import tz from notebook.utils import ( is_hidden, to_api_path, + same_file, ) _script_exporter = None @@ -460,7 +461,7 @@ class FileContentsManager(FileManagerMixin, ContentsManager): old_os_path = self._get_os_path(old_path) # Should we proceed with the move? - if os.path.exists(new_os_path): + if os.path.exists(new_os_path) and not same_file(old_os_path, new_os_path): raise web.HTTPError(409, u'File already exists: %s' % new_path) # Move the file diff --git a/notebook/utils.py b/notebook/utils.py index 92a2410cc..fa083b029 100644 --- a/notebook/utils.py +++ b/notebook/utils.py @@ -141,6 +141,26 @@ def is_hidden(abs_path, abs_root=''): return False +def same_file(path, other_path): + """ + Check if path and other_path are hard links to the same file. This is a + utility implementation of Python's os.path.samefile which is not available + with Python 2.x and Windows. + + Parameters + ----------- + path: String representing a path to a file + other_path: String representing a path to another file + + Returns + ----------- + same: Boolean that is True if both path and other path are the same + """ + path_stat = os.stat(path) + other_path_stat = os.stat(other_path) + return (path_stat.st_ino == other_path_stat.st_ino and + path_stat.st_dev == other_path_stat.st_dev) + def to_os_path(path, root=''): """Convert an API path to a filesystem path