mirror of
https://github.com/nushell/nushell.git
synced 2025-02-16 18:41:44 +01:00
fix exit_code handling when running a scripts with ctrlc (#11466)
# Description Fixes: #11394 When run `^sleep 3` we have an `exit_code ListStream`, and when we press ctrl-c, this `ListStream` will return None. But it's not expected, because `exit_code` sender in `run_external` always send an exit code out. This pr is trying to fix the issue by introducing a `first_guard` into ListStream, it will always generate a value from underlying stream if `first_guard` is true, so it's guarantee to have at least one value to return. And the pr also do a little refactor, which makes use of `ListStream::from_stream` rather than construct it manually. # User-Facing Changes ## Before ``` > nu -c "^sleep 3" # press ctrl-c > echo $env.LAST_EXIT_CODE 0 ``` ## After ``` > nu -c "^sleep 3" # press ctrl-c > echo $env.LAST_EXIT_CODE 255 ``` # Tests + Formatting None, sorry that I don't think it's easy to test the ctrlc behavior. # After Submitting None
This commit is contained in:
parent
4e0a65c822
commit
c371d1a535
@ -140,30 +140,30 @@ pub fn run_seq(
|
|||||||
if !contains_decimals {
|
if !contains_decimals {
|
||||||
// integers only
|
// integers only
|
||||||
Ok(PipelineData::ListStream(
|
Ok(PipelineData::ListStream(
|
||||||
nu_protocol::ListStream {
|
nu_protocol::ListStream::from_stream(
|
||||||
stream: Box::new(IntSeq {
|
IntSeq {
|
||||||
count: first as i64,
|
count: first as i64,
|
||||||
step: step as i64,
|
step: step as i64,
|
||||||
last: last as i64,
|
last: last as i64,
|
||||||
span,
|
span,
|
||||||
}),
|
},
|
||||||
ctrlc: engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
},
|
),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// floats
|
// floats
|
||||||
Ok(PipelineData::ListStream(
|
Ok(PipelineData::ListStream(
|
||||||
nu_protocol::ListStream {
|
nu_protocol::ListStream::from_stream(
|
||||||
stream: Box::new(FloatSeq {
|
FloatSeq {
|
||||||
first,
|
first,
|
||||||
step,
|
step,
|
||||||
last,
|
last,
|
||||||
index: 0,
|
index: 0,
|
||||||
span,
|
span,
|
||||||
}),
|
},
|
||||||
ctrlc: engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
},
|
),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -270,25 +270,19 @@ impl PipelineData {
|
|||||||
match self {
|
match self {
|
||||||
PipelineData::Value(val, metadata) => match val {
|
PipelineData::Value(val, metadata) => match val {
|
||||||
Value::List { vals, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
Value::List { vals, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(vals.into_iter(), None),
|
||||||
stream: Box::new(vals.into_iter()),
|
|
||||||
ctrlc: None,
|
|
||||||
},
|
|
||||||
metadata,
|
metadata,
|
||||||
))),
|
))),
|
||||||
Value::Binary { val, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
Value::Binary { val, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(
|
||||||
stream: Box::new(val.into_iter().map(move |x| Value::int(x as i64, span))),
|
val.into_iter().map(move |x| Value::int(x as i64, span)),
|
||||||
ctrlc: None,
|
None,
|
||||||
},
|
),
|
||||||
metadata,
|
metadata,
|
||||||
))),
|
))),
|
||||||
Value::Range { val, .. } => match val.into_range_iter(None) {
|
Value::Range { val, .. } => match val.into_range_iter(None) {
|
||||||
Ok(iter) => Ok(PipelineIterator(PipelineData::ListStream(
|
Ok(iter) => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(iter, None),
|
||||||
stream: Box::new(iter),
|
|
||||||
ctrlc: None,
|
|
||||||
},
|
|
||||||
metadata,
|
metadata,
|
||||||
))),
|
))),
|
||||||
Err(error) => Err(error),
|
Err(error) => Err(error),
|
||||||
@ -822,25 +816,19 @@ impl IntoIterator for PipelineData {
|
|||||||
let span = v.span();
|
let span = v.span();
|
||||||
match v {
|
match v {
|
||||||
Value::List { vals, .. } => PipelineIterator(PipelineData::ListStream(
|
Value::List { vals, .. } => PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(vals.into_iter(), None),
|
||||||
stream: Box::new(vals.into_iter()),
|
|
||||||
ctrlc: None,
|
|
||||||
},
|
|
||||||
metadata,
|
metadata,
|
||||||
)),
|
)),
|
||||||
Value::Range { val, .. } => match val.into_range_iter(None) {
|
Value::Range { val, .. } => match val.into_range_iter(None) {
|
||||||
Ok(iter) => PipelineIterator(PipelineData::ListStream(
|
Ok(iter) => PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(iter, None),
|
||||||
stream: Box::new(iter),
|
|
||||||
ctrlc: None,
|
|
||||||
},
|
|
||||||
metadata,
|
metadata,
|
||||||
)),
|
)),
|
||||||
Err(error) => PipelineIterator(PipelineData::ListStream(
|
Err(error) => PipelineIterator(PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(
|
||||||
stream: Box::new(std::iter::once(Value::error(error, span))),
|
std::iter::once(Value::error(error, span)),
|
||||||
ctrlc: None,
|
None,
|
||||||
},
|
),
|
||||||
metadata,
|
metadata,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
@ -965,10 +953,7 @@ where
|
|||||||
{
|
{
|
||||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
||||||
PipelineData::ListStream(
|
PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(self.into_iter().map(Into::into), ctrlc),
|
||||||
stream: Box::new(self.into_iter().map(Into::into)),
|
|
||||||
ctrlc,
|
|
||||||
},
|
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -979,10 +964,7 @@ where
|
|||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
) -> PipelineData {
|
) -> PipelineData {
|
||||||
PipelineData::ListStream(
|
PipelineData::ListStream(
|
||||||
ListStream {
|
ListStream::from_stream(self.into_iter().map(Into::into), ctrlc),
|
||||||
stream: Box::new(self.into_iter().map(Into::into)),
|
|
||||||
ctrlc,
|
|
||||||
},
|
|
||||||
metadata.into(),
|
metadata.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,7 @@ impl Iterator for RawStream {
|
|||||||
pub struct ListStream {
|
pub struct ListStream {
|
||||||
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
||||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
first_guard: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListStream {
|
impl ListStream {
|
||||||
@ -209,6 +210,7 @@ impl ListStream {
|
|||||||
ListStream {
|
ListStream {
|
||||||
stream: Box::new(input),
|
stream: Box::new(input),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
|
first_guard: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,6 +225,17 @@ impl Iterator for ListStream {
|
|||||||
type Item = Value;
|
type Item = Value;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// We need to check `first_guard` to guarantee that it always have something to return in
|
||||||
|
// underlying stream.
|
||||||
|
//
|
||||||
|
// A realworld example is running an external commands, which have an `exit_code`
|
||||||
|
// ListStream.
|
||||||
|
// When we press ctrl-c, the external command receives the signal too, if we don't have
|
||||||
|
// `first_guard`, the `exit_code` ListStream will return Nothing, which is not expected
|
||||||
|
if self.first_guard {
|
||||||
|
self.first_guard = false;
|
||||||
|
return self.stream.next();
|
||||||
|
}
|
||||||
if nu_utils::ctrl_c::was_pressed(&self.ctrlc) {
|
if nu_utils::ctrl_c::was_pressed(&self.ctrlc) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user