Skip to content

Commit f58ca1b

Browse files
authored
new: Type hints for python module (#2)
* new: Type hints for python module * new: Add get_domain_without_tld method * new: Add pytest suite * new: add .orig in tests, run mypy in test
1 parent 9b656ec commit f58ca1b

File tree

6 files changed

+229
-8
lines changed

6 files changed

+229
-8
lines changed

.github/workflows/maturin.yml

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
# This file is autogenerated by maturin v1.9.4
1+
# This file is autogenerated by maturin v1.9.5
22
# To update, run
33
#
4-
# maturin generate-ci github
4+
# maturin generate-ci github --pytest -m python/Cargo.toml -o .github/workflows/pytest.yml
5+
#
6+
# NOTE: this file has been modified to add mypy and force installing the packages from the dist directory
57
#
68
name: Maturin
79

@@ -45,14 +47,39 @@ jobs:
4547
uses: PyO3/maturin-action@v1
4648
with:
4749
target: ${{ matrix.platform.target }}
48-
args: --release --out dist --find-interpreter -m python/Cargo.toml
50+
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
4951
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
5052
manylinux: auto
5153
- name: Upload wheels
5254
uses: actions/upload-artifact@v4
5355
with:
5456
name: wheels-linux-${{ matrix.platform.target }}
5557
path: dist
58+
- name: pytest
59+
if: ${{ startsWith(matrix.platform.target, 'x86_64') }}
60+
shell: bash
61+
run: |
62+
set -e
63+
python3 -m venv .venv
64+
source .venv/bin/activate
65+
pip install pyfaup-rs --no-index --find-links dist --force-reinstall
66+
pip install pytest mypy
67+
cd python && pytest && mypy .
68+
- name: pytest
69+
if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }}
70+
uses: uraimo/run-on-arch-action@v2
71+
with:
72+
arch: ${{ matrix.platform.target }}
73+
distro: ubuntu22.04
74+
githubToken: ${{ github.token }}
75+
install: |
76+
apt-get update
77+
apt-get install -y --no-install-recommends python3 python3-pip
78+
pip3 install -U pip pytest mypy
79+
run: |
80+
set -e
81+
pip3 install pyfaup-rs --no-index --find-links dist --force-reinstall
82+
cd python && pytest && mypy .
5683
5784
musllinux:
5885
runs-on: ${{ matrix.platform.runner }}
@@ -76,14 +103,44 @@ jobs:
76103
uses: PyO3/maturin-action@v1
77104
with:
78105
target: ${{ matrix.platform.target }}
79-
args: --release --out dist --find-interpreter -m python/Cargo.toml
106+
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
80107
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
81108
manylinux: musllinux_1_2
82109
- name: Upload wheels
83110
uses: actions/upload-artifact@v4
84111
with:
85112
name: wheels-musllinux-${{ matrix.platform.target }}
86113
path: dist
114+
- name: pytest
115+
if: ${{ startsWith(matrix.platform.target, 'x86_64') }}
116+
uses: addnab/docker-run-action@v3
117+
with:
118+
image: alpine:latest
119+
options: -v ${{ github.workspace }}:/io -w /io
120+
run: |
121+
set -e
122+
apk add py3-pip py3-virtualenv
123+
python3 -m virtualenv .venv
124+
source .venv/bin/activate
125+
pip install pyfaup-rs --no-index --find-links dist --force-reinstall
126+
pip install pytest mypy
127+
cd python && pytest && mypy .
128+
- name: pytest
129+
if: ${{ !startsWith(matrix.platform.target, 'x86') }}
130+
uses: uraimo/run-on-arch-action@v2
131+
with:
132+
arch: ${{ matrix.platform.target }}
133+
distro: alpine_latest
134+
githubToken: ${{ github.token }}
135+
install: |
136+
apk add py3-virtualenv
137+
run: |
138+
set -e
139+
python3 -m virtualenv .venv
140+
source .venv/bin/activate
141+
pip install pytest mypy
142+
pip install pyfaup-rs --no-index --find-links dist --force-reinstall
143+
cd python && pytest && mypy .
87144
88145
windows:
89146
runs-on: ${{ matrix.platform.runner }}
@@ -104,13 +161,24 @@ jobs:
104161
uses: PyO3/maturin-action@v1
105162
with:
106163
target: ${{ matrix.platform.target }}
107-
args: --release --out dist --find-interpreter -m python/Cargo.toml
164+
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
108165
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
166+
maturin-version: 1.9.4
109167
- name: Upload wheels
110168
uses: actions/upload-artifact@v4
111169
with:
112170
name: wheels-windows-${{ matrix.platform.target }}
113171
path: dist
172+
- name: pytest
173+
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
174+
shell: bash
175+
run: |
176+
set -e
177+
python3 -m venv .venv
178+
source .venv/Scripts/activate
179+
pip install pyfaup-rs --no-index --find-links dist --force-reinstall
180+
pip install pytest mypy
181+
cd python && pytest && mypy .
114182
115183
macos:
116184
runs-on: ${{ matrix.platform.runner }}
@@ -130,13 +198,21 @@ jobs:
130198
uses: PyO3/maturin-action@v1
131199
with:
132200
target: ${{ matrix.platform.target }}
133-
args: --release --out dist --find-interpreter -m python/Cargo.toml
201+
args: --release --out dist --find-interpreter --manifest-path python/Cargo.toml
134202
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
135203
- name: Upload wheels
136204
uses: actions/upload-artifact@v4
137205
with:
138206
name: wheels-macos-${{ matrix.platform.target }}
139207
path: dist
208+
- name: pytest
209+
run: |
210+
set -e
211+
python3 -m venv .venv
212+
source .venv/bin/activate
213+
pip install pyfaup-rs --no-index --find-links dist --force-reinstall
214+
pip install pytest mypy
215+
cd python && pytest && mypy .
140216
141217
sdist:
142218
runs-on: ubuntu-latest
@@ -146,7 +222,7 @@ jobs:
146222
uses: PyO3/maturin-action@v1
147223
with:
148224
command: sdist
149-
args: --out dist -m python/Cargo.toml
225+
args: --out dist --manifest-path python/Cargo.toml
150226
- name: Upload sdist
151227
uses: actions/upload-artifact@v4
152228
with:

