From 892953211edcaa2c106144364c926fbaaa436918 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Wed, 18 Jun 2025 22:20:56 +0200 Subject: [PATCH 1/5] Add unit test sample based on 3.3.0 regression of expires_in --- oauthlib/oauth2/rfc6749/parameters.py | 4 ++-- .../rfc6749/clients/test_web_application.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py index 4675a31f..ea5821fd 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -500,9 +500,9 @@ def parse_expires(params): # Attempt to convert to int expires_in = int(params.get('expires_in')) except ValueError: - raise ValueError("expires_int must be an int") + raise ValueError("expires_in must be an int") elif params.get('expires_in') is not None: - raise ValueError("expires_int must be an int") + raise ValueError("expires_in must be an int") if 'expires_at' in params: if isinstance(params.get('expires_at'), (float, int)): diff --git a/tests/oauth2/rfc6749/clients/test_web_application.py b/tests/oauth2/rfc6749/clients/test_web_application.py index 2a7a8ff3..1f7c4138 100644 --- a/tests/oauth2/rfc6749/clients/test_web_application.py +++ b/tests/oauth2/rfc6749/clients/test_web_application.py @@ -262,3 +262,19 @@ def test_prepare_request_body(self): with self.assertWarns(DeprecationWarning), self.assertRaises(ValueError): client.prepare_request_body(client_id='different_client_id') # testing the exact exception message in Python2&Python3 is a pain + + def test_expires_in_as_str(self): + """ + see regression issue #906 + """ + + client = WebApplicationClient( + client_id="dummy", + token={"access_token": "xyz", "expires_in": "3600"} # ← str on purpose + ) + self.assertIsNotNone(client) + client = WebApplicationClient( + client_id="dummy", + token={"access_token": "xyz", "expires_in": 3600} # ← str on purpose + ) + self.assertIsNotNone(client) From 34c90128402e24460ec4bce54ef4d9de8a0cd24c Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Thu, 19 Jun 2025 11:47:10 +0200 Subject: [PATCH 2/5] Handle expires_in as float to be backward compatible with 3.2.* --- oauthlib/oauth2/rfc6749/parameters.py | 4 +++- tests/oauth2/rfc6749/clients/test_web_application.py | 9 +++++++-- tests/oauth2/rfc6749/test_parameters.py | 5 +++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py index ea5821fd..8268ef92 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -476,7 +476,7 @@ def parse_expires(params): """Parse `expires_in`, `expires_at` fields from params Parse following these rules: - - `expires_in` must be either blank or a valid integer. + - `expires_in` must be either integer, float or None. If a float, it is converted into an integer. - `expires_at` is not in specification so it does its best to: - convert into a int, else - convert into a float, else @@ -495,6 +495,8 @@ def parse_expires(params): if 'expires_in' in params: if isinstance(params.get('expires_in'), int): expires_in = params.get('expires_in') + elif isinstance(params.get('expires_in'), float): + expires_in = int(params.get('expires_in')) elif isinstance(params.get('expires_in'), str): try: # Attempt to convert to int diff --git a/tests/oauth2/rfc6749/clients/test_web_application.py b/tests/oauth2/rfc6749/clients/test_web_application.py index 1f7c4138..f9a4c9d1 100644 --- a/tests/oauth2/rfc6749/clients/test_web_application.py +++ b/tests/oauth2/rfc6749/clients/test_web_application.py @@ -270,11 +270,16 @@ def test_expires_in_as_str(self): client = WebApplicationClient( client_id="dummy", - token={"access_token": "xyz", "expires_in": "3600"} # ← str on purpose + token={"access_token": "xyz", "expires_in": "3600"} ) self.assertIsNotNone(client) client = WebApplicationClient( client_id="dummy", - token={"access_token": "xyz", "expires_in": 3600} # ← str on purpose + token={"access_token": "xyz", "expires_in": 3600} + ) + self.assertIsNotNone(client) + client = WebApplicationClient( + client_id="dummy", + token={"access_token": "xyz", "expires_in": 3600.12} ) self.assertIsNotNone(client) diff --git a/tests/oauth2/rfc6749/test_parameters.py b/tests/oauth2/rfc6749/test_parameters.py index 63b74c37..bd8a8b61 100644 --- a/tests/oauth2/rfc6749/test_parameters.py +++ b/tests/oauth2/rfc6749/test_parameters.py @@ -312,6 +312,11 @@ def test_parse_expires(self): ('expires_in and expires_at float', (3600, 200.42), (3600, 200.42, 200.42)), ('expires_in and expires_at str-int', (3600, "200"), (3600, 200, 200)), ('expires_in and expires_at str-float', (3600, "200.42"), (3600, 200.42, 200.42)), + ('expires_in float only', (3600.12, None), (3600, 4600, 4600)), + ('expires_in float and expires_at', (3600.12, 200), (3600, 200, 200)), + ('expires_in float and expires_at float', (3600.12, 200.42), (3600, 200.42, 200.42)), + ('expires_in float and expires_at str-int', (3600.12, "200"), (3600, 200, 200)), + ('expires_in float and expires_at str-float', (3600.12, "200.42"), (3600, 200.42, 200.42)), ('expires_in str only', ("3600", None), (3600, 4600, 4600)), ('expires_in str and expires_at', ("3600", 200), (3600, 200, 200)), ('expires_in str and expires_at float', ("3600", 200.42), (3600, 200.42, 200.42)), From 93fdf9144060751d555915960054431cadb6679a Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Thu, 19 Jun 2025 17:39:49 +0200 Subject: [PATCH 3/5] Add twine manual instructions --- docs/release_process.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release_process.rst b/docs/release_process.rst index 70cc1a5f..8588ce28 100644 --- a/docs/release_process.rst +++ b/docs/release_process.rst @@ -67,6 +67,12 @@ List of tasks to do a release from a maintainer point of view: - Create a release with GitHub Releases - Merge PR, close Github milestone +In case of issues with CICD and a manual publish is required, follow these steps: + + - Install dependencies `pip install build twine` + - Run `python -m build` + - Run `twine check dist/*` + - Run `twine upload dist/*` Initial setup: - Because we currently use "trusted publisher", it does not require to setup From 9b65baf2432e33f51c4e9a834dd6973277385ad1 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Thu, 19 Jun 2025 17:39:55 +0200 Subject: [PATCH 4/5] Bump version --- CHANGELOG.rst | 6 ++++++ oauthlib/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8064cd0d..2b2b6d56 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= +3.3.1 (2025-06-19): +------------------ +OAuth2.0 Client: +* #906: fix regression of expires_in parsing when float in string. + + 3.3.0 (2025-06-17): ------------------ OAuth2.0 Provider: diff --git a/oauthlib/__init__.py b/oauthlib/__init__.py index 2920cf44..462612f8 100644 --- a/oauthlib/__init__.py +++ b/oauthlib/__init__.py @@ -12,7 +12,7 @@ from logging import NullHandler __author__ = 'The OAuthlib Community' -__version__ = '3.3.0' +__version__ = '3.3.1' logging.getLogger('oauthlib').addHandler(NullHandler()) From 38c2a8e859a0d0890b76fbb1a42e4d72ce84ae04 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Fri, 20 Jun 2025 00:46:38 +0200 Subject: [PATCH 5/5] Merge publish into build workflow Having two workflows are usually required when different events are used. Calling from one workflow another workflow of the same repository is not efficient and clear. --- .github/workflows/lint_python.yml | 5 +++-- .github/workflows/python-build.yml | 33 ++++++++++++++++++++++++++++ .github/workflows/python-publish.yml | 32 --------------------------- 3 files changed, 36 insertions(+), 34 deletions(-) delete mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 683a3283..25e7f88b 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -3,6 +3,8 @@ on: [pull_request, push] jobs: lint_python: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -10,9 +12,8 @@ jobs: python-version: 3.x check-latest: true - run: pip install --upgrade pip setuptools wheel - - run: pip install black codespell mypy pytest ruff safety + - run: pip install codespell mypy pytest ruff safety - run: ruff check --output-format=github . - - run: black --check . || true - run: codespell --ignore-words-list="implementor,mimiced,provicers,re-use,THIRDPARTY,assertIn" # --skip="*.css,*.js,*.lock" - run: pip install -r requirements-test.txt - run: pip install --editable . diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 8108b6dc..51623fad 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -9,6 +9,8 @@ jobs: matrix: python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v5 @@ -32,6 +34,8 @@ jobs: needs: tests runs-on: ubuntu-latest container: python:3-slim + permissions: + contents: read steps: - name: Finished run: | @@ -44,6 +48,8 @@ jobs: matrix: toxenv: ["docs", "readme"] runs-on: ubuntu-latest + permissions: + contents: read steps: - run: sudo apt install -y graphviz - name: Set up Python @@ -56,3 +62,30 @@ jobs: run: pip install tox - name: Run python tests run: tox -e ${{ matrix.toxenv }} + pypi-publish: + needs: + - tests + - docs + - coveralls + if: ${{ success() }} && github.repository == 'oauthlib/oauthlib' && startsWith(github.ref, 'refs/tags') + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/oauthlib + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install prereq + run: pip install build twine + - name: Build python package + run: python -m build + - name: Check python package + run: twine check dist/* + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index b36e018a..00000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Publish release -on: - workflow_run: - workflows: ["Python Tests"] - types: - - completed -jobs: - pypi-publish: - if: github.repository_owner == 'oauthlib' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - name: Upload release to PyPI - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/oauthlib - permissions: - id-token: write # IMPORTANT: this permission is mandatory for trusted publishing - steps: - - name: Check out repository code - uses: actions/checkout@v4 - with: # by default, this event will trigger a workflow on the default branch. - ref: ${{ github.event.workflow_run.head_ref }} # set source branch - - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - name: Install prereq - run: pip install wheel - - name: Build python package - run: python setup.py build - - name: Package python package - run: python setup.py sdist bdist_wheel - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy