Platform specific directories now have one module for each platform.

Added some notes about install mode and build scripts
sys.executable is no longer used.
Added getSrcFolder for finding the src/ folder of a source checkout.
This commit is contained in:
David Vierra 2015-07-05 00:36:56 -10:00
parent 1e4ae2e688
commit d1eda07ac1
6 changed files with 183 additions and 45 deletions

View File

@ -1,41 +0,0 @@
import os
import sys
def getUserFilesDirectory():
if sys.platform == "win32":
import win32api
# On Windows, sys.executable is codepage-encoded.
# It cannot represent all possible filenames, so get the exe filename
# using this wide-character API, which returns a `unicode`
exe = win32api.GetModuleFileNameW(None)
else:
# On OS X, the FS encoding is always UTF-8
# OS X filenames are defined to be UTF-8 encoded.
# On Linux, the FS encoding is given by the current locale
# Linux filenames are defined to be bytestrings.
exe = sys.executable.decode(sys.getfilesystemencoding())
assert os.path.exists(exe), "%r does not exist" % exe
if hasattr(sys, 'frozen'):
folder = os.path.dirname(exe)
else:
if exe.endswith("python") or exe.endswith("python.exe"):
script = sys.argv[0]
# assert the source checkout is not in a non-representable path...
assert os.path.exists(script), "Source checkout path cannot be represented with 'mbcs' encoding. Put the source checkout somewhere else."
folder = os.path.dirname(os.path.dirname(os.path.dirname(script))) # from src/mcedit, ../../
else:
folder = os.path.dirname(exe)
dataDir = os.path.join(folder, "MCEdit User Data")
if not os.path.exists(dataDir):
os.makedirs(dataDir)
return dataDir
def getUserSchematicsDirectory():
return os.path.join(getUserFilesDirectory(), "schematics")
def getUserPluginsDirectory():
return os.path.join(getUserFilesDirectory(), "plugins")

View File

@ -0,0 +1,24 @@
"""
directories
Get platform specific user data folders.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import logging
import os
import sys
log = logging.getLogger(__name__)
if sys.platform == "win32":
from .win32 import getUserFilesDirectory
elif sys.platform == "darwin":
from .mac import getUserFilesDirectory
else:
from .posix import getUserFilesDirectory
def getUserSchematicsDirectory():
return os.path.join(getUserFilesDirectory(), "schematics")
def getUserPluginsDirectory():
return os.path.join(getUserFilesDirectory(), "plugins")

View File

@ -0,0 +1,28 @@
"""
mac
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import logging
import os
import sys
log = logging.getLogger(__name__)
# no build scripts for OS X yet. assuming py2app or py2installer will be used.
#
# store user files in source checkout folder
def getUserFilesDirectory():
# TODO if sys.getattr('frozen', False):
# TODO os.getenv('RESOURCEPATH') or sys.getattr('_MEIPASS'), etc etc...
# On OS X, the FS encoding is always UTF-8
# OS X filenames are defined to be UTF-8 encoded.
# We internally handle filenames as unicode.
script = sys.argv[0].decode(sys.getfilesystemencoding())
folder = os.path.dirname(os.path.dirname(os.path.dirname(script))) # main script is src/mcedit/main.py, so, ../../
dataDir = os.path.join(folder, "MCEdit User Data")
return dataDir

View File