python/py.typed

Whitespace-only changes.

python/pyfaup.pyi

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
class FaupCompat:
2+
3+
url: bytes
4+
5+
def __init__(self, url: str | None=None) -> None:
6+
...
7+
8+
def decode(self, url: str) -> None:
9+
...
10+
11+
def get_credential(self) -> str | None:
12+
...
13+
14+
def get_domain(self) -> str | None:
15+
...
16+
17+
def get_subdomain(self) -> str | None:
18+
...
19+
20+
def get_fragment(self) -> str | None:
21+
...
22+
23+
def get_host(self) -> str | None:
24+
...
25+
26+
def get_resource_path(self) -> str | None:
27+
...
28+
29+
def get_tld(self) -> str | None:
30+
...
31+
32+
def get_query_string(self) -> str | None:
33+
...
34+
35+
def get_scheme(self) -> str | None:
36+
...
37+
38+
def get_domain_without_tld(self) -> str | None:
39+
...
40+
41+
def get_port(self) -> int | None:
42+
...
43+
44+
45+
class Url:
46+
47+
orig: str
48+
scheme: str
49+
username: str | None
50+
password: str | None
51+
host: str
52+
subdomain: str | None
53+
domain: str | None
54+
suffix: str | None
55+
port: int | None
56+
path: str | None
57+
query: str | None
58+
fragment: str | None
59+
60+
def __init__(self, url: str | None = None) -> None:
61+
...

python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["maturin>=1.9,<2.0"]
2+
requires = ["maturin (>=1.9.4,!=1.9.5,<2.0)"]
33
build-backend = "maturin"
44

55
[project]

