profileapp.py
315 lines
| 10.5 KiB
| text/x-python
|
PythonLexer
MinRK
|
r4024 | # encoding: utf-8 | ||
""" | ||||
An application for managing IPython profiles. | ||||
To be invoked as the `ipython profile` subcommand. | ||||
Authors: | ||||
* Min RK | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r12419 | # Copyright (C) 2008 The IPython Development Team | ||
MinRK
|
r4024 | # | ||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import os | ||||
Min RK
|
r21253 | from traitlets.config.application import Application | ||
MinRK
|
r4024 | from IPython.core.application import ( | ||
Thomas Kluyver
|
r9399 | BaseIPythonApplication, base_flags | ||
MinRK
|
r4024 | ) | ||
from IPython.core.profiledir import ProfileDir | ||||
MinRK
|
r12419 | from IPython.utils.importstring import import_item | ||
Min RK
|
r21253 | from IPython.paths import get_ipython_dir, get_ipython_package_dir | ||
Matthias Bussonnier
|
r22341 | from traitlets import Unicode, Bool, Dict, observe | ||
MinRK
|
r4024 | |||
#----------------------------------------------------------------------------- | ||||
# Constants | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r4038 | create_help = """Create an IPython profile by name | ||
MinRK
|
r4024 | |||
Create an ipython profile directory by its name or | ||||
profile directory path. Profile directories contain | ||||
configuration, log and security related files and are named | ||||
using the convention 'profile_<name>'. By default they are | ||||
located in your ipython directory. Once created, you will | ||||
can edit the configuration files in the profile | ||||
directory to configure IPython. Most users will create a | ||||
MinRK
|
r4038 | profile directory by name, | ||
MinRK
|
r4024 | `ipython profile create myprofile`, which will put the directory | ||
in `<ipython_dir>/profile_myprofile`. | ||||
""" | ||||
list_help = """List available IPython profiles | ||||
List all available profiles, by profile location, that can | ||||
be found in the current working directly or in the ipython | ||||
directory. Profile directories are named using the convention | ||||
'profile_<profile>'. | ||||
""" | ||||
profile_help = """Manage IPython profiles | ||||
Profile directories contain | ||||
configuration, log and security related files and are named | ||||
using the convention 'profile_<name>'. By default they are | ||||
located in your ipython directory. You can create profiles | ||||
with `ipython profile create <name>`, or see the profiles you | ||||
already have with `ipython profile list` | ||||
To get started configuring IPython, simply do: | ||||
$> ipython profile create | ||||
and IPython will create the default profile in <ipython_dir>/profile_default, | ||||
where you can edit ipython_config.py to start configuring IPython. | ||||
""" | ||||
Brian Granger
|
r4216 | _list_examples = "ipython profile list # list all profiles" | ||
_create_examples = """ | ||||
Brian E. Granger
|
r4218 | ipython profile create foo # create profile foo w/ default config files | ||
ipython profile create foo --reset # restage default config files over current | ||||
ipython profile create foo --parallel # also stage parallel config files | ||||
Brian Granger
|
r4216 | """ | ||
_main_examples = """ | ||||
ipython profile create -h # show the help string for the create subcommand | ||||
ipython profile list -h # show the help string for the list subcommand | ||||
MinRK
|
r6901 | |||
Katie Silverio
|
r12954 | ipython locate profile foo # print the path to the directory for profile 'foo' | ||
Brian Granger
|
r4216 | """ | ||
MinRK
|
r4024 | #----------------------------------------------------------------------------- | ||
# Profile Application Class (for `ipython profile` subcommand) | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r6191 | def list_profiles_in(path): | ||
"""list profiles in a given root directory""" | ||||
profiles = [] | ||||
kd2718
|
r24652 | |||
kd2718
|
r24685 | # for python 3.6+ rewrite to: with os.scandir(path) as dirlist: | ||
kd2718
|
r24652 | files = os.scandir(path) | ||
for f in files: | ||||
if f.is_dir() and f.name.startswith('profile_'): | ||||
profiles.append(f.name.split('_', 1)[-1]) | ||||
Brian Granger
|
r6191 | return profiles | ||
def list_bundled_profiles(): | ||||
"""list profiles that are bundled with IPython.""" | ||||
Min RK
|
r20870 | path = os.path.join(get_ipython_package_dir(), u'core', u'profile') | ||
Brian Granger
|
r6191 | profiles = [] | ||
kd2718
|
r24652 | |||
kd2718
|
r24685 | # for python 3.6+ rewrite to: with os.scandir(path) as dirlist: | ||
kd2718
|
r24652 | files = os.scandir(path) | ||
for profile in files: | ||||
if profile.is_dir() and profile.name != "__pycache__": | ||||
profiles.append(profile.name) | ||||
Brian Granger
|
r6191 | return profiles | ||
MinRK
|
r6901 | class ProfileLocate(BaseIPythonApplication): | ||
Katie Silverio
|
r12949 | description = """print the path to an IPython profile dir""" | ||
MinRK
|
r6901 | |||
def parse_command_line(self, argv=None): | ||||
super(ProfileLocate, self).parse_command_line(argv) | ||||
if self.extra_args: | ||||
self.profile = self.extra_args[0] | ||||
def start(self): | ||||
Thomas Kluyver
|
r13348 | print(self.profile_dir.location) | ||
MinRK
|
r6901 | |||
MinRK
|
r4024 | class ProfileList(Application): | ||
name = u'ipython-profile' | ||||
description = list_help | ||||
Brian Granger
|
r4216 | examples = _list_examples | ||
MinRK
|
r4214 | aliases = Dict({ | ||
'ipython-dir' : 'ProfileList.ipython_dir', | ||||
'log-level' : 'Application.log_level', | ||||
}) | ||||
MinRK
|
r4024 | flags = Dict(dict( | ||
debug = ({'Application' : {'log_level' : 0}}, | ||||
MinRK
|
r4214 | "Set Application.log_level to 0, maximizing log output." | ||
MinRK
|
r4024 | ) | ||
)) | ||||
Brian Granger
|
r4216 | |||
Matthias Bussonnier
|
r22341 | ipython_dir = Unicode(get_ipython_dir(), | ||
MinRK
|
r4024 | help=""" | ||
The name of the IPython directory. This directory is used for logging | ||||
configuration (through profiles), history storage, etc. The default | ||||
is usually $HOME/.ipython. This options can also be specified through | ||||
Bradley M. Froehle
|
r6696 | the environment variable IPYTHONDIR. | ||
MinRK
|
r4024 | """ | ||
Matthias Bussonnier
|
r22341 | ).tag(config=True) | ||
Brian Granger
|
r6191 | |||
MinRK
|
r5770 | def _print_profiles(self, profiles): | ||
"""print list of profiles, indented.""" | ||||
for profile in profiles: | ||||
Thomas Kluyver
|
r13348 | print(' %s' % profile) | ||
Brian Granger
|
r6191 | |||
MinRK
|
r4024 | def list_profile_dirs(self): | ||
Brian Granger
|
r6191 | profiles = list_bundled_profiles() | ||
MinRK
|
r5769 | if profiles: | ||
Thomas Kluyver
|
r13348 | print() | ||
print("Available profiles in IPython:") | ||||
MinRK
|
r5770 | self._print_profiles(profiles) | ||
Thomas Kluyver
|
r13348 | print() | ||
print(" The first request for a bundled profile will copy it") | ||||
print(" into your IPython directory (%s)," % self.ipython_dir) | ||||
print(" where you can customize it.") | ||||
MinRK
|
r5769 | |||
Brian Granger
|
r6191 | profiles = list_profiles_in(self.ipython_dir) | ||
MinRK
|
r5769 | if profiles: | ||
Thomas Kluyver
|
r13348 | print() | ||
print("Available profiles in %s:" % self.ipython_dir) | ||||
MinRK
|
r5770 | self._print_profiles(profiles) | ||
MinRK
|
r5769 | |||
Srinivas Reddy Thatiparthy
|
r23045 | profiles = list_profiles_in(os.getcwd()) | ||
MinRK
|
r5769 | if profiles: | ||
Thomas Kluyver
|
r13348 | print() | ||
Matthias Bussonnier
|
r27464 | print( | ||
"Profiles from CWD have been removed for security reason, see CVE-2022-21699:" | ||||
) | ||||
Thomas Kluyver
|
r13348 | print() | ||
print("To use any of the above profiles, start IPython with:") | ||||
print(" ipython --profile=<name>") | ||||
print() | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4024 | def start(self): | ||
self.list_profile_dirs() | ||||
create_flags = {} | ||||
create_flags.update(base_flags) | ||||
MinRK
|
r4247 | # don't include '--init' flag, which implies running profile create in other apps | ||
create_flags.pop('init') | ||||
create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}}, | ||||
"reset config files in this profile to the defaults.") | ||||
create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}}, | ||||
"Include the config files for parallel " | ||||
"computing apps (ipengine, ipcontroller, etc.)") | ||||
MinRK
|
r4024 | |||
Brian Granger
|
r4215 | |||
MinRK
|
r4024 | class ProfileCreate(BaseIPythonApplication): | ||
name = u'ipython-profile' | ||||
description = create_help | ||||
Brian Granger
|
r4216 | examples = _create_examples | ||
Matthias Bussonnier
|
r28565 | auto_create = Bool(True).tag(config=True) | ||
MinRK
|
r12420 | def _log_format_default(self): | ||
return "[%(name)s] %(message)s" | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4024 | def _copy_config_files_default(self): | ||
return True | ||||
Bernardo B. Marques
|
r4872 | |||
Matthias Bussonnier
|
r22341 | parallel = Bool(False, | ||
help="whether to include parallel computing config files" | ||||
).tag(config=True) | ||||
@observe('parallel') | ||||
Min RK
|
r22655 | def _parallel_changed(self, change): | ||
Bernardo B. Marques
|
r4872 | parallel_files = [ 'ipcontroller_config.py', | ||
'ipengine_config.py', | ||||
MinRK
|
r4024 | 'ipcluster_config.py' | ||
] | ||||
Min RK
|
r22655 | if change['new']: | ||
MinRK
|
r4038 | for cf in parallel_files: | ||
MinRK
|
r4024 | self.config_files.append(cf) | ||
else: | ||||
MinRK
|
r4038 | for cf in parallel_files: | ||
MinRK
|
r4024 | if cf in self.config_files: | ||
self.config_files.remove(cf) | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4024 | def parse_command_line(self, argv): | ||
super(ProfileCreate, self).parse_command_line(argv) | ||||
# accept positional arg as profile name | ||||
if self.extra_args: | ||||
self.profile = self.extra_args[0] | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4024 | flags = Dict(create_flags) | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4024 | classes = [ProfileDir] | ||
MinRK
|
r12419 | |||
def _import_app(self, app_path): | ||||
"""import an app class""" | ||||
app = None | ||||
name = app_path.rsplit('.', 1)[-1] | ||||
try: | ||||
app = import_item(app_path) | ||||
Thomas Kluyver
|
r13447 | except ImportError: | ||
MinRK
|
r12419 | self.log.info("Couldn't import %s, config file will be excluded", name) | ||
except Exception: | ||||
Pierre Gerold
|
r21888 | self.log.warning('Unexpected error importing %s', name, exc_info=True) | ||
MinRK
|
r12419 | return app | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4025 | def init_config_files(self): | ||
super(ProfileCreate, self).init_config_files() | ||||
# use local imports, since these classes may import from here | ||||
Fernando Perez
|
r11024 | from IPython.terminal.ipapp import TerminalIPythonApp | ||
MinRK
|
r4025 | apps = [TerminalIPythonApp] | ||
MinRK
|
r12419 | for app_path in ( | ||
Min RK
|
r21337 | 'ipykernel.kernelapp.IPKernelApp', | ||
MinRK
|
r12419 | ): | ||
app = self._import_app(app_path) | ||||
if app is not None: | ||||
apps.append(app) | ||||
MinRK
|
r4038 | if self.parallel: | ||
Min RK
|
r21326 | from ipyparallel.apps.ipcontrollerapp import IPControllerApp | ||
from ipyparallel.apps.ipengineapp import IPEngineApp | ||||
from ipyparallel.apps.ipclusterapp import IPClusterStart | ||||
MinRK
|
r4025 | apps.extend([ | ||
IPControllerApp, | ||||
IPEngineApp, | ||||
IPClusterStart, | ||||
]) | ||||
for App in apps: | ||||
app = App() | ||||
app.config.update(self.config) | ||||
app.log = self.log | ||||
app.overwrite = self.overwrite | ||||
app.copy_config_files=True | ||||
MinRK
|
r14898 | app.ipython_dir=self.ipython_dir | ||
app.profile_dir=self.profile_dir | ||||
MinRK
|
r4025 | app.init_config_files() | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4025 | def stage_default_config_file(self): | ||
pass | ||||
MinRK
|
r4024 | |||
Brian Granger
|
r4215 | |||
MinRK
|
r4024 | class ProfileApp(Application): | ||
Thomas Kluyver
|
r16561 | name = u'ipython profile' | ||
MinRK
|
r4024 | description = profile_help | ||
Brian Granger
|
r4216 | examples = _main_examples | ||
Brian Granger
|
r4215 | |||
MinRK
|
r4024 | subcommands = Dict(dict( | ||
MinRK
|
r6901 | create = (ProfileCreate, ProfileCreate.description.splitlines()[0]), | ||
list = (ProfileList, ProfileList.description.splitlines()[0]), | ||||
Katie Silverio
|
r12949 | locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]), | ||
MinRK
|
r4024 | )) | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4026 | def start(self): | ||
if self.subapp is None: | ||||
Antony Lee
|
r28743 | print( | ||
"No subcommand specified. Must specify one of: " | ||||
+ ", ".join(map(repr, self.subcommands)) | ||||
+ ".\n" | ||||
) | ||||
MinRK
|
r4026 | self.print_description() | ||
self.print_subcommands() | ||||
self.exit(1) | ||||
else: | ||||
return self.subapp.start() | ||||