refactor(get,select,reject)!: deprecate --ignore-errors in favor of --optional (#16007)

# Description
As decided on the team meeting on 2025-06-19, rename `--ignore-errors
(-i)` to `--optional (-o)` with a (currently) indefinite grace period.

After `--ignore-errors (-i)` is removed, the short flag `-i` can be used
for `--ignore-case` (not implemented as of this PR)

# User-Facing Changes
`get`/`select`/`reject`: rename `--ignore-errors (-i)` to `--optional
(-o)` to better reflect its behavior.

# Tests + Formatting
- 🟢 toolkit fmt
- 🟢 toolkit clippy
- 🟢 toolkit test
- 🟢 toolkit test stdlib

# After Submitting
Update docs and inform third parties that integrate with nushell.

---------

Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
This commit is contained in:
Bahex
2025-07-15 07:26:41 +03:00
committed by GitHub
parent a506d3f9b5
commit beb3ec6a49
15 changed files with 77 additions and 27 deletions

View File

@ -82,7 +82,7 @@ impl Command for Default {
},
Example {
description: "Get the env value of `MY_ENV` with a default value 'abc' if not present",
example: "$env | get --ignore-errors MY_ENV | default 'abc'",
example: "$env | get --optional MY_ENV | default 'abc'",
result: Some(Value::test_string("abc")),
},
Example {

View File

@ -40,9 +40,14 @@ If multiple cell paths are given, this will produce a list of values."#
"The cell path to the data.",
)
.rest("rest", SyntaxShape::CellPath, "Additional cell paths.")
.switch(
"optional",
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional)",
"ignore missing data (make all cell path members optional) (deprecated)",
Some('i'),
)
.switch(
@ -131,13 +136,14 @@ If multiple cell paths are given, this will produce a list of values."#
) -> Result<PipelineData, ShellError> {
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let metadata = input.metadata();
action(
input,
cell_path,
rest,
ignore_errors,
optional,
engine_state.signals().clone(),
call.head,
)
@ -152,6 +158,13 @@ If multiple cell paths are given, this will produce a list of values."#
since: Some("0.105.0".into()),
expected_removal: None,
help: Some("Cell-paths are now case-sensitive by default.\nTo access fields case-insensitively, add `!` after the relevant path member.".into())
},
DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some("This flag has been renamed to `--optional (-o)` to better reflect its behavior.".into())
}
]
}
@ -161,11 +174,11 @@ fn action(
input: PipelineData,
mut cell_path: CellPath,
mut rest: Vec<CellPath>,
ignore_errors: bool,
optional: bool,
signals: Signals,
span: Span,
) -> Result<PipelineData, ShellError> {
if ignore_errors {
if optional {
cell_path.make_optional();
for path in &mut rest {
path.make_optional();

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{ast::PathMember, casing::Casing};
use nu_protocol::{DeprecationEntry, DeprecationType, ReportMode, ast::PathMember, casing::Casing};
use std::{cmp::Reverse, collections::HashSet};
#[derive(Clone)]
@ -17,9 +17,10 @@ impl Command for Reject {
(Type::table(), Type::table()),
(Type::list(Type::Any), Type::list(Type::Any)),
])
.switch("optional", "make all cell path members optional", Some('o'))
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional)",
"ignore missing data (make all cell path members optional) (deprecated)",
Some('i'),
)
.rest(
@ -90,8 +91,9 @@ impl Command for Reject {
}
let span = call.head;
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
if ignore_errors {
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
if optional {
for cell_path in &mut new_columns {
cell_path.make_optional();
}
@ -100,6 +102,19 @@ impl Command for Reject {
reject(engine_state, span, input, new_columns)
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some(
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
.into(),
),
}]
}
fn examples(&self) -> Vec<Example> {
vec![
Example {

View File

@ -1,5 +1,8 @@
use nu_engine::command_prelude::*;
use nu_protocol::{PipelineIterator, ast::PathMember, casing::Casing};
use nu_protocol::{
DeprecationEntry, DeprecationType, PipelineIterator, ReportMode, ast::PathMember,
casing::Casing,
};
use std::collections::BTreeSet;
#[derive(Clone)]
@ -18,9 +21,14 @@ impl Command for Select {
(Type::table(), Type::table()),
(Type::List(Box::new(Type::Any)), Type::Any),
])
.switch(
"optional",
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional)",
"ignore missing data (make all cell path members optional) (deprecated)",
Some('i'),
)
.rest(
@ -100,10 +108,11 @@ produce a table, a list will produce a list, and a record will produce a record.
}
}
}
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let span = call.head;
if ignore_errors {
if optional {
for cell_path in &mut new_columns {
cell_path.make_optional();
}
@ -112,6 +121,19 @@ produce a table, a list will produce a list, and a record will produce a record.
select(engine_state, span, new_columns, input)
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some(
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
.into(),
),
}]
}
fn examples(&self) -> Vec<Example> {
vec![
Example {

View File

@ -31,7 +31,7 @@ fn adds_row_data_if_column_missing() {
#[test]
fn default_after_empty_filter() {
let actual = nu!("[a b] | where $it == 'c' | get -i 0 | default 'd'");
let actual = nu!("[a b] | where $it == 'c' | get -o 0 | default 'd'");
assert_eq!(actual.out, "d");
}

View File

@ -196,14 +196,14 @@ fn get_does_not_delve_too_deep_in_nested_lists() {
#[test]
fn ignore_errors_works() {
let actual = nu!(r#" let path = "foo"; {} | get -i $path | to nuon "#);
let actual = nu!(r#" let path = "foo"; {} | get -o $path | to nuon "#);
assert_eq!(actual.out, "null");
}
#[test]
fn ignore_multiple() {
let actual = nu!(r#"[[a];[b]] | get -i c d | to nuon"#);
let actual = nu!(r#"[[a];[b]] | get -o c d | to nuon"#);
assert_eq!(actual.out, "[[null], [null]]");
}

View File

@ -174,14 +174,14 @@ fn reject_multiple_rows_descending() {
#[test]
fn test_ignore_errors_flag() {
let actual = nu!("[[a, b]; [1, 2], [3, 4], [5, 6]] | reject 5 -i | to nuon");
let actual = nu!("[[a, b]; [1, 2], [3, 4], [5, 6]] | reject 5 -o | to nuon");
assert_eq!(actual.out, "[[a, b]; [1, 2], [3, 4], [5, 6]]");
}
#[test]
fn test_ignore_errors_flag_var() {
let actual =
nu!("let arg = [5 c]; [[a, b]; [1, 2], [3, 4], [5, 6]] | reject ...$arg -i | to nuon");
nu!("let arg = [5 c]; [[a, b]; [1, 2], [3, 4], [5, 6]] | reject ...$arg -o | to nuon");
assert_eq!(actual.out, "[[a, b]; [1, 2], [3, 4], [5, 6]]");
}

View File

@ -236,7 +236,7 @@ fn select_repeated_column() {
fn ignore_errors_works() {
let actual = nu!(r#"
let path = "foo";
[{}] | select -i $path | to nuon
[{}] | select -o $path | to nuon
"#);
assert_eq!(actual.out, "[[foo]; [null]]");

View File

@ -326,7 +326,7 @@ fn from_csv_text_with_missing_columns_to_table() {
r#"
open los_tres_caballeros.txt
| from csv --flexible
| get -i rusty_luck
| get -o rusty_luck
| compact
| length
"#

View File

@ -245,7 +245,7 @@ fn from_tsv_text_with_missing_columns_to_table() {
r#"
open los_tres_caballeros.txt
| from tsv --flexible
| get -i rusty_luck
| get -o rusty_luck
| compact
| length
"#

View File

@ -42,7 +42,7 @@ def get-annotated [
| from nuon
| each {|e|
# filter commands with test attributes, and map attributes to annotation name
let test_attributes = $e.attributes.name | each {|x| $valid_annotations | get -i $x }
let test_attributes = $e.attributes.name | each {|x| $valid_annotations | get -o $x }
if ($test_attributes | is-not-empty) {
$e | update attributes $test_attributes.0
}

View File

@ -11,7 +11,7 @@ def run [
} else {
^$nu.current-exe --no-config-file --commands $'use std; use std/log; NU_LOG_LEVEL=($system_level) log ($message_level) "test message"'
}
| complete | get --ignore-errors stderr
| complete | get --optional stderr
}
def "assert no message" [

View File

@ -19,7 +19,7 @@ def run-command [
} else {
^$nu.current-exe --no-config-file --commands $'use std/log; NU_LOG_LEVEL=($system_level) log custom "($message)" "($format)" ($log_level) --level-prefix "($level_prefix)" --ansi "($ansi)"'
}
| complete | get --ignore-errors stderr
| complete | get --optional stderr
}
@test

View File

@ -16,7 +16,7 @@ def run-command [
} else {
^$nu.current-exe --no-config-file --commands $'use std; use std/log; NU_LOG_LEVEL=($system_level) log ($message_level) --format "($format)" "($message)"'
}
| complete | get --ignore-errors stderr
| complete | get --optional stderr
}

View File

@ -10,7 +10,7 @@ export def "update" [
let input = $in
match ($input | describe | str replace --regex '<.*' '') {
record => {
if ($input | get -i $field) != null {
if ($input | get -o $field) != null {
$input | orig update $field $value
} else { $input }
}