22from __future__ import annotations
33
44import re
5+ import struct
56import sys
67from dataclasses import dataclass
78from enum import Enum
@@ -22,6 +23,15 @@ class PlatformError(Exception):
2223 r"(?P<os>manylinux|macos|musllinux)_(?P<major>\d+?)_(?P<minor>\d+?)_(?P<arch>[a-z0-9_]+)$"
2324)
2425
26+ _os_mapping = {
27+ "freebsd" : os .FreeBsd ,
28+ "netbsd" : os .NetBsd ,
29+ "openbsd" : os .OpenBsd ,
30+ "dragonfly" : os .Dragonfly ,
31+ "illumos" : os .Illumos ,
32+ "haiku" : os .Haiku ,
33+ }
34+
2535
2636@dataclass (frozen = True )
2737class Platform :
@@ -73,6 +83,9 @@ def parse(cls, platform: str) -> Self:
7383 return cls (os .Musllinux (int (major ), int (minor )), Arch .parse (arch ))
7484 else :
7585 os_ , arch = platform .split ("_" , 1 )
86+ if os_ in _os_mapping :
87+ release , _ , arch = arch .partition ("_" )
88+ return cls (_os_mapping [os_ ](release ), Arch .parse (arch ))
7689 try :
7790 return cls (os .Generic (os_ ), Arch .parse (arch ))
7891 except ValueError as e :
@@ -89,38 +102,61 @@ def __str__(self) -> str:
89102 def current (cls ) -> Self :
90103 """Return the current platform."""
91104 import platform
105+ import sysconfig
92106
93- system = platform .system ()
94- arch = Arch .parse (platform .machine ().lower ())
95- if (
96- platform .machine ().lower () == "amd64"
97- and platform .architecture ()[0 ] == "32bit"
98- ):
99- arch = Arch .parse ("x86" )
100- if system == "Linux" :
101- libc_ver = platform .libc_ver ()[1 ]
102- if libc_ver :
103- parts = libc_ver .split ("." )
104- return cls (os .Manylinux (int (parts [0 ]), int (parts [1 ])), arch )
105- else : # musl
106- from packaging ._musllinux import _get_musl_version
107-
108- musl_version = _get_musl_version (sys .executable )
109- if musl_version is None :
110- raise PlatformError (
111- "Failed to detect musl version or glibc version"
112- )
113- return cls (os .Musllinux (musl_version .major , musl_version .minor ), arch )
114- elif system == "Windows" :
115- return cls (os .Windows (), arch )
116- elif system == "Darwin" :
117- mac_ver = platform .mac_ver ()[0 ].split ("." )
118- major , minor = int (mac_ver [0 ]), int (mac_ver [1 ])
119- if major >= 11 :
120- minor = 0
121- return cls (os .Macos (major , minor ), arch )
107+ platform_ = sysconfig .get_platform ()
108+ platform_info = platform_ .split ("-" , 1 )
109+ if len (platform_info ) == 1 :
110+ if platform_info [0 ] == "win32" :
111+ return cls (os .Windows (), Arch .X86 )
112+ else :
113+ raise PlatformError (f"Unsupported platform { platform_ } " )
114+ else :
115+ [operating_system , version_arch ] = platform_info
116+ if "-" in version_arch :
117+ # Ex: macosx-11.2-arm64
118+ version , architecture = version_arch .rsplit ("-" , 1 )
122119 else :
123- raise PlatformError ("Unsupported platform" )
120+ # Ex: linux-x86_64
121+ version = None
122+ architecture = version_arch
123+
124+ if operating_system == "linux" :
125+ from packaging ._manylinux import _get_glibc_version
126+ from packaging ._musllinux import _get_musl_version
127+
128+ musl_version = _get_musl_version (sys .executable )
129+ glibc_version = _get_glibc_version ()
130+ if musl_version :
131+ os_ = os .Musllinux (musl_version [0 ], musl_version [1 ])
132+ elif glibc_version != (- 1 , - 1 ):
133+ os_ = os .Manylinux (glibc_version [0 ], glibc_version [1 ])
134+ else :
135+ raise PlatformError ("Unsupported platform: libc not found" )
136+ elif operating_system == "win" :
137+ os_ = os .Windows ()
138+ elif operating_system == "macosx" :
139+ # Apparently, Mac OS is reporting i386 sometimes in sysconfig.get_platform even
140+ # though that's not a thing anymore.
141+ # https://github.com/astral-sh/uv/issues/2450
142+ version , _ , architecture = platform .mac_ver ()
143+
144+ # https://github.com/pypa/packaging/blob/cc938f984bbbe43c5734b9656c9837ab3a28191f/src/packaging/tags.py#L356-L363
145+ is_32bit = struct .calcsize ("P" ) == 4
146+ if is_32bit :
147+ if architecture .startswith ("ppc" ):
148+ architecture = "ppc"
149+ else :
150+ architecture = "i386"
151+
152+ version = version .split ("." )
153+ os_ = os .Macos (int (version [0 ]), int (version [1 ]))
154+ elif operating_system in _os_mapping :
155+ os_ = _os_mapping [operating_system ](version )
156+ else :
157+ os_ = os .Generic (operating_system )
158+
159+ return cls (os_ , Arch .parse (architecture ))
124160
125161 @classmethod
126162 def choices (cls ) -> list [str ]:
0 commit comments