mirror of
https://github.com/enso-org/enso.git
synced 2024-11-27 04:52:45 +03:00
Handle Nothing
values in Filter_Condition.to_predicate
(#8600)
- Fixes #8549 - Ensures that a `Type_Error` is thrown instead of a `No_Such_Method` error on type mismatches. - I think this is more readable.
This commit is contained in:
parent
d41d48e8a0
commit
b3de42eb23
@ -1,11 +1,13 @@
|
||||
import project.Any.Any
|
||||
import project.Data.Locale.Locale
|
||||
import project.Data.Numbers.Number
|
||||
import project.Data.Set.Set
|
||||
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
|
||||
import project.Data.Text.Regex.Regex
|
||||
import project.Data.Text.Text
|
||||
import project.Data.Vector.Vector
|
||||
import project.Error.Error
|
||||
import project.Errors.Common.Incomparable_Values
|
||||
import project.Errors.Illegal_Argument.Illegal_Argument
|
||||
import project.Function.Function
|
||||
import project.Meta
|
||||
@ -184,30 +186,39 @@ type Filter_Condition
|
||||
## Converts a `Filter_Condition` condition into a predicate taking an
|
||||
element and returning a value indicating whether the element should be
|
||||
accepted by the filter.
|
||||
|
||||
The predicate can handle `Nothing` values in all cases. However, the
|
||||
predicate will raise an error if the value is not of the expected type.
|
||||
to_predicate : (Any -> Boolean)
|
||||
to_predicate self = case self of
|
||||
Less value -> <value
|
||||
Equal_Or_Less value -> <=value
|
||||
# == does not need special handling for Nothing
|
||||
Equal value -> ==value
|
||||
Equal_Or_Greater value -> >=value
|
||||
Greater value -> >value
|
||||
Not_Equal value -> !=value
|
||||
Between lower upper -> elem ->
|
||||
Less value -> handle_nothing (<value)
|
||||
Equal_Or_Less value -> handle_nothing (<=value)
|
||||
Equal_Or_Greater value -> handle_nothing (>=value)
|
||||
Greater value -> handle_nothing (>value)
|
||||
Between lower upper -> handle_nothing <| elem->
|
||||
(lower <= elem) && (elem <= upper)
|
||||
Equal_Ignore_Case value locale -> elem-> elem.equals_ignore_case value locale
|
||||
Starts_With prefix case_sensitivity -> _.starts_with prefix case_sensitivity
|
||||
Ends_With suffix case_sensitivity -> _.ends_with suffix case_sensitivity
|
||||
Contains substring case_sensitivity -> _.contains substring case_sensitivity
|
||||
Not_Contains substring case_sensitivity -> v-> v.contains substring case_sensitivity . not
|
||||
Equal_Ignore_Case value locale ->
|
||||
handle_nothing <| txt-> (txt : Text).equals_ignore_case value locale
|
||||
Starts_With prefix case_sensitivity ->
|
||||
handle_nothing <| txt-> (txt : Text).starts_with prefix case_sensitivity
|
||||
Ends_With suffix case_sensitivity ->
|
||||
handle_nothing <| txt-> (txt : Text).ends_with suffix case_sensitivity
|
||||
Contains substring case_sensitivity ->
|
||||
handle_nothing <| txt-> (txt : Text).contains substring case_sensitivity
|
||||
Not_Contains substring case_sensitivity ->
|
||||
handle_nothing <| txt-> (txt : Text).contains substring case_sensitivity . not
|
||||
Is_Nothing -> elem -> case elem of
|
||||
Nothing -> True
|
||||
_ -> False
|
||||
Not_Nothing -> elem -> case elem of
|
||||
Nothing -> False
|
||||
_ -> True
|
||||
Is_Nan -> .is_nan
|
||||
Is_Infinite -> .is_infinite
|
||||
Is_Finite -> .is_finite
|
||||
Is_Nan -> handle_nothing x-> (x:Number).is_nan
|
||||
Is_Infinite -> handle_nothing x-> (x:Number).is_infinite
|
||||
Is_Finite -> handle_nothing x-> (x:Number).is_finite
|
||||
Is_True -> ==True
|
||||
Is_False -> ==False
|
||||
Is_Empty -> elem -> case elem of
|
||||
@ -220,16 +231,16 @@ type Filter_Condition
|
||||
_ -> True
|
||||
Like sql_pattern ->
|
||||
regex = sql_like_to_regex sql_pattern
|
||||
regex.matches
|
||||
handle_nothing <| regex.matches
|
||||
Not_Like sql_pattern ->
|
||||
regex = sql_like_to_regex sql_pattern
|
||||
elem -> regex.matches elem . not
|
||||
handle_nothing <| elem-> regex.matches elem . not
|
||||
Is_In values ->
|
||||
set = Set.from_vector values
|
||||
set.contains
|
||||
Not_In values ->
|
||||
set = Set.from_vector values
|
||||
elem -> set.contains elem . not
|
||||
elem-> set.contains elem . not
|
||||
|
||||
## PRIVATE
|
||||
Convert to a display representation of this Filter_Condition.
|
||||
@ -345,8 +356,23 @@ handle_constructor_missing_arguments function ~continuation =
|
||||
We rely on its text representation being of the form `Filter_Condition.Between[Filter_Condition.enso:41-343]`.
|
||||
_ : Meta.Primitive ->
|
||||
text = function.to_text
|
||||
text.starts_with "Filter_Condition." && text.contains "[Filter_Condition.enso:"
|
||||
prefix = "Filter_Condition."
|
||||
if (text.starts_with prefix && text.contains "[Filter_Condition.enso:") . not then False else
|
||||
## The additional check for capital letter is needed, because otherwise, we get false positives:
|
||||
`(Filter_Condition.Greater 1).to_predicate` evaluates to function whose text representation
|
||||
may start with `Filter_Condition.handle_nothing`. Such functions are not constructors.
|
||||
constructor_letter = text.get prefix.length ""
|
||||
constructor_letter >= "A" && constructor_letter <= "Z"
|
||||
|
||||
_ -> False
|
||||
if is_filter_condition_constructor.not then continuation else
|
||||
message = "Got a Filter_Condition constructor without all required arguments provided. Please provide the missing arguments."
|
||||
Error.throw (Illegal_Argument.Error message)
|
||||
|
||||
## PRIVATE
|
||||
Extends the provided predicate to handle `Nothing` values without error.
|
||||
The new predicate will return `False` for `Nothing`.
|
||||
handle_nothing : (Any -> Boolean) -> (Any -> Boolean)
|
||||
handle_nothing f = elem-> case elem of
|
||||
Nothing -> False
|
||||
_ -> f elem
|
||||
|
@ -372,7 +372,7 @@ type Date_Range
|
||||
> Example
|
||||
Checking that at least one date in the range is after 2020-10-01.
|
||||
|
||||
(Date.new 2020 10 01).up_to (Date.new 2020 10 31) . any (> (Date.new 2020 10 01))
|
||||
(Date.new 2020 10 01).up_to (Date.new 2020 10 31) . any (Filter_Condition.Greater (Date.new 2020 10 01))
|
||||
@condition date_range_default_filter_condition_widget
|
||||
any : (Filter_Condition | (Date -> Boolean)) -> Boolean
|
||||
any self condition = self.find condition . is_nothing . not
|
||||
|
@ -865,11 +865,12 @@ spec =
|
||||
expected_vector = column_vector.filter (Filter_Condition.Is_In in_vector)
|
||||
expected_neg_vector = negated_column_vector.filter (Filter_Condition.Is_In in_vector)
|
||||
|
||||
t.filter "X" (Filter_Condition.Is_In in_vector) . at "X" . to_vector . should_equal expected_vector
|
||||
t.filter "X" (Filter_Condition.Is_In in_column) . at "X" . to_vector . should_equal expected_vector
|
||||
t2 = t.set (t.at "X" . not) new_name="Y"
|
||||
t2.filter "Y" (Filter_Condition.Is_In in_vector) . at "Y" . to_vector . should_equal expected_neg_vector
|
||||
t2.filter "Y" (Filter_Condition.Is_In in_column) . at "Y" . to_vector . should_equal expected_neg_vector
|
||||
Test.with_clue "(Is_In "+in_vector.to_text+"): " <|
|
||||
t.filter "X" (Filter_Condition.Is_In in_vector) . at "X" . to_vector . should_equal expected_vector
|
||||
t.filter "X" (Filter_Condition.Is_In in_column) . at "X" . to_vector . should_equal expected_vector
|
||||
t2 = t.set (t.at "X" . not) new_name="Y"
|
||||
t2.filter "Y" (Filter_Condition.Is_In in_vector) . at "Y" . to_vector . should_equal expected_neg_vector
|
||||
t2.filter "Y" (Filter_Condition.Is_In in_column) . at "Y" . to_vector . should_equal expected_neg_vector
|
||||
|
||||
Test.group "[In-Memory-specific] Table.join" <|
|
||||
Test.specify "should correctly report unsupported cross-backend joins" <|
|
||||
|
@ -1,7 +1,6 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Data.List.Empty_Error
|
||||
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
|
||||
import Standard.Base.Errors.Common.No_Such_Method
|
||||
import Standard.Base.Errors.Common.Not_Found
|
||||
import Standard.Base.Errors.Common.Type_Error
|
||||
import Standard.Base.Errors.Common.Unsupported_Argument_Types
|
||||
@ -139,7 +138,7 @@ spec = Test.group "List" <|
|
||||
list.filter (Filter_Condition.Is_In [7, 3, 2]) . should_equal [2, 3].to_list
|
||||
list.filter (Filter_Condition.Not_In [7, 3, 2]) . should_equal [1, 4, 5].to_list
|
||||
|
||||
Test.expect_panic_with (list.filter (Filter_Condition.Starts_With "a")) No_Such_Method
|
||||
Test.expect_panic Type_Error (list.filter (Filter_Condition.Starts_With "a"))
|
||||
list.filter Filter_Condition.Is_True . should_equal List.Nil
|
||||
list.filter Filter_Condition.Is_False . should_equal List.Nil
|
||||
list.filter Filter_Condition.Is_Nothing . should_equal List.Nil
|
||||
|
@ -148,8 +148,8 @@ spec = Test.group "Range" <|
|
||||
range.filter (Filter_Condition.Is_In [7, 3, 2]) . should_equal [2, 3]
|
||||
range.filter (Filter_Condition.Not_In [7, 3, 2]) . should_equal [1, 4, 5]
|
||||
|
||||
Test.expect_panic_with (range.filter (Filter_Condition.Starts_With "a")) No_Such_Method
|
||||
Test.expect_panic_with (range.filter (Filter_Condition.Equal_Ignore_Case "a")) No_Such_Method
|
||||
Test.expect_panic Type_Error (range.filter (Filter_Condition.Starts_With "a"))
|
||||
Test.expect_panic Type_Error (range.filter (Filter_Condition.Equal_Ignore_Case "a"))
|
||||
range.filter (Filter_Condition.Like "a%") . should_fail_with Type_Error
|
||||
range.filter (Filter_Condition.Not_Like "a_") . should_fail_with Type_Error
|
||||
range.filter Filter_Condition.Is_Nan . should_equal []
|
||||
|
@ -5,7 +5,6 @@ import Standard.Base.Data.Vector.No_Wrap
|
||||
import Standard.Base.Errors.Common.Additional_Warnings
|
||||
import Standard.Base.Errors.Common.Incomparable_Values
|
||||
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
|
||||
import Standard.Base.Errors.Common.No_Such_Method
|
||||
import Standard.Base.Errors.Common.Not_Found
|
||||
import Standard.Base.Errors.Common.Type_Error
|
||||
import Standard.Base.Errors.Common.Unsupported_Argument_Types
|
||||
@ -221,7 +220,7 @@ type_spec name alter = Test.group name <|
|
||||
vec.filter (Filter_Condition.Is_In []) . should_equal []
|
||||
vec.filter (Filter_Condition.Not_In [7, 3, 2, 2]) . should_equal [1, 4, 5]
|
||||
|
||||
Test.expect_panic_with (vec.filter (Filter_Condition.Starts_With "a")) No_Such_Method
|
||||
Test.expect_panic_with (vec.filter (Filter_Condition.Starts_With "a")) Type_Error
|
||||
vec.filter Filter_Condition.Is_True . should_equal []
|
||||
vec.filter Filter_Condition.Is_False . should_equal []
|
||||
vec.filter Filter_Condition.Is_Nothing . should_equal []
|
||||
@ -292,16 +291,41 @@ type_spec name alter = Test.group name <|
|
||||
mixed.filter Filter_Condition.Is_Empty . should_equal [Nothing]
|
||||
mixed.filter Filter_Condition.Not_Empty . should_equal [1, "b"]
|
||||
|
||||
boolvec = [True, False, Nothing, True]
|
||||
boolvec.filter Filter_Condition.Is_True . should_equal [True, True]
|
||||
boolvec.filter Filter_Condition.Is_False . should_equal [False]
|
||||
|
||||
numvec = [1, 2.5, Number.nan, Number.positive_infinity, Number.negative_infinity, 0]
|
||||
numvec = alter [1, 2.5, Number.nan, Number.positive_infinity, Number.negative_infinity, 0]
|
||||
# We need to use to_text because NaN!=NaN
|
||||
numvec.filter Filter_Condition.Is_Nan . map .to_text . should_equal ["NaN"]
|
||||
numvec.filter Filter_Condition.Is_Infinite . should_equal [Number.positive_infinity, Number.negative_infinity]
|
||||
numvec.filter Filter_Condition.Is_Finite . should_equal [1, 2.5, 0]
|
||||
|
||||
Test.expect_panic Type_Error (txtvec.filter Filter_Condition.Is_Finite)
|
||||
|
||||
(alter [2, "a"]).filter (Filter_Condition.Greater 1) . should_fail_with Incomparable_Values
|
||||
|
||||
Test.specify "should allow Nothing when filtering by Filter_Condition" <|
|
||||
(alter [1, 2, Nothing, 3]).filter (Filter_Condition.Greater 2) . should_equal [3]
|
||||
(alter [1, 2, Nothing, 3]).filter (Filter_Condition.Equal_Or_Less 2) . should_equal [1, 2]
|
||||
(alter ["a", 2, Nothing, 2]).filter (Filter_Condition.Equal 2) . should_equal [2, 2]
|
||||
(alter ["a", 2, Nothing, "a", "a"]).filter (Filter_Condition.Equal "a") . should_equal ["a", "a", "a"]
|
||||
|
||||
(alter [1, Nothing, (1/0), (0.log 0)]).filter Filter_Condition.Is_Nan . map .to_text . should_equal ["NaN"]
|
||||
(alter [1, Nothing, (1/0), (0.log 0)]).filter Filter_Condition.Is_Infinite . should_equal [Number.positive_infinity]
|
||||
(alter [1, Nothing, (1/0), (0.log 0)]).filter Filter_Condition.Is_Finite . should_equal [1]
|
||||
|
||||
boolvec = alter [True, False, Nothing, True]
|
||||
boolvec.filter Filter_Condition.Is_True . should_equal [True, True]
|
||||
boolvec.filter Filter_Condition.Is_False . should_equal [False]
|
||||
|
||||
txtvec = alter ["abab", "baaa", Nothing, "cccc", "BAAA"]
|
||||
txtvec.filter (Filter_Condition.Equal_Ignore_Case "baaA") . should_equal ["baaa", "BAAA"]
|
||||
txtvec.filter (Filter_Condition.Contains "a") . should_equal ["abab", "baaa"]
|
||||
txtvec.filter (Filter_Condition.Starts_With "a") . should_equal ["abab"]
|
||||
txtvec.filter (Filter_Condition.Ends_With "a") . should_equal ["baaa"]
|
||||
txtvec.filter (Filter_Condition.Like "b%a") . should_equal ["baaa"]
|
||||
# Nothing is not included in the negation either
|
||||
txtvec.filter (Filter_Condition.Not_Like "b%a") . should_equal ["abab", "cccc", "BAAA"]
|
||||
|
||||
(alter ["a", 2, Nothing, 3]).filter (Filter_Condition.Is_In [Nothing, 2]) . should_equal [2, Nothing]
|
||||
|
||||
Test.specify "should have a friendly error when missing Filter_Condition arguments" <|
|
||||
v = alter [0, 1, 2]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user