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.
Files changed (18) hide show
  1. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/PKG-INFO +3 -1
  2. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/Cargo.lock +2 -2
  3. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/Cargo.toml +5 -1
  4. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/kernel_abi_check.pyi +93 -5
  5. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/src/lib.rs +188 -23
  6. kernel_abi_check-0.6.2.dev0/bindings/python/tests/test_kernel_abi_check.py +54 -0
  7. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/Cargo.lock +1 -1
  8. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/Cargo.toml +1 -1
  9. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/pyproject.toml +5 -0
  10. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/bindings/python/MANIFEST.in +0 -0
  11. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/lib.rs +0 -0
  12. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/macos.rs +0 -0
  13. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/main.rs +0 -0
  14. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/manylinux/manylinux-policy.json +0 -0
  15. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/manylinux/mod.rs +0 -0
  16. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/python_abi/mod.rs +0 -0
  17. {kernel_abi_check-0.1.0 → kernel_abi_check-0.6.2.dev0}/kernel-abi-check/src/python_abi/stable_abi.toml +0 -0
  18. {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.1.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.1"
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.1.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.1.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(self, manylinux_version: str) -> List[ManylinuxSymbolViolation]:
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[MissingMinOS, IncompatibleMinOS]]:
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 ManylinuxSymbolViolation:
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 MissingMinOS:
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 IncompatibleMinOS:
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: OsString,
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: OsString) -> PyResult<Self> {
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, PyManylinuxSymbolViolation { name, dep, version })?.into()
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, PyMissingMinOS)?.into(),
136
+ MacOSViolation::MissingMinOS => Py::new(py, PyMissingMacOSVersion)?.into(),
136
137
  MacOSViolation::IncompatibleMinOS { version } => {
137
- Py::new(py, PyIncompatibleMinOS { version })?.into()
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
- #[pyclass(name = "ManylinuxSymbolViolation")]
193
- struct PyManylinuxSymbolViolation {
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 PyManylinuxSymbolViolation {
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
- "ManylinuxSymbolViolation(name='{}', dep='{}', version='{}')",
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
- #[pyclass(name = "MissingMinOS")]
226
- struct PyMissingMinOS;
348
+ #[derive(Clone, Debug, Eq, PartialEq)]
349
+ #[pyclass(name = "MissingMacOSVersion")]
350
+ struct PyMissingMacOSVersion;
227
351
 
228
352
  #[pymethods]
229
- impl PyMissingMinOS {
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
- "MissingMinOS()".to_string()
368
+ "MissingMacOSVersion()".to_string()
232
369
  }
233
370
  }
234
371
 
235
372
  /// Incompatible minimum OS version violation
236
- #[pyclass(name = "IncompatibleMinOS")]
237
- struct PyIncompatibleMinOS {
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 PyIncompatibleMinOS {
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!("IncompatibleMinOS(version='{}')", self.version)
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::<PyManylinuxSymbolViolation>()?;
427
+ m.add_class::<PyIncompatibleManylinuxSymbol>()?;
263
428
 
264
429
  // macOS violation classes
265
- m.add_class::<PyMissingMinOS>()?;
266
- m.add_class::<PyIncompatibleMinOS>()?;
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
@@ -274,7 +274,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
274
274
 
275
275
  [[package]]
276
276
  name = "kernel-abi-check"
277
- version = "0.6.1"
277
+ version = "0.6.2-dev0"
278
278
  dependencies = [
279
279
  "clap",
280
280
  "color-eyre",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "kernel-abi-check"
3
- version = "0.6.1"
3
+ version = "0.6.2-dev0"
4
4
  edition = "2021"
5
5
  description = "Check the ABI of Hub Kernels"
6
6
  homepage = "https://github.com/huggingface/kernel-builder"
@@ -15,6 +15,11 @@ dynamic = [
15
15
  requires = ["maturin>=1,<2"]
16
16
  build-backend = "maturin"
17
17
 
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest>=8",
21
+ ]
22
+
18
23
  [tool.maturin]
19
24
  module-name = "kernel_abi_check"
20
25
  bindings = 'pyo3'