profiledir.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. # encoding: utf-8
  2. """An object for managing IPython profile directories."""
  3. # Copyright (c) IPython Development Team.
  4. # Distributed under the terms of the Modified BSD License.
  5. import os
  6. import shutil
  7. import errno
  8. from traitlets.config.configurable import LoggingConfigurable
  9. from IPython.paths import get_ipython_package_dir
  10. from IPython.utils.path import expand_path, ensure_dir_exists
  11. from IPython.utils import py3compat
  12. from traitlets import Unicode, Bool, observe
  13. #-----------------------------------------------------------------------------
  14. # Module errors
  15. #-----------------------------------------------------------------------------
  16. class ProfileDirError(Exception):
  17. pass
  18. #-----------------------------------------------------------------------------
  19. # Class for managing profile directories
  20. #-----------------------------------------------------------------------------
  21. class ProfileDir(LoggingConfigurable):
  22. """An object to manage the profile directory and its resources.
  23. The profile directory is used by all IPython applications, to manage
  24. configuration, logging and security.
  25. This object knows how to find, create and manage these directories. This
  26. should be used by any code that wants to handle profiles.
  27. """
  28. security_dir_name = Unicode('security')
  29. log_dir_name = Unicode('log')
  30. startup_dir_name = Unicode('startup')
  31. pid_dir_name = Unicode('pid')
  32. static_dir_name = Unicode('static')
  33. security_dir = Unicode(u'')
  34. log_dir = Unicode(u'')
  35. startup_dir = Unicode(u'')
  36. pid_dir = Unicode(u'')
  37. static_dir = Unicode(u'')
  38. location = Unicode(u'',
  39. help="""Set the profile location directly. This overrides the logic used by the
  40. `profile` option.""",
  41. ).tag(config=True)
  42. _location_isset = Bool(False) # flag for detecting multiply set location
  43. @observe('location')
  44. def _location_changed(self, change):
  45. if self._location_isset:
  46. raise RuntimeError("Cannot set profile location more than once.")
  47. self._location_isset = True
  48. new = change['new']
  49. ensure_dir_exists(new)
  50. # ensure config files exist:
  51. self.security_dir = os.path.join(new, self.security_dir_name)
  52. self.log_dir = os.path.join(new, self.log_dir_name)
  53. self.startup_dir = os.path.join(new, self.startup_dir_name)
  54. self.pid_dir = os.path.join(new, self.pid_dir_name)
  55. self.static_dir = os.path.join(new, self.static_dir_name)
  56. self.check_dirs()
  57. def _mkdir(self, path, mode=None):
  58. """ensure a directory exists at a given path
  59. This is a version of os.mkdir, with the following differences:
  60. - returns True if it created the directory, False otherwise
  61. - ignores EEXIST, protecting against race conditions where
  62. the dir may have been created in between the check and
  63. the creation
  64. - sets permissions if requested and the dir already exists
  65. """
  66. if os.path.exists(path):
  67. if mode and os.stat(path).st_mode != mode:
  68. try:
  69. os.chmod(path, mode)
  70. except OSError:
  71. self.log.warning(
  72. "Could not set permissions on %s",
  73. path
  74. )
  75. return False
  76. try:
  77. if mode:
  78. os.mkdir(path, mode)
  79. else:
  80. os.mkdir(path)
  81. except OSError as e:
  82. if e.errno == errno.EEXIST:
  83. return False
  84. else:
  85. raise
  86. return True
  87. @observe('log_dir')
  88. def check_log_dir(self, change=None):
  89. self._mkdir(self.log_dir)
  90. @observe('startup_dir')
  91. def check_startup_dir(self, change=None):
  92. self._mkdir(self.startup_dir)
  93. readme = os.path.join(self.startup_dir, 'README')
  94. if not os.path.exists(readme):
  95. import pkgutil
  96. with open(readme, 'wb') as f:
  97. f.write(pkgutil.get_data(__name__, 'profile/README_STARTUP'))
  98. @observe('security_dir')
  99. def check_security_dir(self, change=None):
  100. self._mkdir(self.security_dir, 0o40700)
  101. @observe('pid_dir')
  102. def check_pid_dir(self, change=None):
  103. self._mkdir(self.pid_dir, 0o40700)
  104. def check_dirs(self):
  105. self.check_security_dir()
  106. self.check_log_dir()
  107. self.check_pid_dir()
  108. self.check_startup_dir()
  109. def copy_config_file(self, config_file, path=None, overwrite=False):
  110. """Copy a default config file into the active profile directory.
  111. Default configuration files are kept in :mod:`IPython.core.profile`.
  112. This function moves these from that location to the working profile
  113. directory.
  114. """
  115. dst = os.path.join(self.location, config_file)
  116. if os.path.isfile(dst) and not overwrite:
  117. return False
  118. if path is None:
  119. path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default')
  120. src = os.path.join(path, config_file)
  121. shutil.copy(src, dst)
  122. return True
  123. @classmethod
  124. def create_profile_dir(cls, profile_dir, config=None):
  125. """Create a new profile directory given a full path.
  126. Parameters
  127. ----------
  128. profile_dir : str
  129. The full path to the profile directory. If it does exist, it will
  130. be used. If not, it will be created.
  131. """
  132. return cls(location=profile_dir, config=config)
  133. @classmethod
  134. def create_profile_dir_by_name(cls, path, name=u'default', config=None):
  135. """Create a profile dir by profile name and path.
  136. Parameters
  137. ----------
  138. path : unicode
  139. The path (directory) to put the profile directory in.
  140. name : unicode
  141. The name of the profile. The name of the profile directory will
  142. be "profile_<profile>".
  143. """
  144. if not os.path.isdir(path):
  145. raise ProfileDirError('Directory not found: %s' % path)
  146. profile_dir = os.path.join(path, u'profile_' + name)
  147. return cls(location=profile_dir, config=config)
  148. @classmethod
  149. def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
  150. """Find an existing profile dir by profile name, return its ProfileDir.
  151. This searches through a sequence of paths for a profile dir. If it
  152. is not found, a :class:`ProfileDirError` exception will be raised.
  153. The search path algorithm is:
  154. 1. ``py3compat.getcwd()``
  155. 2. ``ipython_dir``
  156. Parameters
  157. ----------
  158. ipython_dir : unicode or str
  159. The IPython directory to use.
  160. name : unicode or str
  161. The name of the profile. The name of the profile directory
  162. will be "profile_<profile>".
  163. """
  164. dirname = u'profile_' + name
  165. paths = [py3compat.getcwd(), ipython_dir]
  166. for p in paths:
  167. profile_dir = os.path.join(p, dirname)
  168. if os.path.isdir(profile_dir):
  169. return cls(location=profile_dir, config=config)
  170. else:
  171. raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
  172. @classmethod
  173. def find_profile_dir(cls, profile_dir, config=None):
  174. """Find/create a profile dir and return its ProfileDir.
  175. This will create the profile directory if it doesn't exist.
  176. Parameters
  177. ----------
  178. profile_dir : unicode or str
  179. The path of the profile directory.
  180. """
  181. profile_dir = expand_path(profile_dir)
  182. if not os.path.isdir(profile_dir):
  183. raise ProfileDirError('Profile directory not found: %s' % profile_dir)
  184. return cls(location=profile_dir, config=config)