twi-scraper 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.
- twi_scraper-0.1.0/.gitignore +8 -0
- twi_scraper-0.1.0/LICENSE +21 -0
- twi_scraper-0.1.0/PKG-INFO +155 -0
- twi_scraper-0.1.0/README.md +128 -0
- twi_scraper-0.1.0/pyproject.toml +51 -0
- twi_scraper-0.1.0/src/twi/__init__.py +3 -0
- twi_scraper-0.1.0/src/twi/clean.py +599 -0
- twi_scraper-0.1.0/src/twi/cli.py +424 -0
- twi_scraper-0.1.0/src/twi/epub.py +182 -0
- twi_scraper-0.1.0/src/twi/markdown.py +144 -0
- twi_scraper-0.1.0/src/twi/model.py +143 -0
- twi_scraper-0.1.0/src/twi/site.py +337 -0
- twi_scraper-0.1.0/src/twi/unistyle.py +131 -0
- twi_scraper-0.1.0/tests/test_clean.py +149 -0
- twi_scraper-0.1.0/tests/test_output.py +89 -0
- twi_scraper-0.1.0/tests/test_site.py +69 -0
- twi_scraper-0.1.0/tests/test_text_effects.py +142 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Johann
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: twi-scraper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Download The Wandering Inn web serial as clean, screen-reader-friendly Markdown or EPUB.
|
|
5
|
+
Project-URL: Homepage, https://github.com/johanntan/twi-scraper
|
|
6
|
+
Project-URL: Source, https://github.com/johanntan/twi-scraper
|
|
7
|
+
Project-URL: Issues, https://github.com/johanntan/twi-scraper/issues
|
|
8
|
+
Author-email: Johann <johann@jtan.me>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: accessibility,epub,markdown,screen reader,wandering inn,web serial
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: beautifulsoup4>=4.12
|
|
24
|
+
Requires-Dist: lxml>=5.0
|
|
25
|
+
Requires-Dist: requests>=2.31
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# twi
|
|
29
|
+
|
|
30
|
+
Download [The Wandering Inn](https://wanderinginn.com) as clean, screen-reader-friendly
|
|
31
|
+
Markdown or EPUB files.
|
|
32
|
+
|
|
33
|
+
- Single chapters → Markdown, with the author's notes kept under an "Author's Note" heading.
|
|
34
|
+
- Whole volumes → EPUB, with the author's notes and announcements removed.
|
|
35
|
+
- Gallery images, fan-art credits and Previous/Next links are always removed.
|
|
36
|
+
- The author's dash separators (`——`, `—-`) become a real separator: `***` in Markdown,
|
|
37
|
+
`<hr/>` in EPUB.
|
|
38
|
+
- Links between chapters stay inside the book. For example, Solstice Pt. 3's "Changed." link
|
|
39
|
+
jumps to Pt. 4 in the EPUB instead of opening the website. Links to chapters that aren't in
|
|
40
|
+
the file become plain text.
|
|
41
|
+
- Tales of Innworld and other image-only or announcement posts are left out of volumes.
|
|
42
|
+
- Blacked-out text (`███` in 7.25, 7.50) reads as "[redacted]" instead of "full block full
|
|
43
|
+
block". Where the page hides the original words, they go in a footnote: a pop-up link in the
|
|
44
|
+
EPUB, a `[^1]` footnote at the end of the chapter in Markdown.
|
|
45
|
+
- "Fancy font" letters become plain letters a screen reader can say. This covers small capitals
|
|
46
|
+
for gods' voices (`Dᴇᴀᴛʜ ʜᴀs ᴄᴏᴍᴇ` → "Death has come"), superscript fine print, maths-font
|
|
47
|
+
digits, and upside-down text (10.56 F). In the EPUB, small caps and superscripts still look
|
|
48
|
+
the same.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
The package is called `twi-scraper`; the command it installs is `twi`.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
uv tool install twi-scraper # with uv
|
|
56
|
+
pipx install twi-scraper # or with pipx
|
|
57
|
+
uv tool upgrade twi-scraper # later, to update
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
From a checkout of this repository, `uv run twi --help` works without installing.
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
twi list # volumes, the Volume 1 archive, and Griefman
|
|
66
|
+
twi list 7 # chapters in Volume 7
|
|
67
|
+
|
|
68
|
+
twi chapter "10.78 RV" # one chapter as Markdown -> "10.78 RV.md"
|
|
69
|
+
twi chapter 8.31 "solstice pt 3" latest
|
|
70
|
+
twi https://wanderinginn.com/2023/11/28/9-68/ # any chapter by URL
|
|
71
|
+
|
|
72
|
+
twi volume 7 # "The Wandering Inn - Volume 7.epub"
|
|
73
|
+
twi volume 7 --format md # a folder of numbered Markdown files
|
|
74
|
+
twi volume 7 --format md --single-file
|
|
75
|
+
twi volume archive # the original 2016 Volume 1
|
|
76
|
+
|
|
77
|
+
twi book griefman # Griefman as one EPUB, split by its own chapters
|
|
78
|
+
twi book griefman --format md # ...or as one Markdown file
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Chapter titles are matched loosely: `solstice pt 3` finds "Solstice (Pt. 3)". If a title matches
|
|
82
|
+
several chapters, use the full title or the URL.
|
|
83
|
+
|
|
84
|
+
### Options
|
|
85
|
+
|
|
86
|
+
| Option | Effect |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `--format md` / `--format epub` | Change the output format. |
|
|
89
|
+
| `-o DIR`, `--output DIR` | Where to write files (default: the current folder). |
|
|
90
|
+
| `--single-file` | With `--format md`, write a volume as one file instead of a folder. |
|
|
91
|
+
| `--keep-notes` / `--no-notes` | Keep or remove author's notes and announcements. By default, `chapter` keeps them; `volume` and `book` remove them. |
|
|
92
|
+
| `--password PW` | A password to try on locked chapters. Can be given more than once. |
|
|
93
|
+
| `--skip-locked` | Skip locked chapters without asking. |
|
|
94
|
+
| `--trigger-free` | Where a chapter has a trigger-free version (9.68), download that instead. |
|
|
95
|
+
| `--include-extras` | Keep Tales of Innworld and other non-story posts in volumes. |
|
|
96
|
+
| `--refresh` | Download pages again instead of using the cache. |
|
|
97
|
+
|
|
98
|
+
## Locked chapters
|
|
99
|
+
|
|
100
|
+
Some chapters are Patreon-locked, including the newest chapter and many rewritten Volume 1–2
|
|
101
|
+
chapters, which have a password field on the site. When `twi` meets one, it:
|
|
102
|
+
|
|
103
|
+
1. tries the passwords you gave with `--password` or the `TWI_PASSWORD` environment variable,
|
|
104
|
+
then any password you've already typed in this run;
|
|
105
|
+
2. otherwise asks: `"10.79 RVP" is Patreon-locked (Jar of Acid tier). Password (blank to skip):`.
|
|
106
|
+
A wrong password asks again. A blank answer skips that chapter and every other chapter
|
|
107
|
+
behind the same lock for the rest of the run, so you aren't asked 50 times.
|
|
108
|
+
|
|
109
|
+
Skipped chapters are listed at the end. With `--skip-locked`, or when not run in a terminal,
|
|
110
|
+
locked chapters are skipped without asking.
|
|
111
|
+
|
|
112
|
+
**Wrong passwords get you blocked.** The site's firewall blocks a connection from locked pages
|
|
113
|
+
after wrong password attempts, with a "WP Remote Firewall: Blocked because of Malicious
|
|
114
|
+
Activities" page. Other pages keep working. To avoid this, `twi` remembers which password works
|
|
115
|
+
for each Patreon tier and never sends a password again for a tier where it already failed. If
|
|
116
|
+
you do get blocked, `twi` stops and says so. Wait a while before trying again. Everything
|
|
117
|
+
already downloaded is cached, so the rerun is quick.
|
|
118
|
+
|
|
119
|
+
Some extra pages, such as 9.68's trigger-free version, use a password printed in the chapter
|
|
120
|
+
itself. `twi` reads that password and uses it automatically.
|
|
121
|
+
|
|
122
|
+
## Progress messages
|
|
123
|
+
|
|
124
|
+
Progress goes to standard error as plain lines (`[3/95] 7.02`), with no progress bars or
|
|
125
|
+
colour. Each line also says what was removed from a chapter, so nothing disappears silently.
|
|
126
|
+
|
|
127
|
+
## Cache
|
|
128
|
+
|
|
129
|
+
Unlocked chapter pages are cached in `~/.cache/twi`. The table of contents is refreshed every
|
|
130
|
+
hour. Locked pages are never cached. Delete the folder, or pass `--refresh`, to download
|
|
131
|
+
everything again.
|
|
132
|
+
|
|
133
|
+
## Publishing a new version
|
|
134
|
+
|
|
135
|
+
Releases go to PyPI automatically through GitHub Actions (`.github/workflows/publish.yml`),
|
|
136
|
+
using PyPI trusted publishing, so no API token is stored anywhere.
|
|
137
|
+
|
|
138
|
+
One-time setup on pypi.org (account → Publishing → "Add a new pending publisher"):
|
|
139
|
+
|
|
140
|
+
| Field | Value |
|
|
141
|
+
|---|---|
|
|
142
|
+
| PyPI project name | `twi-scraper` |
|
|
143
|
+
| Owner | `johanntan` |
|
|
144
|
+
| Repository name | `twi-scraper` |
|
|
145
|
+
| Workflow name | `publish.yml` |
|
|
146
|
+
| Environment name | `pypi` |
|
|
147
|
+
|
|
148
|
+
Then, for each release:
|
|
149
|
+
|
|
150
|
+
1. Bump `version` in `pyproject.toml` and commit.
|
|
151
|
+
2. Create a GitHub release whose tag is that version, e.g. `v0.1.0`:
|
|
152
|
+
`gh release create v0.1.0 --generate-notes`.
|
|
153
|
+
|
|
154
|
+
The workflow checks that the tag matches the version, runs the tests, builds, and uploads.
|
|
155
|
+
PyPI never accepts the same version twice, so a mistake means bumping to the next version.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# twi
|
|
2
|
+
|
|
3
|
+
Download [The Wandering Inn](https://wanderinginn.com) as clean, screen-reader-friendly
|
|
4
|
+
Markdown or EPUB files.
|
|
5
|
+
|
|
6
|
+
- Single chapters → Markdown, with the author's notes kept under an "Author's Note" heading.
|
|
7
|
+
- Whole volumes → EPUB, with the author's notes and announcements removed.
|
|
8
|
+
- Gallery images, fan-art credits and Previous/Next links are always removed.
|
|
9
|
+
- The author's dash separators (`——`, `—-`) become a real separator: `***` in Markdown,
|
|
10
|
+
`<hr/>` in EPUB.
|
|
11
|
+
- Links between chapters stay inside the book. For example, Solstice Pt. 3's "Changed." link
|
|
12
|
+
jumps to Pt. 4 in the EPUB instead of opening the website. Links to chapters that aren't in
|
|
13
|
+
the file become plain text.
|
|
14
|
+
- Tales of Innworld and other image-only or announcement posts are left out of volumes.
|
|
15
|
+
- Blacked-out text (`███` in 7.25, 7.50) reads as "[redacted]" instead of "full block full
|
|
16
|
+
block". Where the page hides the original words, they go in a footnote: a pop-up link in the
|
|
17
|
+
EPUB, a `[^1]` footnote at the end of the chapter in Markdown.
|
|
18
|
+
- "Fancy font" letters become plain letters a screen reader can say. This covers small capitals
|
|
19
|
+
for gods' voices (`Dᴇᴀᴛʜ ʜᴀs ᴄᴏᴍᴇ` → "Death has come"), superscript fine print, maths-font
|
|
20
|
+
digits, and upside-down text (10.56 F). In the EPUB, small caps and superscripts still look
|
|
21
|
+
the same.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
The package is called `twi-scraper`; the command it installs is `twi`.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
uv tool install twi-scraper # with uv
|
|
29
|
+
pipx install twi-scraper # or with pipx
|
|
30
|
+
uv tool upgrade twi-scraper # later, to update
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
From a checkout of this repository, `uv run twi --help` works without installing.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
twi list # volumes, the Volume 1 archive, and Griefman
|
|
39
|
+
twi list 7 # chapters in Volume 7
|
|
40
|
+
|
|
41
|
+
twi chapter "10.78 RV" # one chapter as Markdown -> "10.78 RV.md"
|
|
42
|
+
twi chapter 8.31 "solstice pt 3" latest
|
|
43
|
+
twi https://wanderinginn.com/2023/11/28/9-68/ # any chapter by URL
|
|
44
|
+
|
|
45
|
+
twi volume 7 # "The Wandering Inn - Volume 7.epub"
|
|
46
|
+
twi volume 7 --format md # a folder of numbered Markdown files
|
|
47
|
+
twi volume 7 --format md --single-file
|
|
48
|
+
twi volume archive # the original 2016 Volume 1
|
|
49
|
+
|
|
50
|
+
twi book griefman # Griefman as one EPUB, split by its own chapters
|
|
51
|
+
twi book griefman --format md # ...or as one Markdown file
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Chapter titles are matched loosely: `solstice pt 3` finds "Solstice (Pt. 3)". If a title matches
|
|
55
|
+
several chapters, use the full title or the URL.
|
|
56
|
+
|
|
57
|
+
### Options
|
|
58
|
+
|
|
59
|
+
| Option | Effect |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `--format md` / `--format epub` | Change the output format. |
|
|
62
|
+
| `-o DIR`, `--output DIR` | Where to write files (default: the current folder). |
|
|
63
|
+
| `--single-file` | With `--format md`, write a volume as one file instead of a folder. |
|
|
64
|
+
| `--keep-notes` / `--no-notes` | Keep or remove author's notes and announcements. By default, `chapter` keeps them; `volume` and `book` remove them. |
|
|
65
|
+
| `--password PW` | A password to try on locked chapters. Can be given more than once. |
|
|
66
|
+
| `--skip-locked` | Skip locked chapters without asking. |
|
|
67
|
+
| `--trigger-free` | Where a chapter has a trigger-free version (9.68), download that instead. |
|
|
68
|
+
| `--include-extras` | Keep Tales of Innworld and other non-story posts in volumes. |
|
|
69
|
+
| `--refresh` | Download pages again instead of using the cache. |
|
|
70
|
+
|
|
71
|
+
## Locked chapters
|
|
72
|
+
|
|
73
|
+
Some chapters are Patreon-locked, including the newest chapter and many rewritten Volume 1–2
|
|
74
|
+
chapters, which have a password field on the site. When `twi` meets one, it:
|
|
75
|
+
|
|
76
|
+
1. tries the passwords you gave with `--password` or the `TWI_PASSWORD` environment variable,
|
|
77
|
+
then any password you've already typed in this run;
|
|
78
|
+
2. otherwise asks: `"10.79 RVP" is Patreon-locked (Jar of Acid tier). Password (blank to skip):`.
|
|
79
|
+
A wrong password asks again. A blank answer skips that chapter and every other chapter
|
|
80
|
+
behind the same lock for the rest of the run, so you aren't asked 50 times.
|
|
81
|
+
|
|
82
|
+
Skipped chapters are listed at the end. With `--skip-locked`, or when not run in a terminal,
|
|
83
|
+
locked chapters are skipped without asking.
|
|
84
|
+
|
|
85
|
+
**Wrong passwords get you blocked.** The site's firewall blocks a connection from locked pages
|
|
86
|
+
after wrong password attempts, with a "WP Remote Firewall: Blocked because of Malicious
|
|
87
|
+
Activities" page. Other pages keep working. To avoid this, `twi` remembers which password works
|
|
88
|
+
for each Patreon tier and never sends a password again for a tier where it already failed. If
|
|
89
|
+
you do get blocked, `twi` stops and says so. Wait a while before trying again. Everything
|
|
90
|
+
already downloaded is cached, so the rerun is quick.
|
|
91
|
+
|
|
92
|
+
Some extra pages, such as 9.68's trigger-free version, use a password printed in the chapter
|
|
93
|
+
itself. `twi` reads that password and uses it automatically.
|
|
94
|
+
|
|
95
|
+
## Progress messages
|
|
96
|
+
|
|
97
|
+
Progress goes to standard error as plain lines (`[3/95] 7.02`), with no progress bars or
|
|
98
|
+
colour. Each line also says what was removed from a chapter, so nothing disappears silently.
|
|
99
|
+
|
|
100
|
+
## Cache
|
|
101
|
+
|
|
102
|
+
Unlocked chapter pages are cached in `~/.cache/twi`. The table of contents is refreshed every
|
|
103
|
+
hour. Locked pages are never cached. Delete the folder, or pass `--refresh`, to download
|
|
104
|
+
everything again.
|
|
105
|
+
|
|
106
|
+
## Publishing a new version
|
|
107
|
+
|
|
108
|
+
Releases go to PyPI automatically through GitHub Actions (`.github/workflows/publish.yml`),
|
|
109
|
+
using PyPI trusted publishing, so no API token is stored anywhere.
|
|
110
|
+
|
|
111
|
+
One-time setup on pypi.org (account → Publishing → "Add a new pending publisher"):
|
|
112
|
+
|
|
113
|
+
| Field | Value |
|
|
114
|
+
|---|---|
|
|
115
|
+
| PyPI project name | `twi-scraper` |
|
|
116
|
+
| Owner | `johanntan` |
|
|
117
|
+
| Repository name | `twi-scraper` |
|
|
118
|
+
| Workflow name | `publish.yml` |
|
|
119
|
+
| Environment name | `pypi` |
|
|
120
|
+
|
|
121
|
+
Then, for each release:
|
|
122
|
+
|
|
123
|
+
1. Bump `version` in `pyproject.toml` and commit.
|
|
124
|
+
2. Create a GitHub release whose tag is that version, e.g. `v0.1.0`:
|
|
125
|
+
`gh release create v0.1.0 --generate-notes`.
|
|
126
|
+
|
|
127
|
+
The workflow checks that the tag matches the version, runs the tests, builds, and uploads.
|
|
128
|
+
PyPI never accepts the same version twice, so a mistake means bumping to the next version.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "twi-scraper"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Download The Wandering Inn web serial as clean, screen-reader-friendly Markdown or EPUB."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Johann", email = "johann@jtan.me" }]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
keywords = ["wandering inn", "web serial", "epub", "markdown", "accessibility", "screen reader"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Intended Audience :: End Users/Desktop",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Text Processing :: Markup :: Markdown",
|
|
21
|
+
"Topic :: Utilities",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"beautifulsoup4>=4.12",
|
|
25
|
+
"lxml>=5.0",
|
|
26
|
+
"requests>=2.31",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/johanntan/twi-scraper"
|
|
31
|
+
Source = "https://github.com/johanntan/twi-scraper"
|
|
32
|
+
Issues = "https://github.com/johanntan/twi-scraper/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
twi = "twi.cli:main"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = ["pytest>=8"]
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/twi"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
exclude = ["CLAUDE.md", ".github", "uv.lock", "out"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|