1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
import json
import os
from collections import namedtuple
from jinja2 import pass_context
from packaging import version as pv
from .theme_check import only_pallets_theme
@only_pallets_theme()
def load_versions(app):
if os.environ.get("READTHEDOCS"):
versions = readthedocs_versions(app)
else:
versions = local_versions(app)
context = app.config.html_context
context["versions"] = versions
context["current_version"] = next((v for v in versions if v.current), None)
context["latest_version"] = next((v for v in versions if v.latest), None)
def local_versions(app):
config_versions = app.config.html_context.get("versions")
if isinstance(config_versions, str):
if os.path.isfile(config_versions):
with open(config_versions, encoding="utf8") as f:
config_versions = json.load(f)
else:
config_versions = json.loads(config_versions)
if not config_versions:
return []
versions = []
for version in config_versions:
if isinstance(version, dict):
version = DocVersion(**version)
versions.append(version)
slug = app.config.version
dev = "dev" in app.config.release
seen_latest = False
for i, version in enumerate(versions):
if version.slug == "dev":
versions[i] = version._replace(dev=True, current=dev)
if version.slug == slug:
versions[i] = version._replace(current=True)
if not seen_latest and version.version is not None:
seen_latest = True
versions[i] = version._replace(latest=True)
return versions
def readthedocs_versions(app):
config_versions = app.config.html_context["versions"]
current_slug = app.config.html_context["current_version"]
number_versions = []
name_versions = []
for slug, _ in config_versions:
dev = slug in {"main", "master", "default", "latest"}
version = _parse_version(slug)
if version is not None:
name = slug
append_to = number_versions
else:
name = "Development" if dev else slug.title()
append_to = name_versions
append_to.append(
DocVersion(name=name, slug=slug, dev=dev, current=slug == current_slug)
)
# put the newest numbered version first
number_versions.sort(key=lambda x: x.version, reverse=True)
# put non-dev named versions first
name_versions.sort(key=lambda x: x.dev, reverse=True)
versions = number_versions + name_versions
# if there are non-dev versions, mark the newest one as the latest
if versions and not versions[0].dev:
versions[0] = versions[0]._replace(latest=True)
return versions
def _parse_version(value: str, placeholder: str = "x"):
if value.endswith(f".{placeholder}"):
value = value[: -(len(placeholder) + 1)]
try:
return pv.Version(value)
except pv.InvalidVersion:
return None
class DocVersion(
namedtuple("DocVersion", ("name", "slug", "version", "latest", "dev", "current"))
):
__slots__ = ()
def __new__(cls, name, slug=None, latest=False, dev=False, current=False):
slug = slug or name
version = _parse_version(slug)
if version is not None:
name = "Version " + name
return super().__new__(cls, name, slug, version, latest, dev, current)
@pass_context
def href(self, context):
pathto = context["pathto"]
master_doc = context["master_doc"]
pagename = context["pagename"]
builder = pathto.__closure__[0].cell_contents
master = pathto(master_doc).rstrip("#/") or "."
path = builder.get_target_uri(pagename)
return "/".join((master, "..", self.slug, path))
@pass_context
def banner(self, context):
if self.latest:
return
latest = context["latest_version"]
# Don't show a banner if the latest version couldn't be determined, or if this
# is the "stable" version.
if latest is None or self.name == "stable":
return
if self.dev:
return (
"This is the development version. The latest stable"
' version is <a href="{href}">{latest}</a>.'
).format(latest=latest.name, href=latest.href(context))
return (
"This is an old version. The latest stable version is"
' <a href="{href}">{latest}</a>.'
).format(latest=latest.name, href=latest.href(context))
|