url-normalize 2.0.1__tar.gz → 2.2.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.
- {url_normalize-2.0.1 → url_normalize-2.2.0}/PKG-INFO +49 -4
- {url_normalize-2.0.1 → url_normalize-2.2.0}/README.md +48 -3
- {url_normalize-2.0.1 → url_normalize-2.2.0}/pyproject.toml +4 -1
- url_normalize-2.2.0/tests/test_cli.py +294 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_fragment.py +2 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_path.py +3 -1
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_query.py +2 -0
- url_normalize-2.2.0/tests/test_provide_url_domain.py +31 -0
- url_normalize-2.2.0/tests/test_url_normalize.py +163 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/__init__.py +1 -1
- url_normalize-2.2.0/url_normalize/cli.py +73 -0
- url_normalize-2.2.0/url_normalize/normalize_fragment.py +27 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_path.py +4 -4
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_query.py +2 -2
- url_normalize-2.2.0/url_normalize/provide_url_domain.py +28 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/provide_url_scheme.py +11 -4
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/url_normalize.py +7 -2
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize.egg-info/PKG-INFO +49 -4
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize.egg-info/SOURCES.txt +5 -0
- url_normalize-2.2.0/url_normalize.egg-info/entry_points.txt +2 -0
- url_normalize-2.0.1/tests/test_url_normalize.py +0 -125
- url_normalize-2.0.1/url_normalize/normalize_fragment.py +0 -18
- {url_normalize-2.0.1 → url_normalize-2.2.0}/LICENSE +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/setup.cfg +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_deconstruct_url.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_generic_url_cleanup.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_host.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_port.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_query_filters.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_scheme.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_normalize_userinfo.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_provide_url_scheme.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_reconstruct_url.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/tests/test_tools.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/generic_url_cleanup.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_host.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_port.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_scheme.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/normalize_userinfo.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/param_allowlist.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize/tools.py +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize.egg-info/dependency_links.txt +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize.egg-info/requires.txt +0 -0
- {url_normalize-2.0.1 → url_normalize-2.2.0}/url_normalize.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: url-normalize
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: URL normalization for Python
|
|
5
5
|
Author-email: Nikolay Panov <github@npanov.com>
|
|
6
6
|
License: MIT
|
|
@@ -26,6 +26,9 @@ Dynamic: license-file
|
|
|
26
26
|
|
|
27
27
|
# url-normalize
|
|
28
28
|
|
|
29
|
+
[](https://github.com/niksite/url-normalize/actions/workflows/ci.yml)
|
|
30
|
+
[](https://github.com/niksite/url-normalize/actions/workflows/publish.yml)
|
|
31
|
+
|
|
29
32
|
URI Normalization function:
|
|
30
33
|
|
|
31
34
|
* Take care of IDN domains.
|
|
@@ -47,6 +50,7 @@ Inspired by Sam Ruby's [urlnorm.py](<http://intertwingly.net/blog/2004/08/04/Url
|
|
|
47
50
|
|
|
48
51
|
* IDN (Internationalized Domain Name) support
|
|
49
52
|
* Configurable default scheme (https by default)
|
|
53
|
+
* Configurable default domain for absolute paths
|
|
50
54
|
* Query parameter filtering with allowlists
|
|
51
55
|
* Support for various URL formats including:
|
|
52
56
|
* Empty string URLs
|
|
@@ -64,8 +68,6 @@ pip install url-normalize
|
|
|
64
68
|
|
|
65
69
|
## Usage
|
|
66
70
|
|
|
67
|
-
Basic usage:
|
|
68
|
-
|
|
69
71
|
```python
|
|
70
72
|
from url_normalize import url_normalize
|
|
71
73
|
|
|
@@ -81,19 +83,62 @@ print(url_normalize("www.foo.com/foo", default_scheme="http"))
|
|
|
81
83
|
print(url_normalize("www.google.com/search?q=test&utm_source=test", filter_params=True))
|
|
82
84
|
# Output: https://www.google.com/search?q=test
|
|
83
85
|
|
|
84
|
-
# With custom parameter allowlist
|
|
86
|
+
# With custom parameter allowlist as a dict
|
|
85
87
|
print(url_normalize(
|
|
86
88
|
"example.com?page=1&id=123&ref=test",
|
|
87
89
|
filter_params=True,
|
|
88
90
|
param_allowlist={"example.com": ["page", "id"]}
|
|
89
91
|
))
|
|
90
92
|
# Output: https://example.com?page=1&id=123
|
|
93
|
+
|
|
94
|
+
# With custom parameter allowlist as a list
|
|
91
95
|
print(url_normalize(
|
|
92
96
|
"example.com?page=1&id=123&ref=test",
|
|
93
97
|
filter_params=True,
|
|
94
98
|
param_allowlist=["page", "id"]
|
|
95
99
|
))
|
|
96
100
|
# Output: https://example.com?page=1&id=123
|
|
101
|
+
|
|
102
|
+
# With default domain for absolute paths
|
|
103
|
+
print(url_normalize("/images/logo.png", default_domain="example.com"))
|
|
104
|
+
# Output: https://example.com/images/logo.png
|
|
105
|
+
|
|
106
|
+
# With default domain and custom scheme
|
|
107
|
+
print(url_normalize("/images/logo.png", default_scheme="http", default_domain="example.com"))
|
|
108
|
+
# Output: http://example.com/images/logo.png
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Command-line usage
|
|
112
|
+
|
|
113
|
+
You can also use `url-normalize` from the command line:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
$ url-normalize "www.foo.com:80/foo"
|
|
117
|
+
# Output: https://www.foo.com/foo
|
|
118
|
+
|
|
119
|
+
# With custom default scheme
|
|
120
|
+
$ url-normalize -s http "www.foo.com/foo"
|
|
121
|
+
# Output: http://www.foo.com/foo
|
|
122
|
+
|
|
123
|
+
# With query parameter filtering
|
|
124
|
+
$ url-normalize -f "www.google.com/search?q=test&utm_source=test"
|
|
125
|
+
# Output: https://www.google.com/search?q=test
|
|
126
|
+
|
|
127
|
+
# With custom allowlist
|
|
128
|
+
$ url-normalize -f -p page,id "example.com?page=1&id=123&ref=test"
|
|
129
|
+
# Output: https://example.com/?page=1&id=123
|
|
130
|
+
|
|
131
|
+
# With default domain for absolute paths
|
|
132
|
+
$ url-normalize -d example.com "/images/logo.png"
|
|
133
|
+
# Output: https://example.com/images/logo.png
|
|
134
|
+
|
|
135
|
+
# With default domain and custom scheme
|
|
136
|
+
$ url-normalize -d example.com -s http "/images/logo.png"
|
|
137
|
+
# Output: http://example.com/images/logo.png
|
|
138
|
+
|
|
139
|
+
# Via uv tool/uvx
|
|
140
|
+
$ uvx url-normalize www.foo.com:80/foo
|
|
141
|
+
# Output: https://www.foo.com:80/foo
|
|
97
142
|
```
|
|
98
143
|
|
|
99
144
|
## Documentation
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# url-normalize
|
|
2
2
|
|
|
3
|
+
[](https://github.com/niksite/url-normalize/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/niksite/url-normalize/actions/workflows/publish.yml)
|
|
5
|
+
|
|
3
6
|
URI Normalization function:
|
|
4
7
|
|
|
5
8
|
* Take care of IDN domains.
|
|
@@ -21,6 +24,7 @@ Inspired by Sam Ruby's [urlnorm.py](<http://intertwingly.net/blog/2004/08/04/Url
|
|
|
21
24
|
|
|
22
25
|
* IDN (Internationalized Domain Name) support
|
|
23
26
|
* Configurable default scheme (https by default)
|
|
27
|
+
* Configurable default domain for absolute paths
|
|
24
28
|
* Query parameter filtering with allowlists
|
|
25
29
|
* Support for various URL formats including:
|
|
26
30
|
* Empty string URLs
|
|
@@ -38,8 +42,6 @@ pip install url-normalize
|
|
|
38
42
|
|
|
39
43
|
## Usage
|
|
40
44
|
|
|
41
|
-
Basic usage:
|
|
42
|
-
|
|
43
45
|
```python
|
|
44
46
|
from url_normalize import url_normalize
|
|
45
47
|
|
|
@@ -55,19 +57,62 @@ print(url_normalize("www.foo.com/foo", default_scheme="http"))
|
|
|
55
57
|
print(url_normalize("www.google.com/search?q=test&utm_source=test", filter_params=True))
|
|
56
58
|
# Output: https://www.google.com/search?q=test
|
|
57
59
|
|
|
58
|
-
# With custom parameter allowlist
|
|
60
|
+
# With custom parameter allowlist as a dict
|
|
59
61
|
print(url_normalize(
|
|
60
62
|
"example.com?page=1&id=123&ref=test",
|
|
61
63
|
filter_params=True,
|
|
62
64
|
param_allowlist={"example.com": ["page", "id"]}
|
|
63
65
|
))
|
|
64
66
|
# Output: https://example.com?page=1&id=123
|
|
67
|
+
|
|
68
|
+
# With custom parameter allowlist as a list
|
|
65
69
|
print(url_normalize(
|
|
66
70
|
"example.com?page=1&id=123&ref=test",
|
|
67
71
|
filter_params=True,
|
|
68
72
|
param_allowlist=["page", "id"]
|
|
69
73
|
))
|
|
70
74
|
# Output: https://example.com?page=1&id=123
|
|
75
|
+
|
|
76
|
+
# With default domain for absolute paths
|
|
77
|
+
print(url_normalize("/images/logo.png", default_domain="example.com"))
|
|
78
|
+
# Output: https://example.com/images/logo.png
|
|
79
|
+
|
|
80
|
+
# With default domain and custom scheme
|
|
81
|
+
print(url_normalize("/images/logo.png", default_scheme="http", default_domain="example.com"))
|
|
82
|
+
# Output: http://example.com/images/logo.png
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Command-line usage
|
|
86
|
+
|
|
87
|
+
You can also use `url-normalize` from the command line:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
$ url-normalize "www.foo.com:80/foo"
|
|
91
|
+
# Output: https://www.foo.com/foo
|
|
92
|
+
|
|
93
|
+
# With custom default scheme
|
|
94
|
+
$ url-normalize -s http "www.foo.com/foo"
|
|
95
|
+
# Output: http://www.foo.com/foo
|
|
96
|
+
|
|
97
|
+
# With query parameter filtering
|
|
98
|
+
$ url-normalize -f "www.google.com/search?q=test&utm_source=test"
|
|
99
|
+
# Output: https://www.google.com/search?q=test
|
|
100
|
+
|
|
101
|
+
# With custom allowlist
|
|
102
|
+
$ url-normalize -f -p page,id "example.com?page=1&id=123&ref=test"
|
|
103
|
+
# Output: https://example.com/?page=1&id=123
|
|
104
|
+
|
|
105
|
+
# With default domain for absolute paths
|
|
106
|
+
$ url-normalize -d example.com "/images/logo.png"
|
|
107
|
+
# Output: https://example.com/images/logo.png
|
|
108
|
+
|
|
109
|
+
# With default domain and custom scheme
|
|
110
|
+
$ url-normalize -d example.com -s http "/images/logo.png"
|
|
111
|
+
# Output: http://example.com/images/logo.png
|
|
112
|
+
|
|
113
|
+
# Via uv tool/uvx
|
|
114
|
+
$ uvx url-normalize www.foo.com:80/foo
|
|
115
|
+
# Output: https://www.foo.com:80/foo
|
|
71
116
|
```
|
|
72
117
|
|
|
73
118
|
## Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "url-normalize"
|
|
3
|
-
version = "2.0
|
|
3
|
+
version = "2.2.0"
|
|
4
4
|
description = "URL normalization for Python"
|
|
5
5
|
authors = [{ name = "Nikolay Panov", email = "github@npanov.com" }]
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -15,6 +15,9 @@ Repository = "https://github.com/niksite/url-normalize"
|
|
|
15
15
|
Issues = "https://github.com/niksite/url-normalize/issues"
|
|
16
16
|
Changelog = "https://github.com/niksite/url-normalize/blob/master/CHANGELOG.md"
|
|
17
17
|
|
|
18
|
+
[project.scripts]
|
|
19
|
+
url-normalize = "url_normalize.cli:main"
|
|
20
|
+
|
|
18
21
|
[project.optional-dependencies]
|
|
19
22
|
dev = [
|
|
20
23
|
"mypy",
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Tests for the command line interface."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from url_normalize import __version__
|
|
10
|
+
from url_normalize.cli import main
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_cli(*args: str) -> subprocess.CompletedProcess:
|
|
14
|
+
"""Run the CLI command with given arguments.
|
|
15
|
+
|
|
16
|
+
Params:
|
|
17
|
+
*args: Command line arguments to pass to the CLI.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A completed process with stdout, stderr, and return code.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
command = [sys.executable, "-m", "url_normalize.cli", *list(args)]
|
|
24
|
+
return subprocess.run( # noqa: S603
|
|
25
|
+
command, capture_output=True, text=True, check=False
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_cli_error_handling(capsys, monkeypatch):
|
|
30
|
+
"""Test CLI error handling when URL normalization fails."""
|
|
31
|
+
with patch("url_normalize.cli.url_normalize") as mock_normalize:
|
|
32
|
+
mock_normalize.side_effect = Exception("Simulated error")
|
|
33
|
+
monkeypatch.setattr("sys.argv", ["url-normalize", "http://example.com"])
|
|
34
|
+
|
|
35
|
+
with pytest.raises(SystemExit) as excinfo:
|
|
36
|
+
main()
|
|
37
|
+
|
|
38
|
+
assert excinfo.value.code == 1
|
|
39
|
+
captured = capsys.readouterr()
|
|
40
|
+
assert "Error normalizing URL: Simulated error" in captured.err
|
|
41
|
+
assert not captured.out
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_cli_basic_normalization() -> None:
|
|
45
|
+
"""Test basic URL normalization via CLI."""
|
|
46
|
+
url = "http://EXAMPLE.com/./path/../other/"
|
|
47
|
+
expected = "http://example.com/other/"
|
|
48
|
+
|
|
49
|
+
result = run_cli(url)
|
|
50
|
+
|
|
51
|
+
assert result.returncode == 0
|
|
52
|
+
assert result.stdout.strip() == expected
|
|
53
|
+
assert not result.stderr
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_cli_basic_normalization_short_args() -> None:
|
|
57
|
+
"""Test basic URL normalization via CLI using short arguments."""
|
|
58
|
+
url = "http://EXAMPLE.com/./path/../other/"
|
|
59
|
+
expected = "http://example.com/other/"
|
|
60
|
+
# Using short args where applicable (none for the URL itself)
|
|
61
|
+
|
|
62
|
+
result = run_cli(url) # No short args needed for basic case
|
|
63
|
+
|
|
64
|
+
assert result.returncode == 0
|
|
65
|
+
assert result.stdout.strip() == expected
|
|
66
|
+
assert not result.stderr
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_cli_default_scheme() -> None:
|
|
70
|
+
"""Test default scheme addition via CLI."""
|
|
71
|
+
url = "//example.com"
|
|
72
|
+
expected = "https://example.com/"
|
|
73
|
+
|
|
74
|
+
result = run_cli(url)
|
|
75
|
+
|
|
76
|
+
assert result.returncode == 0
|
|
77
|
+
assert result.stdout.strip() == expected
|
|
78
|
+
assert not result.stderr
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_cli_default_scheme_short_arg() -> None:
|
|
82
|
+
"""Test default scheme addition via CLI using short argument."""
|
|
83
|
+
url = "//example.com"
|
|
84
|
+
expected = "https://example.com/"
|
|
85
|
+
|
|
86
|
+
result = run_cli(url) # Default scheme is implicit, no arg needed
|
|
87
|
+
|
|
88
|
+
assert result.returncode == 0
|
|
89
|
+
assert result.stdout.strip() == expected
|
|
90
|
+
assert not result.stderr
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_cli_custom_default_scheme() -> None:
|
|
94
|
+
"""Test custom default scheme via CLI."""
|
|
95
|
+
url = "//example.com"
|
|
96
|
+
expected = "ftp://example.com/"
|
|
97
|
+
|
|
98
|
+
result = run_cli("--default-scheme", "ftp", url)
|
|
99
|
+
|
|
100
|
+
assert result.returncode == 0
|
|
101
|
+
assert result.stdout.strip() == expected
|
|
102
|
+
assert not result.stderr
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_cli_custom_default_scheme_short_arg() -> None:
|
|
106
|
+
"""Test custom default scheme via CLI using short argument."""
|
|
107
|
+
url = "//example.com"
|
|
108
|
+
expected = "ftp://example.com/"
|
|
109
|
+
|
|
110
|
+
result = run_cli("-s", "ftp", url)
|
|
111
|
+
|
|
112
|
+
assert result.returncode == 0
|
|
113
|
+
assert result.stdout.strip() == expected
|
|
114
|
+
assert not result.stderr
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_cli_filter_params() -> None:
|
|
118
|
+
"""Test parameter filtering via CLI."""
|
|
119
|
+
url = "http://google.com?utm_source=test&q=1"
|
|
120
|
+
expected = "http://google.com/?q=1"
|
|
121
|
+
|
|
122
|
+
result = run_cli("--filter-params", url)
|
|
123
|
+
|
|
124
|
+
assert result.returncode == 0
|
|
125
|
+
assert result.stdout.strip() == expected
|
|
126
|
+
assert not result.stderr
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_cli_filter_params_short_arg() -> None:
|
|
130
|
+
"""Test parameter filtering via CLI using short argument."""
|
|
131
|
+
url = "http://google.com?utm_source=test&q=1"
|
|
132
|
+
expected = "http://google.com/?q=1"
|
|
133
|
+
|
|
134
|
+
result = run_cli("-f", url)
|
|
135
|
+
|
|
136
|
+
assert result.returncode == 0
|
|
137
|
+
assert result.stdout.strip() == expected
|
|
138
|
+
assert not result.stderr
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_cli_param_allowlist() -> None:
|
|
142
|
+
"""Test parameter allowlist via CLI."""
|
|
143
|
+
url = "http://example.com?remove=me&keep=this&remove_too=true"
|
|
144
|
+
expected = "http://example.com/?keep=this"
|
|
145
|
+
# Use filter_params to enable filtering, then allowlist to keep specific ones
|
|
146
|
+
|
|
147
|
+
result = run_cli("-f", "-p", "keep", url)
|
|
148
|
+
|
|
149
|
+
assert result.returncode == 0
|
|
150
|
+
assert result.stdout.strip() == expected
|
|
151
|
+
assert not result.stderr
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_cli_param_allowlist_multiple() -> None:
|
|
155
|
+
"""Test parameter allowlist with multiple params via CLI."""
|
|
156
|
+
url = "http://example.com?remove=me&keep=this&keep_too=yes&remove_too=true"
|
|
157
|
+
expected = "http://example.com/?keep=this&keep_too=yes"
|
|
158
|
+
|
|
159
|
+
result = run_cli("-f", "-p", "keep,keep_too", url)
|
|
160
|
+
|
|
161
|
+
assert result.returncode == 0
|
|
162
|
+
assert result.stdout.strip() == expected
|
|
163
|
+
assert not result.stderr
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_cli_param_allowlist_without_filtering() -> None:
|
|
167
|
+
"""Test allowlist has no effect if filtering is not enabled."""
|
|
168
|
+
url = "http://example.com?remove=me&keep=this&remove_too=true"
|
|
169
|
+
expected = "http://example.com/?remove=me&keep=this&remove_too=true"
|
|
170
|
+
# Not using -f, so allowlist should be ignored
|
|
171
|
+
|
|
172
|
+
result = run_cli("-p", "keep", url)
|
|
173
|
+
|
|
174
|
+
assert result.returncode == 0
|
|
175
|
+
assert result.stdout.strip() == expected
|
|
176
|
+
assert not result.stderr
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_cli_no_url() -> None:
|
|
180
|
+
"""Test CLI error when no URL is provided."""
|
|
181
|
+
result = run_cli()
|
|
182
|
+
|
|
183
|
+
assert result.returncode != 0
|
|
184
|
+
assert "the following arguments are required: url" in result.stderr
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_cli_version_long() -> None:
|
|
188
|
+
"""Test version output with --version flag."""
|
|
189
|
+
result = run_cli("--version")
|
|
190
|
+
|
|
191
|
+
assert result.returncode == 0
|
|
192
|
+
assert __version__ in result.stdout
|
|
193
|
+
assert not result.stderr
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_cli_version_short() -> None:
|
|
197
|
+
"""Test version output with -v flag."""
|
|
198
|
+
result = run_cli("-v")
|
|
199
|
+
|
|
200
|
+
assert result.returncode == 0
|
|
201
|
+
assert __version__ in result.stdout
|
|
202
|
+
assert not result.stderr
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@pytest.mark.skipif(
|
|
206
|
+
sys.platform == "win32", reason="Charset handling differs on Windows CLI"
|
|
207
|
+
)
|
|
208
|
+
def test_cli_charset() -> None:
|
|
209
|
+
"""Test charset handling via CLI (might be platform-dependent)."""
|
|
210
|
+
# Example using Cyrillic characters which need correct encoding
|
|
211
|
+
url = "http://пример.рф/path"
|
|
212
|
+
expected_idn = "http://xn--e1afmkfd.xn--p1ai/path"
|
|
213
|
+
|
|
214
|
+
# Test with default UTF-8
|
|
215
|
+
result_utf8 = run_cli(url)
|
|
216
|
+
|
|
217
|
+
assert result_utf8.returncode == 0
|
|
218
|
+
assert result_utf8.stdout.strip() == expected_idn
|
|
219
|
+
assert not result_utf8.stderr
|
|
220
|
+
|
|
221
|
+
# Test specifying UTF-8 explicitly
|
|
222
|
+
result_charset = run_cli("--charset", "utf-8", url)
|
|
223
|
+
|
|
224
|
+
assert result_charset.returncode == 0
|
|
225
|
+
assert result_charset.stdout.strip() == expected_idn
|
|
226
|
+
assert not result_charset.stderr
|
|
227
|
+
|
|
228
|
+
# Test specifying UTF-8 explicitly using short arg
|
|
229
|
+
result_charset_short = run_cli("-c", "utf-8", url)
|
|
230
|
+
|
|
231
|
+
assert result_charset_short.returncode == 0
|
|
232
|
+
assert result_charset_short.stdout.strip() == expected_idn
|
|
233
|
+
assert not result_charset_short.stderr
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_cli_default_domain() -> None:
|
|
237
|
+
"""Test adding default domain to absolute path via CLI."""
|
|
238
|
+
url = "/path/to/image.png"
|
|
239
|
+
expected = "https://example.com/path/to/image.png"
|
|
240
|
+
|
|
241
|
+
result = run_cli("--default-domain", "example.com", url)
|
|
242
|
+
|
|
243
|
+
assert result.returncode == 0
|
|
244
|
+
assert result.stdout.strip() == expected
|
|
245
|
+
assert not result.stderr
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_cli_default_domain_short_arg() -> None:
|
|
249
|
+
"""Test adding default domain using short argument."""
|
|
250
|
+
url = "/path/to/image.png"
|
|
251
|
+
expected = "https://example.com/path/to/image.png"
|
|
252
|
+
|
|
253
|
+
result = run_cli("-d", "example.com", url)
|
|
254
|
+
|
|
255
|
+
assert result.returncode == 0
|
|
256
|
+
assert result.stdout.strip() == expected
|
|
257
|
+
assert not result.stderr
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_cli_default_domain_with_scheme() -> None:
|
|
261
|
+
"""Test adding default domain with custom scheme."""
|
|
262
|
+
url = "/path/to/image.png"
|
|
263
|
+
expected = "http://example.com/path/to/image.png"
|
|
264
|
+
|
|
265
|
+
result = run_cli("-d", "example.com", "-s", "http", url)
|
|
266
|
+
|
|
267
|
+
assert result.returncode == 0
|
|
268
|
+
assert result.stdout.strip() == expected
|
|
269
|
+
assert not result.stderr
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_cli_default_domain_no_effect_on_absolute_urls() -> None:
|
|
273
|
+
"""Test default domain has no effect on absolute URLs."""
|
|
274
|
+
url = "http://original-domain.com/path"
|
|
275
|
+
expected = "http://original-domain.com/path"
|
|
276
|
+
|
|
277
|
+
result = run_cli("-d", "example.com", url)
|
|
278
|
+
|
|
279
|
+
assert result.returncode == 0
|
|
280
|
+
assert result.stdout.strip() == expected
|
|
281
|
+
assert not result.stderr
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_cli_default_domain_no_effect_on_relative_paths() -> None:
|
|
285
|
+
"""Test default domain has no effect on relative paths."""
|
|
286
|
+
url = "path/to/file.html"
|
|
287
|
+
# This becomes a regular URL with the default scheme
|
|
288
|
+
expected = "https://path/to/file.html"
|
|
289
|
+
|
|
290
|
+
result = run_cli("-d", "example.com", url)
|
|
291
|
+
|
|
292
|
+
assert result.returncode == 0
|
|
293
|
+
assert result.stdout.strip() == expected
|
|
294
|
+
assert not result.stderr
|
|
@@ -29,10 +29,12 @@ EXPECTED_DATA = {
|
|
|
29
29
|
"/foo/bar/../baz": "/foo/baz",
|
|
30
30
|
"/foo/bar/.": "/foo/bar/",
|
|
31
31
|
"/foo/bar/./": "/foo/bar/",
|
|
32
|
+
# Issue #25: we should preserve ? in the path
|
|
33
|
+
"/More+Tea+Vicar%3F/discussion": "/More+Tea+Vicar%3F/discussion",
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
def
|
|
37
|
+
def test_normalize_path_result_is_expected():
|
|
36
38
|
"""Assert we got expected results from the normalize_path function."""
|
|
37
39
|
for url, expected in EXPECTED_DATA.items():
|
|
38
40
|
result = normalize_path(url, "http")
|
|
@@ -14,6 +14,8 @@ from url_normalize.url_normalize import normalize_query
|
|
|
14
14
|
("Ç=Ç", "%C3%87=%C3%87"),
|
|
15
15
|
("%C3%87=%C3%87", "%C3%87=%C3%87"),
|
|
16
16
|
("q=C%CC%A7", "q=%C3%87"),
|
|
17
|
+
("q=%23test", "q=%23test"), # Preserve encoded # in value, #31
|
|
18
|
+
("where=code%3D123", "where=code%3D123"), # Preserve encoded = in value, #25
|
|
17
19
|
],
|
|
18
20
|
)
|
|
19
21
|
def test_normalize_query_result_is_expected(query, expected):
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Tests for provide_url_domain function."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from url_normalize.provide_url_domain import provide_url_domain
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
("url", "expected"),
|
|
10
|
+
[
|
|
11
|
+
("", ""),
|
|
12
|
+
("-", "-"),
|
|
13
|
+
("http://example.com/", "http://example.com/"),
|
|
14
|
+
("/file/path", "//example.com/file/path"),
|
|
15
|
+
("site/page", "site/page"), # No change for relative paths
|
|
16
|
+
],
|
|
17
|
+
)
|
|
18
|
+
def test_provide_url_domain_result_is_expected(url: str, expected: str) -> None:
|
|
19
|
+
"""Assert we get expected results from provide_url_domain function."""
|
|
20
|
+
result = provide_url_domain(url, default_domain="example.com")
|
|
21
|
+
assert result == expected
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_provide_url_domain_accept_different_domains():
|
|
25
|
+
"""Assert we could provide different default_domain values."""
|
|
26
|
+
url = "/file/path"
|
|
27
|
+
expected = "//custom-domain.org/file/path"
|
|
28
|
+
|
|
29
|
+
actual = provide_url_domain(url, default_domain="custom-domain.org")
|
|
30
|
+
|
|
31
|
+
assert actual == expected
|