Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-proton-core for 
openSUSE:Factory checked in at 2025-11-06 18:15:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-proton-core (Old)
 and      /work/SRC/openSUSE:Factory/.python-proton-core.new.1980 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-proton-core"

Thu Nov  6 18:15:36 2025 rev:5 rq:1316025 version:0.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-proton-core/python-proton-core.changes    
2025-04-02 17:12:20.574780793 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-proton-core.new.1980/python-proton-core.changes
  2025-11-06 18:19:04.994355017 +0100
@@ -1,0 +2,6 @@
+Sun Nov  2 15:24:54 UTC 2025 - Yunhe Guo <[email protected]>
+
+- Update to 0.7.0:
+  * No upstream changelog
+
+-------------------------------------------------------------------

Old:
----
  v0.4.0.tar.gz

New:
----
  v0.7.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-proton-core.spec ++++++
--- /var/tmp/diff_new_pack.sxFJeu/_old  2025-11-06 18:19:05.578379711 +0100
+++ /var/tmp/diff_new_pack.sxFJeu/_new  2025-11-06 18:19:05.582379880 +0100
@@ -19,7 +19,7 @@
 %define skip_python2 1
 %{?sle15_python_module_pythons}
 Name:           python-proton-core
-Version:        0.4.0
+Version:        0.7.0
 Release:        0
 Summary:        Proton VPN core library
 License:        GPL-3.0-or-later

++++++ v0.4.0.tar.gz -> v0.7.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/.gitlab-ci.yml 
new/python-proton-core-0.7.0/.gitlab-ci.yml
--- old/python-proton-core-0.4.0/.gitlab-ci.yml 2024-11-20 10:24:53.000000000 
+0100
+++ new/python-proton-core-0.7.0/.gitlab-ci.yml 2025-09-11 15:37:17.000000000 
+0200
@@ -2,6 +2,9 @@
   ALLOW_LINTING_FAILURE: "true"
 
 include:
- - project: 'ProtonVPN/Linux/integration/ci-libraries'
-   ref: develop
-   file: 'develop-pipeline.yml'
+  - component: 
$CI_SERVER_FQDN/proton/devops/cicd-components/kits/devsecops/[email protected]
+    inputs:
+      stage: scan
+  - project: 'ProtonVPN/Linux/integration/ci-libraries'
+    ref: develop
+    file: 'develop-pipeline.yml'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/CODEOWNERS 
new/python-proton-core-0.7.0/CODEOWNERS
--- old/python-proton-core-0.4.0/CODEOWNERS     1970-01-01 01:00:00.000000000 
+0100
+++ new/python-proton-core-0.7.0/CODEOWNERS     2025-09-11 15:37:17.000000000 
+0200
@@ -0,0 +1,2 @@
+# ownership: tight
+* @ProtonVPN/groups/rnd
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/debian/changelog 
new/python-proton-core-0.7.0/debian/changelog
--- old/python-proton-core-0.4.0/debian/changelog       2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/debian/changelog       2025-09-11 
15:37:17.000000000 +0200
@@ -1,3 +1,21 @@
+proton-core (0.7.0) unstable; urgency=medium
+
+  * Add fido2 support for 2fa authorization.
+
+ -- Luke Titley <[email protected]>  Wed, Sep 10 2025 17:08:07 +0200
+
+proton-core (0.6.0) unstable; urgency=medium
+
+  * Ensure CI passed soc tests.
+
+ -- Alexandru Cheltuitor <[email protected]>  Tue, 17 Jun 2025 
15:00:00 +0000
+
+proton-core (0.5.0) unstable; urgency=medium
+
+  * Provide access to the binary response when requesting with 'return_raw'.
+
+ -- Luke Titley <[email protected]>  Wed, 4 Jun 2025 15:00:00 +0000
+
 proton-core (0.4.0) unstable; urgency=medium
 
   * Require python >= 3.9 to allow libraries using newer language features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/proton/keyring/textfile.py 
