Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/core/profiledir.py: 41%

111 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1# encoding: utf-8 

2"""An object for managing IPython profile directories.""" 

3 

4# Copyright (c) IPython Development Team. 

5# Distributed under the terms of the Modified BSD License. 

6 

7import os 

8import shutil 

9import errno 

10from pathlib import Path 

11 

12from traitlets.config.configurable import LoggingConfigurable 

13from ..paths import get_ipython_package_dir 

14from ..utils.path import expand_path, ensure_dir_exists 

15from traitlets import Unicode, Bool, observe 

16 

17#----------------------------------------------------------------------------- 

18# Module errors 

19#----------------------------------------------------------------------------- 

20 

21class ProfileDirError(Exception): 

22 pass 

23 

24 

25#----------------------------------------------------------------------------- 

26# Class for managing profile directories 

27#----------------------------------------------------------------------------- 

28 

29class ProfileDir(LoggingConfigurable): 

30 """An object to manage the profile directory and its resources. 

31 

32 The profile directory is used by all IPython applications, to manage 

33 configuration, logging and security. 

34 

35 This object knows how to find, create and manage these directories. This 

36 should be used by any code that wants to handle profiles. 

37 """ 

38 

39 security_dir_name = Unicode('security') 

40 log_dir_name = Unicode('log') 

41 startup_dir_name = Unicode('startup') 

42 pid_dir_name = Unicode('pid') 

43 static_dir_name = Unicode('static') 

44 security_dir = Unicode(u'') 

45 log_dir = Unicode(u'') 

46 startup_dir = Unicode(u'') 

47 pid_dir = Unicode(u'') 

48 static_dir = Unicode(u'') 

49 

50 location = Unicode(u'', 

51 help="""Set the profile location directly. This overrides the logic used by the 

52 `profile` option.""", 

53 ).tag(config=True) 

54 

55 _location_isset = Bool(False) # flag for detecting multiply set location 

56 @observe('location') 

57 def _location_changed(self, change): 

58 if self._location_isset: 

59 raise RuntimeError("Cannot set profile location more than once.") 

60 self._location_isset = True 

61 new = change['new'] 

62 ensure_dir_exists(new) 

63 

64 # ensure config files exist: 

65 self.security_dir = os.path.join(new, self.security_dir_name) 

66 self.log_dir = os.path.join(new, self.log_dir_name) 

67 self.startup_dir = os.path.join(new, self.startup_dir_name) 

68 self.pid_dir = os.path.join(new, self.pid_dir_name) 

69 self.static_dir = os.path.join(new, self.static_dir_name) 

70 self.check_dirs() 

71 

72 def _mkdir(self, path, mode=None): 

73 """ensure a directory exists at a given path 

74 

75 This is a version of os.mkdir, with the following differences: 

76 

77 - returns True if it created the directory, False otherwise 

78 - ignores EEXIST, protecting against race conditions where 

79 the dir may have been created in between the check and 

80 the creation 

81 - sets permissions if requested and the dir already exists 

82 """ 

83 if os.path.exists(path): 

84 if mode and os.stat(path).st_mode != mode: 

85 try: 

86 os.chmod(path, mode) 

87 except OSError: 

88 self.log.warning( 

89 "Could not set permissions on %s", 

90 path 

91 ) 

92 return False 

93 try: 

94 if mode: 

95 os.mkdir(path, mode) 

96 else: 

97 os.mkdir(path) 

98 except OSError as e: 

99 if e.errno == errno.EEXIST: 

100 return False 

101 else: 

102 raise 

103 

104 return True 

105 

106 @observe('log_dir') 

107 def check_log_dir(self, change=None): 

108 self._mkdir(self.log_dir) 

109 

110 @observe('startup_dir') 

111 def check_startup_dir(self, change=None): 

112 self._mkdir(self.startup_dir) 

113 

114 readme = os.path.join(self.startup_dir, 'README') 

115 src = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'README_STARTUP') 

