|
| 1 | +from .constants import * |
| 2 | + |
| 3 | +URL_BASE = "https://www.python.org/ftp/python/" |
| 4 | + |
| 5 | +XYZ_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}" |
| 6 | +WIN32_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}.{VER_FIELD4}" |
| 7 | +FULL_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{VER_SUFFIX}" |
| 8 | + |
| 9 | + |
| 10 | +def _not_empty(n, key=None): |
| 11 | + result = [] |
| 12 | + for i in n: |
| 13 | + if key: |
| 14 | + i_l = i[key] |
| 15 | + else: |
| 16 | + i_l = i |
| 17 | + if not i_l: |
| 18 | + continue |
| 19 | + result.append(i) |
| 20 | + return result |
| 21 | + |
| 22 | + |
| 23 | +def calculate_install_json(ns, *, for_embed=False, for_test=False): |
| 24 | + TARGET = "python.exe" |
| 25 | + TARGETW = "pythonw.exe" |
| 26 | + |
| 27 | + SYS_ARCH = { |
| 28 | + "win32": "32bit", |
| 29 | + "amd64": "64bit", |
| 30 | + "arm64": "64bit", # Unfortunate, but this is how it's spec'd |
| 31 | + }[ns.arch] |
| 32 | + TAG_ARCH = { |
| 33 | + "win32": "-32", |
| 34 | + "amd64": "-64", |
| 35 | + "arm64": "-arm64", |
| 36 | + }[ns.arch] |
| 37 | + |
| 38 | + COMPANY = "PythonCore" |
| 39 | + DISPLAY_NAME = "Python" |
| 40 | + TAG_SUFFIX = "" |
| 41 | + ALIAS_PREFIX = "python" |
| 42 | + ALIAS_WPREFIX = "pythonw" |
| 43 | + FILE_PREFIX = "python-" |
| 44 | + FILE_SUFFIX = f"-{ns.arch}" |
| 45 | + DISPLAY_TAGS = [{ |
| 46 | + "win32": "32-bit", |
| 47 | + "amd64": "", |
| 48 | + "arm64": "ARM64", |
| 49 | + }[ns.arch]] |
| 50 | + |
| 51 | + if for_test: |
| 52 | + # Packages with the test suite come under a different Company |
| 53 | + COMPANY = "PythonTest" |
| 54 | + DISPLAY_TAGS.append("with tests") |
| 55 | + FILE_SUFFIX = f"-test-{ns.arch}" |
| 56 | + if for_embed: |
| 57 | + # Embeddable distro comes under a different Company |
| 58 | + COMPANY = "PythonEmbed" |
| 59 | + TARGETW = None |
| 60 | + ALIAS_PREFIX = None |
| 61 | + DISPLAY_TAGS.append("embeddable") |
| 62 | + # Deliberately name the file differently from the existing distro |
| 63 | + # so we can republish old versions without replacing files. |
| 64 | + FILE_SUFFIX = f"-embeddable-{ns.arch}" |
| 65 | + if ns.include_freethreaded: |
| 66 | + # Free-threaded distro comes with a tag suffix |
| 67 | + TAG_SUFFIX = "t" |
| 68 | + TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe" |
| 69 | + TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe" |
| 70 | + DISPLAY_TAGS.append("freethreaded") |
| 71 | + FILE_SUFFIX = f"t-{ns.arch}" |
| 72 | + |
| 73 | + FULL_TAG = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{VER_SUFFIX}{TAG_SUFFIX}" |
| 74 | + FULL_ARCH_TAG = f"{FULL_TAG}{TAG_ARCH}" |
| 75 | + XY_TAG = f"{VER_MAJOR}.{VER_MINOR}{TAG_SUFFIX}" |
| 76 | + XY_ARCH_TAG = f"{XY_TAG}{TAG_ARCH}" |
| 77 | + X_TAG = f"{VER_MAJOR}{TAG_SUFFIX}" |
| 78 | + X_ARCH_TAG = f"{X_TAG}{TAG_ARCH}" |
| 79 | + |
| 80 | + # Tag used in runtime ID (for side-by-side install/updates) |
| 81 | + ID_TAG = XY_ARCH_TAG |
| 82 | + # Tag shown in 'py list' output |
| 83 | + DISPLAY_TAG = f"{XY_TAG}-dev{TAG_ARCH}" if VER_SUFFIX else XY_ARCH_TAG |
| 84 | + |
| 85 | + DISPLAY_SUFFIX = ", ".join(i for i in DISPLAY_TAGS if i) |
| 86 | + if DISPLAY_SUFFIX: |
| 87 | + DISPLAY_SUFFIX = f" ({DISPLAY_SUFFIX})" |
| 88 | + DISPLAY_VERSION = f"{XYZ_VERSION}{VER_SUFFIX}{DISPLAY_SUFFIX}" |
| 89 | + |
| 90 | + STD_RUN_FOR = [] |
| 91 | + STD_ALIAS = [] |
| 92 | + STD_PEP514 = [] |
| 93 | + STD_START = [] |
| 94 | + STD_UNINSTALL = [] |
| 95 | + |
| 96 | + # The list of 'py install <TAG>' tags that will match this runtime. |
| 97 | + # Architecture should always be included here because PyManager will add it. |
| 98 | + INSTALL_TAGS = [ |
| 99 | + FULL_ARCH_TAG, |
| 100 | + XY_ARCH_TAG, |
| 101 | + X_ARCH_TAG, |
| 102 | + # X_TAG and XY_TAG doesn't include VER_SUFFIX, so create -dev versions |
| 103 | + f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG and VER_SUFFIX else "", |
| 104 | + f"{X_TAG}-dev{TAG_ARCH}" if X_TAG and VER_SUFFIX else "", |
| 105 | + ] |
| 106 | + |
| 107 | + # Generate run-for entries for each target. |
| 108 | + # Again, include architecture because PyManager will add it. |
| 109 | + for base in [ |
| 110 | + {"target": TARGET}, |
| 111 | + {"target": TARGETW, "windowed": 1}, |
| 112 | + ]: |
| 113 | + if not base["target"]: |
| 114 | + continue |
| 115 | + STD_RUN_FOR.append({**base, "tag": FULL_ARCH_TAG}) |
| 116 | + if XY_TAG: |
| 117 | + STD_RUN_FOR.append({**base, "tag": XY_ARCH_TAG}) |
| 118 | + if X_TAG: |
| 119 | + STD_RUN_FOR.append({**base, "tag": X_ARCH_TAG}) |
| 120 | + if VER_SUFFIX: |
| 121 | + STD_RUN_FOR.extend([ |
| 122 | + {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, |
| 123 | + {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, |
| 124 | + ]) |
| 125 | + |
| 126 | + # Generate alias entries for each target. We need both arch and non-arch |
| 127 | + # versions as well as windowed/non-windowed versions to make sure that all |
| 128 | + # necessary aliases are created. |
| 129 | + if ALIAS_PREFIX: |
| 130 | + for prefix, base in [ |
| 131 | + (ALIAS_PREFIX, {"target": TARGET}), |
| 132 | + (f"{ALIAS_PREFIX}w", {"target": TARGETW, "windowed": 1}), |
| 133 | + ]: |
| 134 | + if not base["target"]: |
| 135 | + continue |
| 136 | + if XY_TAG: |
| 137 | + STD_ALIAS.extend([ |
| 138 | + {**base, "name": f"{prefix}{XY_TAG}.exe"}, |
| 139 | + {**base, "name": f"{prefix}{XY_ARCH_TAG}.exe"}, |
| 140 | + ]) |
| 141 | + if X_TAG: |
| 142 | + STD_ALIAS.extend([ |
| 143 | + {**base, "name": f"{prefix}{X_TAG}.exe"}, |
| 144 | + {**base, "name": f"{prefix}{X_ARCH_TAG}.exe"}, |
| 145 | + ]) |
| 146 | + |
| 147 | + STD_PEP514.append({ |
| 148 | + "kind": "pep514", |
| 149 | + "Key": rf"{COMPANY}\{ID_TAG}", |
| 150 | + "DisplayName": f"{DISPLAY_NAME} {DISPLAY_VERSION}", |
| 151 | + "SupportUrl": "https://www.python.org/", |
| 152 | + "SysArchitecture": SYS_ARCH, |
| 153 | + "SysVersion": VER_DOT, |
| 154 | + "Version": FULL_VERSION, |
| 155 | + "InstallPath": { |
| 156 | + "_": "%PREFIX%", |
| 157 | + "ExecutablePath": f"%PREFIX%{TARGET}", |
| 158 | + # WindowedExecutablePath is added below |
| 159 | + }, |
| 160 | + "Help": { |
| 161 | + "Online Python Documentation": { |
| 162 | + "_": f"https://docs.python.org/{VER_DOT}/" |
| 163 | + }, |
| 164 | + }, |
| 165 | + }) |
| 166 | + |
| 167 | + STD_START.append({ |
| 168 | + "kind": "start", |
| 169 | + "Name": f"{DISPLAY_NAME} {VER_DOT}{DISPLAY_SUFFIX}", |
| 170 | + "Items": [ |
| 171 | + { |
| 172 | + "Name": f"{DISPLAY_NAME} {VER_DOT}{DISPLAY_SUFFIX}", |
| 173 | + "Target": f"%PREFIX%{TARGET}", |
| 174 | + "Icon": f"%PREFIX%{TARGET}", |
| 175 | + }, |
| 176 | + { |
| 177 | + "Name": f"{DISPLAY_NAME} {VER_DOT} Online Documentation", |
| 178 | + "Icon": r"%SystemRoot%\System32\SHELL32.dll", |
| 179 | + "IconIndex": 13, |
| 180 | + "Target": f"https://docs.python.org/{VER_DOT}/", |
| 181 | + }, |
| 182 | + # IDLE and local documentation items are added below |
| 183 | + ], |
| 184 | + }) |
| 185 | + |
| 186 | + if TARGETW: |
| 187 | + STD_PEP514[0]["InstallPath"]["WindowedExecutablePath"] = f"%PREFIX%{TARGETW}" |
| 188 | + |
| 189 | + if ns.include_idle: |
| 190 | + STD_START[0]["Items"].append({ |
| 191 | + "Name": f"IDLE {VER_DOT}{DISPLAY_SUFFIX}", |
| 192 | + "Target": f"%PREFIX%{TARGETW or TARGET}", |
| 193 | + "Arguments": r'"%PREFIX%Lib\idlelib\idle.pyw"', |
| 194 | + "Icon": r"%PREFIX%Lib\idlelib\Icons\idle.ico", |
| 195 | + "IconIndex": 0, |
| 196 | + }) |
| 197 | + STD_START[0]["Items"].append({ |
| 198 | + "Name": f"PyDoc {VER_DOT}{DISPLAY_SUFFIX}", |
| 199 | + "Target": f"%PREFIX%{TARGET}", |
| 200 | + "Arguments": "-m pydoc -b", |
| 201 | + "Icon": r"%PREFIX%Lib\idlelib\Icons\idle.ico", |
| 202 | + "IconIndex": 0, |
| 203 | + }) |
| 204 | + |
| 205 | + if ns.include_html_doc: |
| 206 | + STD_PEP514[0]["Help"]["Main Python Documentation"] = { |
| 207 | + "_": rf"%PREFIX%Doc\html\index.html", |
| 208 | + } |
| 209 | + STD_START[0]["Items"].append({ |
| 210 | + "Name": f"{DISPLAY_NAME} {VER_DOT} Manuals{DISPLAY_SUFFIX}", |
| 211 | + "Target": r"%PREFIX%Doc\html\index.html", |
| 212 | + }) |
| 213 | + elif ns.include_chm: |
| 214 | + STD_PEP514[0]["Help"]["Main Python Documentation"] = { |
| 215 | + "_": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", |
| 216 | + } |
| 217 | + STD_START[0]["Items"].append({ |
| 218 | + "Name": f"{DISPLAY_NAME} {VER_DOT} Manuals{DISPLAY_SUFFIX}", |
| 219 | + "Target": "%WINDIR%hhc.exe", |
| 220 | + "Arguments": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", |
| 221 | + }) |
| 222 | + |
| 223 | + STD_UNINSTALL.append({ |
| 224 | + "kind": "uninstall", |
| 225 | + # Other settings will pick up sensible defaults |
| 226 | + "Publisher": "Python Software Foundation", |
| 227 | + "HelpLink": f"https://docs.python.org/{VER_DOT}/", |
| 228 | + }) |
| 229 | + |
| 230 | + data = { |
| 231 | + "schema": 1, |
| 232 | + "id": f"{COMPANY.lower()}-{ID_TAG}", |
| 233 | + "sort-version": FULL_VERSION, |
| 234 | + "company": COMPANY, |
| 235 | + "tag": DISPLAY_TAG, |
| 236 | + "install-for": _not_empty(INSTALL_TAGS), |
| 237 | + "run-for": _not_empty(STD_RUN_FOR, "tag"), |
| 238 | + "alias": _not_empty(STD_ALIAS, "name"), |
| 239 | + "shortcuts": [ |
| 240 | + *STD_PEP514, |
| 241 | + *STD_START, |
| 242 | + *STD_UNINSTALL, |
| 243 | + ], |
| 244 | + "display-name": f"{DISPLAY_NAME} {DISPLAY_VERSION}", |
| 245 | + "executable": rf".\{TARGET}", |
| 246 | + "url": f"{URL_BASE}{XYZ_VERSION}/{FILE_PREFIX}{FULL_VERSION}{FILE_SUFFIX}.zip" |
| 247 | + } |
| 248 | + |
| 249 | + return data |
0 commit comments