123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- # encoding: utf-8
- """An object for managing IPython profile directories."""
- # Copyright (c) IPython Development Team.
- # Distributed under the terms of the Modified BSD License.
- import os
- import shutil
- import errno
- from traitlets.config.configurable import LoggingConfigurable
- from IPython.paths import get_ipython_package_dir
- from IPython.utils.path import expand_path, ensure_dir_exists
- from IPython.utils import py3compat
- from traitlets import Unicode, Bool, observe
- #-----------------------------------------------------------------------------
- # Module errors
- #-----------------------------------------------------------------------------
- class ProfileDirError(Exception):
- pass
- #-----------------------------------------------------------------------------
- # Class for managing profile directories
- #-----------------------------------------------------------------------------
- class ProfileDir(LoggingConfigurable):
- """An object to manage the profile directory and its resources.
- The profile directory is used by all IPython applications, to manage
- configuration, logging and security.
- This object knows how to find, create and manage these directories. This
- should be used by any code that wants to handle profiles.
- """
- security_dir_name = Unicode('security')
- log_dir_name = Unicode('log')
- startup_dir_name = Unicode('startup')
- pid_dir_name = Unicode('pid')
- static_dir_name = Unicode('static')
- security_dir = Unicode(u'')
- log_dir = Unicode(u'')
- startup_dir = Unicode(u'')
- pid_dir = Unicode(u'')
- static_dir = Unicode(u'')
- location = Unicode(u'',
- help="""Set the profile location directly. This overrides the logic used by the
- `profile` option.""",
- ).tag(config=True)
- _location_isset = Bool(False) # flag for detecting multiply set location
- @observe('location')
- def _location_changed(self, change):
- if self._location_isset:
- raise RuntimeError("Cannot set profile location more than once.")
- self._location_isset = True
- new = change['new']
- ensure_dir_exists(new)
- # ensure config files exist:
- self.security_dir = os.path.join(new, self.security_dir_name)
- self.log_dir = os.path.join(new, self.log_dir_name)
- self.startup_dir = os.path.join(new, self.startup_dir_name)
- self.pid_dir = os.path.join(new, self.pid_dir_name)
- self.static_dir = os.path.join(new, self.static_dir_name)
- self.check_dirs()
-
- def _mkdir(self, path, mode=None):
- """ensure a directory exists at a given path
- This is a version of os.mkdir, with the following differences:
- - returns True if it created the directory, False otherwise
- - ignores EEXIST, protecting against race conditions where
- the dir may have been created in between the check and
- the creation
- - sets permissions if requested and the dir already exists
- """
- if os.path.exists(path):
- if mode and os.stat(path).st_mode != mode:
- try:
- os.chmod(path, mode)
- except OSError:
- self.log.warning(
- "Could not set permissions on %s",
- path
- )
- return False
- try:
- if mode:
- os.mkdir(path, mode)
- else:
- os.mkdir(path)
- except OSError as e:
- if e.errno == errno.EEXIST:
- return False
- else:
- raise
- return True
-
- @observe('log_dir')
- def check_log_dir(self, change=None):
- self._mkdir(self.log_dir)
-
- @observe('startup_dir')
- def check_startup_dir(self, change=None):
- self._mkdir(self.startup_dir)
- readme = os.path.join(self.startup_dir, 'README')
- if not os.path.exists(readme):
- import pkgutil
- with open(readme, 'wb') as f:
- f.write(pkgutil.get_data(__name__, 'profile/README_STARTUP'))
- @observe('security_dir')
- def check_security_dir(self, change=None):
- self._mkdir(self.security_dir, 0o40700)
- @observe('pid_dir')
- def check_pid_dir(self, change=None):
- self._mkdir(self.pid_dir, 0o40700)
- def check_dirs(self):
- self.check_security_dir()
- self.check_log_dir()
- self.check_pid_dir()
- self.check_startup_dir()
- def copy_config_file(self, config_file, path=None, overwrite=False):
- """Copy a default config file into the active profile directory.
- Default configuration files are kept in :mod:`IPython.core.profile`.
- This function moves these from that location to the working profile
- directory.
- """
- dst = os.path.join(self.location, config_file)
- if os.path.isfile(dst) and not overwrite:
- return False
- if path is None:
- path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default')
- src = os.path.join(path, config_file)
- shutil.copy(src, dst)
- return True
- @classmethod
- def create_profile_dir(cls, profile_dir, config=None):
- """Create a new profile directory given a full path.
- Parameters
- ----------
- profile_dir : str
- The full path to the profile directory. If it does exist, it will
- be used. If not, it will be created.
- """
- return cls(location=profile_dir, config=config)
- @classmethod
- def create_profile_dir_by_name(cls, path, name=u'default', config=None):
- """Create a profile dir by profile name and path.
- Parameters
- ----------
- path : unicode
- The path (directory) to put the profile directory in.
- name : unicode
- The name of the profile. The name of the profile directory will
- be "profile_<profile>".
- """
- if not os.path.isdir(path):
- raise ProfileDirError('Directory not found: %s' % path)
- profile_dir = os.path.join(path, u'profile_' + name)
- return cls(location=profile_dir, config=config)
- @classmethod
- def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
- """Find an existing profile dir by profile name, return its ProfileDir.
- This searches through a sequence of paths for a profile dir. If it
- is not found, a :class:`ProfileDirError` exception will be raised.
- The search path algorithm is:
- 1. ``py3compat.getcwd()``
- 2. ``ipython_dir``
- Parameters
- ----------
- ipython_dir : unicode or str
- The IPython directory to use.
- name : unicode or str
- The name of the profile. The name of the profile directory
- will be "profile_<profile>".
- """
- dirname = u'profile_' + name
- paths = [py3compat.getcwd(), ipython_dir]
- for p in paths:
- profile_dir = os.path.join(p, dirname)
- if os.path.isdir(profile_dir):
- return cls(location=profile_dir, config=config)
- else:
- raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
- @classmethod
- def find_profile_dir(cls, profile_dir, config=None):
- """Find/create a profile dir and return its ProfileDir.
- This will create the profile directory if it doesn't exist.
- Parameters
- ----------
- profile_dir : unicode or str
- The path of the profile directory.
- """
- profile_dir = expand_path(profile_dir)
- if not os.path.isdir(profile_dir):
- raise ProfileDirError('Profile directory not found: %s' % profile_dir)
- return cls(location=profile_dir, config=config)
|