aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2025-05-02 21:33:53 +0000
committerAlexander Smirnov <alex@ydb.tech>2025-05-02 21:33:53 +0000
commit726e4fe93a06affb8a5805f80f779e1ebc891ffc (patch)
tree0a22ac4b5a192f4cfc89252997f3d555396954d2 /contrib/python
parentcfede7fd10c5032b322bc335caff4d30c7674e6f (diff)
parent940be57633df4940e96f5754ce1bc0d4e5934dc8 (diff)
downloadydb-726e4fe93a06affb8a5805f80f779e1ebc891ffc.tar.gz
Merge pull request #17944 from ydb-platform/merge-libs-250501-0050
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/google-auth/py3/.dist-info/METADATA37
-rw-r--r--contrib/python/google-auth/py3/google/auth/_default.py38
-rw-r--r--contrib/python/google-auth/py3/google/auth/compute_engine/_metadata.py6
-rw-r--r--contrib/python/google-auth/py3/google/auth/identity_pool.py93
-rw-r--r--contrib/python/google-auth/py3/google/auth/impersonated_credentials.py75
-rw-r--r--contrib/python/google-auth/py3/google/auth/transport/urllib3.py16
-rw-r--r--contrib/python/google-auth/py3/google/auth/version.py2
-rw-r--r--contrib/python/google-auth/py3/google/oauth2/id_token.py12
-rw-r--r--contrib/python/google-auth/py3/patches/01-fix-tests.patch9
-rw-r--r--contrib/python/google-auth/py3/tests/compute_engine/test__metadata.py26
-rw-r--r--contrib/python/google-auth/py3/tests/data/trust_chain_with_leaf.pem52
-rw-r--r--contrib/python/google-auth/py3/tests/data/trust_chain_without_leaf.pem33
-rw-r--r--contrib/python/google-auth/py3/tests/data/trust_chain_wrong_order.pem52
-rw-r--r--contrib/python/google-auth/py3/tests/oauth2/test_id_token.py15
-rw-r--r--contrib/python/google-auth/py3/tests/test__oauth2client.py16
-rw-r--r--contrib/python/google-auth/py3/tests/test_identity_pool.py157
-rw-r--r--contrib/python/google-auth/py3/tests/test_impersonated_credentials.py39
-rw-r--r--contrib/python/google-auth/py3/ya.make2
-rw-r--r--contrib/python/prompt-toolkit/py3/.dist-info/METADATA16
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py3
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/completion/nested.py2
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py3
-rw-r--r--contrib/python/prompt-toolkit/py3/tests/test_filter.py2
-rw-r--r--contrib/python/prompt-toolkit/py3/tests/test_formatted_text.py14
-rw-r--r--contrib/python/prompt-toolkit/py3/ya.make2
-rw-r--r--contrib/python/pythran/bin/pythran/ya.make115
-rw-r--r--contrib/python/ydb/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/ydb/py3/ya.make2
-rw-r--r--contrib/python/ydb/py3/ydb/types.py89
-rw-r--r--contrib/python/ydb/py3/ydb/ydb_version.py2
30 files changed, 853 insertions, 79 deletions
diff --git a/contrib/python/google-auth/py3/.dist-info/METADATA b/contrib/python/google-auth/py3/.dist-info/METADATA
index 952e020e3cd..53da88bdec8 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.38.0
+Version: 2.39.0
Summary: Google Authentication Library
Home-page: https://github.com/googleapis/google-auth-library-python
Author: Google Cloud Platform
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
@@ -28,21 +29,49 @@ Requires-Dist: cachetools<6.0,>=2.0.0
Requires-Dist: pyasn1-modules>=0.2.1
Requires-Dist: rsa<5,>=3.1.4
Provides-Extra: aiohttp
-Requires-Dist: aiohttp<4.0.0.dev0,>=3.6.2; extra == "aiohttp"
-Requires-Dist: requests<3.0.0.dev0,>=2.20.0; extra == "aiohttp"
+Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "aiohttp"
+Requires-Dist: requests<3.0.0,>=2.20.0; extra == "aiohttp"
Provides-Extra: enterprise_cert
Requires-Dist: cryptography; extra == "enterprise-cert"
Requires-Dist: pyopenssl; extra == "enterprise-cert"
Provides-Extra: pyjwt
Requires-Dist: pyjwt>=2.0; extra == "pyjwt"
Requires-Dist: cryptography>=38.0.3; extra == "pyjwt"
+Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "pyjwt"
Provides-Extra: pyopenssl
Requires-Dist: pyopenssl>=20.0.0; extra == "pyopenssl"
Requires-Dist: cryptography>=38.0.3; extra == "pyopenssl"
+Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "pyopenssl"
Provides-Extra: reauth
Requires-Dist: pyu2f>=0.1.5; extra == "reauth"
Provides-Extra: requests
-Requires-Dist: requests<3.0.0.dev0,>=2.20.0; extra == "requests"
+Requires-Dist: requests<3.0.0,>=2.20.0; extra == "requests"
+Provides-Extra: testing
+Requires-Dist: grpcio; extra == "testing"
+Requires-Dist: flask; extra == "testing"
+Requires-Dist: freezegun; extra == "testing"
+Requires-Dist: mock; extra == "testing"
+Requires-Dist: oauth2client; extra == "testing"
+Requires-Dist: pyjwt>=2.0; extra == "testing"
+Requires-Dist: cryptography>=38.0.3; extra == "testing"
+Requires-Dist: pytest; extra == "testing"
+Requires-Dist: pytest-cov; extra == "testing"
+Requires-Dist: pytest-localserver; extra == "testing"
+Requires-Dist: pyopenssl>=20.0.0; extra == "testing"
+Requires-Dist: pyu2f>=0.1.5; extra == "testing"
+Requires-Dist: responses; extra == "testing"
+Requires-Dist: urllib3; extra == "testing"
+Requires-Dist: packaging; extra == "testing"
+Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "testing"
+Requires-Dist: requests<3.0.0,>=2.20.0; extra == "testing"
+Requires-Dist: aioresponses; extra == "testing"
+Requires-Dist: pytest-asyncio; extra == "testing"
+Requires-Dist: pyopenssl<24.3.0; extra == "testing"
+Requires-Dist: aiohttp<3.10.0; extra == "testing"
+Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "testing"
+Provides-Extra: urllib3
+Requires-Dist: urllib3; extra == "urllib3"
+Requires-Dist: packaging; extra == "urllib3"
Google Auth Python Library
==========================
diff --git a/contrib/python/google-auth/py3/google/auth/_default.py b/contrib/python/google-auth/py3/google/auth/_default.py
index 1234fb25d78..cf0cdd77298 100644
--- a/contrib/python/google-auth/py3/google/auth/_default.py
+++ b/contrib/python/google-auth/py3/google/auth/_default.py
@@ -484,42 +484,8 @@ def _get_impersonated_service_account_credentials(filename, info, scopes):
from google.auth import impersonated_credentials
try:
- source_credentials_info = info.get("source_credentials")
- source_credentials_type = source_credentials_info.get("type")
- if source_credentials_type == _AUTHORIZED_USER_TYPE:
- source_credentials, _ = _get_authorized_user_credentials(
- filename, source_credentials_info
- )
- elif source_credentials_type == _SERVICE_ACCOUNT_TYPE:
- source_credentials, _ = _get_service_account_credentials(
- filename, source_credentials_info
- )
- elif source_credentials_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE:
- source_credentials, _ = _get_external_account_authorized_user_credentials(
- filename, source_credentials_info
- )
- else:
- raise exceptions.InvalidType(
- "source credential of type {} is not supported.".format(
- source_credentials_type
- )
- )
- impersonation_url = info.get("service_account_impersonation_url")
- start_index = impersonation_url.rfind("/")
- end_index = impersonation_url.find(":generateAccessToken")
- if start_index == -1 or end_index == -1 or start_index > end_index:
- raise exceptions.InvalidValue(
- "Cannot extract target principal from {}".format(impersonation_url)
- )
- target_principal = impersonation_url[start_index + 1 : end_index]
- delegates = info.get("delegates")
- quota_project_id = info.get("quota_project_id")
- credentials = impersonated_credentials.Credentials(
- source_credentials,
- target_principal,
- scopes,
- delegates,
- quota_project_id=quota_project_id,
+ credentials = impersonated_credentials.Credentials.from_impersonated_service_account_info(
+ info, scopes=scopes
)
except ValueError as caught_exc:
msg = "Failed to load impersonated service account credentials from {}".format(
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 06f99de0e2c..ddbe8ac2f70 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
@@ -159,6 +159,7 @@ def get(
retry_count=5,
headers=None,
return_none_for_not_found_error=False,
+ timeout=_METADATA_DEFAULT_TIMEOUT,
):
"""Fetch a resource from the metadata server.
@@ -178,6 +179,7 @@ def get(
headers (Optional[Mapping[str, str]]): Headers for the request.
return_none_for_not_found_error (Optional[bool]): If True, returns None
for 404 error instead of throwing an exception.
+ timeout (int): How long to wait, in seconds for the metadata server to respond.
Returns:
Union[Mapping, str]: If the metadata server returns JSON, a mapping of
@@ -204,7 +206,9 @@ def get(
failure_reason = None
for attempt in backoff:
try:
- response = request(url=url, method="GET", headers=headers_to_use)
+ response = request(
+ url=url, method="GET", headers=headers_to_use, timeout=timeout
+ )
if response.status in transport.DEFAULT_RETRYABLE_STATUS_CODES:
_LOGGER.warning(
"Compute Engine Metadata server unavailable on "
diff --git a/contrib/python/google-auth/py3/google/auth/identity_pool.py b/contrib/python/google-auth/py3/google/auth/identity_pool.py
index 47f9a55715c..c06f8842870 100644
--- a/contrib/python/google-auth/py3/google/auth/identity_pool.py
+++ b/contrib/python/google-auth/py3/google/auth/identity_pool.py
@@ -41,6 +41,7 @@ try:
except ImportError: # pragma: NO COVER
from collections import Mapping # type: ignore
import abc
+import base64
import json
import os
from typing import NamedTuple
@@ -145,9 +146,88 @@ class _UrlSupplier(SubjectTokenSupplier):
class _X509Supplier(SubjectTokenSupplier):
"""Internal supplier for X509 workload credentials. This class is used internally and always returns an empty string as the subject token."""
+ def __init__(self, trust_chain_path, leaf_cert_callback):
+ self._trust_chain_path = trust_chain_path
+ self._leaf_cert_callback = leaf_cert_callback
+
@_helpers.copy_docstring(SubjectTokenSupplier)
def get_subject_token(self, context, request):
- return ""
+ # Import OpennSSL inline because it is an extra import only required by customers
+ # using mTLS.
+ from OpenSSL import crypto
+
+ leaf_cert = crypto.load_certificate(
+ crypto.FILETYPE_PEM, self._leaf_cert_callback()
+ )
+ trust_chain = self._read_trust_chain()
+ cert_chain = []
+
+ cert_chain.append(_X509Supplier._encode_cert(leaf_cert))
+
+ if trust_chain is None or len(trust_chain) == 0:
+ return json.dumps(cert_chain)
+
+ # Append the first cert if it is not the leaf cert.
+ first_cert = _X509Supplier._encode_cert(trust_chain[0])
+ if first_cert != cert_chain[0]:
+ cert_chain.append(first_cert)
+
+ for i in range(1, len(trust_chain)):
+ encoded = _X509Supplier._encode_cert(trust_chain[i])
+ # Check if the current cert is the leaf cert and raise an exception if it is.
+ if encoded == cert_chain[0]:
+ raise exceptions.RefreshError(
+ "The leaf certificate must be at the top of the trust chain file"
+ )
+ else:
+ cert_chain.append(encoded)
+ return json.dumps(cert_chain)
+
+ def _read_trust_chain(self):
+ # Import OpennSSL inline because it is an extra import only required by customers
+ # using mTLS.
+ from OpenSSL import crypto
+
+ certificate_trust_chain = []
+ # If no trust chain path was provided, return an empty list.
+ if self._trust_chain_path is None or self._trust_chain_path == "":
+ return certificate_trust_chain
+ try:
+ # Open the trust chain file.
+ with open(self._trust_chain_path, "rb") as f:
+ trust_chain_data = f.read()
+ # Split PEM data into individual certificates.
+ cert_blocks = trust_chain_data.split(b"-----BEGIN CERTIFICATE-----")
+ for cert_block in cert_blocks:
+ # Skip empty blocks.
+ if cert_block.strip():
+ cert_data = b"-----BEGIN CERTIFICATE-----" + cert_block
+ try:
+ # Load each certificate and add it to the trust chain.
+ cert = crypto.load_certificate(
+ crypto.FILETYPE_PEM, cert_data
+ )
+ certificate_trust_chain.append(cert)
+ except Exception as e:
+ raise exceptions.RefreshError(
+ "Error loading PEM certificates from the trust chain file '{}'".format(
+ self._trust_chain_path
+ )
+ ) from e
+ return certificate_trust_chain
+ except FileNotFoundError:
+ raise exceptions.RefreshError(
+ "Trust chain file '{}' was not found.".format(self._trust_chain_path)
+ )
+
+ def _encode_cert(cert):
+ # Import OpennSSL inline because it is an extra import only required by customers
+ # using mTLS.
+ from OpenSSL import crypto
+
+ return base64.b64encode(
+ crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)
+ ).decode("utf-8")
def _parse_token_data(token_content, format_type="text", subject_token_field_name=None):
@@ -296,7 +376,9 @@ class Credentials(external_account.Credentials):
self._credential_source_headers,
)
else: # self._credential_source_certificate
- self._subject_token_supplier = _X509Supplier()
+ self._subject_token_supplier = _X509Supplier(
+ self._trust_chain_path, self._get_cert_bytes
+ )
@_helpers.copy_docstring(external_account.Credentials)
def retrieve_subject_token(self, request):
@@ -314,6 +396,10 @@ class Credentials(external_account.Credentials):
self._certificate_config_location
)
+ def _get_cert_bytes(self):
+ cert_path, _ = self._get_mtls_cert_and_key_paths()
+ return _mtls_helper._read_cert_file(cert_path)
+
def _mtls_required(self):
return self._credential_source_certificate is not None
@@ -350,6 +436,9 @@ class Credentials(external_account.Credentials):
use_default = self._credential_source_certificate.get(
"use_default_certificate_config"
)
+ self._trust_chain_path = self._credential_source_certificate.get(
+ "trust_chain_path"
+ )
if self._certificate_config_location and use_default:
raise exceptions.MalformedError(
"Invalid certificate configuration, certificate_config_location cannot be specified when use_default_certificate_config = true."
diff --git a/contrib/python/google-auth/py3/google/auth/impersonated_credentials.py b/contrib/python/google-auth/py3/google/auth/impersonated_credentials.py
index ed7e3f00b1c..d49998cfbdc 100644
--- a/contrib/python/google-auth/py3/google/auth/impersonated_credentials.py
+++ b/contrib/python/google-auth/py3/google/auth/impersonated_credentials.py
@@ -47,6 +47,12 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
+_SOURCE_CREDENTIAL_AUTHORIZED_USER_TYPE = "authorized_user"
+_SOURCE_CREDENTIAL_SERVICE_ACCOUNT_TYPE = "service_account"
+_SOURCE_CREDENTIAL_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = (
+ "external_account_authorized_user"
+)
+
def _make_iam_token_request(
request,
@@ -410,6 +416,75 @@ class Credentials(
cred._target_scopes = scopes or default_scopes
return cred
+ @classmethod
+ def from_impersonated_service_account_info(cls, info, scopes=None):
+ """Creates a Credentials instance from parsed impersonated service account credentials info.
+
+ Args:
+ info (Mapping[str, str]): The impersonated service account credentials info in Google
+ format.
+ scopes (Sequence[str]): Optional list of scopes to include in the
+ credentials.
+
+ Returns:
+ google.oauth2.credentials.Credentials: The constructed
+ credentials.
+
+ Raises:
+ InvalidType: If the info["source_credentials"] are not a supported impersonation type
+ InvalidValue: If the info["service_account_impersonation_url"] is not in the expected format.
+ ValueError: If the info is not in the expected format.
+ """
+
+ source_credentials_info = info.get("source_credentials")
+ source_credentials_type = source_credentials_info.get("type")
+ if source_credentials_type == _SOURCE_CREDENTIAL_AUTHORIZED_USER_TYPE:
+ from google.oauth2 import credentials
+
+ source_credentials = credentials.Credentials.from_authorized_user_info(
+ source_credentials_info
+ )
+ elif source_credentials_type == _SOURCE_CREDENTIAL_SERVICE_ACCOUNT_TYPE:
+ from google.oauth2 import service_account
+
+ source_credentials = service_account.Credentials.from_service_account_info(
+ source_credentials_info
+ )
+ elif (
+ source_credentials_type
+ == _SOURCE_CREDENTIAL_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE
+ ):
+ from google.auth import external_account_authorized_user
+
+ source_credentials = external_account_authorized_user.Credentials.from_info(
+ source_credentials_info
+ )
+ else:
+ raise exceptions.InvalidType(
+ "source credential of type {} is not supported.".format(
+ source_credentials_type
+ )
+ )
+
+ impersonation_url = info.get("service_account_impersonation_url")
+ start_index = impersonation_url.rfind("/")
+ end_index = impersonation_url.find(":generateAccessToken")
+ if start_index == -1 or end_index == -1 or start_index > end_index:
+ raise exceptions.InvalidValue(
+ "Cannot extract target principal from {}".format(impersonation_url)
+ )
+ target_principal = impersonation_url[start_index + 1 : end_index]
+ delegates = info.get("delegates")
+ quota_project_id = info.get("quota_project_id")
+
+ return cls(
+ source_credentials,
+ target_principal,
+ scopes,
+ delegates,
+ quota_project_id=quota_project_id,
+ )
+
class IDTokenCredentials(credentials.CredentialsWithQuotaProject):
"""Open ID Connect ID Token-based service account credentials.
diff --git a/contrib/python/google-auth/py3/google/auth/transport/urllib3.py b/contrib/python/google-auth/py3/google/auth/transport/urllib3.py
index 63144f5fffa..db4fa93ff11 100644
--- a/contrib/python/google-auth/py3/google/auth/transport/urllib3.py
+++ b/contrib/python/google-auth/py3/google/auth/transport/urllib3.py
@@ -34,13 +34,21 @@ except ImportError: # pragma: NO COVER
try:
import urllib3 # type: ignore
import urllib3.exceptions # type: ignore
+ from packaging import version # type: ignore
except ImportError as caught_exc: # pragma: NO COVER
raise ImportError(
- "The urllib3 library is not installed from please install the "
- "urllib3 package to use the urllib3 transport."
+ ""
+ f"Error: {caught_exc}."
+ " The 'google-auth' library requires the extras installed "
+ "for urllib3 network transport."
+ "\n"
+ "Please install the necessary dependencies using pip:\n"
+ " pip install google-auth[urllib3]\n"
+ "\n"
+ "(Note: Using '[urllib3]' ensures the specific dependencies needed for this feature are installed. "
+ "We recommend running this command in your virtual environment.)"
) from caught_exc
-from packaging import version # type: ignore
from google.auth import environment_vars
from google.auth import exceptions
@@ -414,7 +422,7 @@ class AuthorizedHttp(RequestMethods): # type: ignore
body=body,
headers=headers,
_credential_refresh_attempt=_credential_refresh_attempt + 1,
- **kwargs
+ **kwargs,
)
return response
diff --git a/contrib/python/google-auth/py3/google/auth/version.py b/contrib/python/google-auth/py3/google/auth/version.py
index 41a80e6c676..393caa8ad44 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.38.0"
+__version__ = "2.39.0"
diff --git a/contrib/python/google-auth/py3/google/oauth2/id_token.py b/contrib/python/google-auth/py3/google/oauth2/id_token.py
index b68ab6b303a..a6c51ce6381 100644
--- a/contrib/python/google-auth/py3/google/oauth2/id_token.py
+++ b/contrib/python/google-auth/py3/google/oauth2/id_token.py
@@ -284,6 +284,18 @@ def fetch_id_token_credentials(audience, request=None):
return service_account.IDTokenCredentials.from_service_account_info(
info, target_audience=audience
)
+ elif info.get("type") == "impersonated_service_account":
+ from google.auth import impersonated_credentials
+
+ target_credentials = impersonated_credentials.Credentials.from_impersonated_service_account_info(
+ info
+ )
+
+ return impersonated_credentials.IDTokenCredentials(
+ target_credentials=target_credentials,
+ target_audience=audience,
+ include_email=True,
+ )
except ValueError as caught_exc:
new_exc = exceptions.DefaultCredentialsError(
"GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
diff --git a/contrib/python/google-auth/py3/patches/01-fix-tests.patch b/contrib/python/google-auth/py3/patches/01-fix-tests.patch
index 1065289c64b..0494eab3354 100644
--- a/contrib/python/google-auth/py3/patches/01-fix-tests.patch
+++ b/contrib/python/google-auth/py3/patches/01-fix-tests.patch
@@ -47,7 +47,7 @@
+DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "..", "data")
--- contrib/python/google-auth/py3/tests/oauth2/test_id_token.py (index)
+++ contrib/python/google-auth/py3/tests/oauth2/test_id_token.py (working tree)
-@@ -24,8 +24,9 @@ import google.auth.compute_engine._metadata
+@@ -24,12 +24,13 @@ import google.auth.compute_engine._metadata
from google.oauth2 import id_token
from google.oauth2 import service_account
@@ -56,7 +56,12 @@
- os.path.dirname(__file__), "../data/service_account.json"
+ os.path.dirname(yc.source_path(__file__)), "../data/service_account.json"
)
- ID_TOKEN_AUDIENCE = "https://pubsub.googleapis.com"
+
+ IMPERSONATED_SERVICE_ACCOUNT_FILE = os.path.join(
+- os.path.dirname(__file__),
++ os.path.dirname(yc.source_path(__file__)),
+ "../data/impersonated_service_account_authorized_user_source.json",
+ )
@@ -265,1 +266,1 @@ def test_fetch_id_token_no_cred_exists(monkeypatch):
- os.path.dirname(__file__), "../data/authorized_user.json"
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 a768b17fa0d..98d08fe4505 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
@@ -176,6 +176,7 @@ def test_get_success_json():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result[key] == value
@@ -194,6 +195,7 @@ def test_get_success_json_content_type_charset():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result[key] == value
@@ -213,6 +215,7 @@ def test_get_success_retry(mock_sleep):
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert request.call_count == 2
assert result[key] == value
@@ -228,6 +231,7 @@ def test_get_success_text():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result == data
@@ -243,6 +247,7 @@ def test_get_success_params():
method="GET",
url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result == data
@@ -257,6 +262,7 @@ def test_get_success_recursive_and_params():
method="GET",
url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result == data
@@ -271,6 +277,7 @@ def test_get_success_recursive():
method="GET",
url=_metadata._METADATA_ROOT + PATH + "?recursive=true",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert result == data
@@ -292,6 +299,7 @@ def _test_get_success_custom_root_new_variable():
method="GET",
url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH),
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -312,6 +320,7 @@ def _test_get_success_custom_root_old_variable():
method="GET",
url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH),
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -328,6 +337,7 @@ def test_get_failure(mock_sleep):
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -340,6 +350,7 @@ def test_get_return_none_for_not_found_error():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -359,6 +370,7 @@ def test_get_failure_connection_failed(mock_sleep):
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert request.call_count == 5
@@ -377,6 +389,7 @@ def test_get_too_many_requests_retryable_error_failure():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert request.call_count == 5
@@ -393,6 +406,7 @@ def test_get_failure_bad_json():
method="GET",
url=_metadata._METADATA_ROOT + PATH,
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -406,6 +420,7 @@ def test_get_project_id():
method="GET",
url=_metadata._METADATA_ROOT + "project/project-id",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert project_id == project
@@ -421,6 +436,7 @@ def test_get_universe_domain_success():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert universe_domain == "fake_universe_domain"
@@ -434,6 +450,7 @@ def test_get_universe_domain_success_empty_response():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert universe_domain == "googleapis.com"
@@ -449,6 +466,7 @@ def test_get_universe_domain_not_found():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert universe_domain == "googleapis.com"
@@ -469,6 +487,7 @@ def test_get_universe_domain_retryable_error_failure():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert request.call_count == 5
@@ -511,11 +530,13 @@ def test_get_universe_domain_retryable_error_success():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
request_ok.assert_called_once_with(
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert universe_domain == "fake_universe_domain"
@@ -535,6 +556,7 @@ def test_get_universe_domain_other_error():
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe-domain",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
@@ -559,6 +581,7 @@ def test_get_service_account_token(utcnow, mock_metrics_header_value):
"metadata-flavor": "Google",
"x-goog-api-client": ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE,
},
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert token == "token"
assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
@@ -585,6 +608,7 @@ def test_get_service_account_token_with_scopes_list(utcnow, mock_metrics_header_
"metadata-flavor": "Google",
"x-goog-api-client": ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE,
},
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert token == "token"
assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
@@ -613,6 +637,7 @@ def test_get_service_account_token_with_scopes_string(
"metadata-flavor": "Google",
"x-goog-api-client": ACCESS_TOKEN_REQUEST_METRICS_HEADER_VALUE,
},
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert token == "token"
assert expiry == utcnow() + datetime.timedelta(seconds=ttl)
@@ -630,6 +655,7 @@ def test_get_service_account_info():
method="GET",
url=_metadata._METADATA_ROOT + PATH + "/?recursive=true",
headers=_metadata._METADATA_HEADERS,
+ timeout=_metadata._METADATA_DEFAULT_TIMEOUT,
)
assert info[key] == value
diff --git a/contrib/python/google-auth/py3/tests/data/trust_chain_with_leaf.pem b/contrib/python/google-auth/py3/tests/data/trust_chain_with_leaf.pem
new file mode 100644
index 00000000000..250387d9d59
--- /dev/null
+++ b/contrib/python/google-auth/py3/tests/data/trust_chain_with_leaf.pem
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIIDIzCCAgugAwIBAgIJAMfISuBQ5m+5MA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
+BAMTCnVuaXQtdGVzdHMwHhcNMTExMjA2MTYyNjAyWhcNMjExMjAzMTYyNjAyWjAV
+MRMwEQYDVQQDEwp1bml0LXRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj7wZgkdmM
+7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/xmVU1Wer
+uQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYsSliS5qQp
+gyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18pe+zpyl4
++WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xkSBc//fy3
+ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABo3YwdDAdBgNVHQ4EFgQU2RQ8yO+O
+gN8oVW2SW7RLrfYd9jEwRQYDVR0jBD4wPIAU2RQ8yO+OgN8oVW2SW7RLrfYd9jGh
+GaQXMBUxEzARBgNVBAMTCnVuaXQtdGVzdHOCCQDHyErgUOZvuTAMBgNVHRMEBTAD
+AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBRv+M/6+FiVu7KXNjFI5pSN17OcW5QUtPr
+odJMlWrJBtynn/TA1oJlYu3yV5clc/71Vr/AxuX5xGP+IXL32YDF9lTUJXG/uUGk
++JETpKmQviPbRsvzYhz4pf6ZIOZMc3/GIcNq92ECbseGO+yAgyWUVKMmZM0HqXC9
+ovNslqe0M8C1sLm1zAR5z/h/litE7/8O2ietija3Q/qtl2TOXJdCA6sgjJX2WUql
+ybrC55ct18NKf3qhpcEkGQvFU40rVYApJpi98DiZPYFdx1oBDp/f4uZ3ojpxRVFT
+cDwcJLfNRCPUhormsY7fDS9xSyThiHsW9mjJYdcaKQkwYZ0F11yB
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIJAPBsLZmNGfKtMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTYwOTIxMDI0NTEyWhcNMTYxMDIxMDI0NTEyWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAsiMC7mTsmUXwZoYlT4aHY1FLw8bxIXC+z3IqA+TY1WqfbeiZRo8MA5Zx
+lTTxYMKPCZUE1XBc7jvD8GJhWIj6pToPYHn73B01IBkLBxq4kF1yV2Z7DVmkvc6H
+EcxXXq8zkCx0j6XOfiI4+qkXnuQn8cvrk8xfhtnMMZM7iVm6VSN93iRP/8ey6xuL
+XTHrDX7ukoRce1hpT8O+15GXNrY0irhhYQz5xKibNCJF3EjV28WMry8y7I8uYUFU
+RWDiQawwK9ec1zhZ94v92+GZDlPevmcFmSERKYQ0NsKcT0Y3lGuGnaExs8GyOpnC
+oksu4YJGXQjg7lkv4MxzsNbRqmCkUwxw1Mg6FP0tsCNsw9qTrkvWCRA9zp/aU+sZ
+IBGh1t4UGCub8joeQFvHxvr/3F7mH/dyvCjA34u0Lo1VPx+jYUIi9i0odltMspDW
+xOpjqdGARZYmlJP5Au9q5cQjPMcwS/EBIb8cwNl32mUE6WnFlep+38mNR/FghIjO
+ViAkXuKQmcHe6xppZAoHFsO/t3l4Tjek5vNW7erI1rgrFku/fvkIW/G8V1yIm/+Q
+F+CE4maQzCJfhftpkhM/sPC/FuLNBmNE8BHVX8y58xG4is/cQxL4Z9TsFIw0C5+3
+uTrFW9D0agysahMVzPGtCqhDQqJdIJrBQqlS6bztpzBA8zEI0skCAwEAAaOBpzCB
+pDAdBgNVHQ4EFgQUz/8FmW6TfqXyNJZr7rhc+Tn5sKQwdQYDVR0jBG4wbIAUz/8F
+mW6TfqXyNJZr7rhc+Tn5sKShSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
+b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDw
+bC2ZjRnyrTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQCQmrcfhurX
+riR3Q0Y+nq040/3dJIAJXjyI9CEtxaU0nzCNTng7PwgZ0CKmCelQfInuwWFwBSHS
+6kBfC1rgJeFnjnTt8a3RCgRlIgUr9NCdPSEccB7TurobwPJ2h6cJjjR8urcb0CXh
+CEMvPneyPj0xUFY8vVKXMGWahz/kyfwIiVqcX/OtMZ29fUu1onbWl71g2gVLtUZl
+sECdZ+AC/6HDCVpYIVETMl1T7N/XyqXZQiDLDNRDeZhnapz8w9fsW1KVujAZLNQR
+pVnw2qa2UK1dSf2FHX+lQU5mFSYM4vtwaMlX/LgfdLZ9I796hFh619WwTVz+LO2N
+vHnwBMabld3XSPuZRqlbBulDQ07Vbqdjv8DYSLA2aKI4ZkMMKuFLG/oS28V2ZYmv
+/KpGEs5UgKY+P9NulYpTDwCU/6SomuQpP795wbG6sm7Hzq82r2RmB61GupNRGeqi
+pXKsy69T388zBxYu6zQrosXiDl5YzaViH7tm0J7opye8dCWjjpnahki0vq2znti7
+6cWla2j8Xz1glvLz+JI/NCOMfxUInb82T7ijo80N0VJ2hzf7p2GxRZXAxAV9knLI
+nM4F5TLjSd7ZhOOZ7ni/eZFueTMisWfypt2nc41whGjHMX/Zp1kPfhB4H2bLKIX/
+lSrwNr3qbGTEJX8JqpDBNVAd96XkMvDNyA==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/contrib/python/google-auth/py3/tests/data/trust_chain_without_leaf.pem b/contrib/python/google-auth/py3/tests/data/trust_chain_without_leaf.pem
new file mode 100644
index 00000000000..9da0f37fedf
--- /dev/null
+++ b/contrib/python/google-auth/py3/tests/data/trust_chain_without_leaf.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIJAPBsLZmNGfKtMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTYwOTIxMDI0NTEyWhcNMTYxMDIxMDI0NTEyWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAsiMC7mTsmUXwZoYlT4aHY1FLw8bxIXC+z3IqA+TY1WqfbeiZRo8MA5Zx
+lTTxYMKPCZUE1XBc7jvD8GJhWIj6pToPYHn73B01IBkLBxq4kF1yV2Z7DVmkvc6H
+EcxXXq8zkCx0j6XOfiI4+qkXnuQn8cvrk8xfhtnMMZM7iVm6VSN93iRP/8ey6xuL
+XTHrDX7ukoRce1hpT8O+15GXNrY0irhhYQz5xKibNCJF3EjV28WMry8y7I8uYUFU
+RWDiQawwK9ec1zhZ94v92+GZDlPevmcFmSERKYQ0NsKcT0Y3lGuGnaExs8GyOpnC
+oksu4YJGXQjg7lkv4MxzsNbRqmCkUwxw1Mg6FP0tsCNsw9qTrkvWCRA9zp/aU+sZ
+IBGh1t4UGCub8joeQFvHxvr/3F7mH/dyvCjA34u0Lo1VPx+jYUIi9i0odltMspDW
+xOpjqdGARZYmlJP5Au9q5cQjPMcwS/EBIb8cwNl32mUE6WnFlep+38mNR/FghIjO
+ViAkXuKQmcHe6xppZAoHFsO/t3l4Tjek5vNW7erI1rgrFku/fvkIW/G8V1yIm/+Q
+F+CE4maQzCJfhftpkhM/sPC/FuLNBmNE8BHVX8y58xG4is/cQxL4Z9TsFIw0C5+3
+uTrFW9D0agysahMVzPGtCqhDQqJdIJrBQqlS6bztpzBA8zEI0skCAwEAAaOBpzCB
+pDAdBgNVHQ4EFgQUz/8FmW6TfqXyNJZr7rhc+Tn5sKQwdQYDVR0jBG4wbIAUz/8F
+mW6TfqXyNJZr7rhc+Tn5sKShSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
+b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDw
+bC2ZjRnyrTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQCQmrcfhurX
+riR3Q0Y+nq040/3dJIAJXjyI9CEtxaU0nzCNTng7PwgZ0CKmCelQfInuwWFwBSHS
+6kBfC1rgJeFnjnTt8a3RCgRlIgUr9NCdPSEccB7TurobwPJ2h6cJjjR8urcb0CXh
+CEMvPneyPj0xUFY8vVKXMGWahz/kyfwIiVqcX/OtMZ29fUu1onbWl71g2gVLtUZl
+sECdZ+AC/6HDCVpYIVETMl1T7N/XyqXZQiDLDNRDeZhnapz8w9fsW1KVujAZLNQR
+pVnw2qa2UK1dSf2FHX+lQU5mFSYM4vtwaMlX/LgfdLZ9I796hFh619WwTVz+LO2N
+vHnwBMabld3XSPuZRqlbBulDQ07Vbqdjv8DYSLA2aKI4ZkMMKuFLG/oS28V2ZYmv
+/KpGEs5UgKY+P9NulYpTDwCU/6SomuQpP795wbG6sm7Hzq82r2RmB61GupNRGeqi
+pXKsy69T388zBxYu6zQrosXiDl5YzaViH7tm0J7opye8dCWjjpnahki0vq2znti7
+6cWla2j8Xz1glvLz+JI/NCOMfxUInb82T7ijo80N0VJ2hzf7p2GxRZXAxAV9knLI
+nM4F5TLjSd7ZhOOZ7ni/eZFueTMisWfypt2nc41whGjHMX/Zp1kPfhB4H2bLKIX/
+lSrwNr3qbGTEJX8JqpDBNVAd96XkMvDNyA==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/contrib/python/google-auth/py3/tests/data/trust_chain_wrong_order.pem b/contrib/python/google-auth/py3/tests/data/trust_chain_wrong_order.pem
new file mode 100644
index 00000000000..e8dc5d35931
--- /dev/null
+++ b/contrib/python/google-auth/py3/tests/data/trust_chain_wrong_order.pem
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIJAPBsLZmNGfKtMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTYwOTIxMDI0NTEyWhcNMTYxMDIxMDI0NTEyWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAsiMC7mTsmUXwZoYlT4aHY1FLw8bxIXC+z3IqA+TY1WqfbeiZRo8MA5Zx
+lTTxYMKPCZUE1XBc7jvD8GJhWIj6pToPYHn73B01IBkLBxq4kF1yV2Z7DVmkvc6H
+EcxXXq8zkCx0j6XOfiI4+qkXnuQn8cvrk8xfhtnMMZM7iVm6VSN93iRP/8ey6xuL
+XTHrDX7ukoRce1hpT8O+15GXNrY0irhhYQz5xKibNCJF3EjV28WMry8y7I8uYUFU
+RWDiQawwK9ec1zhZ94v92+GZDlPevmcFmSERKYQ0NsKcT0Y3lGuGnaExs8GyOpnC
+oksu4YJGXQjg7lkv4MxzsNbRqmCkUwxw1Mg6FP0tsCNsw9qTrkvWCRA9zp/aU+sZ
+IBGh1t4UGCub8joeQFvHxvr/3F7mH/dyvCjA34u0Lo1VPx+jYUIi9i0odltMspDW
+xOpjqdGARZYmlJP5Au9q5cQjPMcwS/EBIb8cwNl32mUE6WnFlep+38mNR/FghIjO
+ViAkXuKQmcHe6xppZAoHFsO/t3l4Tjek5vNW7erI1rgrFku/fvkIW/G8V1yIm/+Q
+F+CE4maQzCJfhftpkhM/sPC/FuLNBmNE8BHVX8y58xG4is/cQxL4Z9TsFIw0C5+3
+uTrFW9D0agysahMVzPGtCqhDQqJdIJrBQqlS6bztpzBA8zEI0skCAwEAAaOBpzCB
+pDAdBgNVHQ4EFgQUz/8FmW6TfqXyNJZr7rhc+Tn5sKQwdQYDVR0jBG4wbIAUz/8F
+mW6TfqXyNJZr7rhc+Tn5sKShSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
+b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDw
+bC2ZjRnyrTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQCQmrcfhurX
+riR3Q0Y+nq040/3dJIAJXjyI9CEtxaU0nzCNTng7PwgZ0CKmCelQfInuwWFwBSHS
+6kBfC1rgJeFnjnTt8a3RCgRlIgUr9NCdPSEccB7TurobwPJ2h6cJjjR8urcb0CXh
+CEMvPneyPj0xUFY8vVKXMGWahz/kyfwIiVqcX/OtMZ29fUu1onbWl71g2gVLtUZl
+sECdZ+AC/6HDCVpYIVETMl1T7N/XyqXZQiDLDNRDeZhnapz8w9fsW1KVujAZLNQR
+pVnw2qa2UK1dSf2FHX+lQU5mFSYM4vtwaMlX/LgfdLZ9I796hFh619WwTVz+LO2N
+vHnwBMabld3XSPuZRqlbBulDQ07Vbqdjv8DYSLA2aKI4ZkMMKuFLG/oS28V2ZYmv
+/KpGEs5UgKY+P9NulYpTDwCU/6SomuQpP795wbG6sm7Hzq82r2RmB61GupNRGeqi
+pXKsy69T388zBxYu6zQrosXiDl5YzaViH7tm0J7opye8dCWjjpnahki0vq2znti7
+6cWla2j8Xz1glvLz+JI/NCOMfxUInb82T7ijo80N0VJ2hzf7p2GxRZXAxAV9knLI
+nM4F5TLjSd7ZhOOZ7ni/eZFueTMisWfypt2nc41whGjHMX/Zp1kPfhB4H2bLKIX/
+lSrwNr3qbGTEJX8JqpDBNVAd96XkMvDNyA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIzCCAgugAwIBAgIJAMfISuBQ5m+5MA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
+BAMTCnVuaXQtdGVzdHMwHhcNMTExMjA2MTYyNjAyWhcNMjExMjAzMTYyNjAyWjAV
+MRMwEQYDVQQDEwp1bml0LXRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj7wZgkdmM
+7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/xmVU1Wer
+uQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYsSliS5qQp
+gyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18pe+zpyl4
++WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xkSBc//fy3
+ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABo3YwdDAdBgNVHQ4EFgQU2RQ8yO+O
+gN8oVW2SW7RLrfYd9jEwRQYDVR0jBD4wPIAU2RQ8yO+OgN8oVW2SW7RLrfYd9jGh
+GaQXMBUxEzARBgNVBAMTCnVuaXQtdGVzdHOCCQDHyErgUOZvuTAMBgNVHRMEBTAD
+AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBRv+M/6+FiVu7KXNjFI5pSN17OcW5QUtPr
+odJMlWrJBtynn/TA1oJlYu3yV5clc/71Vr/AxuX5xGP+IXL32YDF9lTUJXG/uUGk
++JETpKmQviPbRsvzYhz4pf6ZIOZMc3/GIcNq92ECbseGO+yAgyWUVKMmZM0HqXC9
+ovNslqe0M8C1sLm1zAR5z/h/litE7/8O2ietija3Q/qtl2TOXJdCA6sgjJX2WUql
+ybrC55ct18NKf3qhpcEkGQvFU40rVYApJpi98DiZPYFdx1oBDp/f4uZ3ojpxRVFT
+cDwcJLfNRCPUhormsY7fDS9xSyThiHsW9mjJYdcaKQkwYZ0F11yB
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py b/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py
index 65189df128c..5dc125fb566 100644
--- a/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py
+++ b/contrib/python/google-auth/py3/tests/oauth2/test_id_token.py
@@ -20,6 +20,7 @@ import pytest # type: ignore
from google.auth import environment_vars
from google.auth import exceptions
+from google.auth import impersonated_credentials
from google.auth import transport
from google.oauth2 import id_token
from google.oauth2 import service_account
@@ -28,6 +29,12 @@ import yatest.common as yc
SERVICE_ACCOUNT_FILE = os.path.join(
os.path.dirname(yc.source_path(__file__)), "../data/service_account.json"
)
+
+IMPERSONATED_SERVICE_ACCOUNT_FILE = os.path.join(
+ os.path.dirname(yc.source_path(__file__)),
+ "../data/impersonated_service_account_authorized_user_source.json",
+)
+
ID_TOKEN_AUDIENCE = "https://pubsub.googleapis.com"
@@ -263,6 +270,14 @@ def test_fetch_id_token_credentials_from_explicit_cred_json_file(monkeypatch):
assert cred._target_audience == ID_TOKEN_AUDIENCE
+def test_fetch_id_token_credentials_from_impersonated_cred_json_file(monkeypatch):
+ monkeypatch.setenv(environment_vars.CREDENTIALS, IMPERSONATED_SERVICE_ACCOUNT_FILE)
+
+ cred = id_token.fetch_id_token_credentials(ID_TOKEN_AUDIENCE)
+ assert isinstance(cred, impersonated_credentials.IDTokenCredentials)
+ assert cred._target_audience == ID_TOKEN_AUDIENCE
+
+
def test_fetch_id_token_credentials_no_cred_exists(monkeypatch):
monkeypatch.delenv(environment_vars.CREDENTIALS, raising=False)
diff --git a/contrib/python/google-auth/py3/tests/test__oauth2client.py b/contrib/python/google-auth/py3/tests/test__oauth2client.py
index 1db595fd9ac..61eaf17c2de 100644
--- a/contrib/python/google-auth/py3/tests/test__oauth2client.py
+++ b/contrib/python/google-auth/py3/tests/test__oauth2client.py
@@ -117,6 +117,14 @@ def _test__convert_appengine_app_assertion_credentials(
app_identity, mock_oauth2client_gae_imports
):
+ # `oauth2client` requires `cgi` which was removed in Python 3.13
+ # See https://github.com/googleapis/oauth2client/blob/50d20532a748f18e53f7d24ccbe6647132c979a9/oauth2client/contrib/appengine.py#L20
+ # oauth2client is no longer being updated so this test must be skipped on newer Python Runtimes
+ if sys.version_info >= (3, 13): # pragma: NO COVER
+ pytest.skip(
+ "Skipping test for Python 3.13+ due to oauth2client incompatibility."
+ )
+
import oauth2client.contrib.appengine # type: ignore
service_account_id = "service_account_id"
@@ -166,6 +174,14 @@ def reset__oauth2client_module():
def _test_import_has_app_engine(
mock_oauth2client_gae_imports, reset__oauth2client_module
):
+ # `oauth2client` requires `cgi` which was removed in Python 3.13
+ # See https://github.com/googleapis/oauth2client/blob/50d20532a748f18e53f7d24ccbe6647132c979a9/oauth2client/contrib/appengine.py#L20
+ # oauth2client is no longer being updated so this test must be skipped on newer Python Runtimes
+ if sys.version_info >= (3, 13): # pragma: NO COVER
+ pytest.skip(
+ "Skipping test for Python 3.13+ due to oauth2client incompatibility."
+ )
+
importlib.reload(_oauth2client)
assert _oauth2client._HAS_APPENGINE
diff --git a/contrib/python/google-auth/py3/tests/test_identity_pool.py b/contrib/python/google-auth/py3/tests/test_identity_pool.py
index cc6cbf08827..4d78a5c22ea 100644
--- a/contrib/python/google-auth/py3/tests/test_identity_pool.py
+++ b/contrib/python/google-auth/py3/tests/test_identity_pool.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import base64
import datetime
import http.client as http_client
import json
@@ -19,6 +20,7 @@ import os
import urllib
import mock
+from OpenSSL import crypto
import pytest # type: ignore
from google.auth import _helpers, external_account
@@ -49,6 +51,13 @@ import yatest.common as yc
DATA_DIR = os.path.join(os.path.dirname(yc.source_path(__file__)), "data")
SUBJECT_TOKEN_TEXT_FILE = os.path.join(DATA_DIR, "external_subject_token.txt")
SUBJECT_TOKEN_JSON_FILE = os.path.join(DATA_DIR, "external_subject_token.json")
+TRUST_CHAIN_WITH_LEAF_FILE = os.path.join(DATA_DIR, "trust_chain_with_leaf.pem")
+TRUST_CHAIN_WITHOUT_LEAF_FILE = os.path.join(DATA_DIR, "trust_chain_without_leaf.pem")
+TRUST_CHAIN_WRONG_ORDER_FILE = os.path.join(DATA_DIR, "trust_chain_wrong_order.pem")
+CERT_FILE = os.path.join(DATA_DIR, "public_cert.pem")
+KEY_FILE = os.path.join(DATA_DIR, "privatekey.pem")
+OTHER_CERT_FILE = os.path.join(DATA_DIR, "other_cert.pem")
+
SUBJECT_TOKEN_FIELD_NAME = "access_token"
with open(SUBJECT_TOKEN_TEXT_FILE) as fh:
@@ -58,6 +67,20 @@ with open(SUBJECT_TOKEN_JSON_FILE) as fh:
JSON_FILE_CONTENT = json.load(fh)
JSON_FILE_SUBJECT_TOKEN = JSON_FILE_CONTENT.get(SUBJECT_TOKEN_FIELD_NAME)
+with open(CERT_FILE, "rb") as f:
+ CERT_FILE_CONTENT = base64.b64encode(
+ crypto.dump_certificate(
+ crypto.FILETYPE_ASN1, crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
+ )
+ ).decode("utf-8")
+
+with open(OTHER_CERT_FILE, "rb") as f:
+ OTHER_CERT_FILE_CONTENT = base64.b64encode(
+ crypto.dump_certificate(
+ crypto.FILETYPE_ASN1, crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
+ )
+ ).decode("utf-8")
+
TOKEN_URL = "https://sts.googleapis.com/v1/token"
TOKEN_INFO_URL = "https://sts.googleapis.com/v1/introspect"
SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"
@@ -186,6 +209,24 @@ class TestCredentials(object):
CREDENTIAL_SOURCE_CERTIFICATE_NOT_DEFAULT = {
"certificate": {"certificate_config_location": "path/to/config"}
}
+ CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WITH_LEAF = {
+ "certificate": {
+ "use_default_certificate_config": "true",
+ "trust_chain_path": TRUST_CHAIN_WITH_LEAF_FILE,
+ }
+ }
+ CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WITHOUT_LEAF = {
+ "certificate": {
+ "use_default_certificate_config": "true",
+ "trust_chain_path": TRUST_CHAIN_WITHOUT_LEAF_FILE,
+ }
+ }
+ CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WRONG_ORDER = {
+ "certificate": {
+ "use_default_certificate_config": "true",
+ "trust_chain_path": TRUST_CHAIN_WRONG_ORDER_FILE,
+ }
+ }
SUCCESS_RESPONSE = {
"access_token": "ACCESS_TOKEN",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
@@ -937,14 +978,126 @@ class TestCredentials(object):
assert subject_token == JSON_FILE_SUBJECT_TOKEN
- def test_retrieve_subject_token_certificate(self):
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_default(
+ self, mock_get_workload_cert_and_key_paths
+ ):
credentials = self.make_credentials(
credential_source=self.CREDENTIAL_SOURCE_CERTIFICATE
)
subject_token = credentials.retrieve_subject_token(None)
- assert subject_token == ""
+ assert subject_token == json.dumps([CERT_FILE_CONTENT])
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_non_default_path(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+ credentials = self.make_credentials(
+ credential_source=self.CREDENTIAL_SOURCE_CERTIFICATE_NOT_DEFAULT
+ )
+
+ subject_token = credentials.retrieve_subject_token(None)
+
+ assert subject_token == json.dumps([CERT_FILE_CONTENT])
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_trust_chain_with_leaf(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+ credentials = self.make_credentials(
+ credential_source=self.CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WITH_LEAF
+ )
+
+ subject_token = credentials.retrieve_subject_token(None)
+ assert subject_token == json.dumps([CERT_FILE_CONTENT, OTHER_CERT_FILE_CONTENT])
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_trust_chain_without_leaf(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+ credentials = self.make_credentials(
+ credential_source=self.CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WITHOUT_LEAF
+ )
+
+ subject_token = credentials.retrieve_subject_token(None)
+ assert subject_token == json.dumps([CERT_FILE_CONTENT, OTHER_CERT_FILE_CONTENT])
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_trust_chain_invalid_order(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+
+ credentials = self.make_credentials(
+ credential_source=self.CREDENTIAL_SOURCE_CERTIFICATE_TRUST_CHAIN_WRONG_ORDER
+ )
+
+ with pytest.raises(exceptions.RefreshError) as excinfo:
+ credentials.retrieve_subject_token(None)
+
+ assert excinfo.match(
+ "The leaf certificate must be at the top of the trust chain file"
+ )
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_trust_chain_file_does_not_exist(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+
+ credentials = self.make_credentials(
+ credential_source={
+ "certificate": {
+ "use_default_certificate_config": "true",
+ "trust_chain_path": "fake.pem",
+ }
+ }
+ )
+
+ with pytest.raises(exceptions.RefreshError) as excinfo:
+ credentials.retrieve_subject_token(None)
+
+ assert excinfo.match("Trust chain file 'fake.pem' was not found.")
+
+ @mock.patch(
+ "google.auth.transport._mtls_helper._get_workload_cert_and_key_paths",
+ return_value=(CERT_FILE, KEY_FILE),
+ )
+ def test_retrieve_subject_token_certificate_invalid_trust_chain_file(
+ self, mock_get_workload_cert_and_key_paths
+ ):
+
+ credentials = self.make_credentials(
+ credential_source={
+ "certificate": {
+ "use_default_certificate_config": "true",
+ "trust_chain_path": SUBJECT_TOKEN_TEXT_FILE,
+ }
+ }
+ )
+
+ with pytest.raises(exceptions.RefreshError) as excinfo:
+ credentials.retrieve_subject_token(None)
+
+ assert excinfo.match("Error loading PEM certificates from the trust chain file")
def test_retrieve_subject_token_json_file_invalid_field_name(self):
credential_source = {
diff --git a/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py b/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py
index 0321a1a1d7b..9aeb505fdd9 100644
--- a/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py
+++ b/contrib/python/google-auth/py3/tests/test_impersonated_credentials.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import copy
import datetime
import http.client as http_client
import json
@@ -36,6 +37,9 @@ with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
PRIVATE_KEY_BYTES = fh.read()
SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json")
+IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_FILE = os.path.join(
+ DATA_DIR, "impersonated_service_account_authorized_user_source.json"
+)
ID_TOKEN_DATA = (
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyOTNhZDk3N2Ew"
@@ -50,6 +54,9 @@ ID_TOKEN_EXPIRY = 1564475051
with open(SERVICE_ACCOUNT_JSON_FILE, "rb") as fh:
SERVICE_ACCOUNT_INFO = json.load(fh)
+with open(IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_FILE, "rb") as fh:
+ IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_INFO = json.load(fh)
+
SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
TOKEN_URI = "https://example.com/oauth2/token"
@@ -149,6 +156,38 @@ class TestImpersonatedCredentials(object):
iam_endpoint_override=iam_endpoint_override,
)
+ def test_from_impersonated_service_account_info(self):
+ credentials = impersonated_credentials.Credentials.from_impersonated_service_account_info(
+ IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_INFO
+ )
+ assert isinstance(credentials, impersonated_credentials.Credentials)
+
+ def test_from_impersonated_service_account_info_with_invalid_source_credentials_type(
+ self
+ ):
+ info = copy.deepcopy(IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_INFO)
+ assert "source_credentials" in info
+ # Set the source_credentials to an invalid type
+ info["source_credentials"]["type"] = "invalid_type"
+ with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
+ impersonated_credentials.Credentials.from_impersonated_service_account_info(
+ info
+ )
+ assert excinfo.match(
+ "source credential of type {} is not supported".format("invalid_type")
+ )
+
+ def test_from_impersonated_service_account_info_with_invalid_impersonation_url(
+ self
+ ):
+ info = copy.deepcopy(IMPERSONATED_SERVICE_ACCOUNT_AUTHORIZED_USER_SOURCE_INFO)
+ info["service_account_impersonation_url"] = "invalid_url"
+ with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
+ impersonated_credentials.Credentials.from_impersonated_service_account_info(
+ info
+ )
+ assert excinfo.match(r"Cannot extract target principal from")
+
def test_get_cred_info(self):
credentials = self.make_credentials()
assert not credentials.get_cred_info()
diff --git a/contrib/python/google-auth/py3/ya.make b/contrib/python/google-auth/py3/ya.make
index bb762919dc6..eddecb079e5 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.38.0)
+VERSION(2.39.0)
LICENSE(Apache-2.0)
diff --git a/contrib/python/prompt-toolkit/py3/.dist-info/METADATA b/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
index 8d4f5d343d2..265fb8183bd 100644
--- a/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
+++ b/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
@@ -1,8 +1,7 @@
-Metadata-Version: 2.2
+Metadata-Version: 2.4
Name: prompt_toolkit
-Version: 3.0.50
+Version: 3.0.51
Summary: Library for building powerful interactive command lines in Python
-Home-page: https://github.com/prompt-toolkit/python-prompt-toolkit
Author: Jonathan Slenders
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
@@ -18,19 +17,12 @@ Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development
-Requires-Python: >=3.8.0
+Requires-Python: >=3.8
Description-Content-Type: text/x-rst
License-File: LICENSE
License-File: AUTHORS.rst
Requires-Dist: wcwidth
-Dynamic: author
-Dynamic: classifier
-Dynamic: description
-Dynamic: description-content-type
-Dynamic: home-page
-Dynamic: requires-dist
-Dynamic: requires-python
-Dynamic: summary
+Dynamic: license-file
Python Prompt Toolkit
=====================
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
index 94727e7cb22..ebaa57dc81b 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
@@ -17,6 +17,7 @@ Probably, to get started, you might also want to have a look at
from __future__ import annotations
import re
+from importlib import metadata
# note: this is a bit more lax than the actual pep 440 to allow for a/b/rc/dev without a number
pep440 = re.compile(
@@ -28,7 +29,7 @@ from .formatted_text import ANSI, HTML
from .shortcuts import PromptSession, print_formatted_text, prompt
# Don't forget to update in `docs/conf.py`!
-__version__ = "3.0.50"
+__version__ = metadata.version("prompt_toolkit")
assert pep440.match(__version__)
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/completion/nested.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/completion/nested.py
index 8569bd2cff7..b72b69ee212 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/completion/nested.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/completion/nested.py
@@ -69,7 +69,7 @@ class NestedCompleter(Completer):
elif isinstance(value, dict):
options[key] = cls.from_nested_dict(value)
elif isinstance(value, set):
- options[key] = cls.from_nested_dict({item: None for item in value})
+ options[key] = cls.from_nested_dict(dict.fromkeys(value))
else:
assert value is None
options[key] = None
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py
index 43228c3cda1..a6f78cb4e06 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/formatted_text/utils.py
@@ -89,8 +89,7 @@ def split_lines(
parts = string.split("\n")
for part in parts[:-1]:
- if part:
- line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler)))
+ line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler)))
yield line
line = []
diff --git a/contrib/python/prompt-toolkit/py3/tests/test_filter.py b/contrib/python/prompt-toolkit/py3/tests/test_filter.py
index f7184c286f2..70ddd5cdf59 100644
--- a/contrib/python/prompt-toolkit/py3/tests/test_filter.py
+++ b/contrib/python/prompt-toolkit/py3/tests/test_filter.py
@@ -16,7 +16,7 @@ def test_always():
def test_invert():
assert not (~Always())()
- assert ~Never()()
+ assert (~Never())()
c = ~Condition(lambda: False)
assert c()
diff --git a/contrib/python/prompt-toolkit/py3/tests/test_formatted_text.py b/contrib/python/prompt-toolkit/py3/tests/test_formatted_text.py
index 843aac16191..60f9cdf459b 100644
--- a/contrib/python/prompt-toolkit/py3/tests/test_formatted_text.py
+++ b/contrib/python/prompt-toolkit/py3/tests/test_formatted_text.py
@@ -274,7 +274,7 @@ def test_split_lines_3():
lines = list(split_lines([("class:a", "\n")]))
assert lines == [
- [],
+ [("class:a", "")],
[("class:a", "")],
]
@@ -284,3 +284,15 @@ def test_split_lines_3():
assert lines == [
[("class:a", "")],
]
+
+
+def test_split_lines_4():
+ "Edge cases: inputs starting and ending with newlines."
+ # -1-
+ lines = list(split_lines([("class:a", "\nline1\n")]))
+
+ assert lines == [
+ [("class:a", "")],
+ [("class:a", "line1")],
+ [("class:a", "")],
+ ]
diff --git a/contrib/python/prompt-toolkit/py3/ya.make b/contrib/python/prompt-toolkit/py3/ya.make
index 5eed9c2519b..fd9a72b9e6e 100644
--- a/contrib/python/prompt-toolkit/py3/ya.make
+++ b/contrib/python/prompt-toolkit/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(3.0.50)
+VERSION(3.0.51)
LICENSE(BSD-3-Clause)
diff --git a/contrib/python/pythran/bin/pythran/ya.make b/contrib/python/pythran/bin/pythran/ya.make
index 7a4a0b9fd6f..370f7347bd6 100644
--- a/contrib/python/pythran/bin/pythran/ya.make
+++ b/contrib/python/pythran/bin/pythran/ya.make
@@ -10,4 +10,119 @@ PEERDIR(
PY_MAIN(pythran.run:run)
+INDUCED_DEPS(h+cpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/assert.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/getattr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/int_.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/max.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/range.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/builtins/tuple.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/core.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/assert.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/getattr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/int_.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/max.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/min.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/range.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/builtins/tuple.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/numpy/empty_like.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/numpy/float64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/numpy/square.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/add.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/div.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/eq.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/floordiv.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/gt.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/iadd.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/le.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/lt.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/mod.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/mul.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/neg.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/operator_/sub.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/float.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/float64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/int.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/ndarray.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/numpy_texpr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/include/types/str.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/numpy/empty_like.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/numpy/float64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/numpy/square.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/add.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/div.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/eq.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/floordiv.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/gt.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/iadd.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/le.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/lt.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/mod.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/mul.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/neg.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/operator_/sub.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/python/exception_handler.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/NoneType.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/array.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/assignable.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/attr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/bool.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/cfun.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/clongdouble.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/combined.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/complex.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/complex128.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/complex256.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/complex64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/dict.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/dynamic_tuple.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/empty_iterator.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/exceptions.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/file.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/finfo.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/float.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/float128.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/float32.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/float64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/generator.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/int.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/int16.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/int32.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/int64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/int8.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/intc.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/intp.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/list.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/longdouble.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/ndarray.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/nditerator.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_binary_op.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_broadcast.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_expr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_gexpr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_iexpr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_nary_expr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_op_helper.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_operators.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_texpr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_unary_op.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/numpy_vexpr.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/pointer.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/raw_array.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/set.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/slice.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/static_if.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/str.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/traits.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/tuple.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uint16.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uint32.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uint64.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uint8.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uintc.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/uintp.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/variant_functor.hpp
+ ${ARCADIA_ROOT}/contrib/python/pythran/pythran/pythonic/types/vectorizable_type.hpp
+)
+
END()
diff --git a/contrib/python/ydb/py3/.dist-info/METADATA b/contrib/python/ydb/py3/.dist-info/METADATA
index 8a15b64dc4b..aa485a3f4c0 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.21.0
+Version: 3.21.1
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 b938db3247b..5f37e78726e 100644
--- a/contrib/python/ydb/py3/ya.make
+++ b/contrib/python/ydb/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(3.21.0)
+VERSION(3.21.1)
LICENSE(Apache-2.0)
diff --git a/contrib/python/ydb/py3/ydb/types.py b/contrib/python/ydb/py3/ydb/types.py
index a48548640c0..47c9c48c2e2 100644
--- a/contrib/python/ydb/py3/ydb/types.py
+++ b/contrib/python/ydb/py3/ydb/types.py
@@ -39,6 +39,19 @@ def _to_date(pb: ydb_value_pb2.Value, value: typing.Union[date, int]) -> None:
pb.uint32_value = value
+def _from_date32(x: ydb_value_pb2.Value, table_client_settings: table.TableClientSettings) -> typing.Union[date, int]:
+ if table_client_settings is not None and table_client_settings._native_date_in_result_sets:
+ return _EPOCH.date() + timedelta(days=x.int32_value)
+ return x.int32_value
+
+
+def _to_date32(pb: ydb_value_pb2.Value, value: typing.Union[date, int]) -> None:
+ if isinstance(value, date):
+ pb.int32_value = (value - _EPOCH.date()).days
+ else:
+ pb.int32_value = value
+
+
def _from_datetime_number(
x: typing.Union[float, datetime], table_client_settings: table.TableClientSettings
) -> datetime:
@@ -62,6 +75,10 @@ def _from_uuid(pb: ydb_value_pb2.Value, value: uuid.UUID):
pb.high_128 = struct.unpack("Q", value.bytes_le[8:16])[0]
+def _timedelta_to_microseconds(value: timedelta) -> int:
+ return (value.days * _SECONDS_IN_DAY + value.seconds) * 1000000 + value.microseconds
+
+
def _from_interval(
value_pb: ydb_value_pb2.Value, table_client_settings: table.TableClientSettings
) -> typing.Union[timedelta, int]:
@@ -70,10 +87,6 @@ def _from_interval(
return value_pb.int64_value
-def _timedelta_to_microseconds(value: timedelta) -> int:
- return (value.days * _SECONDS_IN_DAY + value.seconds) * 1000000 + value.microseconds
-
-
def _to_interval(pb: ydb_value_pb2.Value, value: typing.Union[timedelta, int]):
if isinstance(value, timedelta):
pb.int64_value = _timedelta_to_microseconds(value)
@@ -100,6 +113,25 @@ def _to_timestamp(pb: ydb_value_pb2.Value, value: typing.Union[datetime, int]):
pb.uint64_value = value
+def _from_timestamp64(
+ value_pb: ydb_value_pb2.Value, table_client_settings: table.TableClientSettings
+) -> typing.Union[datetime, int]:
+ if table_client_settings is not None and table_client_settings._native_timestamp_in_result_sets:
+ return _EPOCH + timedelta(microseconds=value_pb.int64_value)
+ return value_pb.int64_value
+
+
+def _to_timestamp64(pb: ydb_value_pb2.Value, value: typing.Union[datetime, int]):
+ if isinstance(value, datetime):
+ if value.tzinfo:
+ epoch = _EPOCH_UTC
+ else:
+ epoch = _EPOCH
+ pb.int64_value = _timedelta_to_microseconds(value - epoch)
+ else:
+ pb.int64_value = value
+
+
@enum.unique
class PrimitiveType(enum.Enum):
"""
@@ -132,23 +164,46 @@ class PrimitiveType(enum.Enum):
_from_date,
_to_date,
)
+ Date32 = (
+ _apis.primitive_types.DATE32,
+ None,
+ _from_date32,
+ _to_date32,
+ )
Datetime = (
_apis.primitive_types.DATETIME,
"uint32_value",
_from_datetime_number,
)
+ Datetime64 = (
+ _apis.primitive_types.DATETIME64,
+ "int64_value",
+ _from_datetime_number,
+ )
Timestamp = (
_apis.primitive_types.TIMESTAMP,
None,
_from_timestamp,
_to_timestamp,
)
+ Timestamp64 = (
+ _apis.primitive_types.TIMESTAMP64,
+ None,
+ _from_timestamp64,
+ _to_timestamp64,
+ )
Interval = (
_apis.primitive_types.INTERVAL,
None,
_from_interval,
_to_interval,
)
+ Interval64 = (
+ _apis.primitive_types.INTERVAL64,
+ None,
+ _from_interval,
+ _to_interval,
+ )
DyNumber = _apis.primitive_types.DYNUMBER, "text_value"
@@ -365,6 +420,32 @@ class DictType(AbstractTypeBuilder):
return self._repr
+class SetType(AbstractTypeBuilder):
+ __slots__ = ("__repr", "__proto")
+
+ def __init__(
+ self,
+ key_type: typing.Union[AbstractTypeBuilder, PrimitiveType],
+ ):
+ """
+ :param key_type: Key type builder
+ """
+ self._repr = "Set<%s>" % (str(key_type))
+ self._proto = _apis.ydb_value.Type(
+ dict_type=_apis.ydb_value.DictType(
+ key=key_type.proto,
+ payload=_apis.ydb_value.Type(void_type=struct_pb2.NULL_VALUE),
+ )
+ )
+
+ @property
+ def proto(self):
+ return self._proto
+
+ def __str__(self):
+ return self._repr
+
+
class TupleType(AbstractTypeBuilder):
__slots__ = ("__elements_repr", "__proto")
diff --git a/contrib/python/ydb/py3/ydb/ydb_version.py b/contrib/python/ydb/py3/ydb/ydb_version.py
index 6b71007009d..3c62627bc85 100644
--- a/contrib/python/ydb/py3/ydb/ydb_version.py
+++ b/contrib/python/ydb/py3/ydb/ydb_version.py
@@ -1 +1 @@
-VERSION = "3.21.0"
+VERSION = "3.21.1"