don't rely on cwd in media.py

media.py sets CWD to the media directory of the collection
(collection.media), and relies on that directory being
maintained as CWD throughout execution.  The original CWD
is restored in the close() function.

Remove reliance on CWD being set and maintained throughout
execution of media.py.  Improves portability and usability
in different codebases.
This commit is contained in:
Joel Kitching 2017-02-05 23:04:02 +08:00
parent 4693e71104
commit 975ca90225
2 changed files with 38 additions and 46 deletions

View File

@ -35,15 +35,6 @@ class MediaManager:
self._dir = re.sub("(?i)\.(anki2)$", ".media", self.col.path)
if not os.path.exists(self._dir):
os.makedirs(self._dir)
try:
self._oldcwd = os.getcwd()
except OSError:
# cwd doesn't exist
self._oldcwd = None
try:
os.chdir(self._dir)
except OSError:
raise Exception("invalidTempFolder")
# change database
self.connect()
@ -52,7 +43,6 @@ class MediaManager:
return
path = self.dir()+".db2"
create = not os.path.exists(path)
os.chdir(self._dir)
self.db = DB(path)
if create:
self._initDB()
@ -94,23 +84,16 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
# anew
self.col.log("failed to import old media db:"+traceback.format_exc())
self.db.execute("detach old")
npath = "../collection.media.db.old"
npath = os.path.join(self.dir(), "collection.media.db.old")
if os.path.exists(npath):
os.unlink(npath)
os.rename("../collection.media.db", npath)
os.rename(os.path.join(self.dir(), "collection.media.db"), npath)
def close(self):
if self.col.server:
return
self.db.close()
self.db = None
# change cwd back to old location
if self._oldcwd:
try:
os.chdir(self._oldcwd)
except:
# may have been deleted
pass
def dir(self):
return self._dir
@ -260,24 +243,26 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
files = local
renamedFiles = False
for file in files:
path = os.path.join(self.dir(), file)
if not local:
if not os.path.isfile(file):
if not os.path.isfile(path):
# ignore directories
continue
if file.startswith("_"):
# leading _ says to ignore file
continue
nfcFile = unicodedata.normalize("NFC", file)
nfcPath = os.path.join(self.dir(), nfcFile)
# we enforce NFC fs encoding on non-macs; on macs we'll have gotten
# NFD so we use the above variable for comparing references
if not isMac and not local:
if file != nfcFile:
# delete if we already have the NFC form, otherwise rename
if os.path.exists(nfcFile):
os.unlink(file)
if os.path.exists(nfcPath):
os.unlink(path)
renamedFiles = True
else:
os.rename(file, nfcFile)
os.rename(path, nfcPath)
renamedFiles = True
file = nfcFile
# compare
@ -347,8 +332,9 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
(added, removed) = self._changes()
media = []
for f in added:
mt = self._mtime(f)
media.append((f, self._checksum(f), mt, 1))
path = os.path.join(self.dir(), f)
mt = self._mtime(path)
media.append((f, self._checksum(path), mt, 1))
for f in removed:
media.append((f, None, 0, 1))
# update media db
@ -366,8 +352,9 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
removed = []
# loop through on-disk files
for f in os.listdir(self.dir()):
path = os.path.join(self.dir(), f)
# ignore folders and thumbs.db
if os.path.isdir(f):
if os.path.isdir(path):
continue
if f.lower() == "thumbs.db":
continue
@ -375,9 +362,9 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
if self.hasIllegal(f):
continue
# empty files are invalid; clean them up and continue
sz = os.path.getsize(f)
sz = os.path.getsize(path)
if not sz:
os.unlink(f)
os.unlink(path)
continue
if sz > 100*1024*1024:
self.col.log("ignoring file over 100MB", f)
@ -385,20 +372,21 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
# check encoding
if not isMac:
normf = unicodedata.normalize("NFC", f)
normpath = os.path.join(self.dir(), normf)
if f != normf:
# wrong filename encoding which will cause sync errors
if os.path.exists(normf):
os.unlink(f)
if os.path.exists(normpath):
os.unlink(path)
else:
os.rename(f, normf)
os.rename(path, normpath)
# newly added?
if f not in self.cache:
added.append(f)
else:
# modified since last time?
if self._mtime(f) != self.cache[f][1]:
if self._mtime(path) != self.cache[f][1]:
# and has different checksum?
if self._checksum(f) != self.cache[f][0]:
if self._checksum(path) != self.cache[f][0]:
added.append(f)
# mark as used
self.cache[f][2] = True
@ -429,8 +417,9 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
"update media set dirty=0 where fname=?", fname)
def syncDelete(self, fname):
if os.path.exists(fname):
os.unlink(fname)
path = os.path.join(self.dir(), fname)
if os.path.exists(path):
os.unlink(path)
self.db.execute("delete from media where fname=?", fname)
def mediaCount(self):
@ -467,14 +456,15 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
"select fname, csum from media where dirty=1"
" limit %d"%SYNC_ZIP_COUNT)):
path = os.path.join(self.dir(), fname)
fnames.append(fname)
normname = unicodedata.normalize("NFC", fname)
if csum:
self.col.log("+media zip", fname)
z.write(fname, str(c))
z.write(path, str(c))
meta.append((normname, str(c)))
sz += os.path.getsize(fname)
sz += os.path.getsize(path)
else:
self.col.log("-media zip", fname)
meta.append((normname, ""))
@ -509,9 +499,10 @@ create table meta (dirMod int, lastUsn int); insert into meta values (0, 0);
else:
name = unicodedata.normalize("NFC", name)
# save file
open(name, "wb").write(data)
path = os.path.join(self.dir(), name)
open(path, "wb").write(data)
# update db
media.append((name, csum, self._mtime(name), 0))
media.append((name, csum, self._mtime(path), 0))
cnt += 1
if media:
self.db.executemany(

View File

@ -77,29 +77,30 @@ def test_changes():
assert not list(added())
assert not list(removed())
# add a file
dir = tempfile.mkdtemp(prefix="anki")
path = os.path.join(dir, "foo.jpg")
open(path, "w").write("hello")
tmp_dir = tempfile.mkdtemp(prefix="anki")
tmp_path = os.path.join(tmp_dir, "foo.jpg")
open(tmp_path, "w").write("hello")
time.sleep(1)
path = d.media.addFile(path)
fname = d.media.addFile(tmp_path)
internal_path = os.path.join(d.media.dir(), fname)
# should have been logged
d.media.findChanges()
assert list(added())
assert not list(removed())
# if we modify it, the cache won't notice
time.sleep(1)
open(path, "w").write("world")
open(internal_path, "w").write("world")
assert len(list(added())) == 1
assert not list(removed())
# but if we add another file, it will
time.sleep(1)
open(path+"2", "w").write("yo")
open(internal_path+"2", "w").write("yo")
d.media.findChanges()
assert len(list(added())) == 2
assert not list(removed())
# deletions should get noticed too
time.sleep(1)
os.unlink(path+"2")
os.unlink(internal_path+"2")
d.media.findChanges()
assert len(list(added())) == 1
assert len(list(removed())) == 1