1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-24 22:11:15 +02:00
This commit is contained in:
Shahar "Dawn" Or 2025-06-23 10:10:54 -07:00 committed by GitHub
commit a2784e510f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 93 additions and 20 deletions

View file

@ -112,6 +112,11 @@ standard input.
When used with `--eval`, print the resulting value as an JSON
representation of the abstract syntax tree rather than as a Nix expression.
- `--replace-eval-errors`
When used with `--eval` and `--json`, replace any evaluation errors with the string
`"«evaluation error»"`.
- `--xml`
When used with `--eval`, print the resulting value as an XML
@ -205,3 +210,10 @@ $ nix-instantiate --eval --xml --strict --expr '{ x = {}; }'
</attrs>
</expr>
```
Replacing evaluation errors:
```console
$ nix-instantiate --eval --json --replace-eval-errors --expr '{ a = throw "fail"; }'
{"a":"«evaluation error»"}
```

View file

@ -9,7 +9,7 @@ namespace nix {
std::string getJSONValue(Value& value) {
std::stringstream ss;
NixStringContext ps;
printValueAsJSON(state, true, value, noPos, ss, ps);
printValueAsJSON(state, true, false, value, noPos, ss, ps);
return ss.str();
}
};

View file

@ -10,10 +10,10 @@
namespace nix {
nlohmann::json printValueAsJSON(EvalState & state, bool strict,
nlohmann::json printValueAsJSON(EvalState & state, bool strict, bool replaceEvalErrors,
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore = true);
void printValueAsJSON(EvalState & state, bool strict,
void printValueAsJSON(EvalState & state, bool strict, bool replaceEvalErrors,
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);

View file

@ -1383,7 +1383,7 @@ static void derivationStrictInternal(
if (i->name == state.sStructuredAttrs) continue;
jsonObject->emplace(key, printValueAsJSON(state, true, *i->value, pos, context));
jsonObject->emplace(key, printValueAsJSON(state, true, false, *i->value, pos, context));
if (i->name == state.sBuilder)
drv.builder = state.forceString(*i->value, context, pos, context_below);
@ -2325,7 +2325,7 @@ static void prim_toJSON(EvalState & state, const PosIdx pos, Value * * args, Val
{
std::ostringstream out;
NixStringContext context;
printValueAsJSON(state, true, *args[0], pos, out, context);
printValueAsJSON(state, true, false, *args[0], pos, out, context);
v.mkString(toView(out), context);
}

View file

@ -135,7 +135,7 @@ static void fetchTree(
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
} else if (state.symbols[attr.name] == "publicKeys") {
experimentalFeatureSettings.require(Xp::VerifiedFetches);
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump());
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, false, *attr.value, pos, context).dump());
}
else
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",

View file

@ -6,12 +6,13 @@
#include <cstdlib>
#include <iomanip>
#include <nlohmann/json.hpp>
#include <typeinfo>
namespace nix {
using json = nlohmann::json;
// TODO: rename. It doesn't print.
json printValueAsJSON(EvalState & state, bool strict,
json printValueAsJSON(EvalState & state, bool strict, bool replaceEvalErrors,
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
{
checkInterrupt();
@ -54,13 +55,27 @@ json printValueAsJSON(EvalState & state, bool strict,
break;
}
if (auto i = v.attrs()->get(state.sOutPath))
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
return printValueAsJSON(state, strict, replaceEvalErrors, *i->value, i->pos, context, copyToStore);
else {
out = json::object();
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
try {
out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore));
out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, replaceEvalErrors, *a->value, a->pos, context, copyToStore));
} catch (Error & e) {
std::cerr << "Caught an Error of type: " << typeid(e).name() << std::endl;
// std::cerr << "Caught an Error of type: " << e.message() << std::endl;
// std::cerr << "Caught an Error of type: " << e.what() << std::endl;
// TODO: Figure out what Error is here?
// We seem to be not catching FileNotFoundError.
bool isEvalError = dynamic_cast<EvalError *>(&e);
bool isFileNotFoundError = dynamic_cast<FileNotFound *>(&e);
// Restrict replaceEvalErrors only only evaluation errors
if (replaceEvalErrors && (isEvalError || isFileNotFoundError)) {
out.emplace(state.symbols[a->name], "«evaluation error»");
continue;
}
e.addTrace(state.positions[a->pos],
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
throw;
@ -75,8 +90,9 @@ json printValueAsJSON(EvalState & state, bool strict,
int i = 0;
for (auto elem : v.listItems()) {
try {
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
out.push_back(printValueAsJSON(state, strict, replaceEvalErrors, *elem, pos, context, copyToStore));
} catch (Error & e) {
// TODO: Missing catch
e.addTrace(state.positions[pos],
HintFmt("while evaluating list element at index %1%", i));
throw;
@ -106,11 +122,11 @@ json printValueAsJSON(EvalState & state, bool strict,
return out;
}
void printValueAsJSON(EvalState & state, bool strict,
void printValueAsJSON(EvalState & state, bool strict, bool replaceEvalErrors,
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore)
{
try {
str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
str << printValueAsJSON(state, strict, replaceEvalErrors, v, pos, context, copyToStore);
} catch (nlohmann::json::exception & e) {
throw JSONSerializationError("JSON serialization error: %s", e.what());
}

View file

@ -91,7 +91,7 @@ static void parseFlakeInputAttr(
if (attr.name == state.symbols.create("publicKeys")) {
experimentalFeatureSettings.require(Xp::VerifiedFetches);
NixStringContext emptyContext = {};
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, attr.pos, emptyContext).dump());
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, false, *attr.value, attr.pos, emptyContext).dump());
} else
state.error<TypeError>("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
state.symbols[attr.name], showType(*attr.value)).debugThrow();

View file

@ -54,7 +54,7 @@ void PosixSourceAccessor::readFile(
#endif
));
if (!fd)
throw SysError("opening file '%1%'", ap.string());
throw SysError("opening file9 '%1%'", ap.string());
struct stat st;
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)

View file

@ -984,7 +984,7 @@ static void queryJSON(Globals & globals, std::vector<PackageInfo> & elems, bool
metaObj[j] = nullptr;
} else {
NixStringContext context;
metaObj[j] = printValueAsJSON(*globals.state, true, *v, noPos, context);
metaObj[j] = printValueAsJSON(*globals.state, true, false, *v, noPos, context);
}
}
}

View file

@ -28,7 +28,7 @@ static int rootNr = 0;
enum OutputKind { okPlain, okRaw, okXML, okJSON };
void processExpr(EvalState & state, const Strings & attrPaths,
bool parseOnly, bool strict, Bindings & autoArgs,
bool parseOnly, bool strict, bool replaceEvalErrors, Bindings & autoArgs,
bool evalOnly, OutputKind output, bool location, Expr * e)
{
if (parseOnly) {
@ -58,7 +58,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
else if (output == okXML)
printValueAsXML(state, strict, location, vRes, std::cout, context, noPos);
else if (output == okJSON) {
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
printValueAsJSON(state, strict, replaceEvalErrors, vRes, v.determinePos(noPos), std::cout, context);
std::cout << std::endl;
} else {
if (strict) state.forceValueDeep(vRes);
@ -106,6 +106,7 @@ static int main_nix_instantiate(int argc, char * * argv)
OutputKind outputKind = okPlain;
bool xmlOutputSourceLocation = true;
bool strict = false;
bool replaceEvalErrors = false;
Strings attrPaths;
bool wantsReadWrite = false;
@ -147,6 +148,8 @@ static int main_nix_instantiate(int argc, char * * argv)
xmlOutputSourceLocation = false;
else if (*arg == "--strict")
strict = true;
else if (*arg == "--replace-eval-errors")
replaceEvalErrors = true;
else if (*arg == "--dry-run")
settings.readOnlyMode = true;
else if (*arg != "" && arg->at(0) == '-')
@ -184,7 +187,7 @@ static int main_nix_instantiate(int argc, char * * argv)
if (readStdin) {
Expr * e = state->parseStdin();
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
processExpr(*state, attrPaths, parseOnly, strict, replaceEvalErrors, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
} else if (files.empty() && !fromArgs)
files.push_back("./default.nix");
@ -193,7 +196,7 @@ static int main_nix_instantiate(int argc, char * * argv)
Expr * e = fromArgs
? state->parseExprFromString(i, state->rootPath("."))
: state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, i)));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
processExpr(*state, attrPaths, parseOnly, strict, replaceEvalErrors, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
}

View file

@ -15,6 +15,7 @@ namespace nix::fs { using namespace std::filesystem; }
struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
{
bool raw = false;
bool replaceEvalErrors = false;
std::optional<std::string> apply;
std::optional<std::filesystem::path> writeTo;
@ -39,6 +40,12 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
.labels = {"path"},
.handler = {&writeTo},
});
addFlag({
.longName = "replace-eval-errors",
.description = "When used with `--json` the Nix evaluator will replace evaluation errors with a fixed value.",
.handler = {&replaceEvalErrors, true},
});
}
std::string description() override
@ -118,7 +125,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
}
else if (json) {
printJSON(printValueAsJSON(*state, true, *v, pos, context, false));
printJSON(printValueAsJSON(*state, true, replaceEvalErrors, *v, pos, context, false));
}
else {

View file

@ -48,6 +48,13 @@ R""(
# cat ./out/subdir/bla
123
* Replace evaluation errors:
```console
$ nix eval --json --replace-eval-errors --expr '{ a = throw "fail"; }'
{"a":"«evaluation error»"}
```
# Description
This command evaluates the given Nix expression, and prints the result on standard output.

View file

@ -35,6 +35,23 @@ nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
[[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]]
[[ "$(nix-instantiate --eval -E '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]]
expected="$(echo '{
"missingAttr":"«evaluation error»",
"insideAList":["«evaluation error»"],
"deeper":{v:"«evaluation error»"},
"failedAssertion":"«evaluation error»",
"missingFile":"«evaluation error»",
"missingImport":"«evaluation error»",
"outOfBounds":"«evaluation error»",
"failedCoersion":"«evaluation error»",
"failedAddition":"«evaluation error»"
}' | tr -d '\n')"
actual="$(nix-instantiate --eval --json --strict --replace-eval-errors "./replace-eval-errors.nix")"
[[ $actual == "$expected" ]] || diff --unified <(echo "$actual") <(echo "$expected") >&2
actual="$(nix eval --json --replace-eval-errors -f "./replace-eval-errors.nix")"
[[ $actual == "$expected" ]] || diff --unified <(echo "$actual") <(echo "$expected") >&2
# Check that symlink cycles don't cause a hang.
ln -sfn cycle.nix "$TEST_ROOT/cycle.nix"
(! nix eval --file "$TEST_ROOT/cycle.nix")

View file

@ -0,0 +1,11 @@
{
missingAttr = let bar = { }; in bar.notExist;
insideAList = [ (throw "a throw") ];
deeper = { v = throw "v"; };
failedAssertion = assert true; assert false; null;
missingFile = builtins.readFile ./missing-file.txt;
missingImport = import ./missing-import.nix;
outOfBounds = builtins.elemAt [ 1 2 3 ] 100;
failedCoersion = "${1}";
failedAddition = 1.0 + "a string";
}