|
|
|
|
|
""" |
|
|
An application for managing IPython profiles. |
|
|
|
|
|
To be invoked as the `ipython profile` subcommand. |
|
|
|
|
|
Authors: |
|
|
|
|
|
* Min RK |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os |
|
|
|
|
|
from traitlets.config.application import Application |
|
|
from IPython.core.application import ( |
|
|
BaseIPythonApplication, base_flags |
|
|
) |
|
|
from IPython.core.profiledir import ProfileDir |
|
|
from IPython.utils.importstring import import_item |
|
|
from IPython.paths import get_ipython_dir, get_ipython_package_dir |
|
|
from traitlets import Unicode, Bool, Dict, observe |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
create_help = """Create an IPython profile by name |
|
|
|
|
|
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 |
|
|
profile directory by name, |
|
|
`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. |
|
|
|
|
|
""" |
|
|
|
|
|
_list_examples = "ipython profile list # list all profiles" |
|
|
|
|
|
_create_examples = """ |
|
|
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 |
|
|
""" |
|
|
|
|
|
_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 |
|
|
|
|
|
ipython locate profile foo # print the path to the directory for profile 'foo' |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_profiles_in(path): |
|
|
"""list profiles in a given root directory""" |
|
|
profiles = [] |
|
|
|
|
|
|
|
|
files = os.scandir(path) |
|
|
for f in files: |
|
|
if f.is_dir() and f.name.startswith('profile_'): |
|
|
profiles.append(f.name.split('_', 1)[-1]) |
|
|
return profiles |
|
|
|
|
|
|
|
|
def list_bundled_profiles(): |
|
|
"""list profiles that are bundled with IPython.""" |
|
|
path = os.path.join(get_ipython_package_dir(), u'core', u'profile') |
|
|
profiles = [] |
|
|
|
|
|
|
|
|
files = os.scandir(path) |
|
|
for profile in files: |
|
|
if profile.is_dir() and profile.name != "__pycache__": |
|
|
profiles.append(profile.name) |
|
|
return profiles |
|
|
|
|
|
|
|
|
class ProfileLocate(BaseIPythonApplication): |
|
|
description = """print the path to an IPython profile dir""" |
|
|
|
|
|
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): |
|
|
print(self.profile_dir.location) |
|
|
|
|
|
|
|
|
class ProfileList(Application): |
|
|
name = u'ipython-profile' |
|
|
description = list_help |
|
|
examples = _list_examples |
|
|
|
|
|
aliases = Dict({ |
|
|
'ipython-dir' : 'ProfileList.ipython_dir', |
|
|
'log-level' : 'Application.log_level', |
|
|
}) |
|
|
flags = Dict(dict( |
|
|
debug = ({'Application' : {'log_level' : 0}}, |
|
|
"Set Application.log_level to 0, maximizing log output." |
|
|
) |
|
|
)) |
|
|
|
|
|
ipython_dir = Unicode(get_ipython_dir(), |
|
|
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 |
|
|
the environment variable IPYTHONDIR. |
|
|
""" |
|
|
).tag(config=True) |
|
|
|
|
|
|
|
|
def _print_profiles(self, profiles): |
|
|
"""print list of profiles, indented.""" |
|
|
for profile in profiles: |
|
|
print(' %s' % profile) |
|
|
|
|
|
def list_profile_dirs(self): |
|
|
profiles = list_bundled_profiles() |
|
|
if profiles: |
|
|
print() |
|
|
print("Available profiles in IPython:") |
|
|
self._print_profiles(profiles) |
|
|
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.") |
|
|
|
|
|
profiles = list_profiles_in(self.ipython_dir) |
|
|
if profiles: |
|
|
print() |
|
|
print("Available profiles in %s:" % self.ipython_dir) |
|
|
self._print_profiles(profiles) |
|
|
|
|
|
profiles = list_profiles_in(os.getcwd()) |
|
|
if profiles: |
|
|
print() |
|
|
print( |
|
|
"Profiles from CWD have been removed for security reason, see CVE-2022-21699:" |
|
|
) |
|
|
|
|
|
print() |
|
|
print("To use any of the above profiles, start IPython with:") |
|
|
print(" ipython --profile=<name>") |
|
|
print() |
|
|
|
|
|
def start(self): |
|
|
self.list_profile_dirs() |
|
|
|
|
|
|
|
|
create_flags = {} |
|
|
create_flags.update(base_flags) |
|
|
|
|
|
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.)") |
|
|
|
|
|
|
|
|
class ProfileCreate(BaseIPythonApplication): |
|
|
name = u'ipython-profile' |
|
|
description = create_help |
|
|
examples = _create_examples |
|
|
auto_create = Bool(True) |
|
|
def _log_format_default(self): |
|
|
return "[%(name)s] %(message)s" |
|
|
|
|
|
def _copy_config_files_default(self): |
|
|
return True |
|
|
|
|
|
parallel = Bool(False, |
|
|
help="whether to include parallel computing config files" |
|
|
).tag(config=True) |
|
|
|
|
|
@observe('parallel') |
|
|
def _parallel_changed(self, change): |
|
|
parallel_files = [ 'ipcontroller_config.py', |
|
|
'ipengine_config.py', |
|
|
'ipcluster_config.py' |
|
|
] |
|
|
if change['new']: |
|
|
for cf in parallel_files: |
|
|
self.config_files.append(cf) |
|
|
else: |
|
|
for cf in parallel_files: |
|
|
if cf in self.config_files: |
|
|
self.config_files.remove(cf) |
|
|
|
|
|
def parse_command_line(self, argv): |
|
|
super(ProfileCreate, self).parse_command_line(argv) |
|
|
|
|
|
if self.extra_args: |
|
|
self.profile = self.extra_args[0] |
|
|
|
|
|
flags = Dict(create_flags) |
|
|
|
|
|
classes = [ProfileDir] |
|
|
|
|
|
def _import_app(self, app_path): |
|
|
"""import an app class""" |
|
|
app = None |
|
|
name = app_path.rsplit('.', 1)[-1] |
|
|
try: |
|
|
app = import_item(app_path) |
|
|
except ImportError: |
|
|
self.log.info("Couldn't import %s, config file will be excluded", name) |
|
|
except Exception: |
|
|
self.log.warning('Unexpected error importing %s', name, exc_info=True) |
|
|
return app |
|
|
|
|
|
def init_config_files(self): |
|
|
super(ProfileCreate, self).init_config_files() |
|
|
|
|
|
from IPython.terminal.ipapp import TerminalIPythonApp |
|
|
apps = [TerminalIPythonApp] |
|
|
for app_path in ( |
|
|
'ipykernel.kernelapp.IPKernelApp', |
|
|
): |
|
|
app = self._import_app(app_path) |
|
|
if app is not None: |
|
|
apps.append(app) |
|
|
if self.parallel: |
|
|
from ipyparallel.apps.ipcontrollerapp import IPControllerApp |
|
|
from ipyparallel.apps.ipengineapp import IPEngineApp |
|
|
from ipyparallel.apps.ipclusterapp import IPClusterStart |
|
|
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 |
|
|
app.ipython_dir=self.ipython_dir |
|
|
app.profile_dir=self.profile_dir |
|
|
app.init_config_files() |
|
|
|
|
|
def stage_default_config_file(self): |
|
|
pass |
|
|
|
|
|
|
|
|
class ProfileApp(Application): |
|
|
name = u'ipython profile' |
|
|
description = profile_help |
|
|
examples = _main_examples |
|
|
|
|
|
subcommands = Dict(dict( |
|
|
create = (ProfileCreate, ProfileCreate.description.splitlines()[0]), |
|
|
list = (ProfileList, ProfileList.description.splitlines()[0]), |
|
|
locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]), |
|
|
)) |
|
|
|
|
|
def start(self): |
|
|
if self.subapp is None: |
|
|
print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())) |
|
|
print() |
|
|
self.print_description() |
|
|
self.print_subcommands() |
|
|
self.exit(1) |
|
|
else: |
|
|
return self.subapp.start() |
|
|
|