Merge pull request #1064 from overviewer/fscaps
Try to track the capabilities of our outputdir filesystem.
This commit is contained in:
@@ -25,7 +25,7 @@ from PIL import Image
|
|||||||
|
|
||||||
import world
|
import world
|
||||||
import util
|
import util
|
||||||
from files import FileReplacer, mirror_dir
|
from files import FileReplacer, mirror_dir, get_fs_caps
|
||||||
|
|
||||||
class AssetManager(object):
|
class AssetManager(object):
|
||||||
"""\
|
"""\
|
||||||
@@ -44,6 +44,8 @@ directory.
|
|||||||
self.custom_assets_dir = custom_assets_dir
|
self.custom_assets_dir = custom_assets_dir
|
||||||
self.renders = dict()
|
self.renders = dict()
|
||||||
|
|
||||||
|
self.fs_caps = get_fs_caps(self.outputdir)
|
||||||
|
|
||||||
# look for overviewerConfig in self.outputdir
|
# look for overviewerConfig in self.outputdir
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c:
|
with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c:
|
||||||
@@ -148,7 +150,7 @@ directory.
|
|||||||
|
|
||||||
# write out config
|
# write out config
|
||||||
jsondump = json.dumps(dump, indent=4)
|
jsondump = json.dumps(dump, indent=4)
|
||||||
with FileReplacer(os.path.join(self.outputdir, "overviewerConfig.js")) as tmpfile:
|
with FileReplacer(os.path.join(self.outputdir, "overviewerConfig.js"), capabilities=self.fs_caps) as tmpfile:
|
||||||
with codecs.open(tmpfile, 'w', encoding='UTF-8') as f:
|
with codecs.open(tmpfile, 'w', encoding='UTF-8') as f:
|
||||||
f.write("var overviewerConfig = " + jsondump + ";\n")
|
f.write("var overviewerConfig = " + jsondump + ";\n")
|
||||||
|
|
||||||
@@ -162,12 +164,12 @@ directory.
|
|||||||
global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
|
global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
|
||||||
if not os.path.isdir(global_assets):
|
if not os.path.isdir(global_assets):
|
||||||
global_assets = os.path.join(util.get_program_path(), "web_assets")
|
global_assets = os.path.join(util.get_program_path(), "web_assets")
|
||||||
mirror_dir(global_assets, self.outputdir)
|
mirror_dir(global_assets, self.outputdir, capabilities=self.fs_caps)
|
||||||
|
|
||||||
if self.custom_assets_dir:
|
if self.custom_assets_dir:
|
||||||
# Could have done something fancy here rather than just overwriting
|
# Could have done something fancy here rather than just overwriting
|
||||||
# the global files, but apparently this what we used to do pre-rewrite.
|
# the global files, but apparently this what we used to do pre-rewrite.
|
||||||
mirror_dir(self.custom_assets_dir, self.outputdir)
|
mirror_dir(self.custom_assets_dir, self.outputdir, capabilities=self.fs_caps)
|
||||||
|
|
||||||
# write a dummy baseMarkers.js if none exists
|
# write a dummy baseMarkers.js if none exists
|
||||||
if not os.path.exists(os.path.join(self.outputdir, "baseMarkers.js")):
|
if not os.path.exists(os.path.join(self.outputdir, "baseMarkers.js")):
|
||||||
@@ -179,7 +181,7 @@ directory.
|
|||||||
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
||||||
if not os.path.isdir(js_src):
|
if not os.path.isdir(js_src):
|
||||||
js_src = os.path.join(util.get_program_path(), "js_src")
|
js_src = os.path.join(util.get_program_path(), "js_src")
|
||||||
with FileReplacer(os.path.join(self.outputdir, "overviewer.js")) as tmpfile:
|
with FileReplacer(os.path.join(self.outputdir, "overviewer.js"), capabilities=self.fs_caps) as tmpfile:
|
||||||
with open(tmpfile, "w") as fout:
|
with open(tmpfile, "w") as fout:
|
||||||
# first copy in js_src/overviewer.js
|
# first copy in js_src/overviewer.js
|
||||||
with open(os.path.join(js_src, "overviewer.js"), 'r') as f:
|
with open(os.path.join(js_src, "overviewer.js"), 'r') as f:
|
||||||
@@ -199,6 +201,6 @@ directory.
|
|||||||
versionstr = "%s (%s)" % (util.findGitVersion(), util.findGitHash()[:7])
|
versionstr = "%s (%s)" % (util.findGitVersion(), util.findGitHash()[:7])
|
||||||
index = index.replace("{version}", versionstr)
|
index = index.replace("{version}", versionstr)
|
||||||
|
|
||||||
with FileReplacer(indexpath) as indexpath:
|
with FileReplacer(indexpath, capabilities=self.fs_caps) as indexpath:
|
||||||
with codecs.open(indexpath, 'w', encoding='UTF-8') as output:
|
with codecs.open(indexpath, 'w', encoding='UTF-8') as output:
|
||||||
output.write(index)
|
output.write(index)
|
||||||
|
|||||||
@@ -20,9 +20,50 @@ import shutil
|
|||||||
import logging
|
import logging
|
||||||
import stat
|
import stat
|
||||||
|
|
||||||
|
default_caps = {"chmod_works": True, "rename_works": True}
|
||||||
|
|
||||||
|
def get_fs_caps(dir_to_test):
|
||||||
|
return {"chmod_works": does_chmod_work(dir_to_test),
|
||||||
|
"rename_works": does_rename_work(dir_to_test)
|
||||||
|
}
|
||||||
|
|
||||||
|
def does_chmod_work(dir_to_test):
|
||||||
|
"Detects if chmod works in a given directory"
|
||||||
|
# a CIFS mounted FS is the only thing known to reliably not provide chmod
|
||||||
|
|
||||||
|
if not os.path.isdir(dir_to_test):
|
||||||
|
return True
|
||||||
|
|
||||||
|
f1 = tempfile.NamedTemporaryFile(dir=dir_to_test)
|
||||||
|
try:
|
||||||
|
f1_stat = os.stat(f1.name)
|
||||||
|
os.chmod(f1.name, f1_stat.st_mode | stat.S_IRUSR)
|
||||||
|
chmod_works = True
|
||||||
|
logging.debug("Detected that chmods work in %r" % dir_to_test)
|
||||||
|
except OSError:
|
||||||
|
chmod_works = False
|
||||||
|
logging.debug("Detected that chmods do NOT work in %r" % dir_to_test)
|
||||||
|
return chmod_works
|
||||||
|
|
||||||
|
def does_rename_work(dir_to_test):
|
||||||
|
with tempfile.NamedTemporaryFile(dir=dir_to_test) as f1:
|
||||||
|
with tempfile.NamedTemporaryFile(dir=dir_to_test) as f2:
|
||||||
|
try:
|
||||||
|
os.rename(f1.name,f2.name)
|
||||||
|
except OSError:
|
||||||
|
renameworks = False
|
||||||
|
logging.debug("Detected that overwriting renames do NOT work in %r" % dir_to_test)
|
||||||
|
else:
|
||||||
|
renameworks = True
|
||||||
|
logging.debug("Detected that overwriting renames work in %r" % dir_to_test)
|
||||||
|
# re-make this file so it can be deleted without error
|
||||||
|
open(f1.name, 'w').close()
|
||||||
|
return renameworks
|
||||||
|
|
||||||
## useful recursive copy, that ignores common OS cruft
|
## useful recursive copy, that ignores common OS cruft
|
||||||
def mirror_dir(src, dst, entities=None):
|
def mirror_dir(src, dst, entities=None, capabilities=default_caps):
|
||||||
'''copies all of the entities from src to dst'''
|
'''copies all of the entities from src to dst'''
|
||||||
|
chmod_works = capabilities.get("chmod_works")
|
||||||
if not os.path.exists(dst):
|
if not os.path.exists(dst):
|
||||||
os.mkdir(dst)
|
os.mkdir(dst)
|
||||||
if entities and type(entities) != list: raise Exception("Expected a list, got a %r instead" % type(entities))
|
if entities and type(entities) != list: raise Exception("Expected a list, got a %r instead" % type(entities))
|
||||||
@@ -38,10 +79,13 @@ def mirror_dir(src, dst, entities=None):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if os.path.isdir(os.path.join(src,entry)):
|
if os.path.isdir(os.path.join(src,entry)):
|
||||||
mirror_dir(os.path.join(src, entry), os.path.join(dst, entry))
|
mirror_dir(os.path.join(src, entry), os.path.join(dst, entry), capabilities=capabilities)
|
||||||
elif os.path.isfile(os.path.join(src,entry)):
|
elif os.path.isfile(os.path.join(src,entry)):
|
||||||
try:
|
try:
|
||||||
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
|
if chmod_works:
|
||||||
|
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
|
||||||
|
else:
|
||||||
|
shutil.copyfile(os.path.join(src, entry), os.path.join(dst, entry))
|
||||||
except IOError as outer:
|
except IOError as outer:
|
||||||
try:
|
try:
|
||||||
# maybe permission problems?
|
# maybe permission problems?
|
||||||
@@ -51,24 +95,16 @@ def mirror_dir(src, dst, entities=None):
|
|||||||
os.chmod(os.path.join(dst, entry), dst_stat.st_mode | stat.S_IWUSR)
|
os.chmod(os.path.join(dst, entry), dst_stat.st_mode | stat.S_IWUSR)
|
||||||
except OSError: # we don't care if this fails
|
except OSError: # we don't care if this fails
|
||||||
pass
|
pass
|
||||||
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
|
# try again; if this stills throws an error, let it propagate up
|
||||||
# if this stills throws an error, let it propagate up
|
if chmod_works:
|
||||||
|
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
|
||||||
|
else:
|
||||||
|
shutil.copyfile(os.path.join(src, entry), os.path.join(dst, entry))
|
||||||
|
|
||||||
# Define a context manager to handle atomic renaming or "just forget it write
|
# Define a context manager to handle atomic renaming or "just forget it write
|
||||||
# straight to the file" depending on whether os.rename provides atomic
|
# straight to the file" depending on whether os.rename provides atomic
|
||||||
# overwrites.
|
# overwrites.
|
||||||
# Detect whether os.rename will overwrite files
|
# Detect whether os.rename will overwrite files
|
||||||
with tempfile.NamedTemporaryFile() as f1:
|
|
||||||
with tempfile.NamedTemporaryFile() as f2:
|
|
||||||
try:
|
|
||||||
os.rename(f1.name,f2.name)
|
|
||||||
except OSError:
|
|
||||||
renameworks = False
|
|
||||||
else:
|
|
||||||
renameworks = True
|
|
||||||
# re-make this file so it can be deleted without error
|
|
||||||
open(f1.name, 'w').close()
|
|
||||||
del tempfile,f1,f2
|
|
||||||
doc = """This class acts as a context manager for files that are to be written
|
doc = """This class acts as a context manager for files that are to be written
|
||||||
out overwriting an existing file.
|
out overwriting an existing file.
|
||||||
|
|
||||||
@@ -89,16 +125,20 @@ with FileReplacer("config") as configname:
|
|||||||
with open(configout, 'w') as configout:
|
with open(configout, 'w') as configout:
|
||||||
configout.write(newconfig)
|
configout.write(newconfig)
|
||||||
"""
|
"""
|
||||||
if renameworks:
|
class FileReplacer(object):
|
||||||
class FileReplacer(object):
|
__doc__ = doc
|
||||||
__doc__ = doc
|
def __init__(self, destname, capabilities=default_caps):
|
||||||
def __init__(self, destname):
|
self.caps = capabilities
|
||||||
self.destname = destname
|
self.destname = destname
|
||||||
|
if self.caps.get("rename_works"):
|
||||||
self.tmpname = destname + ".tmp"
|
self.tmpname = destname + ".tmp"
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
if self.caps.get("rename_works"):
|
||||||
# rename works here. Return a temporary filename
|
# rename works here. Return a temporary filename
|
||||||
return self.tmpname
|
return self.tmpname
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
return self.destname
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if self.caps.get("rename_works"):
|
||||||
if exc_type:
|
if exc_type:
|
||||||
# error
|
# error
|
||||||
try:
|
try:
|
||||||
@@ -109,18 +149,7 @@ if renameworks:
|
|||||||
"'%s'!", self.tmpname)
|
"'%s'!", self.tmpname)
|
||||||
else:
|
else:
|
||||||
# copy permission bits, if needed
|
# copy permission bits, if needed
|
||||||
if os.path.exists(self.destname):
|
if self.caps.get("chmod_works") and os.path.exists(self.destname):
|
||||||
shutil.copymode(self.destname, self.tmpname)
|
shutil.copymode(self.destname, self.tmpname)
|
||||||
# atomic rename into place
|
# atomic rename into place
|
||||||
os.rename(self.tmpname, self.destname)
|
os.rename(self.tmpname, self.destname)
|
||||||
else:
|
|
||||||
class FileReplacer(object):
|
|
||||||
__doc__ = doc
|
|
||||||
def __init__(self, destname):
|
|
||||||
self.destname = destname
|
|
||||||
def __enter__(self):
|
|
||||||
return self.destname
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
return
|
|
||||||
del renameworks
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from PIL import Image
|
|||||||
|
|
||||||
from .util import roundrobin
|
from .util import roundrobin
|
||||||
from . import nbt
|
from . import nbt
|
||||||
from .files import FileReplacer
|
from .files import FileReplacer, get_fs_caps
|
||||||
from .optimizeimages import optimize_image
|
from .optimizeimages import optimize_image
|
||||||
import rendermodes
|
import rendermodes
|
||||||
import c_overviewer
|
import c_overviewer
|
||||||
@@ -357,6 +357,9 @@ class TileSet(object):
|
|||||||
self.options['renderchecks'] = 2
|
self.options['renderchecks'] = 2
|
||||||
os.mkdir(self.outputdir)
|
os.mkdir(self.outputdir)
|
||||||
|
|
||||||
|
# must wait until outputdir exists
|
||||||
|
self.fs_caps = get_fs_caps(self.outputdir)
|
||||||
|
|
||||||
if self.options['renderchecks'] == 2:
|
if self.options['renderchecks'] == 2:
|
||||||
# Set forcerendertime so that upon an interruption the next render
|
# Set forcerendertime so that upon an interruption the next render
|
||||||
# will continue where we left off.
|
# will continue where we left off.
|
||||||
@@ -902,7 +905,7 @@ class TileSet(object):
|
|||||||
logging.error("While attempting to delete corrupt image %s, an error was encountered. You will need to delete it yourself. Error was '%s'", path[1], e)
|
logging.error("While attempting to delete corrupt image %s, an error was encountered. You will need to delete it yourself. Error was '%s'", path[1], e)
|
||||||
|
|
||||||
# Save it
|
# Save it
|
||||||
with FileReplacer(imgpath) as tmppath:
|
with FileReplacer(imgpath, capabilities=self.fs_caps) as tmppath:
|
||||||
if imgformat == 'jpg':
|
if imgformat == 'jpg':
|
||||||
img.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
img.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
||||||
else: # png
|
else: # png
|
||||||
@@ -1006,7 +1009,7 @@ class TileSet(object):
|
|||||||
#draw.text((96,96), "c,r: %s,%s" % (col, row), fill='red')
|
#draw.text((96,96), "c,r: %s,%s" % (col, row), fill='red')
|
||||||
|
|
||||||
# Save them
|
# Save them
|
||||||
with FileReplacer(imgpath) as tmppath:
|
with FileReplacer(imgpath, capabilities=self.fs_caps) as tmppath:
|
||||||
if self.imgextension == 'jpg':
|
if self.imgextension == 'jpg':
|
||||||
tileimg.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
tileimg.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
||||||
else: # png
|
else: # png
|
||||||
|
|||||||
Reference in New Issue
Block a user