From 93fbf756039c7e6f1605740c111149b2fdb35d88 Mon Sep 17 00:00:00 2001 From: Alessio Sergi Date: Tue, 31 Jan 2017 15:55:31 +0100 Subject: Optimize config code - Remove duplicate code (one place to rule them all) - Add SunOS support - Add a safe function for creating a directory tree - Put cache file in the cache directories --- glances/config.py | 76 ++++++++++++++++++++++++++++++++++++++++------------- glances/globals.py | 19 ++++++++++---- glances/outdated.py | 33 +++++++++-------------- glances/password.py | 46 +++++++------------------------- 4 files changed, 94 insertions(+), 80 deletions(-) diff --git a/glances/config.py b/glances/config.py index 84595f16..c04d34c2 100644 --- a/glances/config.py +++ b/glances/config.py @@ -25,10 +25,64 @@ import multiprocessing from io import open from glances.compat import ConfigParser, NoOptionError -from glances.globals import BSD, LINUX, MACOS, WINDOWS, sys_prefix +from glances.globals import BSD, LINUX, MACOS, SUNOS, WINDOWS from glances.logger import logger +def user_config_dir(): + r"""Return the per-user config dir (full path). + + - Linux, *BSD, SunOS: ~/.config/glances + - macOS: ~/Library/Application Support/glances + - Windows: %APPDATA%\glances + """ + if WINDOWS: + path = os.environ.get('APPDATA') + elif MACOS: + path = os.path.expanduser('~/Library/Application Support') + else: + path = os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser('~/.config') + path = os.path.join(path, 'glances') + + return path + + +def user_cache_dir(): + r"""Return the per-user cache dir (full path). + + - Linux, *BSD, SunOS: ~/.cache/glances + - macOS: ~/Library/Caches/glances + - Windows: %LOCALAPPDATA%\glances\cache + """ + if WINDOWS: + path = os.path.join(os.environ.get('LOCALAPPDATA'), 'glances', 'cache') + elif MACOS: + path = os.path.expanduser('~/Library/Caches/glances') + else: + path = os.path.join(os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'), + 'glances') + + return path + + +def system_config_dir(): + r"""Return the system-wide config dir (full path). + + - Linux, SunOS: /etc/glances + - *BSD, macOS: /usr/local/etc/glances + - Windows: %APPDATA%\glances + """ + if LINUX or SUNOS: + path = '/etc' + elif BSD or MACOS: + path = '/usr/local/etc' + else: + path = os.environ.get('APPDATA') + path = os.path.join(path, 'glances') + + return path + + class Config(object): """This class is used to access/read config file, if it exists. @@ -51,7 +105,7 @@ class Config(object): The list is built taking into account of the OS, priority and location. * custom path: /path/to/glances - * Linux: ~/.config/glances, /etc/glances + * Linux, SunOS: ~/.config/glances, /etc/glances * *BSD: ~/.config/glances, /usr/local/etc/glances * macOS: ~/Library/Application Support/glances, /usr/local/etc/glances * Windows: %APPDATA%\glances @@ -66,22 +120,8 @@ class Config(object): if self.config_dir: paths.append(self.config_dir) - if LINUX or BSD: - paths.append( - os.path.join(os.environ.get('XDG_CONFIG_HOME') or - os.path.expanduser('~/.config'), - 'glances', self.config_filename)) - if BSD: - paths.append(os.path.join(sys.prefix, 'etc', 'glances', self.config_filename)) - else: - paths.append(os.path.join('/etc/glances', self.config_filename)) - elif MACOS: - paths.append( - os.path.join(os.path.expanduser('~/Library/Application Support/glances'), - self.config_filename)) - paths.append(os.path.join(sys_prefix, 'etc', 'glances', self.config_filename)) - elif WINDOWS: - paths.append(os.path.join(os.environ.get('APPDATA'), 'glances', self.config_filename)) + paths.append(os.path.join(user_config_dir(), self.config_filename)) + paths.append(os.path.join(system_config_dir(), self.config_filename)) return paths diff --git a/glances/globals.py b/glances/globals.py index c67cf3f3..f335d737 100644 --- a/glances/globals.py +++ b/glances/globals.py @@ -19,6 +19,7 @@ """Common objects shared by all Glances modules.""" +import errno import os import sys @@ -30,12 +31,8 @@ MACOS = sys.platform.startswith('darwin') SUNOS = sys.platform.startswith('sunos') WINDOWS = sys.platform.startswith('win') -# Path definitions -work_path = os.path.realpath(os.path.dirname(__file__)) -appname_path = os.path.split(sys.argv[0])[0] -sys_prefix = os.path.realpath(os.path.dirname(appname_path)) - # Set the AMPs, plugins and export modules path +work_path = os.path.realpath(os.path.dirname(__file__)) amps_path = os.path.realpath(os.path.join(work_path, 'amps')) plugins_path = os.path.realpath(os.path.join(work_path, 'plugins')) exports_path = os.path.realpath(os.path.join(work_path, 'exports')) @@ -43,3 +40,15 @@ sys_path = sys.path[:] sys.path.insert(1, exports_path) sys.path.insert(1, plugins_path) sys.path.insert(1, amps_path) + + +def safe_makedirs(path): + """A safe function for creating a directory tree.""" + try: + os.makedirs(path) + except OSError as err: + if err.errno == errno.EEXIST: + if not os.path.isdir(path): + raise + else: + raise diff --git a/glances/outdated.py b/glances/outdated.py index f402244e..5bfd683b 100644 --- a/glances/outdated.py +++ b/glances/outdated.py @@ -33,7 +33,8 @@ else: outdated_tag = True from glances import __version__ -from glances.globals import BSD, LINUX, MACOS, WINDOWS +from glances.config import user_cache_dir +from glances.globals import safe_makedirs from glances.logger import logger @@ -51,6 +52,8 @@ class Outdated(object): """Init the Outdated class""" self.args = args self.config = config + self.cache_dir = user_cache_dir() + self.cache_file = os.path.join(self.cache_dir, 'glances-version.db') # Set default value... self.data = { @@ -126,10 +129,10 @@ class Outdated(object): # If the cached file exist, read-it cached_data = {} try: - with open(os.path.join(self._cache_path(), 'glances-version.db'), 'rb') as f: + with open(self.cache_file, 'rb') as f: cached_data = pickle.load(f) except Exception as e: - logger.debug("Cannot read the version cache file ({})".format(e)) + logger.debug("Cannot read the version cache file: {}".format(e)) else: logger.debug("Read the version cache file") if cached_data['installed_version'] != self.installed_version() or \ @@ -141,25 +144,13 @@ class Outdated(object): return cached_data def _save_cache(self): - """Save data to a file""" - # If the cached file exist, read-it - try: - with open(os.path.join(self._cache_path(), 'glances-version.db'), 'wb') as f: - pickle.dump(self.data, f) - except IOError: - return False - return True + """Save data to the cache file.""" + # Create the cache directory + safe_makedirs(self.cache_dir) - def _cache_path(self): - """Return the cached file path""" - if LINUX or BSD: - return os.path.join(os.environ.get('XDG_CONFIG_HOME') or - os.path.expanduser('~/.config'), - 'glances') - elif MACOS: - return os.path.expanduser('~/Library/Application Support/glances') - elif WINDOWS: - return os.path.join(os.environ.get('APPDATA'), 'glances') + # Create/overwrite the cache file + with open(self.cache_file, 'wb') as f: + pickle.dump(self.data, f) def _update_pypi_version(self): """Get the latest Pypi version (as a string) via the Restful JSON API""" diff --git a/glances/password.py b/glances/password.py index 82c382a9..f0f9b6b1 100644 --- a/glances/password.py +++ b/glances/password.py @@ -27,7 +27,8 @@ import uuid from io import open from glances.compat import b, input -from glances.globals import BSD, LINUX, MACOS, WINDOWS +from glances.config import user_config_dir +from glances.globals import safe_makedirs from glances.logger import logger @@ -37,30 +38,9 @@ class GlancesPassword(object): def __init__(self, username='glances'): self.username = username - self.password_path = self.get_password_path() + self.password_dir = user_config_dir() self.password_filename = self.username + '.pwd' - self.password_filepath = os.path.join(self.password_path, self.password_filename) - - def get_password_path(self): - r"""Get the path where the password file will be stored. - - * Linux and *BSD: ~/.config/glances - * macOS: ~/Library/glances - * Windows: %APPDATA%\glances - """ - if LINUX or BSD: - app_path = os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser('~/.config') - elif MACOS: - app_path = os.path.join(os.environ.get('HOME'), 'Library') - elif WINDOWS: - app_path = os.environ.get('APPDATA') - else: - app_path = '.' - - # Append the Glances folder - app_path = os.path.join(app_path, 'glances') - - return app_path + self.password_file = os.path.join(self.password_dir, self.password_filename) def sha256_hash(self, plain_password): """Return the SHA-256 of the given password.""" @@ -98,9 +78,9 @@ class GlancesPassword(object): 2) the password is hashed with SHA-256 (only SHA string transit through the network) """ - if os.path.exists(self.password_filepath) and not clear: + if os.path.exists(self.password_file) and not clear: # If the password file exist then use it - logger.info("Read password from file {}".format(self.password_filepath)) + logger.info("Read password from file {}".format(self.password_file)) password = self.load_password() else: # password_sha256 is the plain SHA-256 password @@ -131,23 +111,17 @@ class GlancesPassword(object): def save_password(self, hashed_password): """Save the hashed password to the Glances folder.""" - # Check if the Glances folder already exists - if not os.path.exists(self.password_path): - # Create the Glances folder - try: - os.makedirs(self.password_path) - except OSError as e: - logger.error("Cannot create Glances directory: {}".format(e)) - return + # Create the glances directory + safe_makedirs(self.password_dir) # Create/overwrite the password file - with open(self.password_filepath, 'wb') as file_pwd: + with open(self.password_file, 'wb') as file_pwd: file_pwd.write(b(hashed_password)) def load_password(self): """Load the hashed password from the Glances folder.""" # Read the password file, if it exists - with open(self.password_filepath, 'r') as file_pwd: + with open(self.password_file, 'r') as file_pwd: hashed_password = file_pwd.read() return hashed_password -- cgit v1.2.3