From d04d964acebc17399ffe2faf4610a5b6ae3add3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Sok=C3=B3=C5=82?= <8431159+mtsokol@users.noreply.github.com> Date: Fri, 3 Nov 2023 04:47:01 +0100 Subject: [PATCH] Implement NumPy 2.0 migration rule (#7702) ## Summary Hi! Currently NumPy Python API is undergoing a cleanup process that will be delivered in NumPy 2.0 (release is planned for the end of the year). Most changes are rather simple (renaming, removing or moving a member of the main namespace to a new place), and they could be flagged/fixed by an additional ruff rule for numpy (e.g. changing occurrences of `np.float_` to `np.float64`). Would you accept such rule? I named it `NPY201` in the existing group, so people will receive a heads-up for changes arriving in 2.0 before actually migrating to it. ~~This is still a draft PR.~~ I'm not an expert in rust so if any part of code can be done better please share! NumPy 2.0 migration guide: https://numpy.org/devdocs/numpy_2_0_migration_guide.html NEP 52: https://numpy.org/neps/nep-0052-python-api-cleanup.html NumPy cleanup tracking issue: https://github.com/numpy/numpy/issues/23999 ## Test Plan A unit test is provided that checks all rule's fix cases. --- .../resources/test/fixtures/numpy/NPY201.py | 106 +++ .../src/checkers/ast/analyze/expression.rs | 6 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/numpy/mod.rs | 1 + .../ruff_linter/src/rules/numpy/rules/mod.rs | 2 + .../numpy/rules/numpy_2_0_deprecation.rs | 476 ++++++++++ ...__tests__numpy2-deprecation_NPY201.py.snap | 865 ++++++++++++++++++ ruff.schema.json | 3 + 8 files changed, 1460 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/numpy/NPY201.py create mode 100644 crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs create mode 100644 crates/ruff_linter/src/rules/numpy/snapshots/ruff_linter__rules__numpy__tests__numpy2-deprecation_NPY201.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/numpy/NPY201.py b/crates/ruff_linter/resources/test/fixtures/numpy/NPY201.py new file mode 100644 index 0000000000000..1143e1e7accc1 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/numpy/NPY201.py @@ -0,0 +1,106 @@ +def func(): + import numpy as np + + np.add_docstring + + np.add_newdoc + + np.add_newdoc_ufunc + + np.asfarray([1,2,3]) + + np.byte_bounds(np.array([1,2,3])) + + np.cast + + np.cfloat(12+34j) + + np.clongfloat(12+34j) + + np.compat + + np.complex_(12+34j) + + np.DataSource + + np.deprecate + + np.deprecate_with_doc + + np.disp(10) + + np.fastCopyAndTranspose + + np.find_common_type + + np.get_array_wrap + + np.float_ + + np.geterrobj + + np.Inf + + np.Infinity + + np.infty + + np.issctype + + np.issubclass_(np.int32, np.integer) + + np.issubsctype + + np.mat + + np.maximum_sctype + + np.NaN + + np.nbytes[np.int64] + + np.NINF + + np.NZERO + + np.longcomplex(12+34j) + + np.longfloat(12+34j) + + np.lookfor + + np.obj2sctype(int) + + np.PINF + + np.PZERO + + np.recfromcsv + + np.recfromtxt + + np.round_(12.34) + + np.safe_eval + + np.sctype2char + + np.sctypes + + np.seterrobj + + np.set_numeric_ops + + np.set_string_function + + np.singlecomplex(12+1j) + + np.string_("asdf") + + np.source + + np.tracemalloc_domain + + np.unicode_("asf") + + np.who() diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index b91aceb3c0e01..62878e61aceaf 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -158,6 +158,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::NumpyDeprecatedFunction) { numpy::rules::deprecated_function(checker, expr); } + if checker.enabled(Rule::Numpy2Deprecation) { + numpy::rules::numpy_2_0_deprecation(checker, expr); + } if checker.enabled(Rule::CollectionsNamedTuple) { flake8_pyi::rules::collections_named_tuple(checker, expr); } @@ -314,6 +317,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { if checker.enabled(Rule::NumpyDeprecatedFunction) { numpy::rules::deprecated_function(checker, expr); } + if checker.enabled(Rule::Numpy2Deprecation) { + numpy::rules::numpy_2_0_deprecation(checker, expr); + } if checker.enabled(Rule::DeprecatedMockImport) { pyupgrade::rules::deprecated_mock_attribute(checker, expr); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 605bafd4c1ff7..8df3ad330448e 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -859,6 +859,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Numpy, "001") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedTypeAlias), (Numpy, "002") => (RuleGroup::Stable, rules::numpy::rules::NumpyLegacyRandom), (Numpy, "003") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedFunction), + (Numpy, "201") => (RuleGroup::Preview, rules::numpy::rules::Numpy2Deprecation), // ruff (Ruff, "001") => (RuleGroup::Stable, rules::ruff::rules::AmbiguousUnicodeCharacterString), diff --git a/crates/ruff_linter/src/rules/numpy/mod.rs b/crates/ruff_linter/src/rules/numpy/mod.rs index 027b543dd60f9..b851107a322b3 100644 --- a/crates/ruff_linter/src/rules/numpy/mod.rs +++ b/crates/ruff_linter/src/rules/numpy/mod.rs @@ -16,6 +16,7 @@ mod tests { #[test_case(Rule::NumpyDeprecatedTypeAlias, Path::new("NPY001.py"))] #[test_case(Rule::NumpyLegacyRandom, Path::new("NPY002.py"))] #[test_case(Rule::NumpyDeprecatedFunction, Path::new("NPY003.py"))] + #[test_case(Rule::Numpy2Deprecation, Path::new("NPY201.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/numpy/rules/mod.rs b/crates/ruff_linter/src/rules/numpy/rules/mod.rs index 7c46515e765d3..cde136ab2c6fd 100644 --- a/crates/ruff_linter/src/rules/numpy/rules/mod.rs +++ b/crates/ruff_linter/src/rules/numpy/rules/mod.rs @@ -1,7 +1,9 @@ pub(crate) use deprecated_function::*; pub(crate) use deprecated_type_alias::*; pub(crate) use legacy_random::*; +pub(crate) use numpy_2_0_deprecation::*; mod deprecated_function; mod deprecated_type_alias; mod legacy_random; +mod numpy_2_0_deprecation; diff --git a/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs b/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs new file mode 100644 index 0000000000000..6e585f60b8657 --- /dev/null +++ b/crates/ruff_linter/src/rules/numpy/rules/numpy_2_0_deprecation.rs @@ -0,0 +1,476 @@ +use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::Expr; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; + +/// ## What it does +/// Checks for uses of NumPy functions and constants that were removed from +/// the main namespace in NumPy 2.0. +/// +/// ## Why is this bad? +/// NumPy 2.0 includes an overhaul of NumPy's Python API, intended to remove +/// redundant aliases and routines, and establish unambiguous mechanisms for +/// accessing constants, dtypes, and functions. +/// +/// As part of this overhaul, a variety of deprecated NumPy functions and +/// constants were removed from the main namespace. +/// +/// The majority of these functions and constants can be automatically replaced +/// by other members of the NumPy API, even prior to NumPy 2.0, or by +/// equivalents from the Python standard library. This rule flags all uses of +/// removed members, along with automatic fixes for any backwards-compatible +/// replacements. +/// +/// ## Examples +/// ```python +/// import numpy as np +/// +/// arr1 = [np.Infinity, np.NaN, np.nan, np.PINF, np.inf] +/// arr2 = [np.float_(1.5), np.float64(5.1)] +/// np.round_(arr2) +/// ``` +/// +/// Use instead: +/// ```python +/// import numpy as np +/// +/// arr1 = [np.inf, np.nan, np.nan, np.inf, np.inf] +/// arr2 = [np.float64(1.5), np.float64(5.1)] +/// np.round(arr2) +/// ``` +#[violation] +pub struct Numpy2Deprecation { + existing: String, + migration_guide: Option, +} + +impl Violation for Numpy2Deprecation { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + + #[derive_message_formats] + fn message(&self) -> String { + let Numpy2Deprecation { + existing, + migration_guide, + } = self; + match migration_guide { + Some(migration_guide) => { + format!("`np.{existing}` will be removed in NumPy 2.0. {migration_guide}",) + } + None => format!("`np.{existing}` will be removed without replacement in NumPy 2.0."), + } + } + + fn fix_title(&self) -> Option { + let Numpy2Deprecation { + existing: _, + migration_guide, + } = self; + migration_guide.clone() + } +} + +#[derive(Debug)] +struct Replacement<'a> { + existing: &'a str, + details: Details<'a>, +} + +#[derive(Debug)] +enum Details<'a> { + /// The deprecated member can be replaced by another member in the NumPy API. + AutoImport { path: &'a str, name: &'a str }, + /// The deprecated member can be replaced by a member of the Python standard library. + AutoPurePython { python_expr: &'a str }, + /// The deprecated member can be replaced by a manual migration. + Manual { guideline: Option<&'a str> }, +} + +impl Details<'_> { + fn guideline(&self) -> Option { + match self { + Details::AutoImport { path, name } => Some(format!("Use `{path}.{name}` instead.")), + Details::AutoPurePython { python_expr } => { + Some(format!("Use `{python_expr}` instead.")) + } + Details::Manual { guideline } => guideline.map(ToString::to_string), + } + } +} + +/// NPY201 +pub(crate) fn numpy_2_0_deprecation(checker: &mut Checker, expr: &Expr) { + let maybe_replacement = checker + .semantic() + .resolve_call_path(expr) + .and_then(|call_path| match call_path.as_slice() { + // NumPy's main namespace np.* members removed in 2.0 + ["numpy", "add_docstring"] => Some(Replacement { + existing: "add_docstring", + details: Details::AutoImport { + path: "numpy.lib", + name: "add_docstring", + }, + }), + ["numpy", "add_newdoc"] => Some(Replacement { + existing: "add_newdoc", + details: Details::AutoImport { + path: "numpy.lib", + name: "add_newdoc", + }, + }), + ["numpy", "add_newdoc_ufunc"] => Some(Replacement { + existing: "add_newdoc_ufunc", + details: Details::Manual { + guideline: Some("`add_newdoc_ufunc` is an internal function."), + }, + }), + ["numpy", "asfarray"] => Some(Replacement { + existing: "asfarray", + details: Details::Manual { + guideline: Some("Use `np.asarray` with a `float` dtype instead."), + }, + }), + ["numpy", "byte_bounds"] => Some(Replacement { + existing: "byte_bounds", + details: Details::AutoImport { + path: "numpy.lib.array_utils", + name: "byte_bounds", + }, + }), + ["numpy", "cast"] => Some(Replacement { + existing: "cast", + details: Details::Manual { + guideline: Some("Use `np.asarray(arr, dtype=dtype)` instead."), + }, + }), + ["numpy", "cfloat"] => Some(Replacement { + existing: "cfloat", + details: Details::AutoImport { + path: "numpy", + name: "complex128", + }, + }), + ["numpy", "clongfloat"] => Some(Replacement { + existing: "clongfloat", + details: Details::AutoImport { + path: "numpy", + name: "clongdouble", + }, + }), + ["numpy", "compat"] => Some(Replacement { + existing: "compat", + details: Details::Manual { + guideline: Some("Python 2 is no longer supported."), + }, + }), + ["numpy", "complex_"] => Some(Replacement { + existing: "complex_", + details: Details::AutoImport { + path: "numpy", + name: "complex128", + }, + }), + ["numpy", "DataSource"] => Some(Replacement { + existing: "DataSource", + details: Details::AutoImport { + path: "numpy.lib.npyio", + name: "DataSource", + }, + }), + ["numpy", "deprecate"] => Some(Replacement { + existing: "deprecate", + details: Details::Manual { + guideline: Some("Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`."), + }, + }), + ["numpy", "deprecate_with_doc"] => Some(Replacement { + existing: "deprecate_with_doc", + details: Details::Manual { + guideline: Some("Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`."), + }, + }), + ["numpy", "disp"] => Some(Replacement { + existing: "disp", + details: Details::Manual { + guideline: Some("Use a dedicated print function instead."), + }, + }), + ["numpy", "fastCopyAndTranspose"] => Some(Replacement { + existing: "fastCopyAndTranspose", + details: Details::Manual { + guideline: Some("Use `arr.T.copy()` instead."), + }, + }), + ["numpy", "find_common_type"] => Some(Replacement { + existing: "find_common_type", + details: Details::Manual { + guideline: Some("Use `numpy.promote_types` or `numpy.result_type` instead. To achieve semantics for the `scalar_types` argument, use `numpy.result_type` and pass the Python values `0`, `0.0`, or `0j`."), + }, + }), + ["numpy", "get_array_wrap"] => Some(Replacement { + existing: "get_array_wrap", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "float_"] => Some(Replacement { + existing: "float_", + details: Details::AutoImport { + path: "numpy", + name: "float64", + }, + }), + ["numpy", "geterrobj"] => Some(Replacement { + existing: "geterrobj", + details: Details::Manual { + guideline: Some("Use the `np.errstate` context manager instead."), + }, + }), + ["numpy", "INF"] => Some(Replacement { + existing: "INF", + details: Details::AutoImport { + path: "numpy", + name: "inf", + }, + }), + ["numpy", "Inf"] => Some(Replacement { + existing: "Inf", + details: Details::AutoImport { + path: "numpy", + name: "inf", + }, + }), + ["numpy", "Infinity"] => Some(Replacement { + existing: "Infinity", + details: Details::AutoImport { + path: "numpy", + name: "inf", + }, + }), + ["numpy", "infty"] => Some(Replacement { + existing: "infty", + details: Details::AutoImport { + path: "numpy", + name: "inf", + }, + }), + ["numpy", "issctype"] => Some(Replacement { + existing: "issctype", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "issubclass_"] => Some(Replacement { + existing: "issubclass_", + details: Details::AutoPurePython { + python_expr: "issubclass", + }, + }), + ["numpy", "issubsctype"] => Some(Replacement { + existing: "issubsctype", + details: Details::AutoImport { + path: "numpy", + name: "issubdtype", + }, + }), + ["numpy", "mat"] => Some(Replacement { + existing: "mat", + details: Details::AutoImport { + path: "numpy", + name: "asmatrix", + }, + }), + ["numpy", "maximum_sctype"] => Some(Replacement { + existing: "maximum_sctype", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "NaN"] => Some(Replacement { + existing: "NaN", + details: Details::AutoImport { + path: "numpy", + name: "nan", + }, + }), + ["numpy", "nbytes"] => Some(Replacement { + existing: "nbytes", + details: Details::Manual { + guideline: Some("Use `np.dtype().itemsize` instead."), + }, + }), + ["numpy", "NINF"] => Some(Replacement { + existing: "NINF", + details: Details::AutoPurePython { + python_expr: "-np.inf", + }, + }), + ["numpy", "NZERO"] => Some(Replacement { + existing: "NZERO", + details: Details::AutoPurePython { + python_expr: "-0.0", + }, + }), + ["numpy", "longcomplex"] => Some(Replacement { + existing: "longcomplex", + details: Details::AutoImport { + path: "numpy", + name: "clongdouble", + }, + }), + ["numpy", "longfloat"] => Some(Replacement { + existing: "longfloat", + details: Details::AutoImport { + path: "numpy", + name: "longdouble", + }, + }), + ["numpy", "lookfor"] => Some(Replacement { + existing: "lookfor", + details: Details::Manual { + guideline: Some("Search NumPy’s documentation directly."), + }, + }), + ["numpy", "obj2sctype"] => Some(Replacement { + existing: "obj2sctype", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "PINF"] => Some(Replacement { + existing: "PINF", + details: Details::AutoImport { + path: "numpy", + name: "inf", + }, + }), + ["numpy", "PZERO"] => Some(Replacement { + existing: "PZERO", + details: Details::AutoPurePython { python_expr: "0.0" }, + }), + ["numpy", "recfromcsv"] => Some(Replacement { + existing: "recfromcsv", + details: Details::Manual { + guideline: Some("Use `np.genfromtxt` with comma delimiter instead."), + }, + }), + ["numpy", "recfromtxt"] => Some(Replacement { + existing: "recfromtxt", + details: Details::Manual { + guideline: Some("Use `np.genfromtxt` instead."), + }, + }), + ["numpy", "round_"] => Some(Replacement { + existing: "round_", + details: Details::AutoImport { + path: "numpy", + name: "round", + }, + }), + ["numpy", "safe_eval"] => Some(Replacement { + existing: "safe_eval", + details: Details::AutoImport { + path: "ast", + name: "literal_eval", + }, + }), + ["numpy", "sctype2char"] => Some(Replacement { + existing: "sctype2char", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "sctypes"] => Some(Replacement { + existing: "sctypes", + details: Details::Manual { + guideline: None, + }, + }), + ["numpy", "seterrobj"] => Some(Replacement { + existing: "seterrobj", + details: Details::Manual { + guideline: Some("Use the `np.errstate` context manager instead."), + }, + }), + ["numpy", "set_string_function"] => Some(Replacement { + existing: "set_string_function", + details: Details::Manual { + guideline: Some("Use `np.set_printoptions` for custom printing of NumPy objects."), + }, + }), + ["numpy", "singlecomplex"] => Some(Replacement { + existing: "singlecomplex", + details: Details::AutoImport { + path: "numpy", + name: "complex64", + }, + }), + ["numpy", "string_"] => Some(Replacement { + existing: "string_", + details: Details::AutoImport { + path: "numpy", + name: "bytes_", + }, + }), + ["numpy", "source"] => Some(Replacement { + existing: "source", + details: Details::AutoImport { + path: "inspect", + name: "getsource", + }, + }), + ["numpy", "tracemalloc_domain"] => Some(Replacement { + existing: "tracemalloc_domain", + details: Details::AutoImport { + path: "numpy.lib", + name: "tracemalloc_domain", + }, + }), + ["numpy", "unicode_"] => Some(Replacement { + existing: "unicode_", + details: Details::AutoImport { + path: "numpy", + name: "str_", + }, + }), + ["numpy", "who"] => Some(Replacement { + existing: "who", + details: Details::Manual { + guideline: Some("Use an IDE variable explorer or `locals()` instead."), + }, + }), + _ => None, + }); + + if let Some(replacement) = maybe_replacement { + let mut diagnostic = Diagnostic::new( + Numpy2Deprecation { + existing: replacement.existing.to_string(), + migration_guide: replacement.details.guideline(), + }, + expr.range(), + ); + match replacement.details { + Details::AutoImport { path, name } => { + diagnostic.try_set_fix(|| { + let (import_edit, binding) = checker.importer().get_or_import_symbol( + &ImportRequest::import_from(path, name), + expr.start(), + checker.semantic(), + )?; + let replacement_edit = Edit::range_replacement(binding, expr.range()); + Ok(Fix::safe_edits(import_edit, [replacement_edit])) + }); + } + Details::AutoPurePython { python_expr } => diagnostic.set_fix(Fix::safe_edit( + Edit::range_replacement(python_expr.to_string(), expr.range()), + )), + Details::Manual { guideline: _ } => {} + }; + checker.diagnostics.push(diagnostic); + } +} diff --git a/crates/ruff_linter/src/rules/numpy/snapshots/ruff_linter__rules__numpy__tests__numpy2-deprecation_NPY201.py.snap b/crates/ruff_linter/src/rules/numpy/snapshots/ruff_linter__rules__numpy__tests__numpy2-deprecation_NPY201.py.snap new file mode 100644 index 0000000000000..0d6458cd29d54 --- /dev/null +++ b/crates/ruff_linter/src/rules/numpy/snapshots/ruff_linter__rules__numpy__tests__numpy2-deprecation_NPY201.py.snap @@ -0,0 +1,865 @@ +--- +source: crates/ruff_linter/src/rules/numpy/mod.rs +--- +NPY201.py:4:5: NPY201 [*] `np.add_docstring` will be removed in NumPy 2.0. Use `numpy.lib.add_docstring` instead. + | +2 | import numpy as np +3 | +4 | np.add_docstring + | ^^^^^^^^^^^^^^^^ NPY201 +5 | +6 | np.add_newdoc + | + = help: Use `numpy.lib.add_docstring` instead. + +ℹ Fix + 1 |+from numpy.lib import add_docstring +1 2 | def func(): +2 3 | import numpy as np +3 4 | +4 |- np.add_docstring + 5 |+ add_docstring +5 6 | +6 7 | np.add_newdoc +7 8 | + +NPY201.py:6:5: NPY201 [*] `np.add_newdoc` will be removed in NumPy 2.0. Use `numpy.lib.add_newdoc` instead. + | +4 | np.add_docstring +5 | +6 | np.add_newdoc + | ^^^^^^^^^^^^^ NPY201 +7 | +8 | np.add_newdoc_ufunc + | + = help: Use `numpy.lib.add_newdoc` instead. + +ℹ Fix + 1 |+from numpy.lib import add_newdoc +1 2 | def func(): +2 3 | import numpy as np +3 4 | +4 5 | np.add_docstring +5 6 | +6 |- np.add_newdoc + 7 |+ add_newdoc +7 8 | +8 9 | np.add_newdoc_ufunc +9 10 | + +NPY201.py:8:5: NPY201 `np.add_newdoc_ufunc` will be removed in NumPy 2.0. `add_newdoc_ufunc` is an internal function. + | + 6 | np.add_newdoc + 7 | + 8 | np.add_newdoc_ufunc + | ^^^^^^^^^^^^^^^^^^^ NPY201 + 9 | +10 | np.asfarray([1,2,3]) + | + = help: `add_newdoc_ufunc` is an internal function. + +NPY201.py:10:5: NPY201 `np.asfarray` will be removed in NumPy 2.0. Use `np.asarray` with a `float` dtype instead. + | + 8 | np.add_newdoc_ufunc + 9 | +10 | np.asfarray([1,2,3]) + | ^^^^^^^^^^^ NPY201 +11 | +12 | np.byte_bounds(np.array([1,2,3])) + | + = help: Use `np.asarray` with a `float` dtype instead. + +NPY201.py:12:5: NPY201 [*] `np.byte_bounds` will be removed in NumPy 2.0. Use `numpy.lib.array_utils.byte_bounds` instead. + | +10 | np.asfarray([1,2,3]) +11 | +12 | np.byte_bounds(np.array([1,2,3])) + | ^^^^^^^^^^^^^^ NPY201 +13 | +14 | np.cast + | + = help: Use `numpy.lib.array_utils.byte_bounds` instead. + +ℹ Fix + 1 |+from numpy.lib.array_utils import byte_bounds +1 2 | def func(): +2 3 | import numpy as np +3 4 | +-------------------------------------------------------------------------------- +9 10 | +10 11 | np.asfarray([1,2,3]) +11 12 | +12 |- np.byte_bounds(np.array([1,2,3])) + 13 |+ byte_bounds(np.array([1,2,3])) +13 14 | +14 15 | np.cast +15 16 | + +NPY201.py:14:5: NPY201 `np.cast` will be removed in NumPy 2.0. Use `np.asarray(arr, dtype=dtype)` instead. + | +12 | np.byte_bounds(np.array([1,2,3])) +13 | +14 | np.cast + | ^^^^^^^ NPY201 +15 | +16 | np.cfloat(12+34j) + | + = help: Use `np.asarray(arr, dtype=dtype)` instead. + +NPY201.py:16:5: NPY201 [*] `np.cfloat` will be removed in NumPy 2.0. Use `numpy.complex128` instead. + | +14 | np.cast +15 | +16 | np.cfloat(12+34j) + | ^^^^^^^^^ NPY201 +17 | +18 | np.clongfloat(12+34j) + | + = help: Use `numpy.complex128` instead. + +ℹ Fix +13 13 | +14 14 | np.cast +15 15 | +16 |- np.cfloat(12+34j) + 16 |+ np.complex128(12+34j) +17 17 | +18 18 | np.clongfloat(12+34j) +19 19 | + +NPY201.py:18:5: NPY201 [*] `np.clongfloat` will be removed in NumPy 2.0. Use `numpy.clongdouble` instead. + | +16 | np.cfloat(12+34j) +17 | +18 | np.clongfloat(12+34j) + | ^^^^^^^^^^^^^ NPY201 +19 | +20 | np.compat + | + = help: Use `numpy.clongdouble` instead. + +ℹ Fix +15 15 | +16 16 | np.cfloat(12+34j) +17 17 | +18 |- np.clongfloat(12+34j) + 18 |+ np.clongdouble(12+34j) +19 19 | +20 20 | np.compat +21 21 | + +NPY201.py:20:5: NPY201 `np.compat` will be removed in NumPy 2.0. Python 2 is no longer supported. + | +18 | np.clongfloat(12+34j) +19 | +20 | np.compat + | ^^^^^^^^^ NPY201 +21 | +22 | np.complex_(12+34j) + | + = help: Python 2 is no longer supported. + +NPY201.py:22:5: NPY201 [*] `np.complex_` will be removed in NumPy 2.0. Use `numpy.complex128` instead. + | +20 | np.compat +21 | +22 | np.complex_(12+34j) + | ^^^^^^^^^^^ NPY201 +23 | +24 | np.DataSource + | + = help: Use `numpy.complex128` instead. + +ℹ Fix +19 19 | +20 20 | np.compat +21 21 | +22 |- np.complex_(12+34j) + 22 |+ np.complex128(12+34j) +23 23 | +24 24 | np.DataSource +25 25 | + +NPY201.py:24:5: NPY201 [*] `np.DataSource` will be removed in NumPy 2.0. Use `numpy.lib.npyio.DataSource` instead. + | +22 | np.complex_(12+34j) +23 | +24 | np.DataSource + | ^^^^^^^^^^^^^ NPY201 +25 | +26 | np.deprecate + | + = help: Use `numpy.lib.npyio.DataSource` instead. + +ℹ Fix + 1 |+from numpy.lib.npyio import DataSource +1 2 | def func(): +2 3 | import numpy as np +3 4 | +-------------------------------------------------------------------------------- +21 22 | +22 23 | np.complex_(12+34j) +23 24 | +24 |- np.DataSource + 25 |+ DataSource +25 26 | +26 27 | np.deprecate +27 28 | + +NPY201.py:26:5: NPY201 `np.deprecate` will be removed in NumPy 2.0. Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`. + | +24 | np.DataSource +25 | +26 | np.deprecate + | ^^^^^^^^^^^^ NPY201 +27 | +28 | np.deprecate_with_doc + | + = help: Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`. + +NPY201.py:28:5: NPY201 `np.deprecate_with_doc` will be removed in NumPy 2.0. Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`. + | +26 | np.deprecate +27 | +28 | np.deprecate_with_doc + | ^^^^^^^^^^^^^^^^^^^^^ NPY201 +29 | +30 | np.disp(10) + | + = help: Emit `DeprecationWarning` with `warnings.warn` directly, or use `typing.deprecated`. + +NPY201.py:30:5: NPY201 `np.disp` will be removed in NumPy 2.0. Use a dedicated print function instead. + | +28 | np.deprecate_with_doc +29 | +30 | np.disp(10) + | ^^^^^^^ NPY201 +31 | +32 | np.fastCopyAndTranspose + | + = help: Use a dedicated print function instead. + +NPY201.py:32:5: NPY201 `np.fastCopyAndTranspose` will be removed in NumPy 2.0. Use `arr.T.copy()` instead. + | +30 | np.disp(10) +31 | +32 | np.fastCopyAndTranspose + | ^^^^^^^^^^^^^^^^^^^^^^^ NPY201 +33 | +34 | np.find_common_type + | + = help: Use `arr.T.copy()` instead. + +NPY201.py:34:5: NPY201 `np.find_common_type` will be removed in NumPy 2.0. Use `numpy.promote_types` or `numpy.result_type` instead. To achieve semantics for the `scalar_types` argument, use `numpy.result_type` and pass the Python values `0`, `0.0`, or `0j`. + | +32 | np.fastCopyAndTranspose +33 | +34 | np.find_common_type + | ^^^^^^^^^^^^^^^^^^^ NPY201 +35 | +36 | np.get_array_wrap + | + = help: Use `numpy.promote_types` or `numpy.result_type` instead. To achieve semantics for the `scalar_types` argument, use `numpy.result_type` and pass the Python values `0`, `0.0`, or `0j`. + +NPY201.py:36:5: NPY201 `np.get_array_wrap` will be removed without replacement in NumPy 2.0. + | +34 | np.find_common_type +35 | +36 | np.get_array_wrap + | ^^^^^^^^^^^^^^^^^ NPY201 +37 | +38 | np.float_ + | + +NPY201.py:38:5: NPY201 [*] `np.float_` will be removed in NumPy 2.0. Use `numpy.float64` instead. + | +36 | np.get_array_wrap +37 | +38 | np.float_ + | ^^^^^^^^^ NPY201 +39 | +40 | np.geterrobj + | + = help: Use `numpy.float64` instead. + +ℹ Fix +35 35 | +36 36 | np.get_array_wrap +37 37 | +38 |- np.float_ + 38 |+ np.float64 +39 39 | +40 40 | np.geterrobj +41 41 | + +NPY201.py:40:5: NPY201 `np.geterrobj` will be removed in NumPy 2.0. Use the `np.errstate` context manager instead. + | +38 | np.float_ +39 | +40 | np.geterrobj + | ^^^^^^^^^^^^ NPY201 +41 | +42 | np.Inf + | + = help: Use the `np.errstate` context manager instead. + +NPY201.py:42:5: NPY201 [*] `np.Inf` will be removed in NumPy 2.0. Use `numpy.inf` instead. + | +40 | np.geterrobj +41 | +42 | np.Inf + | ^^^^^^ NPY201 +43 | +44 | np.Infinity + | + = help: Use `numpy.inf` instead. + +ℹ Fix +39 39 | +40 40 | np.geterrobj +41 41 | +42 |- np.Inf + 42 |+ np.inf +43 43 | +44 44 | np.Infinity +45 45 | + +NPY201.py:44:5: NPY201 [*] `np.Infinity` will be removed in NumPy 2.0. Use `numpy.inf` instead. + | +42 | np.Inf +43 | +44 | np.Infinity + | ^^^^^^^^^^^ NPY201 +45 | +46 | np.infty + | + = help: Use `numpy.inf` instead. + +ℹ Fix +41 41 | +42 42 | np.Inf +43 43 | +44 |- np.Infinity + 44 |+ np.inf +45 45 | +46 46 | np.infty +47 47 | + +NPY201.py:46:5: NPY201 [*] `np.infty` will be removed in NumPy 2.0. Use `numpy.inf` instead. + | +44 | np.Infinity +45 | +46 | np.infty + | ^^^^^^^^ NPY201 +47 | +48 | np.issctype + | + = help: Use `numpy.inf` instead. + +ℹ Fix +43 43 | +44 44 | np.Infinity +45 45 | +46 |- np.infty + 46 |+ np.inf +47 47 | +48 48 | np.issctype +49 49 | + +NPY201.py:48:5: NPY201 `np.issctype` will be removed without replacement in NumPy 2.0. + | +46 | np.infty +47 | +48 | np.issctype + | ^^^^^^^^^^^ NPY201 +49 | +50 | np.issubclass_(np.int32, np.integer) + | + +NPY201.py:50:5: NPY201 [*] `np.issubclass_` will be removed in NumPy 2.0. Use `issubclass` instead. + | +48 | np.issctype +49 | +50 | np.issubclass_(np.int32, np.integer) + | ^^^^^^^^^^^^^^ NPY201 +51 | +52 | np.issubsctype + | + = help: Use `issubclass` instead. + +ℹ Fix +47 47 | +48 48 | np.issctype +49 49 | +50 |- np.issubclass_(np.int32, np.integer) + 50 |+ issubclass(np.int32, np.integer) +51 51 | +52 52 | np.issubsctype +53 53 | + +NPY201.py:52:5: NPY201 [*] `np.issubsctype` will be removed in NumPy 2.0. Use `numpy.issubdtype` instead. + | +50 | np.issubclass_(np.int32, np.integer) +51 | +52 | np.issubsctype + | ^^^^^^^^^^^^^^ NPY201 +53 | +54 | np.mat + | + = help: Use `numpy.issubdtype` instead. + +ℹ Fix +49 49 | +50 50 | np.issubclass_(np.int32, np.integer) +51 51 | +52 |- np.issubsctype + 52 |+ np.issubdtype +53 53 | +54 54 | np.mat +55 55 | + +NPY201.py:54:5: NPY201 [*] `np.mat` will be removed in NumPy 2.0. Use `numpy.asmatrix` instead. + | +52 | np.issubsctype +53 | +54 | np.mat + | ^^^^^^ NPY201 +55 | +56 | np.maximum_sctype + | + = help: Use `numpy.asmatrix` instead. + +ℹ Fix +51 51 | +52 52 | np.issubsctype +53 53 | +54 |- np.mat + 54 |+ np.asmatrix +55 55 | +56 56 | np.maximum_sctype +57 57 | + +NPY201.py:56:5: NPY201 `np.maximum_sctype` will be removed without replacement in NumPy 2.0. + | +54 | np.mat +55 | +56 | np.maximum_sctype + | ^^^^^^^^^^^^^^^^^ NPY201 +57 | +58 | np.NaN + | + +NPY201.py:58:5: NPY201 [*] `np.NaN` will be removed in NumPy 2.0. Use `numpy.nan` instead. + | +56 | np.maximum_sctype +57 | +58 | np.NaN + | ^^^^^^ NPY201 +59 | +60 | np.nbytes[np.int64] + | + = help: Use `numpy.nan` instead. + +ℹ Fix +55 55 | +56 56 | np.maximum_sctype +57 57 | +58 |- np.NaN + 58 |+ np.nan +59 59 | +60 60 | np.nbytes[np.int64] +61 61 | + +NPY201.py:60:5: NPY201 `np.nbytes` will be removed in NumPy 2.0. Use `np.dtype().itemsize` instead. + | +58 | np.NaN +59 | +60 | np.nbytes[np.int64] + | ^^^^^^^^^ NPY201 +61 | +62 | np.NINF + | + = help: Use `np.dtype().itemsize` instead. + +NPY201.py:62:5: NPY201 [*] `np.NINF` will be removed in NumPy 2.0. Use `-np.inf` instead. + | +60 | np.nbytes[np.int64] +61 | +62 | np.NINF + | ^^^^^^^ NPY201 +63 | +64 | np.NZERO + | + = help: Use `-np.inf` instead. + +ℹ Fix +59 59 | +60 60 | np.nbytes[np.int64] +61 61 | +62 |- np.NINF + 62 |+ -np.inf +63 63 | +64 64 | np.NZERO +65 65 | + +NPY201.py:64:5: NPY201 [*] `np.NZERO` will be removed in NumPy 2.0. Use `-0.0` instead. + | +62 | np.NINF +63 | +64 | np.NZERO + | ^^^^^^^^ NPY201 +65 | +66 | np.longcomplex(12+34j) + | + = help: Use `-0.0` instead. + +ℹ Fix +61 61 | +62 62 | np.NINF +63 63 | +64 |- np.NZERO + 64 |+ -0.0 +65 65 | +66 66 | np.longcomplex(12+34j) +67 67 | + +NPY201.py:66:5: NPY201 [*] `np.longcomplex` will be removed in NumPy 2.0. Use `numpy.clongdouble` instead. + | +64 | np.NZERO +65 | +66 | np.longcomplex(12+34j) + | ^^^^^^^^^^^^^^ NPY201 +67 | +68 | np.longfloat(12+34j) + | + = help: Use `numpy.clongdouble` instead. + +ℹ Fix +63 63 | +64 64 | np.NZERO +65 65 | +66 |- np.longcomplex(12+34j) + 66 |+ np.clongdouble(12+34j) +67 67 | +68 68 | np.longfloat(12+34j) +69 69 | + +NPY201.py:68:5: NPY201 [*] `np.longfloat` will be removed in NumPy 2.0. Use `numpy.longdouble` instead. + | +66 | np.longcomplex(12+34j) +67 | +68 | np.longfloat(12+34j) + | ^^^^^^^^^^^^ NPY201 +69 | +70 | np.lookfor + | + = help: Use `numpy.longdouble` instead. + +ℹ Fix +65 65 | +66 66 | np.longcomplex(12+34j) +67 67 | +68 |- np.longfloat(12+34j) + 68 |+ np.longdouble(12+34j) +69 69 | +70 70 | np.lookfor +71 71 | + +NPY201.py:70:5: NPY201 `np.lookfor` will be removed in NumPy 2.0. Search NumPy’s documentation directly. + | +68 | np.longfloat(12+34j) +69 | +70 | np.lookfor + | ^^^^^^^^^^ NPY201 +71 | +72 | np.obj2sctype(int) + | + = help: Search NumPy’s documentation directly. + +NPY201.py:72:5: NPY201 `np.obj2sctype` will be removed without replacement in NumPy 2.0. + | +70 | np.lookfor +71 | +72 | np.obj2sctype(int) + | ^^^^^^^^^^^^^ NPY201 +73 | +74 | np.PINF + | + +NPY201.py:74:5: NPY201 [*] `np.PINF` will be removed in NumPy 2.0. Use `numpy.inf` instead. + | +72 | np.obj2sctype(int) +73 | +74 | np.PINF + | ^^^^^^^ NPY201 +75 | +76 | np.PZERO + | + = help: Use `numpy.inf` instead. + +ℹ Fix +71 71 | +72 72 | np.obj2sctype(int) +73 73 | +74 |- np.PINF + 74 |+ np.inf +75 75 | +76 76 | np.PZERO +77 77 | + +NPY201.py:76:5: NPY201 [*] `np.PZERO` will be removed in NumPy 2.0. Use `0.0` instead. + | +74 | np.PINF +75 | +76 | np.PZERO + | ^^^^^^^^ NPY201 +77 | +78 | np.recfromcsv + | + = help: Use `0.0` instead. + +ℹ Fix +73 73 | +74 74 | np.PINF +75 75 | +76 |- np.PZERO + 76 |+ 0.0 +77 77 | +78 78 | np.recfromcsv +79 79 | + +NPY201.py:78:5: NPY201 `np.recfromcsv` will be removed in NumPy 2.0. Use `np.genfromtxt` with comma delimiter instead. + | +76 | np.PZERO +77 | +78 | np.recfromcsv + | ^^^^^^^^^^^^^ NPY201 +79 | +80 | np.recfromtxt + | + = help: Use `np.genfromtxt` with comma delimiter instead. + +NPY201.py:80:5: NPY201 `np.recfromtxt` will be removed in NumPy 2.0. Use `np.genfromtxt` instead. + | +78 | np.recfromcsv +79 | +80 | np.recfromtxt + | ^^^^^^^^^^^^^ NPY201 +81 | +82 | np.round_(12.34) + | + = help: Use `np.genfromtxt` instead. + +NPY201.py:82:5: NPY201 [*] `np.round_` will be removed in NumPy 2.0. Use `numpy.round` instead. + | +80 | np.recfromtxt +81 | +82 | np.round_(12.34) + | ^^^^^^^^^ NPY201 +83 | +84 | np.safe_eval + | + = help: Use `numpy.round` instead. + +ℹ Fix +79 79 | +80 80 | np.recfromtxt +81 81 | +82 |- np.round_(12.34) + 82 |+ np.round(12.34) +83 83 | +84 84 | np.safe_eval +85 85 | + +NPY201.py:84:5: NPY201 [*] `np.safe_eval` will be removed in NumPy 2.0. Use `ast.literal_eval` instead. + | +82 | np.round_(12.34) +83 | +84 | np.safe_eval + | ^^^^^^^^^^^^ NPY201 +85 | +86 | np.sctype2char + | + = help: Use `ast.literal_eval` instead. + +ℹ Fix + 1 |+from ast import literal_eval +1 2 | def func(): +2 3 | import numpy as np +3 4 | +-------------------------------------------------------------------------------- +81 82 | +82 83 | np.round_(12.34) +83 84 | +84 |- np.safe_eval + 85 |+ literal_eval +85 86 | +86 87 | np.sctype2char +87 88 | + +NPY201.py:86:5: NPY201 `np.sctype2char` will be removed without replacement in NumPy 2.0. + | +84 | np.safe_eval +85 | +86 | np.sctype2char + | ^^^^^^^^^^^^^^ NPY201 +87 | +88 | np.sctypes + | + +NPY201.py:88:5: NPY201 `np.sctypes` will be removed without replacement in NumPy 2.0. + | +86 | np.sctype2char +87 | +88 | np.sctypes + | ^^^^^^^^^^ NPY201 +89 | +90 | np.seterrobj + | + +NPY201.py:90:5: NPY201 `np.seterrobj` will be removed in NumPy 2.0. Use the `np.errstate` context manager instead. + | +88 | np.sctypes +89 | +90 | np.seterrobj + | ^^^^^^^^^^^^ NPY201 +91 | +92 | np.set_numeric_ops + | + = help: Use the `np.errstate` context manager instead. + +NPY201.py:94:5: NPY201 `np.set_string_function` will be removed in NumPy 2.0. Use `np.set_printoptions` for custom printing of NumPy objects. + | +92 | np.set_numeric_ops +93 | +94 | np.set_string_function + | ^^^^^^^^^^^^^^^^^^^^^^ NPY201 +95 | +96 | np.singlecomplex(12+1j) + | + = help: Use `np.set_printoptions` for custom printing of NumPy objects. + +NPY201.py:96:5: NPY201 [*] `np.singlecomplex` will be removed in NumPy 2.0. Use `numpy.complex64` instead. + | +94 | np.set_string_function +95 | +96 | np.singlecomplex(12+1j) + | ^^^^^^^^^^^^^^^^ NPY201 +97 | +98 | np.string_("asdf") + | + = help: Use `numpy.complex64` instead. + +ℹ Fix +93 93 | +94 94 | np.set_string_function +95 95 | +96 |- np.singlecomplex(12+1j) + 96 |+ np.complex64(12+1j) +97 97 | +98 98 | np.string_("asdf") +99 99 | + +NPY201.py:98:5: NPY201 [*] `np.string_` will be removed in NumPy 2.0. Use `numpy.bytes_` instead. + | + 96 | np.singlecomplex(12+1j) + 97 | + 98 | np.string_("asdf") + | ^^^^^^^^^^ NPY201 + 99 | +100 | np.source + | + = help: Use `numpy.bytes_` instead. + +ℹ Fix +95 95 | +96 96 | np.singlecomplex(12+1j) +97 97 | +98 |- np.string_("asdf") + 98 |+ np.bytes_("asdf") +99 99 | +100 100 | np.source +101 101 | + +NPY201.py:100:5: NPY201 [*] `np.source` will be removed in NumPy 2.0. Use `inspect.getsource` instead. + | + 98 | np.string_("asdf") + 99 | +100 | np.source + | ^^^^^^^^^ NPY201 +101 | +102 | np.tracemalloc_domain + | + = help: Use `inspect.getsource` instead. + +ℹ Fix + 1 |+from inspect import getsource +1 2 | def func(): +2 3 | import numpy as np +3 4 | +-------------------------------------------------------------------------------- +97 98 | +98 99 | np.string_("asdf") +99 100 | +100 |- np.source + 101 |+ getsource +101 102 | +102 103 | np.tracemalloc_domain +103 104 | + +NPY201.py:102:5: NPY201 [*] `np.tracemalloc_domain` will be removed in NumPy 2.0. Use `numpy.lib.tracemalloc_domain` instead. + | +100 | np.source +101 | +102 | np.tracemalloc_domain + | ^^^^^^^^^^^^^^^^^^^^^ NPY201 +103 | +104 | np.unicode_("asf") + | + = help: Use `numpy.lib.tracemalloc_domain` instead. + +ℹ Fix + 1 |+from numpy.lib import tracemalloc_domain +1 2 | def func(): +2 3 | import numpy as np +3 4 | +-------------------------------------------------------------------------------- +99 100 | +100 101 | np.source +101 102 | +102 |- np.tracemalloc_domain + 103 |+ tracemalloc_domain +103 104 | +104 105 | np.unicode_("asf") +105 106 | + +NPY201.py:104:5: NPY201 [*] `np.unicode_` will be removed in NumPy 2.0. Use `numpy.str_` instead. + | +102 | np.tracemalloc_domain +103 | +104 | np.unicode_("asf") + | ^^^^^^^^^^^ NPY201 +105 | +106 | np.who() + | + = help: Use `numpy.str_` instead. + +ℹ Fix +101 101 | +102 102 | np.tracemalloc_domain +103 103 | +104 |- np.unicode_("asf") + 104 |+ np.str_("asf") +105 105 | +106 106 | np.who() + +NPY201.py:106:5: NPY201 `np.who` will be removed in NumPy 2.0. Use an IDE variable explorer or `locals()` instead. + | +104 | np.unicode_("asf") +105 | +106 | np.who() + | ^^^^^^ NPY201 + | + = help: Use an IDE variable explorer or `locals()` instead. + + diff --git a/ruff.schema.json b/ruff.schema.json index c09dae9daf941..b861d4bf2457f 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2903,6 +2903,9 @@ "NPY001", "NPY002", "NPY003", + "NPY2", + "NPY20", + "NPY201", "NURSERY", "PD", "PD0",