pyrefdev 2025.3__tar.gz → 2025.4__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 (32) hide show
  1. {pyrefdev-2025.3 → pyrefdev-2025.4}/.gitignore +2 -0
  2. pyrefdev-2025.4/CHANGELOG.md +21 -0
  3. {pyrefdev-2025.3 → pyrefdev-2025.4}/PKG-INFO +11 -4
  4. {pyrefdev-2025.3 → pyrefdev-2025.4}/README.md +10 -3
  5. {pyrefdev-2025.3 → pyrefdev-2025.4}/index.html +8 -3
  6. pyrefdev-2025.4/src/pyrefdev/_version.py +1 -0
  7. pyrefdev-2025.4/src/pyrefdev/config.py +67 -0
  8. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/indexer/__main__.py +4 -0
  9. pyrefdev-2025.4/src/pyrefdev/indexer/add_docs.py +49 -0
  10. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/indexer/crawl_docs.py +6 -28
  11. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/indexer/parse_docs.py +6 -6
  12. pyrefdev-2025.4/src/pyrefdev/indexer/update_docs.py +16 -0
  13. pyrefdev-2025.4/src/pyrefdev/mapping/dateutil.py +90 -0
  14. pyrefdev-2025.4/src/pyrefdev/mapping/pandas.py +2135 -0
  15. pyrefdev-2025.4/src/pyrefdev/mapping/requests.py +155 -0
  16. pyrefdev-2025.4/src/pyrefdev/mapping/urllib3.py +264 -0
  17. pyrefdev-2025.3/CHANGELOG.md +0 -9
  18. pyrefdev-2025.3/src/pyrefdev/_version.py +0 -1
  19. pyrefdev-2025.3/src/pyrefdev/config.py +0 -39
  20. {pyrefdev-2025.3 → pyrefdev-2025.4}/.github/workflows/ci.yml +0 -0
  21. {pyrefdev-2025.3 → pyrefdev-2025.4}/.github/workflows/pypi_upload.yml +0 -0
  22. {pyrefdev-2025.3 → pyrefdev-2025.4}/LICENSE +0 -0
  23. {pyrefdev-2025.3 → pyrefdev-2025.4}/pyproject.toml +0 -0
  24. {pyrefdev-2025.3 → pyrefdev-2025.4}/pyrefdev.service +0 -0
  25. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/__init__.py +0 -0
  26. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/__main__.py +0 -0
  27. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/mapping/__init__.py +0 -0
  28. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/mapping/__python__.py +0 -0
  29. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/mapping/numpy.py +0 -0
  30. {pyrefdev-2025.3 → pyrefdev-2025.4}/src/pyrefdev/server.py +0 -0
  31. {pyrefdev-2025.3 → pyrefdev-2025.4}/tests/test_mapping.py +0 -0
  32. {pyrefdev-2025.3 → pyrefdev-2025.4}/uv.lock +0 -0
@@ -1,3 +1,5 @@
1
+ .DS_Store
2
+
1
3
  # Hatch-managed version file
2
4
  src/pyrefdev/_version.py
