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

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

113 statements  

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 

17from typing import Optional 

18 

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

20# Module errors 

21#----------------------------------------------------------------------------- 

22 

23class ProfileDirError(Exception): 

24 pass 

25 

26 

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

28# Class for managing profile directories 

29#----------------------------------------------------------------------------- 

30 

31class ProfileDir(LoggingConfigurable): 

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

33 

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

35 configuration, logging and security. 

36 

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

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

39 """ 

40 

41 security_dir_name = Unicode('security') 

42 log_dir_name = Unicode('log') 

43 startup_dir_name = Unicode('startup') 

44 pid_dir_name = Unicode('pid') 

45 static_dir_name = Unicode('static') 

46 security_dir = Unicode(u'') 

47 log_dir = Unicode(u'') 

48 startup_dir = Unicode(u'') 

49 pid_dir = Unicode(u'') 

50 static_dir = Unicode(u'') 

51 

52 location = Unicode(u'', 

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

54 `profile` option.""", 

55 ).tag(config=True) 

56 

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

58 @observe('location') 

59 def _location_changed(self, change): 

60 if self._location_isset: 

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

62 self._location_isset = True 

63 new = change['new'] 

64 ensure_dir_exists(new) 

65 

66 # ensure config files exist: 

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

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

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

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

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

72 self.check_dirs() 

73 

74 def _mkdir(self, path: str, mode: Optional[int] = None) -> bool: 

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

76 

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

78 

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

80 - ignores EEXIST, protecting against race conditions where 

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

82 the creation 

83 - sets permissions if requested and the dir already exists 

84 

85 Parameters 

86 ---------- 

87 path: str 

88 path of the dir to create 

89 mode: int 

90 see `mode` of `os.mkdir` 

91 

92 Returns 

93 ------- 

94 bool: 

95 returns True if it created the directory, False otherwise 

96 """ 

97 

98 if os.path.exists(path): 

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

100 try: 

101 os.chmod(path, mode) 

102 except OSError: 

103 self.log.warning( 

104 "Could not set permissions on %s", 

105 path 

106 ) 

107 return False 

108 try: 

109 if mode: 

110 os.mkdir(path, mode) 

111 else: 

112 os.mkdir(path) 

113 except OSError as e: 

114 if e.errno == errno.EEXIST: 

115 return False 

116 else: 

117 raise 

118 

119 return True 

120 

121 @observe('log_dir') 

122 def check_log_dir(self, change=None): 

123 self._mkdir(self.log_dir) 

124 

125 @observe('startup_dir') 

126 def check_startup_dir(self, change=None): 

127 if self._mkdir(self.startup_dir): 

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

129 src = os.path.join( 

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

131 ) 

132 

133 if os.path.exists(src): 

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

135 shutil.copy(src, readme) 

136 else: 

137 self.log.warning( 

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

139 src, 

140 ) 

141 

142 @observe('security_dir') 

143 def check_security_dir(self, change=None): 

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

145 

146 @observe('pid_dir') 

147 def check_pid_dir(self, change=None): 

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

149 

150 def check_dirs(self): 

151 self.check_security_dir() 

152 self.check_log_dir() 

153 self.check_pid_dir() 

154 self.check_startup_dir() 

155 

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

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

158 

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

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

161 directory. 

162 """ 

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

164 if dst.exists() and not overwrite: 

165 return False 

166 if path is None: 

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

168 assert isinstance(path, Path) 

169 src = path / config_file 

170 shutil.copy(src, dst) 

171 return True 

172 

173 @classmethod 

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

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

176 

177 Parameters 

178 ---------- 

179 profile_dir : str 

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

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

182 """ 

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

184 

185 @classmethod 

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

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

188 

189 Parameters 

190 ---------- 

191 path : unicode 

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

193 name : unicode 

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

195 be "profile_<profile>". 

196 """ 

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

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

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

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

201 

202 @classmethod 

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

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

205 

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

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

208 

209 The search path algorithm is: 

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

211 2. ``ipython_dir`` 

212 

213 Parameters 

214 ---------- 

215 ipython_dir : unicode or str 

216 The IPython directory to use. 

217 name : unicode or str 

218 The name of the profile. The name of the profile directory 

219 will be "profile_<profile>". 

220 """ 

221 dirname = u'profile_' + name 

222 paths = [ipython_dir] 

223 for p in paths: 

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

225 if os.path.isdir(profile_dir): 

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

227 else: 

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

229 

230 @classmethod 

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

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

233 

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

235 

236 Parameters 

237 ---------- 

238 profile_dir : unicode or str 

239 The path of the profile directory. 

240 """ 

241 profile_dir = expand_path(profile_dir) 

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

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

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