pyrefdev 2025.2__tar.gz → 2025.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.
- pyrefdev-2025.3/.github/workflows/ci.yml +39 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/PKG-INFO +23 -2
- {pyrefdev-2025.2 → pyrefdev-2025.3}/README.md +20 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/index.html +92 -42
- {pyrefdev-2025.2 → pyrefdev-2025.3}/pyproject.toml +7 -1
- pyrefdev-2025.3/src/pyrefdev/_version.py +1 -0
- pyrefdev-2025.3/src/pyrefdev/config.py +39 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/__main__.py +1 -1
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/crawl_docs.py +45 -27
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/parse_docs.py +71 -37
- pyrefdev-2025.3/src/pyrefdev/mapping/__init__.py +21 -0
- pyrefdev-2025.3/src/pyrefdev/mapping/numpy.py +2225 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/server.py +1 -1
- pyrefdev-2025.3/tests/test_mapping.py +6 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/uv.lock +120 -109
- pyrefdev-2025.2/src/pyrefdev/_version.py +0 -1
- pyrefdev-2025.2/src/pyrefdev/indexer/config.py +0 -9
- {pyrefdev-2025.2 → pyrefdev-2025.3}/.github/workflows/pypi_upload.yml +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/.gitignore +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/CHANGELOG.md +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/LICENSE +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/pyrefdev.service +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/__init__.py +0 -0
- {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/__main__.py +0 -0
- /pyrefdev-2025.2/src/pyrefdev/mapping.py → /pyrefdev-2025.3/src/pyrefdev/mapping/__python__.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
permissions: {}
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
18
|
+
python-version:
|
|
19
|
+
- "3.10"
|
|
20
|
+
- "3.11"
|
|
21
|
+
- "3.12"
|
|
22
|
+
- "3.13"
|
|
23
|
+
- "3.13t"
|
|
24
|
+
- "3.14"
|
|
25
|
+
- "3.14t"
|
|
26
|
+
steps:
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v6
|
|
29
|
+
with:
|
|
30
|
+
version: "latest"
|
|
31
|
+
python-version: ${{ matrix.python-version }}
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- name: Build pyrefdev
|
|
37
|
+
run: uv sync --locked --dev
|
|
38
|
+
- name: Run pytest
|
|
39
|
+
run: uv run pytest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyrefdev
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.3
|
|
4
4
|
Summary: The entry point to Python reference docs
|
|
5
5
|
Project-URL: Home, https://pyref.dev
|
|
6
6
|
Project-URL: Source, https://github.com/mangoumbrella/pyref.dev
|
|
@@ -14,11 +14,12 @@ Classifier: Intended Audience :: Developers
|
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: Free Threading
|
|
22
23
|
Classifier: Topic :: Documentation
|
|
23
24
|
Requires-Python: >=3.10
|
|
24
25
|
Requires-Dist: yib>=0.2.0
|
|
@@ -57,6 +58,26 @@ Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI
|
|
|
57
58
|
|
|
58
59
|
For now, the symbols are case-insensitive.
|
|
59
60
|
|
|
61
|
+
## Server Setup
|
|
62
|
+
|
|
63
|
+
To set up a new server:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
> git clone https://github.com/mangoumbrella/pyref.dev
|
|
67
|
+
> cd pyref.dev
|
|
68
|
+
> sudo cp pyrefdev.service /etc/systemd/system/pyrefdev.service
|
|
69
|
+
> systemctl start pyrefdev.service
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
To update to a new version:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
> cd pyref.dev
|
|
76
|
+
> git pull
|
|
77
|
+
> uv sync --all-extras
|
|
78
|
+
> systemctl restart pyrefdev.service
|
|
79
|
+
```
|
|
80
|
+
|
|
60
81
|
## Changelog
|
|
61
82
|
|
|
62
83
|
See [CHANGELOG.md](https://github.com/mangoumbrella/pyref.dev/blob/main/CHANGELOG.md).
|
|
@@ -24,6 +24,26 @@ Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI
|
|
|
24
24
|
|
|
25
25
|
For now, the symbols are case-insensitive.
|
|
26
26
|
|
|
27
|
+
## Server Setup
|
|
28
|
+
|
|
29
|
+
To set up a new server:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
> git clone https://github.com/mangoumbrella/pyref.dev
|
|
33
|
+
> cd pyref.dev
|
|
34
|
+
> sudo cp pyrefdev.service /etc/systemd/system/pyrefdev.service
|
|
35
|
+
> systemctl start pyrefdev.service
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To update to a new version:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
> cd pyref.dev
|
|
42
|
+
> git pull
|
|
43
|
+
> uv sync --all-extras
|
|
44
|
+
> systemctl restart pyrefdev.service
|
|
45
|
+
```
|
|
46
|
+
|
|
27
47
|
## Changelog
|
|
28
48
|
|
|
29
49
|
See [CHANGELOG.md](https://github.com/mangoumbrella/pyref.dev/blob/main/CHANGELOG.md).
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
--gray-600: #6c757d;
|
|
18
18
|
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
19
19
|
--font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
-
--shadow-sm: 0 2px 4px rgba(0,0,0,0.05);
|
|
21
|
-
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
|
|
22
|
-
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
|
|
20
|
+
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
21
|
+
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
22
|
+
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
|
|
23
23
|
--header-height: 60px;
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
* {
|
|
27
27
|
margin: 0;
|
|
28
28
|
padding: 0;
|
|
29
29
|
box-sizing: border-box;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
body {
|
|
33
33
|
font-family: var(--font-sans);
|
|
34
34
|
background: var(--light);
|
|
@@ -40,22 +40,20 @@
|
|
|
40
40
|
position: relative;
|
|
41
41
|
padding-top: var(--header-height);
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
.header {
|
|
45
45
|
position: fixed;
|
|
46
46
|
top: 0;
|
|
47
47
|
left: 0;
|
|
48
48
|
right: 0;
|
|
49
49
|
height: var(--header-height);
|
|
50
|
-
/* background-color: white; */
|
|
51
|
-
/* box-shadow: var(--shadow-sm); */
|
|
52
50
|
z-index: 100;
|
|
53
51
|
display: flex;
|
|
54
52
|
justify-content: flex-end;
|
|
55
53
|
align-items: center;
|
|
56
54
|
padding: 0 1.5rem;
|
|
57
55
|
}
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
.container {
|
|
60
58
|
width: 90%;
|
|
61
59
|
max-width: 800px;
|
|
@@ -63,13 +61,13 @@
|
|
|
63
61
|
padding: 2rem 1.5rem;
|
|
64
62
|
margin: 0 auto;
|
|
65
63
|
}
|
|
66
|
-
|
|
64
|
+
|
|
67
65
|
.logo {
|
|
68
66
|
margin-bottom: 2rem;
|
|
69
67
|
position: relative;
|
|
70
68
|
display: inline-block;
|
|
71
69
|
}
|
|
72
|
-
|
|
70
|
+
|
|
73
71
|
.logo::before {
|
|
74
72
|
content: '';
|
|
75
73
|
position: absolute;
|
|
@@ -82,30 +80,30 @@
|
|
|
82
80
|
z-index: -1;
|
|
83
81
|
opacity: 0.6;
|
|
84
82
|
}
|
|
85
|
-
|
|
83
|
+
|
|
86
84
|
h1 {
|
|
87
85
|
font-size: 3.5rem;
|
|
88
86
|
font-weight: 800;
|
|
89
87
|
margin-bottom: 1rem;
|
|
90
88
|
letter-spacing: -0.05em;
|
|
91
89
|
}
|
|
92
|
-
|
|
90
|
+
|
|
93
91
|
h1 .py-blue {
|
|
94
92
|
color: var(--python-blue);
|
|
95
93
|
}
|
|
96
|
-
|
|
94
|
+
|
|
97
95
|
h1 .py-yellow {
|
|
98
96
|
color: var(--python-yellow);
|
|
99
97
|
position: relative;
|
|
100
98
|
}
|
|
101
|
-
|
|
99
|
+
|
|
102
100
|
.tagline {
|
|
103
101
|
font-size: 1.4rem;
|
|
104
102
|
margin-bottom: 2.5rem;
|
|
105
103
|
color: var(--gray-600);
|
|
106
104
|
font-weight: 500;
|
|
107
105
|
}
|
|
108
|
-
|
|
106
|
+
|
|
109
107
|
p {
|
|
110
108
|
font-size: 1.2rem;
|
|
111
109
|
margin-bottom: 2rem;
|
|
@@ -114,7 +112,7 @@
|
|
|
114
112
|
margin-left: auto;
|
|
115
113
|
margin-right: auto;
|
|
116
114
|
}
|
|
117
|
-
|
|
115
|
+
|
|
118
116
|
.url-pattern {
|
|
119
117
|
display: inline-block;
|
|
120
118
|
padding: 1rem 1.5rem;
|
|
@@ -128,14 +126,14 @@
|
|
|
128
126
|
box-shadow: var(--shadow-sm);
|
|
129
127
|
word-break: break-all;
|
|
130
128
|
}
|
|
131
|
-
|
|
132
|
-
.
|
|
129
|
+
|
|
130
|
+
.section-title {
|
|
133
131
|
font-weight: 600;
|
|
134
132
|
color: var(--dark);
|
|
135
133
|
margin-bottom: 1rem;
|
|
136
134
|
font-size: 1.3rem;
|
|
137
135
|
}
|
|
138
|
-
|
|
136
|
+
|
|
139
137
|
.examples-list {
|
|
140
138
|
list-style: none;
|
|
141
139
|
background-color: white;
|
|
@@ -146,7 +144,7 @@
|
|
|
146
144
|
text-align: left;
|
|
147
145
|
width: 100%;
|
|
148
146
|
}
|
|
149
|
-
|
|
147
|
+
|
|
150
148
|
.examples-list li {
|
|
151
149
|
margin-bottom: 1rem;
|
|
152
150
|
padding-bottom: 1rem;
|
|
@@ -154,7 +152,7 @@
|
|
|
154
152
|
position: relative;
|
|
155
153
|
padding-left: 1.5rem;
|
|
156
154
|
}
|
|
157
|
-
|
|
155
|
+
|
|
158
156
|
.examples-list li::before {
|
|
159
157
|
content: '→';
|
|
160
158
|
position: absolute;
|
|
@@ -162,13 +160,13 @@
|
|
|
162
160
|
color: var(--python-green);
|
|
163
161
|
font-weight: bold;
|
|
164
162
|
}
|
|
165
|
-
|
|
163
|
+
|
|
166
164
|
.examples-list li:last-child {
|
|
167
165
|
margin-bottom: 0;
|
|
168
166
|
padding-bottom: 0;
|
|
169
167
|
border-bottom: none;
|
|
170
168
|
}
|
|
171
|
-
|
|
169
|
+
|
|
172
170
|
.examples-list a {
|
|
173
171
|
color: var(--python-blue);
|
|
174
172
|
text-decoration: none;
|
|
@@ -178,12 +176,47 @@
|
|
|
178
176
|
word-break: break-all;
|
|
179
177
|
display: inline-block;
|
|
180
178
|
}
|
|
181
|
-
|
|
179
|
+
|
|
182
180
|
.examples-list a:hover {
|
|
183
181
|
text-decoration: underline;
|
|
184
182
|
color: var(--python-green);
|
|
185
183
|
}
|
|
186
|
-
|
|
184
|
+
|
|
185
|
+
.supported-packages {
|
|
186
|
+
list-style: none;
|
|
187
|
+
background-color: white;
|
|
188
|
+
border-radius: 10px;
|
|
189
|
+
padding: 1.5rem;
|
|
190
|
+
margin-bottom: 2.5rem;
|
|
191
|
+
box-shadow: var(--shadow-md);
|
|
192
|
+
text-align: left;
|
|
193
|
+
width: 100%;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.supported-packages li {
|
|
197
|
+
margin-bottom: 1rem;
|
|
198
|
+
padding-bottom: 1rem;
|
|
199
|
+
border-bottom: 1px solid var(--gray-200);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.supported-packages li:last-child {
|
|
203
|
+
margin-bottom: 0;
|
|
204
|
+
padding-bottom: 0;
|
|
205
|
+
border-bottom: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.package-name {
|
|
209
|
+
font-weight: 600;
|
|
210
|
+
font-size: 1rem;
|
|
211
|
+
text-decoration: none;
|
|
212
|
+
transition: color 0.2s ease;
|
|
213
|
+
color: var(--python-blue);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.package-name:hover {
|
|
217
|
+
text-decoration: underline;
|
|
218
|
+
}
|
|
219
|
+
|
|
187
220
|
.github-link {
|
|
188
221
|
display: inline-flex;
|
|
189
222
|
align-items: center;
|
|
@@ -198,73 +231,82 @@
|
|
|
198
231
|
border: 1px solid var(--python-blue);
|
|
199
232
|
box-shadow: var(--shadow-sm);
|
|
200
233
|
}
|
|
201
|
-
|
|
234
|
+
|
|
202
235
|
.github-link:hover {
|
|
203
236
|
background: white;
|
|
204
237
|
color: var(--python-blue);
|
|
205
238
|
transform: translateY(-1px);
|
|
206
239
|
box-shadow: var(--shadow-md);
|
|
207
240
|
}
|
|
208
|
-
|
|
241
|
+
|
|
209
242
|
.github-link svg {
|
|
210
243
|
margin-right: 0.5rem;
|
|
211
244
|
}
|
|
212
|
-
|
|
245
|
+
|
|
213
246
|
.footer {
|
|
214
247
|
margin-top: 3rem;
|
|
215
248
|
color: var(--gray-600);
|
|
216
249
|
font-size: 0.9rem;
|
|
217
250
|
}
|
|
218
|
-
|
|
251
|
+
|
|
219
252
|
@media (max-width: 600px) {
|
|
220
253
|
h1 {
|
|
221
254
|
font-size: 2.8rem;
|
|
222
255
|
}
|
|
223
|
-
|
|
256
|
+
|
|
224
257
|
.tagline {
|
|
225
258
|
font-size: 1.2rem;
|
|
226
259
|
}
|
|
227
|
-
|
|
260
|
+
|
|
228
261
|
p {
|
|
229
262
|
font-size: 1.1rem;
|
|
230
263
|
}
|
|
231
|
-
|
|
264
|
+
|
|
232
265
|
.url-pattern {
|
|
233
266
|
font-size: 0.9rem;
|
|
234
267
|
padding: 0.8rem 1rem;
|
|
235
268
|
}
|
|
236
|
-
|
|
269
|
+
|
|
237
270
|
.logo::before {
|
|
238
271
|
width: 50px;
|
|
239
272
|
height: 50px;
|
|
240
273
|
top: -12px;
|
|
241
274
|
left: -20px;
|
|
242
275
|
}
|
|
243
|
-
|
|
276
|
+
|
|
244
277
|
.examples-list {
|
|
245
278
|
padding: 1.2rem 1rem;
|
|
246
279
|
}
|
|
247
|
-
|
|
280
|
+
|
|
248
281
|
.examples-list a {
|
|
249
282
|
font-size: 0.85rem;
|
|
250
283
|
}
|
|
251
|
-
|
|
284
|
+
|
|
252
285
|
.examples-list li {
|
|
253
286
|
padding-left: 1.2rem;
|
|
254
287
|
margin-bottom: 0.8rem;
|
|
255
288
|
padding-bottom: 0.8rem;
|
|
256
289
|
}
|
|
257
|
-
|
|
290
|
+
|
|
291
|
+
.supported-packages {
|
|
292
|
+
padding: 1.2rem 1rem;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.supported-packages li {
|
|
296
|
+
margin-bottom: 0.8rem;
|
|
297
|
+
padding-bottom: 0.8rem;
|
|
298
|
+
}
|
|
299
|
+
|
|
258
300
|
.github-link {
|
|
259
301
|
padding: 0.5rem 0.8rem;
|
|
260
302
|
font-size: 0.8rem;
|
|
261
303
|
}
|
|
262
|
-
|
|
304
|
+
|
|
263
305
|
.github-link svg {
|
|
264
306
|
width: 18px;
|
|
265
307
|
height: 18px;
|
|
266
308
|
}
|
|
267
|
-
|
|
309
|
+
|
|
268
310
|
.header {
|
|
269
311
|
padding: 0 1rem;
|
|
270
312
|
}
|
|
@@ -285,13 +327,21 @@
|
|
|
285
327
|
<h1><span class="py-blue">pyref</span>.<span class="py-yellow">dev</span></h1>
|
|
286
328
|
</div>
|
|
287
329
|
<p class="tagline">A fast, convenient way to access Python reference docs.</p>
|
|
288
|
-
<p>Quickly jump to the official documentation for
|
|
330
|
+
<p>Quickly jump to the official documentation for Python standard library modules and popular packages by using a simple URL pattern:</p>
|
|
289
331
|
<div class="url-pattern">https://pyref.dev/<fully.qualified.symbol.name></div>
|
|
290
|
-
|
|
332
|
+
|
|
333
|
+
<p class="section-title">Examples</p>
|
|
291
334
|
<ul class="examples-list">
|
|
292
335
|
<li><a href="https://pyref.dev/json">https://pyref.dev/json</a></li>
|
|
293
336
|
<li><a href="https://pyref.dev/pathlib.Path">https://pyref.dev/pathlib.Path</a></li>
|
|
294
337
|
<li><a href="https://pyref.dev/datetime.datetime.strftime">https://pyref.dev/datetime.datetime.strftime</a></li>
|
|
338
|
+
<li><a href="https://pyref.dev/numpy.array">https://pyref.dev/numpy.array</a></li>
|
|
339
|
+
</ul>
|
|
340
|
+
|
|
341
|
+
<p class="section-title">Supported Packages</p>
|
|
342
|
+
<ul class="supported-packages">
|
|
343
|
+
<li><a href="https://docs.python.org/3/library/" class="package-name python">Python Standard Library</a></li>
|
|
344
|
+
<li><a href="https://numpy.org/doc/stable/reference/index.html" class="package-name numpy">NumPy</a></li>
|
|
295
345
|
</ul>
|
|
296
346
|
</div>
|
|
297
347
|
</body>
|
|
@@ -16,11 +16,12 @@ classifiers = [
|
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
"Programming Language :: Python",
|
|
19
|
-
"Programming Language :: Python :: 3.9",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
23
22
|
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Programming Language :: Python :: Free Threading",
|
|
24
25
|
"Programming Language :: Python :: 3 :: Only",
|
|
25
26
|
"Topic :: Documentation",
|
|
26
27
|
]
|
|
@@ -60,3 +61,8 @@ version-file = "src/pyrefdev/_version.py"
|
|
|
60
61
|
template = '''
|
|
61
62
|
version = "{version}"
|
|
62
63
|
'''
|
|
64
|
+
|
|
65
|
+
[dependency-groups]
|
|
66
|
+
dev = [
|
|
67
|
+
"pytest>=8.4.1",
|
|
68
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "2025.3"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
from yib import yconsole
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
console = yconsole.Console(stderr=True)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclasses.dataclass
|
|
10
|
+
class Package:
|
|
11
|
+
name: str
|
|
12
|
+
index: str # The URL for pyref.dev/<name>.
|
|
13
|
+
crawler_root: str # The seed and root URL for crawler.
|
|
14
|
+
|
|
15
|
+
def is_stdlib(self):
|
|
16
|
+
return self.name == "__python__"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
SUPPORTED_PACKAGES: dict[str, Package] = {
|
|
20
|
+
"__python__": Package(
|
|
21
|
+
name="__python__",
|
|
22
|
+
index="https://docs.python.org/3/library",
|
|
23
|
+
crawler_root="https://docs.python.org/3/",
|
|
24
|
+
),
|
|
25
|
+
"numpy": Package(
|
|
26
|
+
name="numpy",
|
|
27
|
+
index="https://numpy.org/doc/stable/reference/index.html",
|
|
28
|
+
crawler_root="https://numpy.org/doc/stable/reference/",
|
|
29
|
+
),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_packages(package: str | None) -> list[Package]:
|
|
34
|
+
if package is None:
|
|
35
|
+
return list(SUPPORTED_PACKAGES.values())
|
|
36
|
+
else:
|
|
37
|
+
if package not in SUPPORTED_PACKAGES:
|
|
38
|
+
console.fatal(f"No package named {package}")
|
|
39
|
+
return [SUPPORTED_PACKAGES[package]]
|
|
@@ -9,34 +9,52 @@ from urllib import parse, request
|
|
|
9
9
|
import bs4
|
|
10
10
|
from rich.progress import Progress, TaskID
|
|
11
11
|
|
|
12
|
-
from .config import console,
|
|
12
|
+
from pyrefdev.config import console, get_packages
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def crawl_docs(
|
|
15
|
+
def crawl_docs(
|
|
16
|
+
output_directory: Path | None = None,
|
|
17
|
+
*,
|
|
18
|
+
package: str | None = None,
|
|
19
|
+
num_threads: int = 2,
|
|
20
|
+
) -> None:
|
|
16
21
|
"""Crawl the docs into a local directory."""
|
|
17
22
|
if output_directory:
|
|
18
23
|
if output_directory.exists():
|
|
19
24
|
if not output_directory.is_dir():
|
|
20
|
-
|
|
25
|
+
console.fatal(f"{output_directory} is not a directory")
|
|
21
26
|
else:
|
|
22
27
|
output_directory = Path(tempfile.mkdtemp(prefix="pyref.dev."))
|
|
23
28
|
|
|
24
29
|
console.print(f"Crawling documents into {output_directory}")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
packages = get_packages(package)
|
|
31
|
+
with Progress(console=console) as progress:
|
|
32
|
+
if len(packages) > 1:
|
|
33
|
+
task = progress.add_task(
|
|
34
|
+
f"Crawling {len(packages)} packages", total=len(packages)
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
task = None
|
|
38
|
+
for package in packages:
|
|
39
|
+
try:
|
|
40
|
+
subdir = output_directory / package.name
|
|
41
|
+
if subdir.exists():
|
|
42
|
+
console.print(f"{subdir} already exists, skipping.")
|
|
43
|
+
continue
|
|
44
|
+
subdir.mkdir(parents=True)
|
|
45
|
+
crawler = _Crawler(
|
|
46
|
+
progress, output_directory / package.name, package.crawler_root
|
|
47
|
+
)
|
|
48
|
+
crawler.crawl(num_threads=num_threads)
|
|
49
|
+
crawler.save_url_map(output_directory / f"{package.name}.json")
|
|
50
|
+
finally:
|
|
51
|
+
if task is not None:
|
|
52
|
+
progress.advance(task)
|
|
36
53
|
|
|
37
54
|
|
|
38
55
|
class _Crawler:
|
|
39
|
-
def __init__(self, output_directory: Path, root_url: str):
|
|
56
|
+
def __init__(self, progress: Progress, output_directory: Path, root_url: str):
|
|
57
|
+
self._progress = progress
|
|
40
58
|
self._output_directory = output_directory
|
|
41
59
|
self._root_url = root_url
|
|
42
60
|
|
|
@@ -51,16 +69,16 @@ class _Crawler:
|
|
|
51
69
|
self._to_crawl_queue.put(self._root_url)
|
|
52
70
|
self._seen_urls.add(self._root_url)
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
task = self._progress.add_task(f"Crawling {self._root_url}")
|
|
73
|
+
threads = []
|
|
74
|
+
for _ in range(num_threads):
|
|
75
|
+
thread = threading.Thread(
|
|
76
|
+
target=self._crawl_thread, args=(task,), daemon=True
|
|
77
|
+
)
|
|
78
|
+
thread.start()
|
|
79
|
+
threads.append(thread)
|
|
80
|
+
self._to_crawl_queue.join()
|
|
81
|
+
self._progress.update(task, visible=False)
|
|
64
82
|
|
|
65
83
|
def save_url_map(self, output: Path) -> None:
|
|
66
84
|
metadata = {
|
|
@@ -69,7 +87,7 @@ class _Crawler:
|
|
|
69
87
|
}
|
|
70
88
|
output.write_text(json.dumps(metadata))
|
|
71
89
|
|
|
72
|
-
def _crawl_thread(self,
|
|
90
|
+
def _crawl_thread(self, task: TaskID) -> None:
|
|
73
91
|
while True:
|
|
74
92
|
url = self._to_crawl_queue.get()
|
|
75
93
|
try:
|
|
@@ -77,7 +95,7 @@ class _Crawler:
|
|
|
77
95
|
finally:
|
|
78
96
|
if saved is not None:
|
|
79
97
|
self._crawled_url_to_files[url] = saved
|
|
80
|
-
|
|
98
|
+
self._progress.update(
|
|
81
99
|
task,
|
|
82
100
|
total=len(self._seen_urls),
|
|
83
101
|
completed=len(self._crawled_url_to_files),
|