mirror of
https://github.com/nushell/nushell.git
synced 2024-12-23 15:39:06 +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 {
|
||||
// integers only
|
||||
Ok(PipelineData::ListStream(
|
||||
nu_protocol::ListStream {
|
||||
stream: Box::new(IntSeq {
|
||||
nu_protocol::ListStream::from_stream(
|
||||
IntSeq {
|
||||
count: first as i64,
|
||||
step: step as i64,
|
||||
last: last as i64,
|
||||
span,
|
||||
}),
|
||||
ctrlc: engine_state.ctrlc.clone(),
|
||||
},
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
),
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
// floats
|
||||
Ok(PipelineData::ListStream(
|
||||
nu_protocol::ListStream {
|
||||
stream: Box::new(FloatSeq {
|
||||
nu_protocol::ListStream::from_stream(
|
||||
FloatSeq {
|
||||
first,
|
||||
step,
|
||||
last,
|
||||
index: 0,
|
||||
span,
|
||||
}),
|
||||
ctrlc: engine_state.ctrlc.clone(),
|
||||
},
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
@ -270,25 +270,19 @@ impl PipelineData {
|
||||
match self {
|
||||
PipelineData::Value(val, metadata) => match val {
|
||||
Value::List { vals, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(vals.into_iter()),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(vals.into_iter(), None),
|
||||
metadata,
|
||||
))),
|
||||
Value::Binary { val, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(val.into_iter().map(move |x| Value::int(x as i64, span))),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(
|
||||
val.into_iter().map(move |x| Value::int(x as i64, span)),
|
||||
None,
|
||||
),
|
||||
metadata,
|
||||
))),
|
||||
Value::Range { val, .. } => match val.into_range_iter(None) {
|
||||
Ok(iter) => Ok(PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(iter),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(iter, None),
|
||||
metadata,
|
||||
))),
|
||||
Err(error) => Err(error),
|
||||
@ -822,25 +816,19 @@ impl IntoIterator for PipelineData {
|
||||
let span = v.span();
|
||||
match v {
|
||||
Value::List { vals, .. } => PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(vals.into_iter()),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(vals.into_iter(), None),
|
||||
metadata,
|
||||
)),
|
||||
Value::Range { val, .. } => match val.into_range_iter(None) {
|
||||
Ok(iter) => PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(iter),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(iter, None),
|
||||
metadata,
|
||||
)),
|
||||
Err(error) => PipelineIterator(PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(std::iter::once(Value::error(error, span))),
|
||||
ctrlc: None,
|
||||
},
|
||||
ListStream::from_stream(
|
||||
std::iter::once(Value::error(error, span)),
|
||||
None,
|
||||
),
|
||||
metadata,
|
||||
)),
|
||||
},
|
||||
@ -965,10 +953,7 @@ where
|
||||
{
|
||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
||||
PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(self.into_iter().map(Into::into)),
|
||||
ctrlc,
|
||||
},
|
||||
ListStream::from_stream(self.into_iter().map(Into::into), ctrlc),
|
||||
None,
|
||||
)
|
||||
}
|
||||
@ -979,10 +964,7 @@ where
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> PipelineData {
|
||||
PipelineData::ListStream(
|
||||
ListStream {
|
||||
stream: Box::new(self.into_iter().map(Into::into)),
|
||||
ctrlc,
|
||||
},
|
||||
ListStream::from_stream(self.into_iter().map(Into::into), ctrlc),
|
||||
metadata.into(),
|
||||
)
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ impl Iterator for RawStream {
|
||||
pub struct ListStream {
|
||||
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||
first_guard: bool,
|
||||
}
|
||||
|
||||
impl ListStream {
|
||||
@ -209,6 +210,7 @@ impl ListStream {
|
||||
ListStream {
|
||||
stream: Box::new(input),
|
||||
ctrlc,
|
||||
first_guard: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,6 +225,17 @@ impl Iterator for ListStream {
|
||||
type Item = Value;
|
||||
|
||||
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) {
|
||||
None
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user