From c405e9b648bcf8a16bb694a78f5496df3fafd8af Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Sat, 24 Nov 2012 14:22:02 +0100 Subject: [PATCH 1/3] Rework default settings handling (fixes #409) --- openslides/main.py | 155 ++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 57 deletions(-) diff --git a/openslides/main.py b/openslides/main.py index 83e41395e..80fe370a2 100755 --- a/openslides/main.py +++ b/openslides/main.py @@ -13,13 +13,15 @@ # for python 2.5 support from __future__ import with_statement -import os -import sys -import optparse -import socket -import time -import threading import base64 +import ctypes +import optparse +import os +import socket +import sys +import tempfile +import threading +import time import webbrowser import django.conf @@ -28,14 +30,15 @@ from django.core.management import execute_from_command_line CONFIG_TEMPLATE = """#!/usr/bin/env python # -*- coding: utf-8 -*- +import openslides.main from openslides.global_settings import * # Use 'DEBUG = True' to get more details for server errors -# (Default for relaeses: 'False') +# (Default for releases: 'False') DEBUG = False TEMPLATE_DEBUG = DEBUG -DBPATH = %(dbpath)r +DBPATH = %(dbpath)s DATABASES = { 'default': { @@ -64,6 +67,10 @@ INSTALLED_APPS += INSTALLED_PLUGINS KEY_LENGTH = 30 +# sentinel used to signal that the database ought to be stored +# relative to the portable's directory +_portable_db_path = object() + _fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() def _fs2unicode(s): @@ -71,8 +78,7 @@ def _fs2unicode(s): return s return s.decode(_fs_encoding) - -def main(argv=None, opt_defaults=None, database_path=None): +def process_options(argv = None): if argv is None: argv = sys.argv[1:] @@ -90,21 +96,48 @@ def main(argv=None, opt_defaults=None, database_path=None): parser.add_option( "--no-reload", action="store_true", help="Do not reload the development server") - if not opt_defaults is None: - parser.set_defaults(**opt_defaults) - opts, args = parser.parse_args(argv) if args: sys.stderr.write("This command does not take arguments!\n\n") parser.print_help() sys.exit(1) + return opts + +def main(argv=None): + opts = process_options(argv) + _main(opts) + +def win32_portable_main(argv=None): + """special entry point for the win32 portable version""" + + opts = process_options(argv) + + database_path = None + + if opts.settings is None: + portable_dir = get_portable_path() + try: + fd, test_file = tempfile.mkstemp(dir=portable_dir) + except OSError: + portable_dir_writeable = False + else: + portable_dir_writeable = True + os.close(fd) + os.unlink(test_file) + + if portable_dir_writeable: + opts.settings = os.path.join(portable_dir, + "openslides", "settings.py") + database_path = _portable_db_path + + _main(opts, database_path=database_path) + +def _main(opts, database_path=None): # Find the path to the settings settings_path = opts.settings if settings_path is None: - config_home = os.environ.get('XDG_CONFIG_HOME', \ - os.path.join(os.path.expanduser('~'), '.config')) - settings_path = os.path.join(config_home, 'openslides', 'settings.py') + settings_path = get_user_config_path('openslides', 'settings.py') # Create settings if necessary if not os.path.exists(settings_path): @@ -141,14 +174,17 @@ def main(argv=None, opt_defaults=None, database_path=None): def create_settings(settings_path, database_path=None): settings_module = os.path.dirname(settings_path) - if database_path is None: - data_home = os.environ.get('XDG_DATA_HOME', \ - os.path.join(os.path.expanduser('~'), '.local', 'share')) - database_path = os.path.join(data_home, 'openslides', 'database.sqlite') + if database_path is _portable_db_path: + database_path = get_portable_db_path() + dbpath_value = 'openslides.main.get_portable_db_path()' + else: + if database_path is None: + database_path = get_user_data_path('openslides', 'database.sqlite') + dbpath_value = repr(_fs2unicode(database_path)) settings_content = CONFIG_TEMPLATE % dict( default_key=base64.b64encode(os.urandom(KEY_LENGTH)), - dbpath=_fs2unicode(database_path)) + dbpath=dbpath_value) if not os.path.exists(settings_module): os.makedirs(settings_module) @@ -269,51 +305,56 @@ def start_browser(url): t = threading.Thread(target=f) t.start() -def win32_portable_main(argv=None): - """special entry point for the win32 portable version""" - import tempfile +def get_user_config_path(*args): + if sys.platform == "win32": + return win32_get_app_data_path(*args) + config_home = os.environ.get('XDG_CONFIG_HOME', \ + os.path.join(os.path.expanduser('~'), '.config')) + + return os.path.join(_fs2unicode(config_home), *args) + +def get_user_data_path(*args): + if sys.platform == "win32": + return win32_get_app_data_path(*args) + + data_home = os.environ.get('XDG_DATA_HOME', \ + os.path.join(os.path.expanduser('~'), '.local', 'share')) + + return os.path.join(_fs2unicode(data_home), *args) + +def get_portable_path(*args): # NOTE: sys.executable will be the path to openslides.exe # since it is essentially a small wrapper that embeds the # python interpreter - portable_dir = os.path.dirname(os.path.abspath(sys.executable)) - try: - fd, test_file = tempfile.mkstemp(dir=portable_dir) - except OSError: - portable_dir_writeable = False - else: - portable_dir_writeable = True - os.close(fd) - os.unlink(test_file) - if portable_dir_writeable: - default_settings = os.path.join(portable_dir, "openslides", - "openslides_personal_settings.py") - database_path = os.path.join(portable_dir, "openslides", - "database.sqlite") - else: - import ctypes + exename = os.path.basename(sys.executable).lower() + if exename != "openslides.exe": + raise Exception("Cannot determine portable path when " + "not running as portable") - shell32 = ctypes.WinDLL("shell32.dll") - SHGetFolderPath = shell32.SHGetFolderPathW - SHGetFolderPath.argtypes = (ctypes.c_void_p, ctypes.c_int, - ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p) - SHGetFolderPath.restype = ctypes.c_uint32 + portable_dir = _fs2unicode(os.path.dirname(os.path.abspath(sys.executable))) + return os.path.join(portable_dir, *args) - CSIDL_LOCAL_APPDATA = 0x001c - MAX_PATH = 260 +def get_portable_db_path(): + return get_portable_path('openslides', 'database.sqlite') - buf = ctypes.create_unicode_buffer(MAX_PATH) - res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf) - if res != 0: - raise Exception("Could not deterime APPDATA path") - default_settings = os.path.join(buf.value, "openslides", - "openslides_personal_settings.py") - database_path = os.path.join(buf.value, "openslides", - "database.sqlite") +def win32_get_app_data_path(*args): + shell32 = ctypes.WinDLL("shell32.dll") + SHGetFolderPath = shell32.SHGetFolderPathW + SHGetFolderPath.argtypes = (ctypes.c_void_p, ctypes.c_int, + ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p) + SHGetFolderPath.restype = ctypes.c_uint32 - main(argv, opt_defaults={ "settings": default_settings }, - database_path=database_path) + CSIDL_LOCAL_APPDATA = 0x001c + MAX_PATH = 260 + + buf = ctypes.create_unicode_buffer(MAX_PATH) + res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf) + if res != 0: + raise Exception("Could not deterime APPDATA path") + + return os.path.join(buf.value, *args) if __name__ == "__main__": From 220abe45c340563f85473aed6055554b844837e7 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Sat, 24 Nov 2012 14:53:08 +0100 Subject: [PATCH 2/3] Copy _ctypes.pyd We need ctypes to determine the local app data directory (used for settings.py when portable dir is not writable) --- extras/win32-portable/prepare_portable.py | 1 + 1 file changed, 1 insertion(+) diff --git a/extras/win32-portable/prepare_portable.py b/extras/win32-portable/prepare_portable.py index 9418b8e10..6554a4939 100644 --- a/extras/win32-portable/prepare_portable.py +++ b/extras/win32-portable/prepare_portable.py @@ -87,6 +87,7 @@ PY_DLLS = [ "_sqlite3.pyd", "_socket.pyd", "select.pyd", + "_ctypes.pyd", ] MSVCR_PUBLIC_KEY = "1fc8b3b9a1e18e3b" From 21a51523a4f4c0b4d901f48aeba2ebe300889fd7 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Sat, 24 Nov 2012 20:35:06 +0100 Subject: [PATCH 3/3] Make pep8.py happy --- openslides/main.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/openslides/main.py b/openslides/main.py index 80fe370a2..fa3ce3c98 100755 --- a/openslides/main.py +++ b/openslides/main.py @@ -73,12 +73,15 @@ _portable_db_path = object() _fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() + + def _fs2unicode(s): if isinstance(s, unicode): return s return s.decode(_fs_encoding) -def process_options(argv = None): + +def process_options(argv=None): if argv is None: argv = sys.argv[1:] @@ -104,10 +107,12 @@ def process_options(argv = None): return opts + def main(argv=None): opts = process_options(argv) _main(opts) + def win32_portable_main(argv=None): """special entry point for the win32 portable version""" @@ -127,12 +132,13 @@ def win32_portable_main(argv=None): os.unlink(test_file) if portable_dir_writeable: - opts.settings = os.path.join(portable_dir, - "openslides", "settings.py") + opts.settings = os.path.join( + portable_dir, "openslides", "settings.py") database_path = _portable_db_path _main(opts, database_path=database_path) + def _main(opts, database_path=None): # Find the path to the settings settings_path = opts.settings @@ -305,24 +311,28 @@ def start_browser(url): t = threading.Thread(target=f) t.start() + def get_user_config_path(*args): if sys.platform == "win32": return win32_get_app_data_path(*args) - config_home = os.environ.get('XDG_CONFIG_HOME', \ - os.path.join(os.path.expanduser('~'), '.config')) + config_home = os.environ.get( + 'XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) return os.path.join(_fs2unicode(config_home), *args) + def get_user_data_path(*args): if sys.platform == "win32": return win32_get_app_data_path(*args) - data_home = os.environ.get('XDG_DATA_HOME', \ - os.path.join(os.path.expanduser('~'), '.local', 'share')) + data_home = os.environ.get( + 'XDG_DATA_HOME', os.path.join( + os.path.expanduser('~'), '.local', 'share')) return os.path.join(_fs2unicode(data_home), *args) + def get_portable_path(*args): # NOTE: sys.executable will be the path to openslides.exe # since it is essentially a small wrapper that embeds the @@ -330,20 +340,24 @@ def get_portable_path(*args): exename = os.path.basename(sys.executable).lower() if exename != "openslides.exe": - raise Exception("Cannot determine portable path when " + raise Exception( + "Cannot determine portable path when " "not running as portable") portable_dir = _fs2unicode(os.path.dirname(os.path.abspath(sys.executable))) return os.path.join(portable_dir, *args) + def get_portable_db_path(): return get_portable_path('openslides', 'database.sqlite') + def win32_get_app_data_path(*args): shell32 = ctypes.WinDLL("shell32.dll") SHGetFolderPath = shell32.SHGetFolderPathW - SHGetFolderPath.argtypes = (ctypes.c_void_p, ctypes.c_int, - ctypes.c_void_p, ctypes.c_uint32, ctypes.c_wchar_p) + SHGetFolderPath.argtypes = ( + ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint32, + ctypes.c_wchar_p) SHGetFolderPath.restype = ctypes.c_uint32 CSIDL_LOCAL_APPDATA = 0x001c