new/python-proton-core-0.7.0/proton/keyring/textfile.py
--- old/python-proton-core-0.4.0/proton/keyring/textfile.py     2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/keyring/textfile.py     2025-09-11 
15:37:17.000000000 +0200
@@ -39,7 +39,7 @@
             raise KeyError(key)
 
         try:
-            with open(filepath, 'r') as f:
+            with open(filepath, "r", encoding="utf-8") as f:
                 return json.load(f)
         except json.JSONDecodeError as e:
             self._del_item(key)
@@ -54,7 +54,7 @@
 
     def _set_item(self, key, value):
         try:
-            with open(self.__get_filename_for_key(key), 'w') as f:
+            with open(self.__get_filename_for_key(key), "w", encoding="utf-8") 
as f:
                 json.dump(value, f)
         except TypeError as e:
             # The value we got is not serializable, thus a type error is 
thrown,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/proton/session/api.py 
new/python-proton-core-0.7.0/proton/session/api.py
--- old/python-proton-core-0.4.0/proton/session/api.py  2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/api.py  2025-09-11 
15:37:17.000000000 +0200
@@ -18,8 +18,10 @@
 """
 from __future__ import annotations
 
+from dataclasses import dataclass
 from pathlib import Path
 from typing import *
+import logging
 
 from .exceptions import ProtonCryptoError, ProtonAPIError, 
ProtonAPIAuthenticationNeeded, ProtonAPI2FANeeded, ProtonAPIMissingScopeError, 
ProtonAPIHumanVerificationNeeded
 from .srp import User as PmsrpUser
@@ -32,6 +34,8 @@
 
 from ..utils import ExecutionEnvironment
 
+logger = logging.getLogger(__name__)
+
 SRP_MODULUS_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
 
 xjMEXAHLgxYJKwYBBAHaRw8BAQdAFurWXXwjTemqjD7CXjXVyKf0of7n9Ctm
@@ -45,7 +49,31 @@
 =Y4Mw
 -----END PGP PUBLIC KEY BLOCK-----"""
 
-SRP_MODULUS_KEY_FINGERPRINT = "248097092b458509c508dac0350585c4e9518f26"
+SRP_MODULUS_KEY_FINGERPRINT = "248097092b458509c508dac0350585c4e9518f26"  # 
nosemgrep: 
gitleak-detect-ignore,generic.secrets.gitleaks.generic-api-key.generic-api-key 
# pylint: disable=line-too-long
+
+
+@dataclass
+class Fido2AssertionParameters:
+    """
+    Parameters needed to create a FIDO2 assertion for 2FA.
+    These can be obtained from :attr:`Session.supports_fido2`.
+    """
+    challenge: bytes
+    rp_id: str
+    allow_credentials: list[dict]
+    user_verification: str
+
+
+@dataclass
+class Fido2Assertion:
+    """
+    FIDO2 assertion obtained from the security key.
+    These are passed to :meth:`Session.async_validate_2fa_fido2`.
+    """
+    client_data: bytes
+    authenticator_data: bytes
+    signature: bytes
+    credential_id: bytes
 
 
 def sync_wrapper(f):
@@ -279,10 +307,11 @@
         finally:
             self._requests_unlock(no_condition_check)
 
-    
-
     async def async_provide_2fa(self, code : str, no_condition_check=False, 
additional_headers=None) -> bool:
         """Provide Two Factor Authentication Code to the API.
+
+        This method is deprecated, please use :meth:`async_validate_2fa_code`
+        or :meth:`async_validate_2fa_fido2` instead.
         
         :param code: 2FA code
         :type code: str
@@ -292,11 +321,33 @@
         :rtype: bool
         :raises ProtonAPIAuthenticationNeeded: if 2FA failed, and the session 
was reset by the API backend (this is normally the case)
         """
