def _symlink(path, link, overwrite=0, verbose=0): """ Windows helper for ub.symlink """ if exists(link) and not os.path.islink(link): # On windows a broken link might still exist as a hard link or a # junction. Overwrite it if it is a file and we cannot symlink. # However, if it is a non-junction directory then do not overwrite if verbose: print('link location already exists') is_junc = _win32_is_junction(link) # NOTE: # in python2 broken junctions are directories and exist # in python3 broken junctions are directories and do not exist if os.path.isdir(link): if is_junc: pointed = _win32_read_junction(link) if path == pointed: if verbose: print( '...and is a junction that points to the same place' ) return link else: if verbose: if not exists(pointed): print( '...and is a broken junction that points somewhere else' ) else: print( '...and is a junction that points somewhere else' ) else: if verbose: print('...and is an existing real directory!') raise IOError('Cannot overwrite a real directory') elif os.path.isfile(link): if _win32_is_hardlinked(link, path): if verbose: print( '...and is a hard link that points to the same place') return link else: if verbose: print('...and is a hard link that points somewhere else') if _win32_can_symlink(): raise IOError( 'Cannot overwrite potentially real file if we can symlink' ) if overwrite: if verbose: print('...overwriting') util_io.delete(link, verbose > 1) else: if exists(link): raise IOError('Link already exists') _win32_symlink2(path, link, verbose=verbose)
def delete(self): """ Removes a file or recursively removes a directory. If a path does not exist, then this is does nothing. SeeAlso: :func:`ubelt.delete` Returns: Path: reference to self Example: >>> import ubelt as ub >>> from os.path import join >>> base = ub.Path(ub.ensure_app_cache_dir('ubelt', 'delete_test2')) >>> dpath1 = (base / 'dir').ensuredir() >>> (base / 'dir' / 'subdir').ensuredir() >>> (base / 'dir' / 'to_remove1.txt').touch() >>> fpath1 = (base / 'dir' / 'subdir' / 'to_remove3.txt').touch() >>> fpath2 = (base / 'dir' / 'subdir' / 'to_remove2.txt').touch() >>> assert all(p.exists() for p in [dpath1, fpath1, fpath2]) >>> fpath1.delete() >>> assert all(p.exists() for p in [dpath1, fpath2]) >>> assert not fpath1.exists() >>> dpath1.delete() >>> assert not any(p.exists() for p in [dpath1, fpath1, fpath2]) """ util_io.delete(self) return self
def _win32_can_symlink(verbose=0, force=0, testing=0): """ Args: verbose (int, default=0): flag force (int, default=0): flag testing (int, default=0): flag Example: >>> # xdoctest: +REQUIRES(WIN32) >>> import ubelt as ub >>> _win32_can_symlink(verbose=1, force=1, testing=1) """ global __win32_can_symlink__ if verbose: print('__win32_can_symlink__ = {!r}'.format(__win32_can_symlink__)) if __win32_can_symlink__ is not None and not force: return __win32_can_symlink__ from ubelt import util_platform tempdir = util_platform.ensure_app_cache_dir('ubelt', '_win32_can_symlink') util_io.delete(tempdir) util_path.ensuredir(tempdir) dpath = join(tempdir, 'dpath') fpath = join(tempdir, 'fpath.txt') dlink = join(tempdir, 'dlink') flink = join(tempdir, 'flink.txt') util_path.ensuredir(dpath) util_io.touch(fpath) # Add broken variants of the links for testing purposes # Its ugly, but so is all this windows code. if testing: broken_dpath = join(tempdir, 'broken_dpath') broken_fpath = join(tempdir, 'broken_fpath.txt') # Create files that we will delete after we link to them util_path.ensuredir(broken_dpath) util_io.touch(broken_fpath) try: _win32_symlink(dpath, dlink) if testing: _win32_symlink(broken_dpath, join(tempdir, 'broken_dlink')) can_symlink_directories = os.path.islink(dlink) except OSError: can_symlink_directories = False if verbose: print('can_symlink_directories = {!r}'.format(can_symlink_directories)) try: _win32_symlink(fpath, flink) if testing: _win32_symlink(broken_fpath, join(tempdir, 'broken_flink')) can_symlink_files = os.path.islink(flink) # os.path.islink(flink) except OSError: can_symlink_files = False if verbose: print('can_symlink_files = {!r}'.format(can_symlink_files)) if int(can_symlink_directories) + int(can_symlink_files) == 1: raise AssertionError( 'can do one but not both. Unexpected {} {}'.format( can_symlink_directories, can_symlink_files)) try: # test that we can create junctions, even if symlinks are disabled djunc = _win32_junction(dpath, join(tempdir, 'djunc')) fjunc = _win32_junction(fpath, join(tempdir, 'fjunc.txt')) if testing: _win32_junction(broken_dpath, join(tempdir, 'broken_djunc')) _win32_junction(broken_fpath, join(tempdir, 'broken_fjunc.txt')) if not _win32_is_junction(djunc): raise AssertionError('expected junction') if not _win32_is_hardlinked(fpath, fjunc): raise AssertionError('expected hardlink') except Exception: warnings.warn('We cannot create junctions either!') raise if testing: # break the links util_io.delete(broken_dpath) util_io.delete(broken_fpath) if verbose: from ubelt import util_links util_links._dirstats(tempdir) try: # Cleanup the test directory util_io.delete(tempdir) except Exception: print('ERROR IN DELETE') from ubelt import util_links util_links._dirstats(tempdir) raise can_symlink = can_symlink_directories and can_symlink_files __win32_can_symlink__ = can_symlink if not can_symlink: warnings.warn('Cannot make real symlink. Falling back to junction') if verbose: print('can_symlink = {!r}'.format(can_symlink)) print('__win32_can_symlink__ = {!r}'.format(__win32_can_symlink__)) return can_symlink
def symlink(real_path, link_path, overwrite=False, verbose=0): """ Create a symbolic link. This will work on linux or windows, however windows does have some corner cases. For more details see notes in :mod:`ubelt._win32_links`. Args: path (PathLike): path to real file or directory link_path (PathLike): path to desired location for symlink overwrite (bool, default=False): overwrite existing symlinks. This will not overwrite real files on systems with proper symlinks. However, on older versions of windows junctions are indistinguishable from real files, so we cannot make this guarantee. verbose (int, default=0): verbosity level Returns: PathLike: link path Example: >>> import ubelt as ub >>> dpath = ub.ensure_app_cache_dir('ubelt', 'test_symlink0') >>> real_path = join(dpath, 'real_file.txt') >>> link_path = join(dpath, 'link_file.txt') >>> [ub.delete(p) for p in [real_path, link_path]] >>> ub.writeto(real_path, 'foo') >>> result = symlink(real_path, link_path) >>> assert ub.readfrom(result) == 'foo' >>> [ub.delete(p) for p in [real_path, link_path]] Example: >>> import ubelt as ub >>> from os.path import dirname >>> dpath = ub.ensure_app_cache_dir('ubelt', 'test_symlink1') >>> ub.delete(dpath) >>> ub.ensuredir(dpath) >>> _dirstats(dpath) >>> real_dpath = ub.ensuredir((dpath, 'real_dpath')) >>> link_dpath = ub.augpath(real_dpath, base='link_dpath') >>> real_path = join(dpath, 'afile.txt') >>> link_path = join(dpath, 'afile.txt') >>> [ub.delete(p) for p in [real_path, link_path]] >>> ub.writeto(real_path, 'foo') >>> result = symlink(real_dpath, link_dpath) >>> assert ub.readfrom(link_path) == 'foo', 'read should be same' >>> ub.writeto(link_path, 'bar') >>> _dirstats(dpath) >>> assert ub.readfrom(link_path) == 'bar', 'very bad bar' >>> assert ub.readfrom(real_path) == 'bar', 'changing link did not change real' >>> ub.writeto(real_path, 'baz') >>> _dirstats(dpath) >>> assert ub.readfrom(real_path) == 'baz', 'very bad baz' >>> assert ub.readfrom(link_path) == 'baz', 'changing real did not change link' >>> ub.delete(link_dpath, verbose=1) >>> _dirstats(dpath) >>> assert not exists(link_dpath), 'link should not exist' >>> assert exists(real_path), 'real path should exist' >>> _dirstats(dpath) >>> ub.delete(dpath, verbose=1) >>> _dirstats(dpath) >>> assert not exists(real_path) """ path = normpath(real_path) link = normpath(link_path) if not os.path.isabs(path): # if path is not absolute it must be specified relative to link if _can_symlink(): path = os.path.relpath(path, os.path.dirname(link)) else: # nocover # On windows, we need to use absolute paths path = os.path.abspath(path) if verbose: print('Symlink: {path} -> {link}'.format(path=path, link=link)) if islink(link): if verbose: print('... already exists') pointed = _readlink(link) if pointed == path: if verbose > 1: print('... and points to the right place') return link if verbose > 1: if not exists(link): print('... but it is broken and points somewhere else: {}'. format(pointed)) else: print('... but it points somewhere else: {}'.format(pointed)) if overwrite: util_io.delete(link, verbose=verbose > 1) elif exists(link): if _win32_links is None: if verbose: print('... already exists, but its a file. This will error.') raise FileExistsError( 'cannot overwrite a physical path: "{}"'.format(path)) else: # nocover if verbose: print('... already exists, and is either a file or hard link. ' 'Assuming it is a hard link. ' 'On non-win32 systems this would error.') if _win32_links is None: os.symlink(path, link) else: # nocover _win32_links._symlink(path, link, overwrite=overwrite, verbose=verbose) return link
def symlink(real_path, link_path, overwrite=False, verbose=0): """ Create a symbolic link. This will work on linux or windows, however windows does have some corner cases. For more details see notes in `ubelt._win32_links`. Args: path (str): path to real file or directory link_path (str): path to desired location for symlink overwrite (bool): overwrite existing symlinks. This will not overwrite real files on systems with proper symlinks. However, on older versions of windows junctions are indistinguishable from real files, so we cannot make this guarantee. (default = False) verbose (int): verbosity level (default=0) Returns: str: link path CommandLine: python -m ubelt.util_links symlink:0 Example: >>> import ubelt as ub >>> dpath = ub.ensure_app_cache_dir('ubelt', 'test_symlink0') >>> real_path = join(dpath, 'real_file.txt') >>> link_path = join(dpath, 'link_file.txt') >>> [ub.delete(p) for p in [real_path, link_path]] >>> ub.writeto(real_path, 'foo') >>> result = symlink(real_path, link_path) >>> assert ub.readfrom(result) == 'foo' >>> [ub.delete(p) for p in [real_path, link_path]] Example: >>> import ubelt as ub >>> from os.path import dirname >>> dpath = ub.ensure_app_cache_dir('ubelt', 'test_symlink1') >>> ub.delete(dpath) >>> ub.ensuredir(dpath) >>> _dirstats(dpath) >>> real_dpath = ub.ensuredir((dpath, 'real_dpath')) >>> link_dpath = ub.augpath(real_dpath, base='link_dpath') >>> real_path = join(dpath, 'afile.txt') >>> link_path = join(dpath, 'afile.txt') >>> [ub.delete(p) for p in [real_path, link_path]] >>> ub.writeto(real_path, 'foo') >>> result = symlink(real_dpath, link_dpath) >>> assert ub.readfrom(link_path) == 'foo', 'read should be same' >>> ub.writeto(link_path, 'bar') >>> _dirstats(dpath) >>> assert ub.readfrom(link_path) == 'bar', 'very bad bar' >>> assert ub.readfrom(real_path) == 'bar', 'changing link did not change real' >>> ub.writeto(real_path, 'baz') >>> _dirstats(dpath) >>> assert ub.readfrom(real_path) == 'baz', 'very bad baz' >>> assert ub.readfrom(link_path) == 'baz', 'changing real did not change link' >>> ub.delete(link_dpath, verbose=1) >>> _dirstats(dpath) >>> assert not exists(link_dpath), 'link should not exist' >>> assert exists(real_path), 'real path should exist' >>> _dirstats(dpath) >>> ub.delete(dpath, verbose=1) >>> _dirstats(dpath) >>> assert not exists(real_path) """ path = normpath(real_path) link = normpath(link_path) if not os.path.isabs(path): # if path is not absolute it must be specified relative to link if _can_symlink(): path = os.path.relpath(path, os.path.dirname(link)) else: # nocover # On windows, we need to use absolute paths path = os.path.abspath(path) if verbose: print('Creating symlink: path={} link={}'.format(path, link)) if islink(link): if verbose: print('symlink already exists') if _readlink(link) == path: if verbose > 1: print('... and points to the right place') return link if verbose > 1: if not exists(link): print('... but it is broken and points somewhere else') else: print('... but it points somewhere else') if overwrite: util_io.delete(link, verbose > 1) if _win32_links: # nocover _win32_links._symlink(path, link, overwrite=overwrite, verbose=verbose) else: os.symlink(path, link) return link