aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorrobot-contrib <robot-contrib@yandex-team.com>2023-10-19 17:11:31 +0300
committerrobot-contrib <robot-contrib@yandex-team.com>2023-10-19 18:26:04 +0300
commitb9fe236a503791a3a7b37d4ef5f466225218996c (patch)
treec2f80019399b393ddf0450d0f91fc36478af8bea /contrib
parent44dd27d0a2ae37c80d97a95581951d1d272bd7df (diff)
downloadydb-b9fe236a503791a3a7b37d4ef5f466225218996c.tar.gz
Update contrib/python/traitlets/py3 to 5.11.2
Diffstat (limited to 'contrib')
-rw-r--r--contrib/python/argcomplete/py2/Authors.rst1
-rw-r--r--contrib/python/argcomplete/py2/LICENSE.rst177
-rw-r--r--contrib/python/argcomplete/py2/README.rst367
-rw-r--r--contrib/python/argcomplete/py3/.dist-info/METADATA351
-rw-r--r--contrib/python/argcomplete/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/argcomplete/py3/Authors.rst1
-rw-r--r--contrib/python/argcomplete/py3/LICENSE.rst177
-rw-r--r--contrib/python/argcomplete/py3/NOTICE4
-rw-r--r--contrib/python/argcomplete/py3/README.rst306
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/__init__.py13
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/_check_console_script.py73
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/_check_module.py89
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete236
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/completers.py124
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/exceptions.py2
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/finders.py590
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/io.py42
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/lexers.py57
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/packages/__init__.py0
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/packages/_argparse.py337
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/packages/_shlex.py310
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/py.typed0
-rw-r--r--contrib/python/argcomplete/py3/argcomplete/shell_integration.py183
-rw-r--r--contrib/python/argcomplete/py3/ya.make35
-rw-r--r--contrib/python/argcomplete/ya.make18
-rw-r--r--contrib/python/traitlets/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/traitlets/py3/tests/__init__.py0
-rw-r--r--contrib/python/traitlets/py3/tests/_warnings.py (renamed from contrib/python/traitlets/py3/traitlets/tests/_warnings.py)0
-rw-r--r--contrib/python/traitlets/py3/tests/config/__init__.py0
-rw-r--r--contrib/python/traitlets/py3/tests/config/test_application.py (renamed from contrib/python/traitlets/py3/traitlets/config/tests/test_application.py)4
-rw-r--r--contrib/python/traitlets/py3/tests/config/test_argcomplete.py219
-rw-r--r--contrib/python/traitlets/py3/tests/config/test_configurable.py (renamed from contrib/python/traitlets/py3/traitlets/config/tests/test_configurable.py)3
-rw-r--r--contrib/python/traitlets/py3/tests/config/test_loader.py (renamed from contrib/python/traitlets/py3/traitlets/config/tests/test_loader.py)0
-rw-r--r--contrib/python/traitlets/py3/tests/test_traitlets.py (renamed from contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py)2
-rw-r--r--contrib/python/traitlets/py3/tests/test_traitlets_docstring.py84
-rw-r--r--contrib/python/traitlets/py3/tests/test_traitlets_enum.py (renamed from contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py)0
-rw-r--r--contrib/python/traitlets/py3/tests/test_typing.py395
-rw-r--r--contrib/python/traitlets/py3/tests/utils/__init__.py0
-rw-r--r--contrib/python/traitlets/py3/tests/utils/test_bunch.py (renamed from contrib/python/traitlets/py3/traitlets/utils/tests/test_bunch.py)0
-rw-r--r--contrib/python/traitlets/py3/tests/utils/test_decorators.py (renamed from contrib/python/traitlets/py3/traitlets/utils/tests/test_decorators.py)2
-rw-r--r--contrib/python/traitlets/py3/tests/utils/test_importstring.py (renamed from contrib/python/traitlets/py3/traitlets/utils/tests/test_importstring.py)0
-rw-r--r--contrib/python/traitlets/py3/tests/ya.make27
-rw-r--r--contrib/python/traitlets/py3/traitlets/__init__.py4
-rw-r--r--contrib/python/traitlets/py3/traitlets/_version.py2
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/application.py223
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/argcomplete_config.py6
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/configurable.py79
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/loader.py28
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/manager.py15
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/sphinxdoc.py1
-rw-r--r--contrib/python/traitlets/py3/traitlets/log.py5
-rw-r--r--contrib/python/traitlets/py3/traitlets/tests/utils.py17
-rw-r--r--contrib/python/traitlets/py3/traitlets/traitlets.py41
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/__init__.py9
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/bunch.py9
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/decorators.py4
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/descriptions.py19
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/getargspec.py4
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/nested_update.py3
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/text.py2
-rw-r--r--contrib/python/traitlets/py3/ya.make3
61 files changed, 4480 insertions, 226 deletions
diff --git a/contrib/python/argcomplete/py2/Authors.rst b/contrib/python/argcomplete/py2/Authors.rst
new file mode 100644
index 0000000000..3c0c589908
--- /dev/null
+++ b/contrib/python/argcomplete/py2/Authors.rst
@@ -0,0 +1 @@
+Andrey Kislyuk <kislyuk@gmail.com>
diff --git a/contrib/python/argcomplete/py2/LICENSE.rst b/contrib/python/argcomplete/py2/LICENSE.rst
new file mode 100644
index 0000000000..f433b1a53f
--- /dev/null
+++ b/contrib/python/argcomplete/py2/LICENSE.rst
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/contrib/python/argcomplete/py2/README.rst b/contrib/python/argcomplete/py2/README.rst
new file mode 100644
index 0000000000..86c8d44f5c
--- /dev/null
+++ b/contrib/python/argcomplete/py2/README.rst
@@ -0,0 +1,367 @@
+argcomplete - Bash tab completion for argparse
+==============================================
+*Tab complete all the things!*
+
+Argcomplete provides easy, extensible command line tab completion of arguments for your Python script.
+
+It makes two assumptions:
+
+* You're using bash as your shell (limited support for zsh, fish, and tcsh is available)
+* You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options
+
+Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can
+dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over
+the network).
+
+Installation
+------------
+::
+
+ pip3 install argcomplete
+ activate-global-python-argcomplete
+
+See `Activating global completion`_ below for details about the second step (or if it reports an error).
+
+Refresh your bash environment (start a new shell or ``source /etc/profile``).
+
+Synopsis
+--------
+Python code (e.g. ``my-awesome-script``):
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse
+ parser = argparse.ArgumentParser()
+ ...
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+ ...
+
+Shellcode (only necessary if global completion is not activated - see `Global completion`_ below), to be put in e.g. ``.bashrc``::
+
+ eval "$(register-python-argcomplete my-awesome-script)"
+
+argcomplete.autocomplete(*parser*)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but
+**before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the
+completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by
+default), and exits. Otherwise, it returns to the caller immediately.
+
+.. admonition:: Side effects
+
+ Argcomplete gets completions by running your program. It intercepts the execution flow at the moment
+ ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit``
+ by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those
+ side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or
+ stderr will be suppressed). For this reason it's best to construct the argument parser and call
+ ``argcomplete.autocomplete()`` as early as possible in your execution flow.
+
+.. admonition:: Performance
+
+ If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion
+ process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time
+ of the program up to that point (for example, by deferring initialization or importing of large modules until after
+ parsing options).
+
+Specifying completers
+---------------------
+You can specify custom completion functions for your options and arguments. Two styles are supported: callable and
+readline-style. Callable completers are simpler. They are called with the following keyword arguments:
+
+* ``prefix``: The prefix text of the last word before the cursor on the command line.
+ For dynamic completers, this can be used to reduce the work required to generate possible completions.
+* ``action``: The ``argparse.Action`` instance that this completer was called for.
+* ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by.
+* ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by
+ ``ArgumentParser.parse_args()``).
+
+Completers should return their completions as a list of strings. An example completer for names of environment
+variables might look like this:
+
+.. code-block:: python
+
+ def EnvironCompleter(**kwargs):
+ return os.environ
+
+To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy
+way to do this at definition time is:
+
+.. code-block:: python
+
+ from argcomplete.completers import EnvironCompleter
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--env-var1").completer = EnvironCompleter
+ parser.add_argument("--env-var2").completer = EnvironCompleter
+ argcomplete.autocomplete(parser)
+
+If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be
+used for completions.
+
+A completer that is initialized with a set of all possible choices of values for its action might look like this:
+
+.. code-block:: python
+
+ class ChoicesCompleter(object):
+ def __init__(self, choices):
+ self.choices = choices
+
+ def __call__(self, **kwargs):
+ return self.choices
+
+The following two ways to specify a static set of choices are equivalent for completion purposes:
+
+.. code-block:: python
+
+ from argcomplete.completers import ChoicesCompleter
+
+ parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss'))
+ parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss'))
+
+Note that if you use the ``choices=<completions>`` option, argparse will show
+all these choices in the ``--help`` output by default. To prevent this, set
+``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL",
+choices=('http', 'https', 'ssh', 'rsync', 'wss'))``).
+
+The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses
+``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an
+organization and complete their names, then prints the member description:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse, requests, pprint
+
+ def github_org_members(prefix, parsed_args, **kwargs):
+ resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization)
+ return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix))
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--organization", help="GitHub organization")
+ parser.add_argument("--member", help="GitHub member").completer = github_org_members
+
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+
+ pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json())
+
+`Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this::
+
+ ./describe_github_user.py --organization heroku --member <TAB>
+
+If you have a useful completer to add to the `completer library
+<https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request!
+
+Readline-style completers
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by
+argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the bash
+command line. For example, you can use the readline-style completer provided by IPython_ to get introspective
+completions like you would get in the IPython shell:
+
+.. _readline: http://docs.python.org/3/library/readline.html
+.. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects
+.. _IPython: http://ipython.org/
+
+.. code-block:: python
+
+ import IPython
+ parser.add_argument("--python-name").completer = IPython.core.completer.Completer()
+
+``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer.
+
+Printing warnings in completers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's
+appropriate to print information about why completions generation failed. To do this, use ``warn``:
+
+.. code-block:: python
+
+ from argcomplete import warn
+
+ def AwesomeWebServiceCompleter(prefix, **kwargs):
+ if login_failed:
+ warn("Please log in to Awesome Web Service to use autocompletion")
+ return completions
+
+Using a custom completion validator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You
+can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``:
+
+.. code-block:: python
+
+ def my_validator(current_input, keyword_to_check_against):
+ # Pass through ALL options even if they don't all start with 'current_input'
+ return True
+
+ argcomplete.autocomplete(parser, validator=my_validator)
+
+Global completion
+-----------------
+In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, bash
+will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running
+completion for, and if it's found, follow the rest of the argcomplete protocol as described above.
+
+Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``.
+This also works for alternate Python versions (e.g. ``python3`` and ``pypy``), as long as that version of Python has
+argcomplete installed.
+
+.. admonition:: Bash version compatibility
+
+ Global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. On OS X or older Linux
+ systems, you will need to update bash to use this feature. Check the version of the running copy of bash with
+ ``echo $BASH_VERSION``. On OS X, install bash via `Homebrew <http://brew.sh/>`_ (``brew install bash``), add
+ ``/usr/local/bin/bash`` to ``/etc/shells``, and run ``chsh`` to change your shell.
+
+ Global completion is not currently compatible with zsh.
+
+.. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module,
+ argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the
+ destination code.
+
+If you choose not to use global completion, or ship a bash completion module that depends on argcomplete, you must
+register your script explicitly using ``eval "$(register-python-argcomplete my-awesome-script)"``. Standard bash
+completion registration roules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab
+completed when invoked exactly as it was registered. In the above example, ``my-awesome-script`` must be on the path,
+and the user must be attempting to complete it by that name. The above line alone would **not** allow you to complete
+``./my-awesome-script``, or ``/path/to/my-awesome-script``.
+
+
+Activating global completion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The script ``activate-global-python-argcomplete`` will try to install the file
+``bash_completion.d/python-argcomplete`` (`see on GitHub`_) into an appropriate location on your system
+(``/etc/bash_completion.d/`` or ``~/.bash_completion.d/``). If it
+fails, but you know the correct location of your bash completion scripts directory, you can specify it with ``--dest``::
+
+ activate-global-python-argcomplete --dest=/path/to/bash_completion.d
+
+Otherwise, you can redirect its shellcode output into a file::
+
+ activate-global-python-argcomplete --dest=- > file
+
+The file's contents should then be sourced in e.g. ``~/.bashrc``.
+
+.. _`see on GitHub`: https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/python-argcomplete
+
+Zsh Support
+------------
+To activate completions for zsh you need to have ``bashcompinit`` enabled in zsh::
+
+ autoload -U bashcompinit
+ bashcompinit
+
+Afterwards you can enable completion for your scripts with ``register-python-argcomplete``::
+
+ eval "$(register-python-argcomplete my-awesome-script)"
+
+Tcsh Support
+------------
+To activate completions for tcsh use::
+
+ eval `register-python-argcomplete --shell tcsh my-awesome-script`
+
+The ``python-argcomplete-tcsh`` script provides completions for tcsh.
+The following is an example of the tcsh completion syntax for
+``my-awesome-script`` emitted by ``register-python-argcomplete``::
+
+ complete my-awesome-script 'p@*@`python-argcomplete-tcsh my-awesome-script`@'
+
+Fish Support
+------------
+To activate completions for fish use::
+
+ register-python-argcomplete --shell fish my-awesome-script | source
+
+or create new completion file, e.g::
+
+ register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish
+
+Completion Description For Fish
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+By default help string is added as completion description.
+
+.. image:: docs/fish_help_string.png
+
+You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g::
+
+ register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | .
+
+Git Bash Support
+----------------
+Due to limitations of file descriptor inheritance on Windows,
+Git Bash not supported out of the box. You can opt in to using
+temporary files instead of file descriptors for for IPC
+by setting the environment variable ``ARGCOMPLETE_USE_TEMPFILES``,
+e.g. by adding ``export ARGCOMPLETE_USE_TEMPFILES=1`` to ``~/.bashrc``.
+
+For full support, consider using Bash with the
+Windows Subsystem for Linux (WSL).
+
+External argcomplete script
+---------------------------
+To register an argcomplete script for an arbitrary name, the ``--external-argcomplete-script`` argument of the ``register-python-argcomplete`` script can be used::
+
+ eval "$(register-python-argcomplete --external-argcomplete-script /path/to/script arbitrary-name)"
+
+This allows, for example, to use the auto completion functionality of argcomplete for an application not written in Python.
+The command line interface of this program must be additionally implemented in a Python script with argparse and argcomplete and whenever the application is called the registered external argcomplete script is used for auto completion.
+
+This option can also be used in combination with the other supported shells.
+
+Python Support
+--------------
+Argcomplete requires Python 2.7 or 3.5+.
+
+Common Problems
+---------------
+If global completion is not completing your script, bash may have registered a
+default completion function::
+
+ $ complete | grep my-awesome-script
+ complete -F _minimal my-awesome-script
+
+You can fix this by restarting your shell, or by running
+``complete -r my-awesome-script``.
+
+Debugging
+---------
+Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will
+disrupt the command line composition state of your terminal, but make it possible to see the internal state of the
+completer if it encounters problems.
+
+Acknowledgments
+---------------
+Inspired and informed by the optcomplete_ module by Martin Blais.
+
+.. _optcomplete: http://pypi.python.org/pypi/optcomplete
+
+Links
+-----
+* `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_
+* `Documentation <https://kislyuk.github.io/argcomplete/>`_
+* `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_
+* `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_
+* `xontrib-argcomplete <https://github.com/anki-code/xontrib-argcomplete>`_ - support argcomplete in `xonsh <https://github.com/xonsh/xonsh>`_ shell
+
+Bugs
+~~~~
+Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_.
+
+License
+-------
+Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_.
+
+.. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg
+ :target: https://github.com/kislyuk/argcomplete/actions
+.. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master
+ :target: https://codecov.io/github/kislyuk/argcomplete?branch=master
+.. image:: https://img.shields.io/pypi/v/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
+.. image:: https://img.shields.io/pypi/l/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
diff --git a/contrib/python/argcomplete/py3/.dist-info/METADATA b/contrib/python/argcomplete/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..7d995caedb
--- /dev/null
+++ b/contrib/python/argcomplete/py3/.dist-info/METADATA
@@ -0,0 +1,351 @@
+Metadata-Version: 2.1
+Name: argcomplete
+Version: 3.1.2
+Summary: Bash tab completion for argparse
+Home-page: https://github.com/kislyuk/argcomplete
+Author: Andrey Kislyuk
+Author-email: kislyuk@gmail.com
+License: Apache Software License
+Project-URL: Documentation, https://kislyuk.github.io/argcomplete
+Project-URL: Source Code, https://github.com/kislyuk/argcomplete
+Project-URL: Issue Tracker, https://github.com/kislyuk/argcomplete/issues
+Project-URL: Change Log, https://github.com/kislyuk/argcomplete/blob/master/Changes.rst
+Platform: MacOS X
+Platform: Posix
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Topic :: Software Development
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Shells
+Classifier: Topic :: Terminals
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+License-File: LICENSE.rst
+License-File: NOTICE
+Requires-Dist: importlib-metadata <7,>=0.23 ; python_version < "3.8"
+Provides-Extra: test
+Requires-Dist: coverage ; extra == 'test'
+Requires-Dist: pexpect ; extra == 'test'
+Requires-Dist: wheel ; extra == 'test'
+Requires-Dist: ruff ; extra == 'test'
+Requires-Dist: mypy ; extra == 'test'
+
+argcomplete - Bash/zsh tab completion for argparse
+==================================================
+*Tab complete all the things!*
+
+Argcomplete provides easy, extensible command line tab completion of arguments for your Python application.
+
+It makes two assumptions:
+
+* You're using bash or zsh as your shell
+* You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options
+
+Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can
+dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over
+the network).
+
+Installation
+------------
+::
+
+ pip install argcomplete
+ activate-global-python-argcomplete
+
+See `Activating global completion`_ below for details about the second step.
+
+Refresh your shell environment (start a new shell).
+
+Synopsis
+--------
+Add the ``PYTHON_ARGCOMPLETE_OK`` marker and a call to ``argcomplete.autocomplete()`` to your Python application as
+follows:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse
+ parser = argparse.ArgumentParser()
+ ...
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+ ...
+
+Register your Python application with your shell's completion framework by running ``register-python-argcomplete``::
+
+ eval "$(register-python-argcomplete my-python-app)"
+
+Quotes are significant; the registration will fail without them. See `Global completion`_ below for a way to enable
+argcomplete generally without registering each application individually.
+
+argcomplete.autocomplete(*parser*)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but
+**before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the
+completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by
+default), and exits. Otherwise, it returns to the caller immediately.
+
+.. admonition:: Side effects
+
+ Argcomplete gets completions by running your program. It intercepts the execution flow at the moment
+ ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit``
+ by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those
+ side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or
+ stderr will be suppressed). For this reason it's best to construct the argument parser and call
+ ``argcomplete.autocomplete()`` as early as possible in your execution flow.
+
+.. admonition:: Performance
+
+ If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion
+ process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time
+ of the program up to that point (for example, by deferring initialization or importing of large modules until after
+ parsing options).
+
+Specifying completers
+---------------------
+You can specify custom completion functions for your options and arguments. Two styles are supported: callable and
+readline-style. Callable completers are simpler. They are called with the following keyword arguments:
+
+* ``prefix``: The prefix text of the last word before the cursor on the command line.
+ For dynamic completers, this can be used to reduce the work required to generate possible completions.
+* ``action``: The ``argparse.Action`` instance that this completer was called for.
+* ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by.
+* ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by
+ ``ArgumentParser.parse_args()``).
+
+Completers can return their completions as an iterable of strings or a mapping (dict) of strings to their
+descriptions (zsh will display the descriptions as context help alongside completions). An example completer for names
+of environment variables might look like this:
+
+.. code-block:: python
+
+ def EnvironCompleter(**kwargs):
+ return os.environ
+
+To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy
+way to do this at definition time is:
+
+.. code-block:: python
+
+ from argcomplete.completers import EnvironCompleter
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--env-var1").completer = EnvironCompleter
+ parser.add_argument("--env-var2").completer = EnvironCompleter
+ argcomplete.autocomplete(parser)
+
+If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be
+used for completions.
+
+A completer that is initialized with a set of all possible choices of values for its action might look like this:
+
+.. code-block:: python
+
+ class ChoicesCompleter(object):
+ def __init__(self, choices):
+ self.choices = choices
+
+ def __call__(self, **kwargs):
+ return self.choices
+
+The following two ways to specify a static set of choices are equivalent for completion purposes:
+
+.. code-block:: python
+
+ from argcomplete.completers import ChoicesCompleter
+
+ parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss'))
+ parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss'))
+
+Note that if you use the ``choices=<completions>`` option, argparse will show
+all these choices in the ``--help`` output by default. To prevent this, set
+``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL",
+choices=('http', 'https', 'ssh', 'rsync', 'wss'))``).
+
+The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses
+``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an
+organization and complete their names, then prints the member description:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse, requests, pprint
+
+ def github_org_members(prefix, parsed_args, **kwargs):
+ resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization)
+ return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix))
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--organization", help="GitHub organization")
+ parser.add_argument("--member", help="GitHub member").completer = github_org_members
+
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+
+ pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json())
+
+`Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this::
+
+ ./describe_github_user.py --organization heroku --member <TAB>
+
+If you have a useful completer to add to the `completer library
+<https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request!
+
+Readline-style completers
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by
+argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the command
+line. For example, you can use the readline-style completer provided by IPython_ to get introspective completions like
+you would get in the IPython shell:
+
+.. _readline: http://docs.python.org/3/library/readline.html
+.. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects
+.. _IPython: http://ipython.org/
+
+.. code-block:: python
+
+ import IPython
+ parser.add_argument("--python-name").completer = IPython.core.completer.Completer()
+
+``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer.
+
+Printing warnings in completers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's
+appropriate to print information about why completions generation failed. To do this, use ``warn``:
+
+.. code-block:: python
+
+ from argcomplete import warn
+
+ def AwesomeWebServiceCompleter(prefix, **kwargs):
+ if login_failed:
+ warn("Please log in to Awesome Web Service to use autocompletion")
+ return completions
+
+Using a custom completion validator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You
+can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``:
+
+.. code-block:: python
+
+ def my_validator(completion_candidate, current_input):
+ """Complete non-prefix substring matches."""
+ return current_input in completion_candidate
+
+ argcomplete.autocomplete(parser, validator=my_validator)
+
+Global completion
+-----------------
+In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, the shell
+will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running
+completion for, and if it's found, follow the rest of the argcomplete protocol as described above.
+
+Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. If you're using
+multiple Python versions on the same system, the version being used to run the script must have argcomplete installed.
+
+.. admonition:: Bash version compatibility
+
+ When using bash, global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. Since
+ Mac OS ships with an outdated version of Bash (3.2), you can either use zsh or install a newer version of bash using
+ `Homebrew <http://brew.sh/>`_ (``brew install bash`` - you will also need to add ``/usr/local/bin/bash`` to
+ ``/etc/shells``, and run ``chsh`` to change your shell). You can check the version of the running copy of bash with
+ ``echo $BASH_VERSION``.
+
+.. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module,
+ argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the
+ destination code.
+
+If you choose not to use global completion, or ship a completion module that depends on argcomplete, you must register
+your script explicitly using ``eval "$(register-python-argcomplete my-python-app)"``. Standard completion module
+registration rules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab completed
+when invoked exactly as it was registered. In the above example, ``my-python-app`` must be on the path, and the user
+must be attempting to complete it by that name. The above line alone would **not** allow you to complete
+``./my-python-app``, or ``/path/to/my-python-app``.
+
+Activating global completion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The script ``activate-global-python-argcomplete`` installs the global completion script
+`bash_completion.d/_python-argcomplete <https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/_python-argcomplete>`_
+into an appropriate location on your system for both bash and zsh. The specific location depends on your platform and
+whether you installed argcomplete system-wide using ``sudo`` or locally (into your user's home directory).
+
+Zsh Support
+-----------
+Argcomplete supports zsh. On top of plain completions like in bash, zsh allows you to see argparse help strings as
+completion descriptions. All shellcode included with argcomplete is compatible with both bash and zsh, so the same
+completer commands ``activate-global-python-argcomplete`` and ``eval "$(register-python-argcomplete my-python-app)"``
+work for zsh as well.
+
+Python Support
+--------------
+Argcomplete requires Python 3.7+.
+
+Support for other shells
+------------------------
+Argcomplete maintainers provide support only for the bash and zsh shells on Linux and MacOS. For resources related to
+other shells and platforms, including fish, tcsh, xonsh, powershell, and Windows, please see the
+`contrib <https://github.com/kislyuk/argcomplete/tree/develop/contrib>`_ directory.
+
+Common Problems
+---------------
+If global completion is not completing your script, bash may have registered a default completion function::
+
+ $ complete | grep my-python-app
+ complete -F _minimal my-python-app
+
+You can fix this by restarting your shell, or by running ``complete -r my-python-app``.
+
+Debugging
+---------
+Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will
+disrupt the command line composition state of your terminal, but make it possible to see the internal state of the
+completer if it encounters problems.
+
+Acknowledgments
+---------------
+Inspired and informed by the optcomplete_ module by Martin Blais.
+
+.. _optcomplete: http://pypi.python.org/pypi/optcomplete
+
+Links
+-----
+* `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_
+* `Documentation <https://kislyuk.github.io/argcomplete/>`_
+* `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_
+* `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_
+
+Bugs
+~~~~
+Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_.
+
+License
+-------
+Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
+`Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
+files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
+
+.. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg
+ :target: https://github.com/kislyuk/argcomplete/actions
+.. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master
+ :target: https://codecov.io/github/kislyuk/argcomplete?branch=master
+.. image:: https://img.shields.io/pypi/v/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
+.. image:: https://img.shields.io/pypi/l/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
diff --git a/contrib/python/argcomplete/py3/.dist-info/top_level.txt b/contrib/python/argcomplete/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..e60624ffee
--- /dev/null
+++ b/contrib/python/argcomplete/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+argcomplete
diff --git a/contrib/python/argcomplete/py3/Authors.rst b/contrib/python/argcomplete/py3/Authors.rst
new file mode 100644
index 0000000000..3c0c589908
--- /dev/null
+++ b/contrib/python/argcomplete/py3/Authors.rst
@@ -0,0 +1 @@
+Andrey Kislyuk <kislyuk@gmail.com>
diff --git a/contrib/python/argcomplete/py3/LICENSE.rst b/contrib/python/argcomplete/py3/LICENSE.rst
new file mode 100644
index 0000000000..f433b1a53f
--- /dev/null
+++ b/contrib/python/argcomplete/py3/LICENSE.rst
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/contrib/python/argcomplete/py3/NOTICE b/contrib/python/argcomplete/py3/NOTICE
new file mode 100644
index 0000000000..0f13a0709a
--- /dev/null
+++ b/contrib/python/argcomplete/py3/NOTICE
@@ -0,0 +1,4 @@
+argcomplete is a free open source library that integrates Python applications with Bash and Zsh shell completion.
+The argcomplete project is staffed by volunteers. If you are using this library in a for-profit project, please
+contribute to argcomplete development and maintenance using the "Sponsor" button on the argcomplete GitHub project page,
+https://github.com/kislyuk/argcomplete.
diff --git a/contrib/python/argcomplete/py3/README.rst b/contrib/python/argcomplete/py3/README.rst
new file mode 100644
index 0000000000..aaf9e1a122
--- /dev/null
+++ b/contrib/python/argcomplete/py3/README.rst
@@ -0,0 +1,306 @@
+argcomplete - Bash/zsh tab completion for argparse
+==================================================
+*Tab complete all the things!*
+
+Argcomplete provides easy, extensible command line tab completion of arguments for your Python application.
+
+It makes two assumptions:
+
+* You're using bash or zsh as your shell
+* You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options
+
+Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can
+dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over
+the network).
+
+Installation
+------------
+::
+
+ pip install argcomplete
+ activate-global-python-argcomplete
+
+See `Activating global completion`_ below for details about the second step.
+
+Refresh your shell environment (start a new shell).
+
+Synopsis
+--------
+Add the ``PYTHON_ARGCOMPLETE_OK`` marker and a call to ``argcomplete.autocomplete()`` to your Python application as
+follows:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse
+ parser = argparse.ArgumentParser()
+ ...
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+ ...
+
+Register your Python application with your shell's completion framework by running ``register-python-argcomplete``::
+
+ eval "$(register-python-argcomplete my-python-app)"
+
+Quotes are significant; the registration will fail without them. See `Global completion`_ below for a way to enable
+argcomplete generally without registering each application individually.
+
+argcomplete.autocomplete(*parser*)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but
+**before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the
+completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by
+default), and exits. Otherwise, it returns to the caller immediately.
+
+.. admonition:: Side effects
+
+ Argcomplete gets completions by running your program. It intercepts the execution flow at the moment
+ ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit``
+ by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those
+ side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or
+ stderr will be suppressed). For this reason it's best to construct the argument parser and call
+ ``argcomplete.autocomplete()`` as early as possible in your execution flow.
+
+.. admonition:: Performance
+
+ If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion
+ process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time
+ of the program up to that point (for example, by deferring initialization or importing of large modules until after
+ parsing options).
+
+Specifying completers
+---------------------
+You can specify custom completion functions for your options and arguments. Two styles are supported: callable and
+readline-style. Callable completers are simpler. They are called with the following keyword arguments:
+
+* ``prefix``: The prefix text of the last word before the cursor on the command line.
+ For dynamic completers, this can be used to reduce the work required to generate possible completions.
+* ``action``: The ``argparse.Action`` instance that this completer was called for.
+* ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by.
+* ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by
+ ``ArgumentParser.parse_args()``).
+
+Completers can return their completions as an iterable of strings or a mapping (dict) of strings to their
+descriptions (zsh will display the descriptions as context help alongside completions). An example completer for names
+of environment variables might look like this:
+
+.. code-block:: python
+
+ def EnvironCompleter(**kwargs):
+ return os.environ
+
+To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy
+way to do this at definition time is:
+
+.. code-block:: python
+
+ from argcomplete.completers import EnvironCompleter
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--env-var1").completer = EnvironCompleter
+ parser.add_argument("--env-var2").completer = EnvironCompleter
+ argcomplete.autocomplete(parser)
+
+If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be
+used for completions.
+
+A completer that is initialized with a set of all possible choices of values for its action might look like this:
+
+.. code-block:: python
+
+ class ChoicesCompleter(object):
+ def __init__(self, choices):
+ self.choices = choices
+
+ def __call__(self, **kwargs):
+ return self.choices
+
+The following two ways to specify a static set of choices are equivalent for completion purposes:
+
+.. code-block:: python
+
+ from argcomplete.completers import ChoicesCompleter
+
+ parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss'))
+ parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss'))
+
+Note that if you use the ``choices=<completions>`` option, argparse will show
+all these choices in the ``--help`` output by default. To prevent this, set
+``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL",
+choices=('http', 'https', 'ssh', 'rsync', 'wss'))``).
+
+The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses
+``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an
+organization and complete their names, then prints the member description:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # PYTHON_ARGCOMPLETE_OK
+ import argcomplete, argparse, requests, pprint
+
+ def github_org_members(prefix, parsed_args, **kwargs):
+ resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization)
+ return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix))
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--organization", help="GitHub organization")
+ parser.add_argument("--member", help="GitHub member").completer = github_org_members
+
+ argcomplete.autocomplete(parser)
+ args = parser.parse_args()
+
+ pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json())
+
+`Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this::
+
+ ./describe_github_user.py --organization heroku --member <TAB>
+
+If you have a useful completer to add to the `completer library
+<https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request!
+
+Readline-style completers
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by
+argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the command
+line. For example, you can use the readline-style completer provided by IPython_ to get introspective completions like
+you would get in the IPython shell:
+
+.. _readline: http://docs.python.org/3/library/readline.html
+.. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects
+.. _IPython: http://ipython.org/
+
+.. code-block:: python
+
+ import IPython
+ parser.add_argument("--python-name").completer = IPython.core.completer.Completer()
+
+``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer.
+
+Printing warnings in completers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's
+appropriate to print information about why completions generation failed. To do this, use ``warn``:
+
+.. code-block:: python
+
+ from argcomplete import warn
+
+ def AwesomeWebServiceCompleter(prefix, **kwargs):
+ if login_failed:
+ warn("Please log in to Awesome Web Service to use autocompletion")
+ return completions
+
+Using a custom completion validator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You
+can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``:
+
+.. code-block:: python
+
+ def my_validator(completion_candidate, current_input):
+ """Complete non-prefix substring matches."""
+ return current_input in completion_candidate
+
+ argcomplete.autocomplete(parser, validator=my_validator)
+
+Global completion
+-----------------
+In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, the shell
+will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running
+completion for, and if it's found, follow the rest of the argcomplete protocol as described above.
+
+Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. If you're using
+multiple Python versions on the same system, the version being used to run the script must have argcomplete installed.
+
+.. admonition:: Bash version compatibility
+
+ When using bash, global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. Since
+ Mac OS ships with an outdated version of Bash (3.2), you can either use zsh or install a newer version of bash using
+ `Homebrew <http://brew.sh/>`_ (``brew install bash`` - you will also need to add ``/usr/local/bin/bash`` to
+ ``/etc/shells``, and run ``chsh`` to change your shell). You can check the version of the running copy of bash with
+ ``echo $BASH_VERSION``.
+
+.. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module,
+ argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the
+ destination code.
+
+If you choose not to use global completion, or ship a completion module that depends on argcomplete, you must register
+your script explicitly using ``eval "$(register-python-argcomplete my-python-app)"``. Standard completion module
+registration rules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab completed
+when invoked exactly as it was registered. In the above example, ``my-python-app`` must be on the path, and the user
+must be attempting to complete it by that name. The above line alone would **not** allow you to complete
+``./my-python-app``, or ``/path/to/my-python-app``.
+
+Activating global completion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The script ``activate-global-python-argcomplete`` installs the global completion script
+`bash_completion.d/_python-argcomplete <https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/_python-argcomplete>`_
+into an appropriate location on your system for both bash and zsh. The specific location depends on your platform and
+whether you installed argcomplete system-wide using ``sudo`` or locally (into your user's home directory).
+
+Zsh Support
+-----------
+Argcomplete supports zsh. On top of plain completions like in bash, zsh allows you to see argparse help strings as
+completion descriptions. All shellcode included with argcomplete is compatible with both bash and zsh, so the same
+completer commands ``activate-global-python-argcomplete`` and ``eval "$(register-python-argcomplete my-python-app)"``
+work for zsh as well.
+
+Python Support
+--------------
+Argcomplete requires Python 3.7+.
+
+Support for other shells
+------------------------
+Argcomplete maintainers provide support only for the bash and zsh shells on Linux and MacOS. For resources related to
+other shells and platforms, including fish, tcsh, xonsh, powershell, and Windows, please see the
+`contrib <https://github.com/kislyuk/argcomplete/tree/develop/contrib>`_ directory.
+
+Common Problems
+---------------
+If global completion is not completing your script, bash may have registered a default completion function::
+
+ $ complete | grep my-python-app
+ complete -F _minimal my-python-app
+
+You can fix this by restarting your shell, or by running ``complete -r my-python-app``.
+
+Debugging
+---------
+Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will
+disrupt the command line composition state of your terminal, but make it possible to see the internal state of the
+completer if it encounters problems.
+
+Acknowledgments
+---------------
+Inspired and informed by the optcomplete_ module by Martin Blais.
+
+.. _optcomplete: http://pypi.python.org/pypi/optcomplete
+
+Links
+-----
+* `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_
+* `Documentation <https://kislyuk.github.io/argcomplete/>`_
+* `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_
+* `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_
+
+Bugs
+~~~~
+Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_.
+
+License
+-------
+Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
+`Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
+files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
+
+.. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg
+ :target: https://github.com/kislyuk/argcomplete/actions
+.. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master
+ :target: https://codecov.io/github/kislyuk/argcomplete?branch=master
+.. image:: https://img.shields.io/pypi/v/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
+.. image:: https://img.shields.io/pypi/l/argcomplete.svg
+ :target: https://pypi.python.org/pypi/argcomplete
diff --git a/contrib/python/argcomplete/py3/argcomplete/__init__.py b/contrib/python/argcomplete/py3/argcomplete/__init__.py
new file mode 100644
index 0000000000..9bd90f1f56
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors.
+# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
+
+from . import completers
+from .completers import ChoicesCompleter, DirectoriesCompleter, EnvironCompleter, FilesCompleter, SuppressCompleter
+from .exceptions import ArgcompleteException
+from .finders import CompletionFinder, ExclusiveCompletionFinder, safe_actions
+from .io import debug, mute_stderr, warn
+from .lexers import split_line
+from .shell_integration import shellcode
+
+autocomplete = CompletionFinder()
+autocomplete.__doc__ = """ Use this to access argcomplete. See :meth:`argcomplete.CompletionFinder.__call__()`. """
diff --git a/contrib/python/argcomplete/py3/argcomplete/_check_console_script.py b/contrib/python/argcomplete/py3/argcomplete/_check_console_script.py
new file mode 100644
index 0000000000..ed1e3b34f4
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/_check_console_script.py
@@ -0,0 +1,73 @@
+"""
+Utility for locating the module (or package's __init__.py)
+associated with a given console_script name
+and verifying it contains the PYTHON_ARGCOMPLETE_OK marker.
+
+Such scripts are automatically generated and cannot contain
+the marker themselves, so we defer to the containing module or package.
+
+For more information on setuptools console_scripts, see
+https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation
+
+Intended to be invoked by argcomplete's global completion function.
+"""
+import os
+import sys
+
+try:
+ from importlib.metadata import entry_points as importlib_entry_points
+ from importlib.metadata import EntryPoint
+ use_entry_points_backport = False
+except ImportError:
+ from importlib_metadata import entry_points as importlib_entry_points # type:ignore
+ from importlib_metadata import EntryPoint # type:ignore
+ use_entry_points_backport = True
+
+from ._check_module import ArgcompleteMarkerNotFound, find
+from typing import Iterable
+
+
+def main():
+ # Argument is the full path to the console script.
+ script_path = sys.argv[1]
+
+ # Find the module and function names that correspond to this
+ # assuming it is actually a console script.
+ name = os.path.basename(script_path)
+
+ entry_points : Iterable[EntryPoint] = importlib_entry_points() # type:ignore
+
+ # The importlib_metadata backport returns a tuple of entry point objects
+ # whereas the official library returns a SelectableGroups object
+ # Python 3.12+ behaves like the importlib_metadata backport
+ if not use_entry_points_backport and sys.version_info < (3, 12):
+ entry_points = entry_points["console_scripts"] # type:ignore
+
+ entry_points = [ep for ep in entry_points \
+ if ep.name == name and ep.group == "console_scripts" ] # type:ignore
+
+ if not entry_points:
+ raise ArgcompleteMarkerNotFound("no entry point found matching script")
+ entry_point = entry_points[0]
+ module_name, function_name = entry_point.value.split(":", 1)
+
+ # Check this looks like the script we really expected.
+ with open(script_path) as f:
+ script = f.read()
+ if "from {} import {}".format(module_name, function_name) not in script:
+ raise ArgcompleteMarkerNotFound("does not appear to be a console script")
+ if "sys.exit({}())".format(function_name) not in script:
+ raise ArgcompleteMarkerNotFound("does not appear to be a console script")
+
+ # Look for the argcomplete marker in the script it imports.
+ with open(find(module_name, return_package=True)) as f:
+ head = f.read(1024)
+ if "PYTHON_ARGCOMPLETE_OK" not in head:
+ raise ArgcompleteMarkerNotFound("marker not found")
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except ArgcompleteMarkerNotFound as e:
+ sys.exit(str(e))
diff --git a/contrib/python/argcomplete/py3/argcomplete/_check_module.py b/contrib/python/argcomplete/py3/argcomplete/_check_module.py
new file mode 100644
index 0000000000..4958f0267f
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/_check_module.py
@@ -0,0 +1,89 @@
+"""
+Utility for locating a module (or package's __main__.py) with a given name
+and verifying it contains the PYTHON_ARGCOMPLETE_OK marker.
+
+The module name should be specified in a form usable with `python -m`.
+
+Intended to be invoked by argcomplete's global completion function.
+"""
+import os
+import sys
+import tokenize
+
+try:
+ from importlib.util import find_spec # type:ignore
+except ImportError:
+ import typing as t
+ from collections import namedtuple
+ from imp import find_module
+
+ ModuleSpec = namedtuple("ModuleSpec", ["origin", "has_location", "submodule_search_locations"])
+
+ def find_spec( # type:ignore
+ name: str,
+ package: t.Optional[str] = None,
+ ) -> t.Optional[ModuleSpec]:
+ """Minimal implementation as required by `find`."""
+ try:
+ f, path, _ = find_module(name)
+ except ImportError:
+ return None
+ has_location = path is not None
+ if f is None:
+ return ModuleSpec(None, has_location, [path])
+ f.close()
+ return ModuleSpec(path, has_location, None)
+
+
+class ArgcompleteMarkerNotFound(RuntimeError):
+ pass
+
+
+def find(name, return_package=False):
+ names = name.split(".")
+ spec = find_spec(names[0])
+ if spec is None:
+ raise ArgcompleteMarkerNotFound('no module named "{}"'.format(names[0]))
+ if not spec.has_location:
+ raise ArgcompleteMarkerNotFound("cannot locate file")
+ if spec.submodule_search_locations is None:
+ if len(names) != 1:
+ raise ArgcompleteMarkerNotFound("{} is not a package".format(names[0]))
+ return spec.origin
+ if len(spec.submodule_search_locations) != 1:
+ raise ArgcompleteMarkerNotFound("expecting one search location")
+ path = os.path.join(spec.submodule_search_locations[0], *names[1:])
+ if os.path.isdir(path):
+ filename = "__main__.py"
+ if return_package:
+ filename = "__init__.py"
+ return os.path.join(path, filename)
+ else:
+ return path + ".py"
+
+
+def main():
+ try:
+ name = sys.argv[1]
+ except IndexError:
+ raise ArgcompleteMarkerNotFound("missing argument on the command line")
+
+ filename = find(name)
+
+ try:
+ fp = tokenize.open(filename)
+ except OSError:
+ raise ArgcompleteMarkerNotFound("cannot open file")
+
+ with fp:
+ head = fp.read(1024)
+
+ if "PYTHON_ARGCOMPLETE_OK" not in head:
+ raise ArgcompleteMarkerNotFound("marker not found")
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except ArgcompleteMarkerNotFound as e:
+ sys.exit(str(e))
diff --git a/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete b/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete
new file mode 100644
index 0000000000..4c7edda3c3
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete
@@ -0,0 +1,236 @@
+#compdef -P *
+
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors.
+# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
+
+# Note: both the leading underscore in the name of this file and the first line (compdef) are required by zsh
+
+# Copy of __expand_tilde_by_ref from bash-completion
+# ZSH implementation added
+__python_argcomplete_expand_tilde_by_ref () {
+ if [ -n "${ZSH_VERSION-}" ]; then
+ if [ "${(P)1[1]}" = "~" ]; then
+ eval $1="${(P)1/#\~/$HOME}";
+ fi
+ else
+ if [ "${!1:0:1}" = "~" ]; then
+ if [ "${!1}" != "${!1//\/}" ]; then
+ eval $1="${!1/%\/*}"/'${!1#*/}';
+ else
+ eval $1="${!1}";
+ fi;
+ fi
+ fi
+}
+
+# Run something, muting output or redirecting it to the debug stream
+# depending on the value of _ARC_DEBUG.
+# If ARGCOMPLETE_USE_TEMPFILES is set, use tempfiles for IPC.
+__python_argcomplete_run() {
+ if [[ -z "${ARGCOMPLETE_USE_TEMPFILES-}" ]]; then
+ __python_argcomplete_run_inner "$@"
+ return
+ fi
+ local tmpfile="$(mktemp)"
+ _ARGCOMPLETE_STDOUT_FILENAME="$tmpfile" __python_argcomplete_run_inner "$@"
+ local code=$?
+ cat "$tmpfile"
+ rm "$tmpfile"
+ return $code
+}
+
+__python_argcomplete_run_inner() {
+ if [[ -z "${_ARC_DEBUG-}" ]]; then
+ "$@" 8>&1 9>&2 1>/dev/null 2>&1
+ else
+ "$@" 8>&1 9>&2 1>&9 2>&1
+ fi
+}
+
+__python_argcomplete_upshift_bash_rematch() {
+ if [[ -z "${ZSH_VERSION-}" ]]; then
+ _BASH_REMATCH=( "" "${BASH_REMATCH[@]}" )
+ else
+ _BASH_REMATCH=( "${BASH_REMATCH[@]}" )
+ fi
+}
+
+# This function scans the beginning of an executable file provided as the first
+# argument ($1) for certain indicators, specified by the second argument ($2),
+# or the "target". There are three possible targets: "interpreter",
+# "magic_string", and "easy_install". If the target is "interpreter", the
+# function matches the interpreter line, alongside any optional interpreter
+# arguments. If the target is "magic_string", a match is attempted for the
+# "PYTHON_ARGCOMPLETE_OK" magic string, indicating that the file should be run
+# to get completions. If the target is "easy_install", the function matches either
+# "PBR Generated" or any of the "EASY-INSTALL" scripts (either SCRIPT,
+# ENTRY-SCRIPT, or DEV-SCRIPT). In all cases, only the first kilobyte of
+# the file is searched. The regex matches are returned in BASH_REMATCH,
+# indexed starting at 1, regardless of the shell in use.
+__python_argcomplete_scan_head() {
+ local file="$1"
+ local target="$2"
+
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ read -r -k 1024 -u 0 < "$file";
+ else
+ read -r -N 1024 < "$file"
+ fi
+
+ # Since ZSH does not support a -n option, we
+ # trim all characters after the first line in both shells
+ if [[ "$target" = "interpreter" ]]; then
+ read -r <<< "$REPLY"
+ fi
+
+ local regex
+
+ case "$target" in
+ magic_string) regex='PYTHON_ARGCOMPLETE_OK' ;;
+ easy_install) regex="(PBR Generated)|(EASY-INSTALL-(SCRIPT|ENTRY-SCRIPT|DEV-SCRIPT))" ;;
+ asdf) regex="asdf exec " ;;
+ interpreter) regex='^#!(.*)$' ;;
+ esac
+
+ local ret=""
+ if [[ "$REPLY" =~ $regex ]]; then
+ ret=1
+ fi
+
+ __python_argcomplete_upshift_bash_rematch
+
+ [[ -n $ret ]]
+}
+
+__python_argcomplete_scan_head_noerr() {
+ __python_argcomplete_scan_head "$@" 2>/dev/null
+}
+
+__python_argcomplete_which() {
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ whence -p "$@"
+ else
+ type -P "$@"
+ fi
+}
+
+_python_argcomplete_global() {
+
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ # Store result of a regex match in the
+ # BASH_REMATCH variable rather than MATCH
+ setopt local_options BASH_REMATCH
+ fi
+
+ # 1-based version of BASH_REMATCH. Modifying BASH_REMATCH
+ # directly causes older versions of Bash to exit
+ local _BASH_REMATCH="";
+
+ local executable=""
+
+ # req_argv contains the arguments to the completion
+ # indexed from 1 (regardless of the shell.) In Bash,
+ # the zeroth index is empty
+ local req_argv=()
+
+ if [[ -z "${ZSH_VERSION-}" ]]; then
+ executable=$1
+ req_argv=( "" "${COMP_WORDS[@]:1}" )
+ __python_argcomplete_expand_tilde_by_ref executable
+ else
+ # TODO: check if we should call _default or use a different condition here
+ if [[ "$service" != "-default-" ]]; then
+ return
+ fi
+ executable="${words[1]}"
+ req_argv=( "${words[@]:1}" )
+ fi
+
+ local ARGCOMPLETE=0
+ if [[ "$executable" == python* ]] || [[ "$executable" == pypy* ]]; then
+ if [[ "${req_argv[1]}" == -m ]]; then
+ if __python_argcomplete_run "$executable" -m argcomplete._check_module "${req_argv[2]}"; then
+ ARGCOMPLETE=3
+ else
+ return
+ fi
+ fi
+ if [[ $ARGCOMPLETE == 0 ]]; then
+ local potential_path="${req_argv[1]}"
+ __python_argcomplete_expand_tilde_by_ref potential_path
+ if [[ -f "$potential_path" ]] && __python_argcomplete_scan_head_noerr "$potential_path" magic_string; then
+ req_argv[1]="$potential_path" # not expanded in __python_argcomplete_run
+ ARGCOMPLETE=2
+ else
+ return
+ fi
+ fi
+ elif __python_argcomplete_which "$executable" >/dev/null 2>&1; then
+ local SCRIPT_NAME=$(__python_argcomplete_which "$executable")
+ __python_argcomplete_scan_head_noerr "$SCRIPT_NAME" interpreter
+ if (__python_argcomplete_which pyenv && [[ "$SCRIPT_NAME" = $(pyenv root)/shims/* ]]) >/dev/null 2>&1; then
+ local SCRIPT_NAME=$(pyenv which "$executable")
+ fi
+ if (__python_argcomplete_which asdf && __python_argcomplete_scan_head_noerr "$SCRIPT_NAME" asdf) >/dev/null 2>&1; then
+ local SCRIPT_NAME=$(asdf which "$executable")
+ fi
+ if __python_argcomplete_scan_head_noerr "$SCRIPT_NAME" magic_string; then
+ ARGCOMPLETE=1
+ elif __python_argcomplete_scan_head_noerr "$SCRIPT_NAME" interpreter; then
+ __python_argcomplete_upshift_bash_rematch
+ local interpreter="${_BASH_REMATCH[2]}"
+
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ interpreter=($=interpreter)
+ else
+ interpreter=($interpreter)
+ fi
+
+ if (__python_argcomplete_scan_head_noerr "$SCRIPT_NAME" easy_install \
+ && "${interpreter[@]}" "$(__python_argcomplete_which python-argcomplete-check-easy-install-script)" "$SCRIPT_NAME") >/dev/null 2>&1; then
+ ARGCOMPLETE=1
+ elif __python_argcomplete_run "${interpreter[@]}" -m argcomplete._check_console_script "$SCRIPT_NAME"; then
+ ARGCOMPLETE=1
+ fi
+ fi
+ fi
+
+ if [[ $ARGCOMPLETE != 0 ]]; then
+ local IFS=$'\013'
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ local completions
+ completions=($(IFS="$IFS" \
+ COMP_LINE="$BUFFER" \
+ COMP_POINT="$CURSOR" \
+ _ARGCOMPLETE=$ARGCOMPLETE \
+ _ARGCOMPLETE_SHELL="zsh" \
+ _ARGCOMPLETE_SUPPRESS_SPACE=1 \
+ __python_argcomplete_run "$executable" "${(@)req_argv[1, ${ARGCOMPLETE}-1]}"))
+ _describe "$executable" completions
+ else
+ COMPREPLY=($(IFS="$IFS" \
+ COMP_LINE="$COMP_LINE" \
+ COMP_POINT="$COMP_POINT" \
+ COMP_TYPE="$COMP_TYPE" \
+ _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
+ _ARGCOMPLETE=$ARGCOMPLETE \
+ _ARGCOMPLETE_SHELL="bash" \
+ _ARGCOMPLETE_SUPPRESS_SPACE=1 \
+ __python_argcomplete_run "$executable" "${req_argv[@]:1:${ARGCOMPLETE}-1}"))
+ if [[ $? != 0 ]]; then
+ unset COMPREPLY
+ elif [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
+ compopt -o nospace
+ fi
+ fi
+ elif [[ -n "${ZSH_VERSION-}" ]]; then
+ _default
+ else
+ type -t _completion_loader | grep -q 'function' && _completion_loader "$@"
+ fi
+}
+if [[ -z "${ZSH_VERSION-}" ]]; then
+ complete -o default -o bashdefault -D -F _python_argcomplete_global
+else
+ compdef _python_argcomplete_global -P '*'
+fi
diff --git a/contrib/python/argcomplete/py3/argcomplete/completers.py b/contrib/python/argcomplete/py3/argcomplete/completers.py
new file mode 100644
index 0000000000..d89cdbfbc3
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/completers.py
@@ -0,0 +1,124 @@
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors.
+# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
+
+import argparse
+import os
+import subprocess
+
+
+def _call(*args, **kwargs):
+ # TODO: replace "universal_newlines" with "text" once 3.6 support is dropped
+ kwargs["universal_newlines"] = True
+ try:
+ return subprocess.check_output(*args, **kwargs).splitlines()
+ except subprocess.CalledProcessError:
+ return []
+
+
+class BaseCompleter:
+ """
+ This is the base class that all argcomplete completers should subclass.
+ """
+
+ def __call__(
+ self, *, prefix: str, action: argparse.Action, parser: argparse.ArgumentParser, parsed_args: argparse.Namespace
+ ):
+ raise NotImplementedError("This method should be implemented by a subclass.")
+
+
+class ChoicesCompleter(BaseCompleter):
+ def __init__(self, choices):
+ self.choices = choices
+
+ def _convert(self, choice):
+ if not isinstance(choice, str):
+ choice = str(choice)
+ return choice
+
+ def __call__(self, **kwargs):
+ return (self._convert(c) for c in self.choices)
+
+
+EnvironCompleter = ChoicesCompleter(os.environ)
+
+
+class FilesCompleter(BaseCompleter):
+ """
+ File completer class, optionally takes a list of allowed extensions
+ """
+
+ def __init__(self, allowednames=(), directories=True):
+ # Fix if someone passes in a string instead of a list
+ if isinstance(allowednames, (str, bytes)):
+ allowednames = [allowednames]
+
+ self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames]
+ self.directories = directories
+
+ def __call__(self, prefix, **kwargs):
+ completion = []
+ if self.allowednames:
+ if self.directories:
+ files = _call(["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)])
+ completion += [f + "/" for f in files]
+ for x in self.allowednames:
+ completion += _call(["bash", "-c", "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix)])
+ else:
+ completion += _call(["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)])
+ anticomp = _call(["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)])
+ completion = list(set(completion) - set(anticomp))
+
+ if self.directories:
+ completion += [f + "/" for f in anticomp]
+ return completion
+
+
+class _FilteredFilesCompleter(BaseCompleter):
+ def __init__(self, predicate):
+ """
+ Create the completer
+
+ A predicate accepts as its only argument a candidate path and either
+ accepts it or rejects it.
+ """
+ assert predicate, "Expected a callable predicate"
+ self.predicate = predicate
+
+ def __call__(self, prefix, **kwargs):
+ """
+ Provide completions on prefix
+ """
+ target_dir = os.path.dirname(prefix)
+ try:
+ names = os.listdir(target_dir or ".")
+ except Exception:
+ return # empty iterator
+ incomplete_part = os.path.basename(prefix)
+ # Iterate on target_dir entries and filter on given predicate
+ for name in names:
+ if not name.startswith(incomplete_part):
+ continue
+ candidate = os.path.join(target_dir, name)
+ if not self.predicate(candidate):
+ continue
+ yield candidate + "/" if os.path.isdir(candidate) else candidate
+
+
+class DirectoriesCompleter(_FilteredFilesCompleter):
+ def __init__(self):
+ _FilteredFilesCompleter.__init__(self, predicate=os.path.isdir)
+
+
+class SuppressCompleter(BaseCompleter):
+ """
+ A completer used to suppress the completion of specific arguments
+ """
+
+ def __init__(self):
+ pass
+
+ def suppress(self):
+ """
+ Decide if the completion should be suppressed
+ """
+ return True
diff --git a/contrib/python/argcomplete/py3/argcomplete/exceptions.py b/contrib/python/argcomplete/py3/argcomplete/exceptions.py
new file mode 100644
index 0000000000..077b1850d2
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/exceptions.py
@@ -0,0 +1,2 @@
+class ArgcompleteException(Exception):
+ "Exception raised when the shell argument completion process fails."
diff --git a/contrib/python/argcomplete/py3/argcomplete/finders.py b/contrib/python/argcomplete/py3/argcomplete/finders.py
new file mode 100644
index 0000000000..bf8e0e25f8
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/finders.py
@@ -0,0 +1,590 @@
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
+# `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
+# files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
+# See https://github.com/kislyuk/argcomplete for more info.
+
+import argparse
+import os
+import sys
+from collections.abc import Mapping
+from typing import Callable, Dict, List, Optional, Sequence, Union
+
+from . import io as _io
+from .completers import ChoicesCompleter, FilesCompleter, SuppressCompleter
+from .io import debug, mute_stderr
+from .lexers import split_line
+from .packages._argparse import IntrospectiveArgumentParser, action_is_greedy, action_is_open, action_is_satisfied
+
+safe_actions = {
+ argparse._StoreAction,
+ argparse._StoreConstAction,
+ argparse._StoreTrueAction,
+ argparse._StoreFalseAction,
+ argparse._AppendAction,
+ argparse._AppendConstAction,
+ argparse._CountAction,
+}
+
+
+def default_validator(completion, prefix):
+ return completion.startswith(prefix)
+
+
+class CompletionFinder(object):
+ """
+ Inherit from this class if you wish to override any of the stages below. Otherwise, use
+ ``argcomplete.autocomplete()`` directly (it's a convenience instance of this class). It has the same signature as
+ :meth:`CompletionFinder.__call__()`.
+ """
+
+ def __init__(
+ self,
+ argument_parser=None,
+ always_complete_options=True,
+ exclude=None,
+ validator=None,
+ print_suppressed=False,
+ default_completer=FilesCompleter(),
+ append_space=None,
+ ):
+ self._parser = argument_parser
+ self.always_complete_options = always_complete_options
+ self.exclude = exclude
+ if validator is None:
+ validator = default_validator
+ self.validator = validator
+ self.print_suppressed = print_suppressed
+ self.completing = False
+ self._display_completions: Dict[str, str] = {}
+ self.default_completer = default_completer
+ if append_space is None:
+ append_space = os.environ.get("_ARGCOMPLETE_SUPPRESS_SPACE") != "1"
+ self.append_space = append_space
+
+ def __call__(
+ self,
+ argument_parser: argparse.ArgumentParser,
+ always_complete_options: Union[bool, str] = True,
+ exit_method: Callable = os._exit,
+ output_stream=None,
+ exclude: Optional[Sequence[str]] = None,
+ validator: Optional[Callable] = None,
+ print_suppressed: bool = False,
+ append_space: Optional[bool] = None,
+ default_completer=FilesCompleter(),
+ ):
+ """
+ :param argument_parser: The argument parser to autocomplete on
+ :param always_complete_options:
+ Controls the autocompletion of option strings if an option string opening character (normally ``-``) has not
+ been entered. If ``True`` (default), both short (``-x``) and long (``--x``) option strings will be
+ suggested. If ``False``, no option strings will be suggested. If ``long``, long options and short options
+ with no long variant will be suggested. If ``short``, short options and long options with no short variant
+ will be suggested.
+ :param exit_method:
+ Method used to stop the program after printing completions. Defaults to :meth:`os._exit`. If you want to
+ perform a normal exit that calls exit handlers, use :meth:`sys.exit`.
+ :param exclude: List of strings representing options to be omitted from autocompletion
+ :param validator:
+ Function to filter all completions through before returning (called with two string arguments, completion
+ and prefix; return value is evaluated as a boolean)
+ :param print_suppressed:
+ Whether or not to autocomplete options that have the ``help=argparse.SUPPRESS`` keyword argument set.
+ :param append_space:
+ Whether to append a space to unique matches. The default is ``True``.
+
+ .. note::
+ If you are not subclassing CompletionFinder to override its behaviors,
+ use :meth:`argcomplete.autocomplete()` directly. It has the same signature as this method.
+
+ Produces tab completions for ``argument_parser``. See module docs for more info.
+
+ Argcomplete only executes actions if their class is known not to have side effects. Custom action classes can be
+ added to argcomplete.safe_actions, if their values are wanted in the ``parsed_args`` completer argument, or
+ their execution is otherwise desirable.
+ """
+ self.__init__( # type: ignore
+ argument_parser,
+ always_complete_options=always_complete_options,
+ exclude=exclude,
+ validator=validator,
+ print_suppressed=print_suppressed,
+ append_space=append_space,
+ default_completer=default_completer,
+ )
+
+ if "_ARGCOMPLETE" not in os.environ:
+ # not an argument completion invocation
+ return
+
+ try:
+ _io.debug_stream = os.fdopen(9, "w")
+ except Exception:
+ _io.debug_stream = sys.stderr
+ debug()
+
+ if output_stream is None:
+ filename = os.environ.get("_ARGCOMPLETE_STDOUT_FILENAME")
+ if filename is not None:
+ debug("Using output file {}".format(filename))
+ output_stream = open(filename, "w")
+
+ if output_stream is None:
+ try:
+ output_stream = os.fdopen(8, "w")
+ except Exception:
+ debug("Unable to open fd 8 for writing, quitting")
+ exit_method(1)
+
+ ifs = os.environ.get("_ARGCOMPLETE_IFS", "\013")
+ if len(ifs) != 1:
+ debug("Invalid value for IFS, quitting [{v}]".format(v=ifs))
+ exit_method(1)
+
+ dfs = os.environ.get("_ARGCOMPLETE_DFS")
+ if dfs and len(dfs) != 1:
+ debug("Invalid value for DFS, quitting [{v}]".format(v=dfs))
+ exit_method(1)
+
+ comp_line = os.environ["COMP_LINE"]
+ comp_point = int(os.environ["COMP_POINT"])
+
+ cword_prequote, cword_prefix, cword_suffix, comp_words, last_wordbreak_pos = split_line(comp_line, comp_point)
+
+ # _ARGCOMPLETE is set by the shell script to tell us where comp_words
+ # should start, based on what we're completing.
+ # 1: <script> [args]
+ # 2: python <script> [args]
+ # 3: python -m <module> [args]
+ start = int(os.environ["_ARGCOMPLETE"]) - 1
+ comp_words = comp_words[start:]
+
+ if cword_prefix and cword_prefix[0] in self._parser.prefix_chars and "=" in cword_prefix:
+ # Special case for when the current word is "--optional=PARTIAL_VALUE". Give the optional to the parser.
+ comp_words.append(cword_prefix.split("=", 1)[0])
+
+ debug(
+ "\nLINE: {!r}".format(comp_line),
+ "\nPOINT: {!r}".format(comp_point),
+ "\nPREQUOTE: {!r}".format(cword_prequote),
+ "\nPREFIX: {!r}".format(cword_prefix),
+ "\nSUFFIX: {!r}".format(cword_suffix),
+ "\nWORDS:",
+ comp_words,
+ )
+
+ completions = self._get_completions(comp_words, cword_prefix, cword_prequote, last_wordbreak_pos)
+
+ if dfs:
+ display_completions = {
+ key: value.replace(ifs, " ") if value else "" for key, value in self._display_completions.items()
+ }
+ completions = [dfs.join((key, display_completions.get(key) or "")) for key in completions]
+
+ if os.environ.get("_ARGCOMPLETE_SHELL") == "zsh":
+ completions = [f"{c}:{self._display_completions.get(c)}" for c in completions]
+
+ debug("\nReturning completions:", completions)
+ output_stream.write(ifs.join(completions))
+ output_stream.flush()
+ _io.debug_stream.flush()
+ exit_method(0)
+
+ def _get_completions(self, comp_words, cword_prefix, cword_prequote, last_wordbreak_pos):
+ active_parsers = self._patch_argument_parser()
+
+ parsed_args = argparse.Namespace()
+ self.completing = True
+
+ try:
+ debug("invoking parser with", comp_words[1:])
+ with mute_stderr():
+ a = self._parser.parse_known_args(comp_words[1:], namespace=parsed_args)
+ debug("parsed args:", a)
+ except BaseException as e:
+ debug("\nexception", type(e), str(e), "while parsing args")
+
+ self.completing = False
+
+ if "--" in comp_words:
+ self.always_complete_options = False
+
+ completions = self.collect_completions(active_parsers, parsed_args, cword_prefix)
+ completions = self.filter_completions(completions)
+ completions = self.quote_completions(completions, cword_prequote, last_wordbreak_pos)
+ return completions
+
+ def _patch_argument_parser(self):
+ """
+ Since argparse doesn't support much introspection, we monkey-patch it to replace the parse_known_args method and
+ all actions with hooks that tell us which action was last taken or about to be taken, and let us have the parser
+ figure out which subparsers need to be activated (then recursively monkey-patch those).
+ We save all active ArgumentParsers to extract all their possible option names later.
+ """
+ self.active_parsers: List[argparse.ArgumentParser] = []
+ self.visited_positionals: List[argparse.Action] = []
+
+ completer = self
+
+ def patch(parser):
+ completer.visited_positionals.append(parser)
+ completer.active_parsers.append(parser)
+
+ if isinstance(parser, IntrospectiveArgumentParser):
+ return
+
+ classname = "MonkeyPatchedIntrospectiveArgumentParser"
+
+ parser.__class__ = type(classname, (IntrospectiveArgumentParser, parser.__class__), {})
+
+ for action in parser._actions:
+ if hasattr(action, "_orig_class"):
+ continue
+
+ # TODO: accomplish this with super
+ class IntrospectAction(action.__class__): # type: ignore
+ def __call__(self, parser, namespace, values, option_string=None):
+ debug("Action stub called on", self)
+ debug("\targs:", parser, namespace, values, option_string)
+ debug("\torig class:", self._orig_class)
+ debug("\torig callable:", self._orig_callable)
+
+ if not completer.completing:
+ self._orig_callable(parser, namespace, values, option_string=option_string)
+ elif issubclass(self._orig_class, argparse._SubParsersAction):
+ debug("orig class is a subparsers action: patching and running it")
+ patch(self._name_parser_map[values[0]])
+ self._orig_callable(parser, namespace, values, option_string=option_string)
+ elif self._orig_class in safe_actions:
+ if not self.option_strings:
+ completer.visited_positionals.append(self)
+
+ self._orig_callable(parser, namespace, values, option_string=option_string)
+
+ action._orig_class = action.__class__
+ action._orig_callable = action.__call__
+ action.__class__ = IntrospectAction
+
+ patch(self._parser)
+
+ debug("Active parsers:", self.active_parsers)
+ debug("Visited positionals:", self.visited_positionals)
+
+ return self.active_parsers
+
+ def _get_subparser_completions(self, parser, cword_prefix):
+ aliases_by_parser: Dict[argparse.ArgumentParser, List[str]] = {}
+ for key in parser.choices.keys():
+ p = parser.choices[key]
+ aliases_by_parser.setdefault(p, []).append(key)
+
+ for action in parser._get_subactions():
+ for alias in aliases_by_parser[parser.choices[action.dest]]:
+ if alias.startswith(cword_prefix):
+ self._display_completions[alias] = action.help or ""
+
+ completions = [subcmd for subcmd in parser.choices.keys() if subcmd.startswith(cword_prefix)]
+ return completions
+
+ def _include_options(self, action, cword_prefix):
+ if len(cword_prefix) > 0 or self.always_complete_options is True:
+ return [opt for opt in action.option_strings if opt.startswith(cword_prefix)]
+ long_opts = [opt for opt in action.option_strings if len(opt) > 2]
+ short_opts = [opt for opt in action.option_strings if len(opt) <= 2]
+ if self.always_complete_options == "long":
+ return long_opts if long_opts else short_opts
+ elif self.always_complete_options == "short":
+ return short_opts if short_opts else long_opts
+ return []
+
+ def _get_option_completions(self, parser, cword_prefix):
+ for action in parser._actions:
+ if action.option_strings:
+ for option_string in action.option_strings:
+ if option_string.startswith(cword_prefix):
+ self._display_completions[option_string] = action.help or ""
+
+ option_completions = []
+ for action in parser._actions:
+ if not self.print_suppressed:
+ completer = getattr(action, "completer", None)
+ if isinstance(completer, SuppressCompleter) and completer.suppress():
+ continue
+ if action.help == argparse.SUPPRESS:
+ continue
+ if not self._action_allowed(action, parser):
+ continue
+ if not isinstance(action, argparse._SubParsersAction):
+ option_completions += self._include_options(action, cword_prefix)
+ return option_completions
+
+ @staticmethod
+ def _action_allowed(action, parser):
+ # Logic adapted from take_action in ArgumentParser._parse_known_args
+ # (members are saved by vendor._argparse.IntrospectiveArgumentParser)
+ for conflict_action in parser._action_conflicts.get(action, []):
+ if conflict_action in parser._seen_non_default_actions:
+ return False
+ return True
+
+ def _complete_active_option(self, parser, next_positional, cword_prefix, parsed_args, completions):
+ debug("Active actions (L={l}): {a}".format(l=len(parser.active_actions), a=parser.active_actions))
+
+ isoptional = cword_prefix and cword_prefix[0] in parser.prefix_chars
+ optional_prefix = ""
+ greedy_actions = [x for x in parser.active_actions if action_is_greedy(x, isoptional)]
+ if greedy_actions:
+ assert len(greedy_actions) == 1, "expect at most 1 greedy action"
+ # This means the action will fail to parse if the word under the cursor is not given
+ # to it, so give it exclusive control over completions (flush previous completions)
+ debug("Resetting completions because", greedy_actions[0], "must consume the next argument")
+ self._display_completions = {}
+ completions = []
+ elif isoptional:
+ if "=" in cword_prefix:
+ # Special case for when the current word is "--optional=PARTIAL_VALUE".
+ # The completer runs on PARTIAL_VALUE. The prefix is added back to the completions
+ # (and chopped back off later in quote_completions() by the COMP_WORDBREAKS logic).
+ optional_prefix, _, cword_prefix = cword_prefix.partition("=")
+ else:
+ # Only run completers if current word does not start with - (is not an optional)
+ return completions
+
+ complete_remaining_positionals = False
+ # Use the single greedy action (if there is one) or all active actions.
+ for active_action in greedy_actions or parser.active_actions:
+ if not active_action.option_strings: # action is a positional
+ if action_is_open(active_action):
+ # Any positional arguments after this may slide down into this action
+ # if more arguments are added (since the user may not be done yet),
+ # so it is extremely difficult to tell which completers to run.
+ # Running all remaining completers will probably show more than the user wants
+ # but it also guarantees we won't miss anything.
+ complete_remaining_positionals = True
+ if not complete_remaining_positionals:
+ if action_is_satisfied(active_action) and not action_is_open(active_action):
+ debug("Skipping", active_action)
+ continue
+
+ debug("Activating completion for", active_action, active_action._orig_class)
+ # completer = getattr(active_action, "completer", DefaultCompleter())
+ completer = getattr(active_action, "completer", None)
+
+ if completer is None:
+ if active_action.choices is not None and not isinstance(active_action, argparse._SubParsersAction):
+ completer = ChoicesCompleter(active_action.choices)
+ elif not isinstance(active_action, argparse._SubParsersAction):
+ completer = self.default_completer
+
+ if completer:
+ if isinstance(completer, SuppressCompleter) and completer.suppress():
+ continue
+
+ if callable(completer):
+ completer_output = completer(
+ prefix=cword_prefix, action=active_action, parser=parser, parsed_args=parsed_args
+ )
+ if isinstance(completer_output, Mapping):
+ for completion, description in completer_output.items():
+ if self.validator(completion, cword_prefix):
+ completions.append(completion)
+ self._display_completions[completion] = description
+ else:
+ for completion in completer_output:
+ if self.validator(completion, cword_prefix):
+ completions.append(completion)
+ if isinstance(completer, ChoicesCompleter):
+ self._display_completions[completion] = active_action.help or ""
+ else:
+ self._display_completions[completion] = ""
+ else:
+ debug("Completer is not callable, trying the readline completer protocol instead")
+ for i in range(9999):
+ next_completion = completer.complete(cword_prefix, i) # type: ignore
+ if next_completion is None:
+ break
+ if self.validator(next_completion, cword_prefix):
+ self._display_completions[next_completion] = ""
+ completions.append(next_completion)
+ if optional_prefix:
+ completions = [optional_prefix + "=" + completion for completion in completions]
+ debug("Completions:", completions)
+ return completions
+
+ def collect_completions(
+ self, active_parsers: List[argparse.ArgumentParser], parsed_args: argparse.Namespace, cword_prefix: str
+ ) -> List[str]:
+ """
+ Visits the active parsers and their actions, executes their completers or introspects them to collect their
+ option strings. Returns the resulting completions as a list of strings.
+
+ This method is exposed for overriding in subclasses; there is no need to use it directly.
+ """
+ completions: List[str] = []
+
+ debug("all active parsers:", active_parsers)
+ active_parser = active_parsers[-1]
+ debug("active_parser:", active_parser)
+ if self.always_complete_options or (len(cword_prefix) > 0 and cword_prefix[0] in active_parser.prefix_chars):
+ completions += self._get_option_completions(active_parser, cword_prefix)
+ debug("optional options:", completions)
+
+ next_positional = self._get_next_positional()
+ debug("next_positional:", next_positional)
+
+ if isinstance(next_positional, argparse._SubParsersAction):
+ completions += self._get_subparser_completions(next_positional, cword_prefix)
+
+ completions = self._complete_active_option(
+ active_parser, next_positional, cword_prefix, parsed_args, completions
+ )
+ debug("active options:", completions)
+ debug("display completions:", self._display_completions)
+
+ return completions
+
+ def _get_next_positional(self):
+ """
+ Get the next positional action if it exists.
+ """
+ active_parser = self.active_parsers[-1]
+ last_positional = self.visited_positionals[-1]
+
+ all_positionals = active_parser._get_positional_actions()
+ if not all_positionals:
+ return None
+
+ if active_parser == last_positional:
+ return all_positionals[0]
+
+ i = 0
+ for i in range(len(all_positionals)):
+ if all_positionals[i] == last_positional:
+ break
+
+ if i + 1 < len(all_positionals):
+ return all_positionals[i + 1]
+
+ return None
+
+ def filter_completions(self, completions: List[str]) -> List[str]:
+ """
+ De-duplicates completions and excludes those specified by ``exclude``.
+ Returns the filtered completions as a list.
+
+ This method is exposed for overriding in subclasses; there is no need to use it directly.
+ """
+ filtered_completions = []
+ for completion in completions:
+ if self.exclude is not None:
+ if completion in self.exclude:
+ continue
+ if completion not in filtered_completions:
+ filtered_completions.append(completion)
+ return filtered_completions
+
+ def quote_completions(
+ self, completions: List[str], cword_prequote: str, last_wordbreak_pos: Optional[int]
+ ) -> List[str]:
+ """
+ If the word under the cursor started with a quote (as indicated by a nonempty ``cword_prequote``), escapes
+ occurrences of that quote character in the completions, and adds the quote to the beginning of each completion.
+ Otherwise, escapes all characters that bash splits words on (``COMP_WORDBREAKS``), and removes portions of
+ completions before the first colon if (``COMP_WORDBREAKS``) contains a colon.
+
+ If there is only one completion, and it doesn't end with a **continuation character** (``/``, ``:``, or ``=``),
+ adds a space after the completion.
+
+ This method is exposed for overriding in subclasses; there is no need to use it directly.
+ """
+ special_chars = "\\"
+ # If the word under the cursor was quoted, escape the quote char.
+ # Otherwise, escape all special characters and specially handle all COMP_WORDBREAKS chars.
+ if cword_prequote == "":
+ # Bash mangles completions which contain characters in COMP_WORDBREAKS.
+ # This workaround has the same effect as __ltrim_colon_completions in bash_completion
+ # (extended to characters other than the colon).
+ if last_wordbreak_pos:
+ completions = [c[last_wordbreak_pos + 1 :] for c in completions]
+ special_chars += "();<>|&!`$* \t\n\"'"
+ elif cword_prequote == '"':
+ special_chars += '"`$!'
+
+ if os.environ.get("_ARGCOMPLETE_SHELL") in ("tcsh", "fish"):
+ # tcsh and fish escapes special characters itself.
+ special_chars = ""
+ elif cword_prequote == "'":
+ # Nothing can be escaped in single quotes, so we need to close
+ # the string, escape the single quote, then open a new string.
+ special_chars = ""
+ completions = [c.replace("'", r"'\''") for c in completions]
+
+ # PowerShell uses ` as escape character.
+ if os.environ.get("_ARGCOMPLETE_SHELL") == "powershell":
+ escape_char = '`'
+ special_chars = special_chars.replace('`', '')
+ else:
+ escape_char = "\\"
+ for char in special_chars:
+ completions = [c.replace(char, escape_char + char) for c in completions]
+
+ if self.append_space:
+ # Similar functionality in bash was previously turned off by supplying the "-o nospace" option to complete.
+ # Now it is conditionally disabled using "compopt -o nospace" if the match ends in a continuation character.
+ # This code is retained for environments where this isn't done natively.
+ continuation_chars = "=/:"
+ if len(completions) == 1 and completions[0][-1] not in continuation_chars:
+ if cword_prequote == "":
+ completions[0] += " "
+
+ return completions
+
+ def rl_complete(self, text, state):
+ """
+ Alternate entry point for using the argcomplete completer in a readline-based REPL. See also
+ `rlcompleter <https://docs.python.org/3/library/rlcompleter.html#completer-objects>`_.
+ Usage:
+
+ .. code-block:: python
+
+ import argcomplete, argparse, readline
+ parser = argparse.ArgumentParser()
+ ...
+ completer = argcomplete.CompletionFinder(parser)
+ readline.set_completer_delims("")
+ readline.set_completer(completer.rl_complete)
+ readline.parse_and_bind("tab: complete")
+ result = input("prompt> ")
+ """
+ if state == 0:
+ cword_prequote, cword_prefix, cword_suffix, comp_words, first_colon_pos = split_line(text)
+ comp_words.insert(0, sys.argv[0])
+ matches = self._get_completions(comp_words, cword_prefix, cword_prequote, first_colon_pos)
+ self._rl_matches = [text + match[len(cword_prefix) :] for match in matches]
+
+ if state < len(self._rl_matches):
+ return self._rl_matches[state]
+ else:
+ return None
+
+ def get_display_completions(self):
+ """
+ This function returns a mapping of option names to their help strings for displaying to the user.
+ """
+ return self._display_completions
+
+
+class ExclusiveCompletionFinder(CompletionFinder):
+ @staticmethod
+ def _action_allowed(action, parser):
+ if not CompletionFinder._action_allowed(action, parser):
+ return False
+
+ append_classes = (argparse._AppendAction, argparse._AppendConstAction)
+ if action._orig_class in append_classes:
+ return True
+
+ if action not in parser._seen_non_default_actions:
+ return True
+
+ return False
diff --git a/contrib/python/argcomplete/py3/argcomplete/io.py b/contrib/python/argcomplete/py3/argcomplete/io.py
new file mode 100644
index 0000000000..df2c4f2032
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/io.py
@@ -0,0 +1,42 @@
+import contextlib
+import os
+import sys
+
+_DEBUG = "_ARC_DEBUG" in os.environ
+
+debug_stream = sys.stderr
+
+
+def debug(*args):
+ if _DEBUG:
+ print(file=debug_stream, *args)
+
+
+@contextlib.contextmanager
+def mute_stdout():
+ stdout = sys.stdout
+ sys.stdout = open(os.devnull, "w")
+ try:
+ yield
+ finally:
+ sys.stdout = stdout
+
+
+@contextlib.contextmanager
+def mute_stderr():
+ stderr = sys.stderr
+ sys.stderr = open(os.devnull, "w")
+ try:
+ yield
+ finally:
+ sys.stderr.close()
+ sys.stderr = stderr
+
+
+def warn(*args):
+ """
+ Prints **args** to standard error when running completions. This will interrupt the user's command line interaction;
+ use it to indicate an error condition that is preventing your completer from working.
+ """
+ print(file=debug_stream)
+ print(file=debug_stream, *args)
diff --git a/contrib/python/argcomplete/py3/argcomplete/lexers.py b/contrib/python/argcomplete/py3/argcomplete/lexers.py
new file mode 100644
index 0000000000..72c378b54d
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/lexers.py
@@ -0,0 +1,57 @@
+import os
+
+from .exceptions import ArgcompleteException
+from .io import debug
+from .packages import _shlex
+
+
+def split_line(line, point=None):
+ if point is None:
+ point = len(line)
+ line = line[:point]
+ lexer = _shlex.shlex(line, posix=True)
+ lexer.whitespace_split = True
+ lexer.wordbreaks = os.environ.get("_ARGCOMPLETE_COMP_WORDBREAKS", "")
+ words = []
+
+ def split_word(word):
+ # TODO: make this less ugly
+ point_in_word = len(word) + point - lexer.instream.tell()
+ if isinstance(lexer.state, (str, bytes)) and lexer.state in lexer.whitespace:
+ point_in_word += 1
+ if point_in_word > len(word):
+ debug("In trailing whitespace")
+ words.append(word)
+ word = ""
+ prefix, suffix = word[:point_in_word], word[point_in_word:]
+ prequote = ""
+ # posix
+ if lexer.state is not None and lexer.state in lexer.quotes:
+ prequote = lexer.state
+ # non-posix
+ # if len(prefix) > 0 and prefix[0] in lexer.quotes:
+ # prequote, prefix = prefix[0], prefix[1:]
+
+ return prequote, prefix, suffix, words, lexer.last_wordbreak_pos
+
+ while True:
+ try:
+ word = lexer.get_token()
+ if word == lexer.eof:
+ # TODO: check if this is ever unsafe
+ # raise ArgcompleteException("Unexpected end of input")
+ return "", "", "", words, None
+ if lexer.instream.tell() >= point:
+ debug("word", word, "split, lexer state: '{s}'".format(s=lexer.state))
+ return split_word(word)
+ words.append(word)
+ except ValueError:
+ debug("word", lexer.token, "split (lexer stopped, state: '{s}')".format(s=lexer.state))
+ if lexer.instream.tell() >= point:
+ return split_word(lexer.token)
+ else:
+ msg = (
+ "Unexpected internal state. "
+ "Please report this bug at https://github.com/kislyuk/argcomplete/issues."
+ )
+ raise ArgcompleteException(msg)
diff --git a/contrib/python/argcomplete/py3/argcomplete/packages/__init__.py b/contrib/python/argcomplete/py3/argcomplete/packages/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/packages/__init__.py
diff --git a/contrib/python/argcomplete/py3/argcomplete/packages/_argparse.py b/contrib/python/argcomplete/py3/argcomplete/packages/_argparse.py
new file mode 100644
index 0000000000..7a8b4fc821
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/packages/_argparse.py
@@ -0,0 +1,337 @@
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
+# `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
+# files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
+# See https://github.com/kislyuk/argcomplete for more info.
+
+# This file contains argparse introspection utilities used in the course of argcomplete execution.
+
+from argparse import (
+ ONE_OR_MORE,
+ OPTIONAL,
+ PARSER,
+ REMAINDER,
+ SUPPRESS,
+ ZERO_OR_MORE,
+ Action,
+ ArgumentError,
+ ArgumentParser,
+ _get_action_name,
+ _SubParsersAction,
+)
+from gettext import gettext
+from typing import Dict, List, Set, Tuple
+
+_num_consumed_args: Dict[Action, int] = {}
+
+
+def action_is_satisfied(action):
+ '''Returns False if the parse would raise an error if no more arguments are given to this action, True otherwise.'''
+ num_consumed_args = _num_consumed_args.get(action, 0)
+
+ if action.nargs in [OPTIONAL, ZERO_OR_MORE, REMAINDER]:
+ return True
+ if action.nargs == ONE_OR_MORE:
+ return num_consumed_args >= 1
+ if action.nargs == PARSER:
+ # Not sure what this should be, but this previously always returned False
+ # so at least this won't break anything that wasn't already broken.
+ return False
+ if action.nargs is None:
+ return num_consumed_args == 1
+
+ assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs
+ return num_consumed_args == action.nargs
+
+
+def action_is_open(action):
+ '''Returns True if action could consume more arguments (i.e., its pattern is open).'''
+ num_consumed_args = _num_consumed_args.get(action, 0)
+
+ if action.nargs in [ZERO_OR_MORE, ONE_OR_MORE, PARSER, REMAINDER]:
+ return True
+ if action.nargs == OPTIONAL or action.nargs is None:
+ return num_consumed_args == 0
+
+ assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs
+ return num_consumed_args < action.nargs
+
+
+def action_is_greedy(action, isoptional=False):
+ '''Returns True if action will necessarily consume the next argument.
+ isoptional indicates whether the argument is an optional (starts with -).
+ '''
+ num_consumed_args = _num_consumed_args.get(action, 0)
+
+ if action.option_strings:
+ if not isoptional and not action_is_satisfied(action):
+ return True
+ return action.nargs == REMAINDER
+ else:
+ return action.nargs == REMAINDER and num_consumed_args >= 1
+
+
+class IntrospectiveArgumentParser(ArgumentParser):
+ '''The following is a verbatim copy of ArgumentParser._parse_known_args (Python 2.7.3),
+ except for the lines that contain the string "Added by argcomplete".
+ '''
+
+ def _parse_known_args(self, arg_strings, namespace):
+ _num_consumed_args.clear() # Added by argcomplete
+ self._argcomplete_namespace = namespace
+ self.active_actions: List[Action] = [] # Added by argcomplete
+ # replace arg strings that are file references
+ if self.fromfile_prefix_chars is not None:
+ arg_strings = self._read_args_from_files(arg_strings)
+
+ # map all mutually exclusive arguments to the other arguments
+ # they can't occur with
+ action_conflicts: Dict[Action, List[Action]] = {}
+ self._action_conflicts = action_conflicts # Added by argcomplete
+ for mutex_group in self._mutually_exclusive_groups:
+ group_actions = mutex_group._group_actions
+ for i, mutex_action in enumerate(mutex_group._group_actions):
+ conflicts = action_conflicts.setdefault(mutex_action, [])
+ conflicts.extend(group_actions[:i])
+ conflicts.extend(group_actions[i + 1 :])
+
+ # find all option indices, and determine the arg_string_pattern
+ # which has an 'O' if there is an option at an index,
+ # an 'A' if there is an argument, or a '-' if there is a '--'
+ option_string_indices = {}
+ arg_string_pattern_parts = []
+ arg_strings_iter = iter(arg_strings)
+ for i, arg_string in enumerate(arg_strings_iter):
+ # all args after -- are non-options
+ if arg_string == '--':
+ arg_string_pattern_parts.append('-')
+ for arg_string in arg_strings_iter:
+ arg_string_pattern_parts.append('A')
+
+ # otherwise, add the arg to the arg strings
+ # and note the index if it was an option
+ else:
+ option_tuple = self._parse_optional(arg_string)
+ if option_tuple is None:
+ pattern = 'A'
+ else:
+ option_string_indices[i] = option_tuple
+ pattern = 'O'
+ arg_string_pattern_parts.append(pattern)
+
+ # join the pieces together to form the pattern
+ arg_strings_pattern = ''.join(arg_string_pattern_parts)
+
+ # converts arg strings to the appropriate and then takes the action
+ seen_actions: Set[Action] = set()
+ seen_non_default_actions: Set[Action] = set()
+ self._seen_non_default_actions = seen_non_default_actions # Added by argcomplete
+
+ def take_action(action, argument_strings, option_string=None):
+ seen_actions.add(action)
+ argument_values = self._get_values(action, argument_strings)
+
+ # error if this argument is not allowed with other previously
+ # seen arguments, assuming that actions that use the default
+ # value don't really count as "present"
+ if argument_values is not action.default:
+ seen_non_default_actions.add(action)
+ for conflict_action in action_conflicts.get(action, []):
+ if conflict_action in seen_non_default_actions:
+ msg = gettext('not allowed with argument %s')
+ action_name = _get_action_name(conflict_action)
+ raise ArgumentError(action, msg % action_name)
+
+ # take the action if we didn't receive a SUPPRESS value
+ # (e.g. from a default)
+ if argument_values is not SUPPRESS or isinstance(action, _SubParsersAction):
+ try:
+ action(self, namespace, argument_values, option_string)
+ except BaseException:
+ # Begin added by argcomplete
+ # When a subparser action is taken and fails due to incomplete arguments, it does not merge the
+ # contents of its parsed namespace into the parent namespace. Do that here to allow completers to
+ # access the partially parsed arguments for the subparser.
+ if isinstance(action, _SubParsersAction):
+ subnamespace = action._name_parser_map[argument_values[0]]._argcomplete_namespace
+ for key, value in vars(subnamespace).items():
+ setattr(namespace, key, value)
+ # End added by argcomplete
+ raise
+
+ # function to convert arg_strings into an optional action
+ def consume_optional(start_index):
+ # get the optional identified at this index
+ option_tuple = option_string_indices[start_index]
+ action, option_string, explicit_arg = option_tuple
+
+ # identify additional optionals in the same arg string
+ # (e.g. -xyz is the same as -x -y -z if no args are required)
+ match_argument = self._match_argument
+ action_tuples: List[Tuple[Action, List[str], str]] = []
+ while True:
+ # if we found no optional action, skip it
+ if action is None:
+ extras.append(arg_strings[start_index])
+ return start_index + 1
+
+ # if there is an explicit argument, try to match the
+ # optional's string arguments to only this
+ if explicit_arg is not None:
+ arg_count = match_argument(action, 'A')
+
+ # if the action is a single-dash option and takes no
+ # arguments, try to parse more single-dash options out
+ # of the tail of the option string
+ chars = self.prefix_chars
+ if arg_count == 0 and option_string[1] not in chars:
+ action_tuples.append((action, [], option_string))
+ char = option_string[0]
+ option_string = char + explicit_arg[0]
+ new_explicit_arg = explicit_arg[1:] or None
+ optionals_map = self._option_string_actions
+ if option_string in optionals_map:
+ action = optionals_map[option_string]
+ explicit_arg = new_explicit_arg
+ else:
+ msg = gettext('ignored explicit argument %r')
+ raise ArgumentError(action, msg % explicit_arg)
+
+ # if the action expect exactly one argument, we've
+ # successfully matched the option; exit the loop
+ elif arg_count == 1:
+ stop = start_index + 1
+ args = [explicit_arg]
+ action_tuples.append((action, args, option_string))
+ break
+
+ # error if a double-dash option did not use the
+ # explicit argument
+ else:
+ msg = gettext('ignored explicit argument %r')
+ raise ArgumentError(action, msg % explicit_arg)
+
+ # if there is no explicit argument, try to match the
+ # optional's string arguments with the following strings
+ # if successful, exit the loop
+ else:
+ start = start_index + 1
+ selected_patterns = arg_strings_pattern[start:]
+ self.active_actions = [action] # Added by argcomplete
+ _num_consumed_args[action] = 0 # Added by argcomplete
+ arg_count = match_argument(action, selected_patterns)
+ stop = start + arg_count
+ args = arg_strings[start:stop]
+
+ # Begin added by argcomplete
+ # If the pattern is not open (e.g. no + at the end), remove the action from active actions (since
+ # it wouldn't be able to consume any more args)
+ _num_consumed_args[action] = len(args)
+ if not action_is_open(action):
+ self.active_actions.remove(action)
+ # End added by argcomplete
+
+ action_tuples.append((action, args, option_string))
+ break
+
+ # add the Optional to the list and return the index at which
+ # the Optional's string args stopped
+ assert action_tuples
+ for action, args, option_string in action_tuples:
+ take_action(action, args, option_string)
+ return stop
+
+ # the list of Positionals left to be parsed; this is modified
+ # by consume_positionals()
+ positionals = self._get_positional_actions()
+
+ # function to convert arg_strings into positional actions
+ def consume_positionals(start_index):
+ # match as many Positionals as possible
+ match_partial = self._match_arguments_partial
+ selected_pattern = arg_strings_pattern[start_index:]
+ arg_counts = match_partial(positionals, selected_pattern)
+
+ # slice off the appropriate arg strings for each Positional
+ # and add the Positional and its args to the list
+ for action, arg_count in zip(positionals, arg_counts): # Added by argcomplete
+ self.active_actions.append(action) # Added by argcomplete
+ for action, arg_count in zip(positionals, arg_counts):
+ args = arg_strings[start_index : start_index + arg_count]
+ start_index += arg_count
+ _num_consumed_args[action] = len(args) # Added by argcomplete
+ take_action(action, args)
+
+ # slice off the Positionals that we just parsed and return the
+ # index at which the Positionals' string args stopped
+ positionals[:] = positionals[len(arg_counts) :]
+ return start_index
+
+ # consume Positionals and Optionals alternately, until we have
+ # passed the last option string
+ extras = []
+ start_index = 0
+ if option_string_indices:
+ max_option_string_index = max(option_string_indices)
+ else:
+ max_option_string_index = -1
+ while start_index <= max_option_string_index:
+ # consume any Positionals preceding the next option
+ next_option_string_index = min([index for index in option_string_indices if index >= start_index])
+ if start_index != next_option_string_index:
+ positionals_end_index = consume_positionals(start_index)
+
+ # only try to parse the next optional if we didn't consume
+ # the option string during the positionals parsing
+ if positionals_end_index > start_index:
+ start_index = positionals_end_index
+ continue
+ else:
+ start_index = positionals_end_index
+
+ # if we consumed all the positionals we could and we're not
+ # at the index of an option string, there were extra arguments
+ if start_index not in option_string_indices:
+ strings = arg_strings[start_index:next_option_string_index]
+ extras.extend(strings)
+ start_index = next_option_string_index
+
+ # consume the next optional and any arguments for it
+ start_index = consume_optional(start_index)
+
+ # consume any positionals following the last Optional
+ stop_index = consume_positionals(start_index)
+
+ # if we didn't consume all the argument strings, there were extras
+ extras.extend(arg_strings[stop_index:])
+
+ # if we didn't use all the Positional objects, there were too few
+ # arg strings supplied.
+
+ if positionals:
+ self.active_actions.append(positionals[0]) # Added by argcomplete
+ self.error(gettext('too few arguments'))
+
+ # make sure all required actions were present
+ for action in self._actions:
+ if action.required:
+ if action not in seen_actions:
+ name = _get_action_name(action)
+ self.error(gettext('argument %s is required') % name)
+
+ # make sure all required groups had one option present
+ for group in self._mutually_exclusive_groups:
+ if group.required:
+ for action in group._group_actions:
+ if action in seen_non_default_actions:
+ break
+
+ # if no actions were used, report the error
+ else:
+ names = [
+ str(_get_action_name(action)) for action in group._group_actions if action.help is not SUPPRESS
+ ]
+ msg = gettext('one of the arguments %s is required')
+ self.error(msg % ' '.join(names))
+
+ # return the updated namespace and the extra arguments
+ return namespace, extras
diff --git a/contrib/python/argcomplete/py3/argcomplete/packages/_shlex.py b/contrib/python/argcomplete/py3/argcomplete/packages/_shlex.py
new file mode 100644
index 0000000000..613feb2597
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/packages/_shlex.py
@@ -0,0 +1,310 @@
+# This copy of shlex.py from Python 3.6 is distributed with argcomplete.
+# It contains only the shlex class, with modifications as noted.
+
+"""A lexical analyzer class for simple shell-like syntaxes."""
+
+# Module and documentation by Eric S. Raymond, 21 Dec 1998
+# Input stacking and error message cleanup added by ESR, March 2000
+# push_source() and pop_source() made explicit by ESR, January 2001.
+# Posix compliance, split(), string arguments, and
+# iterator interface by Gustavo Niemeyer, April 2003.
+# changes to tokenize more like Posix shells by Vinay Sajip, July 2016.
+
+import os
+import sys
+from collections import deque
+from io import StringIO
+from typing import Optional
+
+
+class shlex:
+ "A lexical analyzer class for simple shell-like syntaxes."
+
+ def __init__(self, instream=None, infile=None, posix=False, punctuation_chars=False):
+ # Modified by argcomplete: 2/3 compatibility
+ if isinstance(instream, str):
+ instream = StringIO(instream)
+ if instream is not None:
+ self.instream = instream
+ self.infile = infile
+ else:
+ self.instream = sys.stdin
+ self.infile = None
+ self.posix = posix
+ if posix:
+ self.eof = None
+ else:
+ self.eof = ''
+ self.commenters = '#'
+ self.wordchars = 'abcdfeghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
+ # Modified by argcomplete: 2/3 compatibility
+ # if self.posix:
+ # self.wordchars += ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
+ # 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
+ self.whitespace = ' \t\r\n'
+ self.whitespace_split = False
+ self.quotes = '\'"'
+ self.escape = '\\'
+ self.escapedquotes = '"'
+ self.state: Optional[str] = ' '
+ self.pushback: deque = deque()
+ self.lineno = 1
+ self.debug = 0
+ self.token = ''
+ self.filestack: deque = deque()
+ self.source = None
+ if not punctuation_chars:
+ punctuation_chars = ''
+ elif punctuation_chars is True:
+ punctuation_chars = '();<>|&'
+ self.punctuation_chars = punctuation_chars
+ if punctuation_chars:
+ # _pushback_chars is a push back queue used by lookahead logic
+ self._pushback_chars: deque = deque()
+ # these chars added because allowed in file names, args, wildcards
+ self.wordchars += '~-./*?='
+ # remove any punctuation chars from wordchars
+ t = self.wordchars.maketrans(dict.fromkeys(punctuation_chars))
+ self.wordchars = self.wordchars.translate(t)
+
+ # Modified by argcomplete: Record last wordbreak position
+ self.last_wordbreak_pos = None
+ self.wordbreaks = ''
+
+ def push_token(self, tok):
+ "Push a token onto the stack popped by the get_token method"
+ if self.debug >= 1:
+ print("shlex: pushing token " + repr(tok))
+ self.pushback.appendleft(tok)
+
+ def push_source(self, newstream, newfile=None):
+ "Push an input source onto the lexer's input source stack."
+ # Modified by argcomplete: 2/3 compatibility
+ if isinstance(newstream, str):
+ newstream = StringIO(newstream)
+ self.filestack.appendleft((self.infile, self.instream, self.lineno))
+ self.infile = newfile
+ self.instream = newstream
+ self.lineno = 1
+ if self.debug:
+ if newfile is not None:
+ print('shlex: pushing to file %s' % (self.infile,))
+ else:
+ print('shlex: pushing to stream %s' % (self.instream,))
+
+ def pop_source(self):
+ "Pop the input source stack."
+ self.instream.close()
+ (self.infile, self.instream, self.lineno) = self.filestack.popleft()
+ if self.debug:
+ print('shlex: popping to %s, line %d' % (self.instream, self.lineno))
+ self.state = ' '
+
+ def get_token(self):
+ "Get a token from the input stream (or from stack if it's nonempty)"
+ if self.pushback:
+ tok = self.pushback.popleft()
+ if self.debug >= 1:
+ print("shlex: popping token " + repr(tok))
+ return tok
+ # No pushback. Get a token.
+ raw = self.read_token()
+ # Handle inclusions
+ if self.source is not None:
+ while raw == self.source:
+ spec = self.sourcehook(self.read_token())
+ if spec:
+ (newfile, newstream) = spec
+ self.push_source(newstream, newfile)
+ raw = self.get_token()
+ # Maybe we got EOF instead?
+ while raw == self.eof:
+ if not self.filestack:
+ return self.eof
+ else:
+ self.pop_source()
+ raw = self.get_token()
+ # Neither inclusion nor EOF
+ if self.debug >= 1:
+ if raw != self.eof:
+ print("shlex: token=" + repr(raw))
+ else:
+ print("shlex: token=EOF")
+ return raw
+
+ def read_token(self):
+ quoted = False
+ escapedstate = ' '
+ while True:
+ if self.punctuation_chars and self._pushback_chars:
+ nextchar = self._pushback_chars.pop()
+ else:
+ nextchar = self.instream.read(1)
+ if nextchar == '\n':
+ self.lineno += 1
+ if self.debug >= 3:
+ print("shlex: in state %r I see character: %r" % (self.state, nextchar))
+ if self.state is None:
+ self.token = '' # past end of file
+ break
+ elif self.state == ' ':
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print("shlex: I see whitespace in whitespace state")
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno += 1
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif nextchar in self.wordchars:
+ self.token = nextchar
+ self.state = 'a'
+ elif nextchar in self.punctuation_chars:
+ self.token = nextchar
+ self.state = 'c'
+ elif nextchar in self.quotes:
+ if not self.posix:
+ self.token = nextchar
+ self.state = nextchar
+ elif self.whitespace_split:
+ self.token = nextchar
+ self.state = 'a'
+ else:
+ self.token = nextchar
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.state in self.quotes:
+ quoted = True
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print("shlex: I see EOF in quotes state")
+ # XXX what error should be raised here?
+ raise ValueError("No closing quotation")
+ if nextchar == self.state:
+ if not self.posix:
+ self.token += nextchar
+ self.state = ' '
+ break
+ else:
+ self.state = 'a'
+ elif self.posix and nextchar in self.escape and self.state in self.escapedquotes:
+ escapedstate = self.state
+ self.state = nextchar
+ else:
+ self.token += nextchar
+ elif self.state in self.escape:
+ if not nextchar: # end of file
+ if self.debug >= 2:
+ print("shlex: I see EOF in escape state")
+ # XXX what error should be raised here?
+ raise ValueError("No escaped character")
+ # In posix shells, only the quote itself or the escape
+ # character may be escaped within quotes.
+ if escapedstate in self.quotes and nextchar != self.state and nextchar != escapedstate:
+ self.token += self.state
+ self.token += nextchar
+ self.state = escapedstate
+ elif self.state in ('a', 'c'):
+ if not nextchar:
+ self.state = None # end of file
+ break
+ elif nextchar in self.whitespace:
+ if self.debug >= 2:
+ print("shlex: I see whitespace in word state")
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif nextchar in self.commenters:
+ self.instream.readline()
+ self.lineno += 1
+ if self.posix:
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ elif self.posix and nextchar in self.quotes:
+ self.state = nextchar
+ elif self.posix and nextchar in self.escape:
+ escapedstate = 'a'
+ self.state = nextchar
+ elif self.state == 'c':
+ if nextchar in self.punctuation_chars:
+ self.token += nextchar
+ else:
+ if nextchar not in self.whitespace:
+ self._pushback_chars.append(nextchar)
+ self.state = ' '
+ break
+ elif nextchar in self.wordchars or nextchar in self.quotes or self.whitespace_split:
+ self.token += nextchar
+ # Modified by argcomplete: Record last wordbreak position
+ if nextchar in self.wordbreaks:
+ self.last_wordbreak_pos = len(self.token) - 1
+ else:
+ if self.punctuation_chars:
+ self._pushback_chars.append(nextchar)
+ else:
+ self.pushback.appendleft(nextchar)
+ if self.debug >= 2:
+ print("shlex: I see punctuation in word state")
+ self.state = ' '
+ if self.token or (self.posix and quoted):
+ break # emit current token
+ else:
+ continue
+ result: Optional[str] = self.token
+ self.token = ''
+ if self.posix and not quoted and result == '':
+ result = None
+ if self.debug > 1:
+ if result:
+ print("shlex: raw token=" + repr(result))
+ else:
+ print("shlex: raw token=EOF")
+ # Modified by argcomplete: Record last wordbreak position
+ if self.state == ' ':
+ self.last_wordbreak_pos = None
+ return result
+
+ def sourcehook(self, newfile):
+ "Hook called on a filename to be sourced."
+ if newfile[0] == '"':
+ newfile = newfile[1:-1]
+ # This implements cpp-like semantics for relative-path inclusion.
+ # Modified by argcomplete: 2/3 compatibility
+ if isinstance(self.infile, str) and not os.path.isabs(newfile):
+ newfile = os.path.join(os.path.dirname(self.infile), newfile)
+ return (newfile, open(newfile, "r"))
+
+ def error_leader(self, infile=None, lineno=None):
+ "Emit a C-compiler-like, Emacs-friendly error-message leader."
+ if infile is None:
+ infile = self.infile
+ if lineno is None:
+ lineno = self.lineno
+ return "\"%s\", line %d: " % (infile, lineno)
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ token = self.get_token()
+ if token == self.eof:
+ raise StopIteration
+ return token
+
+ # Modified by argcomplete: 2/3 compatibility
+ next = __next__
diff --git a/contrib/python/argcomplete/py3/argcomplete/py.typed b/contrib/python/argcomplete/py3/argcomplete/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/py.typed
diff --git a/contrib/python/argcomplete/py3/argcomplete/shell_integration.py b/contrib/python/argcomplete/py3/argcomplete/shell_integration.py
new file mode 100644
index 0000000000..74bede645a
--- /dev/null
+++ b/contrib/python/argcomplete/py3/argcomplete/shell_integration.py
@@ -0,0 +1,183 @@
+# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
+# `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
+# files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
+# See https://github.com/kislyuk/argcomplete for more info.
+
+from shlex import quote
+
+bashcode = r"""
+# Run something, muting output or redirecting it to the debug stream
+# depending on the value of _ARC_DEBUG.
+# If ARGCOMPLETE_USE_TEMPFILES is set, use tempfiles for IPC.
+__python_argcomplete_run() {
+ if [[ -z "${ARGCOMPLETE_USE_TEMPFILES-}" ]]; then
+ __python_argcomplete_run_inner "$@"
+ return
+ fi
+ local tmpfile="$(mktemp)"
+ _ARGCOMPLETE_STDOUT_FILENAME="$tmpfile" __python_argcomplete_run_inner "$@"
+ local code=$?
+ cat "$tmpfile"
+ rm "$tmpfile"
+ return $code
+}
+
+__python_argcomplete_run_inner() {
+ if [[ -z "${_ARC_DEBUG-}" ]]; then
+ "$@" 8>&1 9>&2 1>/dev/null 2>&1
+ else
+ "$@" 8>&1 9>&2 1>&9 2>&1
+ fi
+}
+
+_python_argcomplete%(function_suffix)s() {
+ local IFS=$'\013'
+ if [[ -n "${ZSH_VERSION-}" ]]; then
+ local completions
+ completions=($(IFS="$IFS" \
+ COMP_LINE="$BUFFER" \
+ COMP_POINT="$CURSOR" \
+ _ARGCOMPLETE=1 \
+ _ARGCOMPLETE_SHELL="zsh" \
+ _ARGCOMPLETE_SUPPRESS_SPACE=1 \
+ __python_argcomplete_run "${words[1]}") )
+ _describe "${words[1]}" completions -o nosort
+ else
+ local SUPPRESS_SPACE=0
+ if compopt +o nospace 2> /dev/null; then
+ SUPPRESS_SPACE=1
+ fi
+ COMPREPLY=($(IFS="$IFS" \
+ COMP_LINE="$COMP_LINE" \
+ COMP_POINT="$COMP_POINT" \
+ COMP_TYPE="$COMP_TYPE" \
+ _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
+ _ARGCOMPLETE=1 \
+ _ARGCOMPLETE_SHELL="bash" \
+ _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
+ __python_argcomplete_run "%(argcomplete_script)s"))
+ if [[ $? != 0 ]]; then
+ unset COMPREPLY
+ elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
+ compopt -o nospace
+ fi
+ fi
+}
+if [[ -z "${ZSH_VERSION-}" ]]; then
+ complete %(complete_opts)s -F _python_argcomplete%(function_suffix)s %(executables)s
+else
+ compdef _python_argcomplete%(function_suffix)s %(executables)s
+fi
+"""
+
+tcshcode = """\
+complete "%(executable)s" 'p@*@`python-argcomplete-tcsh "%(argcomplete_script)s"`@' ;
+"""
+
+fishcode = r"""
+function __fish_%(function_name)s_complete
+ set -x _ARGCOMPLETE 1
+ set -x _ARGCOMPLETE_DFS \t
+ set -x _ARGCOMPLETE_IFS \n
+ set -x _ARGCOMPLETE_SUPPRESS_SPACE 1
+ set -x _ARGCOMPLETE_SHELL fish
+ set -x COMP_LINE (commandline -p)
+ set -x COMP_POINT (string length (commandline -cp))
+ set -x COMP_TYPE
+ if set -q _ARC_DEBUG
+ %(argcomplete_script)s 8>&1 9>&2 1>&9 2>&1
+ else
+ %(argcomplete_script)s 8>&1 9>&2 1>/dev/null 2>&1
+ end
+end
+complete %(completion_arg)s %(executable)s -f -a '(__fish_%(function_name)s_complete)'
+"""
+
+powershell_code = r"""
+Register-ArgumentCompleter -Native -CommandName %(executable)s -ScriptBlock {
+ param($commandName, $wordToComplete, $cursorPosition)
+ $completion_file = New-TemporaryFile
+ $env:ARGCOMPLETE_USE_TEMPFILES = 1
+ $env:_ARGCOMPLETE_STDOUT_FILENAME = $completion_file
+ $env:COMP_LINE = $wordToComplete
+ $env:COMP_POINT = $cursorPosition
+ $env:_ARGCOMPLETE = 1
+ $env:_ARGCOMPLETE_SUPPRESS_SPACE = 0
+ $env:_ARGCOMPLETE_IFS = "`n"
+ $env:_ARGCOMPLETE_SHELL = "powershell"
+ %(argcomplete_script)s 2>&1 | Out-Null
+
+ Get-Content $completion_file | ForEach-Object {
+ [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_)
+ }
+ Remove-Item $completion_file, Env:\_ARGCOMPLETE_STDOUT_FILENAME, Env:\ARGCOMPLETE_USE_TEMPFILES, Env:\COMP_LINE, Env:\COMP_POINT, Env:\_ARGCOMPLETE, Env:\_ARGCOMPLETE_SUPPRESS_SPACE, Env:\_ARGCOMPLETE_IFS, Env:\_ARGCOMPLETE_SHELL
+}
+""" # noqa: E501
+
+shell_codes = {"bash": bashcode, "tcsh": tcshcode, "fish": fishcode, "powershell": powershell_code}
+
+
+def shellcode(executables, use_defaults=True, shell="bash", complete_arguments=None, argcomplete_script=None):
+ """
+ Provide the shell code required to register a python executable for use with the argcomplete module.
+
+ :param list(str) executables: Executables to be completed (when invoked exactly with this name)
+ :param bool use_defaults: Whether to fallback to readline's default completion when no matches are generated
+ (affects bash only)
+ :param str shell: Name of the shell to output code for
+ :param complete_arguments: Arguments to call complete with (affects bash only)
+ :type complete_arguments: list(str) or None
+ :param argcomplete_script: Script to call complete with, if not the executable to complete.
+ If supplied, will be used to complete *all* passed executables.
+ :type argcomplete_script: str or None
+ """
+
+ if complete_arguments is None:
+ complete_options = "-o nospace -o default -o bashdefault" if use_defaults else "-o nospace -o bashdefault"
+ else:
+ complete_options = " ".join(complete_arguments)
+
+ if shell == "bash" or shell == "zsh":
+ quoted_executables = [quote(i) for i in executables]
+ executables_list = " ".join(quoted_executables)
+ script = argcomplete_script
+ if script:
+ function_suffix = "_" + script
+ else:
+ script = "$1"
+ function_suffix = ""
+ code = bashcode % dict(
+ complete_opts=complete_options,
+ executables=executables_list,
+ argcomplete_script=script,
+ function_suffix=function_suffix,
+ )
+ elif shell == "fish":
+ code = ""
+ for executable in executables:
+ script = argcomplete_script or executable
+ completion_arg = "--path" if "/" in executable else "--command" # use path for absolute paths
+ function_name = executable.replace("/", "_") # / not allowed in function name
+
+ code += fishcode % dict(
+ executable=executable,
+ argcomplete_script=script,
+ completion_arg=completion_arg,
+ function_name=function_name,
+ )
+ elif shell == "powershell":
+ code = ""
+ for executable in executables:
+ script = argcomplete_script or executable
+ code += powershell_code % dict(executable=executable, argcomplete_script=script)
+
+ else:
+ code = ""
+ for executable in executables:
+ script = argcomplete_script
+ # If no script was specified, default to the executable being completed.
+ if not script:
+ script = executable
+ code += shell_codes.get(shell, "") % dict(executable=executable, argcomplete_script=script)
+
+ return code
diff --git a/contrib/python/argcomplete/py3/ya.make b/contrib/python/argcomplete/py3/ya.make
new file mode 100644
index 0000000000..eebc255d35
--- /dev/null
+++ b/contrib/python/argcomplete/py3/ya.make
@@ -0,0 +1,35 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(3.1.2)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ argcomplete/__init__.py
+ argcomplete/_check_console_script.py
+ argcomplete/_check_module.py
+ argcomplete/completers.py
+ argcomplete/exceptions.py
+ argcomplete/finders.py
+ argcomplete/io.py
+ argcomplete/lexers.py
+ argcomplete/packages/__init__.py
+ argcomplete/packages/_argparse.py
+ argcomplete/packages/_shlex.py
+ argcomplete/shell_integration.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/argcomplete/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ argcomplete/bash_completion.d/_python-argcomplete
+ argcomplete/py.typed
+)
+
+END()
diff --git a/contrib/python/argcomplete/ya.make b/contrib/python/argcomplete/ya.make
new file mode 100644
index 0000000000..1692542636
--- /dev/null
+++ b/contrib/python/argcomplete/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/argcomplete/py2)
+ELSE()
+ PEERDIR(contrib/python/argcomplete/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/traitlets/py3/.dist-info/METADATA b/contrib/python/traitlets/py3/.dist-info/METADATA
index 5ef59abe28..fb63430a4c 100644
--- a/contrib/python/traitlets/py3/.dist-info/METADATA
+++ b/contrib/python/traitlets/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: traitlets
-Version: 5.10.1
+Version: 5.11.2
Summary: Traitlets Python configuration system
Project-URL: Homepage, https://github.com/ipython/traitlets
Author-email: IPython Development Team <ipython-dev@python.org>
diff --git a/contrib/python/traitlets/py3/tests/__init__.py b/contrib/python/traitlets/py3/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/__init__.py
diff --git a/contrib/python/traitlets/py3/traitlets/tests/_warnings.py b/contrib/python/traitlets/py3/tests/_warnings.py
index e3c3a0ac6d..e3c3a0ac6d 100644
--- a/contrib/python/traitlets/py3/traitlets/tests/_warnings.py
+++ b/contrib/python/traitlets/py3/tests/_warnings.py
diff --git a/contrib/python/traitlets/py3/tests/config/__init__.py b/contrib/python/traitlets/py3/tests/config/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/config/__init__.py
diff --git a/contrib/python/traitlets/py3/traitlets/config/tests/test_application.py b/contrib/python/traitlets/py3/tests/config/test_application.py
index 084ff6f032..610cafc3cd 100644
--- a/contrib/python/traitlets/py3/traitlets/config/tests/test_application.py
+++ b/contrib/python/traitlets/py3/tests/config/test_application.py
@@ -601,7 +601,7 @@ class TestApplication(TestCase):
with self.assertRaises(SyntaxError):
app.load_config_file(name, path=[td])
- def test_subcommands_instanciation(self):
+ def test_subcommands_instantiation(self):
"""Try all ways to specify how to create sub-apps."""
app = Root.instance()
app.parse_command_line(["sub1"])
@@ -695,7 +695,7 @@ def test_cli_multi_scalar(caplog):
class Root(Application):
subcommands = {
- "sub1": ("__tests__.config.tests.test_application.Sub1", "import string"),
+ "sub1": ("__tests__.config.test_application.Sub1", "import string"),
}
diff --git a/contrib/python/traitlets/py3/tests/config/test_argcomplete.py b/contrib/python/traitlets/py3/tests/config/test_argcomplete.py
new file mode 100644
index 0000000000..52ed6d2bb2
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/config/test_argcomplete.py
@@ -0,0 +1,219 @@
+"""
+Tests for argcomplete handling by traitlets.config.application.Application
+"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import io
+import os
+import typing as t
+
+import pytest
+
+argcomplete = pytest.importorskip("argcomplete")
+
+from traitlets import Unicode
+from traitlets.config.application import Application
+from traitlets.config.configurable import Configurable
+from traitlets.config.loader import KVArgParseConfigLoader
+
+
+class ArgcompleteApp(Application):
+ """Override loader to pass through kwargs for argcomplete testing"""
+
+ argcomplete_kwargs: t.Dict[str, t.Any]
+
+ def __init__(self, *args, **kwargs):
+ # For subcommands, inherit argcomplete_kwargs from parent app
+ parent = kwargs.get("parent")
+ super().__init__(*args, **kwargs)
+ if parent:
+ argcomplete_kwargs = getattr(parent, "argcomplete_kwargs", None)
+ if argcomplete_kwargs:
+ self.argcomplete_kwargs = argcomplete_kwargs
+
+ def _create_loader(self, argv, aliases, flags, classes):
+ loader = KVArgParseConfigLoader(
+ argv, aliases, flags, classes=classes, log=self.log, subcommands=self.subcommands
+ )
+ loader._argcomplete_kwargs = self.argcomplete_kwargs # type: ignore[attr-defined]
+ return loader
+
+
+class SubApp1(ArgcompleteApp):
+ pass
+
+
+class SubApp2(ArgcompleteApp):
+ @classmethod
+ def get_subapp_instance(cls, app: Application) -> Application:
+ app.clear_instance() # since Application is singleton, need to clear main app
+ return cls.instance(parent=app) # type: ignore[no-any-return]
+
+
+class MainApp(ArgcompleteApp):
+ subcommands = {
+ "subapp1": (SubApp1, "First subapp"),
+ "subapp2": (SubApp2.get_subapp_instance, "Second subapp"),
+ }
+
+
+class CustomError(Exception):
+ """Helper for exit hook for testing argcomplete"""
+
+ @classmethod
+ def exit(cls, code):
+ raise cls(str(code))
+
+
+class TestArgcomplete:
+ IFS = "\013"
+ COMP_WORDBREAKS = " \t\n\"'><=;|&(:"
+
+ @pytest.fixture
+ def argcomplete_on(self, mocker):
+ """Mostly borrowed from argcomplete's unit test fixtures
+
+ Set up environment variables to mimic those passed by argcomplete
+ """
+ _old_environ = os.environ
+ os.environ = os.environ.copy() # type: ignore[assignment]
+ os.environ["_ARGCOMPLETE"] = "1"
+ os.environ["_ARC_DEBUG"] = "yes"
+ os.environ["IFS"] = self.IFS
+ os.environ["_ARGCOMPLETE_COMP_WORDBREAKS"] = self.COMP_WORDBREAKS
+
+ # argcomplete==2.0.0 always calls fdopen(9, "w") to open a debug stream,
+ # however this could conflict with file descriptors used by pytest
+ # and lead to obscure errors. Since we are not looking at debug stream
+ # in these tests, just mock this fdopen call out.
+ mocker.patch("os.fdopen")
+ try:
+ yield
+ finally:
+ os.environ = _old_environ
+
+ def run_completer(
+ self,
+ app: ArgcompleteApp,
+ command: str,
+ point: t.Union[str, int, None] = None,
+ **kwargs: t.Any,
+ ) -> t.List[str]:
+ """Mostly borrowed from argcomplete's unit tests
+
+ Modified to take an application instead of an ArgumentParser
+
+ Command is the current command being completed and point is the index
+ into the command where the completion is triggered.
+ """
+ if point is None:
+ point = str(len(command))
+ # Flushing tempfile was leading to CI failures with Bad file descriptor, not sure why.
+ # Fortunately we can just write to a StringIO instead.
+ # print("Writing completions to temp file with mode=", write_mode)
+ # from tempfile import TemporaryFile
+ # with TemporaryFile(mode=write_mode) as t:
+ strio = io.StringIO()
+ os.environ["COMP_LINE"] = command
+ os.environ["COMP_POINT"] = str(point)
+
+ with pytest.raises(CustomError) as cm:
+ app.argcomplete_kwargs = dict(
+ output_stream=strio, exit_method=CustomError.exit, **kwargs
+ )
+ app.initialize()
+
+ if str(cm.value) != "0":
+ raise RuntimeError(f"Unexpected exit code {cm.value}")
+ out = strio.getvalue()
+ return out.split(self.IFS)
+
+ def test_complete_simple_app(self, argcomplete_on):
+ app = ArgcompleteApp()
+ expected = [
+ '--help',
+ '--debug',
+ '--show-config',
+ '--show-config-json',
+ '--log-level',
+ '--Application.',
+ '--ArgcompleteApp.',
+ ]
+ assert set(self.run_completer(app, "app --")) == set(expected)
+
+ # completing class traits
+ assert set(self.run_completer(app, "app --App")) > {
+ '--Application.show_config',
+ '--Application.log_level',
+ '--Application.log_format',
+ }
+
+ def test_complete_custom_completers(self, argcomplete_on):
+ app = ArgcompleteApp()
+ # test pre-defined completers for Bool/Enum
+ assert set(self.run_completer(app, "app --Application.log_level=")) > {"DEBUG", "INFO"}
+ assert set(self.run_completer(app, "app --ArgcompleteApp.show_config ")) == {
+ "0",
+ "1",
+ "true",
+ "false",
+ }
+
+ # test custom completer and mid-command completions
+ class CustomCls(Configurable):
+ val = Unicode().tag(
+ config=True, argcompleter=argcomplete.completers.ChoicesCompleter(["foo", "bar"])
+ )
+
+ class CustomApp(ArgcompleteApp):
+ classes = [CustomCls]
+ aliases = {("v", "val"): "CustomCls.val"}
+
+ app = CustomApp()
+ assert self.run_completer(app, "app --val ") == ["foo", "bar"]
+ assert self.run_completer(app, "app --val=") == ["foo", "bar"]
+ assert self.run_completer(app, "app -v ") == ["foo", "bar"]
+ assert self.run_completer(app, "app -v=") == ["foo", "bar"]
+ assert self.run_completer(app, "app --CustomCls.val ") == ["foo", "bar"]
+ assert self.run_completer(app, "app --CustomCls.val=") == ["foo", "bar"]
+ completions = self.run_completer(app, "app --val= abc xyz", point=10)
+ # fixed in argcomplete >= 2.0 to return latter below
+ assert completions == ["--val=foo", "--val=bar"] or completions == ["foo", "bar"]
+ assert self.run_completer(app, "app --val --log-level=", point=10) == ["foo", "bar"]
+
+ def test_complete_subcommands(self, argcomplete_on):
+ app = MainApp()
+ assert set(self.run_completer(app, "app ")) >= {"subapp1", "subapp2"}
+ assert set(self.run_completer(app, "app sub")) == {"subapp1", "subapp2"}
+ assert set(self.run_completer(app, "app subapp1")) == {"subapp1"}
+
+ def test_complete_subcommands_subapp1(self, argcomplete_on):
+ # subcommand handling modifies _ARGCOMPLETE env var global state, so
+ # only can test one completion per unit test
+ app = MainApp()
+ try:
+ assert set(self.run_completer(app, "app subapp1 --Sub")) > {
+ '--SubApp1.show_config',
+ '--SubApp1.log_level',
+ '--SubApp1.log_format',
+ }
+ finally:
+ SubApp1.clear_instance()
+
+ def test_complete_subcommands_subapp2(self, argcomplete_on):
+ app = MainApp()
+ try:
+ assert set(self.run_completer(app, "app subapp2 --")) > {
+ '--Application.',
+ '--SubApp2.',
+ }
+ finally:
+ SubApp2.clear_instance()
+
+ def test_complete_subcommands_main(self, argcomplete_on):
+ app = MainApp()
+ completions = set(self.run_completer(app, "app --"))
+ assert completions > {'--Application.', '--MainApp.'}
+ assert "--SubApp1." not in completions and "--SubApp2." not in completions
diff --git a/contrib/python/traitlets/py3/traitlets/config/tests/test_configurable.py b/contrib/python/traitlets/py3/tests/config/test_configurable.py
index 357aede78a..f6499ea29d 100644
--- a/contrib/python/traitlets/py3/traitlets/config/tests/test_configurable.py
+++ b/contrib/python/traitlets/py3/tests/config/test_configurable.py
@@ -8,6 +8,7 @@ from unittest import TestCase
from pytest import mark
+from .._warnings import expected_warnings
from traitlets.config.application import Application
from traitlets.config.configurable import Configurable, LoggingConfigurable, SingletonConfigurable
from traitlets.config.loader import Config
@@ -26,8 +27,6 @@ from traitlets.traitlets import (
)
from traitlets.utils.warnings import _deprecations_shown
-from traitlets.tests._warnings import expected_warnings
-
class MyConfigurable(Configurable):
a = Integer(1, help="The integer a.").tag(config=True)
diff --git a/contrib/python/traitlets/py3/traitlets/config/tests/test_loader.py b/contrib/python/traitlets/py3/tests/config/test_loader.py
index 3a1f96120f..3a1f96120f 100644
--- a/contrib/python/traitlets/py3/traitlets/config/tests/test_loader.py
+++ b/contrib/python/traitlets/py3/tests/config/test_loader.py
diff --git a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py b/contrib/python/traitlets/py3/tests/test_traitlets.py
index dab1f6ddc7..62fa726f19 100644
--- a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py
+++ b/contrib/python/traitlets/py3/tests/test_traitlets.py
@@ -62,7 +62,7 @@ from traitlets import (
)
from traitlets.utils import cast_unicode
-from traitlets.tests._warnings import expected_warnings
+from ._warnings import expected_warnings
def change_dict(*ordered_values):
diff --git a/contrib/python/traitlets/py3/tests/test_traitlets_docstring.py b/contrib/python/traitlets/py3/tests/test_traitlets_docstring.py
new file mode 100644
index 0000000000..700199108f
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/test_traitlets_docstring.py
@@ -0,0 +1,84 @@
+"""Tests for traitlets.traitlets."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+#
+from traitlets import Dict, Instance, Integer, Unicode, Union
+from traitlets.config import Configurable
+
+
+def test_handle_docstring():
+ class SampleConfigurable(Configurable):
+ pass
+
+ class TraitTypesSampleConfigurable(Configurable):
+ """TraitTypesSampleConfigurable docstring"""
+
+ trait_integer = Integer(
+ help="""trait_integer help text""",
+ config=True,
+ )
+ trait_integer_nohelp = Integer(
+ config=True,
+ )
+ trait_integer_noconfig = Integer(
+ help="""trait_integer_noconfig help text""",
+ )
+
+ trait_unicode = Unicode(
+ help="""trait_unicode help text""",
+ config=True,
+ )
+ trait_unicode_nohelp = Unicode(
+ config=True,
+ )
+ trait_unicode_noconfig = Unicode(
+ help="""trait_unicode_noconfig help text""",
+ )
+
+ trait_dict = Dict(
+ help="""trait_dict help text""",
+ config=True,
+ )
+ trait_dict_nohelp = Dict(
+ config=True,
+ )
+ trait_dict_noconfig = Dict(
+ help="""trait_dict_noconfig help text""",
+ )
+
+ trait_instance = Instance(
+ klass=SampleConfigurable,
+ help="""trait_instance help text""",
+ config=True,
+ )
+ trait_instance_nohelp = Instance(
+ klass=SampleConfigurable,
+ config=True,
+ )
+ trait_instance_noconfig = Instance(
+ klass=SampleConfigurable,
+ help="""trait_instance_noconfig help text""",
+ )
+
+ trait_union = Union(
+ [Integer(), Unicode()],
+ help="""trait_union help text""",
+ config=True,
+ )
+ trait_union_nohelp = Union(
+ [Integer(), Unicode()],
+ config=True,
+ )
+ trait_union_noconfig = Union(
+ [Integer(), Unicode()],
+ help="""trait_union_noconfig help text""",
+ )
+
+ base_names = SampleConfigurable().trait_names()
+ for name in TraitTypesSampleConfigurable().trait_names():
+ if name in base_names:
+ continue
+ doc = getattr(TraitTypesSampleConfigurable, name).__doc__
+ if "nohelp" not in name:
+ assert doc == f"{name} help text"
diff --git a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py b/contrib/python/traitlets/py3/tests/test_traitlets_enum.py
index c39007e8a0..c39007e8a0 100644
--- a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py
+++ b/contrib/python/traitlets/py3/tests/test_traitlets_enum.py
diff --git a/contrib/python/traitlets/py3/tests/test_typing.py b/contrib/python/traitlets/py3/tests/test_typing.py
new file mode 100644
index 0000000000..2b4073ecf7
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/test_typing.py
@@ -0,0 +1,395 @@
+from __future__ import annotations
+
+import typing
+
+import pytest
+
+from traitlets import (
+ Any,
+ Bool,
+ CInt,
+ Dict,
+ HasTraits,
+ Instance,
+ Int,
+ List,
+ Set,
+ TCPAddress,
+ Type,
+ Unicode,
+ Union,
+ default,
+ observe,
+ validate,
+)
+from traitlets.config import Config
+
+if not typing.TYPE_CHECKING:
+
+ def reveal_type(*args, **kwargs):
+ pass
+
+
+# mypy: disallow-untyped-calls
+
+
+class Foo:
+ def __init__(self, c):
+ self.c = c
+
+
+@pytest.mark.mypy_testing
+def mypy_decorator_typing():
+ class T(HasTraits):
+ foo = Unicode("").tag(config=True)
+
+ @default("foo")
+ def _default_foo(self) -> str:
+ return "hi"
+
+ @observe("foo")
+ def _foo_observer(self, change: typing.Any) -> bool:
+ return True
+
+ @validate("foo")
+ def _foo_validate(self, commit: typing.Any) -> bool:
+ return True
+
+ t = T()
+ reveal_type(t.foo) # R: builtins.str
+ reveal_type(t._foo_observer) # R: Any
+ reveal_type(t._foo_validate) # R: Any
+
+
+@pytest.mark.mypy_testing
+def mypy_config_typing():
+ c = Config(
+ {
+ "ExtractOutputPreprocessor": {"enabled": True},
+ }
+ )
+ reveal_type(c) # R: traitlets.config.loader.Config
+
+
+@pytest.mark.mypy_testing
+def mypy_union_typing():
+ class T(HasTraits):
+ style = Union(
+ [Unicode("default"), Type(klass=object)],
+ help="Name of the pygments style to use",
+ default_value="hi",
+ ).tag(config=True)
+
+ t = T()
+ reveal_type(Union("foo")) # R: traitlets.traitlets.Union
+ reveal_type(Union("").tag(sync=True)) # R: traitlets.traitlets.Union
+ reveal_type(Union(None, allow_none=True)) # R: traitlets.traitlets.Union
+ reveal_type(Union(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Union
+ reveal_type(T.style) # R: traitlets.traitlets.Union
+ reveal_type(t.style) # R: Any
+
+
+@pytest.mark.mypy_testing
+def mypy_list_typing():
+ class T(HasTraits):
+ latex_command = List(
+ ["xelatex", "{filename}", "-quiet"], help="Shell command used to compile latex."
+ ).tag(config=True)
+
+ t = T()
+ reveal_type(List("foo")) # R: traitlets.traitlets.List
+ reveal_type(List("").tag(sync=True)) # R: traitlets.traitlets.List
+ reveal_type(List(None, allow_none=True)) # R: traitlets.traitlets.List
+ reveal_type(List(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.List
+ reveal_type(T.latex_command) # R: traitlets.traitlets.List
+ reveal_type(t.latex_command) # R: builtins.list[Any]
+
+
+@pytest.mark.mypy_testing
+def mypy_dict_typing():
+ class T(HasTraits):
+ foo = Dict({}, help="Shell command used to compile latex.").tag(config=True)
+
+ t = T()
+ reveal_type(Dict("foo")) # R: traitlets.traitlets.Dict
+ reveal_type(Dict("").tag(sync=True)) # R: traitlets.traitlets.Dict
+ reveal_type(Dict(None, allow_none=True)) # R: traitlets.traitlets.Dict
+ reveal_type(Dict(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Dict
+ reveal_type(T.foo) # R: traitlets.traitlets.Dict
+ reveal_type(t.foo) # R: builtins.dict[Any, Any]
+
+
+@pytest.mark.mypy_testing
+def mypy_type_typing():
+ class KernelSpec:
+ item = Unicode("foo")
+
+ class KernelSpecManager(HasTraits):
+ """A manager for kernel specs."""
+
+ kernel_spec_class = Type(
+ KernelSpec,
+ config=True,
+ help="""The kernel spec class. This is configurable to allow
+ subclassing of the KernelSpecManager for customized behavior.
+ """,
+ )
+ other_class = Type("foo.bar.baz")
+
+ t = KernelSpecManager()
+ reveal_type(t.kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@124
+ reveal_type(t.kernel_spec_class()) # R: tests.test_typing.KernelSpec@124
+ reveal_type(t.kernel_spec_class().item) # R: builtins.str
+ reveal_type(t.other_class) # R: builtins.type
+ reveal_type(t.other_class()) # R: Any
+
+
+@pytest.mark.mypy_testing
+def mypy_unicode_typing():
+ class T(HasTraits):
+ export_format = Unicode(
+ allow_none=False,
+ help="""The export format to be used, either one of the built-in formats
+ or a dotted object name that represents the import path for an
+ ``Exporter`` class""",
+ ).tag(config=True)
+
+ t = T()
+ reveal_type(
+ Unicode( # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
+ "foo"
+ )
+ )
+ reveal_type(
+ Unicode( # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
+ ""
+ ).tag(
+ sync=True
+ )
+ )
+ reveal_type(
+ Unicode( # R: traitlets.traitlets.Unicode[Union[builtins.str, None], Union[builtins.str, builtins.bytes, None]]
+ None, allow_none=True
+ )
+ )
+ reveal_type(
+ Unicode( # R: traitlets.traitlets.Unicode[Union[builtins.str, None], Union[builtins.str, builtins.bytes, None]]
+ None, allow_none=True
+ ).tag(
+ sync=True
+ )
+ )
+ reveal_type(
+ T.export_format # R: traitlets.traitlets.Unicode[builtins.str, Union[builtins.str, builtins.bytes]]
+ )
+ reveal_type(t.export_format) # R: builtins.str
+
+
+@pytest.mark.mypy_testing
+def mypy_set_typing():
+ class T(HasTraits):
+ remove_cell_tags = Set(
+ Unicode(),
+ default_value=[],
+ help=(
+ "Tags indicating which cells are to be removed,"
+ "matches tags in ``cell.metadata.tags``."
+ ),
+ ).tag(config=True)
+
+ safe_output_keys = Set(
+ config=True,
+ default_value={
+ "metadata", # Not a mimetype per-se, but expected and safe.
+ "text/plain",
+ "text/latex",
+ "application/json",
+ "image/png",
+ "image/jpeg",
+ },
+ help="Cell output mimetypes to render without modification",
+ )
+
+ t = T()
+ reveal_type(Set("foo")) # R: traitlets.traitlets.Set
+ reveal_type(Set("").tag(sync=True)) # R: traitlets.traitlets.Set
+ reveal_type(Set(None, allow_none=True)) # R: traitlets.traitlets.Set
+ reveal_type(Set(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Set
+ reveal_type(T.remove_cell_tags) # R: traitlets.traitlets.Set
+ reveal_type(t.remove_cell_tags) # R: builtins.set[Any]
+ reveal_type(T.safe_output_keys) # R: traitlets.traitlets.Set
+ reveal_type(t.safe_output_keys) # R: builtins.set[Any]
+
+
+@pytest.mark.mypy_testing
+def mypy_any_typing():
+ class T(HasTraits):
+ attributes = Any(
+ config=True,
+ default_value={
+ "a": ["href", "title"],
+ "abbr": ["title"],
+ "acronym": ["title"],
+ },
+ help="Allowed HTML tag attributes",
+ )
+
+ t = T()
+ reveal_type(Any("foo")) # R: traitlets.traitlets.Any
+ reveal_type(Any("").tag(sync=True)) # R: traitlets.traitlets.Any
+ reveal_type(Any(None, allow_none=True)) # R: traitlets.traitlets.Any
+ reveal_type(Any(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Any
+ reveal_type(T.attributes) # R: traitlets.traitlets.Any
+ reveal_type(t.attributes) # R: Any
+
+
+@pytest.mark.mypy_testing
+def mypy_bool_typing():
+ class T(HasTraits):
+ b = Bool(True).tag(sync=True)
+ ob = Bool(None, allow_none=True).tag(sync=True)
+
+ t = T()
+ reveal_type(
+ Bool(True) # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
+ )
+ reveal_type(
+ Bool( # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
+ True
+ ).tag(sync=True)
+ )
+ reveal_type(
+ Bool( # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
+ None, allow_none=True
+ )
+ )
+ reveal_type(
+ Bool( # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
+ None, allow_none=True
+ ).tag(
+ sync=True
+ )
+ )
+ reveal_type(
+ T.b # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
+ )
+ reveal_type(t.b) # R: builtins.bool
+ reveal_type(t.ob) # R: Union[builtins.bool, None]
+ reveal_type(
+ T.b # R: traitlets.traitlets.Bool[builtins.bool, Union[builtins.bool, builtins.int]]
+ )
+ reveal_type(
+ T.ob # R: traitlets.traitlets.Bool[Union[builtins.bool, None], Union[builtins.bool, builtins.int, None]]
+ )
+ # we would expect this to be Optional[Union[bool, int]], but...
+ t.b = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Union[bool, int]") [assignment]
+ t.b = None # E: Incompatible types in assignment (expression has type "None", variable has type "Union[bool, int]") [assignment]
+
+
+@pytest.mark.mypy_testing
+def mypy_int_typing():
+ class T(HasTraits):
+ i: Int[int, int] = Int(42).tag(sync=True)
+ oi: Int[int | None, int | None] = Int(42, allow_none=True).tag(sync=True)
+
+ t = T()
+ reveal_type(Int(True)) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
+ reveal_type(Int(True).tag(sync=True)) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
+ reveal_type(
+ Int( # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
+ None, allow_none=True
+ )
+ )
+ reveal_type(
+ Int( # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
+ None, allow_none=True
+ ).tag(sync=True)
+ )
+ reveal_type(T.i) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
+ reveal_type(t.i) # R: builtins.int
+ reveal_type(t.oi) # R: Union[builtins.int, None]
+ reveal_type(T.i) # R: traitlets.traitlets.Int[builtins.int, builtins.int]
+ reveal_type(
+ T.oi # R: traitlets.traitlets.Int[Union[builtins.int, None], Union[builtins.int, None]]
+ )
+ t.i = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
+ t.i = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [assignment]
+ t.i = 1.2 # E: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
+
+
+@pytest.mark.mypy_testing
+def mypy_cint_typing():
+ class T(HasTraits):
+ i = CInt(42).tag(sync=True)
+ oi = CInt(42, allow_none=True).tag(sync=True)
+
+ t = T()
+ reveal_type(CInt(42)) # R: traitlets.traitlets.CInt[builtins.int, Any]
+ reveal_type(CInt(42).tag(sync=True)) # R: traitlets.traitlets.CInt[builtins.int, Any]
+ reveal_type(
+ CInt(None, allow_none=True) # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
+ )
+ reveal_type(
+ CInt( # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
+ None, allow_none=True
+ ).tag(sync=True)
+ )
+ reveal_type(T.i) # R: traitlets.traitlets.CInt[builtins.int, Any]
+ reveal_type(t.i) # R: builtins.int
+ reveal_type(t.oi) # R: Union[builtins.int, None]
+ reveal_type(T.i) # R: traitlets.traitlets.CInt[builtins.int, Any]
+ reveal_type(T.oi) # R: traitlets.traitlets.CInt[Union[builtins.int, None], Any]
+
+
+@pytest.mark.mypy_testing
+def mypy_tcp_typing():
+ class T(HasTraits):
+ tcp = TCPAddress()
+ otcp = TCPAddress(None, allow_none=True)
+
+ t = T()
+ reveal_type(t.tcp) # R: Tuple[builtins.str, builtins.int]
+ reveal_type(
+ T.tcp # R: traitlets.traitlets.TCPAddress[Tuple[builtins.str, builtins.int], Tuple[builtins.str, builtins.int]]
+ )
+ reveal_type(
+ T.tcp.tag( # R:traitlets.traitlets.TCPAddress[Tuple[builtins.str, builtins.int], Tuple[builtins.str, builtins.int]]
+ sync=True
+ )
+ )
+ reveal_type(t.otcp) # R: Union[Tuple[builtins.str, builtins.int], None]
+ reveal_type(
+ T.otcp # R: traitlets.traitlets.TCPAddress[Union[Tuple[builtins.str, builtins.int], None], Union[Tuple[builtins.str, builtins.int], None]]
+ )
+ reveal_type(
+ T.otcp.tag( # R: traitlets.traitlets.TCPAddress[Union[Tuple[builtins.str, builtins.int], None], Union[Tuple[builtins.str, builtins.int], None]]
+ sync=True
+ )
+ )
+ t.tcp = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Tuple[str, int]") [assignment]
+ t.otcp = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[Tuple[str, int]]") [assignment]
+ t.tcp = None # E: Incompatible types in assignment (expression has type "None", variable has type "Tuple[str, int]") [assignment]
+
+
+@pytest.mark.mypy_testing
+def mypy_instance_typing():
+ class T(HasTraits):
+ inst = Instance(Foo)
+ oinst = Instance(Foo, allow_none=True)
+ oinst_string = Instance("Foo", allow_none=True)
+
+ t = T()
+ reveal_type(t.inst) # R: tests.test_typing.Foo
+ reveal_type(T.inst) # R: traitlets.traitlets.Instance[tests.test_typing.Foo]
+ reveal_type(T.inst.tag(sync=True)) # R: traitlets.traitlets.Instance[tests.test_typing.Foo]
+ reveal_type(t.oinst) # R: Union[tests.test_typing.Foo, None]
+ reveal_type(t.oinst_string) # R: Union[Any, None]
+ reveal_type(T.oinst) # R: traitlets.traitlets.Instance[Union[tests.test_typing.Foo, None]]
+ reveal_type(
+ T.oinst.tag( # R: traitlets.traitlets.Instance[Union[tests.test_typing.Foo, None]]
+ sync=True
+ )
+ )
+ t.inst = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Foo") [assignment]
+ t.oinst = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[Foo]") [assignment]
+ t.inst = None # E: Incompatible types in assignment (expression has type "None", variable has type "Foo") [assignment]
diff --git a/contrib/python/traitlets/py3/tests/utils/__init__.py b/contrib/python/traitlets/py3/tests/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/traitlets/py3/tests/utils/__init__.py
diff --git a/contrib/python/traitlets/py3/traitlets/utils/tests/test_bunch.py b/contrib/python/traitlets/py3/tests/utils/test_bunch.py
index 223124d7d5..223124d7d5 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/tests/test_bunch.py
+++ b/contrib/python/traitlets/py3/tests/utils/test_bunch.py
diff --git a/contrib/python/traitlets/py3/traitlets/utils/tests/test_decorators.py b/contrib/python/traitlets/py3/tests/utils/test_decorators.py
index 5410c20137..d6bf8414e5 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/tests/test_decorators.py
+++ b/contrib/python/traitlets/py3/tests/utils/test_decorators.py
@@ -1,7 +1,7 @@
from inspect import Parameter, signature
from unittest import TestCase
-from traitlets.traitlets import HasTraits, Int, Unicode
+from traitlets import HasTraits, Int, Unicode
from traitlets.utils.decorators import signature_has_traits
diff --git a/contrib/python/traitlets/py3/traitlets/utils/tests/test_importstring.py b/contrib/python/traitlets/py3/tests/utils/test_importstring.py
index 8ce28add41..8ce28add41 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/tests/test_importstring.py
+++ b/contrib/python/traitlets/py3/tests/utils/test_importstring.py
diff --git a/contrib/python/traitlets/py3/tests/ya.make b/contrib/python/traitlets/py3/tests/ya.make
index 6a5cd7cf46..6ffd29993d 100644
--- a/contrib/python/traitlets/py3/tests/ya.make
+++ b/contrib/python/traitlets/py3/tests/ya.make
@@ -1,20 +1,27 @@
PY3TEST()
PEERDIR(
+ contrib/python/argcomplete
contrib/python/traitlets
+ contrib/python/pytest-mock
)
-SRCDIR(contrib/python/traitlets/py3/traitlets)
-
TEST_SRCS(
- config/tests/test_application.py
- config/tests/test_configurable.py
- config/tests/test_loader.py
- tests/test_traitlets.py
- tests/test_traitlets_enum.py
- utils/tests/test_bunch.py
- utils/tests/test_decorators.py
- utils/tests/test_importstring.py
+ __init__.py
+ _warnings.py
+ config/__init__.py
+ config/test_application.py
+ config/test_argcomplete.py
+ config/test_configurable.py
+ config/test_loader.py
+ test_traitlets.py
+ test_traitlets_docstring.py
+ test_traitlets_enum.py
+ test_typing.py
+ utils/__init__.py
+ utils/test_bunch.py
+ utils/test_decorators.py
+ utils/test_importstring.py
)
NO_LINT()
diff --git a/contrib/python/traitlets/py3/traitlets/__init__.py b/contrib/python/traitlets/py3/traitlets/__init__.py
index 96ebe57f1b..2641c443e9 100644
--- a/contrib/python/traitlets/py3/traitlets/__init__.py
+++ b/contrib/python/traitlets/py3/traitlets/__init__.py
@@ -1,4 +1,6 @@
"""Traitlets Python configuration system"""
+import typing as _t
+
from . import traitlets
from ._version import __version__, version_info
from .traitlets import *
@@ -19,7 +21,7 @@ __all__ = [
class Sentinel(traitlets.Sentinel): # type:ignore[name-defined]
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args: _t.Any, **kwargs: _t.Any) -> None:
super().__init__(*args, **kwargs)
warn(
"""
diff --git a/contrib/python/traitlets/py3/traitlets/_version.py b/contrib/python/traitlets/py3/traitlets/_version.py
index d477cb8ce1..5fbb9a2599 100644
--- a/contrib/python/traitlets/py3/traitlets/_version.py
+++ b/contrib/python/traitlets/py3/traitlets/_version.py
@@ -5,7 +5,7 @@ import re
from typing import List
# Version string must appear intact for hatch versioning
-__version__ = "5.10.1"
+__version__ = "5.11.2"
# Build up version_info tuple for backwards compatibility
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
diff --git a/contrib/python/traitlets/py3/traitlets/config/application.py b/contrib/python/traitlets/py3/traitlets/config/application.py
index fb185f0ae5..5993b4c7f2 100644
--- a/contrib/python/traitlets/py3/traitlets/config/application.py
+++ b/contrib/python/traitlets/py3/traitlets/config/application.py
@@ -2,7 +2,7 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
-
+from __future__ import annotations
import functools
import json
@@ -17,7 +17,6 @@ from contextlib import suppress
from copy import deepcopy
from logging.config import dictConfig
from textwrap import dedent
-from typing import Any, Callable, TypeVar, cast
from traitlets.config.configurable import Configurable, SingletonConfigurable
from traitlets.config.loader import (
@@ -40,6 +39,7 @@ from traitlets.traitlets import (
observe,
observe_compat,
)
+from traitlets.utils.bunch import Bunch
from traitlets.utils.nested_update import nested_update
from traitlets.utils.text import indent, wrap_paragraphs
@@ -95,7 +95,11 @@ else:
IS_PYTHONW = sys.executable and sys.executable.endswith("pythonw.exe")
-T = TypeVar("T", bound=Callable[..., Any])
+T = t.TypeVar("T", bound=t.Callable[..., t.Any])
+AnyLogger = t.Union[logging.Logger, logging.LoggerAdapter]
+StrDict = t.Dict[str, t.Any]
+ArgvType = t.Optional[t.List[str]]
+ClassesType = t.List[t.Type[Configurable]]
def catch_config_error(method: T) -> T:
@@ -108,7 +112,7 @@ def catch_config_error(method: T) -> T:
"""
@functools.wraps(method)
- def inner(app, *args, **kwargs):
+ def inner(app: Application, *args: t.Any, **kwargs: t.Any) -> t.Any:
try:
return method(app, *args, **kwargs)
except (TraitError, ArgumentError) as e:
@@ -116,7 +120,7 @@ def catch_config_error(method: T) -> T:
app.log.debug("Config at the time: %s", app.config)
app.exit(1)
- return cast(T, inner)
+ return t.cast(T, inner)
class ApplicationError(Exception):
@@ -136,7 +140,7 @@ class LevelFormatter(logging.Formatter):
highlevel_limit = logging.WARN
highlevel_format = " %(levelname)s |"
- def format(self, record):
+ def format(self, record: logging.LogRecord) -> str:
if record.levelno >= self.highlevel_limit:
record.highlevel = self.highlevel_format % record.__dict__
else:
@@ -149,35 +153,29 @@ class Application(SingletonConfigurable):
# The name of the application, will usually match the name of the command
# line application
- name: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode("application")
+ name: str | Unicode[str, str | bytes] = Unicode("application")
# The description of the application that is printed at the beginning
# of the help.
- description: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
- "This is an application."
- )
+ description: str | Unicode[str, str | bytes] = Unicode("This is an application.")
# default section descriptions
- option_description: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
- option_description
- )
- keyvalue_description: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
- keyvalue_description
- )
- subcommand_description: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
- subcommand_description
- )
+ option_description: str | Unicode[str, str | bytes] = Unicode(option_description)
+ keyvalue_description: str | Unicode[str, str | bytes] = Unicode(keyvalue_description)
+ subcommand_description: str | Unicode[str, str | bytes] = Unicode(subcommand_description)
python_config_loader_class = PyFileConfigLoader
json_config_loader_class = JSONFileConfigLoader
# The usage and example string that goes at the end of the help string.
- examples: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode()
+ examples: str | Unicode[str, str | bytes] = Unicode()
# A sequence of Configurable subclasses whose config=True attributes will
# be exposed at the command line.
- classes: t.List[t.Type[t.Any]] = []
+ classes: ClassesType = []
- def _classes_inc_parents(self, classes=None):
+ def _classes_inc_parents(
+ self, classes: ClassesType | None = None
+ ) -> t.Generator[type[Configurable], None, None]:
"""Iterate through configurable classes, including configurable parents
:param classes:
@@ -198,18 +196,16 @@ class Application(SingletonConfigurable):
yield parent
# The version string of this application.
- version: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode("0.0")
+ version: str | Unicode[str, str | bytes] = Unicode("0.0")
# the argv used to initialize the application
- argv: t.Union[t.List[str], List] = List()
+ argv = List()
# Whether failing to load config files should prevent startup
- raise_config_file_errors: t.Union[bool, Bool[bool, t.Union[bool, int]]] = Bool(
- TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR
- )
+ raise_config_file_errors = Bool(TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR)
# The log level for the application
- log_level: t.Union[str, int, Enum[t.Any, t.Any]] = Enum(
+ log_level = Enum(
(0, 10, 20, 30, 40, 50, "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"),
default_value=logging.WARN,
help="Set the log level by value or name.",
@@ -217,16 +213,16 @@ class Application(SingletonConfigurable):
_log_formatter_cls = LevelFormatter
- log_datefmt: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
+ log_datefmt = Unicode(
"%Y-%m-%d %H:%M:%S", help="The date format used by logging formatters for %(asctime)s"
).tag(config=True)
- log_format: t.Union[str, Unicode[str, t.Union[str, bytes]]] = Unicode(
+ log_format = Unicode(
"[%(name)s]%(highlevel)s %(message)s",
help="The Logging format template",
).tag(config=True)
- def get_default_logging_config(self):
+ def get_default_logging_config(self) -> StrDict:
"""Return the base logging configuration.
The default is to log to stderr using a StreamHandler, if no default
@@ -239,7 +235,7 @@ class Application(SingletonConfigurable):
control of logging.
"""
- config: t.Dict[str, t.Any] = {
+ config: StrDict = {
"version": 1,
"handlers": {
"console": {
@@ -278,7 +274,7 @@ class Application(SingletonConfigurable):
return config
@observe("log_datefmt", "log_format", "log_level", "logging_config")
- def _observe_logging_change(self, change):
+ def _observe_logging_change(self, change: Bunch) -> None:
# convert log level strings to ints
log_level = self.log_level
if isinstance(log_level, str):
@@ -286,10 +282,10 @@ class Application(SingletonConfigurable):
self._configure_logging()
@observe("log", type="default")
- def _observe_logging_default(self, change):
+ def _observe_logging_default(self, change: Bunch) -> None:
self._configure_logging()
- def _configure_logging(self):
+ def _configure_logging(self) -> None:
config = self.get_default_logging_config()
nested_update(config, self.logging_config or {})
dictConfig(config)
@@ -297,7 +293,7 @@ class Application(SingletonConfigurable):
self._logging_configured = True
@default("log")
- def _log_default(self):
+ def _log_default(self) -> AnyLogger:
"""Start logging for this application."""
log = logging.getLogger(self.__class__.__name__)
log.propagate = False
@@ -366,17 +362,13 @@ class Application(SingletonConfigurable):
#: Values might be like "Class.trait" strings of two-tuples: (Class.trait, help-text),
# or just the "Class.trait" string, in which case the help text is inferred from the
# corresponding trait
- aliases: t.Dict[t.Union[str, t.Tuple[str, ...]], t.Union[str, t.Tuple[str, str]]] = {
- "log-level": "Application.log_level"
- }
+ aliases: StrDict = {"log-level": "Application.log_level"}
# flags for loading Configurables or store_const style flags
# flags are loaded from this dict by '--key' flags
# this must be a dict of two-tuples, the first element being the Config/dict
# and the second being the help string for the flag
- flags: t.Dict[
- t.Union[str, t.Tuple[str, ...]], t.Tuple[t.Union[t.Dict[str, t.Any], Config], str]
- ] = {
+ flags: StrDict = {
"debug": (
{
"Application": {
@@ -408,12 +400,12 @@ class Application(SingletonConfigurable):
# this must be a dict of two-tuples,
# the first element being the application class/import string
# and the second being the help string for the subcommand
- subcommands: t.Union[t.Dict[str, t.Tuple[t.Any, str]], Dict] = Dict()
+ subcommands: dict[str, t.Any] | Dict = Dict()
# parse_command_line will initialize a subapp, if requested
subapp = Instance("traitlets.config.application.Application", allow_none=True)
# extra command-line arguments that don't set config values
- extra_args: t.Union[t.List[str], List] = List(Unicode())
+ extra_args = List(Unicode())
cli_config = Instance(
Config,
@@ -428,20 +420,20 @@ class Application(SingletonConfigurable):
_loaded_config_files = List()
- show_config: t.Union[bool, Bool[bool, t.Union[bool, int]]] = Bool(
+ show_config = Bool(
help="Instead of starting the Application, dump configuration to stdout"
).tag(config=True)
- show_config_json: t.Union[bool, Bool[bool, t.Union[bool, int]]] = Bool(
+ show_config_json = Bool(
help="Instead of starting the Application, dump configuration to stdout (as JSON)"
).tag(config=True)
@observe("show_config_json")
- def _show_config_json_changed(self, change):
+ def _show_config_json_changed(self, change: Bunch) -> None:
self.show_config = change.new
@observe("show_config")
- def _show_config_changed(self, change):
+ def _show_config_changed(self, change: Bunch) -> None:
if change.new:
self._save_start = self.start
self.start = self.start_show_config # type:ignore[method-assign]
@@ -460,27 +452,28 @@ class Application(SingletonConfigurable):
@observe("config")
@observe_compat
- def _config_changed(self, change):
+ def _config_changed(self, change: Bunch) -> None:
super()._config_changed(change)
self.log.debug("Config changed: %r", change.new)
@catch_config_error
- def initialize(self, argv=None):
+ def initialize(self, argv: ArgvType = None) -> None:
"""Do the basic steps to configure me.
Override in subclasses.
"""
self.parse_command_line(argv)
- def start(self):
+ def start(self) -> None:
"""Start the app mainloop.
Override in subclasses.
"""
if self.subapp is not None:
+ assert isinstance(self.subapp, Application)
return self.subapp.start()
- def start_show_config(self):
+ def start_show_config(self) -> None:
"""start function used when show_config is True"""
config = self.config.copy()
# exclude show_config flags from displayed config
@@ -507,28 +500,28 @@ class Application(SingletonConfigurable):
if not class_config:
continue
print(classname)
- pformat_kwargs: t.Dict[str, t.Any] = dict(indent=4, compact=True)
+ pformat_kwargs: StrDict = dict(indent=4, compact=True)
for traitname in sorted(class_config):
value = class_config[traitname]
print(f" .{traitname} = {pprint.pformat(value, **pformat_kwargs)}")
- def print_alias_help(self):
+ def print_alias_help(self) -> None:
"""Print the alias parts of the help."""
print("\n".join(self.emit_alias_help()))
- def emit_alias_help(self):
+ def emit_alias_help(self) -> t.Generator[str, None, None]:
"""Yield the lines for alias part of the help."""
if not self.aliases:
return
- classdict = {}
+ classdict: dict[str, type[Configurable]] = {}
for cls in self.classes:
# include all parents (up to, but excluding Configurable) in available names
for c in cls.mro()[:-3]:
- classdict[c.__name__] = c
+ classdict[c.__name__] = t.cast(t.Type[Configurable], c)
- fhelp: t.Optional[str]
+ fhelp: str | None
for alias, longname in self.aliases.items():
try:
if isinstance(longname, tuple):
@@ -540,27 +533,26 @@ class Application(SingletonConfigurable):
cls = classdict[classname]
trait = cls.class_traits(config=True)[traitname]
- fhelp = cls.class_get_trait_help(trait, helptext=fhelp).splitlines()
+ fhelp_lines = cls.class_get_trait_help(trait, helptext=fhelp).splitlines()
if not isinstance(alias, tuple):
- alias = (alias,)
+ alias = (alias,) # type:ignore[assignment]
alias = sorted(alias, key=len) # type:ignore[assignment]
alias = ", ".join(("--%s" if len(m) > 1 else "-%s") % m for m in alias)
# reformat first line
- assert fhelp is not None
- fhelp[0] = fhelp[0].replace("--" + longname, alias) # type:ignore
- yield from fhelp
+ fhelp_lines[0] = fhelp_lines[0].replace("--" + longname, alias)
+ yield from fhelp_lines
yield indent("Equivalent to: [--%s]" % longname)
except Exception as ex:
self.log.error("Failed collecting help-message for alias %r, due to: %s", alias, ex)
raise
- def print_flag_help(self):
+ def print_flag_help(self) -> None:
"""Print the flag part of the help."""
print("\n".join(self.emit_flag_help()))
- def emit_flag_help(self):
+ def emit_flag_help(self) -> t.Generator[str, None, None]:
"""Yield the lines for the flag part of the help."""
if not self.flags:
return
@@ -568,7 +560,7 @@ class Application(SingletonConfigurable):
for flags, (cfg, fhelp) in self.flags.items():
try:
if not isinstance(flags, tuple):
- flags = (flags,)
+ flags = (flags,) # type:ignore[assignment]
flags = sorted(flags, key=len) # type:ignore[assignment]
flags = ", ".join(("--%s" if len(m) > 1 else "-%s") % m for m in flags)
yield flags
@@ -584,11 +576,11 @@ class Application(SingletonConfigurable):
self.log.error("Failed collecting help-message for flag %r, due to: %s", flags, ex)
raise
- def print_options(self):
+ def print_options(self) -> None:
"""Print the options part of the help."""
print("\n".join(self.emit_options_help()))
- def emit_options_help(self):
+ def emit_options_help(self) -> t.Generator[str, None, None]:
"""Yield the lines for the options part of the help."""
if not self.flags and not self.aliases:
return
@@ -603,11 +595,11 @@ class Application(SingletonConfigurable):
yield from self.emit_alias_help()
yield ""
- def print_subcommands(self):
+ def print_subcommands(self) -> None:
"""Print the subcommand part of the help."""
print("\n".join(self.emit_subcommands_help()))
- def emit_subcommands_help(self):
+ def emit_subcommands_help(self) -> t.Generator[str, None, None]:
"""Yield the lines for the subcommand part of the help."""
if not self.subcommands:
return
@@ -624,7 +616,7 @@ class Application(SingletonConfigurable):
yield indent(dedent(help.strip()))
yield ""
- def emit_help_epilogue(self, classes):
+ def emit_help_epilogue(self, classes: bool) -> t.Generator[str, None, None]:
"""Yield the very bottom lines of the help message.
If classes=False (the default), print `--help-all` msg.
@@ -633,14 +625,14 @@ class Application(SingletonConfigurable):
yield "To see all available configurables, use `--help-all`."
yield ""
- def print_help(self, classes=False):
+ def print_help(self, classes: bool = False) -> None:
"""Print the help for each Configurable class in self.classes.
If classes=False (the default), only flags and aliases are printed.
"""
print("\n".join(self.emit_help(classes=classes)))
- def emit_help(self, classes=False):
+ def emit_help(self, classes: bool = False) -> t.Generator[str, None, None]:
"""Yield the help-lines for each Configurable class in self.classes.
If classes=False (the default), only flags and aliases are printed.
@@ -665,28 +657,28 @@ class Application(SingletonConfigurable):
yield from self.emit_help_epilogue(classes)
- def document_config_options(self):
+ def document_config_options(self) -> str:
"""Generate rST format documentation for the config options this application
Returns a multiline string.
"""
return "\n".join(c.class_config_rst_doc() for c in self._classes_inc_parents())
- def print_description(self):
+ def print_description(self) -> None:
"""Print the application description."""
print("\n".join(self.emit_description()))
- def emit_description(self):
+ def emit_description(self) -> t.Generator[str, None, None]:
"""Yield lines with the application description."""
for p in wrap_paragraphs(self.description or self.__doc__ or ""):
yield p
yield ""
- def print_examples(self):
+ def print_examples(self) -> None:
"""Print usage and examples (see `emit_examples()`)."""
print("\n".join(self.emit_examples()))
- def emit_examples(self):
+ def emit_examples(self) -> t.Generator[str, None, None]:
"""Yield lines with the usage and examples.
This usage string goes at the end of the command line help string
@@ -699,12 +691,12 @@ class Application(SingletonConfigurable):
yield indent(dedent(self.examples.strip()))
yield ""
- def print_version(self):
+ def print_version(self) -> None:
"""Print the version string."""
print(self.version)
@catch_config_error
- def initialize_subcommand(self, subc, argv=None):
+ def initialize_subcommand(self, subc: str, argv: ArgvType = None) -> None:
"""Initialize a subcommand with argv."""
val = self.subcommands.get(subc)
assert val is not None
@@ -726,9 +718,9 @@ class Application(SingletonConfigurable):
raise AssertionError("Invalid mappings for subcommand '%s'!" % subc)
# ... and finally initialize subapp.
- self.subapp.initialize(argv)
+ self.subapp.initialize(argv) # type:ignore[union-attr]
- def flatten_flags(self):
+ def flatten_flags(self) -> tuple[dict[str, t.Any], dict[str, t.Any]]:
"""Flatten flags and aliases for loaders, so cl-args override as expected.
This prevents issues such as an alias pointing to InteractiveShell,
@@ -751,11 +743,11 @@ class Application(SingletonConfigurable):
mro_tree[parent.__name__].append(clsname)
# flatten aliases, which have the form:
# { 'alias' : 'Class.trait' }
- aliases: t.Dict[str, str] = {}
+ aliases: dict[str, str] = {}
for alias, longname in self.aliases.items():
if isinstance(longname, tuple):
longname, _ = longname
- cls, trait = longname.split(".", 1) # type:ignore
+ cls, trait = longname.split(".", 1)
children = mro_tree[cls] # type:ignore[index]
if len(children) == 1:
# exactly one descendent, promote alias
@@ -769,8 +761,8 @@ class Application(SingletonConfigurable):
# { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
flags = {}
for key, (flagdict, help) in self.flags.items():
- newflag: t.Dict[t.Any, t.Any] = {}
- for cls, subdict in flagdict.items(): # type:ignore
+ newflag: dict[t.Any, t.Any] = {}
+ for cls, subdict in flagdict.items():
children = mro_tree[cls] # type:ignore[index]
# exactly one descendent, promote flag section
if len(children) == 1:
@@ -782,18 +774,24 @@ class Application(SingletonConfigurable):
newflag[cls] = subdict
if not isinstance(key, tuple):
- key = (key,)
+ key = (key,) # type:ignore[assignment]
for k in key:
flags[k] = (newflag, help)
return flags, aliases
- def _create_loader(self, argv, aliases, flags, classes):
+ def _create_loader(
+ self,
+ argv: list[str] | None,
+ aliases: StrDict,
+ flags: StrDict,
+ classes: ClassesType | None,
+ ) -> KVArgParseConfigLoader:
return KVArgParseConfigLoader(
argv, aliases, flags, classes=classes, log=self.log, subcommands=self.subcommands
)
@classmethod
- def _get_sys_argv(cls, check_argcomplete: bool = False) -> t.List[str]:
+ def _get_sys_argv(cls, check_argcomplete: bool = False) -> list[str]:
"""Get `sys.argv` or equivalent from `argcomplete`
`argcomplete`'s strategy is to call the python script with no arguments,
@@ -819,7 +817,7 @@ class Application(SingletonConfigurable):
return sys.argv
@classmethod
- def _handle_argcomplete_for_subcommand(cls):
+ def _handle_argcomplete_for_subcommand(cls) -> None:
"""Helper for `argcomplete` to recognize `traitlets` subcommands
`argcomplete` does not know that `traitlets` has already consumed subcommands,
@@ -839,7 +837,7 @@ class Application(SingletonConfigurable):
pass
@catch_config_error
- def parse_command_line(self, argv=None):
+ def parse_command_line(self, argv: ArgvType = None) -> None:
"""Parse the command line arguments."""
assert not isinstance(argv, str)
if argv is None:
@@ -877,7 +875,7 @@ class Application(SingletonConfigurable):
# flatten flags&aliases, so cl-args get appropriate priority:
flags, aliases = self.flatten_flags()
- classes = tuple(self._classes_with_config_traits())
+ classes = list(self._classes_with_config_traits())
loader = self._create_loader(argv, aliases, flags, classes=classes)
try:
self.cli_config = deepcopy(loader.load_config())
@@ -890,13 +888,18 @@ class Application(SingletonConfigurable):
self.extra_args = loader.extra_args
@classmethod
- def _load_config_files(cls, basefilename, path=None, log=None, raise_config_file_errors=False):
+ def _load_config_files(
+ cls,
+ basefilename: str,
+ path: str | t.Sequence[str | None] | None,
+ log: AnyLogger | None = None,
+ raise_config_file_errors: bool = False,
+ ) -> t.Generator[t.Any, None, None]:
"""Load config files (py,json) by filename and path.
yield each config object in turn.
"""
-
- if not isinstance(path, list):
+ if isinstance(path, str) or path is None:
path = [path]
for current in reversed(path):
# path list is in descending priority order, so load files backwards:
@@ -904,8 +907,8 @@ class Application(SingletonConfigurable):
if log:
log.debug("Looking for %s in %s", basefilename, current or os.getcwd())
jsonloader = cls.json_config_loader_class(basefilename + ".json", path=current, log=log)
- loaded: t.List[t.Any] = []
- filenames: t.List[str] = []
+ loaded: list[t.Any] = []
+ filenames: list[str] = []
for loader in [pyloader, jsonloader]:
config = None
try:
@@ -941,12 +944,14 @@ class Application(SingletonConfigurable):
filenames.append(loader.full_filename)
@property
- def loaded_config_files(self):
+ def loaded_config_files(self) -> list[str]:
"""Currently loaded configuration files"""
return self._loaded_config_files[:]
@catch_config_error
- def load_config_file(self, filename, path=None):
+ def load_config_file(
+ self, filename: str, path: str | t.Sequence[str | None] | None = None
+ ) -> None:
"""Load config files by filename and path."""
filename, ext = os.path.splitext(filename)
new_config = Config()
@@ -965,7 +970,9 @@ class Application(SingletonConfigurable):
new_config.merge(self.cli_config)
self.update_config(new_config)
- def _classes_with_config_traits(self, classes=None):
+ def _classes_with_config_traits(
+ self, classes: ClassesType | None = None
+ ) -> t.Generator[type[Configurable], None, None]:
"""
Yields only classes with configurable traits, and their subclasses.
@@ -987,7 +994,7 @@ class Application(SingletonConfigurable):
for cls in self._classes_inc_parents(classes)
)
- def is_any_parent_included(cls):
+ def is_any_parent_included(cls: t.Any) -> bool:
return any(b in cls_to_config and cls_to_config[b] for b in cls.__bases__)
# Mark "empty" classes for inclusion if their parents own-traits,
@@ -1005,7 +1012,7 @@ class Application(SingletonConfigurable):
if inc_yes:
yield cl
- def generate_config_file(self, classes=None):
+ def generate_config_file(self, classes: ClassesType | None = None) -> str:
"""generate default config file from Configurables"""
lines = ["# Configuration file for %s." % self.name]
lines.append("")
@@ -1017,7 +1024,7 @@ class Application(SingletonConfigurable):
lines.append(cls.class_config_section(config_classes))
return "\n".join(lines)
- def close_handlers(self):
+ def close_handlers(self) -> None:
if getattr(self, "_logging_configured", False):
# don't attempt to close handlers unless they have been opened
# (note accessing self.log.handlers will create handlers if they
@@ -1027,16 +1034,16 @@ class Application(SingletonConfigurable):
handler.close()
self._logging_configured = False
- def exit(self, exit_status=0):
+ def exit(self, exit_status: int | str | None = 0) -> None:
self.log.debug("Exiting application: %s" % self.name)
self.close_handlers()
sys.exit(exit_status)
- def __del__(self):
+ def __del__(self) -> None:
self.close_handlers()
@classmethod
- def launch_instance(cls, argv=None, **kwargs):
+ def launch_instance(cls, argv: ArgvType = None, **kwargs: t.Any) -> None:
"""Launch a global instance of this Application
If a global instance already exists, this reinitializes and starts it
@@ -1054,7 +1061,7 @@ default_aliases = Application.aliases
default_flags = Application.flags
-def boolean_flag(name, configurable, set_help="", unset_help=""):
+def boolean_flag(name: str, configurable: str, set_help: str = "", unset_help: str = "") -> StrDict:
"""Helper for building basic --trait, --no-trait flags.
Parameters
@@ -1085,7 +1092,7 @@ def boolean_flag(name, configurable, set_help="", unset_help=""):
return {name: (setter, set_help), "no-" + name: (unsetter, unset_help)}
-def get_config():
+def get_config() -> Config:
"""Get the config object for the global Application instance, if there is one
otherwise return an empty config object
diff --git a/contrib/python/traitlets/py3/traitlets/config/argcomplete_config.py b/contrib/python/traitlets/py3/traitlets/config/argcomplete_config.py
index ee1e51b492..82112aaf6b 100644
--- a/contrib/python/traitlets/py3/traitlets/config/argcomplete_config.py
+++ b/contrib/python/traitlets/py3/traitlets/config/argcomplete_config.py
@@ -15,7 +15,7 @@ except ImportError:
# This module and its utility methods are written to not crash even
# if argcomplete is not installed.
class StubModule:
- def __getattr__(self, attr):
+ def __getattr__(self, attr: str) -> t.Any:
if not attr.startswith("__"):
raise ModuleNotFoundError("No module named 'argcomplete'")
raise AttributeError(f"argcomplete stub module has no attribute '{attr}'")
@@ -63,7 +63,7 @@ def get_argcomplete_cwords() -> t.Optional[t.List[str]]:
return comp_words
-def increment_argcomplete_index():
+def increment_argcomplete_index() -> None:
"""Assumes ``$_ARGCOMPLETE`` is set and `argcomplete` is importable
Increment the index pointed to by ``$_ARGCOMPLETE``, which is used to
@@ -122,7 +122,7 @@ class ExtendedCompletionFinder(CompletionFinder):
]
return matched_completions
- def inject_class_to_parser(self, cls):
+ def inject_class_to_parser(self, cls: t.Any) -> None:
"""Add dummy arguments to our ArgumentParser for the traits of this class
The argparse-based loader currently does not actually add any class traits to
diff --git a/contrib/python/traitlets/py3/traitlets/config/configurable.py b/contrib/python/traitlets/py3/traitlets/config/configurable.py
index f448e696b8..77b4214e45 100644
--- a/contrib/python/traitlets/py3/traitlets/config/configurable.py
+++ b/contrib/python/traitlets/py3/traitlets/config/configurable.py
@@ -2,7 +2,7 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
-
+from __future__ import annotations
import logging
import typing as t
@@ -15,12 +15,14 @@ from traitlets.traitlets import (
Dict,
HasTraits,
Instance,
+ TraitType,
default,
observe,
observe_compat,
validate,
)
from traitlets.utils import warnings
+from traitlets.utils.bunch import Bunch
from traitlets.utils.text import indent, wrap_paragraphs
from .loader import Config, DeferredConfig, LazyConfigValue, _is_section_key
@@ -29,6 +31,11 @@ from .loader import Config, DeferredConfig, LazyConfigValue, _is_section_key
# Helper classes for Configurables
# -----------------------------------------------------------------------------
+if t.TYPE_CHECKING:
+ LoggerType = t.Union[logging.Logger, logging.LoggerAdapter[t.Any]]
+else:
+ LoggerType = t.Any
+
class ConfigurableError(Exception):
pass
@@ -87,7 +94,7 @@ class Configurable(HasTraits):
# record traits set by config
config_override_names = set()
- def notice_config_override(change):
+ def notice_config_override(change: Bunch) -> None:
"""Record traits set by both config and kwargs.
They will need to be overridden again after loading config.
@@ -120,7 +127,7 @@ class Configurable(HasTraits):
# -------------------------------------------------------------------------
@classmethod
- def section_names(cls):
+ def section_names(cls) -> list[str]:
"""return section names as a list"""
return [
c.__name__
@@ -128,7 +135,7 @@ class Configurable(HasTraits):
if issubclass(c, Configurable) and issubclass(cls, c)
]
- def _find_my_config(self, cfg):
+ def _find_my_config(self, cfg: Config) -> t.Any:
"""extract my config from a global Config object
will construct a Config object of only the config values that apply to me
@@ -153,7 +160,9 @@ class Configurable(HasTraits):
my_config.merge(c[sname])
return my_config
- def _load_config(self, cfg, section_names=None, traits=None):
+ def _load_config(
+ self, cfg: Config, section_names: list[str] | None = None, traits: list[str] | None = None
+ ) -> None:
"""load traits from a Config object"""
if traits is None:
@@ -187,7 +196,7 @@ class Configurable(HasTraits):
warn = self.log.warning
else:
- def warn(msg):
+ def warn(msg: t.Any) -> None:
return warnings.warn(msg, UserWarning, stacklevel=9)
matches = get_close_matches(name, traits)
@@ -203,7 +212,7 @@ class Configurable(HasTraits):
@observe("config")
@observe_compat
- def _config_changed(self, change):
+ def _config_changed(self, change: Bunch) -> None:
"""Update all the class traits having ``config=True`` in metadata.
For any class trait with a ``config`` metadata attribute that is
@@ -219,7 +228,7 @@ class Configurable(HasTraits):
section_names = self.section_names()
self._load_config(change.new, traits=traits, section_names=section_names)
- def update_config(self, config):
+ def update_config(self, config: Config) -> None:
"""Update config and load the new values"""
# traitlets prior to 4.2 created a copy of self.config in order to trigger change events.
# Some projects (IPython < 5) relied upon one side effect of this,
@@ -236,7 +245,7 @@ class Configurable(HasTraits):
# DO NOT trigger full trait-change
@classmethod
- def class_get_help(cls, inst=None):
+ def class_get_help(cls, inst: HasTraits | None = None) -> str:
"""Get the help string for this class in ReST format.
If `inst` is given, its current trait values will be used in place of
@@ -253,7 +262,12 @@ class Configurable(HasTraits):
return "\n".join(final_help)
@classmethod
- def class_get_trait_help(cls, trait, inst=None, helptext=None):
+ def class_get_trait_help(
+ cls,
+ trait: TraitType[t.Any, t.Any],
+ inst: HasTraits | None = None,
+ helptext: str | None = None,
+ ) -> str:
"""Get the helptext string for a single trait.
:param inst:
@@ -291,7 +305,7 @@ class Configurable(HasTraits):
lines.append(indent("Choices: %s" % trait.info()))
if inst is not None:
- lines.append(indent(f"Current: {getattr(inst, trait.name)!r}"))
+ lines.append(indent(f"Current: {getattr(inst, trait.name or '')!r}"))
else:
try:
dvr = trait.default_value_repr()
@@ -305,12 +319,14 @@ class Configurable(HasTraits):
return "\n".join(lines)
@classmethod
- def class_print_help(cls, inst=None):
+ def class_print_help(cls, inst: HasTraits | None = None) -> None:
"""Get the help string for a single trait and print it."""
print(cls.class_get_help(inst))
@classmethod
- def _defining_class(cls, trait, classes):
+ def _defining_class(
+ cls, trait: TraitType[t.Any, t.Any], classes: t.Sequence[type[HasTraits]]
+ ) -> type[Configurable]:
"""Get the class that defines a trait
For reducing redundant help output in config files.
@@ -338,7 +354,7 @@ class Configurable(HasTraits):
return defining_cls
@classmethod
- def class_config_section(cls, classes=None):
+ def class_config_section(cls, classes: t.Sequence[type[HasTraits]] | None = None) -> str:
"""Get the config section for this class.
Parameters
@@ -348,7 +364,7 @@ class Configurable(HasTraits):
Used to reduce redundant information.
"""
- def c(s):
+ def c(s: str) -> str:
"""return a commented, wrapped block."""
s = "\n\n".join(wrap_paragraphs(s, 78))
@@ -398,7 +414,7 @@ class Configurable(HasTraits):
return "\n".join(lines)
@classmethod
- def class_config_rst_doc(cls):
+ def class_config_rst_doc(cls) -> str:
"""Generate rST documentation for this class' config options.
Excludes traits defined on parent classes.
@@ -447,10 +463,10 @@ class LoggingConfigurable(Configurable):
is to get the logger from the currently running Application.
"""
- log = Any(help="Logger or LoggerAdapter instance")
+ log = Any(help="Logger or LoggerAdapter instance", allow_none=False)
@validate("log")
- def _validate_log(self, proposal):
+ def _validate_log(self, proposal: Bunch) -> LoggerType:
if not isinstance(proposal.value, (logging.Logger, logging.LoggerAdapter)):
# warn about unsupported type, but be lenient to allow for duck typing
warnings.warn(
@@ -459,18 +475,18 @@ class LoggingConfigurable(Configurable):
UserWarning,
stacklevel=2,
)
- return proposal.value
+ return proposal.value # type:ignore[no-any-return]
@default("log")
- def _log_default(self):
+ def _log_default(self) -> LoggerType:
if isinstance(self.parent, LoggingConfigurable):
assert self.parent is not None
- return self.parent.log
+ return t.cast(logging.Logger, self.parent.log)
from traitlets import log
return log.get_logger()
- def _get_log_handler(self):
+ def _get_log_handler(self) -> logging.Handler | None:
"""Return the default Handler
Returns None if none can be found
@@ -478,13 +494,16 @@ class LoggingConfigurable(Configurable):
Deprecated, this now returns the first log handler which may or may
not be the default one.
"""
- logger = self.log
- if isinstance(logger, logging.LoggerAdapter):
- logger = logger.logger
+ if not self.log:
+ return None
+ logger = self.log if isinstance(self.log, logging.Logger) else self.log.logger
if not getattr(logger, "handlers", None):
# no handlers attribute or empty handlers list
return None
- return logger.handlers[0]
+ return logger.handlers[0] # type:ignore[no-any-return]
+
+
+CT = t.TypeVar('CT', bound='SingletonConfigurable')
class SingletonConfigurable(LoggingConfigurable):
@@ -498,7 +517,7 @@ class SingletonConfigurable(LoggingConfigurable):
_instance = None
@classmethod
- def _walk_mro(cls):
+ def _walk_mro(cls) -> t.Generator[type[SingletonConfigurable], None, None]:
"""Walk the cls.mro() for parent classes that are also singletons
For use in instance()
@@ -513,7 +532,7 @@ class SingletonConfigurable(LoggingConfigurable):
yield subclass
@classmethod
- def clear_instance(cls):
+ def clear_instance(cls) -> None:
"""unset _instance for this class and singleton parents."""
if not cls.initialized():
return
@@ -524,7 +543,7 @@ class SingletonConfigurable(LoggingConfigurable):
subclass._instance = None
@classmethod
- def instance(cls, *args, **kwargs):
+ def instance(cls: type[CT], *args: t.Any, **kwargs: t.Any) -> CT:
"""Returns a global instance of this class.
This method create a new instance if none have previously been created
@@ -568,6 +587,6 @@ class SingletonConfigurable(LoggingConfigurable):
)
@classmethod
- def initialized(cls):
+ def initialized(cls) -> bool:
"""Has an instance been created?"""
return hasattr(cls, "_instance") and cls._instance is not None
diff --git a/contrib/python/traitlets/py3/traitlets/config/loader.py b/contrib/python/traitlets/py3/traitlets/config/loader.py
index 34d62e5a50..437c8c17cd 100644
--- a/contrib/python/traitlets/py3/traitlets/config/loader.py
+++ b/contrib/python/traitlets/py3/traitlets/config/loader.py
@@ -2,6 +2,7 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
+# ruff: noqa: ANN201, ANN001, ANN204, ANN102, ANN003, ANN206, ANN002
from __future__ import annotations
import argparse
@@ -50,10 +51,10 @@ class ArgumentError(ConfigLoaderError):
class _Sentinel:
- def __repr__(self):
+ def __repr__(self) -> str:
return "<Sentinel deprecated>"
- def __str__(self):
+ def __str__(self) -> str:
return "<deprecated>"
@@ -208,7 +209,7 @@ class LazyConfigValue(HasTraits):
d["inserts"] = self._inserts
return d
- def __repr__(self):
+ def __repr__(self) -> str:
if self._value is not None:
return f"<{self.__class__.__name__} value={self._value!r}>"
else:
@@ -294,7 +295,7 @@ class Config(dict): # type:ignore[type-arg]
collisions[section][key] = f"{mine[key]!r} ignored, using {theirs[key]!r}"
return collisions
- def __contains__(self, key):
+ def __contains__(self, key: t.Any) -> bool:
# allow nested contains of the form `"Section.key" in config`
if "." in key:
first, remainder = key.split(".", 1)
@@ -344,7 +345,7 @@ class Config(dict): # type:ignore[type-arg]
else:
raise
- def __setitem__(self, key, value):
+ def __setitem__(self, key: str, value: t.Any) -> None:
if _is_section_key(key):
if not isinstance(value, Config):
raise ValueError(
@@ -361,7 +362,7 @@ class Config(dict): # type:ignore[type-arg]
except KeyError as e:
raise AttributeError(e) from e
- def __setattr__(self, key, value):
+ def __setattr__(self, key: str, value: t.Any) -> None:
if key.startswith("__"):
return dict.__setattr__(self, key, value)
try:
@@ -369,7 +370,7 @@ class Config(dict): # type:ignore[type-arg]
except KeyError as e:
raise AttributeError(e) from e
- def __delattr__(self, key):
+ def __delattr__(self, key: str) -> None:
if key.startswith("__"):
return dict.__delattr__(self, key)
try:
@@ -420,7 +421,7 @@ class DeferredConfigString(str, DeferredConfig):
# this will raise a more informative error when config is loaded.
return s
- def __repr__(self):
+ def __repr__(self) -> str:
return f"{self.__class__.__name__}({self._super_repr()})"
@@ -462,7 +463,7 @@ class DeferredConfigList(list, DeferredConfig): # type:ignore[type-arg]
# this will raise a more informative error when config is loaded.
return src
- def __repr__(self):
+ def __repr__(self) -> str:
return f"{self.__class__.__name__}({self._super_repr()})"
@@ -749,7 +750,7 @@ class _DefaultOptionDict(dict): # type:ignore[type-arg]
metavar=key.lstrip("-"),
)
- def __contains__(self, key):
+ def __contains__(self, key: t.Any) -> bool:
if "=" in key:
return False
if super().__contains__(key):
@@ -785,7 +786,6 @@ class _KVArgParser(argparse.ArgumentParser):
# type aliases
-Flags = t.Union[str, t.Tuple[str, ...]]
SubcommandsDict = t.Dict[str, t.Any]
@@ -797,8 +797,8 @@ class ArgParseConfigLoader(CommandLineConfigLoader):
def __init__(
self,
argv: list[str] | None = None,
- aliases: dict[Flags, str] | None = None,
- flags: dict[Flags, str] | None = None,
+ aliases: dict[str, str] | None = None,
+ flags: dict[str, str] | None = None,
log: t.Any = None,
classes: list[type[t.Any]] | None = None,
subcommands: SubcommandsDict | None = None,
@@ -915,7 +915,7 @@ class ArgParseConfigLoader(CommandLineConfigLoader):
if alias in self.flags:
continue
if not isinstance(alias, tuple):
- alias = (alias,)
+ alias = (alias,) # type:ignore[assignment]
for al in alias:
if len(al) == 1:
unpacked_aliases["-" + al] = "--" + alias_target
diff --git a/contrib/python/traitlets/py3/traitlets/config/manager.py b/contrib/python/traitlets/py3/traitlets/config/manager.py
index 728cd2f22c..9102544e50 100644
--- a/contrib/python/traitlets/py3/traitlets/config/manager.py
+++ b/contrib/python/traitlets/py3/traitlets/config/manager.py
@@ -2,15 +2,18 @@
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
import errno
import json
import os
+from typing import Any
from traitlets.config import LoggingConfigurable
from traitlets.traitlets import Unicode
-def recursive_update(target, new):
+def recursive_update(target: dict[Any, Any], new: dict[Any, Any]) -> None:
"""Recursively update one dictionary using another.
None values will delete their keys.
@@ -39,17 +42,17 @@ class BaseJSONConfigManager(LoggingConfigurable):
config_dir = Unicode(".")
- def ensure_config_dir_exists(self):
+ def ensure_config_dir_exists(self) -> None:
try:
os.makedirs(self.config_dir, 0o755)
except OSError as e:
if e.errno != errno.EEXIST:
raise
- def file_name(self, section_name):
+ def file_name(self, section_name: str) -> str:
return os.path.join(self.config_dir, section_name + ".json")
- def get(self, section_name):
+ def get(self, section_name: str) -> Any:
"""Retrieve the config data for the specified section.
Returns the data as a dictionary, or an empty dictionary if the file
@@ -62,7 +65,7 @@ class BaseJSONConfigManager(LoggingConfigurable):
else:
return {}
- def set(self, section_name, data):
+ def set(self, section_name: str, data: Any) -> None:
"""Store the given config data."""
filename = self.file_name(section_name)
self.ensure_config_dir_exists()
@@ -71,7 +74,7 @@ class BaseJSONConfigManager(LoggingConfigurable):
with f:
json.dump(data, f, indent=2)
- def update(self, section_name, new_data):
+ def update(self, section_name: str, new_data: Any) -> Any:
"""Modify the config section by recursively updating it with new_data.
Returns the modified config data as a dictionary.
diff --git a/contrib/python/traitlets/py3/traitlets/config/sphinxdoc.py b/contrib/python/traitlets/py3/traitlets/config/sphinxdoc.py
index a69d89f9e4..300c0a0b03 100644
--- a/contrib/python/traitlets/py3/traitlets/config/sphinxdoc.py
+++ b/contrib/python/traitlets/py3/traitlets/config/sphinxdoc.py
@@ -32,6 +32,7 @@ The generated rST syntax looks like this::
Cross reference like this: :configtrait:`Application.log_datefmt`.
"""
+# ruff: noqa: ANN201, ANN001, ANN204, ANN102, ANN003, ANN206, ANN002
from collections import defaultdict
from textwrap import dedent
diff --git a/contrib/python/traitlets/py3/traitlets/log.py b/contrib/python/traitlets/py3/traitlets/log.py
index 468c7c3c24..d90a9c5284 100644
--- a/contrib/python/traitlets/py3/traitlets/log.py
+++ b/contrib/python/traitlets/py3/traitlets/log.py
@@ -5,11 +5,12 @@
from __future__ import annotations
import logging
+from typing import Any
-_logger: logging.Logger | None = None
+_logger: logging.Logger | logging.LoggerAdapter[Any] | None = None
-def get_logger() -> logging.Logger:
+def get_logger() -> logging.Logger | logging.LoggerAdapter[Any]:
"""Grab the global logger instance.
If a global Application is instantiated, grab its logger.
diff --git a/contrib/python/traitlets/py3/traitlets/tests/utils.py b/contrib/python/traitlets/py3/traitlets/tests/utils.py
index c39d86942a..9552a5c786 100644
--- a/contrib/python/traitlets/py3/traitlets/tests/utils.py
+++ b/contrib/python/traitlets/py3/traitlets/tests/utils.py
@@ -1,20 +1,23 @@
+from __future__ import annotations
+
import sys
from subprocess import PIPE, Popen
-import os
+from typing import Any, Sequence
-def get_output_error_code(cmd):
+def get_output_error_code(cmd: str | Sequence[str]) -> tuple[str, str, Any]:
"""Get stdout, stderr, and exit code from running a command"""
+ import os
env = os.environ.copy()
env["Y_PYTHON_ENTRY_POINT"] = ":main"
p = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) # noqa
out, err = p.communicate()
- out = out.decode("utf8", "replace") # type:ignore
- err = err.decode("utf8", "replace") # type:ignore
- return out, err, p.returncode
+ out_str = out.decode("utf8", "replace")
+ err_str = err.decode("utf8", "replace")
+ return out_str, err_str, p.returncode
-def check_help_output(pkg, subcommand=None):
+def check_help_output(pkg: str, subcommand: Sequence[str] | None = None) -> tuple[str, str]:
"""test that `python -m PKG [subcommand] -h` works"""
cmd = [sys.executable, "-m", pkg]
if subcommand:
@@ -28,7 +31,7 @@ def check_help_output(pkg, subcommand=None):
return out, err
-def check_help_all_output(pkg, subcommand=None):
+def check_help_all_output(pkg: str, subcommand: Sequence[str] | None = None) -> tuple[str, str]:
"""test that `python -m PKG --help-all` works"""
cmd = [sys.executable, "-m", pkg]
if subcommand:
diff --git a/contrib/python/traitlets/py3/traitlets/traitlets.py b/contrib/python/traitlets/py3/traitlets/traitlets.py
index 036f51aac1..50d6face52 100644
--- a/contrib/python/traitlets/py3/traitlets/traitlets.py
+++ b/contrib/python/traitlets/py3/traitlets/traitlets.py
@@ -38,6 +38,9 @@ Inheritance diagram:
#
# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
# also under the terms of the Modified BSD License.
+
+# ruff: noqa: ANN001, ANN204, ANN201, ANN003, ANN206, ANN002
+
from __future__ import annotations
import contextlib
@@ -213,16 +216,16 @@ def parse_notifier_name(names: Sentinel | str | t.Iterable[Sentinel | str]) -> t
class _SimpleTest:
- def __init__(self, value):
+ def __init__(self, value: t.Any) -> None:
self.value = value
- def __call__(self, test):
- return test == self.value
+ def __call__(self, test: t.Any) -> bool:
+ return bool(test == self.value)
- def __repr__(self):
+ def __repr__(self) -> str:
return "<SimpleTest(%r)" % self.value
- def __str__(self):
+ def __str__(self) -> str:
return self.__repr__()
@@ -294,7 +297,7 @@ class link:
self.link()
- def link(self):
+ def link(self) -> None:
try:
setattr(
self.target[0],
@@ -334,7 +337,7 @@ class link:
f"Broken link {self}: the target value changed while updating " "the source."
)
- def unlink(self):
+ def unlink(self) -> None:
self.source[0].unobserve(self._update_target, names=self.source[1])
self.target[0].unobserve(self._update_source, names=self.target[1])
@@ -378,7 +381,7 @@ class directional_link:
self.source, self.target = source, target
self.link()
- def link(self):
+ def link(self) -> None:
try:
setattr(
self.target[0],
@@ -402,7 +405,7 @@ class directional_link:
with self._busy_updating():
setattr(self.target[0], self.target[1], self._transform(change.new))
- def unlink(self):
+ def unlink(self) -> None:
self.source[0].unobserve(self._update, names=self.source[1])
@@ -1123,7 +1126,7 @@ def observe(*names: Sentinel | str, type: str = "change") -> ObserveHandler:
return ObserveHandler(names, type=type)
-def observe_compat(func):
+def observe_compat(func: FuncT) -> FuncT:
"""Backward-compatibility shim decorator for observers
Use with:
@@ -1137,9 +1140,11 @@ def observe_compat(func):
Allows adoption of new observer API without breaking subclasses that override and super.
"""
- def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
+ def compatible_observer(
+ self: t.Any, change_or_name: str, old: t.Any = Undefined, new: t.Any = Undefined
+ ) -> t.Any:
if isinstance(change_or_name, dict):
- change = change_or_name
+ change = Bunch(change_or_name)
else:
clsname = self.__class__.__name__
warn(
@@ -1156,7 +1161,7 @@ def observe_compat(func):
)
return func(self, change)
- return compatible_observer
+ return t.cast(FuncT, compatible_observer)
def validate(*names: Sentinel | str) -> ValidateHandler:
@@ -2027,7 +2032,7 @@ class Type(ClassBasedTraitType[G, S]):
@t.overload
def __init__(
- self: Type[object, object],
+ self: Type[type, type],
default_value: Sentinel | None | str = ...,
klass: None | str = ...,
allow_none: Literal[False] = ...,
@@ -2040,8 +2045,8 @@ class Type(ClassBasedTraitType[G, S]):
@t.overload
def __init__(
- self: Type[object | None, object | None],
- default_value: S | Sentinel | None | str = ...,
+ self: Type[type | None, type | None],
+ default_value: Sentinel | None | str = ...,
klass: None | str = ...,
allow_none: Literal[True] = ...,
read_only: bool | None = ...,
@@ -2054,7 +2059,7 @@ class Type(ClassBasedTraitType[G, S]):
@t.overload
def __init__(
self: Type[S, S],
- default_value: S | Sentinel | str = ...,
+ default_value: S = ...,
klass: type[S] = ...,
allow_none: Literal[False] = ...,
read_only: bool | None = ...,
@@ -2067,7 +2072,7 @@ class Type(ClassBasedTraitType[G, S]):
@t.overload
def __init__(
self: Type[S | None, S | None],
- default_value: S | Sentinel | None | str = ...,
+ default_value: S | None = ...,
klass: type[S] = ...,
allow_none: Literal[True] = ...,
read_only: bool | None = ...,
diff --git a/contrib/python/traitlets/py3/traitlets/utils/__init__.py b/contrib/python/traitlets/py3/traitlets/utils/__init__.py
index dfec4ee322..e8ee7f9856 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/__init__.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/__init__.py
@@ -1,15 +1,18 @@
+from __future__ import annotations
+
import os
import pathlib
+from typing import Sequence
# vestigal things from IPython_genutils.
-def cast_unicode(s, encoding="utf-8"):
+def cast_unicode(s: str | bytes, encoding: str = "utf-8") -> str:
if isinstance(s, bytes):
return s.decode(encoding, "replace")
return s
-def filefind(filename, path_dirs=None):
+def filefind(filename: str, path_dirs: Sequence[str] | None = None) -> str:
"""Find a file by looking through a sequence of paths.
This iterates through a sequence of paths looking for a file and returns
@@ -65,7 +68,7 @@ def filefind(filename, path_dirs=None):
raise OSError(f"File {filename!r} does not exist in any of the search paths: {path_dirs!r}")
-def expand_path(s):
+def expand_path(s: str) -> str:
"""Expand $VARS and ~names in a string, like a shell
:Examples:
diff --git a/contrib/python/traitlets/py3/traitlets/utils/bunch.py b/contrib/python/traitlets/py3/traitlets/utils/bunch.py
index 6b3fffeb12..498563e0b5 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/bunch.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/bunch.py
@@ -5,21 +5,24 @@ attribute-access of items on a dict.
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
+from __future__ import annotations
+
+from typing import Any
class Bunch(dict): # type:ignore[type-arg]
"""A dict with attribute-access"""
- def __getattr__(self, key):
+ def __getattr__(self, key: str) -> Any:
try:
return self.__getitem__(key)
except KeyError as e:
raise AttributeError(key) from e
- def __setattr__(self, key, value):
+ def __setattr__(self, key: str, value: Any) -> None:
self.__setitem__(key, value)
- def __dir__(self):
+ def __dir__(self) -> list[str]:
# py2-compat: can't use super because dict doesn't have __dir__
names = dir({})
names.extend(self.keys())
diff --git a/contrib/python/traitlets/py3/traitlets/utils/decorators.py b/contrib/python/traitlets/py3/traitlets/utils/decorators.py
index a59e8167b0..dedbaad193 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/decorators.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/decorators.py
@@ -2,12 +2,12 @@
import copy
from inspect import Parameter, Signature, signature
-from typing import Type, TypeVar
+from typing import Any, Type, TypeVar
from ..traitlets import HasTraits, Undefined
-def _get_default(value):
+def _get_default(value: Any) -> Any:
"""Get default argument value, given the trait default value."""
return Parameter.empty if value == Undefined else value
diff --git a/contrib/python/traitlets/py3/traitlets/utils/descriptions.py b/contrib/python/traitlets/py3/traitlets/utils/descriptions.py
index 232eb0e728..c068ecdba5 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/descriptions.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/descriptions.py
@@ -1,9 +1,18 @@
+from __future__ import annotations
+
import inspect
import re
import types
+from typing import Any
-def describe(article, value, name=None, verbose=False, capital=False):
+def describe(
+ article: str | None,
+ value: Any,
+ name: str | None = None,
+ verbose: bool = False,
+ capital: bool = False,
+) -> str:
"""Return string that describes a value
Parameters
@@ -110,7 +119,7 @@ def describe(article, value, name=None, verbose=False, capital=False):
)
-def _prefix(value):
+def _prefix(value: Any) -> str:
if isinstance(value, types.MethodType):
name = describe(None, value.__self__, verbose=True) + "."
else:
@@ -122,7 +131,7 @@ def _prefix(value):
return name
-def class_of(value):
+def class_of(value: Any) -> Any:
"""Returns a string of the value's type with an indefinite article.
For example 'an Image' or 'a PlotValue'.
@@ -133,7 +142,7 @@ def class_of(value):
return class_of(type(value))
-def add_article(name, definite=False, capital=False):
+def add_article(name: str, definite: bool = False, capital: bool = False) -> str:
"""Returns the string with a prepended article.
The input does not need to begin with a charater.
@@ -164,7 +173,7 @@ def add_article(name, definite=False, capital=False):
return result
-def repr_type(obj):
+def repr_type(obj: Any) -> str:
"""Return a string representation of a value and its type for readable
error messages.
diff --git a/contrib/python/traitlets/py3/traitlets/utils/getargspec.py b/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
index e2b1f235c8..7cbc82659c 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
@@ -7,14 +7,14 @@
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-
import inspect
from functools import partial
+from typing import Any
# Unmodified from sphinx below this line
-def getargspec(func):
+def getargspec(func: Any) -> inspect.FullArgSpec:
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
diff --git a/contrib/python/traitlets/py3/traitlets/utils/nested_update.py b/contrib/python/traitlets/py3/traitlets/utils/nested_update.py
index 7f09e171a3..37e2d27cd2 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/nested_update.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/nested_update.py
@@ -1,8 +1,9 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
+from typing import Any, Dict
-def nested_update(this, that):
+def nested_update(this: Dict[Any, Any], that: Dict[Any, Any]) -> Dict[Any, Any]:
"""Merge two nested dictionaries.
Effectively a recursive ``dict.update``.
diff --git a/contrib/python/traitlets/py3/traitlets/utils/text.py b/contrib/python/traitlets/py3/traitlets/utils/text.py
index c7d49edece..72ad98fc2a 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/text.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/text.py
@@ -9,7 +9,7 @@ from textwrap import indent as _indent
from typing import List
-def indent(val):
+def indent(val: str) -> str:
res = _indent(val, " ")
return res
diff --git a/contrib/python/traitlets/py3/ya.make b/contrib/python/traitlets/py3/ya.make
index 2d9cfeae1b..ff8ebca152 100644
--- a/contrib/python/traitlets/py3/ya.make
+++ b/contrib/python/traitlets/py3/ya.make
@@ -4,7 +4,7 @@ PY3_LIBRARY()
PROVIDES(python_traitlets)
-VERSION(5.10.1)
+VERSION(5.11.2)
LICENSE(BSD-3-Clause)
@@ -23,7 +23,6 @@ PY_SRCS(
traitlets/config/sphinxdoc.py
traitlets/log.py
traitlets/tests/__init__.py
- traitlets/tests/_warnings.py
traitlets/tests/utils.py
traitlets/traitlets.py
traitlets/utils/__init__.py