+
+        logger.warning("async_provide_2fa is deprecated, please use 
async_validate_2fa_code or async_validate_2fa_fido2 instead")
+        
+        return await self.async_validate_2fa_code(code, no_condition_check,
+                                                  additional_headers)
+
+    async def _async_validate_2fa(self, answer: dict, 
no_condition_check=False, additional_headers=None) -> bool:
+        """
+        Internal function to validate 2FA, either via code or FIDO2.
+
+        Do not use this method directly, use :meth:`async_validate_2fa_code`
+        or :meth:`async_validate_2fa_fido2` instead.
+
+        :param answer: dict containing either the TwoFactorCode or FIDO2 
information
+        :type answer: dict
+        :param no_condition_check: Internal flag to disable locking, defaults 
to False
+        :type no_condition_check: bool, optional
+        :param additional_headers: additional headers to send
+        :type additional_headers: dict, optional
+        :return: True if 2FA succeeded, False otherwise.
+        """
         self._requests_lock(no_condition_check)
         try:
-            ret = await self.__async_api_request_internal('/auth/2fa', {
-                "TwoFactorCode": code
-            }, no_condition_check=True, additional_headers=additional_headers)
+            ret = await self.__async_api_request_internal(
+                '/auth/2fa', answer, no_condition_check=True,
+                additional_headers=additional_headers)
+
             self.__Scopes = ret['Scopes']
             if ret.get('Code') == 1000:
                 self.__2FA = None
@@ -315,6 +366,60 @@
         finally:
             self._requests_unlock(no_condition_check)
 
+    async def async_validate_2fa_code(self,
+                                      code: str,
+                                      no_condition_check=False,
+                                      additional_headers=None) -> bool:
+        """
+        Validate a 2FA code against the API.
+        :param code: 2FA code
+        :type code: str
+        :param no_condition_check: Internal flag to disable locking, defaults 
to False
+        :type no_condition_check: bool, optional
+        :param additional_headers: additional headers to send
+        :type additional_headers: dict, optional
+        :return: True if 2FA succeeded, False otherwise.
+        :rtype: bool
+        """
+
+        return await self._async_validate_2fa(
+            {"TwoFactorCode": code},
+            no_condition_check,
+            additional_headers)
+
+    async def async_validate_2fa_fido2(self,
+                                       assertion: Fido2Assertion,
+                                       no_condition_check=False,
+                                       additional_headers=None) -> bool:
+        """
+        Validate a FIDO2 assertion against the API.
+        :param assertion: FIDO2 assertion obtained from the security key
+        :type assertion: Fido2Assertion
+        :param no_condition_check: Internal flag to disable locking, defaults 
to False
+        :type no_condition_check: bool, optional
+        :param additional_headers: additional headers to send
+        :type additional_headers: dict, optional
+        :return: True if 2FA succeeded, False otherwise.
+        :rtype: bool
+        """
+
+        fido2 = self.__2FA["FIDO2"]
+
+        def to_b64(x): return base64.b64encode(x).decode('ascii')
+
+        answer = {
+            "AuthenticationOptions": fido2["AuthenticationOptions"],
+            "ClientData": to_b64(assertion.client_data),
+            "AuthenticatorData": to_b64(assertion.authenticator_data),
+            "Signature": to_b64(assertion.signature),
+            "CredentialID": list(assertion.credential_id),
+        }
+        return await self._async_validate_2fa(
+            {"FIDO2": answer},
+            no_condition_check,
+            additional_headers
+        )
+
     async def async_refresh(self, only_when_refresh_revision_is=None, 
no_condition_check=False, additional_headers=None):
         """Refresh tokens.
 
@@ -424,7 +529,9 @@
 
     async def async_human_verif_request_code(self, address=None, phone=None, 
additional_headers=None):
         """Request a verification code. Either address (email address) or 
phone (phone number) should be specified."""
-        assert address is not None ^ phone is not None # nosec (we use email 
validation by default if both are provided, but it's not super clean if the dev 
doesn't know about it)
+        # We use email validation by default if both are provided,
+        # but it's not super clean if the dev doesn't know about it
+        assert address is not None ^ phone is not None  # nosec B101 # 
nosemgrep: gitlab.bandit.B101
 
         if address is not None:
             data = {'Type': 'email', 'Destination': {'Address': address}}
@@ -498,6 +605,8 @@
     human_verif_provide_token = sync_wrapper(async_human_verif_provide_token)
     fork = sync_wrapper(async_fork)
     import_fork = sync_wrapper(async_import_fork)
+    validate_2fa_code = sync_wrapper(async_validate_2fa_code)
+    validate_2fa_fido2 = sync_wrapper(async_validate_2fa_fido2)
 
     def register_persistence_observer(self, observer: object):
         """Register an observer that will be notified of any persistent state 
change of the session
@@ -602,6 +711,34 @@
         return 'twofactor' in self.Scopes
 
     @property
+    def supports_fido2(self) -> Optional[Fido2AssertionParameters]:
+        """
+        :return: If 2FA is needed, and FIDO2 is supported, return the
+            parameters needed to create a FIDO2 assertion.
+            None otherwise (either 2FA is not needed, or FIDO2 is not 
supported).
+        :rtype: Fido2AssertionParameters, optional
+        """
+        if not self.__2FA:
+            return None
+
+        # When FIDO2 is not supported, the __2FA property contains:
+        # {'AuthenticationOptions': None, 'RegisteredKeys': []}
+        options = self.__2FA.get('FIDO2', {}).get("AuthenticationOptions")
+        if not options:
+            return None
+
+        public_key = options.get("publicKey")
+        if not public_key:
+            return None
+
+        return Fido2AssertionParameters(
+            challenge=public_key["challenge"],
+            rp_id=public_key["rpId"],
+            allow_credentials=public_key["allowCredentials"],
+            user_verification=public_key["userVerification"]
+        )
+
+    @property
     def environment(self):
         """Get/set the environment in use for that session. It can be only set 
once at the beginning of the session's object lifetime,
         as changing the environment can lead to security hole.
@@ -758,7 +895,8 @@
         if e.http_headers.get('retry-after','-').isnumeric():
             await asyncio.sleep(int(e.http_headers.get('retry-after')))
         else:
-            await asyncio.sleep(3+random.random()*5) # nosec (no crypto risk 
here of using an unsafe generator)
+            # No crypto risk here of using an unsafe generator
+            await asyncio.sleep(3+random.random()*5)  # nosec B311 # 
nosemgrep: gitlab.bandit.B311
 
     async def __async_api_request_internal(
         self, endpoint,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/proton/session/environments.py 
new/python-proton-core-0.7.0/proton/session/environments.py
--- old/python-proton-core-0.4.0/proton/session/environments.py 2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/environments.py 2025-09-11 
15:37:17.000000000 +0200
@@ -23,7 +23,8 @@
     @property
     def name(cls):
         cls_name = cls.__class__.__name__
-        assert cls_name.endswith('Environment'), "Incorrectly named class" # 
nosec (dev should ensure that to avoid issues)
+        # dev should ensure that to avoid issues
+        assert cls_name.endswith('Environment'), "Incorrectly named class"  # 
nosec B101 # nosemgrep: gitlab.bandit.B101 # noqa: E501 # pylint: 
disable=line-too-long
         return cls_name[:-11].lower()
 
     @property
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/proton/session/transports/aiohttp.py 
new/python-proton-core-0.7.0/proton/session/transports/aiohttp.py
--- old/python-proton-core-0.4.0/proton/session/transports/aiohttp.py   
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/transports/aiohttp.py   
2025-09-11 15:37:17.000000000 +0200
@@ -129,8 +129,7 @@
                         json=jsondata, data=form_data, params=params, 
ssl=ssl_specs
                 ) as ret:
                     if return_raw:
-                        return RawResponse(ret.status, 
tuple(ret.headers.items()),
-                                           await self._parse_json(ret, 
allow_unmodified=True))
+                        return await self._build_raw(ret)
 
                     ret_json = await self._parse_json(ret)
 
@@ -146,10 +145,21 @@
             except Exception as e:
                 raise ProtonAPIUnexpectedError(e)
 
-    async def _parse_json(self, ret, allow_unmodified=False):
-        if allow_unmodified and ret.status == NOT_MODIFIED:
-            return None
+    async def _build_raw(self, ret):
+        response = RawResponse(ret.status,
+                               tuple(ret.headers.items()), None, None)
+
+        if ret.status == NOT_MODIFIED:
+            return response
+
+        response.data = await ret.read()
 
+        if ret.headers['content-type'] != 'application/octet-stream':
+            response.json = await self._parse_json(ret, allow_unmodified=True)
+
+        return response
+
+    async def _parse_json(self, ret, allow_unmodified=False):
         if ret.headers['content-type'] != 'application/json':
             raise ProtonAPINotReachable("API returned non-json results")
         try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/proton/session/transports/base.py 
new/python-proton-core-0.7.0/proton/session/transports/base.py
--- old/python-proton-core-0.4.0/proton/session/transports/base.py      
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/transports/base.py      
2025-09-11 15:37:17.000000000 +0200
@@ -30,11 +30,13 @@
 
     :param status_code: The status code of the response
     :param headers: The headers in the response
-    :param json: The body the response parsed as json
+    :param json: The response parsed as json
+    :param data: The response as a binary blob of bytes
     """
     status_code: int
     headers: Tuple[Tuple[str, Any]]
     json: Optional[dict]
+    data: Optional[bytes]
 
     def find_first_header(self, key, default=None):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/proton/session/transports/requests.py 
new/python-proton-core-0.7.0/proton/session/transports/requests.py
--- old/python-proton-core-0.4.0/proton/session/transports/requests.py  
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/transports/requests.py  
2025-09-11 15:37:17.000000000 +0200
@@ -42,6 +42,20 @@
         except ImportError:
             return None
 
+    def _build_raw(self, ret):
+        response = RawResponse(ret.status_code,
+                               tuple(ret.headers.items()), None, None)
+
+        if ret.status_code == NOT_MODIFIED:
+            return response
+
+        response.data = ret.content
+
+        if ret.headers['content-type'] != 'application/octet-stream':
+            response.json = self._parse_json(ret, allow_unmodified=True)
+
+        return response
+
     def _parse_json(self, ret, allow_unmodified=False):
         if allow_unmodified and ret.status_code == NOT_MODIFIED:
             return None
@@ -103,8 +117,7 @@
             raise ProtonAPIUnexpectedError(e)
 
         if return_raw:
-            return RawResponse(ret.status_code, tuple(ret.headers.items()),
-                               self._parse_json(ret, allow_unmodified=True))
+            return self._build_raw(ret)
 
         ret_json = self._parse_json(ret)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/proton/session/transports/utils/dns.py 
new/python-proton-core-0.7.0/proton/session/transports/utils/dns.py
--- old/python-proton-core-0.4.0/proton/session/transports/utils/dns.py 
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/session/transports/utils/dns.py 
2025-09-11 15:37:17.000000000 +0200
@@ -168,7 +168,7 @@
     @classmethod
     def _build_simple_query(cls, domain: bytes, qtype: int, qclass: int):
         """internal utility to build the simplest DNS request we need"""
-        id: bytes = struct.pack('!H', random.randint(0, 65535))
+        id: bytes = struct.pack('!H', random.randint(0, 65535))  # nosemgrep: 
gitlab.bandit.B311 # nosec B311 # noqa: E501 # pylint: disable=line-too-long
         qtype: bytes = struct.pack('!H', qtype)
         qclass: bytes = struct.pack('!H', qclass)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/proton/sso/sso.py 
new/python-proton-core-0.7.0/proton/sso/sso.py
--- old/python-proton-core-0.4.0/proton/sso/sso.py      2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/sso/sso.py      2025-09-11 
15:37:17.000000000 +0200
@@ -71,7 +71,7 @@
         self._session_data_cache = {}
 
         # This is a global lock, we use it when we modify the indexes
-        self._global_adv_lock = open(os.path.join(self._adv_locks_path, 
f'proton-sso.lock'), 'w')
+        self._global_adv_lock = open(os.path.join(self._adv_locks_path, 
"proton-sso.lock"), "w", encoding="utf-8")  # nosemgrep: 
python.lang.best-practice.open-never-closed.open-never-closed
         self.__keyring_backend = None
         self.__keyring_backend_name = keyring_backend_name
 
@@ -262,7 +262,10 @@
             # Don't do anything, we don't know the account yet!
             return
 
-        self._adv_locks[account_name] = 
open(os.path.join(self._adv_locks_path, 
f'proton-sso-{self.__encode_name(account_name)}.lock'), 'w')
+        self._adv_locks[account_name] = open(  # nosemgrep: 
python.lang.best-practice.open-never-closed.open-never-closed # pylint: 
disable=line-too-long
+            os.path.join(self._adv_locks_path, 
f'proton-sso-{self.__encode_name(account_name)}.lock'),
+            "w", encoding="utf-8"
+        )
         # This is a blocking call. 
         # FIXME: this is Linux specific
         fcntl.flock(self._adv_locks[account_name], fcntl.LOCK_EX)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/proton/views/basiccli.py 
new/python-proton-core-0.7.0/proton/views/basiccli.py
--- old/python-proton-core-0.4.0/proton/views/basiccli.py       2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/proton/views/basiccli.py       2025-09-11 
15:37:17.000000000 +0200
@@ -92,8 +92,8 @@
                 login = None
         if ask_password:
             password = getpass.getpass()
-            if password == '':
-                password = None # nosec B105
+            if password == '':  # nosec B105
+                password = None  # nosec B105
         if ask_2fa:
             twofa = input("Please enter your 2FA code: ") # nosec (Python 3 
only code)
             if twofa == '' or not twofa.isnumeric():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/rpmbuild/SPECS/package.spec 