python/src/lib.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ impl From<Error> for PyErr {
4242
/// >>> print(url.port) # 8080
4343
#[pyclass]
4444
pub struct Url {
45+
#[pyo3(get)]
46+
pub orig: String,
4547
#[pyo3(get)]
4648
pub scheme: String,
4749
#[pyo3(get)]
@@ -91,6 +93,7 @@ impl From<faup_rs::Url<'_>> for Url {
9193
};
9294

9395
Self {
96+
orig: value.as_str().into(),
9497
scheme: value.scheme().into(),
9598
username,
9699
password,
@@ -140,6 +143,7 @@ impl Url {
140143
.map(|u| u.into())
141144
.map_err(|e| PyValueError::new_err(e.to_string()))
142145
}
146+
143147
}
144148

145149
/// A compatibility class that mimics the FAUP (Fast URL Parser) Python API.
@@ -233,6 +237,59 @@ impl FaupCompat {
233237

234238
Ok(m)
235239
}
240+
241+
fn get_credential(&self) -> Option<String> {
242+
let url = self.url.as_ref();
243+
url.and_then(|u| u.credentials())
244+
}
245+
246+
fn get_domain(&self) -> Option<&str> {
247+
self.url.as_ref()?.domain.as_deref()
248+
}
249+
250+
fn get_subdomain(&self) -> Option<&str> {
251+
self.url.as_ref()?.subdomain.as_deref()
252+
}
253+
254+
fn get_fragment(&self) -> Option<&str> {
255+
self.url.as_ref()?.fragment.as_deref()
256+
}
257+
258+
fn get_host(&self) -> Option<&str> {
259+
self.url.as_ref().map(|u| u.host.as_str())
260+
}
261+
262+
fn get_resource_path(&self) -> Option<&str> {
263+
self.url.as_ref()?.path.as_deref()
264+
}
265+
266+
fn get_tld(&self) -> Option<&str> {
267+
self.url.as_ref()?.suffix.as_deref()
268+
}
269+
270+
fn get_query_string(&self) -> Option<&str> {
271+
self.url.as_ref()?.query.as_deref()
272+
}
273+
274+
fn get_scheme(&self) -> Option<&str> {
275+
self.url.as_ref().map(|u| u.scheme.as_str())
276+
}
277+
278+
fn get_port(&self) -> Option<u16> {
279+
self.url.as_ref()?.port
280+
}
281+
282+
fn get_domain_without_tld(&self) -> Option<&str> {
283+
if let (Some(domain), Some(tld)) = (self.get_domain(), self.get_tld()) {
284+
domain
285+
.strip_suffix(tld)
286+
.and_then(|dom| dom.strip_suffix('.'))
287+
}
288+
else {
289+
None
290+
}
291+
}
292+
236293
}
237294

238295
/// A Python module implemented in Rust for URL parsing.

python/tests/test_pyfaup.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import unittest
6+
7+
from pyfaup import Url
8+
9+
10+
class TestPyFaupRR(unittest.TestCase):
11+
12+
def test_url(self) -> None:
13+
parsed_url = Url('https://user:pass@sub.example.com:8080/path?query=value#fragment')
14+
15+
self.assertEqual(parsed_url.orig, 'https://user:pass@sub.example.com:8080/path?query=value#fragment')
16+
17+
self.assertEqual(parsed_url.scheme, 'https')
18+
self.assertEqual(parsed_url.username, 'user')
19+
self.assertEqual(parsed_url.password, 'pass')
20+
self.assertEqual(parsed_url.host, 'sub.example.com')
21+
self.assertEqual(parsed_url.subdomain, 'sub')
22+
self.assertEqual(parsed_url.domain, 'example.com')
23+
self.assertEqual(parsed_url.suffix, 'com')
24+
self.assertEqual(parsed_url.port, 8080)
25+
self.assertEqual(parsed_url.path, '/path')
26+
self.assertEqual(parsed_url.query, 'query=value')
27+
self.assertEqual(parsed_url.fragment, 'fragment')

0 commit comments

Comments
 (0)