profileapp.py 10 KB

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