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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

109 statements  

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

2 

3# Copyright (c) IPython Development Team. 

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

5 

6import os 

7import errno 

8from pathlib import Path 

9 

10from traitlets.config.configurable import LoggingConfigurable 

11from ..paths import get_ipython_package_dir 

12from ..utils.path import expand_path, ensure_dir_exists 

13from traitlets import Unicode, Bool, observe 

14 

15#----------------------------------------------------------------------------- 

16# Module errors 

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

18 

19class ProfileDirError(Exception): 

20 pass 

21 

22 

23#----------------------------------------------------------------------------- 

24# Class for managing profile directories 

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

26 

27class ProfileDir(LoggingConfigurable): 

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

29 

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

31 configuration, logging and security. 

32 

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

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

35 """ 

36 

37 security_dir_name = Unicode('security') 

38 log_dir_name = Unicode('log') 

39 startup_dir_name = Unicode('startup') 

40 pid_dir_name = Unicode('pid') 

41 static_dir_name = Unicode('static') 

42 security_dir = Unicode('') 

43 log_dir = Unicode('') 

44 startup_dir = Unicode('') 

45 pid_dir = Unicode('') 

46 static_dir = Unicode('') 

47 

48 location = Unicode('', 

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

50 `profile` option.""", 

51 ).tag(config=True) 

52 

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

54 @observe('location') 

55 def _location_changed(self, change): 

56 if self._location_isset: 

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

58 self._location_isset = True 

59 new = change['new'] 

60 ensure_dir_exists(new) 

61 

62 # ensure config files exist: 

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

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

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

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

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

68 self.check_dirs() 

69 

70 def _mkdir(self, path: str, mode: int | None = None) -> bool: 

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

72 

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

74 

75 - returns whether the directory has been created or not. 

76 - ignores EEXIST, protecting against race conditions where 

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

78 the creation 

79 - sets permissions if requested and the dir already exists 

80 

81 Parameters 

82 ---------- 

83 path: str 

84 path of the dir to create 

85 mode: int 

86 see `mode` of `os.mkdir` 

87 

88 Returns 

89 ------- 

90 bool: 

91 returns True if it created the directory, False otherwise 

92 """ 

93 

94 if os.path.exists(path): 

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

96 try: 

97 os.chmod(path, mode) 

98 except OSError: 

99 self.log.warning( 

100 "Could not set permissions on %s", 

101 path 

102 ) 

103 return False 

104 try: 

105 if mode: 

106 os.mkdir(path, mode) 

107 else: 

108 os.mkdir(path) 

109 except OSError as e: 

110 if e.errno == errno.EEXIST: 

111 return False 

112 else: 

113 raise 

114 

115 return True 

116 

117 @observe('log_dir') 

118 def check_log_dir(self, change=None): 

119 self._mkdir(self.log_dir) 

120 

121 @observe('startup_dir') 

122 def check_startup_dir(self, change=None): 

123 if self._mkdir(self.startup_dir): 

124 readme = os.path.join(self.startup_dir, "README") 

125 src = os.path.join( 

126 get_ipython_package_dir(), "core", "profile", "README_STARTUP" 

127 ) 

128 

129 if os.path.exists(src): 

130 if not os.path.exists(readme): 

131 import shutil 

132 shutil.copy(src, readme) 

133 else: 

134 self.log.warning( 

135 "Could not copy README_STARTUP to startup dir. Source file %s does not exist.", 

136 src, 

137 ) 

138 

139 @observe('security_dir') 

140 def check_security_dir(self, change=None): 

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

142 

143 @observe('pid_dir') 

144 def check_pid_dir(self, change=None): 

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

146 

147 def check_dirs(self): 

148 self.check_security_dir() 

149 self.check_log_dir() 

150 self.check_pid_dir() 

151 self.check_startup_dir() 

152 

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

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

155 

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

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

158 directory. 

159 """ 

160 import shutil 

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

162 if dst.exists() and not overwrite: 

163 return False 

164 src = path / config_file 

165 shutil.copy(src, dst) 

166 return True 

167 

168 @classmethod 

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

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

171 

172 Parameters 

173 ---------- 

174 profile_dir : str 

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

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

177 """ 

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

179 

180 @classmethod 

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

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

183 

184 Parameters 

185 ---------- 

186 path : unicode 

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

188 name : unicode 

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

190 be "profile_<profile>". 

191 """ 

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

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

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

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

196 

197 @classmethod 

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

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

200 

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

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

203 

204 The search path algorithm is: 

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

206 2. ``ipython_dir`` 

207 

208 Parameters 

209 ---------- 

210 ipython_dir : unicode or str 

211 The IPython directory to use. 

212 name : unicode or str 

213 The name of the profile. The name of the profile directory 

214 will be "profile_<profile>". 

215 """ 

216 dirname = 'profile_' + name 

217 paths = [ipython_dir] 

218 for p in paths: 

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

220 if os.path.isdir(profile_dir): 

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

222 else: 

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

224 

225 @classmethod 

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

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

228 

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

230 

231 Parameters 

232 ---------- 

233 profile_dir : unicode or str 

234 The path of the profile directory. 

235 """ 

236 profile_dir = expand_path(profile_dir) 

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

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

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