new/python-proton-core-0.7.0/rpmbuild/SPECS/package.spec
--- old/python-proton-core-0.4.0/rpmbuild/SPECS/package.spec    2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/rpmbuild/SPECS/package.spec    2025-09-11 
15:37:17.000000000 +0200
@@ -1,5 +1,5 @@
 %define unmangled_name proton-core
-%define version 0.4.0
+%define version 0.7.0
 %define release 1
 
 Prefix: %{_prefix}
@@ -56,6 +56,15 @@
 %defattr(-,root,root)
 
 %changelog
+* Wed Sep 10 2025 Luke Titley <[email protected]> 0.7.0
+- Add fido2 support for 2fa authorization.
+
+* Tue Jun 17 2025 Alexandru Cheltuitor <[email protected]> 0.6.0
+- Ensure CI passed soc tests.
+
+* Wed Jun 4 2025 Luke Titley <[email protected]> 0.5.0
+- Provide access to the binary response when requesting with 'return_raw'.
+
 * Tue Nov 19 2024 Alexandru Cheltuitor <[email protected]> 0.4.0
 - Require python >= 3.9 to allow libraries using newer language features
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/setup.py 
new/python-proton-core-0.7.0/setup.py
--- old/python-proton-core-0.4.0/setup.py       2024-11-20 10:24:53.000000000 
+0100
+++ new/python-proton-core-0.7.0/setup.py       2025-09-11 15:37:17.000000000 
+0200
@@ -4,7 +4,7 @@
 
 setup(
     name="proton-core",
-    version="0.4.0",
+    version="0.7.0",
     description="Proton Technologies API wrapper",
     author="Proton Technologies",
     author_email="[email protected]",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/tests/test_aiohttp_transport.py 
new/python-proton-core-0.7.0/tests/test_aiohttp_transport.py
--- old/python-proton-core-0.4.0/tests/test_aiohttp_transport.py        
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/tests/test_aiohttp_transport.py        
2025-09-11 15:37:17.000000000 +0200
@@ -19,6 +19,7 @@
 import unittest
 from io import StringIO
 from unittest.mock import patch, AsyncMock, Mock
+import json
 
 from proton.session import Session
 from proton.session.formdata import FormData, FormField
@@ -101,13 +102,17 @@
 
 class TestAiohttpTransportRawResult(unittest.IsolatedAsyncioTestCase):
 
-    def _setup(self, get_mock, status, headers, json):
+    def _setup(self, get_mock, status, headers, json_value):
         # Mock the GET response
         get_mock.return_value.__aenter__.return_value.status = status
         get_mock.return_value.__aenter__.return_value.headers = headers
-        get_mock.return_value.__aenter__.return_value.json = AsyncMock(
-            return_value=json
-        )
+        get_mock.return_value.__aenter__.return_value.json.return_value =\
+            json_value
+
+        if json_value is not None:
+            byte_value = json.dumps(json_value).encode('utf-8')
+            get_mock.return_value.__aenter__.return_value.read.return_value =\
+                byte_value
 
         session = Session()
         aiohttp_transport = AiohttpTransport(session)
@@ -120,7 +125,7 @@
         session, aiohttp_transport = self._setup(get_mock,
                                                  status=HTTP_STATUS_OK,
                                                  headers={"content-type": 
"application/json"},
-                                                 json={"Code": CODE_SUCCESS})
+                                                 json_value={"Code": 
CODE_SUCCESS})
 
         # Test
         response = await aiohttp_transport.async_api_request("/endpoint", 
return_raw=True)
@@ -129,6 +134,7 @@
         assert isinstance(response, RawResponse), "The response should be a 
