qurl-python 0.5.0__tar.gz → 0.5.1__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.
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/ci.yml +4 -4
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/claude-code-review.yml +1 -1
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/claude.yml +1 -1
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/release-please.yml +2 -2
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/validate-issue-templates.yml +1 -1
- qurl_python-0.5.1/.release-please-manifest.json +1 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/CHANGELOG.md +7 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/PKG-INFO +1 -1
- {qurl_python-0.5.0 → qurl_python-0.5.1}/pyproject.toml +1 -1
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/async_client.py +18 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/client.py +23 -0
- qurl_python-0.5.0/.release-please-manifest.json +0 -1
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/dependabot.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/codeql.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/dependency-age-check-actions.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/dependency-age-check-pip.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/dependency-review.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/issue-priority.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/pr-title.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.github/workflows/secrets-scan.yml +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/.gitignore +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/CLAUDE.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/CONTRIBUTING.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/LICENSE +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/README.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/SECURITY.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/release-please-config.json +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/__init__.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/_pagination.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/_utils.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/crid.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/errors.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/langchain.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/py.typed +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/src/layerv_qurl/types.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/__init__.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/client_support.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/conftest.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/fixtures/README.md +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/fixtures/crid_v1_vectors.json +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_access_code_listing.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_batch_client.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_client.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_conformance.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_contract_client.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_langchain.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_pagination.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_portal.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_post_retry.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_qurl_mutations.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_qurl_reads.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_resource_name.py +0 -0
- {qurl_python-0.5.0 → qurl_python-0.5.1}/tests/test_sdk_alignment.py +0 -0
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
steps:
|
|
19
19
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
20
|
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
-
uses: actions/setup-python@
|
|
21
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
22
22
|
with:
|
|
23
23
|
python-version: ${{ matrix.python-version }}
|
|
24
24
|
- name: Install dependencies
|
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
steps:
|
|
46
46
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
47
47
|
- name: Set up Python 3.10
|
|
48
|
-
uses: actions/setup-python@
|
|
48
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
49
49
|
with:
|
|
50
50
|
python-version: "3.10"
|
|
51
51
|
- name: Install the newest schema-1 conformance release
|
|
@@ -67,7 +67,7 @@ jobs:
|
|
|
67
67
|
steps:
|
|
68
68
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
69
69
|
- name: Set up Python 3.10
|
|
70
|
-
uses: actions/setup-python@
|
|
70
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
71
71
|
with:
|
|
72
72
|
python-version: "3.10"
|
|
73
73
|
- name: Install dependencies
|
|
@@ -89,7 +89,7 @@ jobs:
|
|
|
89
89
|
needs: [test, test-schema1, type-check]
|
|
90
90
|
if: always() && github.event_name != 'pull_request'
|
|
91
91
|
permissions: {}
|
|
92
|
-
uses: layervai/ops-routines-workflows/.github/workflows/notify.yml@
|
|
92
|
+
uses: layervai/ops-routines-workflows/.github/workflows/notify.yml@414821a809395384de83d853a4bfd5b46e6bfc55 # v0.15.0
|
|
93
93
|
with:
|
|
94
94
|
header: "qURL Python SDK Build"
|
|
95
95
|
gates: '{"Tests": "${{ needs.test.result }}", "Conformance schema 1": "${{ needs.test-schema1.result }}", "Type check": "${{ needs.type-check.result }}"}'
|
|
@@ -161,7 +161,7 @@ jobs:
|
|
|
161
161
|
# step, so the verify step never runs and the PR gets a bare
|
|
162
162
|
# `cancelled` check with no comment and nothing saying why.
|
|
163
163
|
timeout-minutes: 10
|
|
164
|
-
uses: anthropics/claude-code-action@
|
|
164
|
+
uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1.0.266
|
|
165
165
|
with:
|
|
166
166
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
167
167
|
github_token: ${{ github.token }}
|
|
@@ -328,7 +328,7 @@ jobs:
|
|
|
328
328
|
if: success()
|
|
329
329
|
# v1.0.180 keeps interactive commands on the same fail-closed runner as
|
|
330
330
|
# automatic review and avoids the v1.0.140/141 issue-comment crash.
|
|
331
|
-
uses: anthropics/claude-code-action@
|
|
331
|
+
uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1.0.266
|
|
332
332
|
with:
|
|
333
333
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
334
334
|
# Omit github_token so the action uses its OIDC/GitHub App token path.
|
|
@@ -41,7 +41,7 @@ jobs:
|
|
|
41
41
|
with:
|
|
42
42
|
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
|
43
43
|
- name: Set up Python ${{ matrix.python-version }}
|
|
44
|
-
uses: actions/setup-python@
|
|
44
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
45
45
|
with:
|
|
46
46
|
python-version: ${{ matrix.python-version }}
|
|
47
47
|
- name: Install dependencies
|
|
@@ -62,7 +62,7 @@ jobs:
|
|
|
62
62
|
with:
|
|
63
63
|
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
|
|
64
64
|
- name: Set up Python
|
|
65
|
-
uses: actions/setup-python@
|
|
65
|
+
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
66
66
|
with:
|
|
67
67
|
python-version: "3.12"
|
|
68
68
|
- name: Install build tools
|
|
@@ -35,4 +35,4 @@ permissions:
|
|
|
35
35
|
|
|
36
36
|
jobs:
|
|
37
37
|
validate-templates:
|
|
38
|
-
uses: layervai/ops-routines-workflows/.github/workflows/validate-issue-templates.yml@
|
|
38
|
+
uses: layervai/ops-routines-workflows/.github/workflows/validate-issue-templates.yml@414821a809395384de83d853a4bfd5b46e6bfc55 # v0.15.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{".":"0.5.1"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.1](https://github.com/layervai/qurl-python/compare/v0.5.0...v0.5.1) (2026-11-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* support scheduled invitations ([#190](https://github.com/layervai/qurl-python/issues/190)) ([8222a63](https://github.com/layervai/qurl-python/commit/8222a636d1d82811ee9841df6e0facbc75ce99b1))
|
|
9
|
+
|
|
3
10
|
## [0.5.0](https://github.com/layervai/qurl-python/compare/v0.4.0...v0.5.0) (2026-11-07)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: qurl-python
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Python SDK for the qURL™ API — secure, time-limited access links. Quantum URL is how you enter the hidden layer of the internet.
|
|
5
5
|
Project-URL: Homepage, https://github.com/layervai/qurl-python
|
|
6
6
|
Project-URL: Repository, https://github.com/layervai/qurl-python
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qurl-python"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "Python SDK for the qURL™ API — secure, time-limited access links. Quantum URL is how you enter the hidden layer of the internet."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -321,6 +321,8 @@ class AsyncQURLClient:
|
|
|
321
321
|
self,
|
|
322
322
|
resource: AsyncProtectedResource | str,
|
|
323
323
|
*,
|
|
324
|
+
not_before: str | None = None,
|
|
325
|
+
expires_at: str | None = None,
|
|
324
326
|
valid_for: str | timedelta | None = None,
|
|
325
327
|
label: str | None = None,
|
|
326
328
|
one_time_use: bool | None = None,
|
|
@@ -360,6 +362,12 @@ class AsyncQURLClient:
|
|
|
360
362
|
max_sessions=max_sessions,
|
|
361
363
|
session_duration=session_duration,
|
|
362
364
|
)
|
|
365
|
+
if expires_at is not None and "expires_in" in body:
|
|
366
|
+
raise ValueError("expires_at cannot be combined with valid_for")
|
|
367
|
+
if not_before is not None:
|
|
368
|
+
body["not_before"] = not_before
|
|
369
|
+
if expires_at is not None:
|
|
370
|
+
body["expires_at"] = expires_at
|
|
363
371
|
resp = await self._request(
|
|
364
372
|
"POST",
|
|
365
373
|
f"/v1/resources/{resource_id}/qurls",
|
|
@@ -1088,6 +1096,8 @@ class AsyncQURLClient:
|
|
|
1088
1096
|
self,
|
|
1089
1097
|
resource_id: str,
|
|
1090
1098
|
*,
|
|
1099
|
+
not_before: str | None = None,
|
|
1100
|
+
expires_at: str | None = None,
|
|
1091
1101
|
expires_in: str | None = None,
|
|
1092
1102
|
label: str | None = None,
|
|
1093
1103
|
one_time_use: bool | None = None,
|
|
@@ -1099,8 +1109,12 @@ class AsyncQURLClient:
|
|
|
1099
1109
|
"""Mint a qURL against an existing resource."""
|
|
1100
1110
|
validate_id(resource_id)
|
|
1101
1111
|
validate_mint_input(label=label, max_sessions=max_sessions)
|
|
1112
|
+
if expires_at is not None and expires_in is not None:
|
|
1113
|
+
raise ValueError("expires_at cannot be combined with expires_in")
|
|
1102
1114
|
body = build_body(
|
|
1103
1115
|
{
|
|
1116
|
+
"not_before": not_before,
|
|
1117
|
+
"expires_at": expires_at,
|
|
1104
1118
|
"expires_in": expires_in,
|
|
1105
1119
|
"label": label,
|
|
1106
1120
|
"one_time_use": one_time_use,
|
|
@@ -1888,6 +1902,8 @@ class AsyncProtectedResource:
|
|
|
1888
1902
|
async def create_portal(
|
|
1889
1903
|
self,
|
|
1890
1904
|
*,
|
|
1905
|
+
not_before: str | None = None,
|
|
1906
|
+
expires_at: str | None = None,
|
|
1891
1907
|
valid_for: str | timedelta | None = None,
|
|
1892
1908
|
label: str | None = None,
|
|
1893
1909
|
one_time_use: bool | None = None,
|
|
@@ -1901,6 +1917,8 @@ class AsyncProtectedResource:
|
|
|
1901
1917
|
"""
|
|
1902
1918
|
return await self._client.create_portal(
|
|
1903
1919
|
self,
|
|
1920
|
+
not_before=not_before,
|
|
1921
|
+
expires_at=expires_at,
|
|
1904
1922
|
valid_for=valid_for,
|
|
1905
1923
|
label=label,
|
|
1906
1924
|
one_time_use=one_time_use,
|
|
@@ -371,6 +371,8 @@ class QURLClient:
|
|
|
371
371
|
self,
|
|
372
372
|
resource: ProtectedResource | str,
|
|
373
373
|
*,
|
|
374
|
+
not_before: str | None = None,
|
|
375
|
+
expires_at: str | None = None,
|
|
374
376
|
valid_for: str | timedelta | None = None,
|
|
375
377
|
label: str | None = None,
|
|
376
378
|
one_time_use: bool | None = None,
|
|
@@ -391,6 +393,11 @@ class QURLClient:
|
|
|
391
393
|
id string — the CRID a handle carries or that
|
|
392
394
|
:meth:`protect_url` returns. CRID-shaped IDs are validated
|
|
393
395
|
locally; the service validates other identifier forms.
|
|
396
|
+
not_before: Future opening time as an RFC 3339 timestamp.
|
|
397
|
+
An early attempt does not consume a single-use link.
|
|
398
|
+
expires_at: Absolute expiry as an RFC 3339 timestamp. With
|
|
399
|
+
``not_before``, it also caps active sessions. Use it in
|
|
400
|
+
place of ``valid_for``.
|
|
394
401
|
valid_for: How long the link stays valid — a duration string
|
|
395
402
|
(``"5m"``, ``"24h"``) or :class:`datetime.timedelta`. A
|
|
396
403
|
timedelta must be whole seconds and **at least one minute**;
|
|
@@ -445,6 +452,12 @@ class QURLClient:
|
|
|
445
452
|
max_sessions=max_sessions,
|
|
446
453
|
session_duration=session_duration,
|
|
447
454
|
)
|
|
455
|
+
if expires_at is not None and "expires_in" in body:
|
|
456
|
+
raise ValueError("expires_at cannot be combined with valid_for")
|
|
457
|
+
if not_before is not None:
|
|
458
|
+
body["not_before"] = not_before
|
|
459
|
+
if expires_at is not None:
|
|
460
|
+
body["expires_at"] = expires_at
|
|
448
461
|
resp = self._request(
|
|
449
462
|
"POST",
|
|
450
463
|
f"/v1/resources/{resource_id}/qurls",
|
|
@@ -1218,6 +1231,8 @@ class QURLClient:
|
|
|
1218
1231
|
self,
|
|
1219
1232
|
resource_id: str,
|
|
1220
1233
|
*,
|
|
1234
|
+
not_before: str | None = None,
|
|
1235
|
+
expires_at: str | None = None,
|
|
1221
1236
|
expires_in: str | None = None,
|
|
1222
1237
|
label: str | None = None,
|
|
1223
1238
|
one_time_use: bool | None = None,
|
|
@@ -1232,8 +1247,12 @@ class QURLClient:
|
|
|
1232
1247
|
"""
|
|
1233
1248
|
validate_id(resource_id)
|
|
1234
1249
|
validate_mint_input(label=label, max_sessions=max_sessions)
|
|
1250
|
+
if expires_at is not None and expires_in is not None:
|
|
1251
|
+
raise ValueError("expires_at cannot be combined with expires_in")
|
|
1235
1252
|
body = build_body(
|
|
1236
1253
|
{
|
|
1254
|
+
"not_before": not_before,
|
|
1255
|
+
"expires_at": expires_at,
|
|
1237
1256
|
"expires_in": expires_in,
|
|
1238
1257
|
"label": label,
|
|
1239
1258
|
"one_time_use": one_time_use,
|
|
@@ -2012,6 +2031,8 @@ class ProtectedResource:
|
|
|
2012
2031
|
def create_portal(
|
|
2013
2032
|
self,
|
|
2014
2033
|
*,
|
|
2034
|
+
not_before: str | None = None,
|
|
2035
|
+
expires_at: str | None = None,
|
|
2015
2036
|
valid_for: str | timedelta | None = None,
|
|
2016
2037
|
label: str | None = None,
|
|
2017
2038
|
one_time_use: bool | None = None,
|
|
@@ -2025,6 +2046,8 @@ class ProtectedResource:
|
|
|
2025
2046
|
"""
|
|
2026
2047
|
return self._client.create_portal(
|
|
2027
2048
|
self,
|
|
2049
|
+
not_before=not_before,
|
|
2050
|
+
expires_at=expires_at,
|
|
2028
2051
|
valid_for=valid_for,
|
|
2029
2052
|
label=label,
|
|
2030
2053
|
one_time_use=one_time_use,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{".":"0.5.0"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|