from .constants import * URL_BASE = "https://www.python.org/ftp/python/" XYZ_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}" WIN32_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}.{VER_FIELD4}" FULL_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{VER_SUFFIX}" def _not_empty(n, key=None): result = [] for i in n: if key: i_l = i[key] else: i_l = i if not i_l: continue result.append(i) return result def calculate_install_json(ns, *, for_embed=False, for_test=False): TARGET = "python.exe" TARGETW = "pythonw.exe" SYS_ARCH = { "win32": "32bit", "amd64": "64bit", "arm64": "64bit", # Unfortunate, but this is how it's spec'd }[ns.arch] TAG_ARCH = { "win32": "-32", "amd64": "-64", "arm64": "-arm64", }[ns.arch] COMPANY = "PythonCore" DISPLAY_NAME = "Python" TAG_SUFFIX = "" ALIAS_PREFIX = "python" ALIAS_WPREFIX = "pythonw" FILE_PREFIX = "python-" FILE_SUFFIX = f"-{ns.arch}" DISPLAY_TAGS = [{ "win32": "32-bit", "amd64": "", "arm64": "ARM64", }[ns.arch]] if for_test: # Packages with the test suite come under a different Company COMPANY = "PythonTest" DISPLAY_TAGS.append("with tests") FILE_SUFFIX = f"-test-{ns.arch}" if for_embed: # Embeddable distro comes under a different Company COMPANY = "PythonEmbed" TARGETW = None ALIAS_PREFIX = None ALIAS_WPREFIX = None DISPLAY_TAGS.append("embeddable") # Deliberately name the file differently from the existing distro # so we can republish old versions without replacing files. FILE_SUFFIX = f"-embeddable-{ns.arch}" if ns.include_freethreaded: # Free-threaded distro comes with a tag suffix TAG_SUFFIX = "t" TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe" TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe" DISPLAY_TAGS.append("free-threaded") FILE_SUFFIX = f"t-{ns.arch}" FULL_TAG = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{VER_SUFFIX}{TAG_SUFFIX}" FULL_ARCH_TAG = f"{FULL_TAG}{TAG_ARCH}" XY_TAG = f"{VER_MAJOR}.{VER_MINOR}{TAG_SUFFIX}" XY_ARCH_TAG = f"{XY_TAG}{TAG_ARCH}" X_TAG = f"{VER_MAJOR}{TAG_SUFFIX}" X_ARCH_TAG = f"{X_TAG}{TAG_ARCH}" # Tag used in runtime ID (for side-by-side install/updates) ID_TAG = XY_ARCH_TAG # Tag shown in 'py list' output DISPLAY_TAG = f"{XY_TAG}-dev{TAG_ARCH}" if VER_SUFFIX else XY_ARCH_TAG # Tag used for PEP 514 registration SYS_WINVER = XY_TAG + (TAG_ARCH if TAG_ARCH != '-64' else '') DISPLAY_SUFFIX = ", ".join(i for i in DISPLAY_TAGS if i) if DISPLAY_SUFFIX: DISPLAY_SUFFIX = f" ({DISPLAY_SUFFIX})" DISPLAY_VERSION = f"{XYZ_VERSION}{VER_SUFFIX}{DISPLAY_SUFFIX}" STD_RUN_FOR = [] STD_ALIAS = [] STD_PEP514 = [] STD_START = [] STD_UNINSTALL = [] # The list of 'py install ' tags that will match this runtime. # Architecture should always be included here because PyManager will add it. INSTALL_TAGS = [ FULL_ARCH_TAG, XY_ARCH_TAG, X_ARCH_TAG, # X_TAG and XY_TAG doesn't include VER_SUFFIX, so create -dev versions f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG and VER_SUFFIX else "", f"{X_TAG}-dev{TAG_ARCH}" if X_TAG and VER_SUFFIX else "", ] # Generate run-for entries for each target. # Again, include architecture because PyManager will add it. for base in [ {"target": TARGET}, {"target": TARGETW, "windowed": 1}, ]: if not base["target"]: continue STD_RUN_FOR.append({**base, "tag": FULL_ARCH_TAG}) if XY_TAG: STD_RUN_FOR.append({**base, "tag": XY_ARCH_TAG}) if X_TAG: STD_RUN_FOR.append({**base, "tag": X_ARCH_TAG}) if VER_SUFFIX: STD_RUN_FOR.extend([ {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, ]) # Generate alias entries for each target. We need both arch and non-arch # versions as well as windowed/non-windowed versions to make sure that all # necessary aliases are created. for prefix, base in ( (ALIAS_PREFIX, {"target": TARGET}), (ALIAS_WPREFIX, {"target": TARGETW, "windowed": 1}), ): if not prefix: continue if not base["target"]: continue if XY_TAG: STD_ALIAS.extend([ {**base, "name": f"{prefix}{XY_TAG}.exe"}, {**base, "name": f"{prefix}{XY_ARCH_TAG}.exe"}, ]) if X_TAG: STD_ALIAS.extend([ {**base, "name": f"{prefix}{X_TAG}.exe"}, {**base, "name": f"{prefix}{X_ARCH_TAG}.exe"}, ]) if SYS_WINVER: STD_PEP514.append({ "kind": "pep514", "Key": rf"{COMPANY}\{SYS_WINVER}", "DisplayName": f"{DISPLAY_NAME} {DISPLAY_VERSION}", "SupportUrl": "https://www.python.org/", "SysArchitecture": SYS_ARCH, "SysVersion": VER_DOT, "Version": FULL_VERSION, "InstallPath": { "_": "%PREFIX%", "ExecutablePath": f"%PREFIX%{TARGET}", # WindowedExecutablePath is added below }, "Help": { "Online Python Documentation": { "_": f"https://docs.python.org/{VER_DOT}/" }, }, }) STD_START.append({ "kind": "start", "Name": f"{DISPLAY_NAME} {VER_DOT}{DISPLAY_SUFFIX}", "Items": [ { "Name": f"{DISPLAY_NAME} {VER_DOT}{DISPLAY_SUFFIX}", "Target": f"%PREFIX%{TARGET}", "Icon": f"%PREFIX%{TARGET}", }, { "Name": f"{DISPLAY_NAME} {VER_DOT} Online Documentation", "Icon": r"%SystemRoot%\System32\SHELL32.dll", "IconIndex": 13, "Target": f"https://docs.python.org/{VER_DOT}/", }, # IDLE and local documentation items are added below ], }) if TARGETW and STD_PEP514: STD_PEP514[0]["InstallPath"]["WindowedExecutablePath"] = f"%PREFIX%{TARGETW}" if ns.include_idle: STD_START[0]["Items"].append({ "Name": f"IDLE (Python {VER_DOT}{DISPLAY_SUFFIX})", "Target": f"%PREFIX%{TARGETW or TARGET}", "Arguments": r'"%PREFIX%Lib\idlelib\idle.pyw"', "Icon": r"%PREFIX%Lib\idlelib\Icons\idle.ico", "IconIndex": 0, }) STD_START[0]["Items"].append({ "Name": f"PyDoc (Python {VER_DOT}{DISPLAY_SUFFIX})", "Target": f"%PREFIX%{TARGET}", "Arguments": "-m pydoc -b", "Icon": r"%PREFIX%Lib\idlelib\Icons\idle.ico", "IconIndex": 0, }) if STD_PEP514: STD_PEP514[0]["InstallPath"]["IdlePath"] = f"%PREFIX%Lib\\idlelib\\idle.pyw" if ns.include_html_doc: STD_PEP514[0]["Help"]["Main Python Documentation"] = { "_": rf"%PREFIX%Doc\html\index.html", } STD_START[0]["Items"].append({ "Name": f"{DISPLAY_NAME} {VER_DOT} Manuals{DISPLAY_SUFFIX}", "Target": r"%PREFIX%Doc\html\index.html", }) elif ns.include_chm: STD_PEP514[0]["Help"]["Main Python Documentation"] = { "_": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", } STD_START[0]["Items"].append({ "Name": f"{DISPLAY_NAME} {VER_DOT} Manuals{DISPLAY_SUFFIX}", "Target": "%WINDIR%hhc.exe", "Arguments": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", }) STD_UNINSTALL.append({ "kind": "uninstall", # Other settings will pick up sensible defaults "Publisher": "Python Software Foundation", "HelpLink": f"https://docs.python.org/{VER_DOT}/", }) data = { "schema": 1, "id": f"{COMPANY.lower()}-{ID_TAG}", "sort-version": FULL_VERSION, "company": COMPANY, "tag": DISPLAY_TAG, "install-for": _not_empty(INSTALL_TAGS), "run-for": _not_empty(STD_RUN_FOR, "tag"), "alias": _not_empty(STD_ALIAS, "name"), "shortcuts": [ *STD_PEP514, *STD_START, *STD_UNINSTALL, ], "display-name": f"{DISPLAY_NAME} {DISPLAY_VERSION}", "executable": rf".\{TARGET}", "url": f"{URL_BASE}{XYZ_VERSION}/{FILE_PREFIX}{FULL_VERSION}{FILE_SUFFIX}.zip" } return data