-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathpython_info.py
More file actions
175 lines (154 loc) · 5.12 KB
/
Copy pathpython_info.py
File metadata and controls
175 lines (154 loc) · 5.12 KB
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from __future__ import annotations
import dataclasses
import platform
import sys
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from pathlib import Path
from packaging.version import Version
@dataclasses.dataclass
class PythonInfo:
"""
A simple dataclass to store Python version information.
This replaces the complex PythonVersion class from the original implementation.
"""
path: Path
version_str: str
major: int | None
minor: int | None = None
patch: int | None = None
is_prerelease: bool = False
is_postrelease: bool = False
is_devrelease: bool = False
is_debug: bool = False
version: Version | None = None
architecture: str | None = None
company: str | None = None
name: str | None = None
executable: str | Path | None = None
@property
def is_python(self) -> bool:
"""
Check if this is a valid Python executable.
"""
return True # Since this object is only created for valid Python executables
@property
def as_python(self) -> PythonInfo:
"""
Return self as a PythonInfo object.
This is for compatibility with the test suite.
"""
return self
@property
def version_tuple(self) -> tuple[int | None, int | None, int | None, bool, bool, bool]:
"""
Provides a version tuple for using as a dictionary key.
"""
return (
self.major,
self.minor,
self.patch,
self.is_prerelease,
self.is_devrelease,
self.is_debug,
)
@property
def version_sort(self) -> tuple[int, int, int, int, int]:
"""
A tuple for sorting against other instances of the same class.
"""
company_sort = 1 if (self.company and self.company == "PythonCore") else 0
release_sort = 2
if self.is_postrelease:
release_sort = 3
elif self.is_prerelease:
release_sort = 1
elif self.is_devrelease:
release_sort = 0
elif self.is_debug:
release_sort = 1
return (
company_sort,
self.major or 0, # Handle None case by defaulting to 0
self.minor or 0,
self.patch or 0,
release_sort,
)
def matches(
self,
major: int | None = None,
minor: int | None = None,
patch: int | None = None,
pre: bool | None = None,
dev: bool | None = None,
arch: str | None = None,
debug: bool | None = None,
python_name: str | None = None,
) -> bool:
"""
Check if this Python version matches the specified criteria.
"""
if arch:
own_arch = self.architecture or self._get_architecture()
if arch.isdigit():
arch = f"{arch}bit"
return (
(major is None or self.major == major)
and (minor is None or self.minor == minor)
and (patch is None or self.patch == patch)
and (pre is None or self.is_prerelease == pre)
and (dev is None or self.is_devrelease == dev)
and (arch is None or own_arch == arch)
and (debug is None or self.is_debug == debug)
and (
python_name is None
or (python_name and self.name)
and (self.name == python_name or self.name.startswith(python_name))
)
)
def _get_architecture(self) -> str:
"""
Get the architecture of this Python version.
"""
if self.architecture:
return self.architecture
arch = None
if self.path:
arch, _ = platform.architecture(str(self.path))
elif self.executable:
arch, _ = platform.architecture(str(self.executable))
if arch is None:
arch, _ = platform.architecture(sys.executable)
self.architecture = arch
return arch
def as_dict(self) -> dict[str, Any]:
"""
Convert this PythonInfo to a dictionary.
"""
return {
"major": self.major, # Can be None
"minor": self.minor,
"patch": self.patch,
"is_prerelease": self.is_prerelease,
"is_postrelease": self.is_postrelease,
"is_devrelease": self.is_devrelease,
"is_debug": self.is_debug,
"version": self.version,
"company": self.company,
}
def __eq__(self, other: object) -> bool:
"""
Check if this PythonInfo is equal to another PythonInfo.
Two PythonInfo objects are considered equal if they have the same path.
"""
if not isinstance(other, PythonInfo):
return NotImplemented
return self.path == other.path
def __lt__(self, other: object) -> bool:
"""
Check if this PythonInfo is less than another PythonInfo.
This is used for sorting PythonInfo objects by version.
"""
if not isinstance(other, PythonInfo):
return NotImplemented
return self.version_sort < other.version_sort