pywhatwgurl 0.1.0__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.
- pywhatwgurl-0.1.0/.github/workflows/actions/fetch_wpt_url_test_data/action.yml +46 -0
- pywhatwgurl-0.1.0/.github/workflows/docs-check.yml +33 -0
- pywhatwgurl-0.1.0/.github/workflows/docs.yml +64 -0
- pywhatwgurl-0.1.0/.github/workflows/fetch_test_data.yml +24 -0
- pywhatwgurl-0.1.0/.github/workflows/main.yml +73 -0
- pywhatwgurl-0.1.0/.github/workflows/package.yml +33 -0
- pywhatwgurl-0.1.0/.github/workflows/release.yml +66 -0
- pywhatwgurl-0.1.0/.github/workflows/update_wpt_url.yml +131 -0
- pywhatwgurl-0.1.0/.gitignore +31 -0
- pywhatwgurl-0.1.0/.pre-commit-config.yaml +24 -0
- pywhatwgurl-0.1.0/.python-version +1 -0
- pywhatwgurl-0.1.0/CHANGELOG.md +25 -0
- pywhatwgurl-0.1.0/CONTRIBUTING.md +214 -0
- pywhatwgurl-0.1.0/LICENSE +21 -0
- pywhatwgurl-0.1.0/PKG-INFO +207 -0
- pywhatwgurl-0.1.0/README.md +161 -0
- pywhatwgurl-0.1.0/docs/about/changelog.md +50 -0
- pywhatwgurl-0.1.0/docs/about/compliance.md +90 -0
- pywhatwgurl-0.1.0/docs/about/contributing.md +200 -0
- pywhatwgurl-0.1.0/docs/api/exceptions.md +130 -0
- pywhatwgurl-0.1.0/docs/api/url.md +157 -0
- pywhatwgurl-0.1.0/docs/api/urlsearchparams.md +161 -0
- pywhatwgurl-0.1.0/docs/getting-started/installation.md +76 -0
- pywhatwgurl-0.1.0/docs/getting-started/quickstart.md +172 -0
- pywhatwgurl-0.1.0/docs/guide/migration.md +325 -0
- pywhatwgurl-0.1.0/docs/guide/search-params.md +249 -0
- pywhatwgurl-0.1.0/docs/guide/url-components.md +227 -0
- pywhatwgurl-0.1.0/docs/guide/url-parsing.md +174 -0
- pywhatwgurl-0.1.0/docs/index.md +116 -0
- pywhatwgurl-0.1.0/mkdocs.yml +107 -0
- pywhatwgurl-0.1.0/pyproject.toml +115 -0
- pywhatwgurl-0.1.0/pytest.ini +2 -0
- pywhatwgurl-0.1.0/pywhatwgurl/__init__.py +43 -0
- pywhatwgurl-0.1.0/pywhatwgurl/_version.py +34 -0
- pywhatwgurl-0.1.0/pywhatwgurl/encoding.py +189 -0
- pywhatwgurl-0.1.0/pywhatwgurl/host.py +321 -0
- pywhatwgurl-0.1.0/pywhatwgurl/idna_processor.py +363 -0
- pywhatwgurl-0.1.0/pywhatwgurl/interfaces.py +457 -0
- pywhatwgurl-0.1.0/pywhatwgurl/parser.py +767 -0
- pywhatwgurl-0.1.0/pywhatwgurl/py.typed +0 -0
- pywhatwgurl-0.1.0/pywhatwgurl/record.py +50 -0
- pywhatwgurl-0.1.0/pywhatwgurl/search_params.py +233 -0
- pywhatwgurl-0.1.0/pywhatwgurl/url.py +256 -0
- pywhatwgurl-0.1.0/tests/conformance/README.md +39 -0
- pywhatwgurl-0.1.0/tests/conformance/WPT_TRACEABILITY.md +132 -0
- pywhatwgurl-0.1.0/tests/conformance/conftest.py +5 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_idna.py +102 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_percent_encoding.py +31 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_setters.py +30 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_setters_stripping.py +350 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_toascii.py +86 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_url_searchparams_integration.py +94 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_url_statics.py +68 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_url_tojson.py +13 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_urlsearchparams.py +830 -0
- pywhatwgurl-0.1.0/tests/conformance/test_wpt_urltestdata.py +88 -0
- pywhatwgurl-0.1.0/tests/conformance/wpt_data.py +104 -0
- pywhatwgurl-0.1.0/tests/conftest.py +23 -0
- pywhatwgurl-0.1.0/tests/mocks.py +162 -0
- pywhatwgurl-0.1.0/tests/unit/test_interfaces.py +93 -0
- pywhatwgurl-0.1.0/tests/unit/test_python_api.py +170 -0
- pywhatwgurl-0.1.0/tools/wpt_url_test_data.py +343 -0
- pywhatwgurl-0.1.0/util/bump_version.py +61 -0
- pywhatwgurl-0.1.0/util/wpt_url_test_data.py +343 -0
- pywhatwgurl-0.1.0/uv.lock +1772 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: 'fetch_wpt_url_test_data'
|
|
2
|
+
description: 'Retrieve WPT URL test suite JSON.'
|
|
3
|
+
inputs:
|
|
4
|
+
dest_dir:
|
|
5
|
+
description: 'Directory (relative to $GITHUB_WORKSPACE) in which to save the sanitized file'
|
|
6
|
+
required: true
|
|
7
|
+
commit:
|
|
8
|
+
description: 'Pinned WPT commit hash to fetch URL resources from'
|
|
9
|
+
required: false
|
|
10
|
+
default: 'efb889eb4c9ca0b4e3ebd8ab308646b7483a8f54'
|
|
11
|
+
runs:
|
|
12
|
+
using: "composite"
|
|
13
|
+
steps:
|
|
14
|
+
- name: Ensure destination directory exists
|
|
15
|
+
shell: bash
|
|
16
|
+
run: |
|
|
17
|
+
mkdir -p "${{ github.workspace }}/${{ inputs.dest_dir }}"
|
|
18
|
+
|
|
19
|
+
- name: Restore cached JSON
|
|
20
|
+
id: cache
|
|
21
|
+
uses: actions/cache@v3
|
|
22
|
+
with:
|
|
23
|
+
path: |
|
|
24
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/urltestdata.json
|
|
25
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/setters_tests.json
|
|
26
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/percent-encoding.json
|
|
27
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/toascii.json
|
|
28
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/IdnaTestV2.json
|
|
29
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/IdnaTestV2-removed.json
|
|
30
|
+
${{ github.workspace }}/${{ inputs.dest_dir }}/wpt_url_test_data_meta.json
|
|
31
|
+
key: wpt-urltestdata-${{ inputs.commit }}
|
|
32
|
+
|
|
33
|
+
- name: Download JSON if cache miss
|
|
34
|
+
if: steps.cache.outputs.cache-hit != 'true'
|
|
35
|
+
shell: bash
|
|
36
|
+
run: |
|
|
37
|
+
echo "Cache miss! Downloading fresh copy…"
|
|
38
|
+
python3 "${{ github.workspace }}/util/wpt_url_test_data.py" download \
|
|
39
|
+
--dest_dir "${{ github.workspace }}/${{ inputs.dest_dir }}" \
|
|
40
|
+
--commit "${{ inputs.commit }}"
|
|
41
|
+
echo "Download complete."
|
|
42
|
+
|
|
43
|
+
- name: Skip download if cache hit
|
|
44
|
+
if: steps.cache.outputs.cache-hit == 'true'
|
|
45
|
+
shell: bash
|
|
46
|
+
run: echo "Cache hit! Using existing JSON."
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Docstring Coverage
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- master
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
docstring-coverage:
|
|
13
|
+
name: docstring coverage
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version-file: ".python-version"
|
|
28
|
+
|
|
29
|
+
- name: Install the project (including dev tools)
|
|
30
|
+
run: uv sync --locked --all-extras --dev
|
|
31
|
+
|
|
32
|
+
- name: Check docstring coverage (interrogate)
|
|
33
|
+
run: uv run interrogate pywhatwgurl/
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Deploy Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- master
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: "pages"
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout repository
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
|
|
30
|
+
- name: Set up Python
|
|
31
|
+
uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
uses: astral-sh/setup-uv@v4
|
|
37
|
+
with:
|
|
38
|
+
version: "latest"
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: uv sync --group docs
|
|
42
|
+
|
|
43
|
+
- name: Build documentation
|
|
44
|
+
run: uv run mkdocs build --strict
|
|
45
|
+
|
|
46
|
+
- name: Create .nojekyll
|
|
47
|
+
run: touch site/.nojekyll
|
|
48
|
+
|
|
49
|
+
- name: Upload artifact
|
|
50
|
+
uses: actions/upload-pages-artifact@v3
|
|
51
|
+
with:
|
|
52
|
+
path: site
|
|
53
|
+
|
|
54
|
+
deploy:
|
|
55
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment:
|
|
59
|
+
name: github-pages
|
|
60
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
61
|
+
steps:
|
|
62
|
+
- name: Deploy to GitHub Pages
|
|
63
|
+
id: deployment
|
|
64
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Fetch WPT URL Test Data
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
WPT_URL_COMMIT: efb889eb4c9ca0b4e3ebd8ab308646b7483a8f54
|
|
5
|
+
WPT_TEST_DATA_PATH: tests/conformance/data
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: '0 0 * * *' # Runs every day at midnight UTC
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
fetch-wpt-url-test-data:
|
|
14
|
+
name: Fetch fixtures
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v3
|
|
19
|
+
|
|
20
|
+
- name: Fetch WPT URL test suite JSON
|
|
21
|
+
uses: ./.github/workflows/actions/fetch_wpt_url_test_data
|
|
22
|
+
with:
|
|
23
|
+
commit: ${{ env.WPT_URL_COMMIT }}
|
|
24
|
+
dest_dir: ${{ env.WPT_TEST_DATA_PATH }}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
SOURCE_ROOT: pywhatwgurl
|
|
5
|
+
WPT_URL_COMMIT: efb889eb4c9ca0b4e3ebd8ab308646b7483a8f54
|
|
6
|
+
WPT_TEST_DATA_PATH: tests/conformance/data
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- master
|
|
12
|
+
push:
|
|
13
|
+
branches:
|
|
14
|
+
- master
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: lint
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v5
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version-file: ".python-version"
|
|
33
|
+
|
|
34
|
+
- name: Install the project (including dev tools)
|
|
35
|
+
run: uv sync --locked --all-extras --dev
|
|
36
|
+
|
|
37
|
+
- name: Run Ruff
|
|
38
|
+
run: uv run ruff check "${{ env.SOURCE_ROOT }}"
|
|
39
|
+
|
|
40
|
+
- name: Run mypy
|
|
41
|
+
run: uv run mypy "${{ env.SOURCE_ROOT }}"
|
|
42
|
+
|
|
43
|
+
- name: Run pip-audit
|
|
44
|
+
run: |
|
|
45
|
+
uv export --no-dev --no-hashes --no-emit-project -o /tmp/requirements.txt
|
|
46
|
+
uv run pip-audit --disable-pip --no-deps -r /tmp/requirements.txt
|
|
47
|
+
|
|
48
|
+
test:
|
|
49
|
+
name: test
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
strategy:
|
|
52
|
+
matrix:
|
|
53
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
54
|
+
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
with:
|
|
58
|
+
fetch-depth: 0
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v5
|
|
62
|
+
|
|
63
|
+
- name: Fetch WPT URL resources
|
|
64
|
+
uses: ./.github/workflows/actions/fetch_wpt_url_test_data
|
|
65
|
+
with:
|
|
66
|
+
commit: ${{ env.WPT_URL_COMMIT }}
|
|
67
|
+
dest_dir: ${{ env.WPT_TEST_DATA_PATH }}
|
|
68
|
+
|
|
69
|
+
- name: Install the project
|
|
70
|
+
run: uv sync --locked --all-extras --dev
|
|
71
|
+
|
|
72
|
+
- name: Run tests
|
|
73
|
+
run: uv run pytest
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- master
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v5
|
|
19
|
+
with:
|
|
20
|
+
version: "latest"
|
|
21
|
+
enable-cache: true
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: uv build
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
- name: Publish package
|
|
30
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
31
|
+
run: uv publish --publish-url "$UV_PUBLISH_URL"
|
|
32
|
+
env:
|
|
33
|
+
UV_PUBLISH_URL: https://${{ secrets.GEMFURY_PUSH_TOKEN }}@push.fury.io/pywhatwgurl/
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
bump_rule:
|
|
7
|
+
description: 'Version Bump Rule'
|
|
8
|
+
required: true
|
|
9
|
+
default: 'patch'
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- patch
|
|
13
|
+
- minor
|
|
14
|
+
- major
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
release:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
permissions:
|
|
20
|
+
contents: write
|
|
21
|
+
id-token: write
|
|
22
|
+
environment:
|
|
23
|
+
name: pypi
|
|
24
|
+
url: https://pypi.org/project/pywhatwgurl/
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
|
|
30
|
+
- name: Configure Git
|
|
31
|
+
run: |
|
|
32
|
+
git config user.name "github-actions[bot]"
|
|
33
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
34
|
+
|
|
35
|
+
- name: Calculate Next Version
|
|
36
|
+
id: version
|
|
37
|
+
run: python util/bump_version.py ${{ inputs.bump_rule }}
|
|
38
|
+
|
|
39
|
+
- name: Create and Push Tag
|
|
40
|
+
run: |
|
|
41
|
+
git tag ${{ steps.version.outputs.new_tag }}
|
|
42
|
+
git push origin ${{ steps.version.outputs.new_tag }}
|
|
43
|
+
|
|
44
|
+
- name: Install uv
|
|
45
|
+
uses: astral-sh/setup-uv@v5
|
|
46
|
+
with:
|
|
47
|
+
version: "latest"
|
|
48
|
+
enable-cache: true
|
|
49
|
+
|
|
50
|
+
- name: Build package
|
|
51
|
+
run: uv build
|
|
52
|
+
|
|
53
|
+
- name: Generate SBOM
|
|
54
|
+
run: |
|
|
55
|
+
uv export --no-dev --no-hashes --format=requirements-txt > requirements.txt
|
|
56
|
+
uv run cyclonedx-py requirements requirements.txt --output-format json --output-file sbom.json
|
|
57
|
+
|
|
58
|
+
- name: Publish to PyPI
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
60
|
+
|
|
61
|
+
- name: Create GitHub Release
|
|
62
|
+
uses: softprops/action-gh-release@v2
|
|
63
|
+
with:
|
|
64
|
+
tag_name: ${{ steps.version.outputs.new_tag }}
|
|
65
|
+
generate_release_notes: true
|
|
66
|
+
files: sbom.json
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
name: Bump WPT URL Pin
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: "0 3 * * 1" # Mondays at 03:00 UTC
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
update-wpt-url:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.x"
|
|
24
|
+
|
|
25
|
+
- name: Get pinned WPT commit
|
|
26
|
+
id: current
|
|
27
|
+
shell: bash
|
|
28
|
+
run: |
|
|
29
|
+
CURRENT=$(python - <<'PY'
|
|
30
|
+
import pathlib, re, sys
|
|
31
|
+
text = pathlib.Path("util/wpt_url_test_data.py").read_text()
|
|
32
|
+
m = re.search(r'DEFAULT_WPT_COMMIT = "([0-9a-f]+)"', text)
|
|
33
|
+
if not m:
|
|
34
|
+
sys.exit("Could not find DEFAULT_WPT_COMMIT")
|
|
35
|
+
print(m.group(1))
|
|
36
|
+
PY
|
|
37
|
+
)
|
|
38
|
+
echo "commit=${CURRENT}" >> "$GITHUB_OUTPUT"
|
|
39
|
+
|
|
40
|
+
- name: Get latest WPT url commit
|
|
41
|
+
id: latest
|
|
42
|
+
shell: bash
|
|
43
|
+
run: |
|
|
44
|
+
LATEST=$(python - <<'PY'
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import urllib.request
|
|
48
|
+
|
|
49
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
50
|
+
headers = {"User-Agent": "pywhatwgurl-ci"}
|
|
51
|
+
if token:
|
|
52
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
53
|
+
|
|
54
|
+
req = urllib.request.Request(
|
|
55
|
+
"https://api.github.com/repos/web-platform-tests/wpt/commits?path=url&per_page=1",
|
|
56
|
+
headers=headers,
|
|
57
|
+
)
|
|
58
|
+
with urllib.request.urlopen(req) as resp:
|
|
59
|
+
data = json.load(resp)
|
|
60
|
+
print(data[0]["sha"])
|
|
61
|
+
PY
|
|
62
|
+
)
|
|
63
|
+
echo "commit=${LATEST}" >> "$GITHUB_OUTPUT"
|
|
64
|
+
|
|
65
|
+
- name: Stop if already up to date
|
|
66
|
+
if: steps.latest.outputs.commit == steps.current.outputs.commit
|
|
67
|
+
run: |
|
|
68
|
+
echo "Pinned commit matches latest (${{ steps.latest.outputs.commit }}). Nothing to do."
|
|
69
|
+
exit 0
|
|
70
|
+
|
|
71
|
+
- name: Update pinned commit hashes
|
|
72
|
+
if: steps.latest.outputs.commit != steps.current.outputs.commit
|
|
73
|
+
shell: bash
|
|
74
|
+
run: |
|
|
75
|
+
TARGET="${{ steps.latest.outputs.commit }}"
|
|
76
|
+
python - <<'PY'
|
|
77
|
+
import pathlib
|
|
78
|
+
import re
|
|
79
|
+
import sys
|
|
80
|
+
|
|
81
|
+
target = "${{ steps.latest.outputs.commit }}"
|
|
82
|
+
def replace_yaml_scalar_key(key: str, text: str) -> str:
|
|
83
|
+
pattern = rf"^(\s*){re.escape(key)}\s*:\s*([0-9a-f]+)\s*$"
|
|
84
|
+
new_text, count = re.subn(
|
|
85
|
+
pattern,
|
|
86
|
+
lambda m: f"{m.group(1)}{key}: {target}",
|
|
87
|
+
text,
|
|
88
|
+
flags=re.MULTILINE,
|
|
89
|
+
)
|
|
90
|
+
if count == 0:
|
|
91
|
+
raise RuntimeError(f"Failed to update {key}")
|
|
92
|
+
return new_text
|
|
93
|
+
|
|
94
|
+
updates = [
|
|
95
|
+
".github/workflows/fetch_test_data.yml",
|
|
96
|
+
".github/workflows/main.yml",
|
|
97
|
+
]
|
|
98
|
+
for path in updates:
|
|
99
|
+
p = pathlib.Path(path)
|
|
100
|
+
p.write_text(replace_yaml_scalar_key("WPT_URL_COMMIT", p.read_text()))
|
|
101
|
+
|
|
102
|
+
action_path = pathlib.Path(".github/workflows/actions/fetch_wpt_url_test_data/action.yml")
|
|
103
|
+
action_text = action_path.read_text()
|
|
104
|
+
def action_repl(m: re.Match[str]) -> str:
|
|
105
|
+
return f"{m.group(1)}'{target}'"
|
|
106
|
+
new_action, count = re.subn(r"(default: )'[^']+'", action_repl, action_text)
|
|
107
|
+
if count == 0:
|
|
108
|
+
raise RuntimeError("Failed to update action default commit")
|
|
109
|
+
action_path.write_text(new_action)
|
|
110
|
+
|
|
111
|
+
util_path = pathlib.Path("util/wpt_url_test_data.py")
|
|
112
|
+
util_text = util_path.read_text()
|
|
113
|
+
def util_repl(m: re.Match[str]) -> str:
|
|
114
|
+
return f'{m.group(1)}{target}{m.group(3)}'
|
|
115
|
+
new_util, count = re.subn(r'(DEFAULT_WPT_COMMIT = ")([0-9a-f]+)(")', util_repl, util_text)
|
|
116
|
+
if count == 0:
|
|
117
|
+
raise RuntimeError("Failed to update util DEFAULT_WPT_COMMIT")
|
|
118
|
+
util_path.write_text(new_util)
|
|
119
|
+
PY
|
|
120
|
+
|
|
121
|
+
- name: Create pull request
|
|
122
|
+
if: steps.latest.outputs.commit != steps.current.outputs.commit
|
|
123
|
+
uses: peter-evans/create-pull-request@v6
|
|
124
|
+
with:
|
|
125
|
+
token: ${{ secrets.WORKFLOW_PAT || github.token }}
|
|
126
|
+
commit-message: "chore: bump WPT URL tests to ${{ steps.latest.outputs.commit }}"
|
|
127
|
+
title: "chore: bump WPT URL tests to ${{ steps.latest.outputs.commit }}"
|
|
128
|
+
branch: "ci/update-wpt-url"
|
|
129
|
+
delete-branch: true
|
|
130
|
+
body: |
|
|
131
|
+
- Update pinned WPT URL commit to `${{ steps.latest.outputs.commit }}`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# VS Code configuration
|
|
13
|
+
.vscode/
|
|
14
|
+
*.code-workspace
|
|
15
|
+
|
|
16
|
+
# Downloaded WPT URL test data
|
|
17
|
+
tests/conformance/data/
|
|
18
|
+
|
|
19
|
+
# Local caches (do not commit)
|
|
20
|
+
.cache/
|
|
21
|
+
.cache/tests/wpt/
|
|
22
|
+
.uv-cache/
|
|
23
|
+
|
|
24
|
+
# Generated version file
|
|
25
|
+
pywhatwgurl/_version.py
|
|
26
|
+
|
|
27
|
+
# MkDocs build output
|
|
28
|
+
site/
|
|
29
|
+
|
|
30
|
+
# Agent files
|
|
31
|
+
AGENTS.md
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: ruff
|
|
5
|
+
name: Run ruff check with fix
|
|
6
|
+
entry: uv run ruff check --fix
|
|
7
|
+
language: system # Assumes `uv` is installed and available in the system PATH
|
|
8
|
+
types: [python]
|
|
9
|
+
files: \.py$
|
|
10
|
+
|
|
11
|
+
- id: mypy
|
|
12
|
+
name: Run mypy type checking
|
|
13
|
+
entry: uv run mypy pywhatwgurl
|
|
14
|
+
language: system
|
|
15
|
+
types: [python]
|
|
16
|
+
files: ^pywhatwgurl/
|
|
17
|
+
pass_filenames: false
|
|
18
|
+
|
|
19
|
+
- id: pip-audit
|
|
20
|
+
name: Run pip-audit (dependency vulnerability scan)
|
|
21
|
+
entry: bash -c 'uv export --no-dev --no-hashes --no-emit-project -o /tmp/requirements.txt && uv run pip-audit --disable-pip --no-deps -r /tmp/requirements.txt'
|
|
22
|
+
language: system
|
|
23
|
+
always_run: true
|
|
24
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-03-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial public release of `pywhatwgurl`.
|
|
14
|
+
- Full `URL` and `URLSearchParams` API implementation per the WHATWG URL Standard.
|
|
15
|
+
- 100% WHATWG URL Standard conformance for core URL parsing (validated against [Web Platform Tests](https://github.com/web-platform-tests/wpt/tree/master/url)).
|
|
16
|
+
- Pure Python, fully type-annotated (`py.typed`), zero compiled dependencies.
|
|
17
|
+
- Comprehensive documentation with API reference, user guide, and migration notes.
|
|
18
|
+
- CI pipeline with linting (ruff), type checking (mypy), and security scanning (pip-audit).
|
|
19
|
+
- Automated weekly WPT test data updates via GitHub Actions.
|
|
20
|
+
- SBOM generation (CycloneDX) attached to every GitHub release.
|
|
21
|
+
|
|
22
|
+
### Known Limitations
|
|
23
|
+
- **IDNA Conformance**: IDNA tests are marked as expected failures (`xfail`). The Python `idna` library follows stricter RFC 5891/5892 rules than the WHATWG URL Standard's lenient UTS46 processing.
|
|
24
|
+
- Real-world domains generally work correctly; failures are primarily with obscure edge cases not covered by the stricter RFCs.
|
|
25
|
+
- No Python WHATWG-compliant IDNA implementation currently exists.
|