mirror of
https://github.com/NixOS/nix
synced 2025-06-24 22:11:15 +02:00
Merge 88705f848b
into f9afc1e68c
This commit is contained in:
commit
a2784e510f
14 changed files with 93 additions and 20 deletions
|
@ -112,6 +112,11 @@ standard input.
|
||||||
When used with `--eval`, print the resulting value as an JSON
|
When used with `--eval`, print the resulting value as an JSON
|
||||||
representation of the abstract syntax tree rather than as a Nix expression.
|
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`
|
- `--xml`
|
||||||
|
|
||||||
When used with `--eval`, print the resulting value as an XML
|
When used with `--eval`, print the resulting value as an XML
|
||||||
|
@ -205,3 +210,10 @@ $ nix-instantiate --eval --xml --strict --expr '{ x = {}; }'
|
||||||
</attrs>
|
</attrs>
|
||||||
</expr>
|
</expr>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Replacing evaluation errors:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-instantiate --eval --json --replace-eval-errors --expr '{ a = throw "fail"; }'
|
||||||
|
{"a":"«evaluation error»"}
|
||||||
|
```
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
||||||
std::string getJSONValue(Value& value) {
|
std::string getJSONValue(Value& value) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
NixStringContext ps;
|
NixStringContext ps;
|
||||||
printValueAsJSON(state, true, value, noPos, ss, ps);
|
printValueAsJSON(state, true, false, value, noPos, ss, ps);
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
namespace nix {
|
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);
|
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);
|
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1383,7 +1383,7 @@ static void derivationStrictInternal(
|
||||||
|
|
||||||
if (i->name == state.sStructuredAttrs) continue;
|
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)
|
if (i->name == state.sBuilder)
|
||||||
drv.builder = state.forceString(*i->value, context, pos, context_below);
|
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;
|
std::ostringstream out;
|
||||||
NixStringContext context;
|
NixStringContext context;
|
||||||
printValueAsJSON(state, true, *args[0], pos, out, context);
|
printValueAsJSON(state, true, false, *args[0], pos, out, context);
|
||||||
v.mkString(toView(out), context);
|
v.mkString(toView(out), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ static void fetchTree(
|
||||||
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
|
attrs.emplace(state.symbols[attr.name], uint64_t(intValue));
|
||||||
} else if (state.symbols[attr.name] == "publicKeys") {
|
} else if (state.symbols[attr.name] == "publicKeys") {
|
||||||
experimentalFeatureSettings.require(Xp::VerifiedFetches);
|
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
|
else
|
||||||
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
|
state.error<TypeError>("argument '%s' to '%s' is %s while a string, Boolean or integer is expected",
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
// TODO: rename. It doesn't print.
|
// 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)
|
Value & v, const PosIdx pos, NixStringContext & context, bool copyToStore)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
@ -54,13 +55,27 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (auto i = v.attrs()->get(state.sOutPath))
|
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 {
|
else {
|
||||||
out = json::object();
|
out = json::object();
|
||||||
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
|
for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) {
|
||||||
try {
|
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) {
|
} 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],
|
e.addTrace(state.positions[a->pos],
|
||||||
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
|
HintFmt("while evaluating attribute '%1%'", state.symbols[a->name]));
|
||||||
throw;
|
throw;
|
||||||
|
@ -75,8 +90,9 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto elem : v.listItems()) {
|
for (auto elem : v.listItems()) {
|
||||||
try {
|
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) {
|
} catch (Error & e) {
|
||||||
|
// TODO: Missing catch
|
||||||
e.addTrace(state.positions[pos],
|
e.addTrace(state.positions[pos],
|
||||||
HintFmt("while evaluating list element at index %1%", i));
|
HintFmt("while evaluating list element at index %1%", i));
|
||||||
throw;
|
throw;
|
||||||
|
@ -106,11 +122,11 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||||
return out;
|
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)
|
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
|
str << printValueAsJSON(state, strict, replaceEvalErrors, v, pos, context, copyToStore);
|
||||||
} catch (nlohmann::json::exception & e) {
|
} catch (nlohmann::json::exception & e) {
|
||||||
throw JSONSerializationError("JSON serialization error: %s", e.what());
|
throw JSONSerializationError("JSON serialization error: %s", e.what());
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ static void parseFlakeInputAttr(
|
||||||
if (attr.name == state.symbols.create("publicKeys")) {
|
if (attr.name == state.symbols.create("publicKeys")) {
|
||||||
experimentalFeatureSettings.require(Xp::VerifiedFetches);
|
experimentalFeatureSettings.require(Xp::VerifiedFetches);
|
||||||
NixStringContext emptyContext = {};
|
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
|
} else
|
||||||
state.error<TypeError>("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
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();
|
state.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||||
|
|
|
@ -54,7 +54,7 @@ void PosixSourceAccessor::readFile(
|
||||||
#endif
|
#endif
|
||||||
));
|
));
|
||||||
if (!fd)
|
if (!fd)
|
||||||
throw SysError("opening file '%1%'", ap.string());
|
throw SysError("opening file9 '%1%'", ap.string());
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)
|
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)
|
||||||
|
|
|
@ -984,7 +984,7 @@ static void queryJSON(Globals & globals, std::vector<PackageInfo> & elems, bool
|
||||||
metaObj[j] = nullptr;
|
metaObj[j] = nullptr;
|
||||||
} else {
|
} else {
|
||||||
NixStringContext context;
|
NixStringContext context;
|
||||||
metaObj[j] = printValueAsJSON(*globals.state, true, *v, noPos, context);
|
metaObj[j] = printValueAsJSON(*globals.state, true, false, *v, noPos, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ static int rootNr = 0;
|
||||||
enum OutputKind { okPlain, okRaw, okXML, okJSON };
|
enum OutputKind { okPlain, okRaw, okXML, okJSON };
|
||||||
|
|
||||||
void processExpr(EvalState & state, const Strings & attrPaths,
|
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)
|
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||||
{
|
{
|
||||||
if (parseOnly) {
|
if (parseOnly) {
|
||||||
|
@ -58,7 +58,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
else if (output == okXML)
|
else if (output == okXML)
|
||||||
printValueAsXML(state, strict, location, vRes, std::cout, context, noPos);
|
printValueAsXML(state, strict, location, vRes, std::cout, context, noPos);
|
||||||
else if (output == okJSON) {
|
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;
|
std::cout << std::endl;
|
||||||
} else {
|
} else {
|
||||||
if (strict) state.forceValueDeep(vRes);
|
if (strict) state.forceValueDeep(vRes);
|
||||||
|
@ -106,6 +106,7 @@ static int main_nix_instantiate(int argc, char * * argv)
|
||||||
OutputKind outputKind = okPlain;
|
OutputKind outputKind = okPlain;
|
||||||
bool xmlOutputSourceLocation = true;
|
bool xmlOutputSourceLocation = true;
|
||||||
bool strict = false;
|
bool strict = false;
|
||||||
|
bool replaceEvalErrors = false;
|
||||||
Strings attrPaths;
|
Strings attrPaths;
|
||||||
bool wantsReadWrite = false;
|
bool wantsReadWrite = false;
|
||||||
|
|
||||||
|
@ -147,6 +148,8 @@ static int main_nix_instantiate(int argc, char * * argv)
|
||||||
xmlOutputSourceLocation = false;
|
xmlOutputSourceLocation = false;
|
||||||
else if (*arg == "--strict")
|
else if (*arg == "--strict")
|
||||||
strict = true;
|
strict = true;
|
||||||
|
else if (*arg == "--replace-eval-errors")
|
||||||
|
replaceEvalErrors = true;
|
||||||
else if (*arg == "--dry-run")
|
else if (*arg == "--dry-run")
|
||||||
settings.readOnlyMode = true;
|
settings.readOnlyMode = true;
|
||||||
else if (*arg != "" && arg->at(0) == '-')
|
else if (*arg != "" && arg->at(0) == '-')
|
||||||
|
@ -184,7 +187,7 @@ static int main_nix_instantiate(int argc, char * * argv)
|
||||||
|
|
||||||
if (readStdin) {
|
if (readStdin) {
|
||||||
Expr * e = state->parseStdin();
|
Expr * e = state->parseStdin();
|
||||||
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
|
processExpr(*state, attrPaths, parseOnly, strict, replaceEvalErrors, autoArgs,
|
||||||
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
||||||
} else if (files.empty() && !fromArgs)
|
} else if (files.empty() && !fromArgs)
|
||||||
files.push_back("./default.nix");
|
files.push_back("./default.nix");
|
||||||
|
@ -193,7 +196,7 @@ static int main_nix_instantiate(int argc, char * * argv)
|
||||||
Expr * e = fromArgs
|
Expr * e = fromArgs
|
||||||
? state->parseExprFromString(i, state->rootPath("."))
|
? state->parseExprFromString(i, state->rootPath("."))
|
||||||
: state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, i)));
|
: state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, i)));
|
||||||
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
|
processExpr(*state, attrPaths, parseOnly, strict, replaceEvalErrors, autoArgs,
|
||||||
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace nix::fs { using namespace std::filesystem; }
|
||||||
struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
|
struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
|
||||||
{
|
{
|
||||||
bool raw = false;
|
bool raw = false;
|
||||||
|
bool replaceEvalErrors = false;
|
||||||
std::optional<std::string> apply;
|
std::optional<std::string> apply;
|
||||||
std::optional<std::filesystem::path> writeTo;
|
std::optional<std::filesystem::path> writeTo;
|
||||||
|
|
||||||
|
@ -39,6 +40,12 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
|
||||||
.labels = {"path"},
|
.labels = {"path"},
|
||||||
.handler = {&writeTo},
|
.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
|
std::string description() override
|
||||||
|
@ -118,7 +125,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (json) {
|
else if (json) {
|
||||||
printJSON(printValueAsJSON(*state, true, *v, pos, context, false));
|
printJSON(printValueAsJSON(*state, true, replaceEvalErrors, *v, pos, context, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -48,6 +48,13 @@ R""(
|
||||||
# cat ./out/subdir/bla
|
# cat ./out/subdir/bla
|
||||||
123
|
123
|
||||||
|
|
||||||
|
* Replace evaluation errors:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix eval --json --replace-eval-errors --expr '{ a = throw "fail"; }'
|
||||||
|
{"a":"«evaluation error»"}
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command evaluates the given Nix expression, and prints the result on standard output.
|
This command evaluates the given Nix expression, and prints the result on standard output.
|
||||||
|
|
|
@ -35,6 +35,23 @@ nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
||||||
[[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]]
|
[[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]]
|
||||||
[[ "$(nix-instantiate --eval -E '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]]
|
[[ "$(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.
|
# Check that symlink cycles don't cause a hang.
|
||||||
ln -sfn cycle.nix "$TEST_ROOT/cycle.nix"
|
ln -sfn cycle.nix "$TEST_ROOT/cycle.nix"
|
||||||
(! nix eval --file "$TEST_ROOT/cycle.nix")
|
(! nix eval --file "$TEST_ROOT/cycle.nix")
|
||||||
|
|
11
tests/functional/replace-eval-errors.nix
Normal file
11
tests/functional/replace-eval-errors.nix
Normal 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";
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue