Add option to not load std-lib. Default tests to not use std-lib (#8833)

this adds a `--no-std-lib` flag. Moves `nu!` to use the `--no-std-lib`.
Adds a new `nu_with_std!` macro for future tests that need the std-lib.

# Description

_(Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.)_

_(Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.)_

# User-Facing Changes

_(List of all changes that impact the user experience here. This helps
us keep track of breaking changes.)_

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
JT 2023-04-10 10:55:29 +12:00 committed by GitHub
parent 9e3d6c3bfd
commit 58b96fdede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 174 additions and 3 deletions

View File

@ -113,6 +113,167 @@ macro_rules! nu {
format!($path, $( $part ),*)
}};
// Do the actual work.
(@main $opts:expr, $path:expr) => {{
pub use std::error::Error;
pub use std::io::prelude::*;
pub use std::process::{Command, Stdio};
pub use $crate::NATIVE_PATH_ENV_VAR;
pub fn escape_quote_string(input: String) -> String {
let mut output = String::with_capacity(input.len() + 2);
output.push('"');
for c in input.chars() {
if c == '"' || c == '\\' {
output.push('\\');
}
output.push(c);
}
output.push('"');
output
}
let test_bins = $crate::fs::binaries();
let cwd = std::env::current_dir().expect("Could not get current working directory.");
let test_bins = $crate::nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| {
panic!(
"Couldn't canonicalize dummy binaries path {}: {:?}",
test_bins.display(),
e
)
});
let mut paths = $crate::shell_os_paths();
paths.insert(0, test_bins);
let path = $path.lines().collect::<Vec<_>>().join("; ");
let paths_joined = match std::env::join_paths(paths) {
Ok(all) => all,
Err(_) => panic!("Couldn't join paths for PATH var."),
};
let target_cwd = $opts.cwd.unwrap_or(".".to_string());
let locale = $opts.locale.unwrap_or("en_US.UTF-8".to_string());
let mut command = Command::new($crate::fs::executable_path());
command
.env("PWD", &target_cwd)
.env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
.current_dir(target_cwd)
.env(NATIVE_PATH_ENV_VAR, paths_joined)
// .arg("--skip-plugins")
// .arg("--no-history")
// .arg("--config-file")
// .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml")))
.arg("--no-std-lib")
.arg(format!("-c {}", escape_quote_string(path)))
.stdout(Stdio::piped())
// .stdin(Stdio::piped())
.stderr(Stdio::piped());
let mut process = match command.spawn()
{
Ok(child) => child,
Err(why) => panic!("Can't run test {:?} {}", $crate::fs::executable_path(), why.to_string()),
};
// let stdin = process.stdin.as_mut().expect("couldn't open stdin");
// stdin
// .write_all(b"exit\n")
// .expect("couldn't write to stdin");
let output = process
.wait_with_output()
.expect("couldn't read from stdout/stderr");
let out = $crate::macros::read_std(&output.stdout);
let err = String::from_utf8_lossy(&output.stderr);
println!("=== stderr\n{}", err);
$crate::Outcome::new(out,err.into_owned())
}};
// This is the entrypoint for this macro.
($($token:tt)*) => {{
#[derive(Default)]
struct NuOpts {
cwd: Option<String>,
locale: Option<String>,
}
nu!(@options [ ] $($token)*)
}};
}
#[macro_export]
macro_rules! nu_with_std {
// In the `@options` phase, we restucture all the
// `$field_1: $value_1, $field_2: $value_2, ...`
// pairs to a structure like
// `@options[ $field_1 => $value_1 ; $field_2 => $value_2 ; ... ]`.
// We do this to later distinguish the options from the `$path` and `$part`s.
// (See
// https://users.rust-lang.org/t/i-dont-think-this-local-ambiguity-when-calling-macro-is-ambiguous/79401?u=x3ro
// )
//
// If there is any special treatment needed for the `$value`, we can just
// match for the specific `field` name.
(
@options [ $($options:tt)* ]
cwd: $value:expr,
$($rest:tt)*
) => {
nu!(@options [ $($options)* cwd => $crate::fs::in_directory($value) ; ] $($rest)*)
};
// For all other options, we call `.into()` on the `$value` and hope for the best. ;)
(
@options [ $($options:tt)* ]
$field:ident : $value:expr,
$($rest:tt)*
) => {
nu!(@options [ $($options)* $field => $value.into() ; ] $($rest)*)
};
// When the `$field: $value,` pairs are all parsed, the next tokens are the `$path` and any
// number of `$part`s, potentially followed by a trailing comma.
(
@options [ $($options:tt)* ]
$path:expr
$(, $part:expr)*
$(,)*
) => {{
// Here we parse the options into a `NuOpts` struct
let opts = nu!(@nu_opts $($options)*);
// and format the `$path` using the `$part`s
let path = nu!(@format_path $path, $($part),*);
// Then finally we go to the `@main` phase, where the actual work is done.
nu!(@main opts, path)
}};
// Create the NuOpts struct from the `field => value ;` pairs
(@nu_opts $( $field:ident => $value:expr ; )*) => {
NuOpts{
$(
$field: Some($value),
)*
..Default::default()
}
};
// Helper to format `$path`.
(@format_path $path:expr $(,)?) => {
// When there are no `$part`s, do not format anything
$path
};
(@format_path $path:expr, $($part:expr),* $(,)?) => {{
format!($path, $( $part ),*)
}};
// Do the actual work.
(@main $opts:expr, $path:expr) => {{
pub use std::error::Error;

View File

@ -103,6 +103,7 @@ pub(crate) fn parse_commandline_args(
#[cfg(feature = "plugin")]
let plugin_file: Option<Expression> = call.get_flag_expr("plugin-config");
let no_config_file = call.get_named_arg("no-config-file");
let no_std_lib = call.get_named_arg("no-std-lib");
let config_file: Option<Expression> = call.get_flag_expr("config");
let env_file: Option<Expression> = call.get_flag_expr("env-config");
let log_level: Option<Expression> = call.get_flag_expr("log-level");
@ -176,6 +177,7 @@ pub(crate) fn parse_commandline_args(
#[cfg(feature = "plugin")]
plugin_file,
no_config_file,
no_std_lib,
config_file,
env_file,
log_level,
@ -211,6 +213,7 @@ pub(crate) struct NushellCliArgs {
#[cfg(feature = "plugin")]
pub(crate) plugin_file: Option<Spanned<String>>,
pub(crate) no_config_file: Option<Spanned<String>>,
pub(crate) no_std_lib: Option<Spanned<String>>,
pub(crate) config_file: Option<Spanned<String>>,
pub(crate) env_file: Option<Spanned<String>>,
pub(crate) log_level: Option<Spanned<String>>,
@ -259,6 +262,7 @@ impl Command for Nu {
"start with no config file and no env file",
Some('n'),
)
.switch("no-std-lib", "start with no standard library", None)
.named(
"threads",
SyntaxShape::Int,

View File

@ -22,7 +22,9 @@ pub(crate) fn run_commands(
let mut stack = nu_protocol::engine::Stack::new();
let start_time = std::time::Instant::now();
load_standard_library(engine_state)?;
if parsed_nu_cli_args.no_std_lib.is_none() {
load_standard_library(engine_state)?;
}
#[cfg(feature = "plugin")]
read_plugin_file(
@ -111,7 +113,9 @@ pub(crate) fn run_file(
let mut stack = nu_protocol::engine::Stack::new();
let start_time = std::time::Instant::now();
load_standard_library(engine_state)?;
if parsed_nu_cli_args.no_std_lib.is_none() {
load_standard_library(engine_state)?;
}
#[cfg(feature = "plugin")]
read_plugin_file(
@ -210,7 +214,9 @@ pub(crate) fn run_repl(
let mut stack = nu_protocol::engine::Stack::new();
let start_time = std::time::Instant::now();
load_standard_library(engine_state)?;
if parsed_nu_cli_args.no_std_lib.is_none() {
load_standard_library(engine_state)?;
}
if parsed_nu_cli_args.no_config_file.is_none() {
setup_config(