116 

117 if not os.path.exists(src): 

118 self.log.warning("Could not copy README_STARTUP to startup dir. Source file %s does not exist.", src) 

119 

120 if os.path.exists(src) and not os.path.exists(readme): 

121 shutil.copy(src, readme) 

122 

123 @observe('security_dir') 

124 def check_security_dir(self, change=None): 

125 self._mkdir(self.security_dir, 0o40700) 

126 

127 @observe('pid_dir') 

128 def check_pid_dir(self, change=None): 

129 self._mkdir(self.pid_dir, 0o40700) 

130 

131 def check_dirs(self): 

132 self.check_security_dir() 

133 self.check_log_dir() 

134 self.check_pid_dir() 

135 self.check_startup_dir() 

136 

137 def copy_config_file(self, config_file: str, path: Path, overwrite=False) -> bool: 

138 """Copy a default config file into the active profile directory. 

139 

140 Default configuration files are kept in :mod:`IPython.core.profile`. 

141 This function moves these from that location to the working profile 

142 directory. 

143 """ 

144 dst = Path(os.path.join(self.location, config_file)) 

145 if dst.exists() and not overwrite: 

146 return False 

147 if path is None: 

148 path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default') 

149 assert isinstance(path, Path) 

150 src = path / config_file 

151 shutil.copy(src, dst) 

152 return True 

153 

154 @classmethod 

155 def create_profile_dir(cls, profile_dir, config=None): 

156 """Create a new profile directory given a full path. 

157 

158 Parameters 

159 ---------- 

160 profile_dir : str 

161 The full path to the profile directory. If it does exist, it will 

162 be used. If not, it will be created. 

163 """ 

164 return cls(location=profile_dir, config=config) 

165 

166 @classmethod 

167 def create_profile_dir_by_name(cls, path, name=u'default', config=None): 

168 """Create a profile dir by profile name and path. 

169 

170 Parameters 

171 ---------- 

172 path : unicode 

173 The path (directory) to put the profile directory in. 

174 name : unicode 

175 The name of the profile. The name of the profile directory will 

176 be "profile_<profile>". 

177 """ 

178 if not os.path.isdir(path): 

179 raise ProfileDirError('Directory not found: %s' % path) 

180 profile_dir = os.path.join(path, u'profile_' + name) 

181 return cls(location=profile_dir, config=config) 

182 

183 @classmethod 

184 def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None): 

185 """Find an existing profile dir by profile name, return its ProfileDir. 

186 

187 This searches through a sequence of paths for a profile dir. If it 

188 is not found, a :class:`ProfileDirError` exception will be raised. 

189 

190 The search path algorithm is: 

191 1. ``os.getcwd()`` # removed for security reason. 

192 2. ``ipython_dir`` 

193 

194 Parameters 

195 ---------- 

196 ipython_dir : unicode or str 

197 The IPython directory to use. 

198 name : unicode or str 

199 The name of the profile. The name of the profile directory 

200 will be "profile_<profile>". 

201 """ 

202 dirname = u'profile_' + name 

203 paths = [ipython_dir] 

204 for p in paths: 

205 profile_dir = os.path.join(p, dirname) 

206 if os.path.isdir(profile_dir): 

207 return cls(location=profile_dir, config=config) 

208 else: 

209 raise ProfileDirError('Profile directory not found in paths: %s' % dirname) 

210 

211 @classmethod 

212 def find_profile_dir(cls, profile_dir, config=None): 

213 """Find/create a profile dir and return its ProfileDir. 

214 

215 This will create the profile directory if it doesn't exist. 

216 

217 Parameters 

218 ---------- 

219 profile_dir : unicode or str 

220 The path of the profile directory. 

221 """ 

222 profile_dir = expand_path(profile_dir) 

223 if not os.path.isdir(profile_dir): 

224 raise ProfileDirError('Profile directory not found: %s' % profile_dir) 

225 return cls(location=profile_dir, config=config)