From 1110808a9d39d4b808aef724c861a2e1a38d2a69 Mon Sep 17 00:00:00 2001
From: Devtools Arcadia <arcadia-devtools@yandex-team.ru>
Date: Mon, 7 Feb 2022 18:08:42 +0300
Subject: intermediate changes ref:cde9a383711a11544ce7e107a78147fb96cc4029

---
 .../python/ipython/py3/IPython/core/profileapp.py  | 312 +++++++++++++++++++++
 1 file changed, 312 insertions(+)
 create mode 100644 contrib/python/ipython/py3/IPython/core/profileapp.py

(limited to 'contrib/python/ipython/py3/IPython/core/profileapp.py')

diff --git a/contrib/python/ipython/py3/IPython/core/profileapp.py b/contrib/python/ipython/py3/IPython/core/profileapp.py
new file mode 100644
index 0000000000..9a1bae55ac
--- /dev/null
+++ b/contrib/python/ipython/py3/IPython/core/profileapp.py
@@ -0,0 +1,312 @@
+# encoding: utf-8
+"""
+An application for managing IPython profiles.
+
+To be invoked as the `ipython profile` subcommand.
+
+Authors:
+
+* Min RK
+
+"""
+
+#-----------------------------------------------------------------------------
+#  Copyright (C) 2008  The IPython Development Team
+#
+#  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
+
+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
+
+#-----------------------------------------------------------------------------
+# Constants
+#-----------------------------------------------------------------------------
+
+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'
+"""
+
+#-----------------------------------------------------------------------------
+# Profile Application Class (for `ipython profile` subcommand)
+#-----------------------------------------------------------------------------
+
+
+def list_profiles_in(path):
+    """list profiles in a given root directory"""
+    profiles = []
+
+    # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
+    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 = []
+
+    # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
+    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)
+# 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.)")
+
+
+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)
+        # accept positional arg as profile name
+        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()
+        # use local imports, since these classes may import from here
+        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()
-- 
cgit v1.2.3