Skip to content

Commit b0f9a14

Browse files
Mark byte_bounds as a non-backwards-compatible NumPy 2.0 change (#8474)
This is the one refactor in the NumPy 2.0 upgrade rule that isn't compatible with earlier versions of NumPy, so I'm marking it as unsafe and adding a dedicated message.
1 parent f56bc19 commit b0f9a14

File tree

2 files changed

+72
-11
lines changed

2 files changed

+72
-11
lines changed

crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs

+69-8
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ use crate::importer::ImportRequest;
1919
/// constants were removed from the main namespace.
2020
///
2121
/// The majority of these functions and constants can be automatically replaced
22-
/// by other members of the NumPy API, even prior to NumPy 2.0, or by
23-
/// equivalents from the Python standard library. This rule flags all uses of
24-
/// removed members, along with automatic fixes for any backwards-compatible
25-
/// replacements.
22+
/// by other members of the NumPy API or by equivalents from the Python
23+
/// standard library. With the exception of renaming `numpy.byte_bounds` to
24+
/// `numpy.lib.array_utils.byte_bounds`, all such replacements are backwards
25+
/// compatible with earlier versions of NumPy.
26+
///
27+
/// This rule flags all uses of removed members, along with automatic fixes for
28+
/// any backwards-compatible replacements.
2629
///
2730
/// ## Examples
2831
/// ```python
@@ -82,7 +85,11 @@ struct Replacement<'a> {
8285
#[derive(Debug)]
8386
enum Details<'a> {
8487
/// The deprecated member can be replaced by another member in the NumPy API.
85-
AutoImport { path: &'a str, name: &'a str },
88+
AutoImport {
89+
path: &'a str,
90+
name: &'a str,
91+
compatibility: Compatibility,
92+
},
8693
/// The deprecated member can be replaced by a member of the Python standard library.
8794
AutoPurePython { python_expr: &'a str },
8895
/// The deprecated member can be replaced by a manual migration.
@@ -92,7 +99,18 @@ enum Details<'a> {
9299
impl Details<'_> {
93100
fn guideline(&self) -> Option<String> {
94101
match self {
95-
Details::AutoImport { path, name } => Some(format!("Use `{path}.{name}` instead.")),
102+
Details::AutoImport {
103+
path,
104+
name,
105+
compatibility: Compatibility::BackwardsCompatible,
106+
} => Some(format!("Use `{path}.{name}` instead.")),
107+
Details::AutoImport {
108+
path,
109+
name,
110+
compatibility: Compatibility::Breaking,
111+
} => Some(format!(
112+
"Use `{path}.{name}` on NumPy 2.0, or ignore this warning on earlier versions."
113+
)),
96114
Details::AutoPurePython { python_expr } => {
97115
Some(format!("Use `{python_expr}` instead."))
98116
}
@@ -101,6 +119,13 @@ impl Details<'_> {
101119
}
102120
}
103121

122+
#[derive(Debug)]
123+
enum Compatibility {
124+
/// The changes is backwards compatible with earlier versions of NumPy.
125+
BackwardsCompatible,
126+
/// The change is breaking in NumPy 2.0.
127+
Breaking,
128+
}
104129
/// NPY201
105130
pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
106131
let maybe_replacement = checker
@@ -113,13 +138,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
113138
details: Details::AutoImport {
114139
path: "numpy.lib",
115140
name: "add_docstring",
141+
compatibility: Compatibility::BackwardsCompatible,
116142
},
117143
}),
118144
["numpy", "add_newdoc"] => Some(Replacement {
119145
existing: "add_newdoc",
120146
details: Details::AutoImport {
121147
path: "numpy.lib",
122148
name: "add_newdoc",
149+
compatibility: Compatibility::BackwardsCompatible,
123150
},
124151
}),
125152
["numpy", "add_newdoc_ufunc"] => Some(Replacement {
@@ -139,6 +166,7 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
139166
details: Details::AutoImport {
140167
path: "numpy.lib.array_utils",
141168
name: "byte_bounds",
169+
compatibility: Compatibility::Breaking,
142170
},
143171
}),
144172
["numpy", "cast"] => Some(Replacement {
@@ -152,13 +180,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
152180
details: Details::AutoImport {
153181
path: "numpy",
154182
name: "complex128",
183+
compatibility: Compatibility::BackwardsCompatible,
155184
},
156185
}),
157186
["numpy", "clongfloat"] => Some(Replacement {
158187
existing: "clongfloat",
159188
details: Details::AutoImport {
160189
path: "numpy",
161190
name: "clongdouble",
191+
compatibility: Compatibility::BackwardsCompatible,
162192
},
163193
}),
164194
["numpy", "compat"] => Some(Replacement {
@@ -172,13 +202,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
172202
details: Details::AutoImport {
173203
path: "numpy",
174204
name: "complex128",
205+
compatibility: Compatibility::BackwardsCompatible,
175206
},
176207
}),
177208
["numpy", "DataSource"] => Some(Replacement {
178209
existing: "DataSource",
179210
details: Details::AutoImport {
180211
path: "numpy.lib.npyio",
181212
name: "DataSource",
213+
compatibility: Compatibility::BackwardsCompatible,
182214
},
183215
}),
184216
["numpy", "deprecate"] => Some(Replacement {
@@ -222,6 +254,7 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
222254
details: Details::AutoImport {
223255
path: "numpy",
224256
name: "float64",
257+
compatibility: Compatibility::BackwardsCompatible,
225258
},
226259
}),
227260
["numpy", "geterrobj"] => Some(Replacement {
@@ -235,27 +268,31 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
235268
details: Details::AutoImport {
236269
path: "numpy",
237270
name: "inf",
271+
compatibility: Compatibility::BackwardsCompatible,
238272
},
239273
}),
240274
["numpy", "Inf"] => Some(Replacement {
241275
existing: "Inf",
242276
details: Details::AutoImport {
243277
path: "numpy",
244278
name: "inf",
279+
compatibility: Compatibility::BackwardsCompatible,
245280
},
246281
}),
247282
["numpy", "Infinity"] => Some(Replacement {
248283
existing: "Infinity",
249284
details: Details::AutoImport {
250285
path: "numpy",
251286
name: "inf",
287+
compatibility: Compatibility::BackwardsCompatible,
252288
},
253289
}),
254290
["numpy", "infty"] => Some(Replacement {
255291
existing: "infty",
256292
details: Details::AutoImport {
257293
path: "numpy",
258294
name: "inf",
295+
compatibility: Compatibility::BackwardsCompatible,
259296
},
260297
}),
261298
["numpy", "issctype"] => Some(Replacement {
@@ -275,13 +312,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
275312
details: Details::AutoImport {
276313
path: "numpy",
277314
name: "issubdtype",
315+
compatibility: Compatibility::BackwardsCompatible,
278316
},
279317
}),
280318
["numpy", "mat"] => Some(Replacement {
281319
existing: "mat",
282320
details: Details::AutoImport {
283321
path: "numpy",
284322
name: "asmatrix",
323+
compatibility: Compatibility::BackwardsCompatible,
285324
},
286325
}),
287326
["numpy", "maximum_sctype"] => Some(Replacement {
@@ -295,6 +334,7 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
295334
details: Details::AutoImport {
296335
path: "numpy",
297336
name: "nan",
337+
compatibility: Compatibility::BackwardsCompatible,
298338
},
299339
}),
300340
["numpy", "nbytes"] => Some(Replacement {
@@ -320,13 +360,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
320360
details: Details::AutoImport {
321361
path: "numpy",
322362
name: "clongdouble",
363+
compatibility: Compatibility::BackwardsCompatible,
323364
},
324365
}),
325366
["numpy", "longfloat"] => Some(Replacement {
326367
existing: "longfloat",
327368
details: Details::AutoImport {
328369
path: "numpy",
329370
name: "longdouble",
371+
compatibility: Compatibility::BackwardsCompatible,
330372
},
331373
}),
332374
["numpy", "lookfor"] => Some(Replacement {
@@ -346,6 +388,7 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
346388
details: Details::AutoImport {
347389
path: "numpy",
348390
name: "inf",
391+
compatibility: Compatibility::BackwardsCompatible,
349392
},
350393
}),
351394
["numpy", "PZERO"] => Some(Replacement {
@@ -369,13 +412,15 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
369412
details: Details::AutoImport {
370413
path: "numpy",
371414
name: "round",
415+
compatibility: Compatibility::BackwardsCompatible,
372416
},
373417
}),
374418
["numpy", "safe_eval"] => Some(Replacement {
375419
existing: "safe_eval",
376420
details: Details::AutoImport {
377421
path: "ast",
378422
name: "literal_eval",
423+
compatibility: Compatibility::BackwardsCompatible,
379424
},
380425
}),
381426
["numpy", "sctype2char"] => Some(Replacement {
@@ -407,34 +452,39 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
407452
details: Details::AutoImport {
408453
path: "numpy",
409454
name: "complex64",
455+
compatibility: Compatibility::BackwardsCompatible,
410456
},
411457
}),
412458
["numpy", "string_"] => Some(Replacement {
413459
existing: "string_",
414460
details: Details::AutoImport {
415461
path: "numpy",
416462
name: "bytes_",
463+
compatibility: Compatibility::BackwardsCompatible,
417464
},
418465
}),
419466
["numpy", "source"] => Some(Replacement {
420467
existing: "source",
421468
details: Details::AutoImport {
422469
path: "inspect",
423470
name: "getsource",
471+
compatibility: Compatibility::BackwardsCompatible,
424472
},
425473
}),
426474
["numpy", "tracemalloc_domain"] => Some(Replacement {
427475
existing: "tracemalloc_domain",
428476
details: Details::AutoImport {
429477
path: "numpy.lib",
430478
name: "tracemalloc_domain",
479+
compatibility: Compatibility::BackwardsCompatible,
431480
},
432481
}),
433482
["numpy", "unicode_"] => Some(Replacement {
434483
existing: "unicode_",
435484
details: Details::AutoImport {
436485
path: "numpy",
437486
name: "str_",
487+
compatibility: Compatibility::BackwardsCompatible,
438488
},
439489
}),
440490
["numpy", "who"] => Some(Replacement {
@@ -455,15 +505,26 @@ pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) {
455505
expr.range(),
456506
);
457507
match replacement.details {
458-
Details::AutoImport { path, name } => {
508+
Details::AutoImport {
509+
path,
510+
name,
511+
compatibility,
512+
} => {
459513
diagnostic.try_set_fix(|| {
460514
let (import_edit, binding) = checker.importer().get_or_import_symbol(
461515
&ImportRequest::import_from(path, name),
462516
expr.start(),
463517
checker.semantic(),
464518
)?;
465519
let replacement_edit = Edit::range_replacement(binding, expr.range());
466-
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
520+
Ok(match compatibility {
521+
Compatibility::BackwardsCompatible => {
522+
Fix::safe_edits(import_edit, [replacement_edit])
523+
}
524+
Compatibility::Breaking => {
525+
Fix::unsafe_edits(import_edit, [replacement_edit])
526+
}
527+
})
467528
});
468529
}
469530
Details::AutoPurePython { python_expr } => diagnostic.set_fix(Fix::safe_edit(

crates/ruff_linter/src/rules/numpy/snapshots/ruff_linter__rules__numpy__tests__numpy2-deprecation_NPY201.py.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ NPY201.py:10:5: NPY201 `np.asfarray` will be removed in NumPy 2.0. Use `np.asarr
6969
|
7070
= help: Use `np.asarray` with a `float` dtype instead.
7171

72-
NPY201.py:12:5: NPY201 [*] `np.byte_bounds` will be removed in NumPy 2.0. Use `numpy.lib.array_utils.byte_bounds` instead.
72+
NPY201.py:12:5: NPY201 [*] `np.byte_bounds` will be removed in NumPy 2.0. Use `numpy.lib.array_utils.byte_bounds` on NumPy 2.0, or ignore this warning on earlier versions.
7373
|
7474
10 | np.asfarray([1,2,3])
7575
11 |
@@ -78,9 +78,9 @@ NPY201.py:12:5: NPY201 [*] `np.byte_bounds` will be removed in NumPy 2.0. Use `n
7878
13 |
7979
14 | np.cast
8080
|
81-
= help: Use `numpy.lib.array_utils.byte_bounds` instead.
81+
= help: Use `numpy.lib.array_utils.byte_bounds` on NumPy 2.0, or ignore this warning on earlier versions.
8282

83-
Fix
83+
Suggested fix
8484
1 |+from numpy.lib.array_utils import byte_bounds
8585
1 2 | def func():
8686
2 3 | import numpy as np

0 commit comments

Comments
 (0)