pyrefdev 2025.6__tar.gz → 2025.7__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 (120) hide show
  1. {pyrefdev-2025.6 → pyrefdev-2025.7}/CHANGELOG.md +5 -0
  2. {pyrefdev-2025.6 → pyrefdev-2025.7}/CLAUDE.md +4 -1
  3. {pyrefdev-2025.6 → pyrefdev-2025.7}/PKG-INFO +14 -2
  4. {pyrefdev-2025.6 → pyrefdev-2025.7}/README.md +13 -1
  5. pyrefdev-2025.7/src/pyrefdev/_version.py +1 -0
  6. pyrefdev-2025.7/src/pyrefdev/server.py +116 -0
  7. pyrefdev-2025.7/static/styles.css +693 -0
  8. {pyrefdev-2025.6 → pyrefdev-2025.7}/templates/index.html +28 -414
  9. pyrefdev-2025.7/templates/search.html +152 -0
  10. pyrefdev-2025.6/src/pyrefdev/_version.py +0 -1
  11. pyrefdev-2025.6/src/pyrefdev/server.py +0 -35
  12. {pyrefdev-2025.6 → pyrefdev-2025.7}/.github/workflows/ci.yml +0 -0
  13. {pyrefdev-2025.6 → pyrefdev-2025.7}/.github/workflows/pypi_upload.yml +0 -0
  14. {pyrefdev-2025.6 → pyrefdev-2025.7}/.gitignore +0 -0
  15. {pyrefdev-2025.6 → pyrefdev-2025.7}/.pre-commit-config.yaml +0 -0
  16. {pyrefdev-2025.6 → pyrefdev-2025.7}/LICENSE +0 -0
  17. {pyrefdev-2025.6 → pyrefdev-2025.7}/pyproject.toml +0 -0
  18. {pyrefdev-2025.6 → pyrefdev-2025.7}/pyrefdev.service +0 -0
  19. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/__init__.py +0 -0
  20. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/__main__.py +0 -0
  21. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/config.py +0 -0
  22. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/__main__.py +0 -0
  23. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/add_docs.py +0 -0
  24. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/crawl_docs.py +0 -0
  25. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/parse_docs.py +0 -0
  26. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/requests.py +0 -0
  27. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/schema.py +0 -0
  28. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/update_docs.py +0 -0
  29. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/indexer/update_landing_page.py +0 -0
  30. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/__init__.py +0 -0
  31. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/__python__.py +0 -0
  32. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/aiohttp.py +0 -0
  33. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/aioitertools.py +0 -0
  34. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/aiosignal.py +0 -0
  35. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/anyio.py +0 -0
  36. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/apache-beam.py +0 -0
  37. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/arrow.py +0 -0
  38. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/attrs.py +0 -0
  39. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/azure-identity.py +0 -0
  40. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/boto3.py +0 -0
  41. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/botocore.py +0 -0
  42. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/cachetools.py +0 -0
  43. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/cattrs.py +0 -0
  44. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/charset-normalizer.py +0 -0
  45. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/click.py +0 -0
  46. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/coverage.py +0 -0
  47. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/cryptography.py +0 -0
  48. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/cyclopts.py +0 -0
  49. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/distlib.py +0 -0
  50. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/fastapi.py +0 -0
  51. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/fastjsonschema.py +0 -0
  52. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/filelock.py +0 -0
  53. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/flask.py +0 -0
  54. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/frozenlist.py +0 -0
  55. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/fsspec.py +0 -0
  56. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/google-api-core.py +0 -0
  57. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/google-auth.py +0 -0
  58. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/grpcio.py +0 -0
  59. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/h11.py +0 -0
  60. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/h5py.py +0 -0
  61. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/httplib2.py +0 -0
  62. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/ipython.py +0 -0
  63. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/jinja2.py +0 -0
  64. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/jsonschema.py +0 -0
  65. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/jupyter-core.py +0 -0
  66. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/libcst.py +0 -0
  67. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/markupsafe.py +0 -0
  68. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/matplotlib.py +0 -0
  69. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/more-itertools.py +0 -0
  70. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/multidict.py +0 -0
  71. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/nbformat.py +0 -0
  72. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/networkx.py +0 -0
  73. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/numba.py +0 -0
  74. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/numpy.py +0 -0
  75. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/oauthlib.py +0 -0
  76. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/packaging.py +0 -0
  77. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pandas.py +0 -0
  78. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pathspec.py +0 -0
  79. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pillow.py +0 -0
  80. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/platformdirs.py +0 -0
  81. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pluggy.py +0 -0
  82. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/propcache.py +0 -0
  83. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/protobuf.py +0 -0
  84. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/psutil.py +0 -0
  85. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pyOpenSSL.py +0 -0
  86. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pyarrow.py +0 -0
  87. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pyasn1.py +0 -0
  88. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pydantic-core.py +0 -0
  89. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pydantic.py +0 -0
  90. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pygments.py +0 -0
  91. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pyjwt.py +0 -0
  92. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pyparsing.py +0 -0
  93. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/pytest.py +0 -0
  94. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/python-dateutil.py +0 -0
  95. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/requests-oauthlib.py +0 -0
  96. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/requests-toolbelt.py +0 -0
  97. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/requests.py +0 -0
  98. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/rich.py +0 -0
  99. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/rpds-py.py +0 -0
  100. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/s3fs.py +0 -0
  101. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/scikit-learn.py +0 -0
  102. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/scipy.py +0 -0
  103. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/semver.py +0 -0
  104. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/setuptools.py +0 -0
  105. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/six.py +0 -0
  106. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/sniffio.py +0 -0
  107. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/sqlalchemy.py +0 -0
  108. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/tomlkit.py +0 -0
  109. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/torch.py +0 -0
  110. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/tox.py +0 -0
  111. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/trio.py +0 -0
  112. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/typing-extensions.py +0 -0
  113. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/urllib3.py +0 -0
  114. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/werkzeug.py +0 -0
  115. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/wrapt.py +0 -0
  116. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/yarl.py +0 -0
  117. {pyrefdev-2025.6 → pyrefdev-2025.7}/src/pyrefdev/mapping/yib.py +0 -0
  118. {pyrefdev-2025.6 → pyrefdev-2025.7}/static/logo128.png +0 -0
  119. {pyrefdev-2025.6 → pyrefdev-2025.7}/tests/test_pyrefdev.py +0 -0
  120. {pyrefdev-2025.6 → pyrefdev-2025.7}/uv.lock +0 -0
