pyoncat 2.8__tar.gz → 2.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyoncat
3
- Version: 2.8
3
+ Version: 2.9
4
4
  Summary: A Python Client for ONCat (the ORNL Neutron Catalog).
5
5
  Keywords: ornl,neutron,metadata,catalog
6
6
  Author: ONCat Support
@@ -30,7 +30,7 @@ The `pyoncat` package serves as a Python client to more easily interact with the
30
30
 
31
31
  ### Requirements
32
32
 
33
- - Python version: `>=3.7, <4`
33
+ - Python version: `>=3.9, <4`
34
34
  - Python packages: `requests`
35
35
  - Optional packages for authenticated API access: `oauthlib`, `requests-oauthlib`
36
36
 
@@ -10,7 +10,7 @@ The `pyoncat` package serves as a Python client to more easily interact with the
10
10
 
11
11
  ### Requirements
12
12
 
13
- - Python version: `>=3.7, <4`
13
+ - Python version: `>=3.9, <4`
14
14
  - Python packages: `requests`
15
15
  - Optional packages for authenticated API access: `oauthlib`, `requests-oauthlib`
16
16
 
@@ -34,7 +34,7 @@ from pyoncat.reduction import ReductionFileBuilder
34
34
  if TYPE_CHECKING:
35
35
  import threading
36
36
 
37
- __version__ = "2.8"
37
+ __version__ = "2.9"
38
38
 
39
39
  # Sent on every request PyONCat makes, API calls and token requests alike,
40
40
  # so server logs can tell PyONCat traffic from other clients.
@@ -937,12 +937,13 @@ class ONCat:
937
937
  device_authorization_timeout: float | None
938
938
  Device Authorization Grant only. An overall caller
939
939
  deadline in seconds for the sign-in, distinct from the
940
- RFC 8628 server-side `expires_in`. When set, polling
941
- stops at the deadline with `DeviceAuthorizationTimeout`.
942
- Polling also stops no later than the server's own
943
- `expires_in` (the effective deadline is the earlier of
944
- the two). `None` (the default) means no overall deadline
945
- -- today's behavior.
940
+ RFC 8628 server-side `expires_in`. Polling stops at the
941
+ earlier of the two. When the caller deadline is reached
942
+ first, the sign-in raises `DeviceAuthorizationTimeout`;
943
+ when the server's `expires_in` is reached first (or at
944
+ the same moment, or no caller deadline is set), it raises
945
+ `DeviceAuthorizationExpired`. `None` (the default) means
946
+ no overall caller deadline.
946
947
 
947
948
  http_timeout: float | None
948
949
  Device Authorization Grant only. The connect/read HTTP
@@ -1212,14 +1213,22 @@ class ONCat:
1212
1213
  # The effective deadline is the earlier of the caller's overall
1213
1214
  # `device_authorization_timeout` (clocked from before the
1214
1215
  # initial POST) and the server's own `expires_in` (clocked from
1215
- # now, once known).
1216
+ # now, once known). Which one it is decides the outcome raised
1217
+ # when it passes: the server expiry means the device code is dead
1218
+ # (`DeviceAuthorizationExpired`, the same outcome as a server
1219
+ # `expired_token`), the caller deadline means the caller gave up
1220
+ # (`DeviceAuthorizationTimeout`). On a tie the device code is dead
1221
+ # either way, so the server expiry wins.
1216
1222
  server_expiry = (
1217
1223
  monotonic() + expires_in if expires_in is not None else None
1218
1224
  )
1219
- deadlines = [
1220
- d for d in (caller_deadline, server_expiry) if d is not None
1221
- ]
1222
- deadline = min(deadlines) if deadlines else None
1225
+ deadline = caller_deadline
1226
+ deadline_is_server_expiry = False
1227
+ if server_expiry is not None and (
1228
+ caller_deadline is None or server_expiry <= caller_deadline
1229
+ ):
1230
+ deadline = server_expiry
1231
+ deadline_is_server_expiry = True
1223
1232
 
