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

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

108 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 shutil 

8import errno 

9from pathlib import Path 

10 

11from traitlets.config.configurable import LoggingConfigurable 

12from ..paths import get_ipython_package_dir 

13from ..utils.path import expand_path, ensure_dir_exists 

14from traitlets import Unicode, Bool, observe 

15 

16#----------------------------------------------------------------------------- 

17# Module errors 

18#----------------------------------------------------------------------------- 

19 

20class ProfileDirError(Exception): 

21 pass 

22 

23 

24#----------------------------------------------------------------------------- 

25# Class for managing profile directories 

26#----------------------------------------------------------------------------- 

27 

28class ProfileDir(LoggingConfigurable): 

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

30 

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

32 configuration, logging and security. 

33 

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

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

36 """ 

37 

38 security_dir_name = Unicode('security') 

39 log_dir_name = Unicode('log') 

40 startup_dir_name = Unicode('startup') 

41 pid_dir_name = Unicode('pid') 

42 static_dir_name = Unicode('static') 

43 security_dir = Unicode('') 

44 log_dir = Unicode('') 

45 startup_dir = Unicode('') 

46 pid_dir = Unicode('') 

47 static_dir = Unicode('') 

48 

49 location = Unicode('', 

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

51 `profile` option.""", 

52 ).tag(config=True) 

53 

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

55 @observe('location') 

56 def _location_changed(self, change): 

57 if self._location_isset: 

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

59 self._location_isset = True 

60 new = change['new'] 

61 ensure_dir_exists(new) 

62 

63 # ensure config files exist: 

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

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

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

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

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

69 self.check_dirs() 

70 

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

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

73 

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

75 

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

77 - ignores EEXIST, protecting against race conditions where 

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

79 the creation 

80 - sets permissions if requested and the dir already exists 

81 

82 Parameters 

83 ---------- 

84 path: str 

85 path of the dir to create 

86 mode: int 

87 see `mode` of `os.mkdir` 

88 

89 Returns 

90 ------- 

91 bool: 

92 returns True if it created the directory, False otherwise 

93 """ 

94 

95 if os.path.exists(path): 

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

97 try: 

98 os.chmod(path, mode) 

99 except OSError: 

100 self.log.warning( 

101 "Could not set permissions on %s", 

102 path 

103 ) 

104 return False 

105 try: 

106 if mode: 

107 os.mkdir(path, mode) 

108 else: 

109 os.mkdir(path) 

110 except OSError as e: 

111 if e.errno == errno.EEXIST: 

112 return False 

113 else: 

114 raise 

115 

116 return True 

117 

118 @observe('log_dir') 

119 def check_log_dir(self, change=None): 

120 self._mkdir(self.log_dir) 

121 

122 @observe('startup_dir') 

123 def check_startup_dir(self, change=None): 

124 if self._mkdir(self.startup_dir): 

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

126 src = os.path.join( 

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

128 ) 

129 

130 if os.path.exists(src): 

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

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 dst = Path(os.path.join(self.location, config_file)) 

161 if dst.exists() and not overwrite: 

162 return False 

163 src = path / config_file 

164 shutil.copy(src, dst) 

165 return True 

166 

167 @classmethod 

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

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

170 

171 Parameters 

172 ---------- 

173 profile_dir : str 

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

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

176 """ 

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

178 

179 @classmethod 

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

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

182 

183 Parameters 

184 ---------- 

185 path : unicode 

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

187 name : unicode 

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

189 be "profile_<profile>". 

190 """ 

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

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

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

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

195 

196 @classmethod 

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

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

199 

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

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

202 

203 The search path algorithm is: 

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

205 2. ``ipython_dir`` 

206 

207 Parameters 

208 ---------- 

209 ipython_dir : unicode or str 

210 The IPython directory to use. 

211 name : unicode or str 

212 The name of the profile. The name of the profile directory 

213 will be "profile_<profile>". 

214 """ 

215 dirname = 'profile_' + name 

216 paths = [ipython_dir] 

217 for p in paths: 

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

219 if os.path.isdir(profile_dir): 

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

221 else: 

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

223 

224 @classmethod 

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

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

227 

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

229 

230 Parameters 

231 ---------- 

232 profile_dir : unicode or str 

233 The path of the profile directory. 

234 """ 

235 profile_dir = expand_path(profile_dir) 

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

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

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