-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[IMP] evaluation: functions can handle errors #2932
Conversation
350c0b7
to
818db7c
Compare
818db7c
to
0a65786
Compare
a0b0713
to
8b2b53f
Compare
8b2b53f
to
c49698f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small problems and nitpicking.
Really nice work testing every function that may or may not accept error, I certenly wouldn't have bothered testing everything 😛
src/functions/index.ts
Outdated
@@ -74,7 +75,7 @@ class FunctionRegistry extends Registry<FunctionDescription> { | |||
} | |||
const descr = addMetaInfoFromArg(addDescr); | |||
validateArguments(descr.args); | |||
this.mapping[name] = createComputeFunctionFromDescription(descr); | |||
this.mapping[name] = addErrorHandling(createComputeFunctionFromDescription(descr), name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might as well fo the try/catch directly in createComputeFunctionFromDescription
to avoid creating a second function wrapping the first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, to avoid that, we can put the tryCatch directly in buildComputeFunctionFromDescription
. @LucasLefevre are you agree with that ?
src/helpers/cells/cell_evaluation.ts
Outdated
return evaluateCell; | ||
} | ||
function _createEvaluatedCell(value: CellValue, localeFormat: LocaleFormat): EvaluatedCell { | ||
if (value instanceof EvaluationError) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm didn't really liked the builders so I don't really care, but what's the rationale to remove them and to use simple if
? Because that's more readable, or is there a practical reason ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it easier to read, less code to have in mind.
Actually, if it really bothers you, I'll go back.
61a747d
to
c42f2f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice !
I think we finally found a good way ! Good job :)
Some general comment (more detailed in individual comments)
- if we find a more meaningful name for
UnitData
, it would be nice - throwing objects (and not Errors) makes me feel 🤨 but 🤷
- I think there's some useless changes that could be reverted.
- Now that the functional side is working with this PR, a next step is to improve performance by using returns as much as possible (especially for
ODOO.*
functions)
src/types/misc.ts
Outdated
@@ -181,13 +179,11 @@ export interface RangeCompiledFormula extends Omit<CompiledFormula, "dependencie | |||
} | |||
|
|||
export type Matrix<T = unknown> = T[][]; | |||
export type ValueAndFormat = { value: CellValue; format?: Format }; | |||
export type UnitData = { value: CellValue; format?: Format; message?: string }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UnitData
doesn't really makes sense to me. But I don't have any brilliant better idea
CellEvaluation
? CellEvaluationResult
? CellFunctionResult
? EvaluationResult
? FunctionResult
? FormulaResult
? FormulaValue
? FormulaPayload
(FP
for short) ? FormulaData
? FData
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunctionResult
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FPayload
@@ -210,17 +216,21 @@ export class Evaluator { | |||
} | |||
|
|||
const cellId = cell.id; | |||
|
|||
const localeFormat = { format: cell.format, locale: this.getters.getLocale() }; | |||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A next step would be to change every throw in this file to returns.
@@ -228,9 +226,11 @@ export class EvaluationDataValidationPlugin extends UIPlugin { | |||
); | |||
|
|||
const evaluated = this.getters.evaluateFormula(sheetId, translatedFormula); | |||
return evaluated && !isMatrix(evaluated) ? evaluated.toString() : ""; | |||
return evaluated?.toString() || ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if it's a matrix? Is it safe to remove the check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what we want exactly:
Here I removed the check because the try catch below takes care of this case.
The evaluateFormula
getter has been modified to throw when an array is received.
Perhaps we should first ask ourselves what exactly we want to do with this getters when it returns an array ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok understood. After live discussion: this.getters.evaluateFormula
shouldn't throw
src/plugins/ui_core_views/cell_evaluation/compilation_parameters.ts
Outdated
Show resolved
Hide resolved
80bce50
to
6cb8d40
Compare
bbae191
to
416e207
Compare
5de6280
to
b9c4ec4
Compare
src/formulas/compiler.ts
Outdated
@@ -64,10 +64,10 @@ export function compileTokens(tokens: Token[]): CompiledFormula { | |||
const scope = new Scope(); | |||
|
|||
if (ast.type === "BIN_OPERATION" && ast.value === ":") { | |||
throw new BadExpressionError(_t("Invalid formula")); | |||
throw { value: CellErrorType.BadExpression, message: _t("Invalid formula") }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personnally I prefere the old way to throw/return error. Why did you change this ?. Maybe we can keep something like a function BadExpressionError(message: string)
so that we can have return BadExpressionError(_t("Invalid formula"));
. Same for the other type of error ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the goal would be that the formulas end up returning errors in the form of the normal payload rather than throwing errors, (performance reason)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must admit I agree with Anthony. It feels very here where we are not in the context of a function implementation that would return a value (or an error).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BadExpressionError(message)
and other errors could indeed be functions that result in {value: "#SomeError", message}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or keep BadExpressionError
as a class extending Error
but with an additional property value
.
That way it would:
- still be an error (and it feels natural to throw it)
- respect the same interface
{ value, message }
Basically the same as now, but errorType
becomes value
robodoo rebase-merge r+ |
Branch |
f2eaf85
to
4b3e29c
Compare
et maintenant ? |
Merge method set to rebase and merge, using the PR as merge commit message. |
1 similar comment
Merge method set to rebase and merge, using the PR as merge commit message. |
I'm sorry, @LucasLefevre: this PR is already reviewed, reviewing it again is useless. |
@laa-odoo @LucasLefevre I didn't know about this PR and had to retrieve its information, you may have to re-approve it as I didn't see previous commands. |
we would like in a future commit change spreadsheet error object by string errors and keep the error message at the level of the formula payload, if we want to do this, we will encounter a problem with the 'compute' functions of the formulas because the compute functions only manage the value (and not the entire payload), so we will not be able to transcribe the errors with their message. To resolve this, this commit unifies all the computes ('compute', 'computeFormat', computeValueAndFormat') into a same 'compute' which manages the entire payload. To avoid too many changes, it is now the casting helpers (toNumber, toString...) which take the payload and extract the value. Task: 2969401
This commit changes the form of user errors in the formula evaluation process. With the previous commits, having a user error is no longer an event that destroys the process of evaluating a formula: A cell with a user error is a cell that can functionally have an interest in changing the result of a formula in one direction rather than another. Thus, it is possible for each sub-formula of the same formula to return a payload corresponding to an error. Before this commit, an error payload corresponds to an error object stored on the "value" attribute of the formula payload {value: errorObject, format?: string} Now, the important error information is stored at the payload level. {value: string, format?: string, message?: string} There are two advantages to doing this: - In terms of performance, it can be expensive to create an error object and throw it. This commit gives the possibility for future commits to no longer throw an error object but to simply return an error payload. - the form of the formula payload is close to evaluatedCelle --> code simplification Task: 2969401
Jest doesn't fail if a console.error or console.warn is triggered. With this commit, any such message will make the test to fail Task: 2969401
4b3e29c
to
da02f26
Compare
robodoo r+ |
- refactor the code used to create the evaluated cells - removes certain parameters not used when handling errors during evaluation Task: 2969401 Part-of: #2932
The purpose of this commit is to allow the formulas to return errors as results. Functionally we want formulas like TRANSPOSE to be able to transpose an error without invalidating the entire evaluation process. We also want formulas like LOOKUP not to be interrupted when the search range has one or more errors. To make this change we accepted errors in input and output on the formulas, It nevertheless remains possible to throw errors in the formulas: it is no longer the evaluation process at the cell level which is stopped but the evaluation process at the formula level. Technically there is no longer a "try catch" at the evaluator level, here each formula is encapsulated in a “try catch” when it enters the formula register Other point, the _lastFnCalled present in the evaluation context and allowing to know which is the last formula to have been called before the raise of an error is no longer used. The information is available during the encapsulation of the formula in the "try catch". So, _lastFnCalled has therefore been removed from the evaluation context. Task: 2969401 Part-of: #2932 Co-authored-by: Lucas Lefèvre (lul) <lul@odoo.com>
Before the previous commit, the lazy type arguments made it possible to not stop the entire evaluation process when an error was provided as an argument. Here, with the previous commit, the evaluation process is no longer stopped when an error is passed as formula input, So we no longer need lazy type arguments, This commit remove it. Task: 2969401 Part-of: #2932
Allow specific function to accept/throw errors when they are present in arguments Task: 2969401 Part-of: #2932
with the idea of extending ValueAndFormat to something more complex that can handle errors and their messages, we are renaming it to FPayload Task: 2969401 Part-of: #2932
we would like in a future commit change spreadsheet error object by string errors and keep the error message at the level of the formula payload, if we want to do this, we will encounter a problem with the 'compute' functions of the formulas because the compute functions only manage the value (and not the entire payload), so we will not be able to transcribe the errors with their message. To resolve this, this commit unifies all the computes ('compute', 'computeFormat', computeValueAndFormat') into a same 'compute' which manages the entire payload. To avoid too many changes, it is now the casting helpers (toNumber, toString...) which take the payload and extract the value. Task: 2969401 Part-of: #2932
This commit changes the form of user errors in the formula evaluation process. With the previous commits, having a user error is no longer an event that destroys the process of evaluating a formula: A cell with a user error is a cell that can functionally have an interest in changing the result of a formula in one direction rather than another. Thus, it is possible for each sub-formula of the same formula to return a payload corresponding to an error. Before this commit, an error payload corresponds to an error object stored on the "value" attribute of the formula payload {value: errorObject, format?: string} Now, the important error information is stored at the payload level. {value: string, format?: string, message?: string} There are two advantages to doing this: - In terms of performance, it can be expensive to create an error object and throw it. This commit gives the possibility for future commits to no longer throw an error object but to simply return an error payload. - the form of the formula payload is close to evaluatedCelle --> code simplification Task: 2969401 Part-of: #2932
Jest doesn't fail if a console.error or console.warn is triggered. With this commit, any such message will make the test to fail Task: 2969401 Part-of: #2932
With those commits, functions can handle errors more easily. See individual commits for details. The purpose of this commit is to allow the formulas to return errors as results. Functionally we want formulas like TRANSPOSE to be able to transpose an error without invalidating the entire evaluation process. We also want formulas like LOOKUP not to be interrupted when the search range has one or more errors. closes #2932 Task: 2969401 Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com>
With those commits, functions can handle errors more easily. See individual commits for details. The purpose of this commit is to allow the formulas to return errors as results. Functionally we want formulas like TRANSPOSE to be able to transpose an error without invalidating the entire evaluation process. We also want formulas like LOOKUP not to be interrupted when the search range has one or more errors. closes #3454 Task: 2969401 Forward-port-of: #2932 Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com>
With those commits, functions can handle errors more easily. See individual commits for details.
The purpose of this commit is to allow the formulas
to return errors as results.
Functionally we want formulas like TRANSPOSE to be
able to transpose an error without invalidating the
entire evaluation process.
We also want formulas like LOOKUP not to be interrupted
when the search range has one or more errors.
Task: 2969401