1224
1233
  self._dispatch_device_authorization_challenge(
1225
1234
  prompt,
@@ -1241,10 +1250,18 @@ class ONCat:
1241
1250
 
1242
1251
  while True:
1243
1252
  self._raise_if_cancelled(cancel_event)
1244
- self._raise_if_past_deadline(deadline, monotonic)
1253
+ self._raise_if_past_deadline(
1254
+ deadline,
1255
+ monotonic,
1256
+ server_expiry=deadline_is_server_expiry,
1257
+ )
1245
1258
  sleep(self._poll_wait_seconds(interval, deadline, monotonic))
1246
1259
  self._raise_if_cancelled(cancel_event)
1247
- self._raise_if_past_deadline(deadline, monotonic)
1260
+ self._raise_if_past_deadline(
1261
+ deadline,
1262
+ monotonic,
1263
+ server_expiry=deadline_is_server_expiry,
1264
+ )
1248
1265
 
1249
1266
  try:
1250
1267
  token_response = requests.post(
@@ -1377,14 +1394,32 @@ class ONCat:
1377
1394
 
1378
1395
  @staticmethod
1379
1396
  def _raise_if_past_deadline(
1380
- deadline: float | None, monotonic: Callable[[], float]
1397
+ deadline: float | None,
1398
+ monotonic: Callable[[], float],
1399
+ *,
1400
+ server_expiry: bool = False,
1381
1401
  ) -> None:
1382
- if deadline is not None and monotonic() >= deadline:
1402
+ """Raise once `deadline` has passed.
1403
+
1404
+ `server_expiry` says the deadline is the device code's own
1405
+ `expires_in`, so passing it raises `DeviceAuthorizationExpired`;
1406
+ otherwise it is the caller's `device_authorization_timeout` and
1407
+ passing it raises `DeviceAuthorizationTimeout`.
1408
+ """
1409
+ if deadline is None or monotonic() < deadline:
1410
+ return
1411
+ if server_expiry:
1383
1412
  msg = (
1384
- "Device authorization timed out before the user completed "
1385
- "the sign-in."
1413
+ "Device authorization expired: the device code's "
1414
+ "expires_in lapsed before the user completed the sign-in."
1386
1415
  )
1387
- raise DeviceAuthorizationTimeout(msg)
1416
+ raise DeviceAuthorizationExpired(msg)
1417
+ msg = (
1418
+ "Device authorization timed out: the caller's "
1419
+ "device_authorization_timeout lapsed before the user completed "
1420
+ "the sign-in."
1421
+ )
1422
+ raise DeviceAuthorizationTimeout(msg)
1388
1423
 
1389
1424
  def _dispatch_device_authorization_challenge(
1390
1425
  self,
@@ -2160,7 +2195,10 @@ class DeviceAuthorizationDenied(DeviceAuthorizationError): # ruff: ignore[error
2160
2195
 
2161
2196
  class DeviceAuthorizationExpired(DeviceAuthorizationError): # ruff: ignore[error-suffix-on-exception-name] - public API name (ADR-000097)
2162
2197
  """Raised when the device code expired before the user approved the
2163
- sign-in (the IdP returned `expired_token`, RFC 8628 §3.5)."""
2198
+ sign-in: the IdP returned `expired_token` (RFC 8628 §3.5), or the
2199
+ server's `expires_in` lapsed no later than the caller's
2200
+ `device_authorization_timeout`. The code is dead, so a new sign-in
2201
+ must start from a fresh device code."""
2164
2202
 
2165
2203
 
2166
2204
  class DeviceAuthorizationCancelled(DeviceAuthorizationError): # ruff: ignore[error-suffix-on-exception-name] - public API name (ADR-000097)
@@ -2170,8 +2208,9 @@ class DeviceAuthorizationCancelled(DeviceAuthorizationError): # ruff: ignore[er
2170
2208
 
2171
2209
  class DeviceAuthorizationTimeout(DeviceAuthorizationError): # ruff: ignore[error-suffix-on-exception-name] - public API name (ADR-000097)
2172
2210
  """Raised when the sign-in did not complete before the overall
2173
- caller deadline (`device_authorization_timeout`) or the server's own
2174
- `expires_in`, whichever came first."""
2211
+ caller deadline (`device_authorization_timeout`) and that deadline
2212
+ came strictly before the server's own `expires_in`. A server expiry
2213
+ reached first raises `DeviceAuthorizationExpired` instead."""
2175
2214
 
2176
2215
 
2177
2216
  class DeviceAuthorizationNetworkError(DeviceAuthorizationError):
@@ -1,13 +1,13 @@
1
1
  [build-system]
2
- requires = ["poetry"]
3
- build-backend = "poetry.masonry.api"
2
+ requires = ["poetry-core"]
3
+ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  # Poetry requires it's own condfiguration because it predates the
6
6
  # "[project]" standard. See:
7
7
  # https://github.com/python-poetry/roadmap/issues/3
8
8
  [tool.poetry]
9
9
  name = "pyoncat"
10
- version = "2.8"
10
+ version = "2.9"
11
11
  description = "A Python Client for ONCat (the ORNL Neutron Catalog)."
12
12
  authors = ["ONCat Support <oncat-support@ornl.gov>"]
13
13
  readme = ["pyoncat/README.md", "pyoncat/LICENSE"]
File without changes
File without changes