def test_paths(tmpdir, HOME): from homely._utils import isnecessarypath l1 = os.path.join(tmpdir, 'link1') d1 = os.path.join(tmpdir, 'dir1') d1s = os.path.join(d1, 'subdir') l1s = os.path.join(l1, 'subdir') os.mkdir(d1) os.mkdir(d1s) os.symlink(d1, l1) assert os.path.isdir(l1s) assert isnecessarypath(tmpdir, d1) assert isnecessarypath(tmpdir, l1) assert isnecessarypath(l1, l1s) assert isnecessarypath(d1, l1s) assert not isnecessarypath(d1, l1)
def _trycleanpath(self, path, type_, conflicts): def _discard(): note(" Forgetting about %s %s" % (type_, path)) self._old_paths_owned.pop(path) self._postponed.discard(path) self._created.discard(path) self._savecfg() def _remove(): # remove the thing if type_ == self.TYPE_FOLDER_ONLY: # TODO: what do we do if the folder isn't empty? with note("Removing dir %s" % path): try: os.rmdir(path) except OSError as err: from errno import ENOTEMPTY if err.errno == ENOTEMPTY: warn("Directory not empty: {}".format(path)) else: raise elif type_ == self.TYPE_FILE_ALL: note("Removing {}".format(path)) os.unlink(path) elif type_ == self.TYPE_FILE_PART: if os.stat(path).st_size == 0: note("Removing empty {}".format(path)) os.unlink(path) else: note("Refusing to remove non-empty {}".format(path)) else: note("Removing link {}".format(path)) os.unlink(path) _discard() def _postpone(): with note("Postponing cleanup of path: {}".format(path)): self._postponed.add(path) assert path not in self._new_paths_owned self._new_paths_owned[path] = type_ self._old_paths_owned.pop(path) self._savecfg() # if we didn't create the path, then we don't need to clean it up if path not in self._created: return _discard() # if the path no longer exists, we have nothing to do if not _exists(path): return _discard() # if the thing has the wrong type, we'll issue an note() and just skip if type_ in (self.TYPE_FILE_PART, self.TYPE_FILE_ALL): correcttype = os.path.isfile(path) elif type_ in (self.TYPE_FOLDER_ONLY, self.TYPE_FOLDER_ALL): correcttype = os.path.isdir(path) else: assert type_ == self.TYPE_LINK correcttype = os.path.islink(path) if not correcttype: with note("Ignoring: Won't remove {} as it is no longer a {}" .format(path, type_)): return _discard() # work out if there is another path we need to remove first for otherpath in self._old_paths_owned: if otherpath != path and isnecessarypath(path, otherpath): # If there's another path we need to do first, then don't do # anything just yet. NOTE: there is an assertion to ensure that # we can't get stuck in an infinite loop repeatedly not # removing things. return # if any helpers want the path, don't delete it wantedby = None for c in self._new_cleaners: if c.wantspath(path): wantedby = c break if not wantedby: for otherpath in self._new_paths_owned: if isnecessarypath(path, otherpath): wantedby = otherpath if wantedby: # if we previously postponed this path, keep postponing it # while there are still things hanging around that want it if path in self._postponed: return _postpone() if conflicts == self.ASK: raise Exception("TODO: ask the user what to do") # noqa # NOTE: options are: # A) postpone until it can run later # B) discard it if conflicts == self.RAISE: raise CleanupConflict(conflictpath=path, pathwanter=wantedby) if conflicts == self.POSTPONE: return _postpone() assert conflicts == self.WARN warn("Couldn't clean up path {}; it is still needed for {}" .format(path, wantedby)) return _discard() # if nothing else wants this path, clean it up now return _remove()
def affectspath(self, path): return isnecessarypath(self._real_clone_to, path)
def wantspath(self, path): return path == self._filename or isnecessarypath(path, self._filename)