@ -0,0 +1,35 @@
"""
posix
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import logging
import os
import sys
log = logging.getLogger(__name__)
# no build scripts for linux yet. no idea if frozen apps will be built for linux.
# assume always running from source checkout and put user files in source checkout.
# xxx for .whl or distro-specific distrib, put user files in ~/.mcedit2
def getUserFilesDirectory():
# On Linux, the FS encoding is given by the current locale
# Linux filenames are defined to be bytestrings.
# TODO: if not os.path.exists(os.path.join(mumble_mumble, ".git")):
# We handle filenames internally as 'unicode', so decode 'sys.argv[0]'
# If a linux filename cannot be decoded with the current locale, ignore it.
# If this filename is the script filename, you lose.
try:
# assert the source checkout is not in a non-representable path...
script = sys.argv[0].decode(sys.getfilesystemencoding())
except UnicodeDecodeError:
print("Script filename %r cannot be decoded with the current locale %s! Please use sensible filenames." % (
sys.argv[0], sys.getfilesystemencoding()))
raise
folder = os.path.dirname(os.path.dirname(os.path.dirname(script))) # main script is src/mcedit/main.py, so, ../../
dataDir = os.path.join(folder, "MCEdit User Data")
return dataDir

View File

@ -0,0 +1,57 @@
import os
import sys
# when running from source, put the user files in the source checkout
# when running from a built app ("frozen"), decide from install mode
# TODO how to do install mode?
# 1. two different downloads
# - .exe installer for system-wide/per-user install
# - .zip for self-contained/portable install
# 2. one download. prompt on first app startup for install mode. create INSTALL_MODE file in
# exe folder. check contents of file for install mode and prompt if file is missing.
# 3. like MCEdit1. option in app to change install mode and move user folder into app or docs folder
# check for presence of user folder in app folder on startup to decide install mode.
# 4. ALWAYS self-contained/portable install. same as MultiMC.
#
# ... also, get auto updater working. manually updating a self-contained install may be weird.
# I think 4 is the best.
from mcedit2.util import resources
PORTABLE_INSTALL = True
_userFilesDirectory = None
def getUserFilesDirectory():
global _userFilesDirectory
if _userFilesDirectory is not None:
return _userFilesDirectory
if hasattr(sys, 'frozen'):
# On Windows, filenames are UTF-16 encoded.
# Filenames are defined as UTF-16.
# However, sys.executable is codepage-encoded. Codepages cannot represent all possible
# filenames, so we must get the exe filename using this wide-character API.
# Wide-character APIs in pywin32 always return a `unicode`.
#
# Filenames must be passed to Python's filesystem functions as `unicode` to call the
# wide-character forms of the underlying Win32 APIs.
#
# We take care not to store filenames as `bytes`, as this causes the filesystem functions
# to use the legacy codepage APIs.
import win32api
exe = win32api.GetModuleFileNameW(None)
assert os.path.exists(exe), "MCEdit executable %r does not exist! Something is very wrong." % exe
folder = os.path.dirname(exe)
else:
folder = os.path.dirname(resources.getSrcFolder())
dataDir = os.path.join(folder, "MCEdit User Data")
if not os.path.exists(dataDir):
os.makedirs(dataDir)
_userFilesDirectory = dataDir
return dataDir

View File

@ -8,15 +8,50 @@ import sys
log = logging.getLogger(__name__)
def getSrcFolder():
"""
Find the 'src/' folder of a source checkout.
...should maybe assert that '.git' exists?
:return: unicode
"""
import mcedit2
mod = os.path.realpath(mcedit2.__file__) # src/mcedit2/__init__.py
# On Windows, sys.argv[0] is always codepage-encoded, as is the __file__ attribute of any module.
# in fact, the entire module import subsystem of python2.7 is either restricted to codepages
# or to ASCII (haven't found out which) as `import` seems to break with non-ascii paths.
# On OS X, it is always UTF-8 encoded and filenames are *always* UTF-8 encoded.
# On Linux, it is locale-encoded and filenames are defined as bytestrings, so it is possible
# to have a filename that cannot be interpreted as unicode. If the user writes a filename
# that is not locale-encoded, he loses.
mod = mod.decode(sys.getfilesystemencoding())
# Assert the source checkout is not in a non-representable path...
assert os.path.exists(mod), ("Source checkout path cannot be represented as unicode. "
"Put the source checkout somewhere else.")
return os.path.dirname((os.path.dirname(mod)))
def resourcePath(filename):
"""
Return the absolute path of a filename included as a resource.
"Resource" is not well-defined. When packaged with PyInstaller, the filename is found
relative to the app's folder (_MEIPASS). When running from source, it is relative to the
'src' folder. I'd imagine that when installed as a .whl (on linux) we need to use
pkg_resources to get filenames.
:param filename:
:return:
"""
filename = filename.replace('/', os.path.sep)
basedir = getattr(sys, "_MEIPASS", None) # if pyinstaller'd
if basedir is None:
import mcedit2
mod = mcedit2.__file__
basedir = os.path.dirname(mod) + "/.."
basedir = os.path.normpath(basedir)
# should work across platforms
basedir = getSrcFolder()
path = os.path.join(basedir, filename)
if not os.path.exists(path):
raise RuntimeError("Could not get resource path for %s\n(Tried %s which does not exist)" % (filename, path))