RawResponse object."
         assert response.status_code == HTTP_STATUS_OK
         assert response.find_first_header("content-type") == "application/json"
+        assert response.data == b'{"Code": 1000}', "The response data should 
not be None."
         assert response.json == {"Code": CODE_SUCCESS}
         get_mock.assert_called_once()
 
@@ -138,7 +144,7 @@
         session, aiohttp_transport = self._setup(get_mock,
                                                  
status=HTTP_STATUS_NOT_MODIFIED,
                                                  headers={},
-                                                 json=None)
+                                                 json_value=None)
 
         # Test
         response = await aiohttp_transport.async_api_request("/endpoint", 
return_raw=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-proton-core-0.4.0/tests/test_requests_transport.py 
new/python-proton-core-0.7.0/tests/test_requests_transport.py
--- old/python-proton-core-0.4.0/tests/test_requests_transport.py       
2024-11-20 10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/tests/test_requests_transport.py       
2025-09-11 15:37:17.000000000 +0200
@@ -19,6 +19,7 @@
 import unittest
 from io import StringIO
 from unittest.mock import Mock
+import json
 
 import requests
 
@@ -72,12 +73,15 @@
 
 class TestRequestsTransportRawResult(unittest.IsolatedAsyncioTestCase):
 
-    def _setup(self, status, headers, json):
+    def _setup(self, status, headers, json_value):
         # Mock requests get call.
         req_session = Mock(spec=requests.Session)
         req_session.headers = headers
         req_session.get.return_value.headers = headers
-        req_session.get.return_value.json.return_value = json
+        req_session.get.return_value.json.return_value = json_value
+        if json_value is not None:
+            req_session.get.return_value.content =\
+                json.dumps(json_value).encode('utf-8')
         req_session.get.return_value.status_code = status
 
         session = Session()
@@ -96,6 +100,7 @@
 
         # Checks
         assert isinstance(response, RawResponse), "The response should be a 
RawResponse object."
+        assert response.data == b'{"Code": 1000}', "The response data should 
not be None."
         assert response.status_code == HTTP_STATUS_OK
         assert response.find_first_header("content-type") == "application/json"
         assert response.json == {"Code": CODE_SUCCESS}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-proton-core-0.4.0/tests/test_session.py 
new/python-proton-core-0.7.0/tests/test_session.py
--- old/python-proton-core-0.4.0/tests/test_session.py  2024-11-20 
10:24:53.000000000 +0100
+++ new/python-proton-core-0.7.0/tests/test_session.py  2025-09-11 
15:37:17.000000000 +0200
@@ -32,6 +32,12 @@
         s = Session()
         assert await s.async_api_request('/tests/ping') == {'Code': 1000}
 
+    async def test_raw_ping(self):
+        s = Session()
+        response = await s.async_api_request('/tests/ping', return_raw=True)
+        assert response.json == {'Code': 1000}
+        assert response.data == b'{"Code":1000}'
+
     async def test_session_refresh(self):
         session_state = {
             "UID": "7pqrddjjxmbqpmxcqzg3utlscjgw74xq",

Reply via email to