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

View file

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