@@ -4,6 +4,11 @@
4
4
 
5
5
  Nothing noticeable unreleased.
6
6
 
7
+ ## v2025.7
8
+
9
+ - Added search to [pyref.dev](https://pyref.dev).
10
+ - When the symbol is not found, redirect to the search page.
11
+
7
12
  ## v2025.6
8
13
 
9
14
  - Added a `-p/--print` flag to the `pyrefdev` CLI to print the URL instead of opening it in the browser.
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
6
6
 
7
7
  The project has several components:
8
8
 
9
- 1. **Web Server** (`src/pyrefdev/server.py`): FastAPI application that serves redirects based on symbol lookup. `index.html` is the landing page of the server
9
+ 1. **Web Server** (`src/pyrefdev/server.py`): FastAPI application that serves redirects based on symbol lookup. `templates/index.html` is the landing page of the server, `static/styles.css` contains CSS styles
10
10
  2. **CLI Tool** (`src/pyrefdev/__main__.py`): Command-line interface that opens documentation in browser
11
11
  3. **Indexer** (`src/pyrefdev/indexer/`): Tools for crawling, parsing, and managing documentation mappings
12
12
  4. **Mapping System** (`src/pyrefdev/mapping/`): Individual Python files per package containing symbol-to-URL mappings
@@ -48,4 +48,7 @@ pyrefdev-indexer update-landing-page
48
48
 
49
49
  ## Important Notes
50
50
 
51
+ - Do not add tests unless explictly asked to.
52
+ - When creating a new file with content, ensure the file has an extra new line at the end.
53
+ - Do NOT add redundant comments describe what the cod does. When needed, DO add comments that explains "why".
51
54
  - Server deployment uses systemd and is configured in `pyrefdev.service`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrefdev
3
- Version: 2025.6
3
+ Version: 2025.7
4
4
  Summary: The entry point to Python reference docs
5
5
  Project-URL: Homepage, https://pyref.dev
6
6
  Project-URL: Source, https://github.com/mangoumbrella/pyref.dev
@@ -51,7 +51,19 @@ It allows you to quickly jump to the official documentation for Python standard
51
51
  https://pyref.dev/<fully.qualified.symbol.name>
52
52
  ```
53
53
 
54
- Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI tool.
54
+ You can also search for symbols using:
55
+
56
+ ```
57
+ https://pyref.dev/is?symbol=<SYMBOL>
58
+ ```
59
+
60
+ And if you are feeling lucky, ask it to redirect to the first result:
61
+
62
+ ```
63
+ https://pyref.dev/is?lucky=true&symbol=<SYMBOL>
64
+ ```
65
+
66
+ Lastly, you can `pip install pyrefdev` and run the `pyrefdev` CLI tool.
55
67
 
56
68
  ## Examples
57
69
 
@@ -14,7 +14,19 @@ It allows you to quickly jump to the official documentation for Python standard
14
14
  https://pyref.dev/<fully.qualified.symbol.name>
15
15
  ```
16
16
 
17
- Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI tool.
17
+ You can also search for symbols using:
18
+
19
+ ```
20
+ https://pyref.dev/is?symbol=<SYMBOL>
21
+ ```
22
+
23
+ And if you are feeling lucky, ask it to redirect to the first result:
24
+
25
+ ```
26
+ https://pyref.dev/is?lucky=true&symbol=<SYMBOL>
27
+ ```
28
+
29
+ Lastly, you can `pip install pyrefdev` and run the `pyrefdev` CLI tool.
18
30
 
19
31
  ## Examples
20
32
 
@@ -0,0 +1 @@
1
+ version = "2025.7"
@@ -0,0 +1,116 @@
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import RedirectResponse, PlainTextResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.templating import Jinja2Templates
5
+ import importlib.metadata
6
+ from urllib import parse
7
+
8
+ from pyrefdev.mapping import MAPPING
9
+
10
+
11
+ app = FastAPI()
12
+ app.mount("/static", StaticFiles(directory="static"), name="static")
13
+
14
+ templates = Jinja2Templates(directory="templates")
15
+
16
+
17
+ @app.get("/")
18
+ async def root(request: Request):
19
+ try:
20
+ version = importlib.metadata.version("pyrefdev")
21
+ version = version.split("+")[0] # Strip the git hash
22
+ except importlib.metadata.PackageNotFoundError:
23
+ version = "unknown"
24
+
25
+ return templates.TemplateResponse(
26
+ "index.html", {"request": request, "version": version}
27
+ )
28
+
29
+
30
+ @app.get("/is")
31
+ async def search_symbols(request: Request, symbol: str = "", lucky: bool = False):
32
+ if not symbol:
33
+ return templates.TemplateResponse(
34
+ "search.html", {"request": request, "symbol": "", "results": []}
35
+ )
36
+
37
+ # Search by substring for now.
38
+ results = []
39
+ symbol_lower = symbol.lower()
40
+ for key in MAPPING.keys():
41
+ if symbol_lower in key.lower():
42
+ fragment = parse.urlparse(MAPPING[key]).fragment
43
+ if fragment.lower() == key.lower():
44
+ candidate = fragment
45
+ else:
46
+ candidate = key
47
+ results.append({"symbol": candidate, "url": MAPPING[key]})
48
+
49
+ def ranking_key(result):
50
+ symbol_path = result["symbol"]
51
+ components = symbol_path.split(".")
52
+ num_components = len(components)
53
+
54
+ if symbol_path.lower() == symbol_lower:
55
+ # Prioritize exact case matches over case-insensitive matches (only if search term has uppercase)
56
+ case_match_priority = 0 if (symbol != symbol_lower and symbol_path == symbol) else 1
57
+ return (0, 0, case_match_priority, num_components, len(symbol_path), symbol_path)
58
+
59
+ # Check for exact component matches (case-insensitive)
60
+ exact_component_matches = []
61
+ exact_case_component_matches = []
62
+ for i, component in enumerate(components):
63
+ if component.lower() == symbol_lower:
64
+ # Position from right (0 = rightmost, higher = more left)
65
+ position_from_right = len(components) - 1 - i
66
+ exact_component_matches.append(position_from_right)
67
+
68
+ # Check if it's also an exact case match (only if search term has uppercase)
69
+ if symbol != symbol_lower and component == symbol:
70
+ exact_case_component_matches.append(position_from_right)
71
+
72
+ if exact_case_component_matches:
73
+ # Prioritize rightmost position, then exact case matches
74
+ best_position = min(exact_case_component_matches)
75
+ return (1, best_position, 0, num_components, len(symbol_path), symbol_path)
76
+ elif exact_component_matches:
77
+ # Case-insensitive exact matches
78
+ best_position = min(exact_component_matches)
79
+ return (1, best_position, 1, num_components, len(symbol_path), symbol_path)
80
+
81
+ # Check for component substring matches
82
+ component_substring_matches = []
83
+ for i, component in enumerate(components):
84
+ if symbol_lower in component.lower():
85
+ position_from_right = len(components) - 1 - i
86
+ component_substring_matches.append(position_from_right)
87
+
88
+ if component_substring_matches:
89
+ best_position = min(component_substring_matches)
90
+ return (2, best_position, 0, num_components, len(symbol_path), symbol_path)
91
+
92
+ # Fallback to general substring match
93
+ return (3, 0, 0, num_components, len(symbol_path), symbol_path)
94
+
95
+ results.sort(key=ranking_key)
96
+
97
+ # If lucky=true and we have results, redirect to the first (best) result
98
+ if lucky and results:
99
+ return RedirectResponse(results[0]["url"])
100
+
101
+ return templates.TemplateResponse(
102
+ "search.html", {"request": request, "symbol": symbol, "results": results}
103
+ )
104
+
105
+
106
+ @app.get("/{symbol}")
107
+ async def redirects(symbol: str, lucky: bool = False):
108
+ if url := MAPPING.get(symbol):
109
+ return RedirectResponse(url)
110
+ if url := MAPPING.get(symbol.lower()):
111
+ return RedirectResponse(url)
112
+
113
+ if lucky:
114
+ return RedirectResponse(f"/is?symbol={symbol}&lucky=true")
115
+ else:
116
+ return RedirectResponse(f"/is?symbol={symbol}")