diff --git a/cmd/flux/cmd/test.go b/cmd/flux/cmd/test.go index 5042cc7232..0144568a7f 100644 --- a/cmd/flux/cmd/test.go +++ b/cmd/flux/cmd/test.go @@ -144,8 +144,12 @@ func runFluxTests(out io.Writer, setup TestSetupFunc, flags TestFlags) (bool, er return runner.Finish(), nil } +var defaultCmdFeatureFlags = executetest.TestFlagger{ + "prettyError": true, +} + func WithFeatureFlags(ctx context.Context, features string) (context.Context, error) { - flagger := executetest.TestFlagger{} + flagger := defaultCmdFeatureFlags if len(features) != 0 { if err := json.Unmarshal([]byte(features), &flagger); err != nil { return nil, errors.Newf(codes.Invalid, "Unable to unmarshal features as json: %s", err) diff --git a/cmd/flux/fmt.go b/cmd/flux/fmt.go index a2bf3984fe..703350773b 100644 --- a/cmd/flux/fmt.go +++ b/cmd/flux/fmt.go @@ -1,11 +1,13 @@ package main import ( + "context" "errors" "fmt" "os" "path/filepath" + fluxcmd "github.com/influxdata/flux/cmd/flux/cmd" "github.com/influxdata/flux/libflux/go/libflux" "github.com/spf13/cobra" ) @@ -16,9 +18,17 @@ var fmtFlags struct { } func formatFile(cmd *cobra.Command, args []string) error { + + ctx := context.Background() + + ctx, err := fluxcmd.WithFeatureFlags(ctx, flags.Features) + if err != nil { + return err + } + script := args[0] var bad []string - err := filepath.Walk(script, + err = filepath.Walk(script, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -26,7 +36,7 @@ func formatFile(cmd *cobra.Command, args []string) error { if info.IsDir() || filepath.Ext(info.Name()) != ".flux" { return nil } - ok, err := format(path) + ok, err := format(ctx, path) if err != nil { return err } @@ -50,7 +60,7 @@ func formatFile(cmd *cobra.Command, args []string) error { return nil } -func format(script string) (bool, error) { +func format(ctx context.Context, script string) (bool, error) { fromFile, err := os.ReadFile(script) if err != nil { return false, err @@ -58,7 +68,7 @@ func format(script string) (bool, error) { curFileStr := string(fromFile) ast := libflux.ParseString(curFileStr) defer ast.Free() - if err := ast.GetError(); err != nil { + if err := ast.GetError(libflux.NewOptions(ctx)); err != nil { return false, fmt.Errorf("parse error: %s, %s", script, err) } diff --git a/internal/feature/flags.go b/internal/feature/flags.go index c4c862f74a..81b956c94a 100644 --- a/internal/feature/flags.go +++ b/internal/feature/flags.go @@ -209,6 +209,18 @@ func Strictnulllogicalops() BoolFlag { return strictNullLogicalOps } +var prettyError = feature.MakeBoolFlag( + "Pretty Error", + "prettyError", + "Markus Westerlind", + false, +) + +// PrettyError - Enables formatting with codespan for errors +func PrettyError() BoolFlag { + return prettyError +} + // Inject will inject the Flagger into the context. func Inject(ctx context.Context, flagger Flagger) context.Context { return feature.Inject(ctx, flagger) @@ -231,6 +243,7 @@ var all = []Flag{ vectorizedFloat, vectorizedUnaryOps, strictNullLogicalOps, + prettyError, } var byKey = map[string]Flag{ @@ -250,6 +263,7 @@ var byKey = map[string]Flag{ "vectorizedFloat": vectorizedFloat, "vectorizedUnaryOps": vectorizedUnaryOps, "strictNullLogicalOps": strictNullLogicalOps, + "prettyError": prettyError, } // Flags returns all feature flags. diff --git a/internal/feature/flags.yml b/internal/feature/flags.yml index dd56fd36c4..1facb20920 100644 --- a/internal/feature/flags.yml +++ b/internal/feature/flags.yml @@ -105,3 +105,9 @@ key: strictNullLogicalOps default: false contact: Owen Nelson + +- name: Pretty Error + description: Enables formatting with codespan for errors + key: prettyError + default: false + contact: Markus Westerlind diff --git a/internal/spec/build.go b/internal/spec/build.go index af44ce0302..c06dbb3bd6 100644 --- a/internal/spec/build.go +++ b/internal/spec/build.go @@ -195,7 +195,7 @@ func FromTableObject(ctx context.Context, to *flux.TableObject, now time.Time) ( // This function is used in tests that compare flux.Specs (e.g. in planner tests). func FromScript(ctx context.Context, runtime flux.Runtime, now time.Time, script string) (*operation.Spec, error) { s, _ := opentracing.StartSpanFromContext(ctx, "parse") - astPkg, err := runtime.Parse(script) + astPkg, err := runtime.Parse(ctx, script) if err != nil { return nil, err } diff --git a/lang/compiler.go b/lang/compiler.go index 87f2b6905c..2b4c0192fa 100644 --- a/lang/compiler.go +++ b/lang/compiler.go @@ -15,6 +15,7 @@ import ( "github.com/influxdata/flux/internal/operation" "github.com/influxdata/flux/internal/spec" "github.com/influxdata/flux/interpreter" + "github.com/influxdata/flux/libflux/go/libflux" "github.com/influxdata/flux/memory" "github.com/influxdata/flux/metadata" "github.com/influxdata/flux/plan" @@ -92,8 +93,8 @@ func applyOptions(opts ...CompileOption) *compileOptions { // Compile evaluates a Flux script producing a flux.Program. // now parameter must be non-zero, that is the default now time should be set before compiling. -func Compile(q string, runtime flux.Runtime, now time.Time, opts ...CompileOption) (*AstProgram, error) { - astPkg, err := runtime.Parse(q) +func Compile(ctx context.Context, q string, runtime flux.Runtime, now time.Time, opts ...CompileOption) (*AstProgram, error) { + astPkg, err := runtime.Parse(ctx, q) if err != nil { return nil, err } @@ -183,9 +184,9 @@ func (c FluxCompiler) Compile(ctx context.Context, runtime flux.Runtime) (flux.P if err != nil { return nil, errors.Wrap(err, codes.Inherit, "extern json parse error") } - return Compile(query, runtime, c.Now, WithExtern(hdl)) + return Compile(ctx, query, runtime, c.Now, WithExtern(hdl)) } - return Compile(query, runtime, c.Now) + return Compile(ctx, query, runtime, c.Now) } func (c FluxCompiler) CompilerType() flux.CompilerType { @@ -208,7 +209,7 @@ func (c ASTCompiler) Compile(ctx context.Context, runtime flux.Runtime) (flux.Pr if err != nil { return nil, err } - if err := hdl.GetError(); err != nil { + if err := hdl.GetError(libflux.NewOptions(ctx)); err != nil { return nil, err } diff --git a/lang/compiler_test.go b/lang/compiler_test.go index d3b4ebb8ac..5b11a23a44 100644 --- a/lang/compiler_test.go +++ b/lang/compiler_test.go @@ -209,15 +209,15 @@ func TestFluxCompiler(t *testing.T) { } func TestCompilationError(t *testing.T) { - program, err := lang.Compile(`illegal query`, runtime.Default, time.Unix(0, 0)) + ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) + defer deps.Finish() + + program, err := lang.Compile(ctx, `illegal query`, runtime.Default, time.Unix(0, 0)) if err != nil { // This shouldn't happen, has the script should be evaluated at program Start. t.Fatal(err) } - ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) - defer deps.Finish() - _, err = program.Start(ctx, &memory.ResourceAllocator{}) if err == nil { t.Fatal("compilation error expected, got none") @@ -346,10 +346,13 @@ csv.from(csv: "foo,bar") |> range(start: 2017-10-10T00:00:00Z) rt := runtime.Default for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { + ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) + defer deps.Finish() + var c lang.ASTCompiler { if tc.script != "" { - astPkg, err := rt.Parse(tc.script) + astPkg, err := rt.Parse(ctx, tc.script) if err != nil { t.Fatalf("failed to parse script: %v", err) } @@ -407,8 +410,6 @@ csv.from(csv: "foo,bar") |> range(start: 2017-10-10T00:00:00Z) if err != nil { t.Fatalf("failed to compile AST: %v", err) } - ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) - defer deps.Finish() // we need to start the program to get compile errors derived from AST evaluation if _, err := program.Start(ctx, &memory.ResourceAllocator{}); err != nil { @@ -487,6 +488,10 @@ csv.from(csv: " } func TestCompileOptions(t *testing.T) { + // start program in order to evaluate planner options + ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) + defer deps.Finish() + src := `import "csv" csv.from(csv: "foo,bar") |> range(start: 2017-10-10T00:00:00Z) @@ -496,15 +501,11 @@ func TestCompileOptions(t *testing.T) { opt := lang.WithLogPlanOpts(plan.OnlyLogicalRules(removeCount{})) - program, err := lang.Compile(src, runtime.Default, now, opt) + program, err := lang.Compile(ctx, src, runtime.Default, now, opt) if err != nil { t.Fatalf("failed to compile script: %v", err) } - // start program in order to evaluate planner options - ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) - defer deps.Finish() - if _, err := program.Start(ctx, &memory.ResourceAllocator{}); err != nil { t.Fatalf("failed to start program: %v", err) } @@ -783,14 +784,18 @@ option planner.disableLogicalRules = ["removeCountRule"]`}, if len(tc.files) == 0 { t.Fatal("the test should have at least one file") } - astPkg, err := runtime.Parse(tc.files[0]) + + ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) + defer deps.Finish() + + astPkg, err := runtime.Parse(ctx, tc.files[0]) if err != nil { t.Fatal(err) } if len(tc.files) > 1 { for _, file := range tc.files[1:] { - otherPkg, err := runtime.Parse(file) + otherPkg, err := runtime.Parse(ctx, file) if err != nil { t.Fatal(err) } @@ -801,8 +806,6 @@ option planner.disableLogicalRules = ["removeCountRule"]`}, } program := lang.CompileAST(astPkg, runtime.Default, nowFn()) - ctx, deps := dependency.Inject(context.Background(), executetest.NewTestExecuteDependencies()) - defer deps.Finish() if q, err := program.Start(ctx, &memory.ResourceAllocator{}); err != nil { if tc.wantErr == "" { diff --git a/lang/execopts_test.go b/lang/execopts_test.go index 8470666210..7c4774c502 100644 --- a/lang/execopts_test.go +++ b/lang/execopts_test.go @@ -19,7 +19,12 @@ func TestExecutionOptions(t *testing.T) { option now = () => ( 2020-10-15T00:00:00Z ) ` - h, err := parser.ParseToHandle([]byte(src)) + // Prepare a context with execution dependencies. + ctx := context.TODO() + deps := execute.DefaultExecutionDependencies() + ctx = deps.Inject(ctx) + + h, err := parser.ParseToHandle(ctx, []byte(src)) if err != nil { t.Fatalf("failed to parse test case: %v", err) } @@ -39,11 +44,6 @@ func TestExecutionOptions(t *testing.T) { pkg.Range(scope.Set) } - // Prepare a context with execution dependencies. - ctx := context.TODO() - deps := execute.DefaultExecutionDependencies() - ctx = deps.Inject(ctx) - // Pass lang.ExecutionOptions as the options configurator. It is // responsible for installing the configured options into the execution // dependencies. The goal of this test is to verify the option diff --git a/lang/query_test.go b/lang/query_test.go index 7eaaff303e..848b26425a 100644 --- a/lang/query_test.go +++ b/lang/query_test.go @@ -19,11 +19,12 @@ import ( ) func runQuery(ctx context.Context, script string) (flux.Query, func(), error) { - program, err := lang.Compile(script, runtime.Default, time.Unix(0, 0)) + ctx, deps := dependency.Inject(ctx, executetest.NewTestExecuteDependencies()) + + program, err := lang.Compile(ctx, script, runtime.Default, time.Unix(0, 0)) if err != nil { return nil, nil, err } - ctx, deps := dependency.Inject(ctx, executetest.NewTestExecuteDependencies()) q, err := program.Start(ctx, memory.DefaultAllocator) if err != nil { deps.Finish() diff --git a/libflux/c/main.c b/libflux/c/main.c index e9ce1ff5ea..2a1fd66412 100644 --- a/libflux/c/main.c +++ b/libflux/c/main.c @@ -25,7 +25,7 @@ void test_ast() { struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx = 1 + 1"); assert(ast_pkg_foo != NULL); - struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo); + struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo, ""); assert(err == NULL); printf("Marshaling to JSON\n"); @@ -43,7 +43,7 @@ void test_ast() { struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "x = 1 + / 1"); assert(ast_pkg_foo != NULL); - struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo); + struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo, ""); assert(err != NULL); const char* err_str = flux_error_str(err); printf(" error: %s\n", err_str); @@ -56,7 +56,7 @@ void test_ast() { struct flux_ast_pkg_t *ast_pkg_foo = flux_parse("test", "package foo\nx=1+1"); assert(ast_pkg_foo != NULL); - struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo); + struct flux_error_t* err = flux_ast_get_error(ast_pkg_foo, ""); assert(err == NULL); struct flux_buffer_t buf; diff --git a/libflux/flux-core/src/ast/check/mod.rs b/libflux/flux-core/src/ast/check/mod.rs index a4bbaf65d2..2cdaed63b4 100644 --- a/libflux/flux-core/src/ast/check/mod.rs +++ b/libflux/flux-core/src/ast/check/mod.rs @@ -10,7 +10,11 @@ use crate::{ /// Inspects an AST node and returns a list of found AST errors plus /// any errors existed before `ast.check()` is performed. -pub fn check(node: walk::Node) -> Result<(), Errors> { +pub fn check<'a>(node: impl Into>) -> Result<(), Errors> { + check_(node.into()) +} + +fn check_(node: walk::Node) -> Result<(), Errors> { const MAX_DEPTH: u32 = 180; #[derive(Default)] diff --git a/libflux/flux-core/src/semantic/mod.rs b/libflux/flux-core/src/semantic/mod.rs index 287319016f..c4065ebad2 100644 --- a/libflux/flux-core/src/semantic/mod.rs +++ b/libflux/flux-core/src/semantic/mod.rs @@ -333,19 +333,6 @@ fn build_record( (r, cons) } -/// Wrapper around `FileErrors` which defaults to using codespan to print the errors -#[derive(Error, Debug, PartialEq)] -pub struct PrettyFileErrors(pub FileErrors); - -impl fmt::Display for PrettyFileErrors { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.0.source { - Some(source) => f.write_str(&self.0.pretty(source)), - None => self.0.fmt(f), - } - } -} - /// Error represents any any error that can occur during any step of the type analysis process. #[derive(Error, Debug, PartialEq)] pub struct FileErrors { @@ -358,11 +345,17 @@ pub struct FileErrors { #[source] /// The collection of diagnostics pub diagnostics: Diagnostics, + + /// Whether to use codespan to display diagnostics + pub pretty_fmt: bool, } impl fmt::Display for FileErrors { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.diagnostics.fmt(f) + match &self.source { + Some(source) if self.pretty_fmt => f.write_str(&self.pretty(source)), + _ => self.diagnostics.fmt(f), + } } } @@ -382,7 +375,6 @@ where W: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO Use codespan's formatting for errors self.errors.fmt(f) } } @@ -419,8 +411,9 @@ impl FileErrors { /// Wraps `FileErrors` in type which defaults to the more readable codespan error /// representation - pub fn pretty_error(self) -> PrettyFileErrors { - PrettyFileErrors(self) + pub fn pretty_error(mut self) -> Self { + self.pretty_fmt = true; + self } /// Prints the errors in their short form @@ -522,6 +515,9 @@ pub enum Feature { /// Enables warnings for unused symbols UnusedSymbolWarnings, + /// Enables formatting with codespan for errors + PrettyError, + /// Enables calls to map to be vectorized when the function contains select /// literal values. VectorizedConst, @@ -585,7 +581,9 @@ impl<'env, I: import::Importer> Analyzer<'env, I> { files: vec![ast_file], }; self.analyze_ast(&ast_pkg).map_err(|mut err| { - err.error.source = Some(src.into()); + if err.error.source.is_none() { + err.error.source = Some(src.into()); + } err }) } @@ -619,6 +617,7 @@ impl<'env, I: import::Importer> Analyzer<'env, I> { errors, warnings: Errors::new(), }, + pretty_fmt: self.config.features.contains(&Feature::PrettyError), }, value: None, }); @@ -676,8 +675,9 @@ impl<'env, I: import::Importer> Analyzer<'env, I> { return Err(Salvage { error: FileErrors { file: sem_pkg.package.clone(), - source: None, + source: ast_pkg.files[0].base.location.source.clone(), diagnostics: Diagnostics { errors, warnings }, + pretty_fmt: self.config.features.contains(&Feature::PrettyError), }, value: Some((env, sem_pkg)), }); diff --git a/libflux/flux/src/cffi.rs b/libflux/flux/src/cffi.rs index df881f422b..aa57611cb4 100644 --- a/libflux/flux/src/cffi.rs +++ b/libflux/flux/src/cffi.rs @@ -166,11 +166,28 @@ pub extern "C" fn flux_ast_format( #[no_mangle] pub unsafe extern "C" fn flux_ast_get_error( ast_pkg: *const ast::Package, + options: *const c_char, ) -> Option> { catch_unwind(|| { - let ast_pkg = ast::walk::Node::Package(&*ast_pkg); + let options = match Options::from_c_str(options) { + Ok(x) => x, + Err(err) => return Some(err.into()), + }; + + let ast_pkg = &*ast_pkg; match ast::check::check(ast_pkg) { - Err(e) => Some(Error::from(anyhow::Error::from(e)).into()), + Err(e) => Some( + Error::from(anyhow::Error::from(semantic::FileErrors { + file: ast_pkg.package.clone(), + source: ast_pkg.files[0].base.location.source.clone(), + diagnostics: semantic::Diagnostics { + errors: e.into_iter().map(From::from).collect::>().into(), + warnings: Default::default(), + }, + pretty_fmt: options.features.contains(&Feature::PrettyError), + })) + .into(), + ), Ok(_) => None, } }) @@ -905,7 +922,10 @@ from(bucket: v.bucket) let ast = crate::parser::parse_string("test".to_string(), "x = 3 + / 10 - \""); let ast = Box::new(ast.into()); // Safety: `ast` is a valid pointer - let errh = unsafe { flux_ast_get_error(&*ast) }; + let errh = unsafe { + let options = CString::new("").unwrap(); + flux_ast_get_error(&*ast, options.as_ptr()) + }; expect_test::expect![[r#" error test@1:9-1:10: invalid expression: invalid token for primary expression: DIV diff --git a/libflux/go/libflux/analyze.go b/libflux/go/libflux/analyze.go index 522459872e..b743b5e812 100644 --- a/libflux/go/libflux/analyze.go +++ b/libflux/go/libflux/analyze.go @@ -52,6 +52,7 @@ type Options struct { func NewOptions(ctx context.Context) Options { var features []string + features = addFlag(ctx, features, feature.PrettyError()) features = addFlag(ctx, features, feature.VectorizedConst()) features = addFlag(ctx, features, feature.VectorizedConditionals()) features = addFlag(ctx, features, feature.VectorizedFloat()) @@ -212,11 +213,6 @@ type Analyzer struct { ptr *C.struct_flux_stateful_analyzer_t } -func NewAnalyzer() *Analyzer { - analyzer, _ := NewAnalyzerWithOptions(Options{}) - return analyzer -} - func NewAnalyzerWithOptions(options Options) (*Analyzer, error) { stringOptions, err := marshalOptions(options) if err != nil { diff --git a/libflux/go/libflux/buildinfo.gen.go b/libflux/go/libflux/buildinfo.gen.go index f255ac8b80..00fe7a1c12 100644 --- a/libflux/go/libflux/buildinfo.gen.go +++ b/libflux/go/libflux/buildinfo.gen.go @@ -16,7 +16,7 @@ var sourceHashes = map[string]string{ "libflux/Cargo.lock": "5f21e43655bf7ae60d4c59731ab1e5a74567fa9561c090555545cdaa0a09132d", "libflux/Cargo.toml": "91ac4e8b467440c6e8a9438011de0e7b78c2732403bb067d4dd31539ac8a90c1", "libflux/flux-core/Cargo.toml": "9c49e87c57b0027b7a0f525c55174eea2ddf391dadb7cb38e7f354b5b3b894b1", - "libflux/flux-core/src/ast/check/mod.rs": "14ceca38c9c57c2a3fd56a8eca23616128badcc79708238cef17788c729bd825", + "libflux/flux-core/src/ast/check/mod.rs": "4f5cf44c5fd55def344b4e2f637e4f95b835a1a30dd95e32fdf850c248e21cf1", "libflux/flux-core/src/ast/mod.rs": "53a22049c37450669757b7bdd1dca3a030df1fc1f0db44d303ce642452d5a33c", "libflux/flux-core/src/ast/walk/mod.rs": "76b93aa401766a680474141c280fab35af096efb0686f9560d54ffd66187b37d", "libflux/flux-core/src/bin/README.md": "c1245a4938c923d065647b4dc4f7e19486e85c93d868ef2f7f47ddff62ec81df", @@ -49,7 +49,7 @@ var sourceHashes = map[string]string{ "libflux/flux-core/src/semantic/fs.rs": "f7f609bc8149769d99b737150e184a2d54029c0b768365dbcf08ff193b0e1f6f", "libflux/flux-core/src/semantic/import.rs": "184e955211db1ceb1be782b4daf75584b86907b1428e50015497909cfc2dd89a", "libflux/flux-core/src/semantic/infer.rs": "df061911c19d80f00be7779b1b1ec899dbdcf49225c129606ebd2a16b48b6159", - "libflux/flux-core/src/semantic/mod.rs": "db29142aa82ae75d4b0ff30356d91ef13d1ee9d67b80052bd307da65aeb8061e", + "libflux/flux-core/src/semantic/mod.rs": "7ba96e5ca52c8571193384765d4cbaaaf9eb8bb9424d57bc26e88c9de52314c2", "libflux/flux-core/src/semantic/nodes.rs": "f795ddf2cc540a8f813ec2d507bda3199370c4bcc6b4a1cd07ce34055dd739b2", "libflux/flux-core/src/semantic/sub.rs": "8bc05ffff0990facea2130e0faf5a837f8663d59996ff85869c6e631ac654553", "libflux/flux-core/src/semantic/symbols.rs": "f061d57fe4ef7f23d0adad80d43fe1c8ae92d5e25a0da4b81e21b8087e30d253", @@ -62,13 +62,13 @@ var sourceHashes = map[string]string{ "libflux/flux/Cargo.toml": "18988dcca5d0f54bef4772d0e19a21bd656b25eb07e51b9fb52f50693a27dfc7", "libflux/flux/FLUXDOC.md": "92e6dd8043bd87b4924e09aa28fb5346630aee1214de28ea2c8fc0687cad0785", "libflux/flux/build.rs": "3faa8d9f04edceddf43f237e9c9b1c03f662af821ef33412691c1a6296fe66ff", - "libflux/flux/src/cffi.rs": "c0501fdd7ead45540c34604cc58a8620b15ebb9325b6e1ef1f409f6690deb7cf", + "libflux/flux/src/cffi.rs": "43b01a20ea77fb6b3869f81774dc187b45761ca4b3e5b4a14d1d597716c68a4a", "libflux/flux/src/lib.rs": "3cd7dfcf7491f5797d501a647ee92a3f66b5790f6df7ed2238f1969b4bd929ed", "libflux/flux/templates/base.html": "a818747b9621828bb96b94291c60922db54052bbe35d5e354f8e589d2a4ebd02", "libflux/flux/templates/home.html": "f9927514dd42ca7271b4817ad1ca33ec79c03a77a783581b4dcafabd246ebf3f", "libflux/flux/templates/package.html": "635e1d638ad1a430f3894ff0a458572ca3c4dc6b9cec2c57fe771443849e1402", "libflux/flux/templates/value.html": "2af699af2535f83635acbc75c92d2ee941c2335350fc7e82ceb2dccf479360bf", - "libflux/include/influxdata/flux.h": "a50070b13297a6427fc47488d4cfbf18d3c804a2c3cb6f7a6f918b0ff29715a8", + "libflux/include/influxdata/flux.h": "35f4bfcf252329f6f941c75096488d7b5a902f05a09a41db76649c3632d85fb7", "stdlib/array/array.flux": "d0737bb4ee1cea99eb82d1fd6150f5686edd58d1c36a676593e623933dbcc424", "stdlib/array/array_test.flux": "c4281ef1128ba6164c16f1dcb1b051d2231fe39617f23a070ae1942b5ddb70d5", "stdlib/array/from_test.flux": "688a3593d27aed7110d016c0b7634808dee533311d4349669a1104bc50a9f3e7", diff --git a/libflux/go/libflux/parser.go b/libflux/go/libflux/parser.go index 2c77b4b494..bf5f4dc4ad 100644 --- a/libflux/go/libflux/parser.go +++ b/libflux/go/libflux/parser.go @@ -48,8 +48,15 @@ func (p ASTPkg) Format() (string, error) { } // GetError will return the first error in the AST, if any -func (p ASTPkg) GetError() error { - if err := C.flux_ast_get_error(p.ptr); err != nil { +func (p ASTPkg) GetError(options Options) error { + stringOptions, err := marshalOptions(options) + if err != nil { + return err + } + cOptions := C.CString(stringOptions) + defer C.free(unsafe.Pointer(cOptions)) + + if err := C.flux_ast_get_error(p.ptr, cOptions); err != nil { defer C.flux_free_error(err) cstr := C.flux_error_str(err) str := C.GoString(cstr) diff --git a/libflux/go/libflux/parser_test.go b/libflux/go/libflux/parser_test.go index 9266a0cc93..cf98a21ec9 100644 --- a/libflux/go/libflux/parser_test.go +++ b/libflux/go/libflux/parser_test.go @@ -2,6 +2,7 @@ package libflux_test import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -21,7 +22,7 @@ from(bucket: "telegraf") |> mean() ` ast := libflux.ParseString(text) - if err := ast.GetError(); err != nil { + if err := ast.GetError(libflux.NewOptions(context.Background())); err != nil { t.Fatal(err) } @@ -62,7 +63,7 @@ func TestASTPkg_GetError(t *testing.T) { src := `x = 1 + / 3` ast := libflux.ParseString(src) defer ast.Free() - err := ast.GetError() + err := ast.GetError(libflux.NewOptions(context.Background())) if err == nil { t.Fatal("expected parse error, got none") } diff --git a/libflux/include/influxdata/flux.h b/libflux/include/influxdata/flux.h index 6b17c836d4..e98786495b 100644 --- a/libflux/include/influxdata/flux.h +++ b/libflux/include/influxdata/flux.h @@ -48,7 +48,7 @@ struct flux_ast_pkg_t *flux_parse(const char *file_name, const char *flux_source struct flux_error_t *flux_ast_format(struct flux_ast_pkg_t *, struct flux_buffer_t *); // flux_ast_get_error will return the first error in the AST, if any. -struct flux_error_t *flux_ast_get_error(struct flux_ast_pkg_t *); +struct flux_error_t *flux_ast_get_error(struct flux_ast_pkg_t *, const char* options); // flux_free_ast_pkg will release the memory associated with the given pointer. void flux_free_ast_pkg(struct flux_ast_pkg_t *); diff --git a/parser/parser.go b/parser/parser.go index b822418943..50ba97f133 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1,6 +1,7 @@ package parser import ( + "context" "os" "path/filepath" @@ -112,9 +113,9 @@ func HandleToJSON(hdl flux.ASTHandle) ([]byte, error) { return libfluxHdl.MarshalJSON() } -func ParseToHandle(src []byte) (*libflux.ASTPkg, error) { +func ParseToHandle(ctx context.Context, src []byte) (*libflux.ASTPkg, error) { pkg := libflux.ParseString(string(src)) - if err := pkg.GetError(); err != nil { + if err := pkg.GetError(libflux.NewOptions(ctx)); err != nil { return nil, err } return pkg, nil diff --git a/parser/parser_test.go b/parser/parser_test.go index 13863c8193..f102b1cf12 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1,6 +1,7 @@ package parser_test import ( + "context" "os" "path/filepath" "testing" @@ -187,7 +188,7 @@ a = 1 func TestHandleToJSON(t *testing.T) { src := `x = 0` - hdl, err := parser.ParseToHandle([]byte(src)) + hdl, err := parser.ParseToHandle(context.Background(), []byte(src)) if err != nil { t.Fatal(err) } diff --git a/runtime.go b/runtime.go index 93e1a98546..d6c3b6e1a0 100644 --- a/runtime.go +++ b/runtime.go @@ -5,6 +5,7 @@ import ( "time" "github.com/influxdata/flux/interpreter" + "github.com/influxdata/flux/libflux/go/libflux" "github.com/influxdata/flux/semantic" "github.com/influxdata/flux/values" ) @@ -12,7 +13,7 @@ import ( // Runtime encapsulates the operations supported by the flux runtime. type Runtime interface { // Parse parses a Flux script and produces a handle to an AST. - Parse(flux string) (ASTHandle, error) + Parse(ctx context.Context, flux string) (ASTHandle, error) // JSONToHandle takes JSON data and returns an AST handle. JSONToHandle(json []byte) (ASTHandle, error) @@ -44,7 +45,7 @@ type ASTHandle interface { // GetError will return the first error encountered when parsing Flux source code, // if any. - GetError() error + GetError(options libflux.Options) error } // ScopeMutator is any function that mutates the scope of an identifier. diff --git a/runtime/global.go b/runtime/global.go index bdb54ab746..230d7047dc 100644 --- a/runtime/global.go +++ b/runtime/global.go @@ -37,7 +37,7 @@ func Prelude() values.Scope { // Eval accepts a Flux script and evaluates it to produce a set of side effects (as a slice of values) and a scope. func Eval(ctx context.Context, flux string, opts ...flux.ScopeMutator) ([]interpreter.SideEffect, values.Scope, error) { - h, err := parser.ParseToHandle([]byte(flux)) + h, err := parser.ParseToHandle(ctx, []byte(flux)) if err != nil { return nil, nil, err } diff --git a/runtime/parse.go b/runtime/parse.go index 25c2545ef3..b71fd7d664 100644 --- a/runtime/parse.go +++ b/runtime/parse.go @@ -1,22 +1,24 @@ package runtime import ( + "context" + "github.com/influxdata/flux" "github.com/influxdata/flux/libflux/go/libflux" "github.com/influxdata/flux/parser" ) // Parse parses a Flux script and produces an ast.Package. -func Parse(flux string) (flux.ASTHandle, error) { - astPkg, err := parser.ParseToHandle([]byte(flux)) +func Parse(ctx context.Context, flux string) (flux.ASTHandle, error) { + astPkg, err := parser.ParseToHandle(ctx, []byte(flux)) if err != nil { return nil, err } return astPkg, nil } -func ParseToJSON(flux string) ([]byte, error) { - h, err := Parse(flux) +func ParseToJSON(ctx context.Context, flux string) ([]byte, error) { + h, err := Parse(ctx, flux) if err != nil { return nil, err } diff --git a/runtime/runtime.go b/runtime/runtime.go index 7c0bc09975..c017150356 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -24,8 +24,8 @@ type runtime struct { finalized bool } -func (r *runtime) Parse(flux string) (flux.ASTHandle, error) { - return Parse(flux) +func (r *runtime) Parse(ctx context.Context, flux string) (flux.ASTHandle, error) { + return Parse(ctx, flux) } func (r *runtime) JSONToHandle(json []byte) (flux.ASTHandle, error) { diff --git a/stdlib/contrib/bonitoo-io/alerta/alerta_test.go b/stdlib/contrib/bonitoo-io/alerta/alerta_test.go index 699247d3e1..0ffa1c95fb 100644 --- a/stdlib/contrib/bonitoo-io/alerta/alerta_test.go +++ b/stdlib/contrib/bonitoo-io/alerta/alerta_test.go @@ -159,12 +159,13 @@ endpoint = ` + tc.fn + `(mapFn: (r) => ({ csv.from(csv:data) |> endpoint()` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { t.Fatal(err) diff --git a/stdlib/contrib/bonitoo-io/servicenow/servicenow_test.go b/stdlib/contrib/bonitoo-io/servicenow/servicenow_test.go index cf039aefcb..b446202e4a 100644 --- a/stdlib/contrib/bonitoo-io/servicenow/servicenow_test.go +++ b/stdlib/contrib/bonitoo-io/servicenow/servicenow_test.go @@ -134,12 +134,12 @@ endpoint = servicenow.endpoint(url: url, username: username, password: password, csv.from(csv:data) |> endpoint()` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { t.Fatal(err) diff --git a/stdlib/contrib/bonitoo-io/victorops/victorops_test.go b/stdlib/contrib/bonitoo-io/victorops/victorops_test.go index be3e13b7f7..d7c00aa720 100644 --- a/stdlib/contrib/bonitoo-io/victorops/victorops_test.go +++ b/stdlib/contrib/bonitoo-io/victorops/victorops_test.go @@ -123,12 +123,13 @@ endpoint = victorops.endpoint(url: url` + tc.extraParams + `)(mapFn: (r) => ({ csv.from(csv:data) |> endpoint()` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { t.Fatal(err) diff --git a/stdlib/contrib/bonitoo-io/zenoss/zenoss_test.go b/stdlib/contrib/bonitoo-io/zenoss/zenoss_test.go index ccff117ee4..c225b5d3de 100644 --- a/stdlib/contrib/bonitoo-io/zenoss/zenoss_test.go +++ b/stdlib/contrib/bonitoo-io/zenoss/zenoss_test.go @@ -168,12 +168,12 @@ endpoint = ` + tc.fn + `(mapFn: (r) => ({ csv.from(csv:data) |> endpoint()` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { t.Fatal(err) diff --git a/stdlib/contrib/chobbs/discord/discord_test.go b/stdlib/contrib/chobbs/discord/discord_test.go index e5c3c1173e..d34a6b3bc3 100644 --- a/stdlib/contrib/chobbs/discord/discord_test.go +++ b/stdlib/contrib/chobbs/discord/discord_test.go @@ -139,15 +139,16 @@ data = " ,result,,qtext ,,,` + tc.content + `"` - extHdl, err := runtime.Default.Parse(extern) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/rhajek/bigpanda/bigpanda_test.go b/stdlib/contrib/rhajek/bigpanda/bigpanda_test.go index 5fae5e112a..1fb2c17f60 100644 --- a/stdlib/contrib/rhajek/bigpanda/bigpanda_test.go +++ b/stdlib/contrib/rhajek/bigpanda/bigpanda_test.go @@ -137,7 +137,7 @@ endpoint = bigpanda.endpoint(url: url, appKey: appKey, token: "token123")(mapFn: return {r with status: bigpanda.statusFromLevel(level: r.level)} }) -csv.from(csv:data) |> endpoint() +csv.from(csv:data) |> endpoint() ` extern := ` @@ -150,15 +150,16 @@ data = " ,result,,host,level,description,check ,,,` + strings.Join([]string{tc.host, tc.level, tc.description, tc.check}, ",") + `"` - extHdl, err := runtime.Default.Parse(extern) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/sranka/opsgenie/opsgenie_test.go b/stdlib/contrib/sranka/opsgenie/opsgenie_test.go index 51fa332cf4..e360980a5a 100644 --- a/stdlib/contrib/sranka/opsgenie/opsgenie_test.go +++ b/stdlib/contrib/sranka/opsgenie/opsgenie_test.go @@ -204,15 +204,15 @@ data = " ,result,,qmessage,qalias ,,,` + strings.Join([]string{tc.message, tc.alias}, ",") + `"` - extHdl, err := runtime.Default.Parse(extern) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/sranka/sensu/sensu_test.go b/stdlib/contrib/sranka/sensu/sensu_test.go index af5a7ddc2b..fc302e97d9 100644 --- a/stdlib/contrib/sranka/sensu/sensu_test.go +++ b/stdlib/contrib/sranka/sensu/sensu_test.go @@ -120,7 +120,7 @@ entityName = "` + tc.entityName + `" endpoint = sensu.endpoint(` + tc.endpointExtraArgs + `url:url,apiKey:apiKey)(mapFn: (r) => { return {checkName:r.qcheck,text:r.qtext, status:r.qstatus} }) - + data = " #datatype,string,string,string,string,long #group,false,false,false,false,false @@ -130,11 +130,11 @@ endpoint = sensu.endpoint(` + tc.endpointExtraArgs + `url:url,apiKey:apiKey)(map csv.from(csv:data) |> endpoint() ` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/sranka/teams/teams_test.go b/stdlib/contrib/sranka/teams/teams_test.go index 3176a89967..f63aaad27b 100644 --- a/stdlib/contrib/sranka/teams/teams_test.go +++ b/stdlib/contrib/sranka/teams/teams_test.go @@ -172,15 +172,16 @@ data = " ,result,,qtitle,qtext,qsummary ,,,` + strings.Join([]string{tc.title, tc.text, tc.summary}, ",") + `"` - extHdl, err := runtime.Default.Parse(extern) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/sranka/telegram/telegram_test.go b/stdlib/contrib/sranka/telegram/telegram_test.go index c3e2debcf6..5f2df47ef7 100644 --- a/stdlib/contrib/sranka/telegram/telegram_test.go +++ b/stdlib/contrib/sranka/telegram/telegram_test.go @@ -170,15 +170,15 @@ data = " ,result,,qchannel,qtext ,,,` + strings.Join([]string{tc.channel, tc.text}, ",") + `"` - extHdl, err := runtime.Default.Parse(extern) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { diff --git a/stdlib/contrib/sranka/webexteams/webexteams_test.go b/stdlib/contrib/sranka/webexteams/webexteams_test.go index 29131a0368..7ab4a4d14d 100644 --- a/stdlib/contrib/sranka/webexteams/webexteams_test.go +++ b/stdlib/contrib/sranka/webexteams/webexteams_test.go @@ -81,7 +81,7 @@ token = "` + token + `" endpoint = webexteams.endpoint(url:url,token:token)(mapFn: (r) => { return {roomId:r.roomId,text:r.text,markdown:r.markdown} }) - + data = " #datatype,string,string,string,string,string #group,false,false,false,false,false @@ -91,11 +91,12 @@ endpoint = webexteams.endpoint(url:url,token:token)(mapFn: (r) => { csv.from(csv:data) |> endpoint() ` - prog, err := lang.Compile(fluxString, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now()) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) fmt.Println("*** ", tc.name) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) diff --git a/stdlib/experimental/mqtt/to_test.go b/stdlib/experimental/mqtt/to_test.go index 658325be7b..287d05119f 100644 --- a/stdlib/experimental/mqtt/to_test.go +++ b/stdlib/experimental/mqtt/to_test.go @@ -120,11 +120,11 @@ func runScript(script string) error { } func runScriptWithPipeline(script string) error { - prog, err := lang.Compile(script, runtime.Default, time.Now()) + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + prog, err := lang.Compile(ctx, script, runtime.Default, time.Now()) if err != nil { return err } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { return err diff --git a/stdlib/pagerduty/pagerduty_test.go b/stdlib/pagerduty/pagerduty_test.go index 01b4a102ad..eb021291da 100644 --- a/stdlib/pagerduty/pagerduty_test.go +++ b/stdlib/pagerduty/pagerduty_test.go @@ -343,16 +343,18 @@ data = " tc.name, tc.otherGroupKey, "0"}, ",") + `"` - extHdl, err := rt.Parse(extern) + + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + extHdl, err := rt.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Error(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil { t.Fatal(err) diff --git a/stdlib/slack/slack_test.go b/stdlib/slack/slack_test.go index 11a12af76f..0aef7bd5a6 100644 --- a/stdlib/slack/slack_test.go +++ b/stdlib/slack/slack_test.go @@ -183,15 +183,17 @@ data = " #default,_result,,,, ,result,,qchannel,qtext,wcolor ,,,` + strings.Join([]string{tc.channel, tc.text, tc.color}, ",") + `"` - extHdl, err := runtime.Default.Parse(extern) + + ctx := flux.NewDefaultDependencies().Inject(context.Background()) + + extHdl, err := runtime.Default.Parse(ctx, extern) if err != nil { t.Fatal(err) } - prog, err := lang.Compile(fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) + prog, err := lang.Compile(ctx, fluxString, runtime.Default, time.Now(), lang.WithExtern(extHdl)) if err != nil { t.Fatal(err) } - ctx := flux.NewDefaultDependencies().Inject(context.Background()) query, err := prog.Start(ctx, &memory.ResourceAllocator{}) if err != nil {