| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  | """
 | 
					
						
							|  |  |  | File generation for APPX/MSIX manifests. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __author__ = "Steve Dower <steve.dower@python.org>" | 
					
						
							|  |  |  | __version__ = "3.8" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import collections | 
					
						
							|  |  |  | import ctypes | 
					
						
							|  |  |  | import io | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from pathlib import Path, PureWindowsPath | 
					
						
							|  |  |  | from xml.etree import ElementTree as ET | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from .constants import * | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-14 08:29:20 -07:00
										 |  |  | __all__ = ["get_appx_layout"] | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | APPX_DATA = dict( | 
					
						
							|  |  |  |     Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), | 
					
						
							|  |  |  |     Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), | 
					
						
							|  |  |  |     Publisher=os.getenv( | 
					
						
							|  |  |  |         "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     DisplayName="Python {}".format(VER_DOT), | 
					
						
							|  |  |  |     Description="The Python {} runtime and console.".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | APPX_PLATFORM_DATA = dict( | 
					
						
							|  |  |  |     _keys=("ProcessorArchitecture",), | 
					
						
							|  |  |  |     win32=("x86",), | 
					
						
							|  |  |  |     amd64=("x64",), | 
					
						
							|  |  |  |     arm32=("arm",), | 
					
						
							|  |  |  |     arm64=("arm64",), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PYTHON_VE_DATA = dict( | 
					
						
							|  |  |  |     DisplayName="Python {}".format(VER_DOT), | 
					
						
							|  |  |  |     Description="Python interactive console", | 
					
						
							|  |  |  |     Square150x150Logo="_resources/pythonx150.png", | 
					
						
							|  |  |  |     Square44x44Logo="_resources/pythonx44.png", | 
					
						
							|  |  |  |     BackgroundColor="transparent", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PYTHONW_VE_DATA = dict( | 
					
						
							|  |  |  |     DisplayName="Python {} (Windowed)".format(VER_DOT), | 
					
						
							|  |  |  |     Description="Python windowed app launcher", | 
					
						
							|  |  |  |     Square150x150Logo="_resources/pythonwx150.png", | 
					
						
							|  |  |  |     Square44x44Logo="_resources/pythonwx44.png", | 
					
						
							|  |  |  |     BackgroundColor="transparent", | 
					
						
							|  |  |  |     AppListEntry="none", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PIP_VE_DATA = dict( | 
					
						
							|  |  |  |     DisplayName="pip (Python {})".format(VER_DOT), | 
					
						
							|  |  |  |     Description="pip package manager for Python {}".format(VER_DOT), | 
					
						
							|  |  |  |     Square150x150Logo="_resources/pythonx150.png", | 
					
						
							|  |  |  |     Square44x44Logo="_resources/pythonx44.png", | 
					
						
							|  |  |  |     BackgroundColor="transparent", | 
					
						
							|  |  |  |     AppListEntry="none", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | IDLE_VE_DATA = dict( | 
					
						
							|  |  |  |     DisplayName="IDLE (Python {})".format(VER_DOT), | 
					
						
							|  |  |  |     Description="IDLE editor for Python {}".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2020-10-20 10:20:05 -07:00
										 |  |  |     Square150x150Logo="_resources/idlex150.png", | 
					
						
							|  |  |  |     Square44x44Logo="_resources/idlex44.png", | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     BackgroundColor="transparent", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  | PY_PNG = "_resources/py.png" | 
					
						
							| 
									
										
										
										
											2019-08-07 10:50:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  | APPXMANIFEST_NS = { | 
					
						
							|  |  |  |     "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", | 
					
						
							|  |  |  |     "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", | 
					
						
							|  |  |  |     "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", | 
					
						
							|  |  |  |     "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", | 
					
						
							|  |  |  |     "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", | 
					
						
							|  |  |  |     "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", | 
					
						
							|  |  |  |     "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", | 
					
						
							|  |  |  |     "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", | 
					
						
							|  |  |  |     "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", | 
					
						
							|  |  |  |     "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | APPXMANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
 | 
					
						
							|  |  |  | <Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" | 
					
						
							|  |  |  |     xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" | 
					
						
							|  |  |  |     xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" | 
					
						
							|  |  |  |     xmlns:rescap4="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4" | 
					
						
							|  |  |  |     xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" | 
					
						
							|  |  |  |     xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" | 
					
						
							|  |  |  |     xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"> | 
					
						
							|  |  |  |     <Identity Name="" | 
					
						
							|  |  |  |               Version="" | 
					
						
							|  |  |  |               Publisher="" | 
					
						
							|  |  |  |               ProcessorArchitecture="" /> | 
					
						
							|  |  |  |     <Properties> | 
					
						
							|  |  |  |         <DisplayName></DisplayName> | 
					
						
							|  |  |  |         <PublisherDisplayName>Python Software Foundation</PublisherDisplayName> | 
					
						
							|  |  |  |         <Description></Description> | 
					
						
							|  |  |  |         <Logo>_resources/pythonx50.png</Logo> | 
					
						
							|  |  |  |     </Properties> | 
					
						
							|  |  |  |     <Resources> | 
					
						
							|  |  |  |         <Resource Language="en-US" /> | 
					
						
							|  |  |  |     </Resources> | 
					
						
							|  |  |  |     <Dependencies> | 
					
						
							|  |  |  |         <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="" /> | 
					
						
							|  |  |  |     </Dependencies> | 
					
						
							|  |  |  |     <Capabilities> | 
					
						
							|  |  |  |         <rescap:Capability Name="runFullTrust"/> | 
					
						
							|  |  |  |     </Capabilities> | 
					
						
							|  |  |  |     <Applications> | 
					
						
							|  |  |  |     </Applications> | 
					
						
							|  |  |  |     <Extensions> | 
					
						
							|  |  |  |     </Extensions> | 
					
						
							|  |  |  | </Package>"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RESOURCES_XML_TEMPLATE = r"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 | 
					
						
							|  |  |  | <!--This file is input for makepri.exe. It should be excluded from the final package.--> | 
					
						
							|  |  |  | <resources targetOsVersion="10.0.0" majorVersion="1"> | 
					
						
							|  |  |  |     <packaging> | 
					
						
							|  |  |  |         <autoResourcePackage qualifier="Language"/> | 
					
						
							|  |  |  |         <autoResourcePackage qualifier="Scale"/> | 
					
						
							|  |  |  |         <autoResourcePackage qualifier="DXFeatureLevel"/> | 
					
						
							|  |  |  |     </packaging> | 
					
						
							|  |  |  |     <index root="\" startIndexAt="\"> | 
					
						
							|  |  |  |         <default> | 
					
						
							|  |  |  |             <qualifier name="Language" value="en-US"/> | 
					
						
							|  |  |  |             <qualifier name="Contrast" value="standard"/> | 
					
						
							|  |  |  |             <qualifier name="Scale" value="100"/> | 
					
						
							|  |  |  |             <qualifier name="HomeRegion" value="001"/> | 
					
						
							|  |  |  |             <qualifier name="TargetSize" value="256"/> | 
					
						
							|  |  |  |             <qualifier name="LayoutDirection" value="LTR"/> | 
					
						
							|  |  |  |             <qualifier name="Theme" value="dark"/> | 
					
						
							|  |  |  |             <qualifier name="AlternateForm" value=""/> | 
					
						
							|  |  |  |             <qualifier name="DXFeatureLevel" value="DX9"/> | 
					
						
							|  |  |  |             <qualifier name="Configuration" value=""/> | 
					
						
							|  |  |  |             <qualifier name="DeviceFamily" value="Universal"/> | 
					
						
							|  |  |  |             <qualifier name="Custom" value=""/> | 
					
						
							|  |  |  |         </default> | 
					
						
							|  |  |  |         <indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="$"/> | 
					
						
							|  |  |  |         <indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/> | 
					
						
							|  |  |  |         <indexer-config type="resjson" initialPath=""/> | 
					
						
							|  |  |  |         <indexer-config type="PRI"/> | 
					
						
							|  |  |  |     </index> | 
					
						
							|  |  |  | </resources>"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SCCD_FILENAME = "PC/classicAppCompat.sccd" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  | SPECIAL_LOOKUP = object() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  | REGISTRY = { | 
					
						
							|  |  |  |     "HKCU\\Software\\Python\\PythonCore": { | 
					
						
							|  |  |  |         VER_DOT: { | 
					
						
							|  |  |  |             "DisplayName": APPX_DATA["DisplayName"], | 
					
						
							|  |  |  |             "SupportUrl": "https://www.python.org/", | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |             "SysArchitecture": SPECIAL_LOOKUP, | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             "SysVersion": VER_DOT, | 
					
						
							|  |  |  |             "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							| 
									
										
										
										
											2019-08-07 11:39:09 -07:00
										 |  |  |                 "": "[{AppVPackageRoot}]", | 
					
						
							| 
									
										
										
										
											2019-08-07 10:49:40 -07:00
										 |  |  |                 "ExecutablePath": "[{{AppVPackageRoot}}]\\python{}.exe".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |                 "WindowedExecutablePath": "[{{AppVPackageRoot}}]\\pythonw{}.exe".format( | 
					
						
							|  |  |  |                     VER_DOT | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             }, | 
					
						
							|  |  |  |             "Help": { | 
					
						
							|  |  |  |                 "Main Python Documentation": { | 
					
						
							|  |  |  |                     "_condition": lambda ns: ns.include_chm, | 
					
						
							| 
									
										
										
										
											2019-06-14 08:29:20 -07:00
										 |  |  |                     "": "[{{AppVPackageRoot}}]\\Doc\\{}".format(PYTHON_CHM_NAME), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |                 }, | 
					
						
							|  |  |  |                 "Local Python Documentation": { | 
					
						
							|  |  |  |                     "_condition": lambda ns: ns.include_html_doc, | 
					
						
							| 
									
										
										
										
											2019-04-17 14:31:32 -07:00
										 |  |  |                     "": "[{AppVPackageRoot}]\\Doc\\html\\index.html", | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |                 }, | 
					
						
							|  |  |  |                 "Online Python Documentation": { | 
					
						
							|  |  |  |                     "": "https://docs.python.org/{}".format(VER_DOT) | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             "Idle": { | 
					
						
							|  |  |  |                 "_condition": lambda ns: ns.include_idle, | 
					
						
							| 
									
										
										
										
											2019-04-17 14:31:32 -07:00
										 |  |  |                 "": "[{AppVPackageRoot}]\\Lib\\idlelib\\idle.pyw", | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             }, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_packagefamilyname(name, publisher_id): | 
					
						
							|  |  |  |     class PACKAGE_ID(ctypes.Structure): | 
					
						
							|  |  |  |         _fields_ = [ | 
					
						
							|  |  |  |             ("reserved", ctypes.c_uint32), | 
					
						
							|  |  |  |             ("processorArchitecture", ctypes.c_uint32), | 
					
						
							|  |  |  |             ("version", ctypes.c_uint64), | 
					
						
							|  |  |  |             ("name", ctypes.c_wchar_p), | 
					
						
							|  |  |  |             ("publisher", ctypes.c_wchar_p), | 
					
						
							|  |  |  |             ("resourceId", ctypes.c_wchar_p), | 
					
						
							|  |  |  |             ("publisherId", ctypes.c_wchar_p), | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         _pack_ = 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) | 
					
						
							|  |  |  |     result = ctypes.create_unicode_buffer(256) | 
					
						
							|  |  |  |     result_len = ctypes.c_uint32(256) | 
					
						
							|  |  |  |     r = ctypes.windll.kernel32.PackageFamilyNameFromId( | 
					
						
							|  |  |  |         pid, ctypes.byref(result_len), result | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     if r: | 
					
						
							|  |  |  |         raise OSError(r, "failed to get package family name") | 
					
						
							|  |  |  |     return result.value[: result_len.value] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _fixup_sccd(ns, sccd, new_hash=None): | 
					
						
							|  |  |  |     if not new_hash: | 
					
						
							|  |  |  |         return sccd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") | 
					
						
							|  |  |  |     with open(sccd, "rb") as f: | 
					
						
							|  |  |  |         xml = ET.parse(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ae = xml.find("s:AuthorizedEntities", NS) | 
					
						
							|  |  |  |     ae.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) | 
					
						
							|  |  |  |     e.set("AppPackageFamilyName", pfn) | 
					
						
							|  |  |  |     e.set("CertificateSignatureHash", new_hash) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for e in xml.findall("s:Catalog", NS): | 
					
						
							|  |  |  |         e.text = "FFFF" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sccd = ns.temp / sccd.name | 
					
						
							|  |  |  |     sccd.parent.mkdir(parents=True, exist_ok=True) | 
					
						
							|  |  |  |     with open(sccd, "wb") as f: | 
					
						
							|  |  |  |         xml.write(f, encoding="utf-8") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return sccd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def find_or_add(xml, element, attr=None, always_add=False): | 
					
						
							|  |  |  |     if always_add: | 
					
						
							|  |  |  |         e = None | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         q = element | 
					
						
							|  |  |  |         if attr: | 
					
						
							|  |  |  |             q += "[@{}='{}']".format(*attr) | 
					
						
							|  |  |  |         e = xml.find(q, APPXMANIFEST_NS) | 
					
						
							|  |  |  |     if e is None: | 
					
						
							|  |  |  |         prefix, _, name = element.partition(":") | 
					
						
							|  |  |  |         name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) | 
					
						
							|  |  |  |         e = ET.SubElement(xml, name) | 
					
						
							|  |  |  |         if attr: | 
					
						
							|  |  |  |             e.set(*attr) | 
					
						
							|  |  |  |     return e | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _get_app(xml, appid): | 
					
						
							|  |  |  |     if appid: | 
					
						
							|  |  |  |         app = xml.find( | 
					
						
							|  |  |  |             "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         if app is None: | 
					
						
							|  |  |  |             raise LookupError(appid) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         app = xml | 
					
						
							|  |  |  |     return app | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def add_visual(xml, appid, data): | 
					
						
							|  |  |  |     app = _get_app(xml, appid) | 
					
						
							|  |  |  |     e = find_or_add(app, "uap:VisualElements") | 
					
						
							|  |  |  |     for i in data.items(): | 
					
						
							|  |  |  |         e.set(*i) | 
					
						
							|  |  |  |     return e | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def add_alias(xml, appid, alias, subsystem="windows"): | 
					
						
							|  |  |  |     app = _get_app(xml, appid) | 
					
						
							|  |  |  |     e = find_or_add(app, "m:Extensions") | 
					
						
							|  |  |  |     e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) | 
					
						
							|  |  |  |     e = find_or_add(e, "uap5:AppExecutionAlias") | 
					
						
							|  |  |  |     e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) | 
					
						
							|  |  |  |     e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 10:50:17 -07:00
										 |  |  | def add_file_type(xml, appid, name, suffix, parameters='"%1"', info=None, logo=None): | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     app = _get_app(xml, appid) | 
					
						
							|  |  |  |     e = find_or_add(app, "m:Extensions") | 
					
						
							|  |  |  |     e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) | 
					
						
							|  |  |  |     e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) | 
					
						
							|  |  |  |     e.set("Parameters", parameters) | 
					
						
							| 
									
										
										
										
											2019-08-07 10:50:17 -07:00
										 |  |  |     if info: | 
					
						
							|  |  |  |         find_or_add(e, "uap:DisplayName").text = info | 
					
						
							|  |  |  |     if logo: | 
					
						
							|  |  |  |         find_or_add(e, "uap:Logo").text = logo | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     e = find_or_add(e, "uap:SupportedFileTypes") | 
					
						
							|  |  |  |     if isinstance(suffix, str): | 
					
						
							|  |  |  |         suffix = [suffix] | 
					
						
							|  |  |  |     for s in suffix: | 
					
						
							|  |  |  |         ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def add_application( | 
					
						
							|  |  |  |     ns, xml, appid, executable, aliases, visual_element, subsystem, file_types | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     node = xml.find("m:Applications", APPXMANIFEST_NS) | 
					
						
							|  |  |  |     suffix = "_d.exe" if ns.debug else ".exe" | 
					
						
							|  |  |  |     app = ET.SubElement( | 
					
						
							|  |  |  |         node, | 
					
						
							|  |  |  |         ET.QName(APPXMANIFEST_NS[""], "Application"), | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             "Id": appid, | 
					
						
							|  |  |  |             "Executable": executable + suffix, | 
					
						
							|  |  |  |             "EntryPoint": "Windows.FullTrustApplication", | 
					
						
							|  |  |  |             ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     if visual_element: | 
					
						
							|  |  |  |         add_visual(app, None, visual_element) | 
					
						
							|  |  |  |     for alias in aliases: | 
					
						
							|  |  |  |         add_alias(app, None, alias + suffix, subsystem) | 
					
						
							|  |  |  |     if file_types: | 
					
						
							|  |  |  |         add_file_type(app, None, *file_types) | 
					
						
							|  |  |  |     return app | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _get_registry_entries(ns, root="", d=None): | 
					
						
							|  |  |  |     r = root if root else PureWindowsPath("") | 
					
						
							|  |  |  |     if d is None: | 
					
						
							|  |  |  |         d = REGISTRY | 
					
						
							|  |  |  |     for key, value in d.items(): | 
					
						
							|  |  |  |         if key == "_condition": | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-12-09 08:43:13 -08:00
										 |  |  |         if value is SPECIAL_LOOKUP: | 
					
						
							|  |  |  |             if key == "SysArchitecture": | 
					
						
							|  |  |  |                 value = { | 
					
						
							|  |  |  |                     "win32": "32bit", | 
					
						
							|  |  |  |                     "amd64": "64bit", | 
					
						
							|  |  |  |                     "arm32": "32bit", | 
					
						
							|  |  |  |                     "arm64": "64bit", | 
					
						
							|  |  |  |                 }[ns.arch] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise ValueError(f"Key '{key}' unhandled for special lookup") | 
					
						
							|  |  |  |         if isinstance(value, dict): | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             cond = value.get("_condition") | 
					
						
							|  |  |  |             if cond and not cond(ns): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             fullkey = r | 
					
						
							|  |  |  |             for part in PureWindowsPath(key).parts: | 
					
						
							|  |  |  |                 fullkey /= part | 
					
						
							|  |  |  |                 if len(fullkey.parts) > 1: | 
					
						
							|  |  |  |                     yield str(fullkey), None, None | 
					
						
							|  |  |  |             yield from _get_registry_entries(ns, fullkey, value) | 
					
						
							|  |  |  |         elif len(r.parts) > 1: | 
					
						
							|  |  |  |             yield str(r), key, value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def add_registry_entries(ns, xml): | 
					
						
							|  |  |  |     e = find_or_add(xml, "m:Extensions") | 
					
						
							|  |  |  |     e = find_or_add(e, "rescap4:Extension") | 
					
						
							|  |  |  |     e.set("Category", "windows.classicAppCompatKeys") | 
					
						
							|  |  |  |     e.set("EntryPoint", "Windows.FullTrustApplication") | 
					
						
							|  |  |  |     e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) | 
					
						
							|  |  |  |     for name, valuename, value in _get_registry_entries(ns): | 
					
						
							|  |  |  |         k = ET.SubElement( | 
					
						
							|  |  |  |             e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         k.set("Name", name) | 
					
						
							|  |  |  |         if value: | 
					
						
							|  |  |  |             k.set("ValueName", valuename) | 
					
						
							|  |  |  |             k.set("Value", value) | 
					
						
							|  |  |  |             k.set("ValueType", "REG_SZ") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def disable_registry_virtualization(xml): | 
					
						
							|  |  |  |     e = find_or_add(xml, "m:Properties") | 
					
						
							|  |  |  |     e = find_or_add(e, "desktop6:RegistryWriteVirtualization") | 
					
						
							|  |  |  |     e.text = "disabled" | 
					
						
							|  |  |  |     e = find_or_add(xml, "m:Capabilities") | 
					
						
							|  |  |  |     e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_appxmanifest(ns): | 
					
						
							|  |  |  |     for k, v in APPXMANIFEST_NS.items(): | 
					
						
							|  |  |  |         ET.register_namespace(k, v) | 
					
						
							|  |  |  |     ET.register_namespace("", APPXMANIFEST_NS["m"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) | 
					
						
							|  |  |  |     NS = APPXMANIFEST_NS | 
					
						
							|  |  |  |     QN = ET.QName | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |     data = dict(APPX_DATA) | 
					
						
							|  |  |  |     for k, v in zip(APPX_PLATFORM_DATA["_keys"], APPX_PLATFORM_DATA[ns.arch]): | 
					
						
							|  |  |  |         data[k] = v | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     node = xml.find("m:Identity", NS) | 
					
						
							|  |  |  |     for k in node.keys(): | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |         value = data.get(k) | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |         if value: | 
					
						
							|  |  |  |             node.set(k, value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for node in xml.find("m:Properties", NS): | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |         value = data.get(node.tag.rpartition("}")[2]) | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |         if value: | 
					
						
							|  |  |  |             node.text = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     winver = sys.getwindowsversion()[:3] | 
					
						
							|  |  |  |     if winver < (10, 0, 17763): | 
					
						
							|  |  |  |         winver = 10, 0, 17763 | 
					
						
							|  |  |  |     find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( | 
					
						
							|  |  |  |         "MaxVersionTested", "{}.{}.{}.0".format(*winver) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if winver > (10, 0, 17763): | 
					
						
							|  |  |  |         disable_registry_virtualization(xml) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     app = add_application( | 
					
						
							|  |  |  |         ns, | 
					
						
							|  |  |  |         xml, | 
					
						
							|  |  |  |         "Python", | 
					
						
							| 
									
										
										
										
											2019-08-07 10:49:40 -07:00
										 |  |  |         "python{}".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |         ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], | 
					
						
							|  |  |  |         PYTHON_VE_DATA, | 
					
						
							|  |  |  |         "console", | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |         ("python.file", [".py"], '"%1"', "Python File", PY_PNG), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     add_application( | 
					
						
							|  |  |  |         ns, | 
					
						
							|  |  |  |         xml, | 
					
						
							|  |  |  |         "PythonW", | 
					
						
							| 
									
										
										
										
											2019-08-07 10:49:40 -07:00
										 |  |  |         "pythonw{}".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |         ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], | 
					
						
							|  |  |  |         PYTHONW_VE_DATA, | 
					
						
							|  |  |  |         "windows", | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |         ("python.windowedfile", [".pyw"], '"%1"', "Python File (no console)", PY_PNG), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ns.include_pip and ns.include_launchers: | 
					
						
							|  |  |  |         add_application( | 
					
						
							|  |  |  |             ns, | 
					
						
							|  |  |  |             xml, | 
					
						
							|  |  |  |             "Pip", | 
					
						
							| 
									
										
										
										
											2019-08-07 10:49:40 -07:00
										 |  |  |             "pip{}".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], | 
					
						
							|  |  |  |             PIP_VE_DATA, | 
					
						
							|  |  |  |             "console", | 
					
						
							| 
									
										
										
										
											2019-11-20 09:30:47 -08:00
										 |  |  |             ("python.wheel", [".whl"], 'install "%1"', "Python Wheel"), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ns.include_idle and ns.include_launchers: | 
					
						
							|  |  |  |         add_application( | 
					
						
							|  |  |  |             ns, | 
					
						
							|  |  |  |             xml, | 
					
						
							|  |  |  |             "Idle", | 
					
						
							| 
									
										
										
										
											2019-08-07 10:49:40 -07:00
										 |  |  |             "idle{}".format(VER_DOT), | 
					
						
							| 
									
										
										
										
											2018-12-10 18:52:57 -08:00
										 |  |  |             ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], | 
					
						
							|  |  |  |             IDLE_VE_DATA, | 
					
						
							|  |  |  |             "windows", | 
					
						
							|  |  |  |             None, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ns.source / SCCD_FILENAME).is_file(): | 
					
						
							|  |  |  |         add_registry_entries(ns, xml) | 
					
						
							|  |  |  |         node = xml.find("m:Capabilities", NS) | 
					
						
							|  |  |  |         node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) | 
					
						
							|  |  |  |         node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     buffer = io.BytesIO() | 
					
						
							|  |  |  |     xml.write(buffer, encoding="utf-8", xml_declaration=True) | 
					
						
							|  |  |  |     return buffer.getbuffer() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_resources_xml(ns): | 
					
						
							|  |  |  |     return RESOURCES_XML_TEMPLATE.encode("utf-8") | 
					
						
							| 
									
										
										
										
											2019-06-14 08:29:20 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_appx_layout(ns): | 
					
						
							|  |  |  |     if not ns.include_appxmanifest: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     yield "AppxManifest.xml", ("AppxManifest.xml", get_appxmanifest(ns)) | 
					
						
							|  |  |  |     yield "_resources.xml", ("_resources.xml", get_resources_xml(ns)) | 
					
						
							|  |  |  |     icons = ns.source / "PC" / "icons" | 
					
						
							| 
									
										
										
										
											2019-08-07 10:50:17 -07:00
										 |  |  |     for px in [44, 50, 150]: | 
					
						
							|  |  |  |         src = icons / "pythonx{}.png".format(px) | 
					
						
							|  |  |  |         yield f"_resources/pythonx{px}.png", src | 
					
						
							|  |  |  |         yield f"_resources/pythonx{px}$targetsize-{px}_altform-unplated.png", src | 
					
						
							|  |  |  |     for px in [44, 150]: | 
					
						
							|  |  |  |         src = icons / "pythonwx{}.png".format(px) | 
					
						
							|  |  |  |         yield f"_resources/pythonwx{px}.png", src | 
					
						
							|  |  |  |         yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src | 
					
						
							| 
									
										
										
										
											2020-10-20 10:20:05 -07:00
										 |  |  |     if ns.include_idle and ns.include_launchers: | 
					
						
							|  |  |  |         for px in [44, 150]: | 
					
						
							|  |  |  |             src = icons / "idlex{}.png".format(px) | 
					
						
							|  |  |  |             yield f"_resources/idlex{px}.png", src | 
					
						
							|  |  |  |             yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src | 
					
						
							| 
									
										
										
										
											2019-08-07 10:50:17 -07:00
										 |  |  |     yield f"_resources/py.png", icons / "py.png" | 
					
						
							| 
									
										
										
										
											2019-06-14 08:29:20 -07:00
										 |  |  |     sccd = ns.source / SCCD_FILENAME | 
					
						
							|  |  |  |     if sccd.is_file(): | 
					
						
							|  |  |  |         # This should only be set for side-loading purposes. | 
					
						
							|  |  |  |         sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) | 
					
						
							|  |  |  |         yield sccd.name, sccd |