3
5
 
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ - Fix a bug in the crawler where relative paths aren't joined correctly.
6
+ - Support [pandas](https://pandas.pydata.org/docs/reference/index.html)
7
+ - Support [urllib3](https://urllib3.readthedocs.io/en/stable/reference/index.html)
8
+ - Support [requests](https://requests.readthedocs.io/en/latest/)
9
+
10
+ ## v2025.3
11
+
12
+ - Support [numpy](https://numpy.org/doc/stable/reference/index.html).
13
+
14
+ ## v2025.2
15
+
16
+ - Added a new `pyrefdev-indexer` CLI to `crawl-docs` and `parse-docs`. The `mapping.py` is now reproducible and easily updatable when Python 3.14 is released.
17
+ - Update the mappings.
18
+
19
+ ## v2025.1
20
+
21
+ Initial release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrefdev
3
- Version: 2025.3
3
+ Version: 2025.4
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
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
  <a href="https://github.com/mangoumbrella/pyref.dev/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/pyrefdev.svg"></a>
43
43
  </p>
44
44
 
45
- It allows you to quickly jump to the official documentation for any Python standard library module, class, or function by using a simple URL pattern:
45
+ It allows you to quickly jump to the official documentation for Python standard library modules and popular packages by using a simple URL pattern:
46
46
 
47
47
  ```
48
48
  https://pyref.dev/<fully.qualified.symbol.name>
@@ -55,10 +55,17 @@ Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI
55
55
  * https://pyref.dev/json
56
56
  * https://pyref.dev/pathlib.Path
57
57
  * https://pyref.dev/datetime.datetime.strftime
58
+ * https://pyref.dev/numpy.array
58
59
 
59
- For now, the symbols are case-insensitive.
60
+ ## Supported packages
60
61
 
61
- ## Server Setup
62
+ See [pyref.dev](https://pyref.dev/#supported-packages) for the list of supported packages.
63
+
64
+ # Case sensitivity
65
+
66
+ For most of the cases, they are case-insensitive. However, for symbols like `typing.final` and `typing.Final`, you need to access them with the correct case.
67
+
68
+ ## Server setup
62
69
 
63
70
  To set up a new server:
64
71
 
@@ -8,7 +8,7 @@
8
8
  <a href="https://github.com/mangoumbrella/pyref.dev/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/pyrefdev.svg"></a>
9
9
  </p>
10
10
 
11
- It allows you to quickly jump to the official documentation for any Python standard library module, class, or function by using a simple URL pattern:
11
+ It allows you to quickly jump to the official documentation for Python standard library modules and popular packages by using a simple URL pattern:
12
12
 
13
13
  ```
14
14
  https://pyref.dev/<fully.qualified.symbol.name>
@@ -21,10 +21,17 @@ Alternatively, you could also `pip install pyrefdev` and run the `pyrefdev` CLI
21
21
  * https://pyref.dev/json
22
22
  * https://pyref.dev/pathlib.Path
23
23
  * https://pyref.dev/datetime.datetime.strftime
24
+ * https://pyref.dev/numpy.array
24
25
 
25
- For now, the symbols are case-insensitive.
26
+ ## Supported packages
26
27
 
27
- ## Server Setup
28
+ See [pyref.dev](https://pyref.dev/#supported-packages) for the list of supported packages.
29
+
30
+ # Case sensitivity
31
+
32
+ For most of the cases, they are case-insensitive. However, for symbols like `typing.final` and `typing.Final`, you need to access them with the correct case.
33
+
34
+ ## Server setup
28
35
 
29
36
  To set up a new server:
30
37
 
@@ -338,10 +338,15 @@
338
338
  <li><a href="https://pyref.dev/numpy.array">https://pyref.dev/numpy.array</a></li>
339
339
  </ul>
340
340
 
341
- <p class="section-title">Supported Packages</p>
341
+ <p id="supported-packages" class="section-title">Supported Packages</p>
342
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>
343
+ <li><a href="https://docs.python.org/3/library/" class="package-name">Python Standard Library</a></li>
344
+ <li><a href="https://numpy.org/doc/stable/reference/index.html" class="package-name">numpy</a></li>
345
+ <li><a href="https://pandas.pydata.org/docs/reference/index.html" class="package-name">pandas</a></li>
346
+ <li><a href="https://urllib3.readthedocs.io/en/stable/reference/index.html" class="package-name">urllib3</a></li>
347
+ <li><a href="https://requests.readthedocs.io/en/latest/" class="package-name">requests</a></li>
348
+ <li><a href="https://dateutil.readthedocs.io/en/stable/" class="package-name">python-dateutil</a></li>
349
+ <!-- ENTRY-LINE-MARKER -->
345
350
  </ul>
346
351
  </div>
347
352
  </body>
@@ -0,0 +1 @@
1
+ version = "2025.4"
@@ -0,0 +1,67 @@
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
+ package: str
12
+ pypi: str
13
+ index: str # The URL for pyref.dev/<name>.
14
+ crawler_root: str # The seed and root URL for crawler.
15
+
16
+ def is_stdlib(self):
17
+ return self.package == "__python__"
18
+
19
+
20
+ SUPPORTED_PACKAGES: dict[str, Package] = {
21
+ "__python__": Package(
22
+ package="__python__",
23
+ pypi="__python__",
24
+ index="https://docs.python.org/3/library",
25
+ crawler_root="https://docs.python.org/3/",
26
+ ),
27
+ "numpy": Package(
28
+ package="numpy",
29
+ pypi="numpy",
30
+ index="https://numpy.org/doc/stable/reference/index.html",
31
+ crawler_root="https://numpy.org/doc/stable/reference/",
32
+ ),
33
+ "pandas": Package(
34
+ package="pandas",
35
+ pypi="pandas",
36
+ index="https://pandas.pydata.org/docs/reference/index.html",
37
+ crawler_root="https://pandas.pydata.org/docs/reference/",
38
+ ),
39
+ "urllib3": Package(
40
+ package="urllib3",
41
+ pypi="urllib3",
42
+ index="https://urllib3.readthedocs.io/en/stable/reference/index.html",
43
+ crawler_root="https://urllib3.readthedocs.io/en/stable/reference/",
44
+ ),
45
+ "requests": Package(
46
+ package="requests",
47
+ pypi="requests",
48
+ index="https://requests.readthedocs.io/en/latest/",
49
+ crawler_root="https://requests.readthedocs.io/en/latest/",
50
+ ),
51
+ "dateutil": Package(
52
+ package="dateutil",
53
+ pypi="python-dateutil",
54
+ index="https://dateutil.readthedocs.io/en/stable/",
55
+ crawler_root="https://dateutil.readthedocs.io/en/stable/",
56
+ ),
57
+ # ENTRY-LINE-MARKER
58
+ }
59
+
60
+
61
+ def get_packages(package: str | None) -> list[Package]:
62
+ if package is None:
63
+ return list(SUPPORTED_PACKAGES.values())
64
+ else:
65
+ if package not in SUPPORTED_PACKAGES:
66
+ console.fatal(f"No package named {package}")
67
+ return [SUPPORTED_PACKAGES[package]]
@@ -1,8 +1,10 @@
1
1
  import cyclopts
2
2
 
3
3
  import pyrefdev
4
+ from pyrefdev.indexer.add_docs import add_docs
4
5
  from pyrefdev.indexer.crawl_docs import crawl_docs
5
6
  from pyrefdev.indexer.parse_docs import parse_docs
7
+ from pyrefdev.indexer.update_docs import update_docs
6
8
  from pyrefdev.config import console
7
9
 
8
10
 
@@ -12,8 +14,10 @@ app = cyclopts.App(
12
14
  version=pyrefdev.__version__,
13
15
  console=console,
14
16
  )
17
+ app.command(add_docs)
15
18
  app.command(crawl_docs)
16
19
  app.command(parse_docs)
20
+ app.command(update_docs)
17
21
 
18
22
 
19
23
  if __name__ == "__main__":
@@ -0,0 +1,49 @@
1
+ import importlib
2
+ from pathlib import Path
3
+ import re
4
+
5
+ from pyrefdev import config
6
+ from pyrefdev import mapping
7
+ from pyrefdev.config import console
8
+ from pyrefdev.indexer.update_docs import update_docs
9
+
10
+
11
+ _MARKER = re.compile("(\n.*ENTRY-LINE-MARKER.*\n)")
12
+
13
+
14
+ def add_docs(
15
+ package: str,
16
+ *,
17
+ index: str,
18
+ crawler_root: str,
19
+ pypi: str | None = None,
20
+ docs_directory: Path | None = None,
21
+ ) -> None:
22
+ if package in config.SUPPORTED_PACKAGES:
23
+ console.fatal(f"Package exists: {package}")
24
+ pypi = pypi or package
25
+
26
+ config_entry = f"""
27
+ "{package}": Package(
28
+ package="{package}",
29
+ pypi="{pypi}",
30
+ index="{index}",
31
+ crawler_root="{crawler_root}",
32
+ ),"""
33
+ config_file = Path(config.__file__)
34
+ config_content = config_file.read_text()
35
+ config_content = _MARKER.sub(config_entry + r"\g<1>", config_content)
36
+ config_file.write_text(config_content)
37
+
38
+ mapping_file = Path(mapping.__file__).parent / f"{package}.py"
39
+ mapping_file.write_text("MAPPING = {}")
40
+
41
+ index_entry = f"""
42
+ <li><a href="{index}" class="package-name">{pypi}</a></li>"""
43
+ index_file = Path(config.__file__).parent.parent.parent / "index.html"
44
+ index_content = index_file.read_text()
45
+ index_content = _MARKER.sub(index_entry + r"\g<1>", index_content)
46
+ index_file.write_text(index_content)
47
+
48
+ importlib.reload(config)
49
+ update_docs(package, docs_directory=docs_directory)
@@ -37,16 +37,13 @@ def crawl_docs(
37
37
  task = None
38
38
  for package in packages:
39
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)
40
+ subdir = output_directory / package.package
41
+ subdir.mkdir(parents=True, exist_ok=True)
45
42
  crawler = _Crawler(
46
- progress, output_directory / package.name, package.crawler_root
43
+ progress, output_directory / package.package, package.crawler_root
47
44
  )
48
45
  crawler.crawl(num_threads=num_threads)
49
- crawler.save_url_map(output_directory / f"{package.name}.json")
46
+ crawler.save_url_map(output_directory / f"{package.package}.json")
50
47
  finally:
51
48
  if task is not None:
52
49
  progress.advance(task)
@@ -153,33 +150,14 @@ class _Crawler:
153
150
  soup = bs4.BeautifulSoup(content, "html.parser")
154
151
  except bs4.ParserRejectedMarkup:
155
152
  return set()
156
- parsed_current_url = parse.urlparse(current_url)
157
153
  links = set()
158
154
  for link in soup.find_all("a"):
159
155
  if (href := link.get("href")) is None:
160
156
  continue
157
+ absolute_href = parse.urljoin(current_url, href)
161
158
  # href could be full URL, absolute path, and relative path.
162
- parsed_href = parse.urlparse(href)
159
+ parsed_href = parse.urlparse(absolute_href)
163
160
  # Remove the fragment.
164
161
  parsed_href = parsed_href._replace(fragment="")
165
- if parsed_href.netloc:
166
- pass
167
- elif parsed_href.path.startswith("/"):
168
- # Absolute path
169
- parsed_href = parsed_href._replace(
170
- scheme=parsed_current_url.scheme, netloc=parsed_current_url.netloc
171
- )
172
- else:
173
- # Relative path
174
- new_path = os.path.normpath(
175
- os.path.join(
176
- os.path.dirname(parsed_current_url.path), parsed_href.path
177
- )
178
- )
179
- parsed_href = parsed_href._replace(
180
- scheme=parsed_current_url.scheme,
181
- netloc=parsed_current_url.netloc,
182
- path=new_path,
183
- )
184
162
  links.add(parse.urlunparse(parsed_href))
185
163
  return links
@@ -19,7 +19,7 @@ _STDLIB_MODULES_NAMES = frozenset({*sys.stdlib_module_names, "test"})
19
19
 
20
20
 
21
21
  def parse_docs(
22
- docs_directory: Path, package: str | None = None, in_place: bool = False
22
+ docs_directory: Path, *, package: str | None = None, in_place: bool = False
23
23
  ) -> None:
24
24
  if sys.version_info[:2] != (3, 13):
25
25
  console.fatal("pyrefdev-indexer parse_docs must be run on Python 3.13.")
@@ -32,7 +32,7 @@ def parse_docs(
32
32
  task = None
33
33
  for package in packages:
34
34
  _parse_package(
35
- progress, package, docs_directory / package.name, in_place=in_place
35
+ progress, package, docs_directory / package.package, in_place=in_place
36
36
  )
37
37
  if task is not None:
38
38
  progress.advance(task)
@@ -41,14 +41,14 @@ def parse_docs(
41
41
  def _parse_package(
42
42
  progress: Progress, package: Package, package_docs: Path, *, in_place: bool
43
43
  ) -> None:
44
- metadata_file = package_docs.parent / f"{package.name}.json"
44
+ metadata_file = package_docs.parent / f"{package.package}.json"
45
45
  file_and_urls: list[tuple[str, str]] = list(
46
46
  json.loads(metadata_file.read_text()).items()
47
47
  )
48
48
  if package.is_stdlib():
49
49
  symbol_to_urls: dict[str, str] = _SPECIAL_SYMBOLS.copy()
50
50
  else:
51
- symbol_to_urls: dict[str, str] = {package.name: package.index}
51
+ symbol_to_urls: dict[str, str] = {package.package: package.index}
52
52
  parser = _Parser(package)
53
53
  task = progress.add_task(f"Parsing {package_docs}", total=len(file_and_urls))
54
54
  while file_and_urls:
@@ -69,7 +69,7 @@ def _parse_package(
69
69
 
70
70
  console.print(f"Found {len(symbol_to_urls)} symbols.")
71
71
  lines = _create_symbols_map(symbol_to_urls)
72
- mapping_module = importlib.import_module(f"pyrefdev.mapping.{package.name}")
72
+ mapping_module = importlib.import_module(f"pyrefdev.mapping.{package.package}")
73
73
  if in_place:
74
74
  Path(mapping_module.__file__).write_text(
75
75
  "\n".join(itertools.chain(lines, [""]))
@@ -142,7 +142,7 @@ class _Parser:
142
142
  return True
143
143
  return False
144
144
  else:
145
- return prefix == self._package.name
145
+ return prefix == self._package.package
146
146
 
147
147
 
148
148
  _SPECIAL_SYMBOLS = {
@@ -0,0 +1,16 @@
1
+ from pathlib import Path
2
+ import tempfile
3
+
4
+ from pyrefdev.indexer.crawl_docs import crawl_docs
5
+ from pyrefdev.indexer.parse_docs import parse_docs
6
+
7
+
8
+ def update_docs(
9
+ package: str,
10
+ *,
11
+ docs_directory: Path | None = None,
12
+ ) -> None:
13
+ if docs_directory is None:
14
+ docs_directory = Path(tempfile.mkdtemp(prefix=f"{package}."))
15
+ crawl_docs(docs_directory, package=package)
16
+ parse_docs(docs_directory, package=package, in_place=True)
@@ -0,0 +1,90 @@
1
+ # fmt: off
2
+
3
+ MAPPING = {
4
+ "dateutil": "https://dateutil.readthedocs.io/en/stable/",
5
+ "dateutil.easter.easter": "https://dateutil.readthedocs.io/en/stable/easter.html#dateutil.easter.easter",
6
+ "dateutil.parser.isoparse": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.isoparse",
7
+ "dateutil.parser.parse": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parse",
8
+ "dateutil.parser.parsererror": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.ParserError",
9
+ "dateutil.parser.parserinfo": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo",
10
+ "dateutil.parser.parserinfo.AMPM": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.AMPM",
11
+ "dateutil.parser.parserinfo.ampm": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.ampm",
12
+ "dateutil.parser.parserinfo.convertyear": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.convertyear",
13
+ "dateutil.parser.parserinfo.HMS": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.HMS",
14
+ "dateutil.parser.parserinfo.hms": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.hms",
15
+ "dateutil.parser.parserinfo.JUMP": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.JUMP",
16
+ "dateutil.parser.parserinfo.jump": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.jump",
17
+ "dateutil.parser.parserinfo.month": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.month",
18
+ "dateutil.parser.parserinfo.months": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.MONTHS",
19
+ "dateutil.parser.parserinfo.PERTAIN": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.PERTAIN",
20
+ "dateutil.parser.parserinfo.pertain": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.pertain",
21
+ "dateutil.parser.parserinfo.TZOFFSET": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.TZOFFSET",
22
+ "dateutil.parser.parserinfo.tzoffset": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.tzoffset",
23
+ "dateutil.parser.parserinfo.UTCZONE": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.UTCZONE",
24
+ "dateutil.parser.parserinfo.utczone": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.utczone",
25
+ "dateutil.parser.parserinfo.validate": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.validate",
26
+ "dateutil.parser.parserinfo.weekday": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.weekday",
27
+ "dateutil.parser.parserinfo.weekdays": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parserinfo.WEEKDAYS",
28
+ "dateutil.parser.unknowntimezonewarning": "https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.UnknownTimezoneWarning",
29
+ "dateutil.relativedelta.relativedelta": "https://dateutil.readthedocs.io/en/stable/relativedelta.html#dateutil.relativedelta.relativedelta",
30
+ "dateutil.relativedelta.relativedelta.normalized": "https://dateutil.readthedocs.io/en/stable/relativedelta.html#dateutil.relativedelta.relativedelta.normalized",
31
+ "dateutil.relativedelta.relativedelta.weeks": "https://dateutil.readthedocs.io/en/stable/relativedelta.html#dateutil.relativedelta.relativedelta.weeks",
32
+ "dateutil.rrule.rrule": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule",
33
+ "dateutil.rrule.rrule.after": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.after",
34
+ "dateutil.rrule.rrule.before": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.before",
35
+ "dateutil.rrule.rrule.between": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.between",
36
+ "dateutil.rrule.rrule.count": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.count",
37
+ "dateutil.rrule.rrule.replace": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.replace",
38
+ "dateutil.rrule.rrule.xafter": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrule.xafter",
39
+ "dateutil.rrule.rruleset": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset",
40
+ "dateutil.rrule.rruleset.after": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.after",
41
+ "dateutil.rrule.rruleset.before": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.before",
42
+ "dateutil.rrule.rruleset.between": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.between",
43
+ "dateutil.rrule.rruleset.count": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.count",
44
+ "dateutil.rrule.rruleset.exdate": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.exdate",
45
+ "dateutil.rrule.rruleset.exrule": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.exrule",
46
+ "dateutil.rrule.rruleset.rdate": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.rdate",
47
+ "dateutil.rrule.rruleset.rrule": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.rrule",
48
+ "dateutil.rrule.rruleset.xafter": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rruleset.xafter",
49
+ "dateutil.rrule.rrulestr": "https://dateutil.readthedocs.io/en/stable/rrule.html#dateutil.rrule.rrulestr",
50
+ "dateutil.tz.datetime_ambiguous": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.datetime_ambiguous",
51
+ "dateutil.tz.datetime_exists": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.datetime_exists",
52
+ "dateutil.tz.dateutil.tz.utc": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.dateutil.tz.UTC",
53
+ "dateutil.tz.enfold": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.enfold",
54
+ "dateutil.tz.gettz": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.gettz",
55
+ "dateutil.tz.gettz.cache_clear": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.gettz.cache_clear",
56
+ "dateutil.tz.gettz.nocache": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.gettz.nocache",
57
+ "dateutil.tz.resolve_imaginary": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.resolve_imaginary",
58
+ "dateutil.tz.tzical": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzical",
59
+ "dateutil.tz.tzical.get": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzical.get",
60
+ "dateutil.tz.tzical.keys": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzical.keys",
61
+ "dateutil.tz.tzlocal": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzlocal",
62
+ "dateutil.tz.tzoffset": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzoffset",
63
+ "dateutil.tz.tzrange": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzrange",
64
+ "dateutil.tz.tzstr": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzstr",
65
+ "dateutil.tz.tzutc": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzutc",
66
+ "dateutil.tz.tzwin": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwin",
67
+ "dateutil.tz.tzwin.display": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwin.display",
68
+ "dateutil.tz.tzwin.list": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwin.list",
69
+ "dateutil.tz.tzwin.transitions": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwin.transitions",
70
+ "dateutil.tz.tzwinlocal": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwinlocal",
71
+ "dateutil.tz.tzwinlocal.display": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwinlocal.display",
72
+ "dateutil.tz.tzwinlocal.transitions": "https://dateutil.readthedocs.io/en/stable/tz.html#dateutil.tz.tzwinlocal.transitions",
73
+ "dateutil.tz.win.tzres": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzres",
74
+ "dateutil.tz.win.tzres.load_name": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzres.load_name",
75
+ "dateutil.tz.win.tzres.name_from_string": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzres.name_from_string",
76
+ "dateutil.tz.win.tzwin": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwin",
77
+ "dateutil.tz.win.tzwin.display": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwin.display",
78
+ "dateutil.tz.win.tzwin.list": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwin.list",
79
+ "dateutil.tz.win.tzwin.transitions": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwin.transitions",
80
+ "dateutil.tz.win.tzwinlocal": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwinlocal",
81
+ "dateutil.tz.win.tzwinlocal.display": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwinlocal.display",
82
+ "dateutil.tz.win.tzwinlocal.transitions": "https://dateutil.readthedocs.io/en/stable/tzwin.html#dateutil.tz.win.tzwinlocal.transitions",
83
+ "dateutil.utils.default_tzinfo": "https://dateutil.readthedocs.io/en/stable/utils.html#dateutil.utils.default_tzinfo",
84
+ "dateutil.utils.today": "https://dateutil.readthedocs.io/en/stable/utils.html#dateutil.utils.today",
85
+ "dateutil.utils.within_delta": "https://dateutil.readthedocs.io/en/stable/utils.html#dateutil.utils.within_delta",
86
+ "dateutil.zoneinfo.get_zonefile_instance": "https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.get_zonefile_instance",
87
+ "dateutil.zoneinfo.gettz": "https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.gettz",
88
+ "dateutil.zoneinfo.gettz_db_metadata": "https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.gettz_db_metadata",
89
+ "dateutil.zoneinfo.rebuild.rebuild": "https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.rebuild.rebuild",
90
+ }