kernel-abi-check 0.1.0__tar.gz → 0.6.2.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/PKG-INFO +3 -1
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/Cargo.lock +2 -2
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/Cargo.toml +5 -1
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/kernel_abi_check.pyi +93 -5
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/src/lib.rs +188 -23
- kernel_abi_check-0.6.2.dev0/bindings/python/tests/test_kernel_abi_check.py +54 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/Cargo.lock +1 -1
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/Cargo.toml +1 -1
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/pyproject.toml +5 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/MANIFEST.in +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/lib.rs +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/macos.rs +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/main.rs +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/manylinux/manylinux-policy.json +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/manylinux/mod.rs +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/python_abi/mod.rs +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/python_abi/stable_abi.toml +0 -0
- {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/version.rs +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kernel-abi-check
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.2.dev0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Summary: Check the ABI of Hub Kernels
|
|
7
|
+
Home-Page: https://github.com/huggingface/kernel-builder
|
|
6
8
|
Requires-Python: >=3.8
|
|
@@ -271,7 +271,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
|
271
271
|
|
|
272
272
|
[[package]]
|
|
273
273
|
name = "kernel-abi-check"
|
|
274
|
-
version = "0.6.
|
|
274
|
+
version = "0.6.2-dev0"
|
|
275
275
|
dependencies = [
|
|
276
276
|
"clap",
|
|
277
277
|
"color-eyre",
|
|
@@ -286,7 +286,7 @@ dependencies = [
|
|
|
286
286
|
|
|
287
287
|
[[package]]
|
|
288
288
|
name = "kernel-abi-check-python"
|
|
289
|
-
version = "0.
|
|
289
|
+
version = "0.6.2-dev0"
|
|
290
290
|
dependencies = [
|
|
291
291
|
"kernel-abi-check",
|
|
292
292
|
"object",
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "kernel-abi-check-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.2-dev0"
|
|
4
4
|
edition = "2024"
|
|
5
|
+
description = "Check the ABI of Hub Kernels"
|
|
6
|
+
homepage = "https://github.com/huggingface/kernel-builder"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
repository = "https://github.com/huggingface/kernel-builder"
|
|
5
9
|
|
|
6
10
|
[lib]
|
|
7
11
|
name = "kernel_abi_check"
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
"""Type stubs for kernel_abi_check module."""
|
|
2
2
|
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import List, Union
|
|
4
5
|
import os
|
|
5
6
|
|
|
6
7
|
__version__: str
|
|
7
8
|
|
|
9
|
+
class BinaryFormat(Enum):
|
|
10
|
+
"""Binary format of an object file."""
|
|
11
|
+
|
|
12
|
+
COFF = "COFF"
|
|
13
|
+
"""COFF (Common Object File Format)"""
|
|
14
|
+
|
|
15
|
+
ELF = "ELF"
|
|
16
|
+
"""ELF (Executable and Linkable Format)"""
|
|
17
|
+
|
|
18
|
+
MACH_O = "MACH_O"
|
|
19
|
+
"""Mach-O (Mach Object file format)"""
|
|
20
|
+
|
|
21
|
+
PE = "PE"
|
|
22
|
+
"""PE (Portable Executable)"""
|
|
23
|
+
|
|
24
|
+
WASM = "WASM"
|
|
25
|
+
"""WebAssembly"""
|
|
26
|
+
|
|
27
|
+
XCOFF = "XCOFF"
|
|
28
|
+
"""XCOFF (Extended Common Object File Format)"""
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str: ...
|
|
31
|
+
def __repr__(self) -> str: ...
|
|
32
|
+
|
|
8
33
|
class ObjectFile:
|
|
9
34
|
"""Object file that can be validated for ABI compatibility."""
|
|
10
35
|
|
|
@@ -19,6 +44,17 @@ class ObjectFile:
|
|
|
19
44
|
"""
|
|
20
45
|
...
|
|
21
46
|
|
|
47
|
+
def format(self) -> BinaryFormat:
|
|
48
|
+
"""Get the binary format of this object file.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
The binary format of the object file
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
ValueError: If the object file cannot be parsed or has an unsupported format
|
|
55
|
+
"""
|
|
56
|
+
...
|
|
57
|
+
|
|
22
58
|
def check_python_abi(
|
|
23
59
|
self, abi_version: str
|
|
24
60
|
) -> List[Union[IncompatibleAbi3Symbol, NonAbi3Symbol]]:
|
|
@@ -35,7 +71,9 @@ class ObjectFile:
|
|
|
35
71
|
"""
|
|
36
72
|
...
|
|
37
73
|
|
|
38
|
-
def check_manylinux(
|
|
74
|
+
def check_manylinux(
|
|
75
|
+
self, manylinux_version: str
|
|
76
|
+
) -> List[IncompatibleManylinuxSymbol]:
|
|
39
77
|
"""Check manylinux compatibility for this object file.
|
|
40
78
|
|
|
41
79
|
Args:
|
|
@@ -51,7 +89,7 @@ class ObjectFile:
|
|
|
51
89
|
|
|
52
90
|
def check_macos(
|
|
53
91
|
self, macos_version: str
|
|
54
|
-
) -> List[Union[
|
|
92
|
+
) -> List[Union[MissingMacOSVersion, IncompatibleMacOSVersion]]:
|
|
55
93
|
"""Check macOS compatibility for this object file.
|
|
56
94
|
|
|
57
95
|
Args:
|
|
@@ -68,6 +106,19 @@ class ObjectFile:
|
|
|
68
106
|
class IncompatibleAbi3Symbol:
|
|
69
107
|
"""ABI3 symbol that is not compatible with the specified Python ABI version."""
|
|
70
108
|
|
|
109
|
+
def __init__(self, *, name: str, added: str) -> None:
|
|
110
|
+
"""Create a new IncompatibleAbi3Symbol.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
name: Name of the symbol
|
|
114
|
+
added: Version when this symbol was added to Python
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
ValueError: If the version string cannot be parsed
|
|
118
|
+
TypeError: If positional arguments are used
|
|
119
|
+
"""
|
|
120
|
+
...
|
|
121
|
+
|
|
71
122
|
@property
|
|
72
123
|
def name(self) -> str:
|
|
73
124
|
"""Name of the symbol."""
|
|
@@ -78,6 +129,8 @@ class IncompatibleAbi3Symbol:
|
|
|
78
129
|
"""Version when this symbol was added to Python."""
|
|
79
130
|
...
|
|
80
131
|
|
|
132
|
+
def __eq__(self, other: object) -> bool: ...
|
|
133
|
+
def __ne__(self, other: object) -> bool: ...
|
|
81
134
|
def __repr__(self) -> str: ...
|
|
82
135
|
|
|
83
136
|
class NonAbi3Symbol:
|
|
@@ -90,9 +143,22 @@ class NonAbi3Symbol:
|
|
|
90
143
|
|
|
91
144
|
def __repr__(self) -> str: ...
|
|
92
145
|
|
|
93
|
-
class
|
|
146
|
+
class IncompatibleManylinuxSymbol:
|
|
94
147
|
"""Symbol that is not allowed by the manylinux version."""
|
|
95
148
|
|
|
149
|
+
def __init__(self, *, name: str, dep: str, version: str) -> None:
|
|
150
|
+
"""Create a new IncompatibleManylinuxSymbol.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
name: Name of the symbol
|
|
154
|
+
dep: Dependency that contains the symbol
|
|
155
|
+
version: Version of the symbol
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
TypeError: If positional arguments are used
|
|
159
|
+
"""
|
|
160
|
+
...
|
|
161
|
+
|
|
96
162
|
@property
|
|
97
163
|
def name(self) -> str:
|
|
98
164
|
"""Name of the symbol."""
|
|
@@ -108,20 +174,42 @@ class ManylinuxSymbolViolation:
|
|
|
108
174
|
"""Version of the symbol."""
|
|
109
175
|
...
|
|
110
176
|
|
|
177
|
+
def __eq__(self, other: object) -> bool: ...
|
|
178
|
+
def __ne__(self, other: object) -> bool: ...
|
|
111
179
|
def __repr__(self) -> str: ...
|
|
112
180
|
|
|
113
|
-
class
|
|
181
|
+
class MissingMacOSVersion:
|
|
114
182
|
"""Object file does not specify minimum OS version."""
|
|
115
183
|
|
|
184
|
+
def __init__(self) -> None:
|
|
185
|
+
"""Create a new MissingMacOSVersion."""
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
def __eq__(self, other: object) -> bool: ...
|
|
189
|
+
def __ne__(self, other: object) -> bool: ...
|
|
116
190
|
def __repr__(self) -> str: ...
|
|
117
191
|
|
|
118
|
-
class
|
|
192
|
+
class IncompatibleMacOSVersion:
|
|
119
193
|
"""The minimum OS version of the object file is higher than the
|
|
120
194
|
specified macOS version."""
|
|
121
195
|
|
|
196
|
+
def __init__(self, *, version: str) -> None:
|
|
197
|
+
"""Create a new IncompatibleMacOSVersion.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
version: Minimum OS version of the object file
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
ValueError: If the version string cannot be parsed
|
|
204
|
+
TypeError: If positional arguments are used
|
|
205
|
+
"""
|
|
206
|
+
...
|
|
207
|
+
|
|
122
208
|
@property
|
|
123
209
|
def version(self) -> str:
|
|
124
210
|
"""Minimum OS version of the object file."""
|
|
125
211
|
...
|
|
126
212
|
|
|
213
|
+
def __eq__(self, other: object) -> bool: ...
|
|
214
|
+
def __ne__(self, other: object) -> bool: ...
|
|
127
215
|
def __repr__(self) -> str: ...
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
use std::ffi::OsString;
|
|
2
1
|
use std::fs;
|
|
2
|
+
use std::path::PathBuf;
|
|
3
3
|
use std::str::FromStr;
|
|
4
4
|
|
|
5
5
|
use kernel_abi_check::{
|
|
6
6
|
MacOSViolation, ManylinuxViolation, PythonAbiViolation, Version, check_macos, check_manylinux,
|
|
7
7
|
};
|
|
8
|
-
use object::Object as ObjectTrait;
|
|
8
|
+
use object::{BinaryFormat, Object as ObjectTrait};
|
|
9
9
|
use pyo3::Bound as PyBound;
|
|
10
10
|
use pyo3::exceptions::PyIOError;
|
|
11
11
|
use pyo3::exceptions::PyValueError;
|
|
12
12
|
use pyo3::prelude::*;
|
|
13
|
+
use pyo3::types::PyTuple;
|
|
13
14
|
|
|
14
15
|
/// Object file that can be validated.
|
|
15
16
|
#[pyclass(name = "ObjectFile")]
|
|
16
17
|
struct PyObjectFile {
|
|
17
|
-
filename:
|
|
18
|
+
filename: PathBuf,
|
|
18
19
|
data: Vec<u8>,
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
impl PyObjectFile {
|
|
22
|
-
fn parse_file(&self) -> PyResult<object::File
|
|
23
|
+
fn parse_file(&self) -> PyResult<object::File<'_>> {
|
|
23
24
|
object::File::parse(&*self.data).map_err(|err| {
|
|
24
25
|
PyValueError::new_err(format!(
|
|
25
26
|
"Cannot parse object file `{}`: {}",
|
|
@@ -34,7 +35,7 @@ impl PyObjectFile {
|
|
|
34
35
|
impl PyObjectFile {
|
|
35
36
|
/// Create a new `ObjectFile` from a path.
|
|
36
37
|
#[new]
|
|
37
|
-
fn new(filename:
|
|
38
|
+
fn new(filename: PathBuf) -> PyResult<Self> {
|
|
38
39
|
let data = fs::read(&filename).map_err(|err| {
|
|
39
40
|
PyIOError::new_err(format!(
|
|
40
41
|
"Cannot open object file `{}`: {}",
|
|
@@ -103,7 +104,7 @@ impl PyObjectFile {
|
|
|
103
104
|
for violation in violations {
|
|
104
105
|
let py_violation: Py<PyAny> = match violation {
|
|
105
106
|
ManylinuxViolation::Symbol { name, dep, version } => {
|
|
106
|
-
Py::new(py,
|
|
107
|
+
Py::new(py, PyIncompatibleManylinuxSymbol { name, dep, version })?.into()
|
|
107
108
|
}
|
|
108
109
|
};
|
|
109
110
|
result.push(py_violation);
|
|
@@ -132,18 +133,91 @@ impl PyObjectFile {
|
|
|
132
133
|
let mut result = Vec::new();
|
|
133
134
|
for violation in violations {
|
|
134
135
|
let py_violation: Py<PyAny> = match violation {
|
|
135
|
-
MacOSViolation::MissingMinOS => Py::new(py,
|
|
136
|
+
MacOSViolation::MissingMinOS => Py::new(py, PyMissingMacOSVersion)?.into(),
|
|
136
137
|
MacOSViolation::IncompatibleMinOS { version } => {
|
|
137
|
-
Py::new(py,
|
|
138
|
+
Py::new(py, PyIncompatibleMacOSVersion { version })?.into()
|
|
138
139
|
}
|
|
139
140
|
};
|
|
140
141
|
result.push(py_violation);
|
|
141
142
|
}
|
|
142
143
|
Ok(result)
|
|
143
144
|
}
|
|
145
|
+
|
|
146
|
+
/// Get the binary format of this object file
|
|
147
|
+
fn format(&self) -> PyResult<PyBinaryFormat> {
|
|
148
|
+
let file = self.parse_file()?;
|
|
149
|
+
let binary_format = file.format();
|
|
150
|
+
|
|
151
|
+
let py_format = match binary_format {
|
|
152
|
+
BinaryFormat::Coff => PyBinaryFormat::Coff,
|
|
153
|
+
BinaryFormat::Elf => PyBinaryFormat::Elf,
|
|
154
|
+
BinaryFormat::MachO => PyBinaryFormat::MachO,
|
|
155
|
+
BinaryFormat::Pe => PyBinaryFormat::Pe,
|
|
156
|
+
BinaryFormat::Wasm => PyBinaryFormat::Wasm,
|
|
157
|
+
BinaryFormat::Xcoff => PyBinaryFormat::Xcoff,
|
|
158
|
+
_ => {
|
|
159
|
+
return Err(PyValueError::new_err(format!(
|
|
160
|
+
"Unsupported binary format: {binary_format:?}"
|
|
161
|
+
)));
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
Ok(py_format)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Binary format of an object file
|
|
170
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
171
|
+
#[pyclass(name = "BinaryFormat")]
|
|
172
|
+
pub enum PyBinaryFormat {
|
|
173
|
+
/// COFF (Common Object File Format)
|
|
174
|
+
#[pyo3(name = "COFF")]
|
|
175
|
+
Coff,
|
|
176
|
+
/// ELF (Executable and Linkable Format)
|
|
177
|
+
#[pyo3(name = "ELF")]
|
|
178
|
+
Elf,
|
|
179
|
+
/// Mach-O (Mach Object file format)
|
|
180
|
+
#[pyo3(name = "MACH_O")]
|
|
181
|
+
MachO,
|
|
182
|
+
/// PE (Portable Executable)
|
|
183
|
+
#[pyo3(name = "PE")]
|
|
184
|
+
Pe,
|
|
185
|
+
/// WebAssembly
|
|
186
|
+
#[pyo3(name = "WASM")]
|
|
187
|
+
Wasm,
|
|
188
|
+
/// XCOFF (Extended Common Object File Format)
|
|
189
|
+
#[pyo3(name = "XCOFF")]
|
|
190
|
+
Xcoff,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[pymethods]
|
|
194
|
+
impl PyBinaryFormat {
|
|
195
|
+
fn __eq__(&self, other: &Self) -> bool {
|
|
196
|
+
self == other
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn __ne__(&self, other: &Self) -> bool {
|
|
200
|
+
self != other
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn __repr__(&self) -> String {
|
|
204
|
+
match self {
|
|
205
|
+
PyBinaryFormat::Coff => "BinaryFormat.COFF".to_string(),
|
|
206
|
+
PyBinaryFormat::Elf => "BinaryFormat.ELF".to_string(),
|
|
207
|
+
PyBinaryFormat::MachO => "BinaryFormat.MACH_O".to_string(),
|
|
208
|
+
PyBinaryFormat::Pe => "BinaryFormat.PE".to_string(),
|
|
209
|
+
PyBinaryFormat::Wasm => "BinaryFormat.WASM".to_string(),
|
|
210
|
+
PyBinaryFormat::Xcoff => "BinaryFormat.XCOFF".to_string(),
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
fn __str__(&self) -> String {
|
|
215
|
+
self.__repr__()
|
|
216
|
+
}
|
|
144
217
|
}
|
|
145
218
|
|
|
146
219
|
/// Incompatible ABI3 symbol violation
|
|
220
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
147
221
|
#[pyclass(name = "IncompatibleAbi3Symbol")]
|
|
148
222
|
struct PyIncompatibleAbi3Symbol {
|
|
149
223
|
name: String,
|
|
@@ -152,6 +226,22 @@ struct PyIncompatibleAbi3Symbol {
|
|
|
152
226
|
|
|
153
227
|
#[pymethods]
|
|
154
228
|
impl PyIncompatibleAbi3Symbol {
|
|
229
|
+
#[new]
|
|
230
|
+
#[pyo3(signature = (*py_args, name, added))]
|
|
231
|
+
fn new(py_args: &Bound<'_, PyTuple>, name: String, added: String) -> PyResult<Self> {
|
|
232
|
+
if !py_args.is_empty() {
|
|
233
|
+
return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
234
|
+
"All arguments must be provided as keyword arguments",
|
|
235
|
+
));
|
|
236
|
+
}
|
|
237
|
+
let added = Version::from_str(&added).map_err(|err| {
|
|
238
|
+
PyValueError::new_err(format!(
|
|
239
|
+
"Cannot parse the version the symbol was added `{added}`: {err}",
|
|
240
|
+
))
|
|
241
|
+
})?;
|
|
242
|
+
Ok(Self { name, added })
|
|
243
|
+
}
|
|
244
|
+
|
|
155
245
|
#[getter]
|
|
156
246
|
fn name(&self) -> &str {
|
|
157
247
|
&self.name
|
|
@@ -162,6 +252,14 @@ impl PyIncompatibleAbi3Symbol {
|
|
|
162
252
|
self.added.to_string()
|
|
163
253
|
}
|
|
164
254
|
|
|
255
|
+
fn __eq__(&self, other: &Self) -> bool {
|
|
256
|
+
self == other
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
fn __ne__(&self, other: &Self) -> bool {
|
|
260
|
+
self != other
|
|
261
|
+
}
|
|
262
|
+
|
|
165
263
|
fn __repr__(&self) -> String {
|
|
166
264
|
format!(
|
|
167
265
|
"IncompatibleAbi3Symbol(name='{}', version_added='{}')",
|
|
@@ -189,15 +287,32 @@ impl PyNonAbi3Symbol {
|
|
|
189
287
|
}
|
|
190
288
|
|
|
191
289
|
/// Manylinux symbol violation
|
|
192
|
-
#[
|
|
193
|
-
|
|
290
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
291
|
+
#[pyclass(name = "IncompatibleManylinuxSymbol")]
|
|
292
|
+
struct PyIncompatibleManylinuxSymbol {
|
|
194
293
|
name: String,
|
|
195
294
|
dep: String,
|
|
196
295
|
version: String,
|
|
197
296
|
}
|
|
198
297
|
|
|
199
298
|
#[pymethods]
|
|
200
|
-
impl
|
|
299
|
+
impl PyIncompatibleManylinuxSymbol {
|
|
300
|
+
#[new]
|
|
301
|
+
#[pyo3(signature = (*py_args, name, dep, version))]
|
|
302
|
+
fn new(
|
|
303
|
+
py_args: &Bound<'_, PyTuple>,
|
|
304
|
+
name: String,
|
|
305
|
+
dep: String,
|
|
306
|
+
version: String,
|
|
307
|
+
) -> PyResult<Self> {
|
|
308
|
+
if !py_args.is_empty() {
|
|
309
|
+
return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
310
|
+
"All arguments must be provided as keyword arguments",
|
|
311
|
+
));
|
|
312
|
+
}
|
|
313
|
+
Ok(Self { name, dep, version })
|
|
314
|
+
}
|
|
315
|
+
|
|
201
316
|
#[getter]
|
|
202
317
|
fn name(&self) -> &str {
|
|
203
318
|
&self.name
|
|
@@ -213,40 +328,87 @@ impl PyManylinuxSymbolViolation {
|
|
|
213
328
|
&self.version
|
|
214
329
|
}
|
|
215
330
|
|
|
331
|
+
fn __eq__(&self, other: &Self) -> bool {
|
|
332
|
+
self == other
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
fn __ne__(&self, other: &Self) -> bool {
|
|
336
|
+
self != other
|
|
337
|
+
}
|
|
338
|
+
|
|
216
339
|
fn __repr__(&self) -> String {
|
|
217
340
|
format!(
|
|
218
|
-
"
|
|
341
|
+
"IncompatibleManylinuxSymbol(name='{}', dep='{}', version='{}')",
|
|
219
342
|
self.name, self.dep, self.version
|
|
220
343
|
)
|
|
221
344
|
}
|
|
222
345
|
}
|
|
223
346
|
|
|
224
347
|
/// Missing minimum OS version violation
|
|
225
|
-
#[
|
|
226
|
-
|
|
348
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
349
|
+
#[pyclass(name = "MissingMacOSVersion")]
|
|
350
|
+
struct PyMissingMacOSVersion;
|
|
227
351
|
|
|
228
352
|
#[pymethods]
|
|
229
|
-
impl
|
|
353
|
+
impl PyMissingMacOSVersion {
|
|
354
|
+
#[new]
|
|
355
|
+
pub fn new() -> Self {
|
|
356
|
+
Self
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
fn __eq__(&self, other: &Self) -> bool {
|
|
360
|
+
self == other
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
fn __ne__(&self, other: &Self) -> bool {
|
|
364
|
+
self != other
|
|
365
|
+
}
|
|
366
|
+
|
|
230
367
|
fn __repr__(&self) -> String {
|
|
231
|
-
"
|
|
368
|
+
"MissingMacOSVersion()".to_string()
|
|
232
369
|
}
|
|
233
370
|
}
|
|
234
371
|
|
|
235
372
|
/// Incompatible minimum OS version violation
|
|
236
|
-
#[
|
|
237
|
-
|
|
373
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
374
|
+
#[pyclass(name = "IncompatibleMacOSVersion")]
|
|
375
|
+
struct PyIncompatibleMacOSVersion {
|
|
238
376
|
version: Version,
|
|
239
377
|
}
|
|
240
378
|
|
|
241
379
|
#[pymethods]
|
|
242
|
-
impl
|
|
380
|
+
impl PyIncompatibleMacOSVersion {
|
|
381
|
+
#[new]
|
|
382
|
+
#[pyo3(signature = (*py_args, version))]
|
|
383
|
+
fn new(py_args: &Bound<'_, PyTuple>, version: String) -> PyResult<Self> {
|
|
384
|
+
if !py_args.is_empty() {
|
|
385
|
+
return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
|
386
|
+
"All arguments must be provided as keyword arguments",
|
|
387
|
+
));
|
|
388
|
+
}
|
|
389
|
+
let version = Version::from_str(&version).map_err(|err| {
|
|
390
|
+
PyValueError::new_err(format!(
|
|
391
|
+
"Cannot parse the version the symbol was added `{version}`: {err}",
|
|
392
|
+
))
|
|
393
|
+
})?;
|
|
394
|
+
Ok(Self { version })
|
|
395
|
+
}
|
|
396
|
+
|
|
243
397
|
#[getter]
|
|
244
398
|
fn version(&self) -> String {
|
|
245
399
|
self.version.to_string()
|
|
246
400
|
}
|
|
247
401
|
|
|
402
|
+
fn __eq__(&self, other: &Self) -> bool {
|
|
403
|
+
self == other
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fn __ne__(&self, other: &Self) -> bool {
|
|
407
|
+
self != other
|
|
408
|
+
}
|
|
409
|
+
|
|
248
410
|
fn __repr__(&self) -> String {
|
|
249
|
-
format!("
|
|
411
|
+
format!("IncompatibleMacOSVersion(version='{}')", self.version)
|
|
250
412
|
}
|
|
251
413
|
}
|
|
252
414
|
|
|
@@ -254,16 +416,19 @@ impl PyIncompatibleMinOS {
|
|
|
254
416
|
fn kernel_abi_check_py(m: &PyBound<'_, PyModule>) -> PyResult<()> {
|
|
255
417
|
m.add_class::<PyObjectFile>()?;
|
|
256
418
|
|
|
419
|
+
// Binary format enum
|
|
420
|
+
m.add_class::<PyBinaryFormat>()?;
|
|
421
|
+
|
|
257
422
|
// Python ABI violation classes
|
|
258
423
|
m.add_class::<PyIncompatibleAbi3Symbol>()?;
|
|
259
424
|
m.add_class::<PyNonAbi3Symbol>()?;
|
|
260
425
|
|
|
261
426
|
// Manylinux violation classes
|
|
262
|
-
m.add_class::<
|
|
427
|
+
m.add_class::<PyIncompatibleManylinuxSymbol>()?;
|
|
263
428
|
|
|
264
429
|
// macOS violation classes
|
|
265
|
-
m.add_class::<
|
|
266
|
-
m.add_class::<
|
|
430
|
+
m.add_class::<PyMissingMacOSVersion>()?;
|
|
431
|
+
m.add_class::<PyIncompatibleMacOSVersion>()?;
|
|
267
432
|
|
|
268
433
|
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
|
|
269
434
|
Ok(())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from kernel_abi_check import (
|
|
6
|
+
BinaryFormat,
|
|
7
|
+
IncompatibleAbi3Symbol,
|
|
8
|
+
IncompatibleMacOSVersion,
|
|
9
|
+
ObjectFile,
|
|
10
|
+
IncompatibleManylinuxSymbol,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def test_dir():
|
|
16
|
+
return Path(__file__).parent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_macos_shared_lib(test_dir):
|
|
20
|
+
o = ObjectFile(test_dir / "hello-darwin-x86_64.abi3.so")
|
|
21
|
+
assert o.check_python_abi("3.8") == []
|
|
22
|
+
assert o.check_python_abi("3.5") == [
|
|
23
|
+
IncompatibleAbi3Symbol(name="PyModule_GetNameObject", added="3.7")
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
assert o.check_macos("15.0") == []
|
|
27
|
+
assert o.check_macos("10.0") == [IncompatibleMacOSVersion(version="11.3")]
|
|
28
|
+
|
|
29
|
+
assert o.format() == BinaryFormat.MACH_O
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_linux_shared_lib(test_dir):
|
|
33
|
+
o = ObjectFile(test_dir / "hello-linux-x86_64.abi3.so")
|
|
34
|
+
assert o.check_python_abi("3.8") == []
|
|
35
|
+
assert o.check_python_abi("3.5") == [
|
|
36
|
+
IncompatibleAbi3Symbol(name="PyModule_GetNameObject", added="3.7")
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
assert o.check_manylinux("manylinux_2_34") == []
|
|
40
|
+
assert o.check_manylinux("manylinux_2_28") == [
|
|
41
|
+
IncompatibleManylinuxSymbol(name="fstat64", dep="GLIBC", version="2.33"),
|
|
42
|
+
IncompatibleManylinuxSymbol(
|
|
43
|
+
name="pthread_key_create", dep="GLIBC", version="2.34"
|
|
44
|
+
),
|
|
45
|
+
IncompatibleManylinuxSymbol(
|
|
46
|
+
name="pthread_key_delete", dep="GLIBC", version="2.34"
|
|
47
|
+
),
|
|
48
|
+
IncompatibleManylinuxSymbol(
|
|
49
|
+
name="pthread_setspecific", dep="GLIBC", version="2.34"
|
|
50
|
+
),
|
|
51
|
+
IncompatibleManylinuxSymbol(name="stat64", dep="GLIBC", version="2.33"),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
assert o.format() == BinaryFormat.ELF
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/manylinux/mod.rs
RENAMED
|
File without changes
|
{kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/python_abi/mod.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|