diff --git a/docs/api.yml b/docs/api.yml
index 71fa7dcd..b64917e0 100644
--- a/docs/api.yml
+++ b/docs/api.yml
@@ -96,6 +96,7 @@ sidebar:
- "api-reference/expressions/fillnull"
- "api-reference/expressions/lit"
- "api-reference/expressions/not"
+ - "api-reference/expressions/now"
- "api-reference/expressions/typeof"
- "api-reference/expressions/when"
diff --git a/docs/examples/api-reference/expressions/basic.py b/docs/examples/api-reference/expressions/basic.py
index b783c034..3a27c2e8 100644
--- a/docs/examples/api-reference/expressions/basic.py
+++ b/docs/examples/api-reference/expressions/basic.py
@@ -1,3 +1,5 @@
+from datetime import datetime
+
import pytest
from typing import Optional
import pandas as pd
@@ -156,3 +158,26 @@ def test_lit():
df = pd.DataFrame({"x": pd.Series([1, 2, None], dtype=pd.Int64Dtype())})
assert expr.eval(df, schema={"x": Optional[int]}).tolist() == [2, 3, pd.NA]
# /docsnip
+
+
+def test_now():
+ # docsnip expr_now
+ from fennel.expr import now, col
+
+ # docsnip-highlight next-line
+ expr = now().dt.since(col("birthdate"), "year")
+
+ assert (
+ expr.typeof(schema={"birthdate": Optional[datetime]}) == Optional[int]
+ )
+
+ # can be evaluated with a dataframe
+ df = pd.DataFrame(
+ {"birthdate": [datetime(1997, 12, 24), datetime(2001, 1, 21), None]}
+ )
+ assert expr.eval(df, schema={"birthdate": Optional[datetime]}).tolist() == [
+ 26,
+ 23,
+ pd.NA,
+ ]
+ # /docsnip
diff --git a/docs/pages/api-reference/expressions/now.md b/docs/pages/api-reference/expressions/now.md
new file mode 100644
index 00000000..44415241
--- /dev/null
+++ b/docs/pages/api-reference/expressions/now.md
@@ -0,0 +1,18 @@
+---
+title: Now
+order: 0
+status: published
+---
+### Now
+
+Function to get current timestamp, similar to what `datetime.now` does in Python.
+
+
+
+
+#### Returns
+
+Returns an expression object denoting a reference to the column. The type of
+the resulting expression is datetime.
+
diff --git a/fennel/CHANGELOG.md b/fennel/CHANGELOG.md
index 8d04212c..ace4c9b0 100644
--- a/fennel/CHANGELOG.md
+++ b/fennel/CHANGELOG.md
@@ -1,5 +1,8 @@
# Changelog
+## [1.5.33] - 2024-09-30
+- Add support for now in expressions.
+
## [1.5.32] - 2024-09-27
- Add dropnull to FirstK
diff --git a/fennel/client_tests/test_expr.py b/fennel/client_tests/test_expr.py
new file mode 100644
index 00000000..fd01f545
--- /dev/null
+++ b/fennel/client_tests/test_expr.py
@@ -0,0 +1,149 @@
+from datetime import datetime, timezone, timedelta
+from typing import Optional
+
+import pandas as pd
+import pytest
+
+from fennel._vendor import requests
+from fennel.connectors import source, Webhook
+from fennel.datasets import dataset, field
+from fennel.expr import col
+from fennel.featuresets import featureset, feature as F
+from fennel.testing import mock
+
+webhook = Webhook(name="fennel_webhook")
+__owner__ = "eng@fennel.ai"
+
+
+@source(webhook.endpoint("UserInfoDataset"), disorder="14d", cdc="upsert")
+@dataset(index=True)
+class UserInfoDataset:
+ user_id: int = field(key=True)
+ name: str
+ birthdate: datetime
+ country: str
+ ts: datetime = field(timestamp=True)
+
+
+@pytest.mark.integration
+@mock
+def test_now(client):
+ from fennel.expr import now
+
+ @featureset
+ class UserInfoFeatures:
+ user_id: int
+ name: Optional[str] = F(UserInfoDataset.name)
+ birthdate: Optional[datetime] = F(UserInfoDataset.birthdate)
+ age: Optional[int] = F(now().dt.since(col("birthdate"), unit="year"))
+ country: Optional[str] = F(UserInfoDataset.country)
+
+ # Sync the dataset
+ response = client.commit(
+ message="msg",
+ datasets=[UserInfoDataset],
+ featuresets=[UserInfoFeatures],
+ )
+ assert response.status_code == requests.codes.OK, response.json()
+
+ now = datetime.now(timezone.utc)
+ now_1y = now - timedelta(days=365)
+ df = pd.DataFrame(
+ {
+ "user_id": [1, 2, 3, 4, 5],
+ "name": ["Ross", "Monica", "Chandler", "Joey", "Rachel"],
+ "birthdate": [
+ datetime(1970, 1, 1, tzinfo=timezone.utc),
+ datetime(1980, 3, 12, tzinfo=timezone.utc),
+ datetime(1990, 5, 15, tzinfo=timezone.utc),
+ datetime(1997, 12, 24, tzinfo=timezone.utc),
+ datetime(2001, 1, 21, tzinfo=timezone.utc),
+ ],
+ "country": ["India", "USA", "Africa", "UK", "Chile"],
+ "ts": [now_1y, now_1y, now_1y, now_1y, now_1y],
+ }
+ )
+ response = client.log("fennel_webhook", "UserInfoDataset", df)
+ assert response.status_code == requests.codes.OK, response.json()
+
+ client.sleep()
+
+ # Querying UserInfoFeatures
+ df = client.query(
+ inputs=[UserInfoFeatures.user_id],
+ outputs=[
+ UserInfoFeatures.name,
+ UserInfoFeatures.age,
+ UserInfoFeatures.country,
+ ],
+ input_dataframe=pd.DataFrame(
+ {"UserInfoFeatures.user_id": [1, 2, 3, 4, 5, 6]}
+ ),
+ )
+ assert df.shape == (6, 3)
+ assert df["UserInfoFeatures.name"].tolist() == [
+ "Ross",
+ "Monica",
+ "Chandler",
+ "Joey",
+ "Rachel",
+ pd.NA,
+ ]
+ assert df["UserInfoFeatures.age"].tolist() == [54, 44, 34, 26, 23, pd.NA]
+ assert df["UserInfoFeatures.country"].tolist() == [
+ "India",
+ "USA",
+ "Africa",
+ "UK",
+ "Chile",
+ pd.NA,
+ ]
+
+ if not client.is_integration_client():
+ df = client.query_offline(
+ inputs=[UserInfoFeatures.user_id],
+ outputs=[
+ UserInfoFeatures.name,
+ UserInfoFeatures.age,
+ UserInfoFeatures.country,
+ ],
+ input_dataframe=pd.DataFrame(
+ {
+ "UserInfoFeatures.user_id": [1, 2, 3, 4, 5, 6],
+ "timestamp": [
+ now_1y,
+ now_1y,
+ now_1y,
+ now_1y,
+ now_1y,
+ now_1y,
+ ],
+ }
+ ),
+ timestamp_column="timestamp",
+ )
+ assert df.shape == (6, 4)
+ assert df["UserInfoFeatures.name"].tolist() == [
+ "Ross",
+ "Monica",
+ "Chandler",
+ "Joey",
+ "Rachel",
+ pd.NA,
+ ]
+ assert df["UserInfoFeatures.age"].tolist() == [
+ 53,
+ 43,
+ 33,
+ 25,
+ 22,
+ pd.NA,
+ ]
+ assert df["UserInfoFeatures.country"].tolist() == [
+ "India",
+ "USA",
+ "Africa",
+ "UK",
+ "Chile",
+ pd.NA,
+ ]
diff --git a/fennel/datasets/test_invalid_dataset.py b/fennel/datasets/test_invalid_dataset.py
index f4a3f8c9..7d61f8d9 100644
--- a/fennel/datasets/test_invalid_dataset.py
+++ b/fennel/datasets/test_invalid_dataset.py
@@ -295,7 +295,7 @@ def transform(cls, rating: Dataset):
assert (
str(e2.value)
- == """invalid assign - '[Pipeline:transform]->assign node' error in expression for column `movie_suffixed`: Failed to compile expression: invalid expression: both sides of '+' must be numeric types but found String & String, left: col("movie"), right: lit(String("_suffix"))"""
+ == """invalid assign - '[Pipeline:transform]->assign node' error in expression for column `movie_suffixed`: failed to compile expression: invalid expression: both sides of '+' must be numeric types but found String & String, left: col("movie"), right: lit(String("_suffix"))"""
)
diff --git a/fennel/expr/__init__.py b/fennel/expr/__init__.py
index 362f199b..43e0dc3e 100644
--- a/fennel/expr/__init__.py
+++ b/fennel/expr/__init__.py
@@ -6,6 +6,7 @@
var,
datetime,
from_epoch,
+ now,
Expr,
InvalidExprException,
)
diff --git a/fennel/expr/expr.py b/fennel/expr/expr.py
index 5e2c8810..cf85430c 100644
--- a/fennel/expr/expr.py
+++ b/fennel/expr/expr.py
@@ -7,6 +7,7 @@
from typing import Any, Callable, Dict, Type, Optional
import pandas as pd
+from fennel._vendor.pydantic import BaseModel # type: ignore
from fennel.dtypes.dtypes import FENNEL_STRUCT, FENNEL_STRUCT_SRC_CODE
from fennel.internal_lib.schema.schema import (
convert_dtype_to_arrow_type_with_nullable,
@@ -17,6 +18,7 @@
from fennel_data_lib import assign, type_of, matches
import fennel.gen.schema_pb2 as schema_proto
+import fennel.gen.expr_pb2 as expr_proto
from fennel.internal_lib.schema import (
get_datatype,
cast_col_to_arrow_dtype,
@@ -27,6 +29,10 @@
)
+class EvalContext(BaseModel):
+ now_col_name: Optional[str] = None
+
+
class InvalidExprException(Exception):
pass
@@ -360,6 +366,7 @@ def eval(
schema: Dict,
output_dtype: Optional[Type] = None,
parse=True,
+ context: Optional[EvalContext] = None,
) -> pd.Series:
from fennel.expr.serializer import ExprSerializer
@@ -410,8 +417,18 @@ def pa_to_pd(pa_data, ret_type, parse=True):
ret_type = output_dtype
serialized_ret_type = get_datatype(ret_type).SerializeToString()
+ if context is None:
+ serialized_context = expr_proto.EvalContext().SerializeToString()
+ else:
+ serialized_context = expr_proto.EvalContext(
+ **context.dict()
+ ).SerializeToString()
arrow_col = assign(
- proto_bytes, df_pa, proto_schema, serialized_ret_type
+ proto_bytes,
+ df_pa,
+ proto_schema,
+ serialized_ret_type,
+ serialized_context,
)
return pa_to_pd(arrow_col, ret_type, parse)
@@ -1089,6 +1106,14 @@ def __str__(self) -> str:
return f"fillnull({self.expr}, {self.fill})"
+class Now(Expr):
+ def __init__(self):
+ super(Now, self).__init__()
+
+ def __str__(self) -> str:
+ return "now()"
+
+
class MakeStruct(Expr):
def __init__(self, fields: Dict[str, Expr], type: Type):
self.fields = fields
@@ -1183,3 +1208,7 @@ def datetime(
),
DateTimeNoop(),
)
+
+
+def now() -> Now:
+ return Now()
diff --git a/fennel/expr/serializer.py b/fennel/expr/serializer.py
index 0673ff04..df8aafb3 100644
--- a/fennel/expr/serializer.py
+++ b/fennel/expr/serializer.py
@@ -485,6 +485,11 @@ def visitDateTimeLiteral(self, obj):
expr.datetime_literal.CopyFrom(datetime_literal)
return expr
+ def visitNow(self, obj):
+ expr = proto.Expr()
+ expr.now.CopyFrom(proto.Now())
+ return expr
+
def val_as_json(val: Any) -> str:
if isinstance(val, str):
diff --git a/fennel/expr/test_expr.py b/fennel/expr/test_expr.py
index 2be31827..ce7caf60 100644
--- a/fennel/expr/test_expr.py
+++ b/fennel/expr/test_expr.py
@@ -3,13 +3,17 @@
import random
import pandas as pd
from dataclasses import dataclass, fields
-from datetime import datetime
+from datetime import datetime, timezone
from typing import Any, Dict, Optional, List
from fennel.datasets import dataset
from fennel.dtypes.dtypes import struct
-from fennel.expr import col, when, lit
-from fennel.expr.expr import TimeUnit, from_epoch, make_struct
+from fennel.expr import col, when, lit, now
+from fennel.expr.expr import (
+ TimeUnit,
+ from_epoch,
+ make_struct,
+)
from fennel.expr.visitor import ExprPrinter, FetchReferences
from fennel.expr.serializer import ExprSerializer
from google.protobuf.json_format import ParseDict # type: ignore
@@ -1345,3 +1349,30 @@ def test_isnull():
for case in cases:
check_test_case(case)
+
+
+def test_now():
+ cases = [
+ ExprTestCase(
+ expr=now().dt.since(col("birthdate"), unit="day"),
+ df=pd.DataFrame(
+ {
+ "birthdate": [
+ datetime.now(timezone.utc),
+ datetime.now(timezone.utc),
+ None,
+ datetime.now(timezone.utc),
+ ],
+ }
+ ),
+ schema={"birthdate": Optional[datetime]},
+ display='SINCE(NOW(), col("birthdate"), unit=TimeUnit.DAY)',
+ refs={"birthdate"},
+ eval_result=[0, 0, pd.NA, 0],
+ expected_dtype=Optional[int],
+ proto_json=None,
+ ),
+ ]
+
+ for case in cases:
+ check_test_case(case)
diff --git a/fennel/expr/test_invalid_expr.py b/fennel/expr/test_invalid_expr.py
index a26f339d..7d467bd2 100644
--- a/fennel/expr/test_invalid_expr.py
+++ b/fennel/expr/test_invalid_expr.py
@@ -27,7 +27,7 @@ def test_invalid_datetime():
expr.eval(df, {"a": str})
assert (
- "Failed to compile expression: invalid timezone: `America/NonYork`"
+ "failed to compile expression: invalid timezone: `America/NonYork`"
in str(e.value)
)
@@ -45,14 +45,14 @@ def test_invalid_datetime():
expr.eval(df, {"a": str})
assert (
str(e.value)
- == 'Failed to evaluate expression: failed to eval expression: col("a").str.parse_datetime("%Y-%m-%d", timezone="America/New_York"), error: invalid operation: conversion from `str` to `datetime[μs, America/New_York]` failed in column \'a\' for 3 out of 3 values: ["1", "2", "3"]'
+ == 'failed to evaluate expression: failed to eval expression: col("a").str.parse_datetime("%Y-%m-%d", timezone="America/New_York"), error: invalid operation: conversion from `str` to `datetime[μs, America/New_York]` failed in column \'a\' for 3 out of 3 values: ["1", "2", "3"]'
)
with pytest.raises(ValueError) as e:
expr.eval(df, {"a": int})
assert (
str(e.value)
- == """Failed to compile expression: invalid expression: expected string type for function 'Strptime { format: "%Y-%m-%d", timezone: Some("America/New_York") }' but found Int"""
+ == """failed to compile expression: invalid expression: expected string type for function 'Strptime { format: "%Y-%m-%d", timezone: Some("America/New_York") }' but found Int"""
)
@@ -99,5 +99,5 @@ def test_invalid_parse():
expr.eval(df, {"a": str})
assert (
str(e.value)
- == "Failed to evaluate expression: failed to convert polars array to fennel array for type 'Int'"
+ == "failed to evaluate expression: failed to convert polars array to fennel array for type 'Int'"
)
diff --git a/fennel/expr/visitor.py b/fennel/expr/visitor.py
index cdf6a6ed..b82ee964 100644
--- a/fennel/expr/visitor.py
+++ b/fennel/expr/visitor.py
@@ -51,6 +51,7 @@
DictNoop,
_DateTime,
DateTimeNoop,
+ Now,
)
@@ -115,6 +116,10 @@ def visit(self, obj):
elif isinstance(obj, DateTimeFromEpoch):
ret = self.visitDateTimeFromEpoch(obj)
+
+ elif isinstance(obj, Now):
+ ret = self.visitNow(obj)
+
else:
raise InvalidExprException("invalid expression type: %s" % obj)
@@ -180,6 +185,9 @@ def visitDateTimeFromEpoch(self, obj):
def visitDateTimeLiteral(self, obj):
raise NotImplementedError
+ def visitNow(self, obj):
+ raise NotImplementedError
+
class ExprPrinter(Visitor):
@@ -344,6 +352,9 @@ def visitDateTimeFromEpoch(self, obj):
def visitDateTimeLiteral(self, obj):
return f"DATETIME({obj.year}, {obj.month}, {obj.day}, {obj.hour}, {obj.minute}, {obj.second}, {obj.microsecond}, timezone={obj.timezone})"
+ def visitNow(self, obj):
+ return "NOW()"
+
class FetchReferences(Visitor):
@@ -444,3 +455,6 @@ def visitDateTimeFromEpoch(self, obj):
def visitDateTimeLiteral(self, obj):
pass
+
+ def visitNow(self, obj):
+ pass
diff --git a/fennel/gen/expr_pb2.py b/fennel/gen/expr_pb2.py
index 69df78aa..e14582da 100644
--- a/fennel/gen/expr_pb2.py
+++ b/fennel/gen/expr_pb2.py
@@ -14,7 +14,7 @@
import fennel.gen.schema_pb2 as schema__pb2
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nexpr.proto\x12\x11\x66\x65nnel.proto.expr\x1a\x0cschema.proto\"\xc1\x06\n\x04\x45xpr\x12%\n\x03ref\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.RefH\x00\x12\x36\n\x0cjson_literal\x18\x02 \x01(\x0b\x32\x1e.fennel.proto.expr.JsonLiteralH\x00\x12)\n\x05unary\x18\x04 \x01(\x0b\x32\x18.fennel.proto.expr.UnaryH\x00\x12\'\n\x04\x63\x61se\x18\x05 \x01(\x0b\x32\x17.fennel.proto.expr.CaseH\x00\x12+\n\x06\x62inary\x18\x06 \x01(\x0b\x32\x19.fennel.proto.expr.BinaryH\x00\x12+\n\x06isnull\x18\x07 \x01(\x0b\x32\x19.fennel.proto.expr.IsNullH\x00\x12/\n\x08\x66illnull\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.FillNullH\x00\x12,\n\x07list_fn\x18\t \x01(\x0b\x32\x19.fennel.proto.expr.ListFnH\x00\x12,\n\x07math_fn\x18\n \x01(\x0b\x32\x19.fennel.proto.expr.MathFnH\x00\x12\x30\n\tstruct_fn\x18\x0b \x01(\x0b\x32\x1b.fennel.proto.expr.StructFnH\x00\x12,\n\x07\x64ict_fn\x18\x0c \x01(\x0b\x32\x19.fennel.proto.expr.DictFnH\x00\x12\x30\n\tstring_fn\x18\r \x01(\x0b\x32\x1b.fennel.proto.expr.StringFnH\x00\x12\x34\n\x0b\x64\x61tetime_fn\x18\x0e \x01(\x0b\x32\x1d.fennel.proto.expr.DateTimeFnH\x00\x12>\n\x10\x64\x61tetime_literal\x18\x0f \x01(\x0b\x32\".fennel.proto.expr.DatetimeLiteralH\x00\x12\x34\n\x0bmake_struct\x18\x10 \x01(\x0b\x32\x1d.fennel.proto.expr.MakeStructH\x00\x12\x32\n\nfrom_epoch\x18\x11 \x01(\x0b\x32\x1c.fennel.proto.expr.FromEpochH\x00\x12%\n\x03var\x18\x12 \x01(\x0b\x32\x16.fennel.proto.expr.VarH\x00\x42\x06\n\x04node\"\x13\n\x03Var\x12\x0c\n\x04name\x18\x01 \x01(\t\"a\n\tFromEpoch\x12)\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x04unit\x18\x02 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"\xad\x01\n\x0f\x44\x61tetimeLiteral\x12\x0c\n\x04year\x18\x01 \x01(\r\x12\r\n\x05month\x18\x02 \x01(\r\x12\x0b\n\x03\x64\x61y\x18\x03 \x01(\r\x12\x0c\n\x04hour\x18\x04 \x01(\r\x12\x0e\n\x06minute\x18\x05 \x01(\r\x12\x0e\n\x06second\x18\x06 \x01(\r\x12\x13\n\x0bmicrosecond\x18\x07 \x01(\r\x12-\n\x08timezone\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"\xc5\x01\n\nMakeStruct\x12\x34\n\x0bstruct_type\x18\x01 \x01(\x0b\x32\x1f.fennel.proto.schema.StructType\x12\x39\n\x06\x66ields\x18\x02 \x03(\x0b\x32).fennel.proto.expr.MakeStruct.FieldsEntry\x1a\x46\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr:\x02\x38\x01\"L\n\x0bJsonLiteral\x12\x0f\n\x07literal\x18\x01 \x01(\t\x12,\n\x05\x64type\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.schema.DataType\"\x13\n\x03Ref\x12\x0c\n\x04name\x18\x01 \x01(\t\"Y\n\x05Unary\x12&\n\x02op\x18\x01 \x01(\x0e\x32\x1a.fennel.proto.expr.UnaryOp\x12(\n\x07operand\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"}\n\x06\x42inary\x12%\n\x04left\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12&\n\x05right\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12$\n\x02op\x18\x03 \x01(\x0e\x32\x18.fennel.proto.expr.BinOp\"b\n\x04\x43\x61se\x12.\n\twhen_then\x18\x01 \x03(\x0b\x32\x1b.fennel.proto.expr.WhenThen\x12*\n\totherwise\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"X\n\x08WhenThen\x12%\n\x04when\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x04then\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"2\n\x06IsNull\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"[\n\x08\x46illNull\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x04\x66ill\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\xa3\x04\n\x06ListOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12&\n\x03get\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.ExprH\x00\x12/\n\x08\x63ontains\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x12.\n\x08has_null\x18\x04 \x01(\x0b\x32\x1a.fennel.proto.expr.HasNullH\x00\x12)\n\x03sum\x18\x05 \x01(\x0b\x32\x1a.fennel.proto.expr.ListSumH\x00\x12)\n\x03min\x18\x06 \x01(\x0b\x32\x1a.fennel.proto.expr.ListMinH\x00\x12)\n\x03max\x18\x07 \x01(\x0b\x32\x1a.fennel.proto.expr.ListMaxH\x00\x12)\n\x03\x61ll\x18\x08 \x01(\x0b\x32\x1a.fennel.proto.expr.ListAllH\x00\x12)\n\x03\x61ny\x18\t \x01(\x0b\x32\x1a.fennel.proto.expr.ListAnyH\x00\x12+\n\x04mean\x18\n \x01(\x0b\x32\x1b.fennel.proto.expr.ListMeanH\x00\x12/\n\x06\x66ilter\x18\x0b \x01(\x0b\x32\x1d.fennel.proto.expr.ListFilterH\x00\x12)\n\x03map\x18\x0c \x01(\x0b\x32\x1a.fennel.proto.expr.ListMapH\x00\x42\t\n\x07\x66n_type\"E\n\nListFilter\x12\x0b\n\x03var\x18\x01 \x01(\t\x12*\n\tpredicate\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"A\n\x07ListMap\x12\x0b\n\x03var\x18\x01 \x01(\t\x12)\n\x08map_expr\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\t\n\x07ListSum\"\t\n\x07ListMin\"\n\n\x08ListMean\"\t\n\x07ListMax\"\t\n\x07ListAll\"\t\n\x07ListAny\"\x05\n\x03Len\"\t\n\x07HasNull\"4\n\x08\x43ontains\x12(\n\x07\x65lement\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"V\n\x06ListFn\x12%\n\x04list\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.ListOp\"\xb9\x01\n\x06MathOp\x12)\n\x05round\x18\x01 \x01(\x0b\x32\x18.fennel.proto.expr.RoundH\x00\x12%\n\x03\x61\x62s\x18\x02 \x01(\x0b\x32\x16.fennel.proto.expr.AbsH\x00\x12\'\n\x04\x63\x65il\x18\x03 \x01(\x0b\x32\x17.fennel.proto.expr.CeilH\x00\x12)\n\x05\x66loor\x18\x04 \x01(\x0b\x32\x18.fennel.proto.expr.FloorH\x00\x42\t\n\x07\x66n_type\"\x1a\n\x05Round\x12\x11\n\tprecision\x18\x01 \x01(\x05\"\x05\n\x03\x41\x62s\"\x06\n\x04\x43\x65il\"\x07\n\x05\x46loor\"Y\n\x06MathFn\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.MathOp\"&\n\x08StructOp\x12\x0f\n\x05\x66ield\x18\x01 \x01(\tH\x00\x42\t\n\x07\x66n_type\"\\\n\x08StructFn\x12\'\n\x06struct\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12\'\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.StructOp\"a\n\x07\x44ictGet\x12&\n\x05\x66ield\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12.\n\rdefault_value\x18\x03 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\x96\x01\n\x06\x44ictOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12)\n\x03get\x18\x02 \x01(\x0b\x32\x1a.fennel.proto.expr.DictGetH\x00\x12/\n\x08\x63ontains\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x42\t\n\x07\x66n_type\"V\n\x06\x44ictFn\x12%\n\x04\x64ict\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.DictOp\"\xc5\x03\n\x08StringOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12-\n\x07tolower\x18\x02 \x01(\x0b\x32\x1a.fennel.proto.expr.ToLowerH\x00\x12-\n\x07toupper\x18\x03 \x01(\x0b\x32\x1a.fennel.proto.expr.ToUpperH\x00\x12/\n\x08\x63ontains\x18\x04 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x12\x33\n\nstartswith\x18\x05 \x01(\x0b\x32\x1d.fennel.proto.expr.StartsWithH\x00\x12/\n\x08\x65ndswith\x18\x06 \x01(\x0b\x32\x1b.fennel.proto.expr.EndsWithH\x00\x12+\n\x06\x63oncat\x18\x07 \x01(\x0b\x32\x19.fennel.proto.expr.ConcatH\x00\x12/\n\x08strptime\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.StrptimeH\x00\x12\x34\n\x0bjson_decode\x18\t \x01(\x0b\x32\x1d.fennel.proto.expr.JsonDecodeH\x00\x42\t\n\x07\x66n_type\"\x1c\n\x08Timezone\x12\x10\n\x08timezone\x18\x01 \x01(\t\":\n\nJsonDecode\x12,\n\x05\x64type\x18\x01 \x01(\x0b\x32\x1d.fennel.proto.schema.DataType\"I\n\x08Strptime\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"\t\n\x07ToLower\"\t\n\x07ToUpper\"2\n\nStartsWith\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"0\n\x08\x45ndsWith\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"0\n\x06\x43oncat\x12&\n\x05other\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\\\n\x08StringFn\x12\'\n\x06string\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12\'\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.StringOp\"b\n\nDateTimeFn\x12)\n\x08\x64\x61tetime\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.expr.DateTimeOp\"\xd2\x01\n\nDateTimeOp\x12)\n\x05since\x18\x01 \x01(\x0b\x32\x18.fennel.proto.expr.SinceH\x00\x12\x34\n\x0bsince_epoch\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.expr.SinceEpochH\x00\x12/\n\x08strftime\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.StrftimeH\x00\x12\'\n\x04part\x18\x04 \x01(\x0b\x32\x17.fennel.proto.expr.PartH\x00\x42\t\n\x07\x66n_type\"Z\n\x05Since\x12&\n\x05other\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x04unit\x18\x02 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"7\n\nSinceEpoch\x12)\n\x04unit\x18\x01 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"I\n\x08Strftime\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"`\n\x04Part\x12)\n\x04unit\x18\x01 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone*\x1b\n\x07UnaryOp\x12\x07\n\x03NEG\x10\x00\x12\x07\n\x03NOT\x10\x01*\x86\x01\n\x05\x42inOp\x12\x07\n\x03\x41\x44\x44\x10\x00\x12\x07\n\x03SUB\x10\x01\x12\x07\n\x03MUL\x10\x02\x12\x07\n\x03\x44IV\x10\x03\x12\x07\n\x03MOD\x10\x04\x12\r\n\tFLOOR_DIV\x10\x05\x12\x06\n\x02\x45Q\x10\x06\x12\x06\n\x02NE\x10\x07\x12\x06\n\x02GT\x10\x08\x12\x07\n\x03GTE\x10\t\x12\x06\n\x02LT\x10\n\x12\x07\n\x03LTE\x10\x0b\x12\x07\n\x03\x41ND\x10\x0c\x12\x06\n\x02OR\x10\r*\x83\x01\n\x08TimeUnit\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06SECOND\x10\x01\x12\n\n\x06MINUTE\x10\x02\x12\x08\n\x04HOUR\x10\x03\x12\x07\n\x03\x44\x41Y\x10\x04\x12\x08\n\x04WEEK\x10\x05\x12\t\n\x05MONTH\x10\x06\x12\x08\n\x04YEAR\x10\x07\x12\x0f\n\x0bMICROSECOND\x10\x08\x12\x0f\n\x0bMILLISECOND\x10\tb\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nexpr.proto\x12\x11\x66\x65nnel.proto.expr\x1a\x0cschema.proto\"9\n\x0b\x45valContext\x12\x19\n\x0cnow_col_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_now_col_name\"\xe8\x06\n\x04\x45xpr\x12%\n\x03ref\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.RefH\x00\x12\x36\n\x0cjson_literal\x18\x02 \x01(\x0b\x32\x1e.fennel.proto.expr.JsonLiteralH\x00\x12)\n\x05unary\x18\x04 \x01(\x0b\x32\x18.fennel.proto.expr.UnaryH\x00\x12\'\n\x04\x63\x61se\x18\x05 \x01(\x0b\x32\x17.fennel.proto.expr.CaseH\x00\x12+\n\x06\x62inary\x18\x06 \x01(\x0b\x32\x19.fennel.proto.expr.BinaryH\x00\x12+\n\x06isnull\x18\x07 \x01(\x0b\x32\x19.fennel.proto.expr.IsNullH\x00\x12/\n\x08\x66illnull\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.FillNullH\x00\x12,\n\x07list_fn\x18\t \x01(\x0b\x32\x19.fennel.proto.expr.ListFnH\x00\x12,\n\x07math_fn\x18\n \x01(\x0b\x32\x19.fennel.proto.expr.MathFnH\x00\x12\x30\n\tstruct_fn\x18\x0b \x01(\x0b\x32\x1b.fennel.proto.expr.StructFnH\x00\x12,\n\x07\x64ict_fn\x18\x0c \x01(\x0b\x32\x19.fennel.proto.expr.DictFnH\x00\x12\x30\n\tstring_fn\x18\r \x01(\x0b\x32\x1b.fennel.proto.expr.StringFnH\x00\x12\x34\n\x0b\x64\x61tetime_fn\x18\x0e \x01(\x0b\x32\x1d.fennel.proto.expr.DateTimeFnH\x00\x12>\n\x10\x64\x61tetime_literal\x18\x0f \x01(\x0b\x32\".fennel.proto.expr.DatetimeLiteralH\x00\x12\x34\n\x0bmake_struct\x18\x10 \x01(\x0b\x32\x1d.fennel.proto.expr.MakeStructH\x00\x12\x32\n\nfrom_epoch\x18\x11 \x01(\x0b\x32\x1c.fennel.proto.expr.FromEpochH\x00\x12%\n\x03var\x18\x12 \x01(\x0b\x32\x16.fennel.proto.expr.VarH\x00\x12%\n\x03now\x18\x13 \x01(\x0b\x32\x16.fennel.proto.expr.NowH\x00\x42\x06\n\x04node\"\x05\n\x03Now\"\x13\n\x03Var\x12\x0c\n\x04name\x18\x01 \x01(\t\"a\n\tFromEpoch\x12)\n\x08\x64uration\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x04unit\x18\x02 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"\xad\x01\n\x0f\x44\x61tetimeLiteral\x12\x0c\n\x04year\x18\x01 \x01(\r\x12\r\n\x05month\x18\x02 \x01(\r\x12\x0b\n\x03\x64\x61y\x18\x03 \x01(\r\x12\x0c\n\x04hour\x18\x04 \x01(\r\x12\x0e\n\x06minute\x18\x05 \x01(\r\x12\x0e\n\x06second\x18\x06 \x01(\r\x12\x13\n\x0bmicrosecond\x18\x07 \x01(\r\x12-\n\x08timezone\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"\xc5\x01\n\nMakeStruct\x12\x34\n\x0bstruct_type\x18\x01 \x01(\x0b\x32\x1f.fennel.proto.schema.StructType\x12\x39\n\x06\x66ields\x18\x02 \x03(\x0b\x32).fennel.proto.expr.MakeStruct.FieldsEntry\x1a\x46\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr:\x02\x38\x01\"L\n\x0bJsonLiteral\x12\x0f\n\x07literal\x18\x01 \x01(\t\x12,\n\x05\x64type\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.schema.DataType\"\x13\n\x03Ref\x12\x0c\n\x04name\x18\x01 \x01(\t\"Y\n\x05Unary\x12&\n\x02op\x18\x01 \x01(\x0e\x32\x1a.fennel.proto.expr.UnaryOp\x12(\n\x07operand\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"}\n\x06\x42inary\x12%\n\x04left\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12&\n\x05right\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12$\n\x02op\x18\x03 \x01(\x0e\x32\x18.fennel.proto.expr.BinOp\"b\n\x04\x43\x61se\x12.\n\twhen_then\x18\x01 \x03(\x0b\x32\x1b.fennel.proto.expr.WhenThen\x12*\n\totherwise\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"X\n\x08WhenThen\x12%\n\x04when\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x04then\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"2\n\x06IsNull\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"[\n\x08\x46illNull\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x04\x66ill\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\xa3\x04\n\x06ListOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12&\n\x03get\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.ExprH\x00\x12/\n\x08\x63ontains\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x12.\n\x08has_null\x18\x04 \x01(\x0b\x32\x1a.fennel.proto.expr.HasNullH\x00\x12)\n\x03sum\x18\x05 \x01(\x0b\x32\x1a.fennel.proto.expr.ListSumH\x00\x12)\n\x03min\x18\x06 \x01(\x0b\x32\x1a.fennel.proto.expr.ListMinH\x00\x12)\n\x03max\x18\x07 \x01(\x0b\x32\x1a.fennel.proto.expr.ListMaxH\x00\x12)\n\x03\x61ll\x18\x08 \x01(\x0b\x32\x1a.fennel.proto.expr.ListAllH\x00\x12)\n\x03\x61ny\x18\t \x01(\x0b\x32\x1a.fennel.proto.expr.ListAnyH\x00\x12+\n\x04mean\x18\n \x01(\x0b\x32\x1b.fennel.proto.expr.ListMeanH\x00\x12/\n\x06\x66ilter\x18\x0b \x01(\x0b\x32\x1d.fennel.proto.expr.ListFilterH\x00\x12)\n\x03map\x18\x0c \x01(\x0b\x32\x1a.fennel.proto.expr.ListMapH\x00\x42\t\n\x07\x66n_type\"E\n\nListFilter\x12\x0b\n\x03var\x18\x01 \x01(\t\x12*\n\tpredicate\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"A\n\x07ListMap\x12\x0b\n\x03var\x18\x01 \x01(\t\x12)\n\x08map_expr\x18\x02 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\t\n\x07ListSum\"\t\n\x07ListMin\"\n\n\x08ListMean\"\t\n\x07ListMax\"\t\n\x07ListAll\"\t\n\x07ListAny\"\x05\n\x03Len\"\t\n\x07HasNull\"4\n\x08\x43ontains\x12(\n\x07\x65lement\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"V\n\x06ListFn\x12%\n\x04list\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.ListOp\"\xb9\x01\n\x06MathOp\x12)\n\x05round\x18\x01 \x01(\x0b\x32\x18.fennel.proto.expr.RoundH\x00\x12%\n\x03\x61\x62s\x18\x02 \x01(\x0b\x32\x16.fennel.proto.expr.AbsH\x00\x12\'\n\x04\x63\x65il\x18\x03 \x01(\x0b\x32\x17.fennel.proto.expr.CeilH\x00\x12)\n\x05\x66loor\x18\x04 \x01(\x0b\x32\x18.fennel.proto.expr.FloorH\x00\x42\t\n\x07\x66n_type\"\x1a\n\x05Round\x12\x11\n\tprecision\x18\x01 \x01(\x05\"\x05\n\x03\x41\x62s\"\x06\n\x04\x43\x65il\"\x07\n\x05\x46loor\"Y\n\x06MathFn\x12(\n\x07operand\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.MathOp\"&\n\x08StructOp\x12\x0f\n\x05\x66ield\x18\x01 \x01(\tH\x00\x42\t\n\x07\x66n_type\"\\\n\x08StructFn\x12\'\n\x06struct\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12\'\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.StructOp\"a\n\x07\x44ictGet\x12&\n\x05\x66ield\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12.\n\rdefault_value\x18\x03 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\x96\x01\n\x06\x44ictOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12)\n\x03get\x18\x02 \x01(\x0b\x32\x1a.fennel.proto.expr.DictGetH\x00\x12/\n\x08\x63ontains\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x42\t\n\x07\x66n_type\"V\n\x06\x44ictFn\x12%\n\x04\x64ict\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12%\n\x02\x66n\x18\x02 \x01(\x0b\x32\x19.fennel.proto.expr.DictOp\"\xc5\x03\n\x08StringOp\x12%\n\x03len\x18\x01 \x01(\x0b\x32\x16.fennel.proto.expr.LenH\x00\x12-\n\x07tolower\x18\x02 \x01(\x0b\x32\x1a.fennel.proto.expr.ToLowerH\x00\x12-\n\x07toupper\x18\x03 \x01(\x0b\x32\x1a.fennel.proto.expr.ToUpperH\x00\x12/\n\x08\x63ontains\x18\x04 \x01(\x0b\x32\x1b.fennel.proto.expr.ContainsH\x00\x12\x33\n\nstartswith\x18\x05 \x01(\x0b\x32\x1d.fennel.proto.expr.StartsWithH\x00\x12/\n\x08\x65ndswith\x18\x06 \x01(\x0b\x32\x1b.fennel.proto.expr.EndsWithH\x00\x12+\n\x06\x63oncat\x18\x07 \x01(\x0b\x32\x19.fennel.proto.expr.ConcatH\x00\x12/\n\x08strptime\x18\x08 \x01(\x0b\x32\x1b.fennel.proto.expr.StrptimeH\x00\x12\x34\n\x0bjson_decode\x18\t \x01(\x0b\x32\x1d.fennel.proto.expr.JsonDecodeH\x00\x42\t\n\x07\x66n_type\"\x1c\n\x08Timezone\x12\x10\n\x08timezone\x18\x01 \x01(\t\":\n\nJsonDecode\x12,\n\x05\x64type\x18\x01 \x01(\x0b\x32\x1d.fennel.proto.schema.DataType\"I\n\x08Strptime\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"\t\n\x07ToLower\"\t\n\x07ToUpper\"2\n\nStartsWith\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"0\n\x08\x45ndsWith\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"0\n\x06\x43oncat\x12&\n\x05other\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\"\\\n\x08StringFn\x12\'\n\x06string\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12\'\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.StringOp\"b\n\nDateTimeFn\x12)\n\x08\x64\x61tetime\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x02\x66n\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.expr.DateTimeOp\"\xd2\x01\n\nDateTimeOp\x12)\n\x05since\x18\x01 \x01(\x0b\x32\x18.fennel.proto.expr.SinceH\x00\x12\x34\n\x0bsince_epoch\x18\x02 \x01(\x0b\x32\x1d.fennel.proto.expr.SinceEpochH\x00\x12/\n\x08strftime\x18\x03 \x01(\x0b\x32\x1b.fennel.proto.expr.StrftimeH\x00\x12\'\n\x04part\x18\x04 \x01(\x0b\x32\x17.fennel.proto.expr.PartH\x00\x42\t\n\x07\x66n_type\"Z\n\x05Since\x12&\n\x05other\x18\x01 \x01(\x0b\x32\x17.fennel.proto.expr.Expr\x12)\n\x04unit\x18\x02 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"7\n\nSinceEpoch\x12)\n\x04unit\x18\x01 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\"I\n\x08Strftime\x12\x0e\n\x06\x66ormat\x18\x01 \x01(\t\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone\"`\n\x04Part\x12)\n\x04unit\x18\x01 \x01(\x0e\x32\x1b.fennel.proto.expr.TimeUnit\x12-\n\x08timezone\x18\x02 \x01(\x0b\x32\x1b.fennel.proto.expr.Timezone*\x1b\n\x07UnaryOp\x12\x07\n\x03NEG\x10\x00\x12\x07\n\x03NOT\x10\x01*\x86\x01\n\x05\x42inOp\x12\x07\n\x03\x41\x44\x44\x10\x00\x12\x07\n\x03SUB\x10\x01\x12\x07\n\x03MUL\x10\x02\x12\x07\n\x03\x44IV\x10\x03\x12\x07\n\x03MOD\x10\x04\x12\r\n\tFLOOR_DIV\x10\x05\x12\x06\n\x02\x45Q\x10\x06\x12\x06\n\x02NE\x10\x07\x12\x06\n\x02GT\x10\x08\x12\x07\n\x03GTE\x10\t\x12\x06\n\x02LT\x10\n\x12\x07\n\x03LTE\x10\x0b\x12\x07\n\x03\x41ND\x10\x0c\x12\x06\n\x02OR\x10\r*\x83\x01\n\x08TimeUnit\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06SECOND\x10\x01\x12\n\n\x06MINUTE\x10\x02\x12\x08\n\x04HOUR\x10\x03\x12\x07\n\x03\x44\x41Y\x10\x04\x12\x08\n\x04WEEK\x10\x05\x12\t\n\x05MONTH\x10\x06\x12\x08\n\x04YEAR\x10\x07\x12\x0f\n\x0bMICROSECOND\x10\x08\x12\x0f\n\x0bMILLISECOND\x10\tb\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -23,118 +23,122 @@
DESCRIPTOR._options = None
_globals['_MAKESTRUCT_FIELDSENTRY']._options = None
_globals['_MAKESTRUCT_FIELDSENTRY']._serialized_options = b'8\001'
- _globals['_UNARYOP']._serialized_start=5275
- _globals['_UNARYOP']._serialized_end=5302
- _globals['_BINOP']._serialized_start=5305
- _globals['_BINOP']._serialized_end=5439
- _globals['_TIMEUNIT']._serialized_start=5442
- _globals['_TIMEUNIT']._serialized_end=5573
- _globals['_EXPR']._serialized_start=48
- _globals['_EXPR']._serialized_end=881
- _globals['_VAR']._serialized_start=883
- _globals['_VAR']._serialized_end=902
- _globals['_FROMEPOCH']._serialized_start=904
- _globals['_FROMEPOCH']._serialized_end=1001
- _globals['_DATETIMELITERAL']._serialized_start=1004
- _globals['_DATETIMELITERAL']._serialized_end=1177
- _globals['_MAKESTRUCT']._serialized_start=1180
- _globals['_MAKESTRUCT']._serialized_end=1377
- _globals['_MAKESTRUCT_FIELDSENTRY']._serialized_start=1307
- _globals['_MAKESTRUCT_FIELDSENTRY']._serialized_end=1377
- _globals['_JSONLITERAL']._serialized_start=1379
- _globals['_JSONLITERAL']._serialized_end=1455
- _globals['_REF']._serialized_start=1457
- _globals['_REF']._serialized_end=1476
- _globals['_UNARY']._serialized_start=1478
- _globals['_UNARY']._serialized_end=1567
- _globals['_BINARY']._serialized_start=1569
- _globals['_BINARY']._serialized_end=1694
- _globals['_CASE']._serialized_start=1696
- _globals['_CASE']._serialized_end=1794
- _globals['_WHENTHEN']._serialized_start=1796
- _globals['_WHENTHEN']._serialized_end=1884
- _globals['_ISNULL']._serialized_start=1886
- _globals['_ISNULL']._serialized_end=1936
- _globals['_FILLNULL']._serialized_start=1938
- _globals['_FILLNULL']._serialized_end=2029
- _globals['_LISTOP']._serialized_start=2032
- _globals['_LISTOP']._serialized_end=2579
- _globals['_LISTFILTER']._serialized_start=2581
- _globals['_LISTFILTER']._serialized_end=2650
- _globals['_LISTMAP']._serialized_start=2652
- _globals['_LISTMAP']._serialized_end=2717
- _globals['_LISTSUM']._serialized_start=2719
- _globals['_LISTSUM']._serialized_end=2728
- _globals['_LISTMIN']._serialized_start=2730
- _globals['_LISTMIN']._serialized_end=2739
- _globals['_LISTMEAN']._serialized_start=2741
- _globals['_LISTMEAN']._serialized_end=2751
- _globals['_LISTMAX']._serialized_start=2753
- _globals['_LISTMAX']._serialized_end=2762
- _globals['_LISTALL']._serialized_start=2764
- _globals['_LISTALL']._serialized_end=2773
- _globals['_LISTANY']._serialized_start=2775
- _globals['_LISTANY']._serialized_end=2784
- _globals['_LEN']._serialized_start=2786
- _globals['_LEN']._serialized_end=2791
- _globals['_HASNULL']._serialized_start=2793
- _globals['_HASNULL']._serialized_end=2802
- _globals['_CONTAINS']._serialized_start=2804
- _globals['_CONTAINS']._serialized_end=2856
- _globals['_LISTFN']._serialized_start=2858
- _globals['_LISTFN']._serialized_end=2944
- _globals['_MATHOP']._serialized_start=2947
- _globals['_MATHOP']._serialized_end=3132
- _globals['_ROUND']._serialized_start=3134
- _globals['_ROUND']._serialized_end=3160
- _globals['_ABS']._serialized_start=3162
- _globals['_ABS']._serialized_end=3167
- _globals['_CEIL']._serialized_start=3169
- _globals['_CEIL']._serialized_end=3175
- _globals['_FLOOR']._serialized_start=3177
- _globals['_FLOOR']._serialized_end=3184
- _globals['_MATHFN']._serialized_start=3186
- _globals['_MATHFN']._serialized_end=3275
- _globals['_STRUCTOP']._serialized_start=3277
- _globals['_STRUCTOP']._serialized_end=3315
- _globals['_STRUCTFN']._serialized_start=3317
- _globals['_STRUCTFN']._serialized_end=3409
- _globals['_DICTGET']._serialized_start=3411
- _globals['_DICTGET']._serialized_end=3508
- _globals['_DICTOP']._serialized_start=3511
- _globals['_DICTOP']._serialized_end=3661
- _globals['_DICTFN']._serialized_start=3663
- _globals['_DICTFN']._serialized_end=3749
- _globals['_STRINGOP']._serialized_start=3752
- _globals['_STRINGOP']._serialized_end=4205
- _globals['_TIMEZONE']._serialized_start=4207
- _globals['_TIMEZONE']._serialized_end=4235
- _globals['_JSONDECODE']._serialized_start=4237
- _globals['_JSONDECODE']._serialized_end=4295
- _globals['_STRPTIME']._serialized_start=4297
- _globals['_STRPTIME']._serialized_end=4370
- _globals['_TOLOWER']._serialized_start=4372
- _globals['_TOLOWER']._serialized_end=4381
- _globals['_TOUPPER']._serialized_start=4383
- _globals['_TOUPPER']._serialized_end=4392
- _globals['_STARTSWITH']._serialized_start=4394
- _globals['_STARTSWITH']._serialized_end=4444
- _globals['_ENDSWITH']._serialized_start=4446
- _globals['_ENDSWITH']._serialized_end=4494
- _globals['_CONCAT']._serialized_start=4496
- _globals['_CONCAT']._serialized_end=4544
- _globals['_STRINGFN']._serialized_start=4546
- _globals['_STRINGFN']._serialized_end=4638
- _globals['_DATETIMEFN']._serialized_start=4640
- _globals['_DATETIMEFN']._serialized_end=4738
- _globals['_DATETIMEOP']._serialized_start=4741
- _globals['_DATETIMEOP']._serialized_end=4951
- _globals['_SINCE']._serialized_start=4953
- _globals['_SINCE']._serialized_end=5043
- _globals['_SINCEEPOCH']._serialized_start=5045
- _globals['_SINCEEPOCH']._serialized_end=5100
- _globals['_STRFTIME']._serialized_start=5102
- _globals['_STRFTIME']._serialized_end=5175
- _globals['_PART']._serialized_start=5177
- _globals['_PART']._serialized_end=5273
+ _globals['_UNARYOP']._serialized_start=5380
+ _globals['_UNARYOP']._serialized_end=5407
+ _globals['_BINOP']._serialized_start=5410
+ _globals['_BINOP']._serialized_end=5544
+ _globals['_TIMEUNIT']._serialized_start=5547
+ _globals['_TIMEUNIT']._serialized_end=5678
+ _globals['_EVALCONTEXT']._serialized_start=47
+ _globals['_EVALCONTEXT']._serialized_end=104
+ _globals['_EXPR']._serialized_start=107
+ _globals['_EXPR']._serialized_end=979
+ _globals['_NOW']._serialized_start=981
+ _globals['_NOW']._serialized_end=986
+ _globals['_VAR']._serialized_start=988
+ _globals['_VAR']._serialized_end=1007
+ _globals['_FROMEPOCH']._serialized_start=1009
+ _globals['_FROMEPOCH']._serialized_end=1106
+ _globals['_DATETIMELITERAL']._serialized_start=1109
+ _globals['_DATETIMELITERAL']._serialized_end=1282
+ _globals['_MAKESTRUCT']._serialized_start=1285
+ _globals['_MAKESTRUCT']._serialized_end=1482
+ _globals['_MAKESTRUCT_FIELDSENTRY']._serialized_start=1412
+ _globals['_MAKESTRUCT_FIELDSENTRY']._serialized_end=1482
+ _globals['_JSONLITERAL']._serialized_start=1484
+ _globals['_JSONLITERAL']._serialized_end=1560
+ _globals['_REF']._serialized_start=1562
+ _globals['_REF']._serialized_end=1581
+ _globals['_UNARY']._serialized_start=1583
+ _globals['_UNARY']._serialized_end=1672
+ _globals['_BINARY']._serialized_start=1674
+ _globals['_BINARY']._serialized_end=1799
+ _globals['_CASE']._serialized_start=1801
+ _globals['_CASE']._serialized_end=1899
+ _globals['_WHENTHEN']._serialized_start=1901
+ _globals['_WHENTHEN']._serialized_end=1989
+ _globals['_ISNULL']._serialized_start=1991
+ _globals['_ISNULL']._serialized_end=2041
+ _globals['_FILLNULL']._serialized_start=2043
+ _globals['_FILLNULL']._serialized_end=2134
+ _globals['_LISTOP']._serialized_start=2137
+ _globals['_LISTOP']._serialized_end=2684
+ _globals['_LISTFILTER']._serialized_start=2686
+ _globals['_LISTFILTER']._serialized_end=2755
+ _globals['_LISTMAP']._serialized_start=2757
+ _globals['_LISTMAP']._serialized_end=2822
+ _globals['_LISTSUM']._serialized_start=2824
+ _globals['_LISTSUM']._serialized_end=2833
+ _globals['_LISTMIN']._serialized_start=2835
+ _globals['_LISTMIN']._serialized_end=2844
+ _globals['_LISTMEAN']._serialized_start=2846
+ _globals['_LISTMEAN']._serialized_end=2856
+ _globals['_LISTMAX']._serialized_start=2858
+ _globals['_LISTMAX']._serialized_end=2867
+ _globals['_LISTALL']._serialized_start=2869
+ _globals['_LISTALL']._serialized_end=2878
+ _globals['_LISTANY']._serialized_start=2880
+ _globals['_LISTANY']._serialized_end=2889
+ _globals['_LEN']._serialized_start=2891
+ _globals['_LEN']._serialized_end=2896
+ _globals['_HASNULL']._serialized_start=2898
+ _globals['_HASNULL']._serialized_end=2907
+ _globals['_CONTAINS']._serialized_start=2909
+ _globals['_CONTAINS']._serialized_end=2961
+ _globals['_LISTFN']._serialized_start=2963
+ _globals['_LISTFN']._serialized_end=3049
+ _globals['_MATHOP']._serialized_start=3052
+ _globals['_MATHOP']._serialized_end=3237
+ _globals['_ROUND']._serialized_start=3239
+ _globals['_ROUND']._serialized_end=3265
+ _globals['_ABS']._serialized_start=3267
+ _globals['_ABS']._serialized_end=3272
+ _globals['_CEIL']._serialized_start=3274
+ _globals['_CEIL']._serialized_end=3280
+ _globals['_FLOOR']._serialized_start=3282
+ _globals['_FLOOR']._serialized_end=3289
+ _globals['_MATHFN']._serialized_start=3291
+ _globals['_MATHFN']._serialized_end=3380
+ _globals['_STRUCTOP']._serialized_start=3382
+ _globals['_STRUCTOP']._serialized_end=3420
+ _globals['_STRUCTFN']._serialized_start=3422
+ _globals['_STRUCTFN']._serialized_end=3514
+ _globals['_DICTGET']._serialized_start=3516
+ _globals['_DICTGET']._serialized_end=3613
+ _globals['_DICTOP']._serialized_start=3616
+ _globals['_DICTOP']._serialized_end=3766
+ _globals['_DICTFN']._serialized_start=3768
+ _globals['_DICTFN']._serialized_end=3854
+ _globals['_STRINGOP']._serialized_start=3857
+ _globals['_STRINGOP']._serialized_end=4310
+ _globals['_TIMEZONE']._serialized_start=4312
+ _globals['_TIMEZONE']._serialized_end=4340
+ _globals['_JSONDECODE']._serialized_start=4342
+ _globals['_JSONDECODE']._serialized_end=4400
+ _globals['_STRPTIME']._serialized_start=4402
+ _globals['_STRPTIME']._serialized_end=4475
+ _globals['_TOLOWER']._serialized_start=4477
+ _globals['_TOLOWER']._serialized_end=4486
+ _globals['_TOUPPER']._serialized_start=4488
+ _globals['_TOUPPER']._serialized_end=4497
+ _globals['_STARTSWITH']._serialized_start=4499
+ _globals['_STARTSWITH']._serialized_end=4549
+ _globals['_ENDSWITH']._serialized_start=4551
+ _globals['_ENDSWITH']._serialized_end=4599
+ _globals['_CONCAT']._serialized_start=4601
+ _globals['_CONCAT']._serialized_end=4649
+ _globals['_STRINGFN']._serialized_start=4651
+ _globals['_STRINGFN']._serialized_end=4743
+ _globals['_DATETIMEFN']._serialized_start=4745
+ _globals['_DATETIMEFN']._serialized_end=4843
+ _globals['_DATETIMEOP']._serialized_start=4846
+ _globals['_DATETIMEOP']._serialized_end=5056
+ _globals['_SINCE']._serialized_start=5058
+ _globals['_SINCE']._serialized_end=5148
+ _globals['_SINCEEPOCH']._serialized_start=5150
+ _globals['_SINCEEPOCH']._serialized_end=5205
+ _globals['_STRFTIME']._serialized_start=5207
+ _globals['_STRFTIME']._serialized_end=5280
+ _globals['_PART']._serialized_start=5282
+ _globals['_PART']._serialized_end=5378
# @@protoc_insertion_point(module_scope)
diff --git a/fennel/gen/expr_pb2.pyi b/fennel/gen/expr_pb2.pyi
index 9c2911dd..9d6c2d73 100644
--- a/fennel/gen/expr_pb2.pyi
+++ b/fennel/gen/expr_pb2.pyi
@@ -110,6 +110,23 @@ MICROSECOND: TimeUnit.ValueType # 8
MILLISECOND: TimeUnit.ValueType # 9
global___TimeUnit = TimeUnit
+@typing_extensions.final
+class EvalContext(google.protobuf.message.Message):
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
+
+ NOW_COL_NAME_FIELD_NUMBER: builtins.int
+ now_col_name: builtins.str
+ def __init__(
+ self,
+ *,
+ now_col_name: builtins.str | None = ...,
+ ) -> None: ...
+ def HasField(self, field_name: typing_extensions.Literal["_now_col_name", b"_now_col_name", "now_col_name", b"now_col_name"]) -> builtins.bool: ...
+ def ClearField(self, field_name: typing_extensions.Literal["_now_col_name", b"_now_col_name", "now_col_name", b"now_col_name"]) -> None: ...
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_now_col_name", b"_now_col_name"]) -> typing_extensions.Literal["now_col_name"] | None: ...
+
+global___EvalContext = EvalContext
+
@typing_extensions.final
class Expr(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
@@ -131,6 +148,7 @@ class Expr(google.protobuf.message.Message):
MAKE_STRUCT_FIELD_NUMBER: builtins.int
FROM_EPOCH_FIELD_NUMBER: builtins.int
VAR_FIELD_NUMBER: builtins.int
+ NOW_FIELD_NUMBER: builtins.int
@property
def ref(self) -> global___Ref: ...
@property
@@ -166,6 +184,8 @@ class Expr(google.protobuf.message.Message):
def from_epoch(self) -> global___FromEpoch: ...
@property
def var(self) -> global___Var: ...
+ @property
+ def now(self) -> global___Now: ...
def __init__(
self,
*,
@@ -186,13 +206,24 @@ class Expr(google.protobuf.message.Message):
make_struct: global___MakeStruct | None = ...,
from_epoch: global___FromEpoch | None = ...,
var: global___Var | None = ...,
+ now: global___Now | None = ...,
) -> None: ...
- def HasField(self, field_name: typing_extensions.Literal["binary", b"binary", "case", b"case", "datetime_fn", b"datetime_fn", "datetime_literal", b"datetime_literal", "dict_fn", b"dict_fn", "fillnull", b"fillnull", "from_epoch", b"from_epoch", "isnull", b"isnull", "json_literal", b"json_literal", "list_fn", b"list_fn", "make_struct", b"make_struct", "math_fn", b"math_fn", "node", b"node", "ref", b"ref", "string_fn", b"string_fn", "struct_fn", b"struct_fn", "unary", b"unary", "var", b"var"]) -> builtins.bool: ...
- def ClearField(self, field_name: typing_extensions.Literal["binary", b"binary", "case", b"case", "datetime_fn", b"datetime_fn", "datetime_literal", b"datetime_literal", "dict_fn", b"dict_fn", "fillnull", b"fillnull", "from_epoch", b"from_epoch", "isnull", b"isnull", "json_literal", b"json_literal", "list_fn", b"list_fn", "make_struct", b"make_struct", "math_fn", b"math_fn", "node", b"node", "ref", b"ref", "string_fn", b"string_fn", "struct_fn", b"struct_fn", "unary", b"unary", "var", b"var"]) -> None: ...
- def WhichOneof(self, oneof_group: typing_extensions.Literal["node", b"node"]) -> typing_extensions.Literal["ref", "json_literal", "unary", "case", "binary", "isnull", "fillnull", "list_fn", "math_fn", "struct_fn", "dict_fn", "string_fn", "datetime_fn", "datetime_literal", "make_struct", "from_epoch", "var"] | None: ...
+ def HasField(self, field_name: typing_extensions.Literal["binary", b"binary", "case", b"case", "datetime_fn", b"datetime_fn", "datetime_literal", b"datetime_literal", "dict_fn", b"dict_fn", "fillnull", b"fillnull", "from_epoch", b"from_epoch", "isnull", b"isnull", "json_literal", b"json_literal", "list_fn", b"list_fn", "make_struct", b"make_struct", "math_fn", b"math_fn", "node", b"node", "now", b"now", "ref", b"ref", "string_fn", b"string_fn", "struct_fn", b"struct_fn", "unary", b"unary", "var", b"var"]) -> builtins.bool: ...
+ def ClearField(self, field_name: typing_extensions.Literal["binary", b"binary", "case", b"case", "datetime_fn", b"datetime_fn", "datetime_literal", b"datetime_literal", "dict_fn", b"dict_fn", "fillnull", b"fillnull", "from_epoch", b"from_epoch", "isnull", b"isnull", "json_literal", b"json_literal", "list_fn", b"list_fn", "make_struct", b"make_struct", "math_fn", b"math_fn", "node", b"node", "now", b"now", "ref", b"ref", "string_fn", b"string_fn", "struct_fn", b"struct_fn", "unary", b"unary", "var", b"var"]) -> None: ...
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["node", b"node"]) -> typing_extensions.Literal["ref", "json_literal", "unary", "case", "binary", "isnull", "fillnull", "list_fn", "math_fn", "struct_fn", "dict_fn", "string_fn", "datetime_fn", "datetime_literal", "make_struct", "from_epoch", "var", "now"] | None: ...
global___Expr = Expr
+@typing_extensions.final
+class Now(google.protobuf.message.Message):
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
+
+ def __init__(
+ self,
+ ) -> None: ...
+
+global___Now = Now
+
@typing_extensions.final
class Var(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
diff --git a/fennel/testing/query_engine.py b/fennel/testing/query_engine.py
index 03ab7ad6..2cf6ce74 100644
--- a/fennel/testing/query_engine.py
+++ b/fennel/testing/query_engine.py
@@ -8,6 +8,7 @@
import fennel.datasets.datasets
import fennel.gen.schema_pb2 as schema_proto
+from fennel.expr.expr import EvalContext
from fennel.featuresets import Extractor, Feature, Featureset, is_valid_feature
from fennel.gen.featureset_pb2 import (
ExtractorType as ProtoExtractorType,
@@ -22,6 +23,8 @@
cast_df_to_arrow_dtype,
)
+NOW_COL_NAME = "__ts_col_name__"
+
class QueryEngine:
"""
@@ -170,7 +173,7 @@ def run_extractors(
if extractor.extractor_type == ProtoExtractorType.EXPR:
output = self._compute_expr_extractor(
- extractor, intermediate_data
+ extractor, timestamps.copy(), intermediate_data
)
self._check_schema_exceptions(output, dsschema, extractor.name)
continue
@@ -356,6 +359,7 @@ def _check_schema_exceptions(
def _compute_expr_extractor(
self,
extractor: Extractor,
+ timestamps: pd.Series,
intermediate_data: Dict[str, pd.Series],
) -> pd.Series:
if len(extractor.outputs) != 1:
@@ -371,8 +375,13 @@ def _compute_expr_extractor(
}
expr = extractor.expr
input_schema = {f.name: f.dtype for f in extractor.inputs}
+ input_schema[NOW_COL_NAME] = datetime
df = pd.DataFrame(input_features)
- res = expr.eval(df, input_schema) # type: ignore
+ df[NOW_COL_NAME] = timestamps
+ res = expr.eval(df, input_schema, context=EvalContext(now_col_name=NOW_COL_NAME)) # type: ignore
+ output_dtype = expr.typeof(input_schema) # type: ignore
+ # Cast to correct arrow dtype after evaluating the expression
+ res = cast_col_to_arrow_dtype(res, get_datatype(output_dtype))
res.name = extractor.fqn_output_features()[0]
intermediate_data[extractor.fqn_output_features()[0]] = res
return res
diff --git a/poetry.lock b/poetry.lock
index c6f7ad6f..ec2e20b2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -766,48 +766,48 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc
[[package]]
name = "fennel-data-lib"
-version = "0.1.21"
+version = "0.1.22"
description = ""
optional = false
python-versions = ">=3.8"
files = [
- {file = "fennel_data_lib-0.1.21-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be633ae9295c346a72db2e667cfba6719621ea2aee6f7c6767684b1ad3e931cc"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80512a481da6c459af43637d40093d64841068f7b33e7b53b37c495249b675b1"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24118b8644dd4754630776ed0b9f08510efd3e0e81e59f10e3776c3565eda223"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7379280c4d9d1651f91dedc828ae5f38f470b6e00c9386d0bc85ad233858f26"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f3940eff028d9615cb178bb11d406d43f420554dd526f19a1d2d24ecb2386a4"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e70495692d523ee6594d8f86619954bdfea9c7a2fa531aede46034d96b17ad8"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:03f5282d6920d10ccb15d12d29cd83ed1b62c3cc9d1a8bbf361a49bc96b951a6"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69a496eece814baac05b4d7b4cac35e967a5fb04ce9fef17f8b073e032e5dfba"},
- {file = "fennel_data_lib-0.1.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c1ab35f93e7f1a8e8c3cf5daebbf28025f92f9797b0cd7677e0f9a77bae73514"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:35597a4f26ed6b2ebd3154b4f6dd346148085328a6dfdbad4a1614b253d444c8"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf53dcf5d79360120b8495a57477b4885b1fbd0fcf383dcde12c9fa19d127f9"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:014059ddf9f8a1110717bdf7aae449afb132bc6889979a2d7490f9d39ada46f1"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a056b978667e5d9f25403ba5f85d7bfcd055f5655214f05741fbb90dee3359b"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79f3d53007c445bbb535f131fcf31c575863fcf475ea48f313cb8157d77443f3"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd55011aa7ce01b585a3866baa6bef09557a16401cf9c4421ed69347e3646a9d"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:a44af4e502a8ff4c8bdc9952e8c0a586e6c468e4b417d00d61154f695f142ded"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fb6ea76adde7ec3b9f6d393e675dfffe11baed6e3ca88edef6b57ca3158a244f"},
- {file = "fennel_data_lib-0.1.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6801459bdbc546bf2adf76b32bc74675c5cc27b4111bbc2e3b2e34bda0ad003e"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8e06cfc288cf1f031082a157dcd8e96309e01178420660087959439b9bfff0f6"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69378c639d09b994d33b5037c28395b4babfa7868da6acaddc566760a5bae1d8"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4b6cad57e9f09a1738f3bdd03f9d493f5f80f827f8cab56c655f02281418d97"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:deae17ccb4cd29906dd26d75f79581f6e8d1e19e7eaed2ed30e401fcb98bc6ef"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e022d096154b32dca0ad0a81028a64571b3ea13fa171ca02bbd65fa904f7c14"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71af0937e8f8d1edb46e0e9ed8d95ce989b4ca6763922f1776a32cd232aa789b"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:b432ea48f3f5ae82b9de81ef27388c52a4f09324b552e0c6e5f462c650bde537"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:de34b96a2aca7e62742ff2cdb747d9500b341b4cd712003638761d348901491e"},
- {file = "fennel_data_lib-0.1.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af2355aeb6e7028ae2796298f0c099095ea35a2240545c788a72618b4a590967"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d80cea61199df9d8fab39467458f5523f2bd40684b160c4501afd7b13784baa0"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b2ace5de48746cdd5aa51c002664e5bf7fc850a057b1323f4cc41d46cb0582"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ccf2d82408c0e60f8c709ae858e9b96530150817e265f4a5505c5d9f6c307ed"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bcd094536ad87da105fff5ceea2897d616683a0e31c588c879b366926df4a51"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d04ab4bd719a6e3983f0f6885c2d761bdde71e5eb7dd6990ee206c4da8e0be8b"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8b6f61838717217fb1d332d887eebebf24fb817423d3e10dfd4cb3c48a5a777"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:9f88f74518e81605ebaebbdd933cbb1bb68f80d3e642b3dccb7ca5ddccfb00bd"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5fa3f086185eb9bdb6416c0ad1898f59c8a06fb6b49f43eb03233ca102f55faf"},
- {file = "fennel_data_lib-0.1.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d24a04eef8f83ebcdfa795a623dc007bce687fca48fa53f7357e5a20fdeed152"},
- {file = "fennel_data_lib-0.1.21.tar.gz", hash = "sha256:53577e8a4affc227e1a972423fb6b2603b4e87c2c3b5d1a9e1b8eaccd28ebe4d"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b7775ef71c464230741a025e3493b11be3da88930368f399dcab6ea190b7de51"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cdc29ecf35bbbd3119af11c3fc7a8b8f8ee76fe5acd233a210a97fc291580ee"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9d11e4d7a3cb2e70b05b6a3e65c149448547158f80558f336f40f062a00bf2d5"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ed37642f62f7e3bfe2b1adc0a8b6d875123e813a110e58a1892de7f8525b841"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8224b227c6f60b1e7aaa7dada4603b7257424b62c9fbc5a844efb086988fda29"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25a088323472094b778bba23e4caac112a2218f6adde724642d15e4bcd5cef6"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:89350b85476130eedcefbaa362664dd8264735becdcd7f53ff9daec358d63119"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:04298328368436585df47f5323f7b234b1ba1657e4203062e604cc95675ec017"},
+ {file = "fennel_data_lib-0.1.22-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f317a820c4c67690cf113478eb1bfe86e7000b7edb5fb88b7233926b513de50"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d0c5f818e26c2ef3925f1da8bc5e9f5e56f9e179d13cf78338068991395fb4e5"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22426bb9e92ecdf9cdc69b2c1c476fdc2f92e7092590c4d24227f5f2d458892f"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8a52eb2561b709eaee3de80cb9ee2ee6ff1de5c51e6a8a15eebf92a1957b241"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:617107f064042c4487f29550d683fc5375e3b14a803d9a2c0e8482e0cc2f4238"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49bba1d2b9ec285ec682f3f251545498ad049a91c1d4407d8d48c5d922f9fe6e"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa37f7a77499720eb30fcb3179aaeda6b3e79ecce7009bc19abdb154499999d1"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:94bcf382186d5e3e5aa9f67a4280c5fc6c575bf9b5e08e0c576e7534272d48e7"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29f7b374576db9bd104d15b1335f5f7db08434aa7f10bcae7be6d28894849c0c"},
+ {file = "fennel_data_lib-0.1.22-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c9a622d83200c46d19ee9a5db7511a9fc47bfbfade447b55933b30b6b98a74"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:dd76f9803357368c8ae1ca8d3af0f313f74d31cdf2e94240d67b8704a1a92cb0"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2639314b899eadfa2beb21c070bde1d995a02a62b6723544d5c751b5b60b99a8"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:611b3b118befd4b6b8a6628d7d381e369641fd88fa0728c366e45a90c28c7d9b"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68d6584f937989d0b3c1587ce08576190176ad9e8b18b56562398f945c8d6bb9"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:feb3d8cf8ae6acfa27663e80ce8f71c32155bbbf8f6624621d8ff2d424794525"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:548a952665203ff9c85285d5eefa8cfb10a878f0d3ef7d2e631d6597649f5824"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:afe170c2e4c4ea8ec0e1f9a7a74a406fefbaa8eb331ab2274f06e7a3141c4bcc"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84dc38d1fd73ef26e1fe433d85a4f0a6411b3dde28b66d4553e84b99b5775f09"},
+ {file = "fennel_data_lib-0.1.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:064f92604c56624a55f7d2edc930206ed56725570584ad618a3df4a33dd7a0a9"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:911a55b9739865f10d29a496023fff3e4ef1d4293656ccef394946d6eb2d65e1"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf266cfe95fb3fa534d303b0ad4a315f407f4e0f241d1e608cc1abd3e647b06"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a63e5d8bd9b2ca9521dc40152b2190e1bb83b346a531f9d731e32e8ee5700081"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de58d19ae2a5079c0c698350a5f83d468bde7a379a5265c108138a385a00eb94"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9a43626e495425ce764385cc5d58a5ac9ee4866eca965924a6d9f11508469b1"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a623c087c174bda0857aef638c18d130873c897c6935fc245d37e34f70f1b0c3"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:703a389c4b748862b9bb71b22a3bd48aa37d0aecdd0c3c439881c818c9690b30"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9118c48de75a26be45c5fd3ad52fb61048d4cbafd7b83599d528fe8b526e0e1a"},
+ {file = "fennel_data_lib-0.1.22-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2aedaec70a3abbf448a1a250e2b96ee98032da2040126c2116cb1ef3ed5882fa"},
+ {file = "fennel_data_lib-0.1.22.tar.gz", hash = "sha256:7e27d220063fef16a284e28a890bbcbd7f3d2aea738ad6e495f0e9d6b3fba961"},
]
[[package]]
@@ -3725,4 +3725,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
-content-hash = "a1cef875ee95243b749253dbf87ceff13d3924bb9267ac0f6d47631a92b45076"
+content-hash = "9f3156d7967bc35926ea8e6efc96ec0a8c720947804b7a4b5a76c82f67e36ee0"
diff --git a/pyproject.toml b/pyproject.toml
index 5936c8e1..5361c2fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "fennel-ai"
-version = "1.5.32"
+version = "1.5.33"
description = "The modern realtime feature engineering platform"
authors = ["Fennel AI "]
packages = [{ include = "fennel" }]
@@ -20,7 +20,7 @@ pytest = "7.1.3"
pytest-rerunfailures = "^13.0"
sortedcontainers = "^2.4.0"
typing-extensions = "^4.12.0"
-fennel-data-lib = "0.1.21"
+fennel-data-lib = "0.1.22"
pyarrow = "^14.0.2"
[tool.poetry.dev-dependencies]
@@ -49,14 +49,14 @@ build-backend = "poetry.core.masonry.api"
# For local development, we use maturin to build the rust library
-# [build-system]
-# requires = ["maturin", "setuptools>=42", "wheel"]
-# build-backend = "maturin"
-
-# [tool.maturin]
-# name = "fennel_data_lib"
-# sdist-directory = "python_package"
-# manifest-path = "../server/fennel_data_lib/Cargo.toml"
+#[build-system]
+#requires = ["maturin", "setuptools>=42", "wheel"]
+#build-backend = "maturin"
+#
+#[tool.maturin]
+#name = "fennel_data_lib"
+#sdist-directory = "python_package"
+#manifest-path = "../server/fennel_data_lib/Cargo.toml"
# inspired from - https://github.com/pypa/pip/blob/main/pyproject.toml