1# encoding: utf-8 2""" 3An application for managing IPython profiles. 4 5To be invoked as the `ipython profile` subcommand. 6 7Authors: 8 9* Min RK 10 11""" 12 13#----------------------------------------------------------------------------- 14# Copyright (C) 2008 The IPython Development Team 15# 16# Distributed under the terms of the BSD License. The full license is in 17# the file COPYING, distributed as part of this software. 18#----------------------------------------------------------------------------- 19 20#----------------------------------------------------------------------------- 21# Imports 22#----------------------------------------------------------------------------- 23 24import os 25 26from traitlets.config.application import Application 27from IPython.core.application import ( 28 BaseIPythonApplication, base_flags 29) 30from IPython.core.profiledir import ProfileDir 31from IPython.utils.importstring import import_item 32from IPython.paths import get_ipython_dir, get_ipython_package_dir 33from traitlets import Unicode, Bool, Dict, observe 34 35#----------------------------------------------------------------------------- 36# Constants 37#----------------------------------------------------------------------------- 38 39create_help = """Create an IPython profile by name 40 41Create an ipython profile directory by its name or 42profile directory path. Profile directories contain 43configuration, log and security related files and are named 44using the convention 'profile_<name>'. By default they are 45located in your ipython directory. Once created, you will 46can edit the configuration files in the profile 47directory to configure IPython. Most users will create a 48profile directory by name, 49`ipython profile create myprofile`, which will put the directory 50in `<ipython_dir>/profile_myprofile`. 51""" 52list_help = """List available IPython profiles 53 54List all available profiles, by profile location, that can 55be found in the current working directly or in the ipython 56directory. Profile directories are named using the convention 57'profile_<profile>'. 58""" 59profile_help = """Manage IPython profiles 60 61Profile directories contain 62configuration, log and security related files and are named 63using the convention 'profile_<name>'. By default they are 64located in your ipython directory. You can create profiles 65with `ipython profile create <name>`, or see the profiles you 66already have with `ipython profile list` 67 68To get started configuring IPython, simply do: 69 70$> ipython profile create 71 72and IPython will create the default profile in <ipython_dir>/profile_default, 73where you can edit ipython_config.py to start configuring IPython. 74 75""" 76 77_list_examples = "ipython profile list # list all profiles" 78 79_create_examples = """ 80ipython profile create foo # create profile foo w/ default config files 81ipython profile create foo --reset # restage default config files over current 82ipython profile create foo --parallel # also stage parallel config files 83""" 84 85_main_examples = """ 86ipython profile create -h # show the help string for the create subcommand 87ipython profile list -h # show the help string for the list subcommand 88 89ipython locate profile foo # print the path to the directory for profile 'foo' 90""" 91 92#----------------------------------------------------------------------------- 93# Profile Application Class (for `ipython profile` subcommand) 94#----------------------------------------------------------------------------- 95 96 97def list_profiles_in(path): 98 """list profiles in a given root directory""" 99 profiles = [] 100 101 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist: 102 files = os.scandir(path) 103 for f in files: 104 if f.is_dir() and f.name.startswith('profile_'): 105 profiles.append(f.name.split('_', 1)[-1]) 106 return profiles 107 108 109def list_bundled_profiles(): 110 """list profiles that are bundled with IPython.""" 111 path = os.path.join(get_ipython_package_dir(), u'core', u'profile') 112 profiles = [] 113 114 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist: 115 files = os.scandir(path) 116 for profile in files: 117 if profile.is_dir() and profile.name != "__pycache__": 118 profiles.append(profile.name) 119 return profiles 120 121 122class ProfileLocate(BaseIPythonApplication): 123 description = """print the path to an IPython profile dir""" 124 125 def parse_command_line(self, argv=None): 126 super(ProfileLocate, self).parse_command_line(argv) 127 if self.extra_args: 128 self.profile = self.extra_args[0] 129 130 def start(self): 131 print(self.profile_dir.location) 132 133 134class ProfileList(Application): 135 name = u'ipython-profile' 136 description = list_help 137 examples = _list_examples 138 139 aliases = Dict({ 140 'ipython-dir' : 'ProfileList.ipython_dir', 141 'log-level' : 'Application.log_level', 142 }) 143 flags = Dict(dict( 144 debug = ({'Application' : {'log_level' : 0}}, 145 "Set Application.log_level to 0, maximizing log output." 146 ) 147 )) 148 149 ipython_dir = Unicode(get_ipython_dir(), 150 help=""" 151 The name of the IPython directory. This directory is used for logging 152 configuration (through profiles), history storage, etc. The default 153 is usually $HOME/.ipython. This options can also be specified through 154 the environment variable IPYTHONDIR. 155 """ 156 ).tag(config=True) 157 158 159 def _print_profiles(self, profiles): 160 """print list of profiles, indented.""" 161 for profile in profiles: 162 print(' %s' % profile) 163 164 def list_profile_dirs(self): 165 profiles = list_bundled_profiles() 166 if profiles: 167 print() 168 print("Available profiles in IPython:") 169 self._print_profiles(profiles) 170 print() 171 print(" The first request for a bundled profile will copy it") 172 print(" into your IPython directory (%s)," % self.ipython_dir) 173 print(" where you can customize it.") 174 175 profiles = list_profiles_in(self.ipython_dir) 176 if profiles: 177 print() 178 print("Available profiles in %s:" % self.ipython_dir) 179 self._print_profiles(profiles) 180 181 profiles = list_profiles_in(os.getcwd()) 182 if profiles: 183 print() 184 print("Available profiles in current directory (%s):" % os.getcwd()) 185 self._print_profiles(profiles) 186 187 print() 188 print("To use any of the above profiles, start IPython with:") 189 print(" ipython --profile=<name>") 190 print() 191 192 def start(self): 193 self.list_profile_dirs() 194 195 196create_flags = {} 197create_flags.update(base_flags) 198# don't include '--init' flag, which implies running profile create in other apps 199create_flags.pop('init') 200create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}}, 201 "reset config files in this profile to the defaults.") 202create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}}, 203 "Include the config files for parallel " 204 "computing apps (ipengine, ipcontroller, etc.)") 205 206 207class ProfileCreate(BaseIPythonApplication): 208 name = u'ipython-profile' 209 description = create_help 210 examples = _create_examples 211 auto_create = Bool(True) 212 def _log_format_default(self): 213 return "[%(name)s] %(message)s" 214 215 def _copy_config_files_default(self): 216 return True 217 218 parallel = Bool(False, 219 help="whether to include parallel computing config files" 220 ).tag(config=True) 221 222 @observe('parallel') 223 def _parallel_changed(self, change): 224 parallel_files = [ 'ipcontroller_config.py', 225 'ipengine_config.py', 226 'ipcluster_config.py' 227 ] 228 if change['new']: 229 for cf in parallel_files: 230 self.config_files.append(cf) 231 else: 232 for cf in parallel_files: 233 if cf in self.config_files: 234 self.config_files.remove(cf) 235 236 def parse_command_line(self, argv): 237 super(ProfileCreate, self).parse_command_line(argv) 238 # accept positional arg as profile name 239 if self.extra_args: 240 self.profile = self.extra_args[0] 241 242 flags = Dict(create_flags) 243 244 classes = [ProfileDir] 245 246 def _import_app(self, app_path): 247 """import an app class""" 248 app = None 249 name = app_path.rsplit('.', 1)[-1] 250 try: 251 app = import_item(app_path) 252 except ImportError: 253 self.log.info("Couldn't import %s, config file will be excluded", name) 254 except Exception: 255 self.log.warning('Unexpected error importing %s', name, exc_info=True) 256 return app 257 258 def init_config_files(self): 259 super(ProfileCreate, self).init_config_files() 260 # use local imports, since these classes may import from here 261 from IPython.terminal.ipapp import TerminalIPythonApp 262 apps = [TerminalIPythonApp] 263 for app_path in ( 264 'ipykernel.kernelapp.IPKernelApp', 265 ): 266 app = self._import_app(app_path) 267 if app is not None: 268 apps.append(app) 269 if self.parallel: 270 from ipyparallel.apps.ipcontrollerapp import IPControllerApp 271 from ipyparallel.apps.ipengineapp import IPEngineApp 272 from ipyparallel.apps.ipclusterapp import IPClusterStart 273 apps.extend([ 274 IPControllerApp, 275 IPEngineApp, 276 IPClusterStart, 277 ]) 278 for App in apps: 279 app = App() 280 app.config.update(self.config) 281 app.log = self.log 282 app.overwrite = self.overwrite 283 app.copy_config_files=True 284 app.ipython_dir=self.ipython_dir 285 app.profile_dir=self.profile_dir 286 app.init_config_files() 287 288 def stage_default_config_file(self): 289 pass 290 291 292class ProfileApp(Application): 293 name = u'ipython profile' 294 description = profile_help 295 examples = _main_examples 296 297 subcommands = Dict(dict( 298 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]), 299 list = (ProfileList, ProfileList.description.splitlines()[0]), 300 locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]), 301 )) 302 303 def start(self): 304 if self.subapp is None: 305 print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())) 306 print() 307 self.print_description() 308 self.print_subcommands() 309 self.exit(1) 310 else: 311 return self.subapp.start() 312