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.
Files changed (25) hide show
  1. pyrefdev-2025.3/.github/workflows/ci.yml +39 -0
  2. {pyrefdev-2025.2 → pyrefdev-2025.3}/PKG-INFO +23 -2
  3. {pyrefdev-2025.2 → pyrefdev-2025.3}/README.md +20 -0
  4. {pyrefdev-2025.2 → pyrefdev-2025.3}/index.html +92 -42
  5. {pyrefdev-2025.2 → pyrefdev-2025.3}/pyproject.toml +7 -1
  6. pyrefdev-2025.3/src/pyrefdev/_version.py +1 -0
  7. pyrefdev-2025.3/src/pyrefdev/config.py +39 -0
  8. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/__main__.py +1 -1
  9. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/crawl_docs.py +45 -27
  10. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/indexer/parse_docs.py +71 -37
  11. pyrefdev-2025.3/src/pyrefdev/mapping/__init__.py +21 -0
  12. pyrefdev-2025.3/src/pyrefdev/mapping/numpy.py +2225 -0
  13. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/server.py +1 -1
  14. pyrefdev-2025.3/tests/test_mapping.py +6 -0
  15. {pyrefdev-2025.2 → pyrefdev-2025.3}/uv.lock +120 -109
  16. pyrefdev-2025.2/src/pyrefdev/_version.py +0 -1
  17. pyrefdev-2025.2/src/pyrefdev/indexer/config.py +0 -9
  18. {pyrefdev-2025.2 → pyrefdev-2025.3}/.github/workflows/pypi_upload.yml +0 -0
  19. {pyrefdev-2025.2 → pyrefdev-2025.3}/.gitignore +0 -0
  20. {pyrefdev-2025.2 → pyrefdev-2025.3}/CHANGELOG.md +0 -0
  21. {pyrefdev-2025.2 → pyrefdev-2025.3}/LICENSE +0 -0
  22. {pyrefdev-2025.2 → pyrefdev-2025.3}/pyrefdev.service +0 -0
  23. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/__init__.py +0 -0
  24. {pyrefdev-2025.2 → pyrefdev-2025.3}/src/pyrefdev/__main__.py +0 -0
  25. /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.2
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
- .examples-title {
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 any Python standard library module, class, or function by using a simple URL pattern:</p>
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/&lt;fully.qualified.symbol.name&gt;</div>
290
- <p class="examples-title">Examples</p>
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]]
@@ -3,7 +3,7 @@ import cyclopts
3
3
  import pyrefdev
4
4
  from pyrefdev.indexer.crawl_docs import crawl_docs
5
5
  from pyrefdev.indexer.parse_docs import parse_docs
6
- from pyrefdev.indexer.config import console
6
+ from pyrefdev.config import console
7
7
 
8
8
 
9
9
  app = cyclopts.App(
@@ -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, SUPPORTED_DOCS
12
+ from pyrefdev.config import console, get_packages
13
13
 
14
14
 
15
- def crawl_docs(output_directory: Path | None = None, num_threads: int = 2) -> None:
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
- pass
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
- for name, website in SUPPORTED_DOCS.items():
26
- subdir = output_directory / name
27
- if subdir.exists():
28
- console.fatal(f"{subdir} already exists.")
29
- subdir.mkdir(parents=True)
30
- crawler = _Crawler(
31
- output_directory / name,
32
- website,
33
- )
34
- crawler.crawl(num_threads=num_threads)
35
- crawler.save_url_map(output_directory / f"{name}.json")
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
- with Progress(console=console) as progress:
55
- task = progress.add_task(f"Crawling {self._root_url}")
56
- threads = []
57
- for _ in range(num_threads):
58
- thread = threading.Thread(
59
- target=self._crawl_thread, args=(progress, task), daemon=True
60
- )
61
- thread.start()
62
- threads.append(thread)
63
- self._to_crawl_queue.join()
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, progress: Progress, task: TaskID) -> None:
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
- progress.update(
98
+ self._progress.update(
81
99
  task,
82
100
  total=len(self._seen_urls),
83
101
  completed=len(self._crawled_url_to_files),