From d18b988a03fd41daa2ae012f6a4c52fa19f8e96a Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Wed, 21 Aug 2024 01:59:56 +0300 Subject: Intermediate changes --- contrib/python/hypothesis/py3/.dist-info/METADATA | 2 +- .../python/hypothesis/py3/hypothesis/internal/conjecture/engine.py | 5 ++++- contrib/python/hypothesis/py3/hypothesis/version.py | 2 +- contrib/python/hypothesis/py3/ya.make | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'contrib/python') diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 557900da698..fa7fb921112 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.108.8 +Version: 6.108.9 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py index 71f2ccc0b59..91f731cf44d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py @@ -999,7 +999,10 @@ class ConjectureRunner: self._current_phase = "generate" prefix = self.generate_novel_prefix() - assert len(prefix) <= BUFFER_SIZE + # it is possible, if unlikely, to generate a > BUFFER_SIZE novel prefix, + # as nodes in the novel tree may be variable sized due to eg integer + # probe retries. + prefix = prefix[:BUFFER_SIZE] if ( self.valid_examples <= small_example_cap and self.call_count <= 5 * small_example_cap diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index f800a2e0aae..d711a8c3832 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 108, 8) +__version_info__ = (6, 108, 9) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 65d6b4d7697..d5dcf568450 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.108.8) +VERSION(6.108.9) LICENSE(MPL-2.0) -- cgit v1.3 From 038539e9e87fc8aa077180e17bd90fa49c6cfe0b Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Wed, 21 Aug 2024 09:13:00 +0300 Subject: Intermediate changes --- contrib/python/hypothesis/py3/.dist-info/METADATA | 78 +++++++++++----------- .../py3/hypothesis/extra/django/_impl.py | 2 +- .../py3/hypothesis/internal/conjecture/data.py | 4 +- .../internal/conjecture/shrinking/collection.py | 5 +- .../internal/conjecture/shrinking/ordering.py | 5 +- .../py3/hypothesis/internal/conjecture/utils.py | 4 ++ .../py3/hypothesis/strategies/_internal/core.py | 10 +-- .../python/hypothesis/py3/hypothesis/version.py | 2 +- contrib/python/hypothesis/py3/ya.make | 2 +- 9 files changed, 55 insertions(+), 57 deletions(-) (limited to 'contrib/python') diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index fa7fb921112..e48da7dc0b5 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.108.9 +Version: 6.108.10 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds @@ -35,59 +35,59 @@ Classifier: Typing :: Typed Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE.txt -Requires-Dist: attrs >=22.2.0 -Requires-Dist: sortedcontainers <3.0.0,>=2.1.0 -Requires-Dist: exceptiongroup >=1.0.0 ; python_version < "3.11" +Requires-Dist: attrs>=22.2.0 +Requires-Dist: sortedcontainers<3.0.0,>=2.1.0 +Requires-Dist: exceptiongroup>=1.0.0; python_version < "3.11" Provides-Extra: all -Requires-Dist: black >=19.10b0 ; extra == 'all' -Requires-Dist: click >=7.0 ; extra == 'all' -Requires-Dist: crosshair-tool >=0.0.65 ; extra == 'all' -Requires-Dist: django >=3.2 ; extra == 'all' -Requires-Dist: dpcontracts >=0.4 ; extra == 'all' -Requires-Dist: hypothesis-crosshair >=0.0.11 ; extra == 'all' -Requires-Dist: lark >=0.10.1 ; extra == 'all' -Requires-Dist: libcst >=0.3.16 ; extra == 'all' -Requires-Dist: numpy >=1.17.3 ; extra == 'all' -Requires-Dist: pandas >=1.1 ; extra == 'all' -Requires-Dist: pytest >=4.6 ; extra == 'all' -Requires-Dist: python-dateutil >=1.4 ; extra == 'all' -Requires-Dist: pytz >=2014.1 ; extra == 'all' -Requires-Dist: redis >=3.0.0 ; extra == 'all' -Requires-Dist: rich >=9.0.0 ; extra == 'all' -Requires-Dist: backports.zoneinfo >=0.2.1 ; (python_version < "3.9") and extra == 'all' -Requires-Dist: tzdata >=2024.1 ; (sys_platform == "win32" or sys_platform == "emscripten") and extra == 'all' +Requires-Dist: black>=19.10b0; extra == "all" +Requires-Dist: click>=7.0; extra == "all" +Requires-Dist: crosshair-tool>=0.0.65; extra == "all" +Requires-Dist: django>=3.2; extra == "all" +Requires-Dist: dpcontracts>=0.4; extra == "all" +Requires-Dist: hypothesis-crosshair>=0.0.11; extra == "all" +Requires-Dist: lark>=0.10.1; extra == "all" +Requires-Dist: libcst>=0.3.16; extra == "all" +Requires-Dist: numpy>=1.17.3; extra == "all" +Requires-Dist: pandas>=1.1; extra == "all" +Requires-Dist: pytest>=4.6; extra == "all" +Requires-Dist: python-dateutil>=1.4; extra == "all" +Requires-Dist: pytz>=2014.1; extra == "all" +Requires-Dist: redis>=3.0.0; extra == "all" +Requires-Dist: rich>=9.0.0; extra == "all" +Requires-Dist: backports.zoneinfo>=0.2.1; python_version < "3.9" and extra == "all" +Requires-Dist: tzdata>=2024.1; (sys_platform == "win32" or sys_platform == "emscripten") and extra == "all" Provides-Extra: cli -Requires-Dist: click >=7.0 ; extra == 'cli' -Requires-Dist: black >=19.10b0 ; extra == 'cli' -Requires-Dist: rich >=9.0.0 ; extra == 'cli' +Requires-Dist: click>=7.0; extra == "cli" +Requires-Dist: black>=19.10b0; extra == "cli" +Requires-Dist: rich>=9.0.0; extra == "cli" Provides-Extra: codemods -Requires-Dist: libcst >=0.3.16 ; extra == 'codemods' +Requires-Dist: libcst>=0.3.16; extra == "codemods" Provides-Extra: crosshair -Requires-Dist: hypothesis-crosshair >=0.0.11 ; extra == 'crosshair' -Requires-Dist: crosshair-tool >=0.0.65 ; extra == 'crosshair' +Requires-Dist: hypothesis-crosshair>=0.0.11; extra == "crosshair" +Requires-Dist: crosshair-tool>=0.0.65; extra == "crosshair" Provides-Extra: dateutil -Requires-Dist: python-dateutil >=1.4 ; extra == 'dateutil' +Requires-Dist: python-dateutil>=1.4; extra == "dateutil" Provides-Extra: django -Requires-Dist: django >=3.2 ; extra == 'django' +Requires-Dist: django>=3.2; extra == "django" Provides-Extra: dpcontracts -Requires-Dist: dpcontracts >=0.4 ; extra == 'dpcontracts' +Requires-Dist: dpcontracts>=0.4; extra == "dpcontracts" Provides-Extra: ghostwriter -Requires-Dist: black >=19.10b0 ; extra == 'ghostwriter' +Requires-Dist: black>=19.10b0; extra == "ghostwriter" Provides-Extra: lark -Requires-Dist: lark >=0.10.1 ; extra == 'lark' +Requires-Dist: lark>=0.10.1; extra == "lark" Provides-Extra: numpy -Requires-Dist: numpy >=1.17.3 ; extra == 'numpy' +Requires-Dist: numpy>=1.17.3; extra == "numpy" Provides-Extra: pandas -Requires-Dist: pandas >=1.1 ; extra == 'pandas' +Requires-Dist: pandas>=1.1; extra == "pandas" Provides-Extra: pytest -Requires-Dist: pytest >=4.6 ; extra == 'pytest' +Requires-Dist: pytest>=4.6; extra == "pytest" Provides-Extra: pytz -Requires-Dist: pytz >=2014.1 ; extra == 'pytz' +Requires-Dist: pytz>=2014.1; extra == "pytz" Provides-Extra: redis -Requires-Dist: redis >=3.0.0 ; extra == 'redis' +Requires-Dist: redis>=3.0.0; extra == "redis" Provides-Extra: zoneinfo -Requires-Dist: backports.zoneinfo >=0.2.1 ; (python_version < "3.9") and extra == 'zoneinfo' -Requires-Dist: tzdata >=2024.1 ; (sys_platform == "win32" or sys_platform == "emscripten") and extra == 'zoneinfo' +Requires-Dist: backports.zoneinfo>=0.2.1; python_version < "3.9" and extra == "zoneinfo" +Requires-Dist: tzdata>=2024.1; (sys_platform == "win32" or sys_platform == "emscripten") and extra == "zoneinfo" ========== Hypothesis diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py b/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py index d4bcefb0c1d..09a1c2707cd 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py @@ -204,7 +204,7 @@ def from_form( return _forms_impl( st.builds( - partial(form, **form_kwargs), + partial(form, **form_kwargs), # type: ignore data=st.fixed_dictionaries(field_strategies), # type: ignore ) ) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py index 785eb92328f..72bc4ba980b 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py @@ -155,9 +155,9 @@ class Status(IntEnum): @dataclass_transform() -@attr.s(frozen=True, slots=True, auto_attribs=True) +@attr.s(slots=True, frozen=True) class StructuralCoverageTag: - label: int + label: int = attr.ib() STRUCTURAL_COVERAGE_CACHE: Dict[int, StructuralCoverageTag] = {} diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/collection.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/collection.py index bc96e14d484..a87a27b359d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/collection.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/collection.py @@ -10,10 +10,7 @@ from hypothesis.internal.conjecture.shrinking.common import Shrinker from hypothesis.internal.conjecture.shrinking.ordering import Ordering - - -def identity(v): - return v +from hypothesis.internal.conjecture.utils import identity class Collection(Shrinker): diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/ordering.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/ordering.py index f112308f5c2..4e48bdf7b98 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/ordering.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/ordering.py @@ -10,10 +10,7 @@ from hypothesis.internal.conjecture.junkdrawer import find_integer from hypothesis.internal.conjecture.shrinking.common import Shrinker - - -def identity(v): - return v +from hypothesis.internal.conjecture.utils import identity class Ordering(Shrinker): diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py index 773324bc80b..3724c82177d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py @@ -51,6 +51,10 @@ ONE_FROM_MANY_LABEL = calc_label_from_name("one more from many()") T = TypeVar("T") +def identity(v: T) -> T: + return v + + def check_sample( values: Union[Type[enum.Enum], Sequence[T]], strategy_name: str ) -> Sequence[T]: diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index f7e7dbc2721..62dcecde207 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -83,7 +83,11 @@ from hypothesis.internal.compat import ( get_type_hints, is_typed_named_tuple, ) -from hypothesis.internal.conjecture.utils import calc_label_from_cls, check_sample +from hypothesis.internal.conjecture.utils import ( + calc_label_from_cls, + check_sample, + identity, +) from hypothesis.internal.entropy import get_seeder_and_restorer from hypothesis.internal.floats import float_of from hypothesis.internal.observability import TESTCASE_CALLBACKS @@ -271,10 +275,6 @@ def sampled_from( return SampledFromStrategy(values, repr_) -def identity(x): - return x - - @cacheable @defines_strategy() def lists( diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index d711a8c3832..c8a8221c541 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 108, 9) +__version_info__ = (6, 108, 10) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index d5dcf568450..8a025cf3596 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.108.9) +VERSION(6.108.10) LICENSE(MPL-2.0) -- cgit v1.3 From 1ca193d726bed5ece8656b0072bfe03ebb0478c6 Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Wed, 21 Aug 2024 14:11:39 +0300 Subject: Intermediate changes --- contrib/python/argcomplete/py3/.dist-info/METADATA | 2 +- .../argcomplete/py3/.dist-info/entry_points.txt | 4 + .../bash_completion.d/_python-argcomplete | 1 + .../argcomplete/py3/argcomplete/completers.py | 2 +- .../python/argcomplete/py3/argcomplete/finders.py | 12 +- .../py3/argcomplete/scripts/__init__.py | 0 .../scripts/activate_global_python_argcomplete.py | 168 +++++++++++++++++++++ ...python_argcomplete_check_easy_install_script.py | 84 +++++++++++ .../scripts/register_python_argcomplete.py | 79 ++++++++++ contrib/python/argcomplete/py3/ya.make | 7 +- 10 files changed, 351 insertions(+), 8 deletions(-) create mode 100644 contrib/python/argcomplete/py3/.dist-info/entry_points.txt create mode 100644 contrib/python/argcomplete/py3/argcomplete/scripts/__init__.py create mode 100644 contrib/python/argcomplete/py3/argcomplete/scripts/activate_global_python_argcomplete.py create mode 100644 contrib/python/argcomplete/py3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py create mode 100644 contrib/python/argcomplete/py3/argcomplete/scripts/register_python_argcomplete.py (limited to 'contrib/python') diff --git a/contrib/python/argcomplete/py3/.dist-info/METADATA b/contrib/python/argcomplete/py3/.dist-info/METADATA index adbd1293a61..ce75d7bfa8b 100644 --- a/contrib/python/argcomplete/py3/.dist-info/METADATA +++ b/contrib/python/argcomplete/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: argcomplete -Version: 3.4.0 +Version: 3.5.0 Summary: Bash tab completion for argparse Home-page: https://github.com/kislyuk/argcomplete Author: Andrey Kislyuk diff --git a/contrib/python/argcomplete/py3/.dist-info/entry_points.txt b/contrib/python/argcomplete/py3/.dist-info/entry_points.txt new file mode 100644 index 00000000000..f13e435fafb --- /dev/null +++ b/contrib/python/argcomplete/py3/.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +activate-global-python-argcomplete = argcomplete.scripts.activate_global_python_argcomplete:main +python-argcomplete-check-easy-install-script = argcomplete.scripts.python_argcomplete_check_easy_install_script:main +register-python-argcomplete = argcomplete.scripts.register_python_argcomplete:main diff --git a/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete b/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete index dec7cdac16d..81c9d41f803 100644 --- a/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete +++ b/contrib/python/argcomplete/py3/argcomplete/bash_completion.d/_python-argcomplete @@ -1,5 +1,6 @@ #compdef -default- +# argcomplete global completion loader for zsh and bash # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. diff --git a/contrib/python/argcomplete/py3/argcomplete/completers.py b/contrib/python/argcomplete/py3/argcomplete/completers.py index d89cdbfbc34..b62c593d64f 100644 --- a/contrib/python/argcomplete/py3/argcomplete/completers.py +++ b/contrib/python/argcomplete/py3/argcomplete/completers.py @@ -22,7 +22,7 @@ class BaseCompleter: def __call__( self, *, prefix: str, action: argparse.Action, parser: argparse.ArgumentParser, parsed_args: argparse.Namespace - ): + ) -> None: raise NotImplementedError("This method should be implemented by a subclass.") diff --git a/contrib/python/argcomplete/py3/argcomplete/finders.py b/contrib/python/argcomplete/py3/argcomplete/finders.py index fb0f31cefd9..793b462eed0 100644 --- a/contrib/python/argcomplete/py3/argcomplete/finders.py +++ b/contrib/python/argcomplete/py3/argcomplete/finders.py @@ -7,10 +7,10 @@ import argparse import os import sys from collections.abc import Mapping -from typing import Callable, Dict, List, Optional, Sequence, Union +from typing import Callable, Dict, List, Optional, Sequence, TextIO, Union from . import io as _io -from .completers import ChoicesCompleter, FilesCompleter, SuppressCompleter +from .completers import BaseCompleter, 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 @@ -66,13 +66,13 @@ class CompletionFinder(object): argument_parser: argparse.ArgumentParser, always_complete_options: Union[bool, str] = True, exit_method: Callable = os._exit, - output_stream=None, + output_stream: Optional[TextIO] = None, exclude: Optional[Sequence[str]] = None, validator: Optional[Callable] = None, print_suppressed: bool = False, append_space: Optional[bool] = None, - default_completer=FilesCompleter(), - ): + default_completer: BaseCompleter = FilesCompleter(), + ) -> None: """ :param argument_parser: The argument parser to autocomplete on :param always_complete_options: @@ -132,6 +132,8 @@ class CompletionFinder(object): debug("Unable to open fd 8 for writing, quitting") exit_method(1) + assert output_stream is not None + ifs = os.environ.get("_ARGCOMPLETE_IFS", "\013") if len(ifs) != 1: debug("Invalid value for IFS, quitting [{v}]".format(v=ifs)) diff --git a/contrib/python/argcomplete/py3/argcomplete/scripts/__init__.py b/contrib/python/argcomplete/py3/argcomplete/scripts/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/python/argcomplete/py3/argcomplete/scripts/activate_global_python_argcomplete.py b/contrib/python/argcomplete/py3/argcomplete/scripts/activate_global_python_argcomplete.py new file mode 100644 index 00000000000..768b8aa6bf3 --- /dev/null +++ b/contrib/python/argcomplete/py3/argcomplete/scripts/activate_global_python_argcomplete.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +Activate the generic bash-completion script or zsh completion autoload function for the argcomplete module. +""" + +import argparse +import os +import shutil +import site +import subprocess +import sys + +import argcomplete + +# PEP 366 +__package__ = "argcomplete.scripts" + +zsh_shellcode = """ +# Begin added by argcomplete +fpath=( {zsh_fpath} "${{fpath[@]}}" ) +# End added by argcomplete +""" + +bash_shellcode = """ +# Begin added by argcomplete +source "{activator}" +# End added by argcomplete +""" + +parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) +parser.add_argument("-y", "--yes", help="automatically answer yes for all questions", action="store_true") +parser.add_argument("--dest", help='Specify the shell completion modules directory to install into, or "-" for stdout') +parser.add_argument("--user", help="Install into user directory", action="store_true") +argcomplete.autocomplete(parser) +args = None + + +def get_local_dir(): + try: + return subprocess.check_output(["brew", "--prefix"]).decode().strip() + except (FileNotFoundError, subprocess.CalledProcessError): + return "/usr/local" + + +def get_zsh_system_dir(): + return f"{get_local_dir()}/share/zsh/site-functions" + + +def get_bash_system_dir(): + if "BASH_COMPLETION_COMPAT_DIR" in os.environ: + return os.environ["BASH_COMPLETION_COMPAT_DIR"] + elif sys.platform == "darwin": + return f"{get_local_dir()}/etc/bash_completion.d" # created by homebrew + else: + return "/etc/bash_completion.d" # created by bash-completion + + +def get_activator_dir(): + return os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)), "bash_completion.d") + + +def get_activator_path(): + return os.path.join(get_activator_dir(), "_python-argcomplete") + + +def install_to_destination(dest): + activator = get_activator_path() + if dest == "-": + with open(activator) as fh: + sys.stdout.write(fh.read()) + return + destdir = os.path.dirname(dest) + if not os.path.exists(destdir): + try: + os.makedirs(destdir, exist_ok=True) + except Exception as e: + parser.error(f"path {destdir} does not exist and could not be created: {e}") + try: + print(f"Installing {activator} to {dest}...", file=sys.stderr) + shutil.copy(activator, dest) + print("Installed.", file=sys.stderr) + except Exception as e: + parser.error( + f"while installing to {dest}: {e}. Please run this command using sudo, or see --help for more options." + ) + + +def get_consent(): + assert args is not None + if args.yes is True: + return True + while True: + res = input("OK to proceed? [y/n] ") + if res.lower() not in {"y", "n", "yes", "no"}: + print('Please answer "yes" or "no".', file=sys.stderr) + elif res.lower() in {"y", "yes"}: + return True + else: + return False + + +def append_to_config_file(path, shellcode): + if os.path.exists(path): + with open(path, 'r') as fh: + if shellcode in fh.read(): + print(f"The code already exists in the file {path}.", file=sys.stderr) + return + print(f"argcomplete needs to append to the file {path}. The following code will be appended:", file=sys.stderr) + for line in shellcode.splitlines(): + print(">", line, file=sys.stderr) + if not get_consent(): + print("Not added.", file=sys.stderr) + return + print(f"Adding shellcode to {path}...", file=sys.stderr) + with open(path, "a") as fh: + fh.write(shellcode) + print("Added.", file=sys.stderr) + + +def link_user_rcfiles(): + # TODO: warn if running as superuser + zsh_rcfile = os.path.join(os.path.expanduser(os.environ.get("ZDOTDIR", "~")), ".zshenv") + append_to_config_file(zsh_rcfile, zsh_shellcode.format(zsh_fpath=get_activator_dir())) + + bash_completion_user_file = os.path.expanduser("~/.bash_completion") + append_to_config_file(bash_completion_user_file, bash_shellcode.format(activator=get_activator_path())) + + +def main(): + global args + args = parser.parse_args() + + destinations = [] + + if args.dest: + if args.dest != "-" and not os.path.exists(args.dest): + parser.error(f"directory {args.dest} was specified via --dest, but it does not exist") + destinations.append(args.dest) + elif site.ENABLE_USER_SITE and site.USER_SITE and site.USER_SITE in argcomplete.__file__: + print( + "Argcomplete was installed in the user site local directory. Defaulting to user installation.", + file=sys.stderr, + ) + link_user_rcfiles() + elif sys.prefix != sys.base_prefix: + print("Argcomplete was installed in a virtual environment. Defaulting to user installation.", file=sys.stderr) + link_user_rcfiles() + elif args.user: + link_user_rcfiles() + else: + print("Defaulting to system-wide installation.", file=sys.stderr) + destinations.append(f"{get_zsh_system_dir()}/_python-argcomplete") + destinations.append(f"{get_bash_system_dir()}/python-argcomplete") + + for destination in destinations: + install_to_destination(destination) + + if args.dest is None: + print("Please restart your shell or source the installed file to activate it.", file=sys.stderr) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/contrib/python/argcomplete/py3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py b/contrib/python/argcomplete/py3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py new file mode 100644 index 00000000000..d914c222fed --- /dev/null +++ b/contrib/python/argcomplete/py3/argcomplete/scripts/python_argcomplete_check_easy_install_script.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +This script is part of the Python argcomplete package (https://github.com/kislyuk/argcomplete). +It is used to check if an EASY-INSTALL-SCRIPT wrapper redirects to a script that contains the string +"PYTHON_ARGCOMPLETE_OK". If you have enabled global completion in argcomplete, the completion hook will run it every +time you press in your shell. + +Usage: + python-argcomplete-check-easy-install-script +""" + +import sys + +# PEP 366 +__package__ = "argcomplete.scripts" + + +def main(): + if len(sys.argv) != 2: + sys.exit(__doc__) + + sys.tracebacklimit = 0 + + with open(sys.argv[1]) as fh: + line1, head = fh.read(1024).split("\n", 1)[:2] + if line1.startswith("#") and ("py" in line1 or "Py" in line1): + import re + + lines = head.split("\n", 12) + for line in lines: + if line.startswith("# EASY-INSTALL-SCRIPT"): + import pkg_resources + + re_match = re.match("# EASY-INSTALL-SCRIPT: '(.+)','(.+)'", line) + assert re_match is not None + dist, script = re_match.groups() + if "PYTHON_ARGCOMPLETE_OK" in pkg_resources.get_distribution(dist).get_metadata( + "scripts/" + script + ): + return 0 + elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"): + re_match = re.match("# EASY-INSTALL-ENTRY-SCRIPT: '(.+)','(.+)','(.+)'", line) + assert re_match is not None + dist, group, name = re_match.groups() + import pkgutil + + import pkg_resources + + entry_point_info = pkg_resources.get_distribution(dist).get_entry_info(group, name) + assert entry_point_info is not None + module_name = entry_point_info.module_name + with open(pkgutil.get_loader(module_name).get_filename()) as mod_fh: # type: ignore + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"): + for line2 in lines: + if line2.startswith("__file__"): + re_match = re.match("__file__ = '(.+)'", line2) + assert re_match is not None + filename = re_match.group(1) + with open(filename) as mod_fh: + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + elif line.startswith("# PBR Generated"): + re_match = re.search("from (.*) import", head) + assert re_match is not None + module = re_match.groups()[0] + import pkgutil + + import pkg_resources + + with open(pkgutil.get_loader(module).get_filename()) as mod_fh: # type: ignore + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/contrib/python/argcomplete/py3/argcomplete/scripts/register_python_argcomplete.py b/contrib/python/argcomplete/py3/argcomplete/scripts/register_python_argcomplete.py new file mode 100644 index 00000000000..1e680298258 --- /dev/null +++ b/contrib/python/argcomplete/py3/argcomplete/scripts/register_python_argcomplete.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +Register a Python executable for use with the argcomplete module. + +To perform the registration, source the output of this script in your bash shell +(quote the output to avoid interpolation). + +Example: + + $ eval "$(register-python-argcomplete my-favorite-script.py)" + +For Tcsh + + $ eval `register-python-argcomplete --shell tcsh my-favorite-script.py` + +For Fish + + $ register-python-argcomplete --shell fish my-favourite-script.py > ~/.config/fish/my-favourite-script.py.fish +""" + +import argparse +import sys + +import argcomplete + +# PEP 366 +__package__ = "argcomplete.scripts" + + +def main(): + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument( + "--no-defaults", + dest="use_defaults", + action="store_false", + default=True, + help="when no matches are generated, do not fallback to readline's default completion (affects bash only)", + ) + parser.add_argument( + "--complete-arguments", + nargs=argparse.REMAINDER, + help="arguments to call complete with; use of this option discards default options (affects bash only)", + ) + parser.add_argument( + "-s", + "--shell", + choices=("bash", "zsh", "tcsh", "fish", "powershell"), + default="bash", + help="output code for the specified shell", + ) + parser.add_argument( + "-e", "--external-argcomplete-script", help="external argcomplete script for auto completion of the executable" + ) + + parser.add_argument("executable", nargs="+", help="executable to completed (when invoked by exactly this name)") + + argcomplete.autocomplete(parser) + + if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + + args = parser.parse_args() + + sys.stdout.write( + argcomplete.shellcode( + args.executable, args.use_defaults, args.shell, args.complete_arguments, args.external_argcomplete_script + ) + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/contrib/python/argcomplete/py3/ya.make b/contrib/python/argcomplete/py3/ya.make index e2cc4f65409..16487619c8f 100644 --- a/contrib/python/argcomplete/py3/ya.make +++ b/contrib/python/argcomplete/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(3.4.0) +VERSION(3.5.0) LICENSE(Apache-2.0) @@ -21,12 +21,17 @@ PY_SRCS( argcomplete/packages/__init__.py argcomplete/packages/_argparse.py argcomplete/packages/_shlex.py + argcomplete/scripts/__init__.py + argcomplete/scripts/activate_global_python_argcomplete.py + argcomplete/scripts/python_argcomplete_check_easy_install_script.py + argcomplete/scripts/register_python_argcomplete.py argcomplete/shell_integration.py ) RESOURCE_FILES( PREFIX contrib/python/argcomplete/py3/ .dist-info/METADATA + .dist-info/entry_points.txt .dist-info/top_level.txt argcomplete/bash_completion.d/_python-argcomplete argcomplete/py.typed -- cgit v1.3 From 06068c509279029ceaa73fd5f3a0d43e99e16f12 Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Wed, 21 Aug 2024 15:52:54 +0300 Subject: Intermediate changes --- contrib/python/ydb/py3/.dist-info/METADATA | 2 +- contrib/python/ydb/py3/ya.make | 7 +- .../ydb/py3/ydb/_grpc/grpcwrapper/common_utils.py | 9 +- contrib/python/ydb/py3/ydb/aio/__init__.py | 1 + contrib/python/ydb/py3/ydb/aio/_utilities.py | 3 + contrib/python/ydb/py3/ydb/aio/query/__init__.py | 7 + contrib/python/ydb/py3/ydb/aio/query/base.py | 11 + contrib/python/ydb/py3/ydb/aio/query/pool.py | 108 +++++++++ contrib/python/ydb/py3/ydb/aio/query/session.py | 144 +++++++++++ .../python/ydb/py3/ydb/aio/query/transaction.py | 153 ++++++++++++ contrib/python/ydb/py3/ydb/query/__init__.py | 7 +- contrib/python/ydb/py3/ydb/query/base.py | 264 +++------------------ contrib/python/ydb/py3/ydb/query/pool.py | 26 +- contrib/python/ydb/py3/ydb/query/session.py | 24 +- contrib/python/ydb/py3/ydb/query/transaction.py | 108 +++++---- contrib/python/ydb/py3/ydb/retries.py | 25 ++ contrib/python/ydb/py3/ydb/ydb_version.py | 2 +- 17 files changed, 602 insertions(+), 299 deletions(-) create mode 100644 contrib/python/ydb/py3/ydb/aio/query/__init__.py create mode 100644 contrib/python/ydb/py3/ydb/aio/query/base.py create mode 100644 contrib/python/ydb/py3/ydb/aio/query/pool.py create mode 100644 contrib/python/ydb/py3/ydb/aio/query/session.py create mode 100644 contrib/python/ydb/py3/ydb/aio/query/transaction.py (limited to 'contrib/python') diff --git a/contrib/python/ydb/py3/.dist-info/METADATA b/contrib/python/ydb/py3/.dist-info/METADATA index 5b155408fcd..b1c2dcdaa21 100644 --- a/contrib/python/ydb/py3/.dist-info/METADATA +++ b/contrib/python/ydb/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ydb -Version: 3.15.0 +Version: 3.16.0 Summary: YDB Python SDK Home-page: http://github.com/ydb-platform/ydb-python-sdk Author: Yandex LLC diff --git a/contrib/python/ydb/py3/ya.make b/contrib/python/ydb/py3/ya.make index cb2a88454aa..bc7c34dd4df 100644 --- a/contrib/python/ydb/py3/ya.make +++ b/contrib/python/ydb/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(3.15.0) +VERSION(3.16.0) LICENSE(Apache-2.0) @@ -57,6 +57,11 @@ PY_SRCS( ydb/aio/iam.py ydb/aio/oauth2_token_exchange.py ydb/aio/pool.py + ydb/aio/query/__init__.py + ydb/aio/query/base.py + ydb/aio/query/pool.py + ydb/aio/query/session.py + ydb/aio/query/transaction.py ydb/aio/resolver.py ydb/aio/scheme.py ydb/aio/table.py diff --git a/contrib/python/ydb/py3/ydb/_grpc/grpcwrapper/common_utils.py b/contrib/python/ydb/py3/ydb/_grpc/grpcwrapper/common_utils.py index 66ef0a8c855..95a5744313e 100644 --- a/contrib/python/ydb/py3/ydb/_grpc/grpcwrapper/common_utils.py +++ b/contrib/python/ydb/py3/ydb/_grpc/grpcwrapper/common_utils.py @@ -24,7 +24,8 @@ from google.protobuf.message import Message from google.protobuf.duration_pb2 import Duration as ProtoDuration from google.protobuf.timestamp_pb2 import Timestamp as ProtoTimeStamp -import ydb.aio +from ...driver import Driver +from ...aio.driver import Driver as DriverIO try: from ydb.public.api.protos import ydb_topic_pb2, ydb_issue_message_pb2 @@ -141,7 +142,7 @@ class IGrpcWrapperAsyncIO(abc.ABC): ... -SupportedDriverType = Union[ydb.Driver, ydb.aio.Driver] +SupportedDriverType = Union[Driver, DriverIO] class GrpcWrapperAsyncIO(IGrpcWrapperAsyncIO): @@ -180,7 +181,7 @@ class GrpcWrapperAsyncIO(IGrpcWrapperAsyncIO): if self._wait_executor: self._wait_executor.shutdown(wait) - async def _start_asyncio_driver(self, driver: ydb.aio.Driver, stub, method): + async def _start_asyncio_driver(self, driver: DriverIO, stub, method): requests_iterator = QueueToIteratorAsyncIO(self.from_client_grpc) stream_call = await driver( requests_iterator, @@ -190,7 +191,7 @@ class GrpcWrapperAsyncIO(IGrpcWrapperAsyncIO): self._stream_call = stream_call self.from_server_grpc = stream_call.__aiter__() - async def _start_sync_driver(self, driver: ydb.Driver, stub, method): + async def _start_sync_driver(self, driver: Driver, stub, method): requests_iterator = AsyncQueueToSyncIteratorAsyncIO(self.from_client_grpc) self._wait_executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) diff --git a/contrib/python/ydb/py3/ydb/aio/__init__.py b/contrib/python/ydb/py3/ydb/aio/__init__.py index acc44db57a2..0e7d4e747a9 100644 --- a/contrib/python/ydb/py3/ydb/aio/__init__.py +++ b/contrib/python/ydb/py3/ydb/aio/__init__.py @@ -1,2 +1,3 @@ from .driver import Driver # noqa from .table import SessionPool, retry_operation # noqa +from .query import QuerySessionPoolAsync, QuerySessionAsync # noqa diff --git a/contrib/python/ydb/py3/ydb/aio/_utilities.py b/contrib/python/ydb/py3/ydb/aio/_utilities.py index 10cbead6674..454378b0d40 100644 --- a/contrib/python/ydb/py3/ydb/aio/_utilities.py +++ b/contrib/python/ydb/py3/ydb/aio/_utilities.py @@ -7,6 +7,9 @@ class AsyncResponseIterator(object): self.it.cancel() return self + def __iter__(self): + return self + def __aiter__(self): return self diff --git a/contrib/python/ydb/py3/ydb/aio/query/__init__.py b/contrib/python/ydb/py3/ydb/aio/query/__init__.py new file mode 100644 index 00000000000..829d7b54cf6 --- /dev/null +++ b/contrib/python/ydb/py3/ydb/aio/query/__init__.py @@ -0,0 +1,7 @@ +__all__ = [ + "QuerySessionPoolAsync", + "QuerySessionAsync", +] + +from .pool import QuerySessionPoolAsync +from .session import QuerySessionAsync diff --git a/contrib/python/ydb/py3/ydb/aio/query/base.py b/contrib/python/ydb/py3/ydb/aio/query/base.py new file mode 100644 index 00000000000..3800ce3d4f0 --- /dev/null +++ b/contrib/python/ydb/py3/ydb/aio/query/base.py @@ -0,0 +1,11 @@ +from .. import _utilities + + +class AsyncResponseContextIterator(_utilities.AsyncResponseIterator): + async def __aenter__(self) -> "AsyncResponseContextIterator": + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + # To close stream on YDB it is necessary to scroll through it to the end + async for _ in self: + pass diff --git a/contrib/python/ydb/py3/ydb/aio/query/pool.py b/contrib/python/ydb/py3/ydb/aio/query/pool.py new file mode 100644 index 00000000000..f91f7465e41 --- /dev/null +++ b/contrib/python/ydb/py3/ydb/aio/query/pool.py @@ -0,0 +1,108 @@ +import logging +from typing import ( + Callable, + Optional, + List, +) + +from .session import ( + QuerySessionAsync, +) +from ...retries import ( + RetrySettings, + retry_operation_async, +) +from ... import convert +from ..._grpc.grpcwrapper import common_utils + +logger = logging.getLogger(__name__) + + +class QuerySessionPoolAsync: + """QuerySessionPoolAsync is an object to simplify operations with sessions of Query Service.""" + + def __init__(self, driver: common_utils.SupportedDriverType): + """ + :param driver: A driver instance + """ + + logger.warning("QuerySessionPoolAsync is an experimental API, which could be changed.") + self._driver = driver + + def checkout(self) -> "SimpleQuerySessionCheckoutAsync": + """WARNING: This API is experimental and could be changed. + Return a Session context manager, that opens session on enter and closes session on exit. + """ + + return SimpleQuerySessionCheckoutAsync(self) + + async def retry_operation_async( + self, callee: Callable, retry_settings: Optional[RetrySettings] = None, *args, **kwargs + ): + """WARNING: This API is experimental and could be changed. + Special interface to execute a bunch of commands with session in a safe, retriable way. + + :param callee: A function, that works with session. + :param retry_settings: RetrySettings object. + + :return: Result sets or exception in case of execution errors. + """ + + retry_settings = RetrySettings() if retry_settings is None else retry_settings + + async def wrapped_callee(): + async with self.checkout() as session: + return await callee(session, *args, **kwargs) + + return await retry_operation_async(wrapped_callee, retry_settings) + + async def execute_with_retries( + self, + query: str, + parameters: Optional[dict] = None, + retry_settings: Optional[RetrySettings] = None, + *args, + **kwargs, + ) -> List[convert.ResultSet]: + """WARNING: This API is experimental and could be changed. + Special interface to execute a one-shot queries in a safe, retriable way. + Note: this method loads all data from stream before return, do not use this + method with huge read queries. + + :param query: A query, yql or sql text. + :param parameters: dict with parameters and YDB types; + :param retry_settings: RetrySettings object. + + :return: Result sets or exception in case of execution errors. + """ + + retry_settings = RetrySettings() if retry_settings is None else retry_settings + + async def wrapped_callee(): + async with self.checkout() as session: + it = await session.execute(query, parameters, *args, **kwargs) + return [result_set async for result_set in it] + + return await retry_operation_async(wrapped_callee, retry_settings) + + async def stop(self, timeout=None): + pass # TODO: implement + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.stop() + + +class SimpleQuerySessionCheckoutAsync: + def __init__(self, pool: QuerySessionPoolAsync): + self._pool = pool + self._session = QuerySessionAsync(pool._driver) + + async def __aenter__(self) -> QuerySessionAsync: + await self._session.create() + return self._session + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self._session.delete() diff --git a/contrib/python/ydb/py3/ydb/aio/query/session.py b/contrib/python/ydb/py3/ydb/aio/query/session.py new file mode 100644 index 00000000000..627a41d895d --- /dev/null +++ b/contrib/python/ydb/py3/ydb/aio/query/session.py @@ -0,0 +1,144 @@ +import asyncio + +from typing import ( + Optional, +) + +from .base import AsyncResponseContextIterator +from .transaction import QueryTxContextAsync +from .. import _utilities +from ... import issues +from ..._grpc.grpcwrapper import common_utils +from ..._grpc.grpcwrapper import ydb_query_public_types as _ydb_query_public + +from ...query import base +from ...query.session import ( + BaseQuerySession, + QuerySessionStateEnum, +) + + +class QuerySessionAsync(BaseQuerySession): + """Session object for Query Service. It is not recommended to control + session's lifecycle manually - use a QuerySessionPool is always a better choise. + """ + + _loop: asyncio.AbstractEventLoop + _status_stream: _utilities.AsyncResponseIterator = None + + def __init__( + self, + driver: common_utils.SupportedDriverType, + settings: Optional[base.QueryClientSettings] = None, + loop: asyncio.AbstractEventLoop = None, + ): + super(QuerySessionAsync, self).__init__(driver, settings) + self._loop = loop if loop is not None else asyncio.get_running_loop() + + async def _attach(self) -> None: + self._stream = await self._attach_call() + self._status_stream = _utilities.AsyncResponseIterator( + self._stream, + lambda response: common_utils.ServerStatus.from_proto(response), + ) + + first_response = await self._status_stream.next() + if first_response.status != issues.StatusCode.SUCCESS: + pass + + self._state.set_attached(True) + self._state._change_state(QuerySessionStateEnum.CREATED) + + self._loop.create_task(self._check_session_status_loop(), name="check session status task") + + async def _check_session_status_loop(self) -> None: + try: + async for status in self._status_stream: + if status.status != issues.StatusCode.SUCCESS: + self._state.reset() + self._state._change_state(QuerySessionStateEnum.CLOSED) + except Exception: + if not self._state._already_in(QuerySessionStateEnum.CLOSED): + self._state.reset() + self._state._change_state(QuerySessionStateEnum.CLOSED) + + async def delete(self) -> None: + """WARNING: This API is experimental and could be changed. + + Deletes a Session of Query Service on server side and releases resources. + + :return: None + """ + if self._state._already_in(QuerySessionStateEnum.CLOSED): + return + + self._state._check_invalid_transition(QuerySessionStateEnum.CLOSED) + await self._delete_call() + self._stream.cancel() + + async def create(self) -> "QuerySessionAsync": + """WARNING: This API is experimental and could be changed. + + Creates a Session of Query Service on server side and attaches it. + + :return: QuerySessionSync object. + """ + if self._state._already_in(QuerySessionStateEnum.CREATED): + return + + self._state._check_invalid_transition(QuerySessionStateEnum.CREATED) + await self._create_call() + await self._attach() + + return self + + def transaction(self, tx_mode=None) -> QueryTxContextAsync: + self._state._check_session_ready_to_use() + tx_mode = tx_mode if tx_mode else _ydb_query_public.QuerySerializableReadWrite() + + return QueryTxContextAsync( + self._driver, + self._state, + self, + tx_mode, + ) + + async def execute( + self, + query: str, + parameters: dict = None, + syntax: base.QuerySyntax = None, + exec_mode: base.QueryExecMode = None, + concurrent_result_sets: bool = False, + ) -> AsyncResponseContextIterator: + """WARNING: This API is experimental and could be changed. + + Sends a query to Query Service + :param query: (YQL or SQL text) to be executed. + :param syntax: Syntax of the query, which is a one from the following choises: + 1) QuerySyntax.YQL_V1, which is default; + 2) QuerySyntax.PG. + :param parameters: dict with parameters and YDB types; + :param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False; + + :return: Iterator with result sets + """ + self._state._check_session_ready_to_use() + + stream_it = await self._execute_call( + query=query, + commit_tx=True, + syntax=syntax, + exec_mode=exec_mode, + parameters=parameters, + concurrent_result_sets=concurrent_result_sets, + ) + + return AsyncResponseContextIterator( + stream_it, + lambda resp: base.wrap_execute_query_response( + rpc_state=None, + response_pb=resp, + settings=self._settings, + ), + ) diff --git a/contrib/python/ydb/py3/ydb/aio/query/transaction.py b/contrib/python/ydb/py3/ydb/aio/query/transaction.py new file mode 100644 index 00000000000..e9993fccc25 --- /dev/null +++ b/contrib/python/ydb/py3/ydb/aio/query/transaction.py @@ -0,0 +1,153 @@ +import logging +from typing import ( + Optional, +) + +from .base import AsyncResponseContextIterator +from ... import issues +from ...query import base +from ...query.transaction import ( + BaseQueryTxContext, + QueryTxStateEnum, +) + +logger = logging.getLogger(__name__) + + +class QueryTxContextAsync(BaseQueryTxContext): + async def __aenter__(self) -> "QueryTxContextAsync": + """ + Enters a context manager and returns a transaction + + :return: A transaction instance + """ + return self + + async def __aexit__(self, *args, **kwargs): + """ + Closes a transaction context manager and rollbacks transaction if + it is not finished explicitly + """ + await self._ensure_prev_stream_finished() + if self._tx_state._state == QueryTxStateEnum.BEGINED: + # It's strictly recommended to close transactions directly + # by using commit_tx=True flag while executing statement or by + # .commit() or .rollback() methods, but here we trying to do best + # effort to avoid useless open transactions + logger.warning("Potentially leaked tx: %s", self._tx_state.tx_id) + try: + await self.rollback() + except issues.Error: + logger.warning("Failed to rollback leaked tx: %s", self._tx_state.tx_id) + + async def _ensure_prev_stream_finished(self) -> None: + if self._prev_stream is not None: + async with self._prev_stream: + pass + self._prev_stream = None + + async def begin(self, settings: Optional[base.QueryClientSettings] = None) -> "QueryTxContextAsync": + """WARNING: This API is experimental and could be changed. + + Explicitly begins a transaction + + :param settings: A request settings + + :return: None or exception if begin is failed + """ + await self._begin_call(settings) + return self + + async def commit(self, settings: Optional[base.QueryClientSettings] = None) -> None: + """WARNING: This API is experimental and could be changed. + + Calls commit on a transaction if it is open otherwise is no-op. If transaction execution + failed then this method raises PreconditionFailed. + + :param settings: A request settings + + :return: A committed transaction or exception if commit is failed + """ + if self._tx_state._already_in(QueryTxStateEnum.COMMITTED): + return + + if self._tx_state._state == QueryTxStateEnum.NOT_INITIALIZED: + self._tx_state._change_state(QueryTxStateEnum.COMMITTED) + return + + await self._ensure_prev_stream_finished() + + await self._commit_call(settings) + + async def rollback(self, settings: Optional[base.QueryClientSettings] = None) -> None: + """WARNING: This API is experimental and could be changed. + + Calls rollback on a transaction if it is open otherwise is no-op. If transaction execution + failed then this method raises PreconditionFailed. + + :param settings: A request settings + + :return: A committed transaction or exception if commit is failed + """ + if self._tx_state._already_in(QueryTxStateEnum.ROLLBACKED): + return + + if self._tx_state._state == QueryTxStateEnum.NOT_INITIALIZED: + self._tx_state._change_state(QueryTxStateEnum.ROLLBACKED) + return + + await self._ensure_prev_stream_finished() + + await self._rollback_call(settings) + + async def execute( + self, + query: str, + parameters: Optional[dict] = None, + commit_tx: Optional[bool] = False, + syntax: Optional[base.QuerySyntax] = None, + exec_mode: Optional[base.QueryExecMode] = None, + concurrent_result_sets: Optional[bool] = False, + settings: Optional[base.QueryClientSettings] = None, + ) -> AsyncResponseContextIterator: + """WARNING: This API is experimental and could be changed. + + Sends a query to Query Service + :param query: (YQL or SQL text) to be executed. + :param parameters: dict with parameters and YDB types; + :param commit_tx: A special flag that allows transaction commit. + :param syntax: Syntax of the query, which is a one from the following choises: + 1) QuerySyntax.YQL_V1, which is default; + 2) QuerySyntax.PG. + :param exec_mode: Exec mode of the query, which is a one from the following choises: + 1) QueryExecMode.EXECUTE, which is default; + 2) QueryExecMode.EXPLAIN; + 3) QueryExecMode.VALIDATE; + 4) QueryExecMode.PARSE. + :param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False; + + :return: Iterator with result sets + """ + await self._ensure_prev_stream_finished() + + stream_it = await self._execute_call( + query=query, + commit_tx=commit_tx, + syntax=syntax, + exec_mode=exec_mode, + parameters=parameters, + concurrent_result_sets=concurrent_result_sets, + ) + + settings = settings if settings is not None else self.session._settings + self._prev_stream = AsyncResponseContextIterator( + stream_it, + lambda resp: base.wrap_execute_query_response( + rpc_state=None, + response_pb=resp, + tx=self, + commit_tx=commit_tx, + settings=settings, + ), + ) + return self._prev_stream diff --git a/contrib/python/ydb/py3/ydb/query/__init__.py b/contrib/python/ydb/py3/ydb/query/__init__.py index eb967abc206..40e512cd6bd 100644 --- a/contrib/python/ydb/py3/ydb/query/__init__.py +++ b/contrib/python/ydb/py3/ydb/query/__init__.py @@ -11,13 +11,12 @@ __all__ = [ import logging from .base import ( - IQueryClient, - SupportedDriverType, QueryClientSettings, ) from .session import QuerySessionSync +from .._grpc.grpcwrapper import common_utils from .._grpc.grpcwrapper.ydb_query_public_types import ( QueryOnlineReadOnly, QuerySerializableReadWrite, @@ -30,8 +29,8 @@ from .pool import QuerySessionPool logger = logging.getLogger(__name__) -class QueryClientSync(IQueryClient): - def __init__(self, driver: SupportedDriverType, query_client_settings: QueryClientSettings = None): +class QueryClientSync: + def __init__(self, driver: common_utils.SupportedDriverType, query_client_settings: QueryClientSettings = None): logger.warning("QueryClientSync is an experimental API, which could be changed.") self._driver = driver self._settings = query_client_settings diff --git a/contrib/python/ydb/py3/ydb/query/base.py b/contrib/python/ydb/py3/ydb/query/base.py index e08d9f52d0e..55087d0c4ee 100644 --- a/contrib/python/ydb/py3/ydb/query/base.py +++ b/contrib/python/ydb/py3/ydb/query/base.py @@ -2,14 +2,11 @@ import abc import enum import functools +import typing from typing import ( - Iterator, Optional, ) -from .._grpc.grpcwrapper.common_utils import ( - SupportedDriverType, -) from .._grpc.grpcwrapper import ydb_query from .._grpc.grpcwrapper.ydb_query_public_types import ( BaseQueryTxMode, @@ -20,6 +17,9 @@ from .. import issues from .. import _utilities from .. import _apis +if typing.TYPE_CHECKING: + from .transaction import BaseQueryTxContext + class QuerySyntax(enum.IntEnum): UNSPECIFIED = 0 @@ -48,12 +48,38 @@ class SyncResponseContextIterator(_utilities.SyncResponseIterator): return self def __exit__(self, exc_type, exc_val, exc_tb): + # To close stream on YDB it is necessary to scroll through it to the end for _ in self: pass class QueryClientSettings: - pass + def __init__(self): + self._native_datetime_in_result_sets = True + self._native_date_in_result_sets = True + self._native_json_in_result_sets = True + self._native_interval_in_result_sets = True + self._native_timestamp_in_result_sets = True + + def with_native_timestamp_in_result_sets(self, enabled: bool) -> "QueryClientSettings": + self._native_timestamp_in_result_sets = enabled + return self + + def with_native_interval_in_result_sets(self, enabled: bool) -> "QueryClientSettings": + self._native_interval_in_result_sets = enabled + return self + + def with_native_json_in_result_sets(self, enabled: bool) -> "QueryClientSettings": + self._native_json_in_result_sets = enabled + return self + + def with_native_date_in_result_sets(self, enabled: bool) -> "QueryClientSettings": + self._native_date_in_result_sets = enabled + return self + + def with_native_datetime_in_result_sets(self, enabled: bool) -> "QueryClientSettings": + self._native_datetime_in_result_sets = enabled + return self class IQuerySessionState(abc.ABC): @@ -92,229 +118,6 @@ class IQuerySessionState(abc.ABC): pass -class IQuerySession(abc.ABC): - """Session object for Query Service. It is not recommended to control - session's lifecycle manually - use a QuerySessionPool is always a better choise. - """ - - @abc.abstractmethod - def __init__(self, driver: SupportedDriverType, settings: Optional[QueryClientSettings] = None): - pass - - @abc.abstractmethod - def create(self) -> "IQuerySession": - """WARNING: This API is experimental and could be changed. - - Creates a Session of Query Service on server side and attaches it. - - :return: Session object. - """ - pass - - @abc.abstractmethod - def delete(self) -> None: - """WARNING: This API is experimental and could be changed. - - Deletes a Session of Query Service on server side and releases resources. - - :return: None - """ - pass - - @abc.abstractmethod - def transaction(self, tx_mode: Optional[BaseQueryTxMode] = None) -> "IQueryTxContext": - """WARNING: This API is experimental and could be changed. - - Creates a transaction context manager with specified transaction mode. - - :param tx_mode: Transaction mode, which is a one from the following choises: - 1) QuerySerializableReadWrite() which is default mode; - 2) QueryOnlineReadOnly(allow_inconsistent_reads=False); - 3) QuerySnapshotReadOnly(); - 4) QueryStaleReadOnly(). - - :return: transaction context manager. - """ - pass - - @abc.abstractmethod - def execute( - self, - query: str, - syntax: Optional[QuerySyntax] = None, - exec_mode: Optional[QueryExecMode] = None, - parameters: Optional[dict] = None, - concurrent_result_sets: Optional[bool] = False, - ) -> Iterator: - """WARNING: This API is experimental and could be changed. - - Sends a query to Query Service - :param query: (YQL or SQL text) to be executed. - :param syntax: Syntax of the query, which is a one from the following choises: - 1) QuerySyntax.YQL_V1, which is default; - 2) QuerySyntax.PG. - :param parameters: dict with parameters and YDB types; - :param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False; - - :return: Iterator with result sets - """ - - -class IQueryTxContext(abc.ABC): - """ - An object that provides a simple transaction context manager that allows statements execution - in a transaction. You don't have to open transaction explicitly, because context manager encapsulates - transaction control logic, and opens new transaction if: - 1) By explicit .begin(); - 2) On execution of a first statement, which is strictly recommended method, because that avoids - useless round trip - - This context manager is not thread-safe, so you should not manipulate on it concurrently. - """ - - @abc.abstractmethod - def __init__( - self, - driver: SupportedDriverType, - session_state: IQuerySessionState, - session: IQuerySession, - tx_mode: BaseQueryTxMode, - ): - """ - An object that provides a simple transaction context manager that allows statements execution - in a transaction. You don't have to open transaction explicitly, because context manager encapsulates - transaction control logic, and opens new transaction if: - - 1) By explicit .begin() method; - 2) On execution of a first statement, which is strictly recommended method, because that avoids useless round trip - - This context manager is not thread-safe, so you should not manipulate on it concurrently. - - :param driver: A driver instance - :param session_state: A state of session - :param tx_mode: Transaction mode, which is a one from the following choises: - 1) QuerySerializableReadWrite() which is default mode; - 2) QueryOnlineReadOnly(allow_inconsistent_reads=False); - 3) QuerySnapshotReadOnly(); - 4) QueryStaleReadOnly(). - """ - pass - - @abc.abstractmethod - def __enter__(self) -> "IQueryTxContext": - """ - Enters a context manager and returns a transaction - - :return: A transaction instance - """ - pass - - @abc.abstractmethod - def __exit__(self, *args, **kwargs): - """ - Closes a transaction context manager and rollbacks transaction if - it is not finished explicitly - """ - pass - - @property - @abc.abstractmethod - def session_id(self) -> str: - """ - A transaction's session id - - :return: A transaction's session id - """ - pass - - @property - @abc.abstractmethod - def tx_id(self) -> Optional[str]: - """ - Returns an id of open transaction or None otherwise - - :return: An id of open transaction or None otherwise - """ - pass - - @abc.abstractmethod - def begin(self, settings: Optional[QueryClientSettings] = None) -> None: - """WARNING: This API is experimental and could be changed. - - Explicitly begins a transaction - - :param settings: A request settings - - :return: None or exception if begin is failed - """ - pass - - @abc.abstractmethod - def commit(self, settings: Optional[QueryClientSettings] = None) -> None: - """WARNING: This API is experimental and could be changed. - - Calls commit on a transaction if it is open. If transaction execution - failed then this method raises PreconditionFailed. - - :param settings: A request settings - - :return: None or exception if commit is failed - """ - pass - - @abc.abstractmethod - def rollback(self, settings: Optional[QueryClientSettings] = None) -> None: - """WARNING: This API is experimental and could be changed. - - Calls rollback on a transaction if it is open. If transaction execution - failed then this method raises PreconditionFailed. - - :param settings: A request settings - - :return: None or exception if rollback is failed - """ - pass - - @abc.abstractmethod - def execute( - self, - query: str, - commit_tx: Optional[bool] = False, - syntax: Optional[QuerySyntax] = None, - exec_mode: Optional[QueryExecMode] = None, - parameters: Optional[dict] = None, - concurrent_result_sets: Optional[bool] = False, - ) -> Iterator: - """WARNING: This API is experimental and could be changed. - - Sends a query to Query Service - :param query: (YQL or SQL text) to be executed. - :param commit_tx: A special flag that allows transaction commit. - :param syntax: Syntax of the query, which is a one from the following choises: - 1) QuerySyntax.YQL_V1, which is default; - 2) QuerySyntax.PG. - :param exec_mode: Exec mode of the query, which is a one from the following choises: - 1) QueryExecMode.EXECUTE, which is default; - 2) QueryExecMode.EXPLAIN; - 3) QueryExecMode.VALIDATE; - 4) QueryExecMode.PARSE. - :param parameters: dict with parameters and YDB types; - :param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False; - - :return: Iterator with result sets - """ - pass - - -class IQueryClient(abc.ABC): - def __init__(self, driver: SupportedDriverType, query_client_settings: Optional[QueryClientSettings] = None): - pass - - @abc.abstractmethod - def session(self) -> IQuerySession: - pass - - def create_execute_query_request( query: str, session_id: str, @@ -365,15 +168,16 @@ def create_execute_query_request( def wrap_execute_query_response( rpc_state: RpcState, response_pb: _apis.ydb_query.ExecuteQueryResponsePart, - tx: Optional[IQueryTxContext] = None, + tx: Optional["BaseQueryTxContext"] = None, commit_tx: Optional[bool] = False, + settings: Optional[QueryClientSettings] = None, ) -> convert.ResultSet: issues._process_response(response_pb) if tx and response_pb.tx_meta and not tx.tx_id: tx._move_to_beginned(response_pb.tx_meta.id) if tx and commit_tx: tx._move_to_commited() - return convert.ResultSet.from_message(response_pb.result_set) + return convert.ResultSet.from_message(response_pb.result_set, settings) def bad_session_handler(func): diff --git a/contrib/python/ydb/py3/ydb/query/pool.py b/contrib/python/ydb/py3/ydb/query/pool.py index e7514cdf76f..afe39f06236 100644 --- a/contrib/python/ydb/py3/ydb/query/pool.py +++ b/contrib/python/ydb/py3/ydb/query/pool.py @@ -5,7 +5,6 @@ from typing import ( List, ) -from . import base from .session import ( QuerySessionSync, ) @@ -14,6 +13,8 @@ from ..retries import ( retry_operation_sync, ) from .. import convert +from .._grpc.grpcwrapper import common_utils + logger = logging.getLogger(__name__) @@ -21,7 +22,7 @@ logger = logging.getLogger(__name__) class QuerySessionPool: """QuerySessionPool is an object to simplify operations with sessions of Query Service.""" - def __init__(self, driver: base.SupportedDriverType): + def __init__(self, driver: common_utils.SupportedDriverType): """ :param driver: A driver instance """ @@ -55,7 +56,12 @@ class QuerySessionPool: return retry_operation_sync(wrapped_callee, retry_settings) def execute_with_retries( - self, query: str, retry_settings: Optional[RetrySettings] = None, *args, **kwargs + self, + query: str, + parameters: Optional[dict] = None, + retry_settings: Optional[RetrySettings] = None, + *args, + **kwargs, ) -> List[convert.ResultSet]: """WARNING: This API is experimental and could be changed. Special interface to execute a one-shot queries in a safe, retriable way. @@ -63,6 +69,7 @@ class QuerySessionPool: method with huge read queries. :param query: A query, yql or sql text. + :param parameters: dict with parameters and YDB types; :param retry_settings: RetrySettings object. :return: Result sets or exception in case of execution errors. @@ -72,18 +79,27 @@ class QuerySessionPool: def wrapped_callee(): with self.checkout() as session: - it = session.execute(query, *args, **kwargs) + it = session.execute(query, parameters, *args, **kwargs) return [result_set for result_set in it] return retry_operation_sync(wrapped_callee, retry_settings) + def stop(self, timeout=None): + pass # TODO: implement + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + class SimpleQuerySessionCheckout: def __init__(self, pool: QuerySessionPool): self._pool = pool self._session = QuerySessionSync(pool._driver) - def __enter__(self) -> base.IQuerySession: + def __enter__(self) -> QuerySessionSync: self._session.create() return self._session diff --git a/contrib/python/ydb/py3/ydb/query/session.py b/contrib/python/ydb/py3/ydb/query/session.py index d6034d348a5..4b051dc16f7 100644 --- a/contrib/python/ydb/py3/ydb/query/session.py +++ b/contrib/python/ydb/py3/ydb/query/session.py @@ -15,7 +15,7 @@ from .._grpc.grpcwrapper import common_utils from .._grpc.grpcwrapper import ydb_query as _ydb_query from .._grpc.grpcwrapper import ydb_query_public_types as _ydb_query_public -from .transaction import BaseQueryTxContext +from .transaction import QueryTxContextSync logger = logging.getLogger(__name__) @@ -126,12 +126,12 @@ def wrapper_delete_session( return session -class BaseQuerySession(base.IQuerySession): - _driver: base.SupportedDriverType +class BaseQuerySession: + _driver: common_utils.SupportedDriverType _settings: base.QueryClientSettings _state: QuerySessionState - def __init__(self, driver: base.SupportedDriverType, settings: Optional[base.QueryClientSettings] = None): + def __init__(self, driver: common_utils.SupportedDriverType, settings: Optional[base.QueryClientSettings] = None): self._driver = driver self._settings = settings if settings is not None else base.QueryClientSettings() self._state = QuerySessionState(settings) @@ -224,7 +224,9 @@ class QuerySessionSync(BaseQuerySession): self._state.reset() self._state._change_state(QuerySessionStateEnum.CLOSED) except Exception: - pass + if not self._state._already_in(QuerySessionStateEnum.CLOSED): + self._state.reset() + self._state._change_state(QuerySessionStateEnum.CLOSED) def delete(self) -> None: """WARNING: This API is experimental and could be changed. @@ -256,7 +258,7 @@ class QuerySessionSync(BaseQuerySession): return self - def transaction(self, tx_mode: Optional[base.BaseQueryTxMode] = None) -> base.IQueryTxContext: + def transaction(self, tx_mode: Optional[base.BaseQueryTxMode] = None) -> QueryTxContextSync: """WARNING: This API is experimental and could be changed. Creates a transaction context manager with specified transaction mode. @@ -273,7 +275,7 @@ class QuerySessionSync(BaseQuerySession): tx_mode = tx_mode if tx_mode else _ydb_query_public.QuerySerializableReadWrite() - return BaseQueryTxContext( + return QueryTxContextSync( self._driver, self._state, self, @@ -283,9 +285,9 @@ class QuerySessionSync(BaseQuerySession): def execute( self, query: str, + parameters: dict = None, syntax: base.QuerySyntax = None, exec_mode: base.QueryExecMode = None, - parameters: dict = None, concurrent_result_sets: bool = False, ) -> base.SyncResponseContextIterator: """WARNING: This API is experimental and could be changed. @@ -313,5 +315,9 @@ class QuerySessionSync(BaseQuerySession): return base.SyncResponseContextIterator( stream_it, - lambda resp: base.wrap_execute_query_response(rpc_state=None, response_pb=resp), + lambda resp: base.wrap_execute_query_response( + rpc_state=None, + response_pb=resp, + settings=self._settings, + ), ) diff --git a/contrib/python/ydb/py3/ydb/query/transaction.py b/contrib/python/ydb/py3/ydb/query/transaction.py index 0a49320293b..750a94b0b57 100644 --- a/contrib/python/ydb/py3/ydb/query/transaction.py +++ b/contrib/python/ydb/py3/ydb/query/transaction.py @@ -169,7 +169,7 @@ def wrap_tx_rollback_response( return tx -class BaseQueryTxContext(base.IQueryTxContext): +class BaseQueryTxContext: def __init__(self, driver, session_state, session, tx_mode): """ An object that provides a simple transaction context manager that allows statements execution @@ -196,31 +196,6 @@ class BaseQueryTxContext(base.IQueryTxContext): self.session = session self._prev_stream = None - def __enter__(self) -> "BaseQueryTxContext": - """ - Enters a context manager and returns a transaction - - :return: A transaction instance - """ - return self - - def __exit__(self, *args, **kwargs): - """ - Closes a transaction context manager and rollbacks transaction if - it is not finished explicitly - """ - self._ensure_prev_stream_finished() - if self._tx_state._state == QueryTxStateEnum.BEGINED: - # It's strictly recommended to close transactions directly - # by using commit_tx=True flag while executing statement or by - # .commit() or .rollback() methods, but here we trying to do best - # effort to avoid useless open transactions - logger.warning("Potentially leaked tx: %s", self._tx_state.tx_id) - try: - self.rollback() - except issues.Error: - logger.warning("Failed to rollback leaked tx: %s", self._tx_state.tx_id) - @property def session_id(self) -> str: """ @@ -240,6 +215,8 @@ class BaseQueryTxContext(base.IQueryTxContext): return self._tx_state.tx_id def _begin_call(self, settings: Optional[base.QueryClientSettings]) -> "BaseQueryTxContext": + self._tx_state._check_invalid_transition(QueryTxStateEnum.BEGINED) + return self._driver( _create_begin_transaction_request(self._session_state, self._tx_state), _apis.QueryService.Stub, @@ -250,6 +227,8 @@ class BaseQueryTxContext(base.IQueryTxContext): ) def _commit_call(self, settings: Optional[base.QueryClientSettings]) -> "BaseQueryTxContext": + self._tx_state._check_invalid_transition(QueryTxStateEnum.COMMITTED) + return self._driver( _create_commit_transaction_request(self._session_state, self._tx_state), _apis.QueryService.Stub, @@ -260,6 +239,8 @@ class BaseQueryTxContext(base.IQueryTxContext): ) def _rollback_call(self, settings: Optional[base.QueryClientSettings]) -> "BaseQueryTxContext": + self._tx_state._check_invalid_transition(QueryTxStateEnum.ROLLBACKED) + return self._driver( _create_rollback_transaction_request(self._session_state, self._tx_state), _apis.QueryService.Stub, @@ -278,6 +259,8 @@ class BaseQueryTxContext(base.IQueryTxContext): parameters: dict = None, concurrent_result_sets: bool = False, ) -> Iterable[_apis.ydb_query.ExecuteQueryResponsePart]: + self._tx_state._check_tx_ready_to_use() + request = base.create_execute_query_request( query=query, session_id=self._session_state.session_id, @@ -296,12 +279,6 @@ class BaseQueryTxContext(base.IQueryTxContext): _apis.QueryService.ExecuteQuery, ) - def _ensure_prev_stream_finished(self) -> None: - if self._prev_stream is not None: - for _ in self._prev_stream: - pass - self._prev_stream = None - def _move_to_beginned(self, tx_id: str) -> None: if self._tx_state._already_in(QueryTxStateEnum.BEGINED): return @@ -313,19 +290,52 @@ class BaseQueryTxContext(base.IQueryTxContext): return self._tx_state._change_state(QueryTxStateEnum.COMMITTED) - def begin(self, settings: Optional[base.QueryClientSettings] = None) -> None: + +class QueryTxContextSync(BaseQueryTxContext): + def __enter__(self) -> "BaseQueryTxContext": + """ + Enters a context manager and returns a transaction + + :return: A transaction instance + """ + return self + + def __exit__(self, *args, **kwargs): + """ + Closes a transaction context manager and rollbacks transaction if + it is not finished explicitly + """ + self._ensure_prev_stream_finished() + if self._tx_state._state == QueryTxStateEnum.BEGINED: + # It's strictly recommended to close transactions directly + # by using commit_tx=True flag while executing statement or by + # .commit() or .rollback() methods, but here we trying to do best + # effort to avoid useless open transactions + logger.warning("Potentially leaked tx: %s", self._tx_state.tx_id) + try: + self.rollback() + except issues.Error: + logger.warning("Failed to rollback leaked tx: %s", self._tx_state.tx_id) + + def _ensure_prev_stream_finished(self) -> None: + if self._prev_stream is not None: + with self._prev_stream: + pass + self._prev_stream = None + + def begin(self, settings: Optional[base.QueryClientSettings] = None) -> "QueryTxContextSync": """WARNING: This API is experimental and could be changed. Explicitly begins a transaction :param settings: A request settings - :return: None or exception if begin is failed + :return: Transaction object or exception if begin is failed """ - self._tx_state._check_invalid_transition(QueryTxStateEnum.BEGINED) - self._begin_call(settings) + return self + def commit(self, settings: Optional[base.QueryClientSettings] = None) -> None: """WARNING: This API is experimental and could be changed. @@ -338,43 +348,51 @@ class BaseQueryTxContext(base.IQueryTxContext): """ if self._tx_state._already_in(QueryTxStateEnum.COMMITTED): return - self._ensure_prev_stream_finished() if self._tx_state._state == QueryTxStateEnum.NOT_INITIALIZED: self._tx_state._change_state(QueryTxStateEnum.COMMITTED) return - self._tx_state._check_invalid_transition(QueryTxStateEnum.COMMITTED) + self._ensure_prev_stream_finished() self._commit_call(settings) def rollback(self, settings: Optional[base.QueryClientSettings] = None) -> None: + """WARNING: This API is experimental and could be changed. + + Calls rollback on a transaction if it is open otherwise is no-op. If transaction execution + failed then this method raises PreconditionFailed. + + :param settings: A request settings + + :return: A committed transaction or exception if commit is failed + """ if self._tx_state._already_in(QueryTxStateEnum.ROLLBACKED): return - self._ensure_prev_stream_finished() - if self._tx_state._state == QueryTxStateEnum.NOT_INITIALIZED: self._tx_state._change_state(QueryTxStateEnum.ROLLBACKED) return - self._tx_state._check_invalid_transition(QueryTxStateEnum.ROLLBACKED) + self._ensure_prev_stream_finished() self._rollback_call(settings) def execute( self, query: str, + parameters: Optional[dict] = None, commit_tx: Optional[bool] = False, syntax: Optional[base.QuerySyntax] = None, exec_mode: Optional[base.QueryExecMode] = None, - parameters: Optional[dict] = None, concurrent_result_sets: Optional[bool] = False, + settings: Optional[base.QueryClientSettings] = None, ) -> base.SyncResponseContextIterator: """WARNING: This API is experimental and could be changed. Sends a query to Query Service :param query: (YQL or SQL text) to be executed. + :param parameters: dict with parameters and YDB types; :param commit_tx: A special flag that allows transaction commit. :param syntax: Syntax of the query, which is a one from the following choises: 1) QuerySyntax.YQL_V1, which is default; @@ -384,13 +402,12 @@ class BaseQueryTxContext(base.IQueryTxContext): 2) QueryExecMode.EXPLAIN; 3) QueryExecMode.VALIDATE; 4) QueryExecMode.PARSE. - :param parameters: dict with parameters and YDB types; :param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False; + :param settings: An additional request settings QueryClientSettings; :return: Iterator with result sets """ self._ensure_prev_stream_finished() - self._tx_state._check_tx_ready_to_use() stream_it = self._execute_call( query=query, @@ -400,6 +417,8 @@ class BaseQueryTxContext(base.IQueryTxContext): parameters=parameters, concurrent_result_sets=concurrent_result_sets, ) + + settings = settings if settings is not None else self.session._settings self._prev_stream = base.SyncResponseContextIterator( stream_it, lambda resp: base.wrap_execute_query_response( @@ -407,6 +426,7 @@ class BaseQueryTxContext(base.IQueryTxContext): response_pb=resp, tx=self, commit_tx=commit_tx, + settings=settings, ), ) return self._prev_stream diff --git a/contrib/python/ydb/py3/ydb/retries.py b/contrib/python/ydb/py3/ydb/retries.py index 5d4f6e6a0f7..c9c23b1a918 100644 --- a/contrib/python/ydb/py3/ydb/retries.py +++ b/contrib/python/ydb/py3/ydb/retries.py @@ -1,3 +1,4 @@ +import asyncio import random import time @@ -134,3 +135,27 @@ def retry_operation_sync(callee, retry_settings=None, *args, **kwargs): time.sleep(next_opt.timeout) else: return next_opt.result + + +async def retry_operation_async(callee, retry_settings=None, *args, **kwargs): # pylint: disable=W1113 + """ + The retry operation helper can be used to retry a coroutine that raises YDB specific + exceptions. + + :param callee: A coroutine to retry. + :param retry_settings: An instance of ydb.RetrySettings that describes how the coroutine + should be retried. If None, default instance of retry settings will be used. + :param args: A tuple with positional arguments to be passed into the coroutine. + :param kwargs: A dictionary with keyword arguments to be passed into the coroutine. + + Returns awaitable result of coroutine. If retries are not succussful exception is raised. + """ + opt_generator = retry_operation_impl(callee, retry_settings, *args, **kwargs) + for next_opt in opt_generator: + if isinstance(next_opt, YdbRetryOperationSleepOpt): + await asyncio.sleep(next_opt.timeout) + else: + try: + return await next_opt.result + except BaseException as e: # pylint: disable=W0703 + next_opt.set_exception(e) diff --git a/contrib/python/ydb/py3/ydb/ydb_version.py b/contrib/python/ydb/py3/ydb/ydb_version.py index 567cda12c76..76caab7b483 100644 --- a/contrib/python/ydb/py3/ydb/ydb_version.py +++ b/contrib/python/ydb/py3/ydb/ydb_version.py @@ -1 +1 @@ -VERSION = "3.15.0" +VERSION = "3.16.0" -- cgit v1.3 From 1fbd27b4e37aecbce5bc29b1084ebc08d49c44ab Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Thu, 22 Aug 2024 10:43:37 +0300 Subject: Intermediate changes --- contrib/python/google-auth/py3/.dist-info/METADATA | 2 +- .../py3/google/auth/_credentials_base.py | 75 +++++++++++ .../py3/google/auth/_exponential_backoff.py | 10 ++ .../google-auth/py3/google/auth/aio/__init__.py | 25 ++++ .../google-auth/py3/google/auth/aio/credentials.py | 143 +++++++++++++++++++++ .../py3/google/auth/compute_engine/_metadata.py | 21 +-- .../google-auth/py3/google/auth/credentials.py | 12 +- .../py3/google/auth/transport/_requests_base.py | 52 ++++++++ .../py3/google/auth/transport/requests.py | 5 +- .../python/google-auth/py3/google/auth/version.py | 2 +- .../google-auth/py3/google/oauth2/_client.py | 22 ++-- .../google-auth/py3/google/oauth2/_client_async.py | 20 +-- .../py3/tests/compute_engine/test__metadata.py | 15 ++- .../google-auth/py3/tests/oauth2/test__client.py | 10 +- .../py3/tests/test__exponential_backoff.py | 35 +++-- .../py3/tests/test_credentials_async.py | 136 ++++++++++++++++++++ contrib/python/google-auth/py3/ya.make | 6 +- contrib/python/hypothesis/py3/.dist-info/METADATA | 2 +- .../hypothesis/py3/hypothesis/internal/cathetus.py | 2 +- .../py3/hypothesis/internal/conjecture/data.py | 6 + .../hypothesis/internal/conjecture/junkdrawer.py | 18 ++- .../hypothesis/internal/conjecture/optimiser.py | 39 ++++-- .../py3/hypothesis/internal/conjecture/shrinker.py | 40 +++--- .../internal/conjecture/shrinking/common.py | 6 +- .../hypothesis/py3/hypothesis/internal/floats.py | 66 +++++++--- .../py3/hypothesis/strategies/_internal/core.py | 73 +++++++++-- .../py3/hypothesis/strategies/_internal/numbers.py | 16 ++- .../py3/hypothesis/strategies/_internal/types.py | 45 ++++++- .../python/hypothesis/py3/hypothesis/version.py | 2 +- contrib/python/hypothesis/py3/ya.make | 2 +- 30 files changed, 750 insertions(+), 158 deletions(-) create mode 100644 contrib/python/google-auth/py3/google/auth/_credentials_base.py create mode 100644 contrib/python/google-auth/py3/google/auth/aio/__init__.py create mode 100644 contrib/python/google-auth/py3/google/auth/aio/credentials.py create mode 100644 contrib/python/google-auth/py3/google/auth/transport/_requests_base.py create mode 100644 contrib/python/google-auth/py3/tests/test_credentials_async.py (limited to 'contrib/python') diff --git a/contrib/python/google-auth/py3/.dist-info/METADATA b/contrib/python/google-auth/py3/.dist-info/METADATA index 1814862af63..cdbc6833961 100644 --- a/contrib/python/google-auth/py3/.dist-info/METADATA +++ b/contrib/python/google-auth/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: google-auth -Version: 2.32.0 +Version: 2.33.0 Summary: Google Authentication Library Home-page: https://github.com/googleapis/google-auth-library-python Author: Google Cloud Platform diff --git a/contrib/python/google-auth/py3/google/auth/_credentials_base.py b/contrib/python/google-auth/py3/google/auth/_credentials_base.py new file mode 100644 index 00000000000..64d5ce34b9a --- /dev/null +++ b/contrib/python/google-auth/py3/google/auth/_credentials_base.py @@ -0,0 +1,75 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Interface for base credentials.""" + +import abc + +from google.auth import _helpers + + +class _BaseCredentials(metaclass=abc.ABCMeta): + """Base class for all credentials. + + All credentials have a :attr:`token` that is used for authentication and + may also optionally set an :attr:`expiry` to indicate when the token will + no longer be valid. + + Most credentials will be :attr:`invalid` until :meth:`refresh` is called. + Credentials can do this automatically before the first HTTP request in + :meth:`before_request`. + + Although the token and expiration will change as the credentials are + :meth:`refreshed ` and used, credentials should be considered + immutable. Various credentials will accept configuration such as private + keys, scopes, and other options. These options are not changeable after + construction. Some classes will provide mechanisms to copy the credentials + with modifications such as :meth:`ScopedCredentials.with_scopes`. + + Attributes: + token (Optional[str]): The bearer token that can be used in HTTP headers to make + authenticated requests. + """ + + def __init__(self): + self.token = None + + @abc.abstractmethod + def refresh(self, request): + """Refreshes the access token. + + Args: + request (google.auth.transport.Request): The object used to make + HTTP requests. + + Raises: + google.auth.exceptions.RefreshError: If the credentials could + not be refreshed. + """ + # pylint: disable=missing-raises-doc + # (pylint doesn't recognize that this is abstract) + raise NotImplementedError("Refresh must be implemented") + + def _apply(self, headers, token=None): + """Apply the token to the authentication header. + + Args: + headers (Mapping): The HTTP request headers. + token (Optional[str]): If specified, overrides the current access + token. + """ + headers["authorization"] = "Bearer {}".format( + _helpers.from_bytes(token or self.token) + ) diff --git a/contrib/python/google-auth/py3/google/auth/_exponential_backoff.py b/contrib/python/google-auth/py3/google/auth/_exponential_backoff.py index 0dd621a9492..04f9f976412 100644 --- a/contrib/python/google-auth/py3/google/auth/_exponential_backoff.py +++ b/contrib/python/google-auth/py3/google/auth/_exponential_backoff.py @@ -15,6 +15,8 @@ import random import time +from google.auth import exceptions + # The default amount of retry attempts _DEFAULT_RETRY_TOTAL_ATTEMPTS = 3 @@ -68,6 +70,11 @@ class ExponentialBackoff: randomization_factor=_DEFAULT_RANDOMIZATION_FACTOR, multiplier=_DEFAULT_MULTIPLIER, ): + if total_attempts < 1: + raise exceptions.InvalidValue( + f"total_attempts must be greater than or equal to 1 but was {total_attempts}" + ) + self._total_attempts = total_attempts self._initial_wait_seconds = initial_wait_seconds @@ -87,6 +94,9 @@ class ExponentialBackoff: raise StopIteration self._backoff_count += 1 + if self._backoff_count <= 1: + return self._backoff_count + jitter_variance = self._current_wait_in_seconds * self._randomization_factor jitter = random.uniform( self._current_wait_in_seconds - jitter_variance, diff --git a/contrib/python/google-auth/py3/google/auth/aio/__init__.py b/contrib/python/google-auth/py3/google/auth/aio/__init__.py new file mode 100644 index 00000000000..331708cba62 --- /dev/null +++ b/contrib/python/google-auth/py3/google/auth/aio/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Auth AIO Library for Python.""" + +import logging + +from google.auth import version as google_auth_version + + +__version__ = google_auth_version.__version__ + +# Set default logging handler to avoid "No handler found" warnings. +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/contrib/python/google-auth/py3/google/auth/aio/credentials.py b/contrib/python/google-auth/py3/google/auth/aio/credentials.py new file mode 100644 index 00000000000..3bc6a5a6762 --- /dev/null +++ b/contrib/python/google-auth/py3/google/auth/aio/credentials.py @@ -0,0 +1,143 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Interfaces for asynchronous credentials.""" + + +from google.auth import _helpers +from google.auth import exceptions +from google.auth._credentials_base import _BaseCredentials + + +class Credentials(_BaseCredentials): + """Base class for all asynchronous credentials. + + All credentials have a :attr:`token` that is used for authentication and + may also optionally set an :attr:`expiry` to indicate when the token will + no longer be valid. + + Most credentials will be :attr:`invalid` until :meth:`refresh` is called. + Credentials can do this automatically before the first HTTP request in + :meth:`before_request`. + + Although the token and expiration will change as the credentials are + :meth:`refreshed ` and used, credentials should be considered + immutable. Various credentials will accept configuration such as private + keys, scopes, and other options. These options are not changeable after + construction. Some classes will provide mechanisms to copy the credentials + with modifications such as :meth:`ScopedCredentials.with_scopes`. + """ + + def __init__(self): + super(Credentials, self).__init__() + + async def apply(self, headers, token=None): + """Apply the token to the authentication header. + + Args: + headers (Mapping): The HTTP request headers. + token (Optional[str]): If specified, overrides the current access + token. + """ + self._apply(headers, token=token) + + async def refresh(self, request): + """Refreshes the access token. + + Args: + request (google.auth.aio.transport.Request): The object used to make + HTTP requests. + + Raises: + google.auth.exceptions.RefreshError: If the credentials could + not be refreshed. + """ + raise NotImplementedError("Refresh must be implemented") + + async def before_request(self, request, method, url, headers): + """Performs credential-specific before request logic. + + Refreshes the credentials if necessary, then calls :meth:`apply` to + apply the token to the authentication header. + + Args: + request (google.auth.aio.transport.Request): The object used to make + HTTP requests. + method (str): The request's HTTP method or the RPC method being + invoked. + url (str): The request's URI or the RPC service's URI. + headers (Mapping): The request's headers. + """ + await self.apply(headers) + + +class StaticCredentials(Credentials): + """Asynchronous Credentials representing an immutable access token. + + The credentials are considered immutable except the tokens which can be + configured in the constructor :: + + credentials = StaticCredentials(token="token123") + + StaticCredentials does not support :meth `refresh` and assumes that the configured + token is valid and not expired. StaticCredentials will never attempt to + refresh the token. + """ + + def __init__(self, token): + """ + Args: + token (str): The access token. + """ + super(StaticCredentials, self).__init__() + self.token = token + + @_helpers.copy_docstring(Credentials) + async def refresh(self, request): + raise exceptions.InvalidOperation("Static credentials cannot be refreshed.") + + # Note: before_request should never try to refresh access tokens. + # StaticCredentials intentionally does not support it. + @_helpers.copy_docstring(Credentials) + async def before_request(self, request, method, url, headers): + await self.apply(headers) + + +class AnonymousCredentials(Credentials): + """Asynchronous Credentials that do not provide any authentication information. + + These are useful in the case of services that support anonymous access or + local service emulators that do not use credentials. + """ + + async def refresh(self, request): + """Raises :class:``InvalidOperation``, anonymous credentials cannot be + refreshed.""" + raise exceptions.InvalidOperation("Anonymous credentials cannot be refreshed.") + + async def apply(self, headers, token=None): + """Anonymous credentials do nothing to the request. + + The optional ``token`` argument is not supported. + + Raises: + google.auth.exceptions.InvalidValue: If a token was specified. + """ + if token is not None: + raise exceptions.InvalidValue("Anonymous credentials don't support tokens.") + + async def before_request(self, request, method, url, headers): + """Anonymous credentials do nothing to the request.""" + pass diff --git a/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py b/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py index e597365851c..69b7b524589 100644 --- a/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py +++ b/contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py @@ -28,11 +28,12 @@ from google.auth import _helpers from google.auth import environment_vars from google.auth import exceptions from google.auth import metrics +from google.auth._exponential_backoff import ExponentialBackoff _LOGGER = logging.getLogger(__name__) # Environment variable GCE_METADATA_HOST is originally named -# GCE_METADATA_ROOT. For compatiblity reasons, here it checks +# GCE_METADATA_ROOT. For compatibility reasons, here it checks # the new variable first; if not set, the system falls back # to the old variable. _GCE_METADATA_HOST = os.getenv(environment_vars.GCE_METADATA_HOST, None) @@ -119,11 +120,12 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3): # could lead to false negatives in the event that we are on GCE, but # the metadata resolution was particularly slow. The latter case is # "unlikely". - retries = 0 headers = _METADATA_HEADERS.copy() headers[metrics.API_CLIENT_HEADER] = metrics.mds_ping() - while retries < retry_count: + backoff = ExponentialBackoff(total_attempts=retry_count) + + for attempt in backoff: try: response = request( url=_METADATA_IP_ROOT, method="GET", headers=headers, timeout=timeout @@ -139,11 +141,10 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3): _LOGGER.warning( "Compute Engine Metadata server unavailable on " "attempt %s of %s. Reason: %s", - retries + 1, + attempt, retry_count, e, ) - retries += 1 return False @@ -179,7 +180,7 @@ def get( Returns: Union[Mapping, str]: If the metadata server returns JSON, a mapping of - the decoded JSON is return. Otherwise, the response content is + the decoded JSON is returned. Otherwise, the response content is returned as a string. Raises: @@ -198,8 +199,9 @@ def get( url = _helpers.update_query(base_url, query_params) - retries = 0 - while retries < retry_count: + backoff = ExponentialBackoff(total_attempts=retry_count) + + for attempt in backoff: try: response = request(url=url, method="GET", headers=headers_to_use) break @@ -208,11 +210,10 @@ def get( _LOGGER.warning( "Compute Engine Metadata server unavailable on " "attempt %s of %s. Reason: %s", - retries + 1, + attempt, retry_count, e, ) - retries += 1 else: raise exceptions.TransportError( "Failed to retrieve {} from the Google Compute Engine " diff --git a/contrib/python/google-auth/py3/google/auth/credentials.py b/contrib/python/google-auth/py3/google/auth/credentials.py index 27abd443dc0..e31930311be 100644 --- a/contrib/python/google-auth/py3/google/auth/credentials.py +++ b/contrib/python/google-auth/py3/google/auth/credentials.py @@ -22,12 +22,13 @@ import os from google.auth import _helpers, environment_vars from google.auth import exceptions from google.auth import metrics +from google.auth._credentials_base import _BaseCredentials from google.auth._refresh_worker import RefreshThreadManager DEFAULT_UNIVERSE_DOMAIN = "googleapis.com" -class Credentials(metaclass=abc.ABCMeta): +class Credentials(_BaseCredentials): """Base class for all credentials. All credentials have a :attr:`token` that is used for authentication and @@ -47,9 +48,8 @@ class Credentials(metaclass=abc.ABCMeta): """ def __init__(self): - self.token = None - """str: The bearer token that can be used in HTTP headers to make - authenticated requests.""" + super(Credentials, self).__init__() + self.expiry = None """Optional[datetime]: When the token expires and is no longer valid. If this is None, the token is assumed to never expire.""" @@ -167,9 +167,7 @@ class Credentials(metaclass=abc.ABCMeta): token (Optional[str]): If specified, overrides the current access token. """ - headers["authorization"] = "Bearer {}".format( - _helpers.from_bytes(token or self.token) - ) + self._apply(headers, token=token) """Trust boundary value will be a cached value from global lookup. The response of trust boundary will be a list of regions and a hex diff --git a/contrib/python/google-auth/py3/google/auth/transport/_requests_base.py b/contrib/python/google-auth/py3/google/auth/transport/_requests_base.py new file mode 100644 index 00000000000..ec718d909a3 --- /dev/null +++ b/contrib/python/google-auth/py3/google/auth/transport/_requests_base.py @@ -0,0 +1,52 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Transport adapter for Base Requests.""" + + +import abc + + +_DEFAULT_TIMEOUT = 120 # in second + + +class _BaseAuthorizedSession(metaclass=abc.ABCMeta): + """Base class for a Request Session with credentials. This class is intended to capture + the common logic between synchronous and asynchronous request sessions and is not intended to + be instantiated directly. + + Args: + credentials (google.auth._credentials_base.BaseCredentials): The credentials to + add to the request. + """ + + def __init__(self, credentials): + self.credentials = credentials + + @abc.abstractmethod + def request( + self, + method, + url, + data=None, + headers=None, + max_allowed_time=None, + timeout=_DEFAULT_TIMEOUT, + **kwargs + ): + raise NotImplementedError("Request must be implemented") + + @abc.abstractmethod + def close(self): + raise NotImplementedError("Close must be implemented") diff --git a/contrib/python/google-auth/py3/google/auth/transport/requests.py b/contrib/python/google-auth/py3/google/auth/transport/requests.py index 23a69783dc3..68f67c59bdf 100644 --- a/contrib/python/google-auth/py3/google/auth/transport/requests.py +++ b/contrib/python/google-auth/py3/google/auth/transport/requests.py @@ -38,6 +38,7 @@ from google.auth import environment_vars from google.auth import exceptions from google.auth import transport import google.auth.transport._mtls_helper +from google.auth.transport._requests_base import _BaseAuthorizedSession from google.oauth2 import service_account _LOGGER = logging.getLogger(__name__) @@ -292,7 +293,7 @@ class _MutualTlsOffloadAdapter(requests.adapters.HTTPAdapter): return super(_MutualTlsOffloadAdapter, self).proxy_manager_for(*args, **kwargs) -class AuthorizedSession(requests.Session): +class AuthorizedSession(requests.Session, _BaseAuthorizedSession): """A Requests Session class with credentials. This class is used to perform requests to API endpoints that require @@ -389,7 +390,7 @@ class AuthorizedSession(requests.Session): default_host=None, ): super(AuthorizedSession, self).__init__() - self.credentials = credentials + _BaseAuthorizedSession.__init__(self, credentials) self._refresh_status_codes = refresh_status_codes self._max_refresh_attempts = max_refresh_attempts self._refresh_timeout = refresh_timeout diff --git a/contrib/python/google-auth/py3/google/auth/version.py b/contrib/python/google-auth/py3/google/auth/version.py index 51f7f62acd7..c41f8776589 100644 --- a/contrib/python/google-auth/py3/google/auth/version.py +++ b/contrib/python/google-auth/py3/google/auth/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.32.0" +__version__ = "2.33.0" diff --git a/contrib/python/google-auth/py3/google/oauth2/_client.py b/contrib/python/google-auth/py3/google/oauth2/_client.py index bce797b88bb..68e13ddc734 100644 --- a/contrib/python/google-auth/py3/google/oauth2/_client.py +++ b/contrib/python/google-auth/py3/google/oauth2/_client.py @@ -183,7 +183,11 @@ def _token_endpoint_request_no_throw( if headers: headers_to_use.update(headers) - def _perform_request(): + response_data = {} + retryable_error = False + + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: response = request( method="POST", url=token_uri, headers=headers_to_use, body=body, **kwargs ) @@ -192,7 +196,7 @@ def _token_endpoint_request_no_throw( if hasattr(response.data, "decode") else response.data ) - response_data = "" + try: # response_body should be a JSON response_data = json.loads(response_body) @@ -206,18 +210,8 @@ def _token_endpoint_request_no_throw( status_code=response.status, response_data=response_data ) - return False, response_data, retryable_error - - request_succeeded, response_data, retryable_error = _perform_request() - - if request_succeeded or not retryable_error or not can_retry: - return request_succeeded, response_data, retryable_error - - retries = _exponential_backoff.ExponentialBackoff() - for _ in retries: - request_succeeded, response_data, retryable_error = _perform_request() - if request_succeeded or not retryable_error: - return request_succeeded, response_data, retryable_error + if not can_retry or not retryable_error: + return False, response_data, retryable_error return False, response_data, retryable_error diff --git a/contrib/python/google-auth/py3/google/oauth2/_client_async.py b/contrib/python/google-auth/py3/google/oauth2/_client_async.py index 2858d862b0b..8867f0a5274 100644 --- a/contrib/python/google-auth/py3/google/oauth2/_client_async.py +++ b/contrib/python/google-auth/py3/google/oauth2/_client_async.py @@ -67,7 +67,11 @@ async def _token_endpoint_request_no_throw( if access_token: headers["Authorization"] = "Bearer {}".format(access_token) - async def _perform_request(): + response_data = {} + retryable_error = False + + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: response = await request( method="POST", url=token_uri, headers=headers, body=body ) @@ -93,18 +97,8 @@ async def _token_endpoint_request_no_throw( status_code=response.status, response_data=response_data ) - return False, response_data, retryable_error - - request_succeeded, response_data, retryable_error = await _perform_request() - - if request_succeeded or not retryable_error or not can_retry: - return request_succeeded, response_data, retryable_error - - retries = _exponential_backoff.ExponentialBackoff() - for _ in retries: - request_succeeded, response_data, retryable_error = await _perform_request() - if request_succeeded or not retryable_error: - return request_succeeded, response_data, retryable_error + if not can_retry or not retryable_error: + return False, response_data, retryable_error return False, response_data, retryable_error diff --git a/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py b/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py index 35e3c089f90..352342f1509 100644 --- a/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py +++ b/contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py @@ -127,13 +127,15 @@ def test_ping_success_retry(mock_metrics_header_value): assert request.call_count == 2 -def test_ping_failure_bad_flavor(): +@mock.patch("time.sleep", return_value=None) +def test_ping_failure_bad_flavor(mock_sleep): request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"}) assert not _metadata.ping(request) -def test_ping_failure_connection_failed(): +@mock.patch("time.sleep", return_value=None) +def test_ping_failure_connection_failed(mock_sleep): request = make_request("") request.side_effect = exceptions.TransportError() @@ -196,7 +198,8 @@ def test_get_success_json_content_type_charset(): assert result[key] == value -def test_get_success_retry(): +@mock.patch("time.sleep", return_value=None) +def test_get_success_retry(mock_sleep): key, value = "foo", "bar" data = json.dumps({key: value}) @@ -312,7 +315,8 @@ def _test_get_success_custom_root_old_variable(): ) -def test_get_failure(): +@mock.patch("time.sleep", return_value=None) +def test_get_failure(mock_sleep): request = make_request("Metadata error", status=http_client.NOT_FOUND) with pytest.raises(exceptions.TransportError) as excinfo: @@ -339,7 +343,8 @@ def test_get_return_none_for_not_found_error(): ) -def test_get_failure_connection_failed(): +@mock.patch("time.sleep", return_value=None) +def test_get_failure_connection_failed(mock_sleep): request = make_request("") request.side_effect = exceptions.TransportError() diff --git a/contrib/python/google-auth/py3/tests/oauth2/test__client.py b/contrib/python/google-auth/py3/tests/oauth2/test__client.py index f9a2d3aff49..8736a4e27be 100644 --- a/contrib/python/google-auth/py3/tests/oauth2/test__client.py +++ b/contrib/python/google-auth/py3/tests/oauth2/test__client.py @@ -195,8 +195,8 @@ def test__token_endpoint_request_internal_failure_error(): _client._token_endpoint_request( request, "http://example.com", {"error_description": "internal_failure"} ) - # request should be called once and then with 3 retries - assert request.call_count == 4 + # request with 2 retries + assert request.call_count == 3 request = make_request( {"error": "internal_failure"}, status=http_client.BAD_REQUEST @@ -206,8 +206,8 @@ def test__token_endpoint_request_internal_failure_error(): _client._token_endpoint_request( request, "http://example.com", {"error": "internal_failure"} ) - # request should be called once and then with 3 retries - assert request.call_count == 4 + # request with 2 retries + assert request.call_count == 3 def test__token_endpoint_request_internal_failure_and_retry_failure_error(): @@ -626,6 +626,6 @@ def test__token_endpoint_request_no_throw_with_retry(can_retry): ) if can_retry: - assert mock_request.call_count == 4 + assert mock_request.call_count == 3 else: assert mock_request.call_count == 1 diff --git a/contrib/python/google-auth/py3/tests/test__exponential_backoff.py b/contrib/python/google-auth/py3/tests/test__exponential_backoff.py index 06a54527e6b..95422502b0d 100644 --- a/contrib/python/google-auth/py3/tests/test__exponential_backoff.py +++ b/contrib/python/google-auth/py3/tests/test__exponential_backoff.py @@ -13,8 +13,10 @@ # limitations under the License. import mock +import pytest # type: ignore from google.auth import _exponential_backoff +from google.auth import exceptions @mock.patch("time.sleep", return_value=None) @@ -24,18 +26,31 @@ def test_exponential_backoff(mock_time): iteration_count = 0 for attempt in eb: - backoff_interval = mock_time.call_args[0][0] - jitter = curr_wait * eb._randomization_factor - - assert (curr_wait - jitter) <= backoff_interval <= (curr_wait + jitter) - assert attempt == iteration_count + 1 - assert eb.backoff_count == iteration_count + 1 - assert eb._current_wait_in_seconds == eb._multiplier ** (iteration_count + 1) - - curr_wait = eb._current_wait_in_seconds + if attempt == 1: + assert mock_time.call_count == 0 + else: + backoff_interval = mock_time.call_args[0][0] + jitter = curr_wait * eb._randomization_factor + + assert (curr_wait - jitter) <= backoff_interval <= (curr_wait + jitter) + assert attempt == iteration_count + 1 + assert eb.backoff_count == iteration_count + 1 + assert eb._current_wait_in_seconds == eb._multiplier ** iteration_count + + curr_wait = eb._current_wait_in_seconds iteration_count += 1 assert eb.total_attempts == _exponential_backoff._DEFAULT_RETRY_TOTAL_ATTEMPTS assert eb.backoff_count == _exponential_backoff._DEFAULT_RETRY_TOTAL_ATTEMPTS assert iteration_count == _exponential_backoff._DEFAULT_RETRY_TOTAL_ATTEMPTS - assert mock_time.call_count == _exponential_backoff._DEFAULT_RETRY_TOTAL_ATTEMPTS + assert ( + mock_time.call_count == _exponential_backoff._DEFAULT_RETRY_TOTAL_ATTEMPTS - 1 + ) + + +def test_minimum_total_attempts(): + with pytest.raises(exceptions.InvalidValue): + _exponential_backoff.ExponentialBackoff(total_attempts=0) + with pytest.raises(exceptions.InvalidValue): + _exponential_backoff.ExponentialBackoff(total_attempts=-1) + _exponential_backoff.ExponentialBackoff(total_attempts=1) diff --git a/contrib/python/google-auth/py3/tests/test_credentials_async.py b/contrib/python/google-auth/py3/tests/test_credentials_async.py new file mode 100644 index 00000000000..51e4f0611c8 --- /dev/null +++ b/contrib/python/google-auth/py3/tests/test_credentials_async.py @@ -0,0 +1,136 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest # type: ignore + +from google.auth import exceptions +from google.auth.aio import credentials + + +class CredentialsImpl(credentials.Credentials): + pass + + +def test_credentials_constructor(): + credentials = CredentialsImpl() + assert not credentials.token + + +@pytest.mark.asyncio +async def test_before_request(): + credentials = CredentialsImpl() + request = "water" + headers = {} + credentials.token = "orchid" + + # before_request should not affect the value of the token. + await credentials.before_request(request, "http://example.com", "GET", headers) + assert credentials.token == "orchid" + assert headers["authorization"] == "Bearer orchid" + assert "x-allowed-locations" not in headers + + request = "earth" + headers = {} + + # Second call shouldn't affect token or headers. + await credentials.before_request(request, "http://example.com", "GET", headers) + assert credentials.token == "orchid" + assert headers["authorization"] == "Bearer orchid" + assert "x-allowed-locations" not in headers + + +@pytest.mark.asyncio +async def test_static_credentials_ctor(): + static_creds = credentials.StaticCredentials(token="orchid") + assert static_creds.token == "orchid" + + +@pytest.mark.asyncio +async def test_static_credentials_apply_default(): + static_creds = credentials.StaticCredentials(token="earth") + headers = {} + + await static_creds.apply(headers) + assert headers["authorization"] == "Bearer earth" + + await static_creds.apply(headers, token="orchid") + assert headers["authorization"] == "Bearer orchid" + + +@pytest.mark.asyncio +async def test_static_credentials_before_request(): + static_creds = credentials.StaticCredentials(token="orchid") + request = "water" + headers = {} + + # before_request should not affect the value of the token. + await static_creds.before_request(request, "http://example.com", "GET", headers) + assert static_creds.token == "orchid" + assert headers["authorization"] == "Bearer orchid" + assert "x-allowed-locations" not in headers + + request = "earth" + headers = {} + + # Second call shouldn't affect token or headers. + await static_creds.before_request(request, "http://example.com", "GET", headers) + assert static_creds.token == "orchid" + assert headers["authorization"] == "Bearer orchid" + assert "x-allowed-locations" not in headers + + +@pytest.mark.asyncio +async def test_static_credentials_refresh(): + static_creds = credentials.StaticCredentials(token="orchid") + request = "earth" + + with pytest.raises(exceptions.InvalidOperation) as exc: + await static_creds.refresh(request) + assert exc.match("Static credentials cannot be refreshed.") + + +@pytest.mark.asyncio +async def test_anonymous_credentials_ctor(): + anon = credentials.AnonymousCredentials() + assert anon.token is None + + +@pytest.mark.asyncio +async def test_anonymous_credentials_refresh(): + anon = credentials.AnonymousCredentials() + request = object() + with pytest.raises(exceptions.InvalidOperation) as exc: + await anon.refresh(request) + assert exc.match("Anonymous credentials cannot be refreshed.") + + +@pytest.mark.asyncio +async def test_anonymous_credentials_apply_default(): + anon = credentials.AnonymousCredentials() + headers = {} + await anon.apply(headers) + assert headers == {} + with pytest.raises(ValueError): + await anon.apply(headers, token="orchid") + + +@pytest.mark.asyncio +async def test_anonymous_credentials_before_request(): + anon = credentials.AnonymousCredentials() + request = object() + method = "GET" + url = "https://example.com/api/endpoint" + headers = {} + await anon.before_request(request, method, url, headers) + assert headers == {} diff --git a/contrib/python/google-auth/py3/ya.make b/contrib/python/google-auth/py3/ya.make index 4ea57aefcc9..caefae5db65 100644 --- a/contrib/python/google-auth/py3/ya.make +++ b/contrib/python/google-auth/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(2.32.0) +VERSION(2.33.0) LICENSE(Apache-2.0) @@ -28,6 +28,7 @@ PY_SRCS( google/auth/__init__.py google/auth/_cloud_sdk.py google/auth/_credentials_async.py + google/auth/_credentials_base.py google/auth/_default.py google/auth/_default_async.py google/auth/_exponential_backoff.py @@ -36,6 +37,8 @@ PY_SRCS( google/auth/_oauth2client.py google/auth/_refresh_worker.py google/auth/_service_account_info.py + google/auth/aio/__init__.py + google/auth/aio/credentials.py google/auth/api_key.py google/auth/app_engine.py google/auth/aws.py @@ -66,6 +69,7 @@ PY_SRCS( google/auth/transport/_custom_tls_signer.py google/auth/transport/_http_client.py google/auth/transport/_mtls_helper.py + google/auth/transport/_requests_base.py google/auth/transport/grpc.py google/auth/transport/mtls.py google/auth/transport/requests.py diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index e48da7dc0b5..9a38b4132f4 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.108.10 +Version: 6.110.0 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/cathetus.py b/contrib/python/hypothesis/py3/hypothesis/internal/cathetus.py index 30e0d214f1c..1f8f2fe82b2 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/cathetus.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/cathetus.py @@ -27,7 +27,7 @@ def cathetus(h, a): may be inaccurate up to a relative error of (around) floating-point epsilon. - Based on the C99 implementation https://github.com/jjgreen/cathetus + Based on the C99 implementation https://gitlab.com/jjg/cathetus """ if isnan(h): return nan diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py index 72bc4ba980b..40aad2e8501 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py @@ -679,6 +679,12 @@ class Examples: i += n return Example(self, i) + # not strictly necessary as we have len/getitem, but required for mypy. + # https://github.com/python/mypy/issues/9737 + def __iter__(self) -> Iterator[Example]: + for i in range(len(self)): + yield self[i] + @dataclass_transform() @attr.s(slots=True, frozen=True) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py index 4465f59e5c4..39382637db5 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py @@ -19,12 +19,14 @@ import time import warnings from random import Random from typing import ( + Any, Callable, Dict, Generic, Iterable, Iterator, List, + Literal, Optional, Sequence, Tuple, @@ -109,10 +111,10 @@ class IntList(Sequence[int]): def count(self, value: int) -> int: return self.__underlying.count(value) - def __repr__(self): + def __repr__(self) -> str: return f"IntList({list(self.__underlying)!r})" - def __len__(self): + def __len__(self) -> int: return len(self.__underlying) @overload @@ -305,7 +307,7 @@ class ensure_free_stackframes: a reasonable value of N). """ - def __enter__(self): + def __enter__(self) -> None: cur_depth = stack_depth_of_caller() self.old_maxdepth = sys.getrecursionlimit() # The default CPython recursionlimit is 1000, but pytest seems to bump @@ -418,8 +420,8 @@ class SelfOrganisingList(Generic[T]): _gc_initialized = False -_gc_start = 0 -_gc_cumulative_time = 0 +_gc_start: float = 0 +_gc_cumulative_time: float = 0 # Since gc_callback potentially runs in test context, and perf_counter # might be monkeypatched, we store a reference to the real one. @@ -431,7 +433,9 @@ def gc_cumulative_time() -> float: if not _gc_initialized: if hasattr(gc, "callbacks"): # CPython - def gc_callback(phase, info): + def gc_callback( + phase: Literal["start", "stop"], info: Dict[str, int] + ) -> None: global _gc_start, _gc_cumulative_time try: now = _perf_counter() @@ -453,7 +457,7 @@ def gc_cumulative_time() -> float: gc.callbacks.insert(0, gc_callback) elif hasattr(gc, "hooks"): # pragma: no cover # pypy only # PyPy - def hook(stats): + def hook(stats: Any) -> None: global _gc_cumulative_time try: _gc_cumulative_time += stats.duration diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/optimiser.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/optimiser.py index 2f8f7612234..a8f4478453e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/optimiser.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/optimiser.py @@ -8,9 +8,11 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. +from typing import Union + from hypothesis.internal.compat import int_from_bytes, int_to_bytes -from hypothesis.internal.conjecture.data import Status -from hypothesis.internal.conjecture.engine import BUFFER_SIZE +from hypothesis.internal.conjecture.data import ConjectureResult, Status, _Overrun +from hypothesis.internal.conjecture.engine import BUFFER_SIZE, ConjectureRunner from hypothesis.internal.conjecture.junkdrawer import find_integer from hypothesis.internal.conjecture.pareto import NO_SCORE @@ -31,7 +33,13 @@ class Optimiser: Software Testing and Analysis. ACM, 2017. """ - def __init__(self, engine, data, target, max_improvements=100): + def __init__( + self, + engine: ConjectureRunner, + data: ConjectureResult, + target: str, + max_improvements: int = 100, + ) -> None: """Optimise ``target`` starting from ``data``. Will stop either when we seem to have found a local maximum or when the target score has been improved ``max_improvements`` times. This limit is in place to @@ -42,21 +50,22 @@ class Optimiser: self.max_improvements = max_improvements self.improvements = 0 - def run(self): + def run(self) -> None: self.hill_climb() - def score_function(self, data): + def score_function(self, data: ConjectureResult) -> float: return data.target_observations.get(self.target, NO_SCORE) @property - def current_score(self): + def current_score(self) -> float: return self.score_function(self.current_data) - def consider_new_test_data(self, data): + def consider_new_data(self, data: Union[ConjectureResult, _Overrun]) -> bool: """Consider a new data object as a candidate target. If it is better than the current one, return True.""" if data.status < Status.VALID: return False + assert isinstance(data, ConjectureResult) score = self.score_function(data) if score < self.current_score: return False @@ -73,7 +82,7 @@ class Optimiser: return True return False - def hill_climb(self): + def hill_climb(self) -> None: """The main hill climbing loop where we actually do the work: Take data, and attempt to improve its score for target. select_example takes a data object and returns an index to an example where we should focus @@ -104,7 +113,7 @@ class Optimiser: if existing_as_int == max_int_value: continue - def attempt_replace(v): + def attempt_replace(v: int) -> bool: """Try replacing the current block in the current best test case with an integer of value i. Note that we use the *current* best and not the one we started with. This helps ensure that @@ -126,12 +135,14 @@ class Optimiser: + bytes(BUFFER_SIZE), ) - if self.consider_new_test_data(attempt): + if self.consider_new_data(attempt): return True - if attempt.status < Status.INVALID or len(attempt.buffer) == len( - self.current_data.buffer - ): + if attempt.status == Status.OVERRUN: + return False + + assert isinstance(attempt, ConjectureResult) + if len(attempt.buffer) == len(self.current_data.buffer): return False for i, ex in enumerate(self.current_data.examples): @@ -143,7 +154,7 @@ class Optimiser: if ex.length == ex_attempt.length: continue # pragma: no cover replacement = attempt.buffer[ex_attempt.start : ex_attempt.end] - if self.consider_new_test_data( + if self.consider_new_data( self.engine.cached_test_function( prefix + replacement diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py index a0043383720..d4418217876 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py @@ -9,7 +9,7 @@ # obtain one at https://mozilla.org/MPL/2.0/. from collections import defaultdict -from typing import TYPE_CHECKING, Callable, Dict, Optional, Union +from typing import TYPE_CHECKING, Callable, Dict, Optional, Tuple, TypeVar, Union import attr @@ -38,10 +38,14 @@ from hypothesis.internal.conjecture.shrinking import ( ) if TYPE_CHECKING: + from random import Random + from hypothesis.internal.conjecture.engine import ConjectureRunner +SortKeyT = TypeVar("SortKeyT", str, bytes) + -def sort_key(buffer): +def sort_key(buffer: SortKeyT) -> Tuple[int, SortKeyT]: """Returns a sort key such that "simpler" buffers are smaller than "more complicated" ones. @@ -357,16 +361,16 @@ class Shrinker: return self.passes_by_name[name] @property - def calls(self): + def calls(self) -> int: """Return the number of calls that have been made to the underlying test function.""" return self.engine.call_count @property - def misaligned(self): + def misaligned(self) -> int: return self.engine.misaligned_count - def check_calls(self): + def check_calls(self) -> None: if self.calls - self.calls_at_last_shrink >= self.max_stall: raise StopShrinking @@ -449,11 +453,11 @@ class Shrinker: self.check_calls() return result - def debug(self, msg): + def debug(self, msg: str) -> None: self.engine.debug(msg) @property - def random(self): + def random(self) -> "Random": return self.engine.random def shrink(self): @@ -1068,10 +1072,11 @@ class Shrinker: if node.was_forced: return False # pragma: no cover - if node.ir_type == "string": + if node.ir_type in {"string", "bytes"}: + size_kwarg = "min_size" if node.ir_type == "string" else "size" # if the size *increased*, we would have to guess what to pad with # in order to try fixing up this attempt. Just give up. - if node.kwargs["min_size"] <= attempt_kwargs["min_size"]: + if node.kwargs[size_kwarg] <= attempt_kwargs[size_kwarg]: return False # the size decreased in our attempt. Try again, but replace with # the min_size that we would have gotten, and truncate the value @@ -1082,22 +1087,7 @@ class Shrinker: initial_attempt[node.index].copy( with_kwargs=attempt_kwargs, with_value=initial_attempt[node.index].value[ - : attempt_kwargs["min_size"] - ], - ) - ] - + initial_attempt[node.index :] - ) - if node.ir_type == "bytes": - if node.kwargs["size"] <= attempt_kwargs["size"]: - return False - return self.consider_new_tree( - initial_attempt[: node.index] - + [ - initial_attempt[node.index].copy( - with_kwargs=attempt_kwargs, - with_value=initial_attempt[node.index].value[ - : attempt_kwargs["size"] + : attempt_kwargs[size_kwarg] ], ) ] diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py index 1de89bd18b8..b0c5ec8694e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py @@ -38,10 +38,10 @@ class Shrinker: self.debugging_enabled = debug @property - def calls(self): + def calls(self) -> int: return len(self.__seen) - def __repr__(self): + def __repr__(self) -> str: return "{}({}initial={!r}, current={!r})".format( type(self).__name__, "" if self.name is None else f"{self.name!r}, ", @@ -75,7 +75,7 @@ class Shrinker: return other_class.shrink(initial, predicate, **kwargs) - def debug(self, *args): + def debug(self, *args: object) -> None: if self.debugging_enabled: print("DEBUG", self, *args) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py index 6c4210e997b..79e6433dca8 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py @@ -11,22 +11,50 @@ import math import struct from sys import float_info -from typing import Callable, Optional, SupportsFloat +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Literal, + Optional, + SupportsFloat, + Tuple, + Union, +) + +if TYPE_CHECKING: + from typing import TypeAlias +else: + TypeAlias = object + +SignedIntFormat: "TypeAlias" = Literal["!h", "!i", "!q"] +UnsignedIntFormat: "TypeAlias" = Literal["!H", "!I", "!Q"] +IntFormat: "TypeAlias" = Union[SignedIntFormat, UnsignedIntFormat] +FloatFormat: "TypeAlias" = Literal["!e", "!f", "!d"] +Width: "TypeAlias" = Literal[16, 32, 64] # Format codes for (int, float) sized types, used for byte-wise casts. # See https://docs.python.org/3/library/struct.html#format-characters -STRUCT_FORMATS = { +STRUCT_FORMATS: Dict[int, Tuple[UnsignedIntFormat, FloatFormat]] = { 16: ("!H", "!e"), 32: ("!I", "!f"), 64: ("!Q", "!d"), } +TO_SIGNED_FORMAT: Dict[UnsignedIntFormat, SignedIntFormat] = { + "!H": "!h", + "!I": "!i", + "!Q": "!q", +} + -def reinterpret_bits(x, from_, to): - return struct.unpack(to, struct.pack(from_, x))[0] +def reinterpret_bits(x: float, from_: str, to: str) -> float: + x = struct.unpack(to, struct.pack(from_, x))[0] + assert isinstance(x, (float, int)) + return x -def float_of(x, width): +def float_of(x: SupportsFloat, width: Width) -> float: assert width in (16, 32, 64) if width == 64: return float(x) @@ -45,7 +73,7 @@ def is_negative(x: SupportsFloat) -> bool: ) from None -def count_between_floats(x, y, width=64): +def count_between_floats(x: float, y: float, width: int = 64) -> int: assert x <= y if is_negative(x): if is_negative(y): @@ -59,17 +87,19 @@ def count_between_floats(x, y, width=64): return float_to_int(y, width) - float_to_int(x, width) + 1 -def float_to_int(value, width=64): +def float_to_int(value: float, width: int = 64) -> int: fmt_int, fmt_flt = STRUCT_FORMATS[width] - return reinterpret_bits(value, fmt_flt, fmt_int) + x = reinterpret_bits(value, fmt_flt, fmt_int) + assert isinstance(x, int) + return x -def int_to_float(value, width=64): +def int_to_float(value: int, width: int = 64) -> float: fmt_int, fmt_flt = STRUCT_FORMATS[width] return reinterpret_bits(value, fmt_int, fmt_flt) -def next_up(value, width=64): +def next_up(value: float, width: int = 64) -> float: """Return the first float larger than finite `val` - IEEE 754's `nextUp`. From https://stackoverflow.com/a/10426033, with thanks to Mark Dickinson. @@ -81,34 +111,34 @@ def next_up(value, width=64): return 0.0 fmt_int, fmt_flt = STRUCT_FORMATS[width] # Note: n is signed; float_to_int returns unsigned - fmt_int = fmt_int.lower() - n = reinterpret_bits(value, fmt_flt, fmt_int) + fmt_int_signed = TO_SIGNED_FORMAT[fmt_int] + n = reinterpret_bits(value, fmt_flt, fmt_int_signed) if n >= 0: n += 1 else: n -= 1 - return reinterpret_bits(n, fmt_int, fmt_flt) + return reinterpret_bits(n, fmt_int_signed, fmt_flt) -def next_down(value, width=64): +def next_down(value: float, width: int = 64) -> float: return -next_up(-value, width) -def next_down_normal(value, width, allow_subnormal): +def next_down_normal(value: float, width: int, *, allow_subnormal: bool) -> float: value = next_down(value, width) if (not allow_subnormal) and 0 < abs(value) < width_smallest_normals[width]: return 0.0 if value > 0 else -width_smallest_normals[width] return value -def next_up_normal(value, width, allow_subnormal): - return -next_down_normal(-value, width, allow_subnormal) +def next_up_normal(value: float, width: int, *, allow_subnormal: bool) -> float: + return -next_down_normal(-value, width, allow_subnormal=allow_subnormal) # Smallest positive non-zero numbers that is fully representable by an # IEEE-754 float, calculated with the width's associated minimum exponent. # Values from https://en.wikipedia.org/wiki/IEEE_754#Basic_and_interchange_formats -width_smallest_normals = { +width_smallest_normals: Dict[int, float] = { 16: 2 ** -(2 ** (5 - 1) - 2), 32: 2 ** -(2 ** (8 - 1) - 2), 64: 2 ** -(2 ** (11 - 1) - 2), diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index 62dcecde207..7fdeedb4976 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -1295,6 +1295,12 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: if types.is_a_union(thing): args = sorted(thing.__args__, key=types.type_sorting_key) return one_of([_from_type(t) for t in args]) + if thing in types.LiteralStringTypes: # pragma: no cover + # We can't really cover this because it needs either + # typing-extensions or python3.11+ typing. + # `LiteralString` from runtime's point of view is just a string. + # Fallback to regular text. + return text() # We also have a special case for TypeVars. # They are represented as instances like `~T` when they come here. # We need to work with their type instead. @@ -1340,27 +1346,68 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: or hasattr(types.typing_extensions, "_TypedDictMeta") # type: ignore and type(thing) is types.typing_extensions._TypedDictMeta # type: ignore ): # pragma: no cover + + def _get_annotation_arg(key, annotation_type): + try: + return get_args(annotation_type)[0] + except IndexError: + raise InvalidArgument( + f"`{key}: {annotation_type.__name__}` is not a valid type annotation" + ) from None + + # Taken from `Lib/typing.py` and modified: + def _get_typeddict_qualifiers(key, annotation_type): + qualifiers = [] + while True: + annotation_origin = types.extended_get_origin(annotation_type) + if annotation_origin in types.AnnotatedTypes: + if annotation_args := get_args(annotation_type): + annotation_type = annotation_args[0] + else: + break + elif annotation_origin in types.RequiredTypes: + qualifiers.append(types.RequiredTypes) + annotation_type = _get_annotation_arg(key, annotation_type) + elif annotation_origin in types.NotRequiredTypes: + qualifiers.append(types.NotRequiredTypes) + annotation_type = _get_annotation_arg(key, annotation_type) + elif annotation_origin in types.ReadOnlyTypes: + qualifiers.append(types.ReadOnlyTypes) + annotation_type = _get_annotation_arg(key, annotation_type) + else: + break + return set(qualifiers), annotation_type + # The __optional_keys__ attribute may or may not be present, but if there's no # way to tell and we just have to assume that everything is required. # See https://github.com/python/cpython/pull/17214 for details. optional = set(getattr(thing, "__optional_keys__", ())) + required = set( + getattr(thing, "__required_keys__", get_type_hints(thing).keys()) + ) anns = {} for k, v in get_type_hints(thing).items(): - origin = get_origin(v) - if origin in types.RequiredTypes + types.NotRequiredTypes: - if origin in types.NotRequiredTypes: - optional.add(k) - else: - optional.discard(k) - try: - v = v.__args__[0] - except IndexError: - raise InvalidArgument( - f"`{k}: {v.__name__}` is not a valid type annotation" - ) from None + qualifiers, v = _get_typeddict_qualifiers(k, v) + # We ignore `ReadOnly` type for now, only unwrap it. + if types.RequiredTypes in qualifiers: + optional.discard(k) + required.add(k) + if types.NotRequiredTypes in qualifiers: + optional.add(k) + required.discard(k) + anns[k] = from_type_guarded(v) if anns[k] is ...: anns[k] = _from_type_deferred(v) + + if not required.isdisjoint(optional): # pragma: no cover + # It is impossible to cover, because `typing.py` or `typing-extensions` + # won't allow creating incorrect TypedDicts, + # this is just a sanity check from our side. + raise InvalidArgument( + f"Required keys overlap with optional keys in a TypedDict:" + f" {required=}, {optional=}" + ) if ( (not anns) and thing.__annotations__ @@ -1368,7 +1415,7 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: ): raise InvalidArgument("Failed to retrieve type annotations for local type") return fixed_dictionaries( # type: ignore - mapping={k: v for k, v in anns.items() if k not in optional}, + mapping={k: v for k, v in anns.items() if k in required}, optional={k: v for k, v in anns.items() if k in optional}, ) diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py index 507cf879d63..033577f2c86 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py @@ -380,22 +380,30 @@ def floats( if min_value is not None and ( exclude_min or (min_arg is not None and min_value < min_arg) ): - min_value = next_up_normal(min_value, width, assumed_allow_subnormal) + min_value = next_up_normal( + min_value, width, allow_subnormal=assumed_allow_subnormal + ) if min_value == min_arg: assert min_value == min_arg == 0 assert is_negative(min_arg) assert not is_negative(min_value) - min_value = next_up_normal(min_value, width, assumed_allow_subnormal) + min_value = next_up_normal( + min_value, width, allow_subnormal=assumed_allow_subnormal + ) assert min_value > min_arg # type: ignore if max_value is not None and ( exclude_max or (max_arg is not None and max_value > max_arg) ): - max_value = next_down_normal(max_value, width, assumed_allow_subnormal) + max_value = next_down_normal( + max_value, width, allow_subnormal=assumed_allow_subnormal + ) if max_value == max_arg: assert max_value == max_arg == 0 assert is_negative(max_value) assert not is_negative(max_arg) - max_value = next_down_normal(max_value, width, assumed_allow_subnormal) + max_value = next_down_normal( + max_value, width, allow_subnormal=assumed_allow_subnormal + ) assert max_value < max_arg # type: ignore if min_value == -math.inf: diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py index 8753bfb7844..d5fef48aad6 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py @@ -147,6 +147,49 @@ except AttributeError: # pragma: no cover pass # `typing_extensions` might not be installed +ReadOnlyTypes: tuple = () +try: + ReadOnlyTypes += (typing.ReadOnly,) # type: ignore +except AttributeError: # pragma: no cover + pass # Is missing for `python<3.13` +try: + ReadOnlyTypes += (typing_extensions.ReadOnly,) +except AttributeError: # pragma: no cover + pass # `typing_extensions` might not be installed + + +AnnotatedTypes: tuple = () +try: + AnnotatedTypes += (typing.Annotated,) +except AttributeError: # pragma: no cover + pass # Is missing for `python<3.9` +try: + AnnotatedTypes += (typing_extensions.Annotated,) +except AttributeError: # pragma: no cover + pass # `typing_extensions` might not be installed + + +LiteralStringTypes: tuple = () +try: + LiteralStringTypes += (typing.LiteralString,) # type: ignore +except AttributeError: # pragma: no cover + pass # Is missing for `python<3.11` +try: + LiteralStringTypes += (typing_extensions.LiteralString,) +except AttributeError: # pragma: no cover + pass # `typing_extensions` might not be installed + + +# We need this function to use `get_origin` on 3.8 for types added later: +# in typing-extensions, so we prefer this function over regular `get_origin` +# when unwrapping `TypedDict`'s annotations. +try: + extended_get_origin = typing_extensions.get_origin +except AttributeError: # pragma: no cover + # `typing_extensions` might not be installed, in this case - fallback: + extended_get_origin = get_origin # type: ignore + + # We use this variable to be sure that we are working with a type from `typing`: typing_root_type = (typing._Final, typing._GenericAlias) # type: ignore @@ -169,10 +212,10 @@ for name in ( "Self", "Required", "NotRequired", + "ReadOnly", "Never", "TypeVarTuple", "Unpack", - "LiteralString", ): try: NON_RUNTIME_TYPES += (getattr(typing, name),) diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index c8a8221c541..007af37ae6e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 108, 10) +__version_info__ = (6, 110, 0) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 8a025cf3596..7e33dc44974 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.108.10) +VERSION(6.110.0) LICENSE(MPL-2.0) -- cgit v1.3 From fb42c24f3a5a933f3e5c6680dd4378e310aab400 Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Thu, 22 Aug 2024 11:54:59 +0300 Subject: Intermediate changes --- contrib/libs/brotli/common/ya.make | 2 ++ contrib/libs/brotli/dec/ya.make | 2 ++ contrib/libs/brotli/enc/ya.make | 2 ++ contrib/python/numpy/py2/numpy/core/src/multiarray/ya.make | 2 ++ contrib/python/numpy/py2/numpy/core/src/umath/ya.make | 2 ++ contrib/python/numpy/py2/numpy/f2py/ya.make | 2 ++ contrib/python/numpy/py2/numpy/fft/ya.make | 2 ++ contrib/python/numpy/py2/numpy/linalg/ya.make | 2 ++ contrib/python/numpy/py2/numpy/random/mtrand/ya.make | 2 ++ contrib/python/numpy/py3/numpy/random/ya.make | 2 ++ contrib/tools/protoc/bin/ya.make | 2 ++ contrib/tools/protoc/plugins/grpc_cpp/bin/ya.make | 2 ++ contrib/tools/protoc/plugins/grpc_python/bin/ya.make | 2 ++ 13 files changed, 26 insertions(+) (limited to 'contrib/python') diff --git a/contrib/libs/brotli/common/ya.make b/contrib/libs/brotli/common/ya.make index 89e38a262cc..51318234221 100644 --- a/contrib/libs/brotli/common/ya.make +++ b/contrib/libs/brotli/common/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(1.0.1) + LICENSE(MIT) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) diff --git a/contrib/libs/brotli/dec/ya.make b/contrib/libs/brotli/dec/ya.make index 826e87a4f7c..8f714a28df1 100644 --- a/contrib/libs/brotli/dec/ya.make +++ b/contrib/libs/brotli/dec/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(1.0.1) + LICENSE(MIT) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) diff --git a/contrib/libs/brotli/enc/ya.make b/contrib/libs/brotli/enc/ya.make index d75ce198809..206e7e2d2df 100644 --- a/contrib/libs/brotli/enc/ya.make +++ b/contrib/libs/brotli/enc/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(1.0.1) + LICENSE(MIT) LICENSE_TEXTS(.yandex_meta/licenses.list.txt) diff --git a/contrib/python/numpy/py2/numpy/core/src/multiarray/ya.make b/contrib/python/numpy/py2/numpy/core/src/multiarray/ya.make index ea7aea99f3d..cd3d566a6a6 100644 --- a/contrib/python/numpy/py2/numpy/core/src/multiarray/ya.make +++ b/contrib/python/numpy/py2/numpy/core/src/multiarray/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/python/numpy/py2/numpy/core/src/umath/ya.make b/contrib/python/numpy/py2/numpy/core/src/umath/ya.make index 3d713128ddf..ef3b61a6b30 100644 --- a/contrib/python/numpy/py2/numpy/core/src/umath/ya.make +++ b/contrib/python/numpy/py2/numpy/core/src/umath/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/python/numpy/py2/numpy/f2py/ya.make b/contrib/python/numpy/py2/numpy/f2py/ya.make index 0501d5c3f8c..69f47f0e864 100644 --- a/contrib/python/numpy/py2/numpy/f2py/ya.make +++ b/contrib/python/numpy/py2/numpy/f2py/ya.make @@ -1,5 +1,7 @@ PY2_PROGRAM() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) PEERDIR( diff --git a/contrib/python/numpy/py2/numpy/fft/ya.make b/contrib/python/numpy/py2/numpy/fft/ya.make index 5038991af20..069b07107e5 100644 --- a/contrib/python/numpy/py2/numpy/fft/ya.make +++ b/contrib/python/numpy/py2/numpy/fft/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/python/numpy/py2/numpy/linalg/ya.make b/contrib/python/numpy/py2/numpy/linalg/ya.make index e514b952fe1..cac0af2f974 100644 --- a/contrib/python/numpy/py2/numpy/linalg/ya.make +++ b/contrib/python/numpy/py2/numpy/linalg/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/python/numpy/py2/numpy/random/mtrand/ya.make b/contrib/python/numpy/py2/numpy/random/mtrand/ya.make index 1ea8f472a1a..64d7da65026 100644 --- a/contrib/python/numpy/py2/numpy/random/mtrand/ya.make +++ b/contrib/python/numpy/py2/numpy/random/mtrand/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(1.16.6) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/python/numpy/py3/numpy/random/ya.make b/contrib/python/numpy/py3/numpy/random/ya.make index 6cc93532fb7..9c428baeee3 100644 --- a/contrib/python/numpy/py3/numpy/random/ya.make +++ b/contrib/python/numpy/py3/numpy/random/ya.make @@ -1,5 +1,7 @@ PY3_LIBRARY() +VERSION(1.26.4) + LICENSE(BSD-3-Clause) ADDINCL( diff --git a/contrib/tools/protoc/bin/ya.make b/contrib/tools/protoc/bin/ya.make index 2b9c2039388..1604015b70f 100644 --- a/contrib/tools/protoc/bin/ya.make +++ b/contrib/tools/protoc/bin/ya.make @@ -1,5 +1,7 @@ PROGRAM(protoc) +VERSION(None) + LICENSE(BSD-3-Clause) NO_COMPILER_WARNINGS() diff --git a/contrib/tools/protoc/plugins/grpc_cpp/bin/ya.make b/contrib/tools/protoc/plugins/grpc_cpp/bin/ya.make index 81672852db6..805b7e7bd79 100644 --- a/contrib/tools/protoc/plugins/grpc_cpp/bin/ya.make +++ b/contrib/tools/protoc/plugins/grpc_cpp/bin/ya.make @@ -1,5 +1,7 @@ PROGRAM(grpc_cpp) +VERSION(Service-proxy-version) + LICENSE(Apache-2.0) PEERDIR( diff --git a/contrib/tools/protoc/plugins/grpc_python/bin/ya.make b/contrib/tools/protoc/plugins/grpc_python/bin/ya.make index ab21ab6c50d..7b1885a2ba3 100644 --- a/contrib/tools/protoc/plugins/grpc_python/bin/ya.make +++ b/contrib/tools/protoc/plugins/grpc_python/bin/ya.make @@ -1,5 +1,7 @@ PROGRAM(grpc_python) +VERSION(None) + LICENSE(Apache-2.0) PEERDIR( -- cgit v1.3 From 9b7b894a85081d8b88c312d5797e86e3166cea7d Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Thu, 22 Aug 2024 12:08:26 +0300 Subject: Intermediate changes --- .../python/more-itertools/py3/.dist-info/METADATA | 4 +- contrib/python/more-itertools/py3/README.rst | 2 +- .../more-itertools/py3/more_itertools/__init__.py | 2 +- .../more-itertools/py3/more_itertools/more.py | 326 ++++++++++++++++----- .../more-itertools/py3/more_itertools/more.pyi | 128 +++++++- .../more-itertools/py3/more_itertools/recipes.py | 48 ++- .../python/more-itertools/py3/tests/test_more.py | 172 ++++++++--- .../more-itertools/py3/tests/test_recipes.py | 13 +- contrib/python/more-itertools/py3/ya.make | 2 +- 9 files changed, 558 insertions(+), 139 deletions(-) (limited to 'contrib/python') diff --git a/contrib/python/more-itertools/py3/.dist-info/METADATA b/contrib/python/more-itertools/py3/.dist-info/METADATA index fb41b0cfe69..c346b408809 100644 --- a/contrib/python/more-itertools/py3/.dist-info/METADATA +++ b/contrib/python/more-itertools/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: more-itertools -Version: 10.3.0 +Version: 10.4.0 Summary: More routines for operating on iterables, beyond itertools Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked Author-email: Erik Rose @@ -247,7 +247,7 @@ Blog posts about ``more-itertools``: * `Yo, I heard you like decorators `__ * `Tour of Python Itertools `__ (`Alternate `__) -* `Real-World Python More Itertools `_ +* `Real-World Python More Itertools `_ Development diff --git a/contrib/python/more-itertools/py3/README.rst b/contrib/python/more-itertools/py3/README.rst index 9f3e9c671f1..5be8552b2d5 100644 --- a/contrib/python/more-itertools/py3/README.rst +++ b/contrib/python/more-itertools/py3/README.rst @@ -223,7 +223,7 @@ Blog posts about ``more-itertools``: * `Yo, I heard you like decorators `__ * `Tour of Python Itertools `__ (`Alternate `__) -* `Real-World Python More Itertools `_ +* `Real-World Python More Itertools `_ Development diff --git a/contrib/python/more-itertools/py3/more_itertools/__init__.py b/contrib/python/more-itertools/py3/more_itertools/__init__.py index 9c4662fc31a..2e2fcbbe7b3 100644 --- a/contrib/python/more-itertools/py3/more_itertools/__init__.py +++ b/contrib/python/more-itertools/py3/more_itertools/__init__.py @@ -3,4 +3,4 @@ from .more import * # noqa from .recipes import * # noqa -__version__ = '10.3.0' +__version__ = '10.4.0' diff --git a/contrib/python/more-itertools/py3/more_itertools/more.py b/contrib/python/more-itertools/py3/more_itertools/more.py index 7b481907dae..3bf2c76b765 100644 --- a/contrib/python/more-itertools/py3/more_itertools/more.py +++ b/contrib/python/more-itertools/py3/more_itertools/more.py @@ -3,8 +3,9 @@ import warnings from collections import Counter, defaultdict, deque, abc from collections.abc import Sequence +from contextlib import suppress from functools import cached_property, partial, reduce, wraps -from heapq import heapify, heapreplace, heappop +from heapq import heapify, heapreplace from itertools import ( chain, combinations, @@ -21,10 +22,10 @@ from itertools import ( zip_longest, product, ) -from math import comb, e, exp, factorial, floor, fsum, log, perm, tau +from math import comb, e, exp, factorial, floor, fsum, log, log1p, perm, tau from queue import Empty, Queue -from random import random, randrange, uniform -from operator import itemgetter, mul, sub, gt, lt, ge, le +from random import random, randrange, shuffle, uniform +from operator import itemgetter, mul, sub, gt, lt, le from sys import hexversion, maxsize from time import monotonic @@ -34,7 +35,6 @@ from .recipes import ( UnequalIterablesError, consume, flatten, - pairwise, powerset, take, unique_everseen, @@ -473,12 +473,10 @@ def ilen(iterable): This consumes the iterable, so handle with care. """ - # This approach was selected because benchmarks showed it's likely the - # fastest of the known implementations at the time of writing. - # See GitHub tracker: #236, #230. - counter = count() - deque(zip(iterable, counter), maxlen=0) - return next(counter) + # This is the "most beautiful of the fast variants" of this function. + # If you think you can improve on it, please ensure that your version + # is both 10x faster and 10x more beautiful. + return sum(compress(repeat(1), zip(iterable))) def iterate(func, start): @@ -666,9 +664,9 @@ def distinct_permutations(iterable, r=None): >>> sorted(distinct_permutations([1, 0, 1])) [(0, 1, 1), (1, 0, 1), (1, 1, 0)] - Equivalent to ``set(permutations(iterable))``, except duplicates are not - generated and thrown away. For larger input sequences this is much more - efficient. + Equivalent to yielding from ``set(permutations(iterable))``, except + duplicates are not generated and thrown away. For larger input sequences + this is much more efficient. Duplicate permutations arise when there are duplicated elements in the input iterable. The number of items returned is @@ -683,6 +681,25 @@ def distinct_permutations(iterable, r=None): >>> sorted(distinct_permutations(range(3), r=2)) [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + *iterable* need not be sortable, but note that using equal (``x == y``) + but non-identical (``id(x) != id(y)``) elements may produce surprising + behavior. For example, ``1`` and ``True`` are equal but non-identical: + + >>> list(distinct_permutations([1, True, '3'])) # doctest: +SKIP + [ + (1, True, '3'), + (1, '3', True), + ('3', 1, True) + ] + >>> list(distinct_permutations([1, 2, '3'])) # doctest: +SKIP + [ + (1, 2, '3'), + (1, '3', 2), + (2, 1, '3'), + (2, '3', 1), + ('3', 1, 2), + ('3', 2, 1) + ] """ # Algorithm: https://w.wiki/Qai @@ -749,14 +766,44 @@ def distinct_permutations(iterable, r=None): i += 1 head[i:], tail[:] = tail[: r - i], tail[r - i :] - items = sorted(iterable) + items = list(iterable) + + try: + items.sort() + sortable = True + except TypeError: + sortable = False + + indices_dict = defaultdict(list) + + for item in items: + indices_dict[items.index(item)].append(item) + + indices = [items.index(item) for item in items] + indices.sort() + + equivalent_items = {k: cycle(v) for k, v in indices_dict.items()} + + def permuted_items(permuted_indices): + return tuple( + next(equivalent_items[index]) for index in permuted_indices + ) size = len(items) if r is None: r = size + # functools.partial(_partial, ... ) + algorithm = _full if (r == size) else partial(_partial, r=r) + if 0 < r <= size: - return _full(items) if (r == size) else _partial(items, r) + if sortable: + return algorithm(items) + else: + return ( + permuted_items(permuted_indices) + for permuted_indices in algorithm(indices) + ) return iter(() if r else ((),)) @@ -1743,7 +1790,9 @@ def zip_offset(*iterables, offsets, longest=False, fillvalue=None): return zip(*staggered) -def sort_together(iterables, key_list=(0,), key=None, reverse=False): +def sort_together( + iterables, key_list=(0,), key=None, reverse=False, strict=False +): """Return the input iterables sorted together, with *key_list* as the priority for sorting. All iterables are trimmed to the length of the shortest one. @@ -1782,6 +1831,10 @@ def sort_together(iterables, key_list=(0,), key=None, reverse=False): >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) [(3, 2, 1), ('a', 'b', 'c')] + If the *strict* keyword argument is ``True``, then + ``UnequalIterablesError`` will be raised if any of the iterables have + different lengths. + """ if key is None: # if there is no key function, the key argument to sorted is an @@ -1804,8 +1857,9 @@ def sort_together(iterables, key_list=(0,), key=None, reverse=False): *get_key_items(zipped_items) ) + zipper = zip_equal if strict else zip return list( - zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) + zipper(*sorted(zipper(*iterables), key=key_argument, reverse=reverse)) ) @@ -2747,8 +2801,6 @@ class seekable: >>> it.seek(0) >>> next(it), next(it), next(it) ('0', '1', '2') - >>> next(it) - '3' You can also seek forward: @@ -2756,15 +2808,29 @@ class seekable: >>> it.seek(10) >>> next(it) '10' - >>> it.relative_seek(-2) # Seeking relative to the current position - >>> next(it) - '9' >>> it.seek(20) # Seeking past the end of the source isn't a problem >>> list(it) [] >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it) + '0' + + Call :meth:`relative_seek` to seek relative to the source iterator's + current position. + + >>> it = seekable((str(n) for n in range(20))) >>> next(it), next(it), next(it) ('0', '1', '2') + >>> it.relative_seek(2) + >>> next(it) + '5' + >>> it.relative_seek(-3) # Source is at '6', we move back to '3' + >>> next(it) + '3' + >>> it.relative_seek(-3) # Source is at '4', we move back to '1' + >>> next(it) + '1' + Call :meth:`peek` to look ahead one item without advancing the iterator: @@ -2873,8 +2939,10 @@ class seekable: consume(self, remainder) def relative_seek(self, count): - index = len(self._cache) - self.seek(max(index + count, 0)) + if self._index is None: + self._index = len(self._cache) + + self.seek(max(self._index + count, 0)) class run_length: @@ -2903,7 +2971,7 @@ class run_length: @staticmethod def decode(iterable): - return chain.from_iterable(repeat(k, n) for k, n in iterable) + return chain.from_iterable(starmap(repeat, iterable)) def exactly_n(iterable, n, predicate=bool): @@ -2924,14 +2992,34 @@ def exactly_n(iterable, n, predicate=bool): return len(take(n + 1, filter(predicate, iterable))) == n -def circular_shifts(iterable): - """Return a list of circular shifts of *iterable*. +def circular_shifts(iterable, steps=1): + """Yield the circular shifts of *iterable*. - >>> circular_shifts(range(4)) + >>> list(circular_shifts(range(4))) [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + + Set *steps* to the number of places to rotate to the left + (or to the right if negative). Defaults to 1. + + >>> list(circular_shifts(range(4), 2)) + [(0, 1, 2, 3), (2, 3, 0, 1)] + + >>> list(circular_shifts(range(4), -1)) + [(0, 1, 2, 3), (3, 0, 1, 2), (2, 3, 0, 1), (1, 2, 3, 0)] + """ - lst = list(iterable) - return take(len(lst), windowed(cycle(lst), len(lst))) + buffer = deque(iterable) + if steps == 0: + raise ValueError('Steps should be a non-zero integer') + + buffer.rotate(steps) + steps = -steps + n = len(buffer) + n //= math.gcd(n, steps) + + for __ in repeat(None, n): + buffer.rotate(steps) + yield tuple(buffer) def make_decorator(wrapping_func, result_index=0): @@ -3191,7 +3279,7 @@ def partitions(iterable): yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] -def set_partitions(iterable, k=None): +def set_partitions(iterable, k=None, min_size=None, max_size=None): """ Yield the set partitions of *iterable* into *k* parts. Set partitions are not order-preserving. @@ -3215,6 +3303,20 @@ def set_partitions(iterable, k=None): ['b', 'ac'] ['a', 'b', 'c'] + if *min_size* and/or *max_size* are given, the minimum and/or maximum size + per block in partition is set. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, min_size=2): + ... print([''.join(p) for p in part]) + ['abc'] + >>> for part in set_partitions(iterable, max_size=2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + """ L = list(iterable) n = len(L) @@ -3226,6 +3328,11 @@ def set_partitions(iterable, k=None): elif k > n: return + min_size = min_size if min_size is not None else 0 + max_size = max_size if max_size is not None else n + if min_size > max_size: + return + def set_partitions_helper(L, k): n = len(L) if k == 1: @@ -3242,9 +3349,15 @@ def set_partitions(iterable, k=None): if k is None: for k in range(1, n + 1): - yield from set_partitions_helper(L, k) + yield from filter( + lambda z: all(min_size <= len(bk) <= max_size for bk in z), + set_partitions_helper(L, k), + ) else: - yield from set_partitions_helper(L, k) + yield from filter( + lambda z: all(min_size <= len(bk) <= max_size for bk in z), + set_partitions_helper(L, k), + ) class time_limited: @@ -3535,32 +3648,27 @@ def map_if(iterable, pred, func, func_else=lambda x: x): yield func(item) if pred(item) else func_else(item) -def _sample_unweighted(iterable, k): - # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: +def _sample_unweighted(iterator, k, strict): + # Algorithm L in the 1994 paper by Kim-Hung Li: # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". - # Fill up the reservoir (collection of samples) with the first `k` samples - reservoir = take(k, iterable) - - # Generate random number that's the largest in a sample of k U(0,1) numbers - # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic - W = exp(log(random()) / k) + reservoir = list(islice(iterator, k)) + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + W = 1.0 - # The number of elements to skip before changing the reservoir is a random - # number with a geometric distribution. Sample it using random() and logs. - next_index = k + floor(log(random()) / log(1 - W)) - - for index, element in enumerate(iterable, k): - if index == next_index: - reservoir[randrange(k)] = element - # The new W is the largest in a sample of k U(0, `old_W`) numbers + with suppress(StopIteration): + while True: W *= exp(log(random()) / k) - next_index += floor(log(random()) / log(1 - W)) + 1 + skip = floor(log(random()) / log1p(-W)) + element = next(islice(iterator, skip, None)) + reservoir[randrange(k)] = element + shuffle(reservoir) return reservoir -def _sample_weighted(iterable, k, weights): +def _sample_weighted(iterator, k, weights, strict): # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : # "Weighted random sampling with a reservoir". @@ -3569,7 +3677,10 @@ def _sample_weighted(iterable, k, weights): # Fill up the reservoir (collection of samples) with the first `k` # weight-keys and elements, then heapify the list. - reservoir = take(k, zip(weight_keys, iterable)) + reservoir = take(k, zip(weight_keys, iterator)) + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + heapify(reservoir) # The number of jumps before changing the reservoir is a random variable @@ -3577,7 +3688,7 @@ def _sample_weighted(iterable, k, weights): smallest_weight_key, _ = reservoir[0] weights_to_skip = log(random()) / smallest_weight_key - for weight, element in zip(weights, iterable): + for weight, element in zip(weights, iterator): if weight >= weights_to_skip: # The notation here is consistent with the paper, but we store # the weight-keys in log-space for better numerical stability. @@ -3591,44 +3702,103 @@ def _sample_weighted(iterable, k, weights): else: weights_to_skip -= weight - # Equivalent to [element for weight_key, element in sorted(reservoir)] - return [heappop(reservoir)[1] for _ in range(k)] + ret = [element for weight_key, element in reservoir] + shuffle(ret) + return ret + +def _sample_counted(population, k, counts, strict): + element = None + remaining = 0 -def sample(iterable, k, weights=None): + def feed(i): + # Advance *i* steps ahead and consume an element + nonlocal element, remaining + + while i + 1 > remaining: + i = i - remaining + element = next(population) + remaining = next(counts) + remaining -= i + 1 + return element + + with suppress(StopIteration): + reservoir = [] + for _ in range(k): + reservoir.append(feed(0)) + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + + W = 1.0 + while True: + W *= exp(log(random()) / k) + skip = floor(log(random()) / log1p(-W)) + element = feed(skip) + reservoir[randrange(k)] = element + + shuffle(reservoir) + return reservoir + + +def sample(iterable, k, weights=None, *, counts=None, strict=False): """Return a *k*-length list of elements chosen (without replacement) - from the *iterable*. Like :func:`random.sample`, but works on iterables - of unknown length. + from the *iterable*. Similar to :func:`random.sample`, but works on + iterables of unknown length. >>> iterable = range(100) >>> sample(iterable, 5) # doctest: +SKIP [81, 60, 96, 16, 4] - An iterable with *weights* may also be given: + For iterables with repeated elements, you may supply *counts* to + indicate the repeats. + + >>> iterable = ['a', 'b'] + >>> counts = [3, 4] # Equivalent to 'a', 'a', 'a', 'b', 'b', 'b', 'b' + >>> sample(iterable, k=3, counts=counts) # doctest: +SKIP + ['a', 'a', 'b'] + + An iterable with *weights* may be given: >>> iterable = range(100) >>> weights = (i * i + 1 for i in range(100)) >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP [79, 67, 74, 66, 78] - The algorithm can also be used to generate weighted random permutations. - The relative weight of each item determines the probability that it - appears late in the permutation. + Weighted selections are made without replacement. + After an element is selected, it is removed from the pool and the + relative weights of the other elements increase (this + does not match the behavior of :func:`random.sample`'s *counts* + parameter). Note that *weights* may not be used with *counts*. + + If the length of *iterable* is less than *k*, + ``ValueError`` is raised if *strict* is ``True`` and + all elements are returned (in shuffled order) if *strict* is ``False``. - >>> data = "abcdefgh" - >>> weights = range(1, len(data) + 1) - >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP - ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + By default, the `Algorithm L `__ reservoir sampling + technique is used. When *weights* are provided, + `Algorithm A-ExpJ `__ is used. """ + iterator = iter(iterable) + + if k < 0: + raise ValueError('k must be non-negative') + if k == 0: return [] - iterable = iter(iterable) - if weights is None: - return _sample_unweighted(iterable, k) - else: + if weights is not None and counts is not None: + raise TypeError('weights and counts are mutally exclusive') + + elif weights is not None: weights = iter(weights) - return _sample_weighted(iterable, k, weights) + return _sample_weighted(iterator, k, weights, strict) + + elif counts is not None: + counts = iter(counts) + return _sample_counted(iterator, k, counts, strict) + + else: + return _sample_unweighted(iterator, k, strict) def is_sorted(iterable, key=None, reverse=False, strict=False): @@ -3650,12 +3820,16 @@ def is_sorted(iterable, key=None, reverse=False, strict=False): False The function returns ``False`` after encountering the first out-of-order - item. If there are no out-of-order items, the iterable is exhausted. + item, which means it may produce results that differ from the built-in + :func:`sorted` function for objects with unusual comparison dynamics. + If there are no out-of-order items, the iterable is exhausted. """ + compare = le if strict else lt + it = iterable if (key is None) else map(key, iterable) + it_1, it_2 = tee(it) + next(it_2 if reverse else it_1, None) - compare = (le if reverse else ge) if strict else (lt if reverse else gt) - it = iterable if key is None else map(key, iterable) - return not any(starmap(compare, pairwise(it))) + return not any(map(compare, it_1, it_2)) class AbortThread(BaseException): diff --git a/contrib/python/more-itertools/py3/more_itertools/more.pyi b/contrib/python/more-itertools/py3/more_itertools/more.pyi index e9460232593..f1a155dce7d 100644 --- a/contrib/python/more-itertools/py3/more_itertools/more.pyi +++ b/contrib/python/more-itertools/py3/more_itertools/more.pyi @@ -2,6 +2,8 @@ from __future__ import annotations +import sys + from types import TracebackType from typing import ( Any, @@ -28,6 +30,9 @@ from typing_extensions import Protocol _T = TypeVar('_T') _T1 = TypeVar('_T1') _T2 = TypeVar('_T2') +_T3 = TypeVar('_T3') +_T4 = TypeVar('_T4') +_T5 = TypeVar('_T5') _U = TypeVar('_U') _V = TypeVar('_V') _W = TypeVar('_W') @@ -35,6 +40,12 @@ _T_co = TypeVar('_T_co', covariant=True) _GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[Any]]) _Raisable = BaseException | Type[BaseException] +# The type of isinstance's second argument (from typeshed builtins) +if sys.version_info >= (3, 10): + _ClassInfo = type | UnionType | tuple[_ClassInfo, ...] +else: + _ClassInfo = type | tuple[_ClassInfo, ...] + @type_check_only class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... @@ -135,7 +146,7 @@ def interleave_evenly( ) -> Iterator[_T]: ... def collapse( iterable: Iterable[Any], - base_type: type | None = ..., + base_type: _ClassInfo | None = ..., levels: int | None = ..., ) -> Iterator[Any]: ... @overload @@ -213,6 +224,7 @@ def stagger( class UnequalIterablesError(ValueError): def __init__(self, details: tuple[int, int, int] | None = ...) -> None: ... +# zip_equal @overload def zip_equal(__iter1: Iterable[_T1]) -> Iterator[tuple[_T1]]: ... @overload @@ -221,11 +233,35 @@ def zip_equal( ) -> Iterator[tuple[_T1, _T2]]: ... @overload def zip_equal( - __iter1: Iterable[_T], - __iter2: Iterable[_T], - __iter3: Iterable[_T], - *iterables: Iterable[_T], -) -> Iterator[tuple[_T, ...]]: ... + __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] +) -> Iterator[tuple[_T1, _T2, _T3]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], +) -> Iterator[tuple[_T1, _T2, _T3, _T4]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], + __iter5: Iterable[_T5], +) -> Iterator[tuple[_T1, _T2, _T3, _T4, _T5]]: ... +@overload +def zip_equal( + __iter1: Iterable[Any], + __iter2: Iterable[Any], + __iter3: Iterable[Any], + __iter4: Iterable[Any], + __iter5: Iterable[Any], + __iter6: Iterable[Any], + *iterables: Iterable[Any], +) -> Iterator[tuple[Any, ...]]: ... + +# zip_offset @overload def zip_offset( __iter1: Iterable[_T1], @@ -285,12 +321,13 @@ def sort_together( key_list: Iterable[int] = ..., key: Callable[..., Any] | None = ..., reverse: bool = ..., + strict: bool = ..., ) -> list[tuple[_T, ...]]: ... def unzip(iterable: Iterable[Sequence[_T]]) -> tuple[Iterator[_T], ...]: ... def divide(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... def always_iterable( obj: object, - base_type: type | tuple[type | tuple[Any, ...], ...] | None = ..., + base_type: _ClassInfo | None = ..., ) -> Iterator[Any]: ... def adjacent( predicate: Callable[[_T], bool], @@ -454,7 +491,9 @@ class run_length: def exactly_n( iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... ) -> bool: ... -def circular_shifts(iterable: Iterable[_T]) -> list[tuple[_T, ...]]: ... +def circular_shifts( + iterable: Iterable[_T], steps: int = 1 +) -> list[tuple[_T, ...]]: ... def make_decorator( wrapping_func: Callable[..., _U], result_index: int = ... ) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... @@ -500,7 +539,10 @@ def replace( ) -> Iterator[_T | _U]: ... def partitions(iterable: Iterable[_T]) -> Iterator[list[list[_T]]]: ... def set_partitions( - iterable: Iterable[_T], k: int | None = ... + iterable: Iterable[_T], + k: int | None = ..., + min_size: int | None = ..., + max_size: int | None = ..., ) -> Iterator[list[list[_T]]]: ... class time_limited(Generic[_T], Iterator[_T]): @@ -538,10 +580,22 @@ def map_if( func: Callable[[Any], Any], func_else: Callable[[Any], Any] | None = ..., ) -> Iterator[Any]: ... +def _sample_unweighted( + iterator: Iterator[_T], k: int, strict: bool +) -> list[_T]: ... +def _sample_counted( + population: Iterator[_T], k: int, counts: Iterable[int], strict: bool +) -> list[_T]: ... +def _sample_weighted( + iterator: Iterator[_T], k: int, weights, strict +) -> list[_T]: ... def sample( iterable: Iterable[_T], k: int, weights: Iterable[float] | None = ..., + *, + counts: Iterable[int] | None = ..., + strict: bool = False, ) -> list[_T]: ... def is_sorted( iterable: Iterable[_T], @@ -577,7 +631,7 @@ class callback_iter(Generic[_T], Iterator[_T]): def windowed_complete( iterable: Iterable[_T], n: int -) -> Iterator[tuple[_T, ...]]: ... +) -> Iterator[tuple[tuple[_T, ...], tuple[_T, ...], tuple[_T, ...]]]: ... def all_unique( iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... ) -> bool: ... @@ -608,9 +662,61 @@ class countable(Generic[_T], Iterator[_T]): items_seen: int def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[list[_T]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + __obj5: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + __obj5: _T | Iterable[_T], + __obj6: _T | Iterable[_T], *objects: _T | Iterable[_T], - scalar_types: type | tuple[type | tuple[Any, ...], ...] | None = ..., + scalar_types: _ClassInfo | None = ..., strict: bool = ..., ) -> Iterable[tuple[_T, ...]]: ... def unique_in_window( diff --git a/contrib/python/more-itertools/py3/more_itertools/recipes.py b/contrib/python/more-itertools/py3/more_itertools/recipes.py index b32fa955339..a21a1f5d88d 100644 --- a/contrib/python/more-itertools/py3/more_itertools/recipes.py +++ b/contrib/python/more-itertools/py3/more_itertools/recipes.py @@ -795,8 +795,30 @@ def triplewise(iterable): [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E')] """ - for (a, _), (b, c) in pairwise(pairwise(iterable)): - yield a, b, c + # This deviates from the itertools documentation reciple - see + # https://github.com/more-itertools/more-itertools/issues/889 + t1, t2, t3 = tee(iterable, 3) + next(t3, None) + next(t3, None) + next(t2, None) + return zip(t1, t2, t3) + + +def _sliding_window_islice(iterable, n): + # Fast path for small, non-zero values of n. + iterators = tee(iterable, n) + for i, iterator in enumerate(iterators): + next(islice(iterator, i, i), None) + return zip(*iterators) + + +def _sliding_window_deque(iterable, n): + # Normal path for other values of n. + it = iter(iterable) + window = deque(islice(it, n - 1), maxlen=n) + for x in it: + window.append(x) + yield tuple(window) def sliding_window(iterable, n): @@ -812,11 +834,16 @@ def sliding_window(iterable, n): For a variant with more features, see :func:`windowed`. """ - it = iter(iterable) - window = deque(islice(it, n - 1), maxlen=n) - for x in it: - window.append(x) - yield tuple(window) + if n > 20: + return _sliding_window_deque(iterable, n) + elif n > 2: + return _sliding_window_islice(iterable, n) + elif n == 2: + return pairwise(iterable) + elif n == 1: + return zip(iterable) + else: + raise ValueError(f'n should be at least one, not {n}') def subslices(iterable): @@ -1038,9 +1065,6 @@ def totient(n): >>> totient(12) 4 """ - # The itertools docs use unique_justseen instead of set; see - # https://github.com/more-itertools/more-itertools/issues/823 - for p in set(factor(n)): - n = n // p * (p - 1) - + for prime in set(factor(n)): + n -= n // prime return n diff --git a/contrib/python/more-itertools/py3/tests/test_more.py b/contrib/python/more-itertools/py3/tests/test_more.py index fda4c0984a3..1a70ea08e57 100644 --- a/contrib/python/more-itertools/py3/tests/test_more.py +++ b/contrib/python/more-itertools/py3/tests/test_more.py @@ -473,36 +473,11 @@ class ConsumerTests(TestCase): class DistinctPermutationsTests(TestCase): - def test_distinct_permutations(self): - """Make sure the output for ``distinct_permutations()`` is the same as - set(permutations(it)). - - """ + def test_basic(self): iterable = ['z', 'a', 'a', 'q', 'q', 'q', 'y'] - test_output = sorted(mi.distinct_permutations(iterable)) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) - - def test_other_iterables(self): - """Make sure ``distinct_permutations()`` accepts a different type of - iterables. - - """ - # a generator - iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y']) - test_output = sorted(mi.distinct_permutations(iterable)) - # "reload" it - iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y']) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) - - # an iterator - iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y']) - test_output = sorted(mi.distinct_permutations(iterable)) - # "reload" it - iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y']) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) + actual = list(mi.distinct_permutations(iterable)) + expected = set(permutations(iterable)) + self.assertCountEqual(actual, expected) def test_r(self): for iterable, r in ( @@ -524,9 +499,35 @@ class DistinctPermutationsTests(TestCase): ([], 4), ): with self.subTest(iterable=iterable, r=r): - expected = sorted(set(permutations(iterable, r))) - actual = sorted(mi.distinct_permutations(iter(iterable), r)) - self.assertEqual(actual, expected) + expected = set(permutations(iterable, r)) + actual = list(mi.distinct_permutations(iter(iterable), r)) + self.assertCountEqual(actual, expected) + + def test_unsortable(self): + iterable = ['1', 2, 2, 3, 3, 3] + actual = list(mi.distinct_permutations(iterable)) + expected = set(permutations(iterable)) + self.assertCountEqual(actual, expected) + + def test_unsortable_r(self): + iterable = ['1', 2, 2, 3, 3, 3] + for r in range(len(iterable) + 1): + with self.subTest(iterable=iterable, r=r): + actual = list(mi.distinct_permutations(iterable, r=r)) + expected = set(permutations(iterable, r=r)) + self.assertCountEqual(actual, expected) + + def test_unsorted_equivalent(self): + iterable = [1, True, '3'] + actual = list(mi.distinct_permutations(iterable)) + expected = set(permutations(iterable)) + self.assertCountEqual(actual, expected) + + def test_unhashable(self): + iterable = ([1], [1], 2) + actual = list(mi.distinct_permutations(iterable)) + expected = list(mi.unique_everseen(permutations(iterable))) + self.assertCountEqual(actual, expected) class IlenTests(TestCase): @@ -2172,6 +2173,29 @@ class SortTogetherTest(TestCase): ], ) + def test_strict(self): + # Test for list of lists or tuples + self.assertRaises( + mi.UnequalIterablesError, + lambda: mi.sort_together( + [(4, 3, 2, 1), ('a', 'b', 'c')], strict=True + ), + ) + + # Test for list of iterables + self.assertRaises( + mi.UnequalIterablesError, + lambda: mi.sort_together([range(4), range(5)], strict=True), + ) + + # Test for iterable of iterables + self.assertRaises( + mi.UnequalIterablesError, + lambda: mi.sort_together( + (range(i) for i in range(4)), strict=True + ), + ) + class DivideTest(TestCase): """Tests for divide()""" @@ -3347,6 +3371,10 @@ class SeekableTest(PeekableMixinTests, TestCase): self.assertEqual(next(s), '2') s.relative_seek(-2) self.assertEqual(next(s), '1') + s.relative_seek(-2) + self.assertEqual( + next(s), '0' + ) # Seek relative to current position within the cache s.relative_seek(-10) # Lower bound self.assertEqual(next(s), '0') s.relative_seek(10) # Lower bound @@ -3488,17 +3516,43 @@ class CircularShiftsTests(TestCase): def test_simple_circular_shifts(self): # test the a simple iterator case self.assertEqual( - mi.circular_shifts(range(4)), + list(mi.circular_shifts(range(4))), [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)], ) def test_duplicates(self): # test non-distinct entries self.assertEqual( - mi.circular_shifts([0, 1, 0, 1]), + list(mi.circular_shifts([0, 1, 0, 1])), [(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)], ) + def test_steps_positive(self): + actual = list(mi.circular_shifts(range(5), steps=2)) + expected = [ + (0, 1, 2, 3, 4), + (2, 3, 4, 0, 1), + (4, 0, 1, 2, 3), + (1, 2, 3, 4, 0), + (3, 4, 0, 1, 2), + ] + self.assertEqual(actual, expected) + + def test_steps_negative(self): + actual = list(mi.circular_shifts(range(5), steps=-2)) + expected = [ + (0, 1, 2, 3, 4), + (3, 4, 0, 1, 2), + (1, 2, 3, 4, 0), + (4, 0, 1, 2, 3), + (2, 3, 4, 0, 1), + ] + self.assertEqual(actual, expected) + + def test_steps_zero(self): + with self.assertRaises(ValueError): + list(mi.circular_shifts(range(5), steps=0)) + class MakeDecoratorTests(TestCase): def test_basic(self): @@ -3885,6 +3939,24 @@ class SetPartitionsTests(TestCase): def test_to_many_groups(self): self.assertEqual([], list(mi.set_partitions(range(4), 5))) + def test_min_size(self): + it = 'abc' + actual = mi.set_partitions(it, min_size=2) + expected = [['abc']] + self.assertEqual( + self._normalize_partitions(expected), + self._normalize_partitions(actual), + ) + + def test_max_size(self): + it = 'abc' + actual = mi.set_partitions(it, max_size=2) + expected = [['a', 'bc'], ['ab', 'c'], ['b', 'ac'], ['a', 'b', 'c']] + self.assertEqual( + self._normalize_partitions(expected), + self._normalize_partitions(actual), + ) + class TimeLimitedTests(TestCase): def test_basic(self): @@ -4145,6 +4217,11 @@ class SampleTests(TestCase): expected = ['f', 'e'] self.assertEqual(actual, expected) + def test_negative(self): + data = [1, 2, 3, 4, 5] + with self.assertRaises(ValueError): + mi.sample(data, k=-1) + def test_length(self): """Check that *k* elements are sampled.""" data = [1, 2, 3, 4, 5] @@ -4154,6 +4231,32 @@ class SampleTests(TestCase): expected = min(k, len(data)) self.assertEqual(actual, expected) + def test_strict(self): + data = ['1', '2', '3', '4', '5'] + self.assertEqual(set(mi.sample(data, 6, strict=False)), set(data)) + with self.assertRaises(ValueError): + mi.sample(data, 6, strict=True) + + def test_counts(self): + # Test with counts + seed(0) + iterable = ['red', 'blue'] + counts = [4, 2] + k = 5 + actual = list(mi.sample(iterable, counts=counts, k=k)) + + # Test without counts + seed(0) + decoded_iterable = (['red'] * 4) + (['blue'] * 2) + expected = list(mi.sample(decoded_iterable, k=k)) + + self.assertEqual(actual, expected) + + def test_counts_all(self): + actual = Counter(mi.sample('uwxyz', 35, counts=(1, 0, 4, 10, 20))) + expected = Counter({'u': 1, 'x': 4, 'y': 10, 'z': 20}) + self.assertEqual(actual, expected) + def test_sampling_entire_iterable(self): """If k=len(iterable), the sample contains the original elements.""" data = ["a", 2, "a", 4, (1, 2, 3)] @@ -4227,6 +4330,7 @@ class IsSortedTests(TestCase): ([1, 2, 3], {}, True), ([1, 1, 2, 3], {}, True), ([1, 10, 2, 3], {}, False), + ([3, float('nan'), 1, 2], {}, True), (['1', '10', '2', '3'], {}, True), (['1', '10', '2', '3'], {'key': int}, False), ([1, 2, 3], {'reverse': True}, False), diff --git a/contrib/python/more-itertools/py3/tests/test_recipes.py b/contrib/python/more-itertools/py3/tests/test_recipes.py index 0035e58d055..d3762d49dbe 100644 --- a/contrib/python/more-itertools/py3/tests/test_recipes.py +++ b/contrib/python/more-itertools/py3/tests/test_recipes.py @@ -838,7 +838,7 @@ class TriplewiseTests(TestCase): class SlidingWindowTests(TestCase): - def test_basic(self): + def test_islice_version(self): for iterable, n, expected in [ ([], 1, []), ([0], 1, [(0,)]), @@ -853,6 +853,17 @@ class SlidingWindowTests(TestCase): actual = list(mi.sliding_window(iterable, n)) self.assertEqual(actual, expected) + def test_deque_version(self): + iterable = map(str, range(100)) + all_windows = list(mi.sliding_window(iterable, 95)) + self.assertEqual(all_windows[0], tuple(map(str, range(95)))) + self.assertEqual(all_windows[-1], tuple(map(str, range(5, 100)))) + + def test_zero(self): + iterable = map(str, range(100)) + with self.assertRaises(ValueError): + list(mi.sliding_window(iterable, 0)) + class SubslicesTests(TestCase): def test_basic(self): diff --git a/contrib/python/more-itertools/py3/ya.make b/contrib/python/more-itertools/py3/ya.make index e902c9d552f..ee8f86bc142 100644 --- a/contrib/python/more-itertools/py3/ya.make +++ b/contrib/python/more-itertools/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(10.3.0) +VERSION(10.4.0) LICENSE(MIT) -- cgit v1.3 From 6494b885fc509f8bb4bee9de2fdc877f7076801d Mon Sep 17 00:00:00 2001 From: robot-piglet Date: Fri, 23 Aug 2024 10:55:51 +0300 Subject: Intermediate changes --- contrib/libs/base64/avx2/ya.make | 2 ++ contrib/libs/base64/neon32/ya.make | 2 ++ contrib/libs/base64/neon64/ya.make | 2 ++ contrib/libs/base64/plain32/ya.make | 2 ++ contrib/libs/base64/plain64/ya.make | 2 ++ contrib/libs/base64/ssse3/ya.make | 2 ++ contrib/libs/openssl/apps/ya.make | 2 ++ contrib/libs/openssl/crypto/ya.make | 2 ++ contrib/libs/tcmalloc/default/ya.make | 2 ++ contrib/libs/tcmalloc/no_percpu_cache/ya.make | 2 ++ contrib/libs/tcmalloc/numa_256k/ya.make | 2 ++ contrib/libs/tcmalloc/numa_large_pages/ya.make | 2 ++ contrib/libs/tcmalloc/small_but_slow/ya.make | 2 ++ contrib/python/hypothesis/py3/.dist-info/METADATA | 2 +- .../hypothesis/py3/hypothesis/strategies/_internal/types.py | 8 ++++++-- contrib/python/hypothesis/py3/hypothesis/version.py | 2 +- contrib/python/hypothesis/py3/ya.make | 2 +- contrib/python/matplotlib/py2/extern/agg24-svn/ya.make | 2 ++ contrib/python/matplotlib/py2/extern/ttconv/ya.make | 2 ++ contrib/python/matplotlib/py2/matplotlib/tri/ya.make | 2 ++ contrib/python/matplotlib/py2/src/ya.make | 2 ++ contrib/tools/python/base/ya.make | 2 ++ contrib/tools/python/include/ya.make | 2 ++ contrib/tools/python/lib/ya.make | 2 ++ contrib/tools/python/src/Modules/_sqlite/ya.make | 2 ++ 25 files changed, 51 insertions(+), 5 deletions(-) (limited to 'contrib/python') diff --git a/contrib/libs/base64/avx2/ya.make b/contrib/libs/base64/avx2/ya.make index d94d6c90614..df7a905a39f 100644 --- a/contrib/libs/base64/avx2/ya.make +++ b/contrib/libs/base64/avx2/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/base64/neon32/ya.make b/contrib/libs/base64/neon32/ya.make index 0e5890b95c1..434f91b78f3 100644 --- a/contrib/libs/base64/neon32/ya.make +++ b/contrib/libs/base64/neon32/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/base64/neon64/ya.make b/contrib/libs/base64/neon64/ya.make index fc89901e701..7f2a5f74938 100644 --- a/contrib/libs/base64/neon64/ya.make +++ b/contrib/libs/base64/neon64/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/base64/plain32/ya.make b/contrib/libs/base64/plain32/ya.make index ead784215c4..f978210f068 100644 --- a/contrib/libs/base64/plain32/ya.make +++ b/contrib/libs/base64/plain32/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/base64/plain64/ya.make b/contrib/libs/base64/plain64/ya.make index ead784215c4..f978210f068 100644 --- a/contrib/libs/base64/plain64/ya.make +++ b/contrib/libs/base64/plain64/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/base64/ssse3/ya.make b/contrib/libs/base64/ssse3/ya.make index 3f686dfda00..16055eac9a2 100644 --- a/contrib/libs/base64/ssse3/ya.make +++ b/contrib/libs/base64/ssse3/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(0.3.0) + LICENSE( BSD-2-Clause AND MIT diff --git a/contrib/libs/openssl/apps/ya.make b/contrib/libs/openssl/apps/ya.make index 4ecd259b071..b53b8caff91 100644 --- a/contrib/libs/openssl/apps/ya.make +++ b/contrib/libs/openssl/apps/ya.make @@ -1,5 +1,7 @@ PROGRAM(openssl) +VERSION(1.1.1t) + LICENSE( OpenSSL AND Public-Domain diff --git a/contrib/libs/openssl/crypto/ya.make b/contrib/libs/openssl/crypto/ya.make index c6e9d82018f..f588d9ff988 100644 --- a/contrib/libs/openssl/crypto/ya.make +++ b/contrib/libs/openssl/crypto/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(1.1.1t) + LICENSE( Apache-2.0 AND BSD-2-Clause AND diff --git a/contrib/libs/tcmalloc/default/ya.make b/contrib/libs/tcmalloc/default/ya.make index b610084ea6b..25a88b4bdeb 100644 --- a/contrib/libs/tcmalloc/default/ya.make +++ b/contrib/libs/tcmalloc/default/ya.make @@ -2,6 +2,8 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() +VERSION(2021-10-04-45c59ccbc062ac96d83710205033c656e490d376) + LICENSE(Apache-2.0) ALLOCATOR_IMPL() diff --git a/contrib/libs/tcmalloc/no_percpu_cache/ya.make b/contrib/libs/tcmalloc/no_percpu_cache/ya.make index cd57c6e24d0..a6b3ce700c4 100644 --- a/contrib/libs/tcmalloc/no_percpu_cache/ya.make +++ b/contrib/libs/tcmalloc/no_percpu_cache/ya.make @@ -2,6 +2,8 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() +VERSION(2021-10-04-45c59ccbc062ac96d83710205033c656e490d376) + LICENSE(Apache-2.0) ALLOCATOR_IMPL() diff --git a/contrib/libs/tcmalloc/numa_256k/ya.make b/contrib/libs/tcmalloc/numa_256k/ya.make index 89fe3f67888..d6b3bb2df31 100644 --- a/contrib/libs/tcmalloc/numa_256k/ya.make +++ b/contrib/libs/tcmalloc/numa_256k/ya.make @@ -2,6 +2,8 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() +VERSION(2021-10-04-45c59ccbc062ac96d83710205033c656e490d376) + LICENSE(Apache-2.0) ALLOCATOR_IMPL() diff --git a/contrib/libs/tcmalloc/numa_large_pages/ya.make b/contrib/libs/tcmalloc/numa_large_pages/ya.make index 56f658e3faf..8cb48bd4231 100644 --- a/contrib/libs/tcmalloc/numa_large_pages/ya.make +++ b/contrib/libs/tcmalloc/numa_large_pages/ya.make @@ -2,6 +2,8 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() +VERSION(2021-10-04-45c59ccbc062ac96d83710205033c656e490d376) + LICENSE(Apache-2.0) ALLOCATOR_IMPL() diff --git a/contrib/libs/tcmalloc/small_but_slow/ya.make b/contrib/libs/tcmalloc/small_but_slow/ya.make index 19803e9070c..6a21988172f 100644 --- a/contrib/libs/tcmalloc/small_but_slow/ya.make +++ b/contrib/libs/tcmalloc/small_but_slow/ya.make @@ -2,6 +2,8 @@ LIBRARY() WITHOUT_LICENSE_TEXTS() +VERSION(2021-10-04-45c59ccbc062ac96d83710205033c656e490d376) + LICENSE(Apache-2.0) ALLOCATOR_IMPL() diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 9a38b4132f4..1e09939db8f 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.110.0 +Version: 6.110.1 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py index d5fef48aad6..85051cfdbe6 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py @@ -120,7 +120,11 @@ try: except AttributeError: # pragma: no cover pass # Is missing for `python<3.10` try: - TypeGuardTypes += (typing_extensions.TypeGuard,) + TypeGuardTypes += (typing.TypeIs,) # type: ignore +except AttributeError: # pragma: no cover + pass # Is missing for `python<3.13` +try: + TypeGuardTypes += (typing_extensions.TypeGuard, typing_extensions.TypeIs) except AttributeError: # pragma: no cover pass # `typing_extensions` might not be installed @@ -1067,7 +1071,7 @@ def resolve_Callable(thing): if get_origin(return_type) in TypeGuardTypes: raise InvalidArgument( "Hypothesis cannot yet construct a strategy for callables which " - f"are PEP-647 TypeGuards (got {return_type!r}). " + f"are PEP-647 TypeGuards or PEP-742 TypeIs (got {return_type!r}). " "Consider using an explicit strategy, or opening an issue." ) diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index 007af37ae6e..e02ca3db876 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 110, 0) +__version_info__ = (6, 110, 1) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 7e33dc44974..44835a36ca2 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.110.0) +VERSION(6.110.1) LICENSE(MPL-2.0) diff --git a/contrib/python/matplotlib/py2/extern/agg24-svn/ya.make b/contrib/python/matplotlib/py2/extern/agg24-svn/ya.make index b8e286b07c4..c2ae9ed70fb 100644 --- a/contrib/python/matplotlib/py2/extern/agg24-svn/ya.make +++ b/contrib/python/matplotlib/py2/extern/agg24-svn/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(2.2.5) + LICENSE(PSF-2.0) ADDINCL( diff --git a/contrib/python/matplotlib/py2/extern/ttconv/ya.make b/contrib/python/matplotlib/py2/extern/ttconv/ya.make index 847ab2038db..c36ec960b9c 100644 --- a/contrib/python/matplotlib/py2/extern/ttconv/ya.make +++ b/contrib/python/matplotlib/py2/extern/ttconv/ya.make @@ -1,5 +1,7 @@ PY23_LIBRARY() +VERSION(2.2.5) + LICENSE(PSF-2.0) NO_WSHADOW() diff --git a/contrib/python/matplotlib/py2/matplotlib/tri/ya.make b/contrib/python/matplotlib/py2/matplotlib/tri/ya.make index ca8a6324716..fb0545dd9f3 100644 --- a/contrib/python/matplotlib/py2/matplotlib/tri/ya.make +++ b/contrib/python/matplotlib/py2/matplotlib/tri/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(2.2.5) + LICENSE(PSF-2.0) NO_COMPILER_WARNINGS() diff --git a/contrib/python/matplotlib/py2/src/ya.make b/contrib/python/matplotlib/py2/src/ya.make index 544aba39961..486bc04e49b 100644 --- a/contrib/python/matplotlib/py2/src/ya.make +++ b/contrib/python/matplotlib/py2/src/ya.make @@ -1,5 +1,7 @@ PY2_LIBRARY() +VERSION(2.2.5) + LICENSE(PSF-2.0) NO_COMPILER_WARNINGS() diff --git a/contrib/tools/python/base/ya.make b/contrib/tools/python/base/ya.make index 87f17811465..529e91dbe6b 100644 --- a/contrib/tools/python/base/ya.make +++ b/contrib/tools/python/base/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(2.7.18) + LICENSE(PSF-2.0) NO_WSHADOW() diff --git a/contrib/tools/python/include/ya.make b/contrib/tools/python/include/ya.make index f5d002f3eb0..7a5fa448664 100644 --- a/contrib/tools/python/include/ya.make +++ b/contrib/tools/python/include/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(2.7.18) + LICENSE(PSF-2.0) NO_WSHADOW() diff --git a/contrib/tools/python/lib/ya.make b/contrib/tools/python/lib/ya.make index 14f63489344..4c72332612d 100644 --- a/contrib/tools/python/lib/ya.make +++ b/contrib/tools/python/lib/ya.make @@ -2,6 +2,8 @@ LIBRARY() PROVIDES(python) +VERSION(2.7.18) + LICENSE(PSF-2.0) INCLUDE(${ARCADIA_ROOT}/contrib/tools/python/pyconfig.inc) diff --git a/contrib/tools/python/src/Modules/_sqlite/ya.make b/contrib/tools/python/src/Modules/_sqlite/ya.make index 9859a3923d0..827ed851515 100644 --- a/contrib/tools/python/src/Modules/_sqlite/ya.make +++ b/contrib/tools/python/src/Modules/_sqlite/ya.make @@ -1,5 +1,7 @@ LIBRARY() +VERSION(2.7.18) + LICENSE(Python-2.0) PEERDIR( -- cgit v1.3