tldextract 5.1.1__tar.gz → 5.1.3__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.
- {tldextract-5.1.1 → tldextract-5.1.3}/.github/workflows/ci.yml +16 -9
- {tldextract-5.1.1 → tldextract-5.1.3}/CHANGELOG.md +24 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/LICENSE +1 -1
- {tldextract-5.1.1 → tldextract-5.1.3}/PKG-INFO +31 -18
- {tldextract-5.1.1 → tldextract-5.1.3}/README.md +23 -14
- {tldextract-5.1.1 → tldextract-5.1.3}/pyproject.toml +12 -6
- tldextract-5.1.3/scripts/release.py +236 -0
- tldextract-5.1.3/tests/__snapshots__/test_release.ambr +247 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/main_test.py +16 -14
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/test_cache.py +4 -3
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/test_parallel.py +1 -13
- tldextract-5.1.3/tests/test_release.py +96 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/test_trie.py +1 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/.tld_set_snapshot +4390 -2405
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/__main__.py +0 -1
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/_version.py +2 -2
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/cache.py +8 -20
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/remote.py +6 -28
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/suffix_list.py +3 -1
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/tldextract.py +16 -22
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/PKG-INFO +31 -18
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/SOURCES.txt +3 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/requires.txt +6 -1
- tldextract-5.1.3/tox.ini +22 -0
- tldextract-5.1.1/tox.ini +0 -22
- {tldextract-5.1.1 → tldextract-5.1.3}/.github/FUNDING.yml +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/.gitignore +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/setup.cfg +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/__init__.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/cli_test.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/conftest.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/custom_suffix_test.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/fixtures/fake_suffix_list_fixture.dat +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tests/integration_test.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/__init__.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/cli.py +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract/py.typed +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/dependency_links.txt +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/entry_points.txt +0 -0
- {tldextract-5.1.1 → tldextract-5.1.3}/tldextract.egg-info/top_level.txt +0 -0
@@ -1,5 +1,11 @@
|
|
1
1
|
name: build
|
2
|
-
on:
|
2
|
+
on:
|
3
|
+
pull_request: {}
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- "master"
|
7
|
+
tags-ignore:
|
8
|
+
- "**"
|
3
9
|
jobs:
|
4
10
|
test:
|
5
11
|
strategy:
|
@@ -8,32 +14,33 @@ jobs:
|
|
8
14
|
os: [macos-latest, windows-latest, ubuntu-latest]
|
9
15
|
language:
|
10
16
|
[
|
11
|
-
{python-version: "3.8", toxenv: "py38"},
|
12
17
|
{python-version: "3.9", toxenv: "py39"},
|
13
18
|
{python-version: "3.10", toxenv: "py310"},
|
14
19
|
{python-version: "3.11", toxenv: "py311"},
|
15
20
|
{python-version: "3.12", toxenv: "py312"},
|
16
|
-
{python-version: "
|
21
|
+
{python-version: "3.13", toxenv: "py313"},
|
22
|
+
{python-version: "pypy3.9", toxenv: "pypy39"},
|
23
|
+
{python-version: "pypy3.10", toxenv: "pypy310"},
|
17
24
|
]
|
18
25
|
include:
|
19
26
|
- os: ubuntu-latest
|
20
|
-
language: {python-version: "3.
|
27
|
+
language: {python-version: "3.9", toxenv: "codestyle"}
|
21
28
|
- os: ubuntu-latest
|
22
|
-
language: {python-version: "3.
|
29
|
+
language: {python-version: "3.9", toxenv: "lint"}
|
23
30
|
- os: ubuntu-latest
|
24
|
-
language: {python-version: "3.
|
31
|
+
language: {python-version: "3.9", toxenv: "typecheck"}
|
25
32
|
runs-on: ${{ matrix.os }}
|
26
33
|
steps:
|
27
34
|
- name: Check out repository
|
28
35
|
uses: actions/checkout@v4
|
29
36
|
- name: Setup Python
|
30
|
-
uses: actions/setup-python@
|
37
|
+
uses: actions/setup-python@v5
|
31
38
|
with:
|
32
39
|
python-version: ${{ matrix.language.python-version }}
|
40
|
+
check-latest: true
|
33
41
|
- name: Install Python requirements
|
34
42
|
run: |
|
35
|
-
pip install --upgrade
|
36
|
-
pip install --upgrade --editable '.[testing]'
|
43
|
+
pip install --upgrade tox tox-uv
|
37
44
|
- name: Test
|
38
45
|
run: tox
|
39
46
|
env:
|
@@ -3,6 +3,30 @@
|
|
3
3
|
After upgrading, update your cache file by deleting it or via `tldextract
|
4
4
|
--update`.
|
5
5
|
|
6
|
+
## 5.1.3 (2024-11-04)
|
7
|
+
|
8
|
+
* Bugfixes
|
9
|
+
* Reduce logging errors ([`921a825`](https://github.com/john-kurkowski/tldextract/commit/921a82523c0e4403d21d50b2c3410d9af43520ac))
|
10
|
+
* Drop support for EOL Python 3.8 ([#340](https://github.com/john-kurkowski/tldextract/issues/340))
|
11
|
+
* Support Python 3.13 ([#341](https://github.com/john-kurkowski/tldextract/issues/341))
|
12
|
+
* Update bundled snapshot
|
13
|
+
* Documentation
|
14
|
+
* Clarify how to use your own definitions
|
15
|
+
* Clarify first-successful definitions vs. merged definitions
|
16
|
+
* Misc.
|
17
|
+
* Switch from Black to Ruff ([#333](https://github.com/john-kurkowski/tldextract/issues/333))
|
18
|
+
* Switch from pip to uv, during tox ([#324](https://github.com/john-kurkowski/tldextract/issues/324))
|
19
|
+
|
20
|
+
## 5.1.2 (2024-03-18)
|
21
|
+
|
22
|
+
* Bugfixes
|
23
|
+
* Remove `socket.inet_pton`, to fix platform-dependent IP parsing ([#318](https://github.com/john-kurkowski/tldextract/issues/318))
|
24
|
+
* Use non-capturing groups for IPv4 address detection, for a slight speed boost ([#323](https://github.com/john-kurkowski/tldextract/issues/323))
|
25
|
+
* Misc.
|
26
|
+
* Add CI for PyPy3.9 and PyPy3.10 ([#316](https://github.com/john-kurkowski/tldextract/issues/316))
|
27
|
+
* Add script to automate package release process ([#325](https://github.com/john-kurkowski/tldextract/issues/325))
|
28
|
+
* Update LICENSE copyright years
|
29
|
+
|
6
30
|
## 5.1.1 (2023-11-16)
|
7
31
|
|
8
32
|
* Bugfixes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tldextract
|
3
|
-
Version: 5.1.
|
3
|
+
Version: 5.1.3
|
4
4
|
Summary: Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well.
|
5
5
|
Author-email: John Kurkowski <john.kurkowski@gmail.com>
|
6
6
|
License: BSD-3-Clause
|
@@ -10,27 +10,31 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Topic :: Utilities
|
11
11
|
Classifier: License :: OSI Approved :: BSD License
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
14
13
|
Classifier: Programming Language :: Python :: 3.9
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
18
|
-
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
18
|
+
Requires-Python: >=3.9
|
19
19
|
Description-Content-Type: text/markdown
|
20
20
|
License-File: LICENSE
|
21
21
|
Requires-Dist: idna
|
22
22
|
Requires-Dist: requests>=2.1.0
|
23
23
|
Requires-Dist: requests-file>=1.4
|
24
24
|
Requires-Dist: filelock>=3.0.8
|
25
|
+
Provides-Extra: release
|
26
|
+
Requires-Dist: build; extra == "release"
|
27
|
+
Requires-Dist: twine; extra == "release"
|
25
28
|
Provides-Extra: testing
|
26
|
-
Requires-Dist: black; extra == "testing"
|
27
29
|
Requires-Dist: mypy; extra == "testing"
|
28
30
|
Requires-Dist: pytest; extra == "testing"
|
29
31
|
Requires-Dist: pytest-gitignore; extra == "testing"
|
30
32
|
Requires-Dist: pytest-mock; extra == "testing"
|
31
33
|
Requires-Dist: responses; extra == "testing"
|
32
34
|
Requires-Dist: ruff; extra == "testing"
|
35
|
+
Requires-Dist: syrupy; extra == "testing"
|
33
36
|
Requires-Dist: tox; extra == "testing"
|
37
|
+
Requires-Dist: tox-uv; extra == "testing"
|
34
38
|
Requires-Dist: types-filelock; extra == "testing"
|
35
39
|
Requires-Dist: types-requests; extra == "testing"
|
36
40
|
|
@@ -125,8 +129,8 @@ tldextract http://forums.bbc.co.uk
|
|
125
129
|
|
126
130
|
Beware when first calling `tldextract`, it updates its TLD list with a live HTTP
|
127
131
|
request. This updated TLD set is usually cached indefinitely in `$HOME/.cache/python-tldextract`.
|
128
|
-
To control the cache's location, set TLDEXTRACT_CACHE environment variable or set the
|
129
|
-
cache_dir path
|
132
|
+
To control the cache's location, set the `TLDEXTRACT_CACHE` environment variable or set the
|
133
|
+
`cache_dir` path when constructing a `TLDExtract`.
|
130
134
|
|
131
135
|
(Arguably runtime bootstrapping like that shouldn't be the default behavior,
|
132
136
|
like for production systems. But I want you to have the latest TLDs, especially
|
@@ -215,10 +219,12 @@ extract = tldextract.TLDExtract(
|
|
215
219
|
fallback_to_snapshot=False)
|
216
220
|
```
|
217
221
|
|
218
|
-
|
219
|
-
|
222
|
+
If the cached version of public suffix definitions doesn't exist, such as on
|
223
|
+
the first run, the above snippet will request the URLs you specified in order,
|
224
|
+
and use the first successful response.
|
220
225
|
|
221
|
-
If you want to use input data from your local filesystem,
|
226
|
+
If you want to use input data from your local filesystem, use the `file://`
|
227
|
+
protocol with an absolute path:
|
222
228
|
|
223
229
|
```python
|
224
230
|
extract = tldextract.TLDExtract(
|
@@ -227,17 +233,24 @@ extract = tldextract.TLDExtract(
|
|
227
233
|
fallback_to_snapshot=False)
|
228
234
|
```
|
229
235
|
|
230
|
-
|
231
|
-
`os.path` is your friend.
|
232
|
-
|
233
|
-
The command line update command can be used with a URL or local file you specify:
|
236
|
+
This also works via command line update:
|
234
237
|
|
235
238
|
```zsh
|
236
239
|
tldextract --update --suffix_list_url "http://foo.bar.baz"
|
237
240
|
```
|
238
241
|
|
239
|
-
|
240
|
-
list on first use, or if you are behind a complex
|
242
|
+
Using your own URLs could be useful in production when you don't want the delay
|
243
|
+
with updating the suffix list on first use, or if you are behind a complex
|
244
|
+
firewall.
|
245
|
+
|
246
|
+
You can also specify additional suffixes in the `extra_suffixes` param. These
|
247
|
+
will be merged into whatever public suffix definitions are already in use by
|
248
|
+
`tldextract`.
|
249
|
+
|
250
|
+
```python
|
251
|
+
extract = tldextract.TLDExtract(
|
252
|
+
extra_suffixes=["foo", "bar", "baz"])
|
253
|
+
```
|
241
254
|
|
242
255
|
## FAQ
|
243
256
|
|
@@ -246,9 +259,9 @@ list on first use, or if you are behind a complex firewall that prevents a simpl
|
|
246
259
|
This project doesn't contain an actual list of public suffixes. That comes from
|
247
260
|
[the Public Suffix List (PSL)](https://publicsuffix.org/). Submit amendments there.
|
248
261
|
|
249
|
-
|
262
|
+
In the meantime, you can tell tldextract about your exception by either
|
250
263
|
forking the PSL and using your fork in the `suffix_list_urls` param, or adding
|
251
|
-
your suffix piecemeal with the `extra_suffixes` param.
|
264
|
+
your suffix piecemeal with the `extra_suffixes` param.
|
252
265
|
|
253
266
|
### I see my suffix in [the Public Suffix List (PSL)](https://publicsuffix.org/), but this library doesn't extract it.
|
254
267
|
|
@@ -305,5 +318,5 @@ tox -e py311
|
|
305
318
|
Automatically format all code:
|
306
319
|
|
307
320
|
```zsh
|
308
|
-
|
321
|
+
ruff format .
|
309
322
|
```
|
@@ -89,8 +89,8 @@ tldextract http://forums.bbc.co.uk
|
|
89
89
|
|
90
90
|
Beware when first calling `tldextract`, it updates its TLD list with a live HTTP
|
91
91
|
request. This updated TLD set is usually cached indefinitely in `$HOME/.cache/python-tldextract`.
|
92
|
-
To control the cache's location, set TLDEXTRACT_CACHE environment variable or set the
|
93
|
-
cache_dir path
|
92
|
+
To control the cache's location, set the `TLDEXTRACT_CACHE` environment variable or set the
|
93
|
+
`cache_dir` path when constructing a `TLDExtract`.
|
94
94
|
|
95
95
|
(Arguably runtime bootstrapping like that shouldn't be the default behavior,
|
96
96
|
like for production systems. But I want you to have the latest TLDs, especially
|
@@ -179,10 +179,12 @@ extract = tldextract.TLDExtract(
|
|
179
179
|
fallback_to_snapshot=False)
|
180
180
|
```
|
181
181
|
|
182
|
-
|
183
|
-
|
182
|
+
If the cached version of public suffix definitions doesn't exist, such as on
|
183
|
+
the first run, the above snippet will request the URLs you specified in order,
|
184
|
+
and use the first successful response.
|
184
185
|
|
185
|
-
If you want to use input data from your local filesystem,
|
186
|
+
If you want to use input data from your local filesystem, use the `file://`
|
187
|
+
protocol with an absolute path:
|
186
188
|
|
187
189
|
```python
|
188
190
|
extract = tldextract.TLDExtract(
|
@@ -191,17 +193,24 @@ extract = tldextract.TLDExtract(
|
|
191
193
|
fallback_to_snapshot=False)
|
192
194
|
```
|
193
195
|
|
194
|
-
|
195
|
-
`os.path` is your friend.
|
196
|
-
|
197
|
-
The command line update command can be used with a URL or local file you specify:
|
196
|
+
This also works via command line update:
|
198
197
|
|
199
198
|
```zsh
|
200
199
|
tldextract --update --suffix_list_url "http://foo.bar.baz"
|
201
200
|
```
|
202
201
|
|
203
|
-
|
204
|
-
list on first use, or if you are behind a complex
|
202
|
+
Using your own URLs could be useful in production when you don't want the delay
|
203
|
+
with updating the suffix list on first use, or if you are behind a complex
|
204
|
+
firewall.
|
205
|
+
|
206
|
+
You can also specify additional suffixes in the `extra_suffixes` param. These
|
207
|
+
will be merged into whatever public suffix definitions are already in use by
|
208
|
+
`tldextract`.
|
209
|
+
|
210
|
+
```python
|
211
|
+
extract = tldextract.TLDExtract(
|
212
|
+
extra_suffixes=["foo", "bar", "baz"])
|
213
|
+
```
|
205
214
|
|
206
215
|
## FAQ
|
207
216
|
|
@@ -210,9 +219,9 @@ list on first use, or if you are behind a complex firewall that prevents a simpl
|
|
210
219
|
This project doesn't contain an actual list of public suffixes. That comes from
|
211
220
|
[the Public Suffix List (PSL)](https://publicsuffix.org/). Submit amendments there.
|
212
221
|
|
213
|
-
|
222
|
+
In the meantime, you can tell tldextract about your exception by either
|
214
223
|
forking the PSL and using your fork in the `suffix_list_urls` param, or adding
|
215
|
-
your suffix piecemeal with the `extra_suffixes` param.
|
224
|
+
your suffix piecemeal with the `extra_suffixes` param.
|
216
225
|
|
217
226
|
### I see my suffix in [the Public Suffix List (PSL)](https://publicsuffix.org/), but this library doesn't extract it.
|
218
227
|
|
@@ -269,5 +278,5 @@ tox -e py311
|
|
269
278
|
Automatically format all code:
|
270
279
|
|
271
280
|
```zsh
|
272
|
-
|
281
|
+
ruff format .
|
273
282
|
```
|
@@ -23,13 +23,13 @@ classifiers = [
|
|
23
23
|
"Topic :: Utilities",
|
24
24
|
"License :: OSI Approved :: BSD License",
|
25
25
|
"Programming Language :: Python :: 3",
|
26
|
-
"Programming Language :: Python :: 3.8",
|
27
26
|
"Programming Language :: Python :: 3.9",
|
28
27
|
"Programming Language :: Python :: 3.10",
|
29
28
|
"Programming Language :: Python :: 3.11",
|
30
29
|
"Programming Language :: Python :: 3.12",
|
30
|
+
"Programming Language :: Python :: 3.13",
|
31
31
|
]
|
32
|
-
requires-python = ">=3.
|
32
|
+
requires-python = ">=3.9"
|
33
33
|
dynamic = ["version"]
|
34
34
|
readme = "README.md"
|
35
35
|
|
@@ -41,15 +41,20 @@ dependencies = [
|
|
41
41
|
]
|
42
42
|
|
43
43
|
[project.optional-dependencies]
|
44
|
+
release = [
|
45
|
+
"build",
|
46
|
+
"twine",
|
47
|
+
]
|
44
48
|
testing = [
|
45
|
-
"black",
|
46
49
|
"mypy",
|
47
50
|
"pytest",
|
48
51
|
"pytest-gitignore",
|
49
52
|
"pytest-mock",
|
50
53
|
"responses",
|
51
54
|
"ruff",
|
55
|
+
"syrupy",
|
52
56
|
"tox",
|
57
|
+
"tox-uv",
|
53
58
|
"types-filelock",
|
54
59
|
"types-requests",
|
55
60
|
]
|
@@ -79,12 +84,13 @@ write_to = "tldextract/_version.py"
|
|
79
84
|
version = {attr = "setuptools_scm.get_version"}
|
80
85
|
|
81
86
|
[tool.mypy]
|
87
|
+
explicit_package_bases = true
|
82
88
|
strict = true
|
83
89
|
|
84
90
|
[tool.pytest.ini_options]
|
85
91
|
addopts = "--doctest-modules"
|
86
92
|
|
87
|
-
[tool.ruff]
|
93
|
+
[tool.ruff.lint]
|
88
94
|
select = [
|
89
95
|
"A",
|
90
96
|
"B",
|
@@ -98,8 +104,8 @@ select = [
|
|
98
104
|
"W",
|
99
105
|
]
|
100
106
|
ignore = [
|
101
|
-
"E501", # line too long; if
|
107
|
+
"E501", # line too long; if formatter does its job, not worried about the rare long line
|
102
108
|
]
|
103
109
|
|
104
|
-
[tool.ruff.pydocstyle]
|
110
|
+
[tool.ruff.lint.pydocstyle]
|
105
111
|
convention = "pep257"
|
@@ -0,0 +1,236 @@
|
|
1
|
+
"""
|
2
|
+
This script automates the release process for a Python package.
|
3
|
+
|
4
|
+
It will:
|
5
|
+
- Add a git tag for the given version.
|
6
|
+
- Remove the previous dist folder.
|
7
|
+
- Create a build.
|
8
|
+
- Ask the user to verify the build.
|
9
|
+
- Upload the build to PyPI.
|
10
|
+
- Push all git tags to the remote.
|
11
|
+
- Create a draft release on GitHub using the version notes in CHANGELOG.md.
|
12
|
+
|
13
|
+
Prerequisites:
|
14
|
+
- This must be run from the root of the repository.
|
15
|
+
- The repo must have a clean git working tree.
|
16
|
+
- The user must have the GITHUB_TOKEN environment variable set to a GitHub personal access token with repository "Contents" read and write permission.
|
17
|
+
- The user will need credentials for the PyPI repository, which the user will be prompted for during the upload step. The user will need to paste the token manually from a password manager or similar.
|
18
|
+
- The CHANGELOG.md file must already contain an entry for the version being released.
|
19
|
+
- Install requirements with: pip install --upgrade --editable '.[release]'
|
20
|
+
|
21
|
+
"""
|
22
|
+
|
23
|
+
from __future__ import annotations
|
24
|
+
|
25
|
+
import contextlib
|
26
|
+
import os
|
27
|
+
import re
|
28
|
+
import subprocess
|
29
|
+
import sys
|
30
|
+
from collections.abc import Iterator
|
31
|
+
from pathlib import Path
|
32
|
+
|
33
|
+
import requests
|
34
|
+
|
35
|
+
|
36
|
+
@contextlib.contextmanager
|
37
|
+
def add_git_tag_for_version(version: str) -> Iterator[None]:
|
38
|
+
"""Add a git tag for the given version."""
|
39
|
+
subprocess.run(["git", "tag", "-a", version, "-m", version], check=True)
|
40
|
+
print(f"Version {version} tag added successfully.")
|
41
|
+
try:
|
42
|
+
yield
|
43
|
+
except:
|
44
|
+
subprocess.run(["git", "tag", "-d", version])
|
45
|
+
raise
|
46
|
+
|
47
|
+
|
48
|
+
def remove_previous_dist() -> None:
|
49
|
+
"""Check for dist folder, and if it exists, remove it."""
|
50
|
+
subprocess.run(["rm", "-rf", Path("dist")], check=True)
|
51
|
+
print("Previous dist folder removed successfully.")
|
52
|
+
|
53
|
+
|
54
|
+
def create_build() -> None:
|
55
|
+
"""Create a build."""
|
56
|
+
subprocess.run(["python", "-m", "build"], check=True)
|
57
|
+
print("Build created successfully.")
|
58
|
+
|
59
|
+
|
60
|
+
def verify_build(is_test: str) -> None:
|
61
|
+
"""Verify the build.
|
62
|
+
|
63
|
+
Print the archives in dist/ and ask the user to manually inspect and
|
64
|
+
confirm they contain the expected files, e.g. source files and test files.
|
65
|
+
"""
|
66
|
+
build_files = os.listdir("dist")
|
67
|
+
if len(build_files) != 2:
|
68
|
+
print(
|
69
|
+
"WARNING: dist folder contains incorrect number of files.", file=sys.stderr
|
70
|
+
)
|
71
|
+
print("Contents of dist folder:")
|
72
|
+
subprocess.run(["ls", "-l", Path("dist")], check=True)
|
73
|
+
print("Contents of tar files in dist folder:")
|
74
|
+
for build_file in build_files:
|
75
|
+
subprocess.run(["tar", "tvf", Path("dist") / build_file], check=True)
|
76
|
+
confirmation = input("Does the build look correct? (y/n): ")
|
77
|
+
if confirmation == "y":
|
78
|
+
print("Build verified successfully.")
|
79
|
+
else:
|
80
|
+
raise Exception("Could not verify. Build was not uploaded.")
|
81
|
+
|
82
|
+
|
83
|
+
def generate_github_release_notes_body(token: str, version: str) -> str:
|
84
|
+
"""Generate and grab release notes URL from Github.
|
85
|
+
|
86
|
+
Delete their first paragraph, because we track its contents in a tighter
|
87
|
+
form in CHANGELOG.md. See `get_changelog_release_notes`.
|
88
|
+
"""
|
89
|
+
response = requests.post(
|
90
|
+
"https://api.github.com/repos/john-kurkowski/tldextract/releases/generate-notes",
|
91
|
+
headers={
|
92
|
+
"Accept": "application/vnd.github+json",
|
93
|
+
"Authorization": f"Bearer {token}",
|
94
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
95
|
+
},
|
96
|
+
json={"tag_name": version},
|
97
|
+
)
|
98
|
+
|
99
|
+
try:
|
100
|
+
response.raise_for_status()
|
101
|
+
except requests.exceptions.HTTPError as err:
|
102
|
+
print(
|
103
|
+
f"WARNING: Failed to generate release notes from Github: {err}",
|
104
|
+
file=sys.stderr,
|
105
|
+
)
|
106
|
+
return ""
|
107
|
+
|
108
|
+
body = str(response.json()["body"])
|
109
|
+
paragraphs = body.split("\n\n")
|
110
|
+
return "\n\n".join(paragraphs[1:])
|
111
|
+
|
112
|
+
|
113
|
+
def get_changelog_release_notes(version: str) -> str:
|
114
|
+
"""Get the changelog release notes.
|
115
|
+
|
116
|
+
Uses a regex starting on a heading beginning with the version number
|
117
|
+
literal, and matching until the next heading. Using regex to match markup
|
118
|
+
is brittle. Consider a Markdown-parsing library instead.
|
119
|
+
"""
|
120
|
+
with open("CHANGELOG.md") as file:
|
121
|
+
changelog_text = file.read()
|
122
|
+
pattern = re.compile(rf"## {re.escape(version)}[^\n]*(.*?)## ", re.DOTALL)
|
123
|
+
match = pattern.search(changelog_text)
|
124
|
+
if match:
|
125
|
+
return str(match.group(1)).strip()
|
126
|
+
else:
|
127
|
+
return ""
|
128
|
+
|
129
|
+
|
130
|
+
def create_github_release_draft(token: str, version: str) -> None:
|
131
|
+
"""Create a release on GitHub."""
|
132
|
+
github_release_body = generate_github_release_notes_body(token, version)
|
133
|
+
changelog_notes = get_changelog_release_notes(version)
|
134
|
+
release_body = f"{changelog_notes}\n\n{github_release_body}"
|
135
|
+
|
136
|
+
response = requests.post(
|
137
|
+
"https://api.github.com/repos/john-kurkowski/tldextract/releases",
|
138
|
+
headers={
|
139
|
+
"Accept": "application/vnd.github+json",
|
140
|
+
"Authorization": f"Bearer {token}",
|
141
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
142
|
+
},
|
143
|
+
json={
|
144
|
+
"tag_name": version,
|
145
|
+
"name": version,
|
146
|
+
"body": release_body,
|
147
|
+
"draft": True,
|
148
|
+
"prerelease": False,
|
149
|
+
},
|
150
|
+
)
|
151
|
+
|
152
|
+
try:
|
153
|
+
response.raise_for_status()
|
154
|
+
except requests.exceptions.HTTPError as err:
|
155
|
+
print(
|
156
|
+
f"WARNING: Failed to create release on Github: {err}",
|
157
|
+
file=sys.stderr,
|
158
|
+
)
|
159
|
+
return
|
160
|
+
|
161
|
+
print(f'Release created successfully: {response.json()["html_url"]}')
|
162
|
+
|
163
|
+
if not changelog_notes:
|
164
|
+
print(
|
165
|
+
"WARNING: Failed to parse changelog release notes. Manually copy this version's notes from the CHANGELOG.md file to the above URL.",
|
166
|
+
file=sys.stderr,
|
167
|
+
)
|
168
|
+
|
169
|
+
|
170
|
+
def upload_build_to_pypi(is_test: str) -> None:
|
171
|
+
"""Upload the build to PyPI."""
|
172
|
+
repository: list[str | Path] = (
|
173
|
+
[] if is_test == "n" else ["--repository", "testpypi"]
|
174
|
+
)
|
175
|
+
upload_command = ["twine", "upload", *repository, Path("dist") / "*"]
|
176
|
+
subprocess.run(
|
177
|
+
upload_command,
|
178
|
+
check=True,
|
179
|
+
)
|
180
|
+
|
181
|
+
|
182
|
+
def push_git_tags() -> None:
|
183
|
+
"""Push all git tags to the remote."""
|
184
|
+
subprocess.run(["git", "push", "--tags", "origin", "master"], check=True)
|
185
|
+
|
186
|
+
|
187
|
+
def check_for_clean_working_tree() -> None:
|
188
|
+
"""Check for a clean git working tree."""
|
189
|
+
git_status = subprocess.run(
|
190
|
+
["git", "status", "--porcelain"], capture_output=True, text=True
|
191
|
+
)
|
192
|
+
if git_status.stdout:
|
193
|
+
print(
|
194
|
+
"Git working tree is not clean. Please commit or stash changes.",
|
195
|
+
file=sys.stderr,
|
196
|
+
)
|
197
|
+
sys.exit(1)
|
198
|
+
|
199
|
+
|
200
|
+
def get_env_github_token() -> str:
|
201
|
+
"""Check for the GITHUB_TOKEN environment variable."""
|
202
|
+
github_token = os.environ.get("GITHUB_TOKEN")
|
203
|
+
if not github_token:
|
204
|
+
print("GITHUB_TOKEN environment variable not set.", file=sys.stderr)
|
205
|
+
sys.exit(1)
|
206
|
+
return github_token
|
207
|
+
|
208
|
+
|
209
|
+
def get_is_test_response() -> str:
|
210
|
+
"""Ask the user if this is a test release."""
|
211
|
+
while True:
|
212
|
+
is_test = input("Is this a test release? (y/n): ")
|
213
|
+
if is_test in ["y", "n"]:
|
214
|
+
return is_test
|
215
|
+
else:
|
216
|
+
print("Invalid input. Please enter 'y' or 'n.'")
|
217
|
+
|
218
|
+
|
219
|
+
def main() -> None:
|
220
|
+
"""Run the main program."""
|
221
|
+
check_for_clean_working_tree()
|
222
|
+
github_token = get_env_github_token()
|
223
|
+
is_test = get_is_test_response()
|
224
|
+
version_number = input("Enter the version number: ")
|
225
|
+
|
226
|
+
with add_git_tag_for_version(version_number):
|
227
|
+
remove_previous_dist()
|
228
|
+
create_build()
|
229
|
+
verify_build(is_test)
|
230
|
+
upload_build_to_pypi(is_test)
|
231
|
+
push_git_tags()
|
232
|
+
create_github_release_draft(github_token, version_number)
|
233
|
+
|
234
|
+
|
235
|
+
if __name__ == "__main__":
|
236
|
+
main()
|