From 0b3fa13373ba12e912e8b4b1a0dc5ea18cbe8fc9 Mon Sep 17 00:00:00 2001 From: Danny C Date: Mon, 4 Sep 2023 18:56:20 -0700 Subject: [PATCH 01/19] ci: Fix ci breakage from old pip version being restored from pip cache Signed-off-by: Danny C --- .github/fork_workflows/fork_pr_integration_tests_aws.yml | 6 +++--- .github/fork_workflows/fork_pr_integration_tests_gcp.yml | 6 +++--- .../fork_workflows/fork_pr_integration_tests_snowflake.yml | 7 +++---- .github/workflows/java_master_only.yml | 7 +++---- .github/workflows/java_pr.yml | 6 +++--- .github/workflows/linter.yml | 6 +++--- .github/workflows/master_only.yml | 6 +++--- .github/workflows/nightly-ci.yml | 6 +++--- .github/workflows/pr_integration_tests.yml | 6 +++--- .github/workflows/pr_local_integration_tests.yml | 6 +++--- .github/workflows/unit_tests.yml | 6 +++--- 11 files changed, 33 insertions(+), 35 deletions(-) diff --git a/.github/fork_workflows/fork_pr_integration_tests_aws.yml b/.github/fork_workflows/fork_pr_integration_tests_aws.yml index ab5130c704..7261833ae6 100644 --- a/.github/fork_workflows/fork_pr_integration_tests_aws.yml +++ b/.github/fork_workflows/fork_pr_integration_tests_aws.yml @@ -109,9 +109,6 @@ jobs: aws-region: us-west-2 - name: Use AWS CLI run: aws sts get-caller-identity - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<22.3" - name: Get pip cache dir id: pip-cache run: | @@ -126,6 +123,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<22.3" - name: Install pip-tools run: pip install pip-tools - name: Install apache-arrow on ubuntu diff --git a/.github/fork_workflows/fork_pr_integration_tests_gcp.yml b/.github/fork_workflows/fork_pr_integration_tests_gcp.yml index 4b74c0ab09..1a05c068b5 100644 --- a/.github/fork_workflows/fork_pr_integration_tests_gcp.yml +++ b/.github/fork_workflows/fork_pr_integration_tests_gcp.yml @@ -53,9 +53,6 @@ jobs: project_id: ${{ secrets.GCP_PROJECT_ID }} - name: Use gcloud CLI run: gcloud info - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -70,6 +67,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install apache-arrow on ubuntu diff --git a/.github/fork_workflows/fork_pr_integration_tests_snowflake.yml b/.github/fork_workflows/fork_pr_integration_tests_snowflake.yml index cef970ab16..9327f5c729 100644 --- a/.github/fork_workflows/fork_pr_integration_tests_snowflake.yml +++ b/.github/fork_workflows/fork_pr_integration_tests_snowflake.yml @@ -43,10 +43,6 @@ jobs: uses: actions/setup-go@v2 with: go-version: 1.18.0 - - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -61,6 +57,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install apache-arrow on ubuntu diff --git a/.github/workflows/java_master_only.yml b/.github/workflows/java_master_only.yml index b15ddddc5d..d82f69dd3c 100644 --- a/.github/workflows/java_master_only.yml +++ b/.github/workflows/java_master_only.yml @@ -112,9 +112,6 @@ jobs: with: python-version: 3.8 architecture: x64 - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -129,9 +126,11 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - - name: Install Python dependencies run: make install-python-ci-dependencies - uses: actions/cache@v2 diff --git a/.github/workflows/java_pr.yml b/.github/workflows/java_pr.yml index a927587d7d..83c52e7dbf 100644 --- a/.github/workflows/java_pr.yml +++ b/.github/workflows/java_pr.yml @@ -148,9 +148,6 @@ jobs: with: python-version: 3.8 architecture: x64 - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -165,6 +162,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install Python dependencies diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c2112ea303..a4a42a11ed 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -15,9 +15,6 @@ jobs: with: python-version: "3.8" architecture: x64 - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -32,6 +29,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install dependencies diff --git a/.github/workflows/master_only.yml b/.github/workflows/master_only.yml index a7d412fd4a..580ea3171b 100644 --- a/.github/workflows/master_only.yml +++ b/.github/workflows/master_only.yml @@ -106,9 +106,6 @@ jobs: aws-region: us-west-2 - name: Use AWS CLI run: aws sts get-caller-identity - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -123,6 +120,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install dependencies diff --git a/.github/workflows/nightly-ci.yml b/.github/workflows/nightly-ci.yml index 03078c0a78..0e1df81262 100644 --- a/.github/workflows/nightly-ci.yml +++ b/.github/workflows/nightly-ci.yml @@ -173,9 +173,6 @@ jobs: aws-region: us-west-2 - name: Use AWS CLI run: aws sts get-caller-identity - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -190,6 +187,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install apache-arrow on ubuntu diff --git a/.github/workflows/pr_integration_tests.yml b/.github/workflows/pr_integration_tests.yml index ed02636c99..73344ec2dd 100644 --- a/.github/workflows/pr_integration_tests.yml +++ b/.github/workflows/pr_integration_tests.yml @@ -133,9 +133,6 @@ jobs: aws-region: us-west-2 - name: Use AWS CLI run: aws sts get-caller-identity - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -150,6 +147,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install dependencies diff --git a/.github/workflows/pr_local_integration_tests.yml b/.github/workflows/pr_local_integration_tests.yml index 1a2c0790d1..111a9b51a9 100644 --- a/.github/workflows/pr_local_integration_tests.yml +++ b/.github/workflows/pr_local_integration_tests.yml @@ -38,9 +38,6 @@ jobs: with: python-version: ${{ matrix.python-version }} architecture: x64 - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -55,6 +52,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install dependencies diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 538a581979..f03cd33346 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -36,9 +36,6 @@ jobs: run: | brew install zlib ln -sv $(brew --prefix zlib)/lib/libz.dylib $(brew --prefix)/lib/libzlib.dylib - - name: Upgrade pip version - run: | - pip install --upgrade "pip>=21.3.1,<23.2" - name: Get pip cache dir id: pip-cache run: | @@ -53,6 +50,9 @@ jobs: key: ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles(format('**/py{0}-ci-requirements.txt', env.PYTHON)) }} restore-keys: | ${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-pip- + - name: Upgrade pip version + run: | + pip install --upgrade "pip>=21.3.1,<23.2" - name: Install pip-tools run: pip install pip-tools - name: Install dependencies From d7041f4cce813d349e9016da55d65a65c1ec2355 Mon Sep 17 00:00:00 2001 From: Mark Snidal Date: Tue, 5 Sep 2023 02:36:03 -0400 Subject: [PATCH 02/19] fix: Handle unknown postgres source types gracefully (#3634) Handle more pg types gracefully Signed-off-by: Mark Snidal --- sdk/python/feast/type_map.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index df85346283..52e2890750 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -873,13 +873,26 @@ def feast_value_type_to_pa( def pg_type_code_to_pg_type(code: int) -> str: - return { + """ Map the postgres type code a Feast type string + + Rather than raise an exception on an unknown type, we return the + string representation of the type code. This way rather than raising + an exception on unknown types, Feast will just skip the problem columns. + + Note that json and jsonb are not supported but this shows up in the + log as a warning. Since postgres allows custom types we return an unknown for those cases. + + See: https://jdbc.postgresql.org/documentation/publicapi/index.html?constant-values.html + """ + PG_TYPE_MAP = { 16: "boolean", 17: "bytea", 20: "bigint", 21: "smallint", 23: "integer", 25: "text", + 114: "json", + 199: "json[]", 700: "real", 701: "double precision", 1000: "boolean[]", @@ -905,7 +918,11 @@ def pg_type_code_to_pg_type(code: int) -> str: 1700: "numeric", 2950: "uuid", 2951: "uuid[]", - }[code] + 3802: "jsonb", + 3807: "jsonb[]", + } + + return PG_TYPE_MAP.get(code, "unknown") def pg_type_code_to_arrow(code: int) -> str: From 1a1849cf3c6224f4c332fdf7111baed8c5725c25 Mon Sep 17 00:00:00 2001 From: Danny C Date: Tue, 5 Sep 2023 00:15:59 -0700 Subject: [PATCH 03/19] chore: Fix linting issue Signed-off-by: Danny C --- sdk/python/feast/type_map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 52e2890750..d246fb4054 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -873,10 +873,10 @@ def feast_value_type_to_pa( def pg_type_code_to_pg_type(code: int) -> str: - """ Map the postgres type code a Feast type string + """Map the postgres type code a Feast type string Rather than raise an exception on an unknown type, we return the - string representation of the type code. This way rather than raising + string representation of the type code. This way rather than raising an exception on unknown types, Feast will just skip the problem columns. Note that json and jsonb are not supported but this shows up in the From 377758b48f89fb7f1b99856d09d3b383a2c80882 Mon Sep 17 00:00:00 2001 From: Ren Date: Tue, 5 Sep 2023 03:18:55 -0400 Subject: [PATCH 04/19] docs: Add Dragonfly as an online-store option to Feast Readme and docs (#3647) * Add dragonfly to feast Readme and docs Signed-off-by: Danny C * Remove new lines Signed-off-by: Danny C * Remove more newlines Signed-off-by: Danny C * rebase and fix using template generation Signed-off-by: Danny C --------- Signed-off-by: Danny C Co-authored-by: Danny C --- README.md | 1 + docs/reference/online-stores/dragonfly.md | 90 +++++++++++++++++++++++ docs/roadmap.md | 1 + 3 files changed, 92 insertions(+) create mode 100644 docs/reference/online-stores/dragonfly.md diff --git a/README.md b/README.md index 1152aa060e..f2c9348b1c 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ The list below contains the functionality that contributors are planning to deve * [x] [Datastore](https://docs.feast.dev/reference/online-stores/datastore) * [x] [Bigtable](https://docs.feast.dev/reference/online-stores/bigtable) * [x] [SQLite](https://docs.feast.dev/reference/online-stores/sqlite) + * [x] [Dragonfly](https://docs.feast.dev/reference/online-stores/dragonfly) * [x] [Azure Cache for Redis (community plugin)](https://github.com/Azure/feast-azure) * [x] [Postgres (contrib plugin)](https://docs.feast.dev/reference/online-stores/postgres) * [x] [Cassandra / AstraDB (contrib plugin)](https://docs.feast.dev/reference/online-stores/cassandra) diff --git a/docs/reference/online-stores/dragonfly.md b/docs/reference/online-stores/dragonfly.md new file mode 100644 index 0000000000..bcd814ecc4 --- /dev/null +++ b/docs/reference/online-stores/dragonfly.md @@ -0,0 +1,90 @@ +# Dragonfly online store + +## Description + +[Dragonfly](https://github.com/dragonflydb/dragonfly) is a modern in-memory datastore that implements novel algorithms and data structures on top of a multi-threaded, share-nothing architecture. Thanks to its API compatibility, Dragonfly can act as a drop-in replacement for Redis. Due to Dragonfly's hardware efficiency, you can run a single node instance on a small 8GB instance or scale vertically to large 768GB machines with 64 cores. This greatly reduces infrastructure costs as well as architectural complexity. + +Similar to Redis, Dragonfly can be used as an online feature store for Feast. + +## Using Dragonfly as a drop-in Feast online store instead of Redis + +Make sure you have Python and `pip` installed. + +Install the Feast SDK and CLI + +`pip install feast` + +In order to use Dragonfly as the online store, you'll need to install the redis extra: + +`pip install 'feast[redis]'` + +### 1. Create a feature repository + +Bootstrap a new feature repository: + +``` +feast init feast_dragonfly +cd feast_dragonfly/feature_repo +``` + +Update `feature_repo/feature_store.yaml` with the below contents: + +``` +project: feast_dragonfly +registry: data/registry.db +provider: local +online_store: +type: redis +connection_string: "localhost:6379" +``` + +### 2. Start Dragonfly + +There are several options available to get Dragonfly up and running quickly. We will be using Docker for this tutorial. + +`docker run --network=host --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonfly` + +### 3. Register feature definitions and deploy your feature store + +`feast apply` + +The `apply` command scans python files in the current directory (`example_repo.py` in this case) for feature view/entity definitions, registers the objects, and deploys infrastructure. +You should see the following output: + +``` +.... +Created entity driver +Created feature view driver_hourly_stats_fresh +Created feature view driver_hourly_stats +Created on demand feature view transformed_conv_rate +Created on demand feature view transformed_conv_rate_fresh +Created feature service driver_activity_v1 +Created feature service driver_activity_v3 +Created feature service driver_activity_v2 +``` + +## Functionality Matrix + +The set of functionality supported by online stores is described in detail [here](overview.md#functionality). +Below is a matrix indicating which functionality is supported by the Redis online store. + +| | Redis | +| :-------------------------------------------------------- | :---- | +| write feature values to the online store | yes | +| read feature values from the online store | yes | +| update infrastructure (e.g. tables) in the online store | yes | +| teardown infrastructure (e.g. tables) in the online store | yes | +| generate a plan of infrastructure changes | no | +| support for on-demand transforms | yes | +| readable by Python SDK | yes | +| readable by Java | yes | +| readable by Go | yes | +| support for entityless feature views | yes | +| support for concurrent writing to the same key | yes | +| support for ttl (time to live) at retrieval | yes | +| support for deleting expired data | yes | +| collocated by feature view | no | +| collocated by feature service | no | +| collocated by entity key | yes | + +To compare this set of functionality against other online stores, please see the full [functionality matrix](overview.md#functionality-matrix). diff --git a/docs/roadmap.md b/docs/roadmap.md index e75e58849b..d5dc88005b 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -33,6 +33,7 @@ The list below contains the functionality that contributors are planning to deve * [x] [Datastore](https://docs.feast.dev/reference/online-stores/datastore) * [x] [Bigtable](https://docs.feast.dev/reference/online-stores/bigtable) * [x] [SQLite](https://docs.feast.dev/reference/online-stores/sqlite) + * [x] [Dragonfly](https://docs.feast.dev/reference/online-stores/dragonfly) * [x] [Azure Cache for Redis (community plugin)](https://github.com/Azure/feast-azure) * [x] [Postgres (contrib plugin)](https://docs.feast.dev/reference/online-stores/postgres) * [x] [Cassandra / AstraDB (contrib plugin)](https://docs.feast.dev/reference/online-stores/cassandra) From f2c59885e31f3f238dbd9c13cd1ba168e3233a9d Mon Sep 17 00:00:00 2001 From: crispin-ki <131684659+crispin-ki@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:50:47 +0100 Subject: [PATCH 05/19] fix: Remove unwanted excessive splitting of gcs path, so expected gcs parquet paths are returned from BigQueryRetrievalJob.to_remote_storage() (#3730) Remove unwanted excessive splitting of gcs path Signed-off-by: Crispin Logan --- sdk/python/feast/infra/offline_stores/bigquery.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/bigquery.py b/sdk/python/feast/infra/offline_stores/bigquery.py index 5dd8f61bce..5913b60f62 100644 --- a/sdk/python/feast/infra/offline_stores/bigquery.py +++ b/sdk/python/feast/infra/offline_stores/bigquery.py @@ -577,7 +577,6 @@ def to_remote_storage(self) -> List[str]: else: storage_client = StorageClient(project=self.client.project) bucket, prefix = self._gcs_path[len("gs://") :].split("/", 1) - prefix = prefix.rsplit("/", 1)[0] if prefix.startswith("/"): prefix = prefix[1:] From a3fcd1f369bdf07174b5ecf2a49ca9864cf145d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tokg=C3=B6z?= <56408993+mehmettokgoz@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:50:17 +0300 Subject: [PATCH 06/19] feat: Implement gRPC server to ingest streaming features (#3687) * Implemented gRPC server for ingesting streaming features. Signed-off-by: mehmettokgoz Signed-off-by: Danny C --- Makefile | 2 +- protos/feast/serving/GrpcServer.proto | 27 ++ sdk/python/feast/cli.py | 31 ++ sdk/python/feast/infra/contrib/grpc_server.py | 95 ++++ .../requirements/py3.10-ci-requirements.txt | 400 +++++++++-------- .../requirements/py3.10-requirements.txt | 105 +++-- .../requirements/py3.8-ci-requirements.txt | 386 +++++++++-------- .../requirements/py3.8-requirements.txt | 109 +++-- .../requirements/py3.9-ci-requirements.txt | 408 +++++++++--------- .../requirements/py3.9-requirements.txt | 109 +++-- setup.py | 23 +- 11 files changed, 999 insertions(+), 696 deletions(-) create mode 100644 protos/feast/serving/GrpcServer.proto create mode 100644 sdk/python/feast/infra/contrib/grpc_server.py diff --git a/Makefile b/Makefile index cf8a899ac6..4b85c0e448 100644 --- a/Makefile +++ b/Makefile @@ -353,7 +353,7 @@ kill-trino-locally: cd ${ROOT_DIR}; docker stop trino install-protoc-dependencies: - pip install --ignore-installed protobuf==4.23.4 grpcio-tools==1.47.0 mypy-protobuf==3.1.0 + pip install --ignore-installed protobuf==4.23.4 "grpcio-tools>=1.56.2,<2" mypy-protobuf==3.1.0 install-feast-ci-locally: pip install -e ".[ci]" diff --git a/protos/feast/serving/GrpcServer.proto b/protos/feast/serving/GrpcServer.proto new file mode 100644 index 0000000000..cd0274c5c7 --- /dev/null +++ b/protos/feast/serving/GrpcServer.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +message PushRequest { + map features = 1; + string stream_feature_view = 2; + bool allow_registry_cache = 3; + string to = 4; +} + +message PushResponse { + bool status = 1; +} + +message WriteToOnlineStoreRequest { + map features = 1; + string feature_view_name = 2; + bool allow_registry_cache = 3; +} + +message WriteToOnlineStoreResponse { + bool status = 1; +} + +service GrpcFeatureServer { + rpc Push (PushRequest) returns (PushResponse) {}; + rpc WriteToOnlineStore (WriteToOnlineStoreRequest) returns (WriteToOnlineStoreResponse); +} \ No newline at end of file diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 229cb99232..8c2c326b59 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -28,6 +28,7 @@ from feast.constants import DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT from feast.errors import FeastObjectNotFoundException, FeastProviderLoginError from feast.feature_view import FeatureView +from feast.infra.contrib.grpc_server import get_grpc_server from feast.on_demand_feature_view import OnDemandFeatureView from feast.repo_config import load_repo_config from feast.repo_operations import ( @@ -689,6 +690,36 @@ def serve_command( ) +@cli.command("listen") +@click.option( + "--address", + "-a", + type=click.STRING, + default="localhost:50051", + show_default=True, + help="Address of the gRPC server", +) +@click.option( + "--max_workers", + "-w", + type=click.INT, + default=10, + show_default=False, + help="The maximum number of threads that can be used to execute the gRPC calls", +) +@click.pass_context +def listen_command( + ctx: click.Context, + address: str, + max_workers: int, +): + """Start a gRPC feature server to ingest streaming features on given address""" + store = create_feature_store(ctx) + server = get_grpc_server(address, store, max_workers) + server.start() + server.wait_for_termination() + + @cli.command("serve_transformations") @click.option( "--port", diff --git a/sdk/python/feast/infra/contrib/grpc_server.py b/sdk/python/feast/infra/contrib/grpc_server.py new file mode 100644 index 0000000000..2017f1095b --- /dev/null +++ b/sdk/python/feast/infra/contrib/grpc_server.py @@ -0,0 +1,95 @@ +import logging +from concurrent import futures + +import grpc +import pandas as pd +from grpc_health.v1 import health, health_pb2_grpc + +from feast.data_source import PushMode +from feast.errors import PushSourceNotFoundException +from feast.feature_store import FeatureStore +from feast.protos.feast.serving.GrpcServer_pb2 import ( + PushResponse, + WriteToOnlineStoreResponse, +) +from feast.protos.feast.serving.GrpcServer_pb2_grpc import ( + GrpcFeatureServerServicer, + add_GrpcFeatureServerServicer_to_server, +) + + +def parse(features): + df = {} + for i in features.keys(): + df[i] = [features.get(i)] + return pd.DataFrame.from_dict(df) + + +class GrpcFeatureServer(GrpcFeatureServerServicer): + fs: FeatureStore + + def __init__(self, fs: FeatureStore): + self.fs = fs + super().__init__() + + def Push(self, request, context): + try: + df = parse(request.features) + if request.to == "offline": + to = PushMode.OFFLINE + elif request.to == "online": + to = PushMode.ONLINE + elif request.to == "online_and_offline": + to = PushMode.ONLINE_AND_OFFLINE + else: + raise ValueError( + f"{request.to} is not a supported push format. Please specify one of these ['online', 'offline', " + f"'online_and_offline']." + ) + self.fs.push( + push_source_name=request.push_source_name, + df=df, + allow_registry_cache=request.allow_registry_cache, + to=to, + ) + except PushSourceNotFoundException as e: + logging.exception(str(e)) + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + return PushResponse(status=False) + except Exception as e: + logging.exception(str(e)) + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(str(e)) + return PushResponse(status=False) + return PushResponse(status=True) + + def WriteToOnlineStore(self, request, context): + logging.warning( + "write_to_online_store is deprecated. Please consider using Push instead" + ) + try: + df = parse(request.features) + self.fs.write_to_online_store( + feature_view_name=request.feature_view_name, + df=df, + allow_registry_cache=request.allow_registry_cache, + ) + except Exception as e: + logging.exception(str(e)) + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(str(e)) + return PushResponse(status=False) + return WriteToOnlineStoreResponse(status=True) + + +def get_grpc_server(address: str, fs: FeatureStore, max_workers: int): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers)) + add_GrpcFeatureServerServicer_to_server(GrpcFeatureServer(fs), server) + health_servicer = health.HealthServicer( + experimental_non_blocking=True, + experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=max_workers), + ) + health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server) + server.add_insecure_port(address) + return server diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index 2f28a271dc..cb72fdaa35 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -8,7 +8,7 @@ adal==1.2.7 # via msrestazure adlfs==0.5.9 # via feast (setup.py) -aiohttp==3.8.4 +aiohttp==3.8.5 # via # adlfs # gcsfs @@ -18,7 +18,7 @@ alabaster==0.7.13 # via sphinx altair==4.2.0 # via great-expectations -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # jupyter-server @@ -30,11 +30,8 @@ appnope==0.1.3 # via # ipykernel # ipython -argon2-cffi==21.3.0 - # via - # jupyter-server - # nbclassic - # notebook +argon2-cffi==23.1.0 + # via jupyter-server argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.2.3 @@ -45,9 +42,11 @@ asn1crypto==1.5.1 # snowflake-connector-python assertpy==1.1 # via feast (setup.py) -asttokens==2.2.1 +asttokens==2.4.0 # via stack-data -async-timeout==4.0.2 +async-lru==2.0.4 + # via jupyterlab +async-timeout==4.0.3 # via # aiohttp # redis @@ -56,9 +55,10 @@ attrs==23.1.0 # aiohttp # bowler # jsonschema + # referencing avro==1.10.0 # via feast (setup.py) -azure-core==1.27.0 +azure-core==1.29.3 # via # adlfs # azure-identity @@ -66,16 +66,18 @@ azure-core==1.27.0 # msrest azure-datalake-store==0.0.53 # via adlfs -azure-identity==1.13.0 +azure-identity==1.14.0 # via # adlfs # feast (setup.py) -azure-storage-blob==12.16.0 +azure-storage-blob==12.17.0 # via # adlfs # feast (setup.py) babel==2.12.1 - # via sphinx + # via + # jupyterlab-server + # sphinx backcall==0.2.0 # via ipython beautifulsoup4==4.12.2 @@ -84,30 +86,30 @@ black==22.12.0 # via feast (setup.py) bleach==6.0.0 # via nbconvert -boto3==1.26.146 +boto3==1.28.42 # via # feast (setup.py) # moto -botocore==1.29.146 +botocore==1.31.42 # via # boto3 # moto # s3transfer bowler==0.9.0 # via feast (setup.py) -build==0.10.0 +build==1.0.3 # via # feast (setup.py) # pip-tools bytewax==0.15.1 # via feast (setup.py) -cachecontrol==0.13.0 +cachecontrol==0.13.1 # via firebase-admin cachetools==5.3.1 # via google-auth -cassandra-driver==3.27.0 +cassandra-driver==3.28.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx @@ -122,14 +124,14 @@ cffi==1.15.1 # azure-datalake-store # cryptography # snowflake-connector-python -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via # aiohttp # requests # snowflake-connector-python -click==8.1.3 +click==8.1.7 # via # black # bowler @@ -146,16 +148,17 @@ colorama==0.4.6 # via # feast (setup.py) # great-expectations -comm==0.1.3 - # via ipykernel -coverage[toml]==7.2.7 +comm==0.1.4 + # via + # ipykernel + # ipywidgets +coverage[toml]==7.3.1 # via pytest-cov -cryptography==40.0.2 +cryptography==41.0.3 # via # adal # azure-identity # azure-storage-blob - # cassandra-driver # feast (setup.py) # great-expectations # moto @@ -165,11 +168,11 @@ cryptography==40.0.2 # snowflake-connector-python # types-pyopenssl # types-redis -dask==2023.5.1 +dask==2023.9.1 # via feast (setup.py) db-dtypes==1.1.1 # via google-cloud-bigquery -debugpy==1.6.7 +debugpy==1.6.7.post1 # via ipykernel decorator==5.1.1 # via @@ -181,12 +184,12 @@ deprecated==1.2.14 # via redis deprecation==2.1.0 # via testcontainers -dill==0.3.6 +dill==0.3.7 # via # bytewax # feast (setup.py) # multiprocess -distlib==0.3.6 +distlib==0.3.7 # via virtualenv docker==6.1.3 # via @@ -196,23 +199,24 @@ docutils==0.19 # via sphinx entrypoints==0.4 # via altair -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via # anyio + # ipython # pytest -execnet==1.9.0 +execnet==2.0.2 # via pytest-xdist executing==1.2.0 # via stack-data -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro -fastjsonschema==2.17.1 +fastjsonschema==2.18.0 # via nbformat -filelock==3.12.0 +filelock==3.12.3 # via # snowflake-connector-python # virtualenv @@ -224,7 +228,7 @@ flake8==6.0.0 # via feast (setup.py) fqdn==1.5.1 # via jsonschema -frozenlist==1.3.3 +frozenlist==1.4.0 # via # aiohttp # aiosignal @@ -239,7 +243,7 @@ geojson==2.5.0 # via rockset geomet==0.2.1.post1 # via cassandra-driver -google-api-core[grpc]==2.11.0 +google-api-core[grpc]==2.11.1 # via # feast (setup.py) # firebase-admin @@ -251,9 +255,9 @@ google-api-core[grpc]==2.11.0 # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-api-python-client==2.88.0 +google-api-python-client==2.98.0 # via firebase-admin -google-auth==2.19.1 +google-auth==2.22.0 # via # gcsfs # google-api-core @@ -267,35 +271,35 @@ google-auth-httplib2==0.1.0 # via google-api-python-client google-auth-oauthlib==1.0.0 # via gcsfs -google-cloud-bigquery[pandas]==3.11.0 +google-cloud-bigquery[pandas]==3.11.4 # via feast (setup.py) -google-cloud-bigquery-storage==2.20.0 +google-cloud-bigquery-storage==2.22.0 # via feast (setup.py) -google-cloud-bigtable==2.18.1 +google-cloud-bigtable==2.21.0 # via feast (setup.py) -google-cloud-core==2.3.2 +google-cloud-core==2.3.3 # via # google-cloud-bigquery # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-cloud-datastore==2.15.2 +google-cloud-datastore==2.18.0 # via feast (setup.py) google-cloud-firestore==2.11.1 # via firebase-admin -google-cloud-storage==2.9.0 +google-cloud-storage==2.10.0 # via # feast (setup.py) # firebase-admin # gcsfs google-crc32c==1.5.0 # via google-resumable-media -google-resumable-media==2.5.0 +google-resumable-media==2.6.0 # via # google-cloud-bigquery # google-cloud-storage -googleapis-common-protos[grpc]==1.59.0 +googleapis-common-protos[grpc]==1.60.0 # via # feast (setup.py) # google-api-core @@ -303,30 +307,31 @@ googleapis-common-protos[grpc]==1.59.0 # grpcio-status great-expectations==0.15.50 # via feast (setup.py) -greenlet==2.0.2 - # via sqlalchemy grpc-google-iam-v1==0.12.6 # via google-cloud-bigtable -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) # google-api-core # google-cloud-bigquery # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing # grpcio-tools -grpcio-reflection==1.54.2 +grpcio-health-checking==1.57.0 # via feast (setup.py) -grpcio-status==1.54.2 +grpcio-reflection==1.57.0 + # via feast (setup.py) +grpcio-status==1.57.0 # via google-api-core -grpcio-testing==1.54.2 +grpcio-testing==1.57.0 # via feast (setup.py) -grpcio-tools==1.54.2 +grpcio-tools==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via @@ -334,21 +339,21 @@ h11==0.14.0 # uvicorn happybase==1.2.0 # via feast (setup.py) -hazelcast-python-client==5.2.0 +hazelcast-python-client==5.3.0 # via feast (setup.py) hiredis==2.2.3 # via feast (setup.py) -httpcore==0.17.2 +httpcore==0.17.3 # via httpx httplib2==0.22.0 # via # google-api-python-client # google-auth-httplib2 -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) -identify==2.5.24 +identify==2.5.27 # via pre-commit idna==3.4 # via @@ -360,27 +365,20 @@ idna==3.4 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via # dask # great-expectations iniconfig==2.0.0 # via pytest -ipykernel==6.23.1 - # via - # ipywidgets - # nbclassic - # notebook -ipython==8.14.0 +ipykernel==6.25.2 + # via jupyterlab +ipython==8.15.0 # via # great-expectations # ipykernel # ipywidgets -ipython-genutils==0.2.0 - # via - # nbclassic - # notebook -ipywidgets==8.0.6 +ipywidgets==8.1.0 # via great-expectations isodate==0.6.1 # via @@ -390,7 +388,7 @@ isoduration==20.11.0 # via jsonschema isort==5.12.0 # via feast (setup.py) -jedi==0.18.2 +jedi==0.19.0 # via ipython jinja2==3.1.2 # via @@ -398,56 +396,69 @@ jinja2==3.1.2 # feast (setup.py) # great-expectations # jupyter-server + # jupyterlab + # jupyterlab-server # moto - # nbclassic # nbconvert - # notebook # sphinx jmespath==1.0.1 # via # boto3 # botocore -jsonpatch==1.32 +json5==0.9.14 + # via jupyterlab-server +jsonpatch==1.33 # via great-expectations -jsonpointer==2.3 +jsonpointer==2.4 # via # jsonpatch # jsonschema -jsonschema[format-nongpl]==4.17.3 +jsonschema[format-nongpl]==4.19.0 # via # altair # feast (setup.py) # great-expectations # jupyter-events + # jupyterlab-server # nbformat -jupyter-client==8.2.0 +jsonschema-specifications==2023.7.1 + # via jsonschema +jupyter-client==8.3.1 # via # ipykernel # jupyter-server - # nbclassic # nbclient - # notebook -jupyter-core==5.3.0 +jupyter-core==5.3.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # nbclient # nbconvert # nbformat - # notebook -jupyter-events==0.6.3 +jupyter-events==0.7.0 # via jupyter-server -jupyter-server==2.6.0 +jupyter-lsp==2.2.0 + # via jupyterlab +jupyter-server==2.7.3 # via - # nbclassic + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook # notebook-shim jupyter-server-terminals==0.4.4 # via jupyter-server +jupyterlab==4.0.5 + # via notebook jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-widgets==3.0.7 +jupyterlab-server==2.24.0 + # via + # jupyterlab + # notebook +jupyterlab-widgets==3.0.8 # via ipywidgets kubernetes==20.13.0 # via feast (setup.py) @@ -460,7 +471,7 @@ markupsafe==2.1.3 # jinja2 # nbconvert # werkzeug -marshmallow==3.19.0 +marshmallow==3.20.1 # via great-expectations matplotlib-inline==0.1.6 # via @@ -470,19 +481,19 @@ mccabe==0.7.0 # via flake8 minio==7.1.0 # via feast (setup.py) -mistune==2.0.5 +mistune==3.0.1 # via # great-expectations # nbconvert -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) mock==2.0.0 # via feast (setup.py) moreorless==0.4.0 # via bowler -moto==4.1.10 +moto==4.2.2 # via feast (setup.py) -msal==1.22.0 +msal==1.23.0 # via # azure-datalake-store # azure-identity @@ -499,7 +510,7 @@ multidict==6.0.4 # via # aiohttp # yarl -multiprocess==0.70.14 +multiprocess==0.70.15 # via bytewax mypy==0.982 # via @@ -511,37 +522,29 @@ mypy-extensions==1.0.0 # mypy mypy-protobuf==3.1 # via feast (setup.py) -mysqlclient==2.1.1 +mysqlclient==2.2.0 # via feast (setup.py) -nbclassic==1.0.0 - # via notebook nbclient==0.8.0 # via nbconvert -nbconvert==7.4.0 - # via - # jupyter-server - # nbclassic - # notebook -nbformat==5.9.0 +nbconvert==7.8.0 + # via jupyter-server +nbformat==5.9.2 # via # great-expectations # jupyter-server - # nbclassic # nbclient # nbconvert - # notebook -nest-asyncio==1.5.6 - # via - # ipykernel - # nbclassic - # notebook +nest-asyncio==1.5.7 + # via ipykernel nodeenv==1.8.0 # via pre-commit -notebook==6.5.4 +notebook==7.0.3 # via great-expectations notebook-shim==0.2.3 - # via nbclassic -numpy==1.24.3 + # via + # jupyterlab + # notebook +numpy==1.25.2 # via # altair # db-dtypes @@ -555,7 +558,7 @@ oauthlib==3.2.2 # via requests-oauthlib oscrypto==1.3.0 # via snowflake-connector-python -overrides==7.3.1 +overrides==7.4.0 # via jupyter-server packaging==23.1 # via @@ -566,8 +569,11 @@ packaging==23.1 # docker # google-cloud-bigquery # great-expectations + # gunicorn # ipykernel # jupyter-server + # jupyterlab + # jupyterlab-server # marshmallow # nbconvert # pytest @@ -591,7 +597,7 @@ parso==0.8.3 # via jedi partd==1.4.0 # via dask -pathspec==0.11.1 +pathspec==0.11.2 # via black pbr==5.11.1 # via mock @@ -599,29 +605,27 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -pip-tools==6.13.0 +pip-tools==7.3.0 # via feast (setup.py) -platformdirs==3.5.1 +platformdirs==3.8.1 # via # black # jupyter-core + # snowflake-connector-python # virtualenv -pluggy==1.0.0 +pluggy==1.3.0 # via pytest ply==3.11 # via thriftpy2 portalocker==2.7.0 # via msal-extensions -pre-commit==3.3.2 +pre-commit==3.3.1 # via feast (setup.py) -prometheus-client==0.17.0 - # via - # jupyter-server - # nbclassic - # notebook -prompt-toolkit==3.0.38 +prometheus-client==0.17.1 + # via jupyter-server +prompt-toolkit==3.0.39 # via ipython -proto-plus==1.22.2 +proto-plus==1.22.3 # via # feast (setup.py) # google-cloud-bigquery @@ -629,7 +633,7 @@ proto-plus==1.22.2 # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) # google-api-core @@ -640,6 +644,7 @@ protobuf==4.23.2 # google-cloud-firestore # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing @@ -650,7 +655,7 @@ psutil==5.9.0 # via # feast (setup.py) # ipykernel -psycopg2-binary==2.9.6 +psycopg2-binary==2.9.7 # via feast (setup.py) ptyprocess==0.7.0 # via @@ -684,43 +689,41 @@ pycparser==2.21 # via cffi pycryptodomex==3.18.0 # via snowflake-connector-python -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) # great-expectations pyflakes==3.0.1 # via flake8 -pygments==2.15.1 +pygments==2.16.1 # via # feast (setup.py) # ipython # nbconvert # sphinx -pyjwt[crypto]==2.7.0 +pyjwt[crypto]==2.8.0 # via # adal # msal # snowflake-connector-python pymssql==2.2.8 # via feast (setup.py) -pymysql==1.0.3 +pymysql==1.1.0 # via feast (setup.py) pyodbc==4.0.39 # via feast (setup.py) pyopenssl==23.2.0 # via snowflake-connector-python -pyparsing==3.0.9 +pyparsing==3.1.1 # via # great-expectations # httplib2 pyproject-hooks==1.0.0 # via build -pyrsistent==0.19.3 - # via jsonschema -pyspark==3.4.0 +pyspark==3.4.1 # via feast (setup.py) -pytest==7.3.1 +pytest==7.4.1 # via # feast (setup.py) # pytest-benchmark @@ -761,7 +764,7 @@ python-dotenv==1.0.0 # via uvicorn python-json-logger==2.0.7 # via jupyter-events -pytz==2023.3 +pytz==2023.3.post1 # via # great-expectations # pandas @@ -776,16 +779,19 @@ pyyaml==6.0.1 # pre-commit # responses # uvicorn -pyzmq==25.1.0 +pyzmq==25.1.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic - # notebook redis==4.2.2 # via feast (setup.py) -regex==2023.5.5 +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +regex==2023.8.8 # via feast (setup.py) requests==2.31.0 # via @@ -801,6 +807,7 @@ requests==2.31.0 # google-cloud-bigquery # google-cloud-storage # great-expectations + # jupyterlab-server # kubernetes # moto # msal @@ -815,7 +822,7 @@ requests-oauthlib==1.3.1 # google-auth-oauthlib # kubernetes # msrest -responses==0.23.1 +responses==0.23.3 # via moto rfc3339-validator==0.1.4 # via @@ -825,26 +832,26 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rockset==2.0.0 +rockset==2.1.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing rsa==4.9 # via google-auth ruamel-yaml==0.17.17 # via great-expectations -s3transfer==0.6.1 +s3transfer==0.6.2 # via boto3 -scipy==1.10.1 +scipy==1.11.2 # via great-expectations send2trash==1.8.2 - # via - # jupyter-server - # nbclassic - # notebook + # via jupyter-server six==1.16.0 # via # asttokens # azure-core - # azure-identity # bleach # cassandra-driver # geomet @@ -866,29 +873,35 @@ sniffio==1.3.0 # httpx snowballstemmer==2.2.0 # via sphinx -snowflake-connector-python[pandas]==3.0.4 +snowflake-connector-python[pandas]==3.1.1 # via feast (setup.py) sortedcontainers==2.4.0 # via snowflake-connector-python -soupsieve==2.4.1 +soupsieve==2.5 # via beautifulsoup4 sphinx==6.2.1 - # via feast (setup.py) -sphinxcontrib-applehelp==1.0.4 + # via + # feast (setup.py) + # sphinxcontrib-applehelp + # sphinxcontrib-devhelp + # sphinxcontrib-htmlhelp + # sphinxcontrib-qthelp + # sphinxcontrib-serializinghtml +sphinxcontrib-applehelp==1.0.7 # via sphinx -sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-devhelp==1.0.5 # via sphinx -sphinxcontrib-htmlhelp==2.0.1 +sphinxcontrib-htmlhelp==2.0.4 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-qthelp==1.0.6 # via sphinx -sphinxcontrib-serializinghtml==1.1.5 +sphinxcontrib-serializinghtml==1.1.9 # via sphinx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy stack-data==0.6.2 # via ipython @@ -896,14 +909,12 @@ starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) terminado==0.17.1 # via # jupyter-server # jupyter-server-terminals - # nbclassic - # notebook testcontainers==3.7.1 # via feast (setup.py) thriftpy2==0.4.16 @@ -917,23 +928,27 @@ tomli==2.0.1 # black # build # coverage + # jupyterlab # mypy + # pip-tools # pyproject-hooks # pytest +tomlkit==0.12.1 + # via snowflake-connector-python toolz==0.12.0 # via # altair # dask # partd -tornado==6.3.2 +tornado==6.3.3 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # notebook # terminado -tqdm==4.65.0 +tqdm==4.66.1 # via # feast (setup.py) # great-expectations @@ -947,12 +962,11 @@ traitlets==5.9.0 # jupyter-core # jupyter-events # jupyter-server + # jupyterlab # matplotlib-inline - # nbclassic # nbclient # nbconvert # nbformat - # notebook trino==0.326.0 # via feast (setup.py) typeguard==2.13.3 @@ -961,42 +975,46 @@ types-protobuf==3.19.22 # via # feast (setup.py) # mypy-protobuf -types-pymysql==1.0.19.7 +types-pymysql==1.1.0.1 # via feast (setup.py) -types-pyopenssl==23.2.0.0 +types-pyopenssl==23.2.0.2 # via types-redis -types-python-dateutil==2.8.19.13 +types-python-dateutil==2.8.19.14 # via feast (setup.py) -types-pytz==2023.3.0.0 +types-pytz==2023.3.0.1 # via feast (setup.py) -types-pyyaml==6.0.12.10 +types-pyyaml==6.0.12.11 # via # feast (setup.py) # responses -types-redis==4.5.5.2 +types-redis==4.6.0.5 # via feast (setup.py) -types-requests==2.31.0.1 +types-requests==2.31.0.2 # via feast (setup.py) -types-setuptools==67.8.0.0 +types-setuptools==68.2.0.0 # via feast (setup.py) -types-tabulate==0.9.0.2 +types-tabulate==0.9.0.3 # via feast (setup.py) -types-urllib3==1.26.25.13 +types-urllib3==1.26.25.14 # via types-requests -typing-extensions==4.6.3 +typing-extensions==4.7.1 # via + # async-lru # azure-core # azure-storage-blob + # fastapi + # filelock # great-expectations # mypy # pydantic # snowflake-connector-python # sqlalchemy2-stubs + # uvicorn tzlocal==5.0.1 # via # great-expectations # trino -uri-template==1.2.0 +uri-template==1.3.0 # via jsonschema uritemplate==4.1.1 # via google-api-python-client @@ -1013,15 +1031,17 @@ urllib3==1.26.16 # responses # rockset # snowflake-connector-python -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn virtualenv==20.23.0 - # via pre-commit + # via + # feast (setup.py) + # pre-commit volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn wcwidth==0.2.6 # via prompt-toolkit @@ -1031,18 +1051,18 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.2 +websocket-client==1.6.2 # via # docker # jupyter-server # kubernetes websockets==11.0.3 # via uvicorn -werkzeug==2.3.4 +werkzeug==2.3.7 # via moto -wheel==0.40.0 +wheel==0.41.2 # via pip-tools -widgetsnbextension==4.0.7 +widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 # via @@ -1052,7 +1072,7 @@ xmltodict==0.13.0 # via moto yarl==1.9.2 # via aiohttp -zipp==3.15.0 +zipp==3.16.2 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/sdk/python/requirements/py3.10-requirements.txt b/sdk/python/requirements/py3.10-requirements.txt index d9b70d5197..4140bea9d0 100644 --- a/sdk/python/requirements/py3.10-requirements.txt +++ b/sdk/python/requirements/py3.10-requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --output-file=sdk/python/requirements/py3.10-requirements.txt # -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # starlette @@ -15,16 +15,17 @@ attrs==23.1.0 # via # bowler # jsonschema + # referencing bowler==0.9.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx # requests -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via requests -click==8.1.3 +click==8.1.7 # via # bowler # dask @@ -35,39 +36,43 @@ cloudpickle==2.2.1 # via dask colorama==0.4.6 # via feast (setup.py) -dask==2023.5.1 +dask==2023.9.1 # via feast (setup.py) -dill==0.3.6 +dill==0.3.7 # via feast (setup.py) -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via anyio -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro fissix==21.11.13 # via bowler -fsspec==2023.5.0 +fsspec==2023.9.0 # via dask -greenlet==2.0.2 - # via sqlalchemy -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection -grpcio-reflection==1.54.2 + # grpcio-tools +grpcio-health-checking==1.57.0 + # via feast (setup.py) +grpcio-reflection==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +grpcio-tools==1.57.0 + # via feast (setup.py) +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via # httpcore # uvicorn -httpcore==0.17.2 +httpcore==0.17.3 # via httpx -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) @@ -76,32 +81,38 @@ idna==3.4 # anyio # httpx # requests -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via dask jinja2==3.1.2 # via feast (setup.py) -jsonschema==4.17.3 +jsonschema==4.19.0 # via feast (setup.py) +jsonschema-specifications==2023.7.1 + # via jsonschema locket==1.0.0 # via partd markupsafe==2.1.3 # via jinja2 -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) moreorless==0.4.0 # via bowler -mypy==1.3.0 +mypy==1.5.1 # via sqlalchemy mypy-extensions==1.0.0 # via mypy -numpy==1.24.3 +mypy-protobuf==3.1 + # via feast (setup.py) +numpy==1.25.2 # via # feast (setup.py) # pandas # pandavro # pyarrow packaging==23.1 - # via dask + # via + # dask + # gunicorn pandas==1.5.3 # via # feast (setup.py) @@ -110,36 +121,45 @@ pandavro==1.5.2 # via feast (setup.py) partd==1.4.0 # via dask -proto-plus==1.22.2 +proto-plus==1.22.3 # via feast (setup.py) -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection + # grpcio-tools + # mypy-protobuf # proto-plus pyarrow==11.0.0 # via feast (setup.py) -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) -pygments==2.15.1 +pygments==2.16.1 # via feast (setup.py) -pyrsistent==0.19.3 - # via jsonschema python-dateutil==2.8.2 # via pandas python-dotenv==1.0.0 # via uvicorn -pytz==2023.3 +pytz==2023.3.post1 # via pandas pyyaml==6.0.1 # via # dask # feast (setup.py) # uvicorn +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications requests==2.31.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing six==1.16.0 # via # pandavro @@ -149,15 +169,15 @@ sniffio==1.3.0 # anyio # httpcore # httpx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) toml==0.10.2 # via feast (setup.py) @@ -167,26 +187,33 @@ toolz==0.12.0 # via # dask # partd -tqdm==4.65.0 +tqdm==4.66.1 # via feast (setup.py) typeguard==2.13.3 # via feast (setup.py) -typing-extensions==4.6.3 +types-protobuf==4.24.0.1 + # via mypy-protobuf +typing-extensions==4.7.1 # via + # fastapi # mypy # pydantic # sqlalchemy2-stubs -urllib3==2.0.2 + # uvicorn +urllib3==2.0.4 # via requests -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn websockets==11.0.3 # via uvicorn -zipp==3.15.0 +zipp==3.16.2 # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/sdk/python/requirements/py3.8-ci-requirements.txt b/sdk/python/requirements/py3.8-ci-requirements.txt index de814374b6..9dfefc2108 100644 --- a/sdk/python/requirements/py3.8-ci-requirements.txt +++ b/sdk/python/requirements/py3.8-ci-requirements.txt @@ -8,7 +8,7 @@ adal==1.2.7 # via msrestazure adlfs==0.5.9 # via feast (setup.py) -aiohttp==3.8.4 +aiohttp==3.8.5 # via # adlfs # gcsfs @@ -18,7 +18,7 @@ alabaster==0.7.13 # via sphinx altair==4.2.0 # via great-expectations -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # jupyter-server @@ -30,11 +30,8 @@ appnope==0.1.3 # via # ipykernel # ipython -argon2-cffi==21.3.0 - # via - # jupyter-server - # nbclassic - # notebook +argon2-cffi==23.1.0 + # via jupyter-server argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.2.3 @@ -45,9 +42,11 @@ asn1crypto==1.5.1 # snowflake-connector-python assertpy==1.1 # via feast (setup.py) -asttokens==2.2.1 +asttokens==2.4.0 # via stack-data -async-timeout==4.0.2 +async-lru==2.0.4 + # via jupyterlab +async-timeout==4.0.3 # via # aiohttp # redis @@ -56,9 +55,10 @@ attrs==23.1.0 # aiohttp # bowler # jsonschema + # referencing avro==1.10.0 # via feast (setup.py) -azure-core==1.27.0 +azure-core==1.29.3 # via # adlfs # azure-identity @@ -66,16 +66,18 @@ azure-core==1.27.0 # msrest azure-datalake-store==0.0.53 # via adlfs -azure-identity==1.13.0 +azure-identity==1.14.0 # via # adlfs # feast (setup.py) -azure-storage-blob==12.16.0 +azure-storage-blob==12.17.0 # via # adlfs # feast (setup.py) babel==2.12.1 - # via sphinx + # via + # jupyterlab-server + # sphinx backcall==0.2.0 # via ipython backports-zoneinfo==0.2.1 @@ -88,30 +90,30 @@ black==22.12.0 # via feast (setup.py) bleach==6.0.0 # via nbconvert -boto3==1.26.146 +boto3==1.28.42 # via # feast (setup.py) # moto -botocore==1.29.146 +botocore==1.31.42 # via # boto3 # moto # s3transfer bowler==0.9.0 # via feast (setup.py) -build==0.10.0 +build==1.0.3 # via # feast (setup.py) # pip-tools bytewax==0.15.1 # via feast (setup.py) -cachecontrol==0.13.0 +cachecontrol==0.13.1 # via firebase-admin cachetools==5.3.1 # via google-auth -cassandra-driver==3.27.0 +cassandra-driver==3.28.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx @@ -126,14 +128,14 @@ cffi==1.15.1 # azure-datalake-store # cryptography # snowflake-connector-python -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via # aiohttp # requests # snowflake-connector-python -click==8.1.3 +click==8.1.7 # via # black # bowler @@ -150,16 +152,17 @@ colorama==0.4.6 # via # feast (setup.py) # great-expectations -comm==0.1.3 - # via ipykernel -coverage[toml]==7.2.7 +comm==0.1.4 + # via + # ipykernel + # ipywidgets +coverage[toml]==7.3.1 # via pytest-cov -cryptography==40.0.2 +cryptography==41.0.3 # via # adal # azure-identity # azure-storage-blob - # cassandra-driver # feast (setup.py) # great-expectations # moto @@ -173,7 +176,7 @@ dask==2023.5.0 # via feast (setup.py) db-dtypes==1.1.1 # via google-cloud-bigquery -debugpy==1.6.7 +debugpy==1.6.7.post1 # via ipykernel decorator==5.1.1 # via @@ -185,12 +188,12 @@ deprecated==1.2.14 # via redis deprecation==2.1.0 # via testcontainers -dill==0.3.6 +dill==0.3.7 # via # bytewax # feast (setup.py) # multiprocess -distlib==0.3.6 +distlib==0.3.7 # via virtualenv docker==6.1.3 # via @@ -200,23 +203,23 @@ docutils==0.19 # via sphinx entrypoints==0.4 # via altair -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via # anyio # pytest -execnet==1.9.0 +execnet==2.0.2 # via pytest-xdist executing==1.2.0 # via stack-data -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro -fastjsonschema==2.17.1 +fastjsonschema==2.18.0 # via nbformat -filelock==3.12.0 +filelock==3.12.3 # via # snowflake-connector-python # virtualenv @@ -228,7 +231,7 @@ flake8==6.0.0 # via feast (setup.py) fqdn==1.5.1 # via jsonschema -frozenlist==1.3.3 +frozenlist==1.4.0 # via # aiohttp # aiosignal @@ -243,7 +246,7 @@ geojson==2.5.0 # via rockset geomet==0.2.1.post1 # via cassandra-driver -google-api-core[grpc]==2.11.0 +google-api-core[grpc]==2.11.1 # via # feast (setup.py) # firebase-admin @@ -255,9 +258,9 @@ google-api-core[grpc]==2.11.0 # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-api-python-client==2.88.0 +google-api-python-client==2.98.0 # via firebase-admin -google-auth==2.19.1 +google-auth==2.22.0 # via # gcsfs # google-api-core @@ -271,35 +274,35 @@ google-auth-httplib2==0.1.0 # via google-api-python-client google-auth-oauthlib==1.0.0 # via gcsfs -google-cloud-bigquery[pandas]==3.11.0 +google-cloud-bigquery[pandas]==3.11.4 # via feast (setup.py) -google-cloud-bigquery-storage==2.20.0 +google-cloud-bigquery-storage==2.22.0 # via feast (setup.py) -google-cloud-bigtable==2.18.1 +google-cloud-bigtable==2.21.0 # via feast (setup.py) -google-cloud-core==2.3.2 +google-cloud-core==2.3.3 # via # google-cloud-bigquery # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-cloud-datastore==2.15.2 +google-cloud-datastore==2.18.0 # via feast (setup.py) google-cloud-firestore==2.11.1 # via firebase-admin -google-cloud-storage==2.9.0 +google-cloud-storage==2.10.0 # via # feast (setup.py) # firebase-admin # gcsfs google-crc32c==1.5.0 # via google-resumable-media -google-resumable-media==2.5.0 +google-resumable-media==2.6.0 # via # google-cloud-bigquery # google-cloud-storage -googleapis-common-protos[grpc]==1.59.0 +googleapis-common-protos[grpc]==1.60.0 # via # feast (setup.py) # google-api-core @@ -307,30 +310,31 @@ googleapis-common-protos[grpc]==1.59.0 # grpcio-status great-expectations==0.15.50 # via feast (setup.py) -greenlet==2.0.2 - # via sqlalchemy grpc-google-iam-v1==0.12.6 # via google-cloud-bigtable -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) # google-api-core # google-cloud-bigquery # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing # grpcio-tools -grpcio-reflection==1.54.2 +grpcio-health-checking==1.57.0 + # via feast (setup.py) +grpcio-reflection==1.57.0 # via feast (setup.py) -grpcio-status==1.54.2 +grpcio-status==1.57.0 # via google-api-core -grpcio-testing==1.54.2 +grpcio-testing==1.57.0 # via feast (setup.py) -grpcio-tools==1.54.2 +grpcio-tools==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via @@ -338,21 +342,21 @@ h11==0.14.0 # uvicorn happybase==1.2.0 # via feast (setup.py) -hazelcast-python-client==5.2.0 +hazelcast-python-client==5.3.0 # via feast (setup.py) hiredis==2.2.3 # via feast (setup.py) -httpcore==0.17.2 +httpcore==0.17.3 # via httpx httplib2==0.22.0 # via # google-api-python-client # google-auth-httplib2 -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) -identify==2.5.24 +identify==2.5.27 # via pre-commit idna==3.4 # via @@ -364,32 +368,32 @@ idna==3.4 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via + # build # dask # great-expectations # jupyter-client + # jupyter-lsp + # jupyterlab + # jupyterlab-server # nbconvert # sphinx -importlib-resources==5.12.0 - # via jsonschema +importlib-resources==6.0.1 + # via + # jsonschema + # jsonschema-specifications + # jupyterlab iniconfig==2.0.0 # via pytest -ipykernel==6.23.1 - # via - # ipywidgets - # nbclassic - # notebook +ipykernel==6.25.2 + # via jupyterlab ipython==8.12.2 # via # great-expectations # ipykernel # ipywidgets -ipython-genutils==0.2.0 - # via - # nbclassic - # notebook -ipywidgets==8.0.6 +ipywidgets==8.1.0 # via great-expectations isodate==0.6.1 # via @@ -399,7 +403,7 @@ isoduration==20.11.0 # via jsonschema isort==5.12.0 # via feast (setup.py) -jedi==0.18.2 +jedi==0.19.0 # via ipython jinja2==3.1.2 # via @@ -407,56 +411,69 @@ jinja2==3.1.2 # feast (setup.py) # great-expectations # jupyter-server + # jupyterlab + # jupyterlab-server # moto - # nbclassic # nbconvert - # notebook # sphinx jmespath==1.0.1 # via # boto3 # botocore -jsonpatch==1.32 +json5==0.9.14 + # via jupyterlab-server +jsonpatch==1.33 # via great-expectations -jsonpointer==2.3 +jsonpointer==2.4 # via # jsonpatch # jsonschema -jsonschema[format-nongpl]==4.17.3 +jsonschema[format-nongpl]==4.19.0 # via # altair # feast (setup.py) # great-expectations # jupyter-events + # jupyterlab-server # nbformat -jupyter-client==8.2.0 +jsonschema-specifications==2023.7.1 + # via jsonschema +jupyter-client==8.3.1 # via # ipykernel # jupyter-server - # nbclassic # nbclient - # notebook -jupyter-core==5.3.0 +jupyter-core==5.3.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # nbclient # nbconvert # nbformat - # notebook -jupyter-events==0.6.3 +jupyter-events==0.7.0 # via jupyter-server -jupyter-server==2.6.0 +jupyter-lsp==2.2.0 + # via jupyterlab +jupyter-server==2.7.3 # via - # nbclassic + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook # notebook-shim jupyter-server-terminals==0.4.4 # via jupyter-server +jupyterlab==4.0.5 + # via notebook jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-widgets==3.0.7 +jupyterlab-server==2.24.0 + # via + # jupyterlab + # notebook +jupyterlab-widgets==3.0.8 # via ipywidgets kubernetes==20.13.0 # via feast (setup.py) @@ -469,7 +486,7 @@ markupsafe==2.1.3 # jinja2 # nbconvert # werkzeug -marshmallow==3.19.0 +marshmallow==3.20.1 # via great-expectations matplotlib-inline==0.1.6 # via @@ -479,19 +496,19 @@ mccabe==0.7.0 # via flake8 minio==7.1.0 # via feast (setup.py) -mistune==2.0.5 +mistune==3.0.1 # via # great-expectations # nbconvert -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) mock==2.0.0 # via feast (setup.py) moreorless==0.4.0 # via bowler -moto==4.1.10 +moto==4.2.2 # via feast (setup.py) -msal==1.22.0 +msal==1.23.0 # via # azure-datalake-store # azure-identity @@ -508,7 +525,7 @@ multidict==6.0.4 # via # aiohttp # yarl -multiprocess==0.70.14 +multiprocess==0.70.15 # via bytewax mypy==0.982 # via @@ -520,37 +537,29 @@ mypy-extensions==1.0.0 # mypy mypy-protobuf==3.1 # via feast (setup.py) -mysqlclient==2.1.1 +mysqlclient==2.2.0 # via feast (setup.py) -nbclassic==1.0.0 - # via notebook nbclient==0.8.0 # via nbconvert -nbconvert==7.4.0 - # via - # jupyter-server - # nbclassic - # notebook -nbformat==5.9.0 +nbconvert==7.8.0 + # via jupyter-server +nbformat==5.9.2 # via # great-expectations # jupyter-server - # nbclassic # nbclient # nbconvert - # notebook -nest-asyncio==1.5.6 - # via - # ipykernel - # nbclassic - # notebook +nest-asyncio==1.5.7 + # via ipykernel nodeenv==1.8.0 # via pre-commit -notebook==6.5.4 +notebook==7.0.3 # via great-expectations notebook-shim==0.2.3 - # via nbclassic -numpy==1.24.3 + # via + # jupyterlab + # notebook +numpy==1.24.4 # via # altair # db-dtypes @@ -564,7 +573,7 @@ oauthlib==3.2.2 # via requests-oauthlib oscrypto==1.3.0 # via snowflake-connector-python -overrides==7.3.1 +overrides==7.4.0 # via jupyter-server packaging==23.1 # via @@ -575,8 +584,11 @@ packaging==23.1 # docker # google-cloud-bigquery # great-expectations + # gunicorn # ipykernel # jupyter-server + # jupyterlab + # jupyterlab-server # marshmallow # nbconvert # pytest @@ -600,7 +612,7 @@ parso==0.8.3 # via jedi partd==1.4.0 # via dask -pathspec==0.11.1 +pathspec==0.11.2 # via black pbr==5.11.1 # via mock @@ -608,31 +620,29 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -pip-tools==6.13.0 +pip-tools==7.3.0 # via feast (setup.py) pkgutil-resolve-name==1.3.10 # via jsonschema -platformdirs==3.5.1 +platformdirs==3.8.1 # via # black # jupyter-core + # snowflake-connector-python # virtualenv -pluggy==1.0.0 +pluggy==1.3.0 # via pytest ply==3.11 # via thriftpy2 portalocker==2.7.0 # via msal-extensions -pre-commit==3.3.2 +pre-commit==3.3.1 # via feast (setup.py) -prometheus-client==0.17.0 - # via - # jupyter-server - # nbclassic - # notebook -prompt-toolkit==3.0.38 +prometheus-client==0.17.1 + # via jupyter-server +prompt-toolkit==3.0.39 # via ipython -proto-plus==1.22.2 +proto-plus==1.22.3 # via # feast (setup.py) # google-cloud-bigquery @@ -640,7 +650,7 @@ proto-plus==1.22.2 # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) # google-api-core @@ -651,6 +661,7 @@ protobuf==4.23.2 # google-cloud-firestore # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing @@ -661,7 +672,7 @@ psutil==5.9.0 # via # feast (setup.py) # ipykernel -psycopg2-binary==2.9.6 +psycopg2-binary==2.9.7 # via feast (setup.py) ptyprocess==0.7.0 # via @@ -695,43 +706,41 @@ pycparser==2.21 # via cffi pycryptodomex==3.18.0 # via snowflake-connector-python -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) # great-expectations pyflakes==3.0.1 # via flake8 -pygments==2.15.1 +pygments==2.16.1 # via # feast (setup.py) # ipython # nbconvert # sphinx -pyjwt[crypto]==2.7.0 +pyjwt[crypto]==2.8.0 # via # adal # msal # snowflake-connector-python pymssql==2.2.8 # via feast (setup.py) -pymysql==1.0.3 +pymysql==1.1.0 # via feast (setup.py) pyodbc==4.0.39 # via feast (setup.py) pyopenssl==23.2.0 # via snowflake-connector-python -pyparsing==3.0.9 +pyparsing==3.1.1 # via # great-expectations # httplib2 pyproject-hooks==1.0.0 # via build -pyrsistent==0.19.3 - # via jsonschema -pyspark==3.4.0 +pyspark==3.4.1 # via feast (setup.py) -pytest==7.3.1 +pytest==7.4.1 # via # feast (setup.py) # pytest-benchmark @@ -772,7 +781,7 @@ python-dotenv==1.0.0 # via uvicorn python-json-logger==2.0.7 # via jupyter-events -pytz==2023.3 +pytz==2023.3.post1 # via # babel # great-expectations @@ -788,16 +797,19 @@ pyyaml==6.0.1 # pre-commit # responses # uvicorn -pyzmq==25.1.0 +pyzmq==25.1.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic - # notebook redis==4.2.2 # via feast (setup.py) -regex==2023.5.5 +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +regex==2023.8.8 # via feast (setup.py) requests==2.31.0 # via @@ -813,6 +825,7 @@ requests==2.31.0 # google-cloud-bigquery # google-cloud-storage # great-expectations + # jupyterlab-server # kubernetes # moto # msal @@ -827,7 +840,7 @@ requests-oauthlib==1.3.1 # google-auth-oauthlib # kubernetes # msrest -responses==0.23.1 +responses==0.23.3 # via moto rfc3339-validator==0.1.4 # via @@ -837,28 +850,28 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rockset==2.0.0 +rockset==2.1.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing rsa==4.9 # via google-auth ruamel-yaml==0.17.17 # via great-expectations ruamel-yaml-clib==0.2.7 # via ruamel-yaml -s3transfer==0.6.1 +s3transfer==0.6.2 # via boto3 scipy==1.10.1 # via great-expectations send2trash==1.8.2 - # via - # jupyter-server - # nbclassic - # notebook + # via jupyter-server six==1.16.0 # via # asttokens # azure-core - # azure-identity # bleach # cassandra-driver # geomet @@ -880,11 +893,11 @@ sniffio==1.3.0 # httpx snowballstemmer==2.2.0 # via sphinx -snowflake-connector-python[pandas]==3.0.4 +snowflake-connector-python[pandas]==3.1.1 # via feast (setup.py) sortedcontainers==2.4.0 # via snowflake-connector-python -soupsieve==2.4.1 +soupsieve==2.5 # via beautifulsoup4 sphinx==6.2.1 # via feast (setup.py) @@ -900,9 +913,9 @@ sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy stack-data==0.6.2 # via ipython @@ -910,14 +923,12 @@ starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) terminado==0.17.1 # via # jupyter-server # jupyter-server-terminals - # nbclassic - # notebook testcontainers==3.7.1 # via feast (setup.py) thriftpy2==0.4.16 @@ -931,23 +942,27 @@ tomli==2.0.1 # black # build # coverage + # jupyterlab # mypy + # pip-tools # pyproject-hooks # pytest +tomlkit==0.12.1 + # via snowflake-connector-python toolz==0.12.0 # via # altair # dask # partd -tornado==6.3.2 +tornado==6.3.3 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # notebook # terminado -tqdm==4.65.0 +tqdm==4.66.1 # via # feast (setup.py) # great-expectations @@ -961,12 +976,11 @@ traitlets==5.9.0 # jupyter-core # jupyter-events # jupyter-server + # jupyterlab # matplotlib-inline - # nbclassic # nbclient # nbconvert # nbformat - # notebook trino==0.326.0 # via feast (setup.py) typeguard==2.13.3 @@ -975,33 +989,36 @@ types-protobuf==3.19.22 # via # feast (setup.py) # mypy-protobuf -types-pymysql==1.0.19.7 +types-pymysql==1.1.0.1 # via feast (setup.py) -types-pyopenssl==23.2.0.0 +types-pyopenssl==23.2.0.2 # via types-redis -types-python-dateutil==2.8.19.13 +types-python-dateutil==2.8.19.14 # via feast (setup.py) -types-pytz==2023.3.0.0 +types-pytz==2023.3.0.1 # via feast (setup.py) -types-pyyaml==6.0.12.10 +types-pyyaml==6.0.12.11 # via # feast (setup.py) # responses -types-redis==4.5.5.2 +types-redis==4.6.0.5 # via feast (setup.py) -types-requests==2.31.0.1 +types-requests==2.31.0.2 # via feast (setup.py) -types-setuptools==67.8.0.0 +types-setuptools==68.2.0.0 # via feast (setup.py) -types-tabulate==0.9.0.2 +types-tabulate==0.9.0.3 # via feast (setup.py) -types-urllib3==1.26.25.13 +types-urllib3==1.26.25.14 # via types-requests -typing-extensions==4.6.3 +typing-extensions==4.7.1 # via + # async-lru # azure-core # azure-storage-blob # black + # fastapi + # filelock # great-expectations # ipython # mypy @@ -1009,11 +1026,12 @@ typing-extensions==4.6.3 # snowflake-connector-python # sqlalchemy2-stubs # starlette + # uvicorn tzlocal==5.0.1 # via # great-expectations # trino -uri-template==1.2.0 +uri-template==1.3.0 # via jsonschema uritemplate==4.1.1 # via google-api-python-client @@ -1030,15 +1048,17 @@ urllib3==1.26.16 # responses # rockset # snowflake-connector-python -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn virtualenv==20.23.0 - # via pre-commit + # via + # feast (setup.py) + # pre-commit volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn wcwidth==0.2.6 # via prompt-toolkit @@ -1048,18 +1068,18 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.2 +websocket-client==1.6.2 # via # docker # jupyter-server # kubernetes websockets==11.0.3 # via uvicorn -werkzeug==2.3.4 +werkzeug==2.3.7 # via moto -wheel==0.40.0 +wheel==0.41.2 # via pip-tools -widgetsnbextension==4.0.7 +widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 # via @@ -1069,7 +1089,7 @@ xmltodict==0.13.0 # via moto yarl==1.9.2 # via aiohttp -zipp==3.15.0 +zipp==3.16.2 # via # importlib-metadata # importlib-resources diff --git a/sdk/python/requirements/py3.8-requirements.txt b/sdk/python/requirements/py3.8-requirements.txt index 55ee634904..636f886133 100644 --- a/sdk/python/requirements/py3.8-requirements.txt +++ b/sdk/python/requirements/py3.8-requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --output-file=sdk/python/requirements/py3.8-requirements.txt # -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # starlette @@ -15,16 +15,17 @@ attrs==23.1.0 # via # bowler # jsonschema + # referencing bowler==0.9.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx # requests -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via requests -click==8.1.3 +click==8.1.7 # via # bowler # dask @@ -37,37 +38,41 @@ colorama==0.4.6 # via feast (setup.py) dask==2023.5.0 # via feast (setup.py) -dill==0.3.6 +dill==0.3.7 # via feast (setup.py) -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via anyio -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro fissix==21.11.13 # via bowler -fsspec==2023.5.0 +fsspec==2023.9.0 # via dask -greenlet==2.0.2 - # via sqlalchemy -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection -grpcio-reflection==1.54.2 + # grpcio-tools +grpcio-health-checking==1.57.0 + # via feast (setup.py) +grpcio-reflection==1.57.0 + # via feast (setup.py) +grpcio-tools==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via # httpcore # uvicorn -httpcore==0.17.2 +httpcore==0.17.3 # via httpx -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) @@ -76,34 +81,42 @@ idna==3.4 # anyio # httpx # requests -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via dask -importlib-resources==5.12.0 - # via jsonschema +importlib-resources==6.0.1 + # via + # jsonschema + # jsonschema-specifications jinja2==3.1.2 # via feast (setup.py) -jsonschema==4.17.3 +jsonschema==4.19.0 # via feast (setup.py) +jsonschema-specifications==2023.7.1 + # via jsonschema locket==1.0.0 # via partd markupsafe==2.1.3 # via jinja2 -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) moreorless==0.4.0 # via bowler -mypy==1.3.0 +mypy==1.5.1 # via sqlalchemy mypy-extensions==1.0.0 # via mypy -numpy==1.24.3 +mypy-protobuf==3.1 + # via feast (setup.py) +numpy==1.24.4 # via # feast (setup.py) # pandas # pandavro # pyarrow packaging==23.1 - # via dask + # via + # dask + # gunicorn pandas==1.5.3 # via # feast (setup.py) @@ -114,36 +127,45 @@ partd==1.4.0 # via dask pkgutil-resolve-name==1.3.10 # via jsonschema -proto-plus==1.22.2 +proto-plus==1.22.3 # via feast (setup.py) -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection + # grpcio-tools + # mypy-protobuf # proto-plus pyarrow==11.0.0 # via feast (setup.py) -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) -pygments==2.15.1 +pygments==2.16.1 # via feast (setup.py) -pyrsistent==0.19.3 - # via jsonschema python-dateutil==2.8.2 # via pandas python-dotenv==1.0.0 # via uvicorn -pytz==2023.3 +pytz==2023.3.post1 # via pandas pyyaml==6.0.1 # via # dask # feast (setup.py) # uvicorn +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications requests==2.31.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing six==1.16.0 # via # pandavro @@ -153,15 +175,15 @@ sniffio==1.3.0 # anyio # httpcore # httpx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) toml==0.10.2 # via feast (setup.py) @@ -171,29 +193,36 @@ toolz==0.12.0 # via # dask # partd -tqdm==4.65.0 +tqdm==4.66.1 # via feast (setup.py) typeguard==2.13.3 # via feast (setup.py) -typing-extensions==4.6.3 +types-protobuf==4.24.0.1 + # via mypy-protobuf +typing-extensions==4.7.1 # via + # fastapi # mypy # pydantic # sqlalchemy2-stubs # starlette -urllib3==2.0.2 + # uvicorn +urllib3==2.0.4 # via requests -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn websockets==11.0.3 # via uvicorn -zipp==3.15.0 +zipp==3.16.2 # via # importlib-metadata # importlib-resources + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt index aa79b8ec3b..3992303d00 100644 --- a/sdk/python/requirements/py3.9-ci-requirements.txt +++ b/sdk/python/requirements/py3.9-ci-requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # pip-compile --extra=ci --output-file=sdk/python/requirements/py3.9-ci-requirements.txt # @@ -8,7 +8,7 @@ adal==1.2.7 # via msrestazure adlfs==0.5.9 # via feast (setup.py) -aiohttp==3.8.4 +aiohttp==3.8.5 # via # adlfs # gcsfs @@ -18,7 +18,7 @@ alabaster==0.7.13 # via sphinx altair==4.2.0 # via great-expectations -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # jupyter-server @@ -30,11 +30,8 @@ appnope==0.1.3 # via # ipykernel # ipython -argon2-cffi==21.3.0 - # via - # jupyter-server - # nbclassic - # notebook +argon2-cffi==23.1.0 + # via jupyter-server argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.2.3 @@ -45,9 +42,11 @@ asn1crypto==1.5.1 # snowflake-connector-python assertpy==1.1 # via feast (setup.py) -asttokens==2.2.1 +asttokens==2.4.0 # via stack-data -async-timeout==4.0.2 +async-lru==2.0.4 + # via jupyterlab +async-timeout==4.0.3 # via # aiohttp # redis @@ -56,9 +55,10 @@ attrs==23.1.0 # aiohttp # bowler # jsonschema + # referencing avro==1.10.0 # via feast (setup.py) -azure-core==1.27.0 +azure-core==1.29.3 # via # adlfs # azure-identity @@ -66,16 +66,18 @@ azure-core==1.27.0 # msrest azure-datalake-store==0.0.53 # via adlfs -azure-identity==1.13.0 +azure-identity==1.14.0 # via # adlfs # feast (setup.py) -azure-storage-blob==12.16.0 +azure-storage-blob==12.17.0 # via # adlfs # feast (setup.py) babel==2.12.1 - # via sphinx + # via + # jupyterlab-server + # sphinx backcall==0.2.0 # via ipython beautifulsoup4==4.12.2 @@ -84,30 +86,30 @@ black==22.12.0 # via feast (setup.py) bleach==6.0.0 # via nbconvert -boto3==1.26.146 +boto3==1.28.42 # via # feast (setup.py) # moto -botocore==1.29.146 +botocore==1.31.42 # via # boto3 # moto # s3transfer bowler==0.9.0 # via feast (setup.py) -build==0.10.0 +build==1.0.3 # via # feast (setup.py) # pip-tools bytewax==0.15.1 # via feast (setup.py) -cachecontrol==0.13.0 +cachecontrol==0.13.1 # via firebase-admin cachetools==5.3.1 # via google-auth -cassandra-driver==3.27.0 +cassandra-driver==3.28.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx @@ -122,14 +124,14 @@ cffi==1.15.1 # azure-datalake-store # cryptography # snowflake-connector-python -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via # aiohttp # requests # snowflake-connector-python -click==8.1.3 +click==8.1.7 # via # black # bowler @@ -146,16 +148,17 @@ colorama==0.4.6 # via # feast (setup.py) # great-expectations -comm==0.1.3 - # via ipykernel -coverage[toml]==7.2.7 +comm==0.1.4 + # via + # ipykernel + # ipywidgets +coverage[toml]==7.3.1 # via pytest-cov -cryptography==40.0.2 +cryptography==41.0.3 # via # adal # azure-identity # azure-storage-blob - # cassandra-driver # feast (setup.py) # great-expectations # moto @@ -165,11 +168,11 @@ cryptography==40.0.2 # snowflake-connector-python # types-pyopenssl # types-redis -dask==2023.5.1 +dask==2023.9.1 # via feast (setup.py) db-dtypes==1.1.1 # via google-cloud-bigquery -debugpy==1.6.7 +debugpy==1.6.7.post1 # via ipykernel decorator==5.1.1 # via @@ -181,12 +184,12 @@ deprecated==1.2.14 # via redis deprecation==2.1.0 # via testcontainers -dill==0.3.6 +dill==0.3.7 # via # bytewax # feast (setup.py) # multiprocess -distlib==0.3.6 +distlib==0.3.7 # via virtualenv docker==6.1.3 # via @@ -196,23 +199,24 @@ docutils==0.19 # via sphinx entrypoints==0.4 # via altair -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via # anyio + # ipython # pytest -execnet==1.9.0 +execnet==2.0.2 # via pytest-xdist executing==1.2.0 # via stack-data -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro -fastjsonschema==2.17.1 +fastjsonschema==2.18.0 # via nbformat -filelock==3.12.0 +filelock==3.12.3 # via # snowflake-connector-python # virtualenv @@ -224,7 +228,7 @@ flake8==6.0.0 # via feast (setup.py) fqdn==1.5.1 # via jsonschema -frozenlist==1.3.3 +frozenlist==1.4.0 # via # aiohttp # aiosignal @@ -239,7 +243,7 @@ geojson==2.5.0 # via rockset geomet==0.2.1.post1 # via cassandra-driver -google-api-core[grpc]==2.11.0 +google-api-core[grpc]==2.11.1 # via # feast (setup.py) # firebase-admin @@ -251,9 +255,9 @@ google-api-core[grpc]==2.11.0 # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-api-python-client==2.88.0 +google-api-python-client==2.98.0 # via firebase-admin -google-auth==2.19.1 +google-auth==2.22.0 # via # gcsfs # google-api-core @@ -267,35 +271,35 @@ google-auth-httplib2==0.1.0 # via google-api-python-client google-auth-oauthlib==1.0.0 # via gcsfs -google-cloud-bigquery[pandas]==3.11.0 +google-cloud-bigquery[pandas]==3.11.4 # via feast (setup.py) -google-cloud-bigquery-storage==2.20.0 +google-cloud-bigquery-storage==2.22.0 # via feast (setup.py) -google-cloud-bigtable==2.18.1 +google-cloud-bigtable==2.21.0 # via feast (setup.py) -google-cloud-core==2.3.2 +google-cloud-core==2.3.3 # via # google-cloud-bigquery # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore # google-cloud-storage -google-cloud-datastore==2.15.2 +google-cloud-datastore==2.18.0 # via feast (setup.py) google-cloud-firestore==2.11.1 # via firebase-admin -google-cloud-storage==2.9.0 +google-cloud-storage==2.10.0 # via # feast (setup.py) # firebase-admin # gcsfs google-crc32c==1.5.0 # via google-resumable-media -google-resumable-media==2.5.0 +google-resumable-media==2.6.0 # via # google-cloud-bigquery # google-cloud-storage -googleapis-common-protos[grpc]==1.59.0 +googleapis-common-protos[grpc]==1.60.0 # via # feast (setup.py) # google-api-core @@ -303,30 +307,31 @@ googleapis-common-protos[grpc]==1.59.0 # grpcio-status great-expectations==0.15.50 # via feast (setup.py) -greenlet==2.0.2 - # via sqlalchemy grpc-google-iam-v1==0.12.6 # via google-cloud-bigtable -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) # google-api-core # google-cloud-bigquery # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing # grpcio-tools -grpcio-reflection==1.54.2 +grpcio-health-checking==1.57.0 # via feast (setup.py) -grpcio-status==1.54.2 +grpcio-reflection==1.57.0 + # via feast (setup.py) +grpcio-status==1.57.0 # via google-api-core -grpcio-testing==1.54.2 +grpcio-testing==1.57.0 # via feast (setup.py) -grpcio-tools==1.54.2 +grpcio-tools==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via @@ -334,21 +339,21 @@ h11==0.14.0 # uvicorn happybase==1.2.0 # via feast (setup.py) -hazelcast-python-client==5.2.0 +hazelcast-python-client==5.3.0 # via feast (setup.py) hiredis==2.2.3 # via feast (setup.py) -httpcore==0.17.2 +httpcore==0.17.3 # via httpx httplib2==0.22.0 # via # google-api-python-client # google-auth-httplib2 -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) -identify==2.5.24 +identify==2.5.27 # via pre-commit idna==3.4 # via @@ -360,30 +365,27 @@ idna==3.4 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via + # build # dask # great-expectations # jupyter-client + # jupyter-lsp + # jupyterlab + # jupyterlab-server # nbconvert # sphinx iniconfig==2.0.0 # via pytest -ipykernel==6.23.1 - # via - # ipywidgets - # nbclassic - # notebook -ipython==8.14.0 +ipykernel==6.25.2 + # via jupyterlab +ipython==8.15.0 # via # great-expectations # ipykernel # ipywidgets -ipython-genutils==0.2.0 - # via - # nbclassic - # notebook -ipywidgets==8.0.6 +ipywidgets==8.1.0 # via great-expectations isodate==0.6.1 # via @@ -393,7 +395,7 @@ isoduration==20.11.0 # via jsonschema isort==5.12.0 # via feast (setup.py) -jedi==0.18.2 +jedi==0.19.0 # via ipython jinja2==3.1.2 # via @@ -401,56 +403,69 @@ jinja2==3.1.2 # feast (setup.py) # great-expectations # jupyter-server + # jupyterlab + # jupyterlab-server # moto - # nbclassic # nbconvert - # notebook # sphinx jmespath==1.0.1 # via # boto3 # botocore -jsonpatch==1.32 +json5==0.9.14 + # via jupyterlab-server +jsonpatch==1.33 # via great-expectations -jsonpointer==2.3 +jsonpointer==2.4 # via # jsonpatch # jsonschema -jsonschema[format-nongpl]==4.17.3 +jsonschema[format-nongpl]==4.19.0 # via # altair # feast (setup.py) # great-expectations # jupyter-events + # jupyterlab-server # nbformat -jupyter-client==8.2.0 +jsonschema-specifications==2023.7.1 + # via jsonschema +jupyter-client==8.3.1 # via # ipykernel # jupyter-server - # nbclassic # nbclient - # notebook -jupyter-core==5.3.0 +jupyter-core==5.3.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # nbclient # nbconvert # nbformat - # notebook -jupyter-events==0.6.3 +jupyter-events==0.7.0 # via jupyter-server -jupyter-server==2.6.0 +jupyter-lsp==2.2.0 + # via jupyterlab +jupyter-server==2.7.3 # via - # nbclassic + # jupyter-lsp + # jupyterlab + # jupyterlab-server + # notebook # notebook-shim jupyter-server-terminals==0.4.4 # via jupyter-server +jupyterlab==4.0.5 + # via notebook jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-widgets==3.0.7 +jupyterlab-server==2.24.0 + # via + # jupyterlab + # notebook +jupyterlab-widgets==3.0.8 # via ipywidgets kubernetes==20.13.0 # via feast (setup.py) @@ -463,7 +478,7 @@ markupsafe==2.1.3 # jinja2 # nbconvert # werkzeug -marshmallow==3.19.0 +marshmallow==3.20.1 # via great-expectations matplotlib-inline==0.1.6 # via @@ -473,19 +488,19 @@ mccabe==0.7.0 # via flake8 minio==7.1.0 # via feast (setup.py) -mistune==2.0.5 +mistune==3.0.1 # via # great-expectations # nbconvert -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) mock==2.0.0 # via feast (setup.py) moreorless==0.4.0 # via bowler -moto==4.1.10 +moto==4.2.2 # via feast (setup.py) -msal==1.22.0 +msal==1.23.0 # via # azure-datalake-store # azure-identity @@ -502,7 +517,7 @@ multidict==6.0.4 # via # aiohttp # yarl -multiprocess==0.70.14 +multiprocess==0.70.15 # via bytewax mypy==0.982 # via @@ -514,37 +529,29 @@ mypy-extensions==1.0.0 # mypy mypy-protobuf==3.1 # via feast (setup.py) -mysqlclient==2.1.1 +mysqlclient==2.2.0 # via feast (setup.py) -nbclassic==1.0.0 - # via notebook nbclient==0.8.0 # via nbconvert -nbconvert==7.4.0 - # via - # jupyter-server - # nbclassic - # notebook -nbformat==5.9.0 +nbconvert==7.8.0 + # via jupyter-server +nbformat==5.9.2 # via # great-expectations # jupyter-server - # nbclassic # nbclient # nbconvert - # notebook -nest-asyncio==1.5.6 - # via - # ipykernel - # nbclassic - # notebook +nest-asyncio==1.5.7 + # via ipykernel nodeenv==1.8.0 # via pre-commit -notebook==6.5.4 +notebook==7.0.3 # via great-expectations notebook-shim==0.2.3 - # via nbclassic -numpy==1.24.3 + # via + # jupyterlab + # notebook +numpy==1.25.2 # via # altair # db-dtypes @@ -558,7 +565,7 @@ oauthlib==3.2.2 # via requests-oauthlib oscrypto==1.3.0 # via snowflake-connector-python -overrides==7.3.1 +overrides==7.4.0 # via jupyter-server packaging==23.1 # via @@ -569,8 +576,11 @@ packaging==23.1 # docker # google-cloud-bigquery # great-expectations + # gunicorn # ipykernel # jupyter-server + # jupyterlab + # jupyterlab-server # marshmallow # nbconvert # pytest @@ -594,7 +604,7 @@ parso==0.8.3 # via jedi partd==1.4.0 # via dask -pathspec==0.11.1 +pathspec==0.11.2 # via black pbr==5.11.1 # via mock @@ -602,29 +612,27 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -pip-tools==6.13.0 +pip-tools==7.3.0 # via feast (setup.py) -platformdirs==3.5.1 +platformdirs==3.8.1 # via # black # jupyter-core + # snowflake-connector-python # virtualenv -pluggy==1.0.0 +pluggy==1.3.0 # via pytest ply==3.11 # via thriftpy2 portalocker==2.7.0 # via msal-extensions -pre-commit==3.3.2 +pre-commit==3.3.1 # via feast (setup.py) -prometheus-client==0.17.0 - # via - # jupyter-server - # nbclassic - # notebook -prompt-toolkit==3.0.38 +prometheus-client==0.17.1 + # via jupyter-server +prompt-toolkit==3.0.39 # via ipython -proto-plus==1.22.2 +proto-plus==1.22.3 # via # feast (setup.py) # google-cloud-bigquery @@ -632,7 +640,7 @@ proto-plus==1.22.2 # google-cloud-bigtable # google-cloud-datastore # google-cloud-firestore -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) # google-api-core @@ -643,6 +651,7 @@ protobuf==4.23.2 # google-cloud-firestore # googleapis-common-protos # grpc-google-iam-v1 + # grpcio-health-checking # grpcio-reflection # grpcio-status # grpcio-testing @@ -653,7 +662,7 @@ psutil==5.9.0 # via # feast (setup.py) # ipykernel -psycopg2-binary==2.9.6 +psycopg2-binary==2.9.7 # via feast (setup.py) ptyprocess==0.7.0 # via @@ -687,43 +696,41 @@ pycparser==2.21 # via cffi pycryptodomex==3.18.0 # via snowflake-connector-python -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) # great-expectations pyflakes==3.0.1 # via flake8 -pygments==2.15.1 +pygments==2.16.1 # via # feast (setup.py) # ipython # nbconvert # sphinx -pyjwt[crypto]==2.7.0 +pyjwt[crypto]==2.8.0 # via # adal # msal # snowflake-connector-python pymssql==2.2.8 # via feast (setup.py) -pymysql==1.0.3 +pymysql==1.1.0 # via feast (setup.py) pyodbc==4.0.39 # via feast (setup.py) pyopenssl==23.2.0 # via snowflake-connector-python -pyparsing==3.0.9 +pyparsing==3.1.1 # via # great-expectations # httplib2 pyproject-hooks==1.0.0 # via build -pyrsistent==0.19.3 - # via jsonschema -pyspark==3.4.0 +pyspark==3.4.1 # via feast (setup.py) -pytest==7.3.1 +pytest==7.4.1 # via # feast (setup.py) # pytest-benchmark @@ -764,7 +771,7 @@ python-dotenv==1.0.0 # via uvicorn python-json-logger==2.0.7 # via jupyter-events -pytz==2023.3 +pytz==2023.3.post1 # via # great-expectations # pandas @@ -779,16 +786,19 @@ pyyaml==6.0.1 # pre-commit # responses # uvicorn -pyzmq==25.1.0 +pyzmq==25.1.1 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic - # notebook redis==4.2.2 # via feast (setup.py) -regex==2023.5.5 +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications + # jupyter-events +regex==2023.8.8 # via feast (setup.py) requests==2.31.0 # via @@ -804,6 +814,7 @@ requests==2.31.0 # google-cloud-bigquery # google-cloud-storage # great-expectations + # jupyterlab-server # kubernetes # moto # msal @@ -818,7 +829,7 @@ requests-oauthlib==1.3.1 # google-auth-oauthlib # kubernetes # msrest -responses==0.23.1 +responses==0.23.3 # via moto rfc3339-validator==0.1.4 # via @@ -828,28 +839,28 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rockset==2.0.0 +rockset==2.1.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing rsa==4.9 # via google-auth ruamel-yaml==0.17.17 # via great-expectations ruamel-yaml-clib==0.2.7 # via ruamel-yaml -s3transfer==0.6.1 +s3transfer==0.6.2 # via boto3 -scipy==1.10.1 +scipy==1.11.2 # via great-expectations send2trash==1.8.2 - # via - # jupyter-server - # nbclassic - # notebook + # via jupyter-server six==1.16.0 # via # asttokens # azure-core - # azure-identity # bleach # cassandra-driver # geomet @@ -871,29 +882,35 @@ sniffio==1.3.0 # httpx snowballstemmer==2.2.0 # via sphinx -snowflake-connector-python[pandas]==3.0.4 +snowflake-connector-python[pandas]==3.1.1 # via feast (setup.py) sortedcontainers==2.4.0 # via snowflake-connector-python -soupsieve==2.4.1 +soupsieve==2.5 # via beautifulsoup4 sphinx==6.2.1 - # via feast (setup.py) -sphinxcontrib-applehelp==1.0.4 + # via + # feast (setup.py) + # sphinxcontrib-applehelp + # sphinxcontrib-devhelp + # sphinxcontrib-htmlhelp + # sphinxcontrib-qthelp + # sphinxcontrib-serializinghtml +sphinxcontrib-applehelp==1.0.7 # via sphinx -sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-devhelp==1.0.5 # via sphinx -sphinxcontrib-htmlhelp==2.0.1 +sphinxcontrib-htmlhelp==2.0.4 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-qthelp==1.0.6 # via sphinx -sphinxcontrib-serializinghtml==1.1.5 +sphinxcontrib-serializinghtml==1.1.9 # via sphinx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy stack-data==0.6.2 # via ipython @@ -901,14 +918,12 @@ starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) terminado==0.17.1 # via # jupyter-server # jupyter-server-terminals - # nbclassic - # notebook testcontainers==3.7.1 # via feast (setup.py) thriftpy2==0.4.16 @@ -922,23 +937,27 @@ tomli==2.0.1 # black # build # coverage + # jupyterlab # mypy + # pip-tools # pyproject-hooks # pytest +tomlkit==0.12.1 + # via snowflake-connector-python toolz==0.12.0 # via # altair # dask # partd -tornado==6.3.2 +tornado==6.3.3 # via # ipykernel # jupyter-client # jupyter-server - # nbclassic + # jupyterlab # notebook # terminado -tqdm==4.65.0 +tqdm==4.66.1 # via # feast (setup.py) # great-expectations @@ -952,12 +971,11 @@ traitlets==5.9.0 # jupyter-core # jupyter-events # jupyter-server + # jupyterlab # matplotlib-inline - # nbclassic # nbclient # nbconvert # nbformat - # notebook trino==0.326.0 # via feast (setup.py) typeguard==2.13.3 @@ -966,33 +984,36 @@ types-protobuf==3.19.22 # via # feast (setup.py) # mypy-protobuf -types-pymysql==1.0.19.7 +types-pymysql==1.1.0.1 # via feast (setup.py) -types-pyopenssl==23.2.0.0 +types-pyopenssl==23.2.0.2 # via types-redis -types-python-dateutil==2.8.19.13 +types-python-dateutil==2.8.19.14 # via feast (setup.py) -types-pytz==2023.3.0.0 +types-pytz==2023.3.0.1 # via feast (setup.py) -types-pyyaml==6.0.12.10 +types-pyyaml==6.0.12.11 # via # feast (setup.py) # responses -types-redis==4.5.5.2 +types-redis==4.6.0.5 # via feast (setup.py) -types-requests==2.31.0.1 +types-requests==2.31.0.2 # via feast (setup.py) -types-setuptools==67.8.0.0 +types-setuptools==68.2.0.0 # via feast (setup.py) -types-tabulate==0.9.0.2 +types-tabulate==0.9.0.3 # via feast (setup.py) -types-urllib3==1.26.25.13 +types-urllib3==1.26.25.14 # via types-requests -typing-extensions==4.6.3 +typing-extensions==4.7.1 # via + # async-lru # azure-core # azure-storage-blob # black + # fastapi + # filelock # great-expectations # ipython # mypy @@ -1000,11 +1021,12 @@ typing-extensions==4.6.3 # snowflake-connector-python # sqlalchemy2-stubs # starlette + # uvicorn tzlocal==5.0.1 # via # great-expectations # trino -uri-template==1.2.0 +uri-template==1.3.0 # via jsonschema uritemplate==4.1.1 # via google-api-python-client @@ -1021,15 +1043,17 @@ urllib3==1.26.16 # responses # rockset # snowflake-connector-python -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn virtualenv==20.23.0 - # via pre-commit + # via + # feast (setup.py) + # pre-commit volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn wcwidth==0.2.6 # via prompt-toolkit @@ -1039,18 +1063,18 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.2 +websocket-client==1.6.2 # via # docker # jupyter-server # kubernetes websockets==11.0.3 # via uvicorn -werkzeug==2.3.4 +werkzeug==2.3.7 # via moto -wheel==0.40.0 +wheel==0.41.2 # via pip-tools -widgetsnbextension==4.0.7 +widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 # via @@ -1060,7 +1084,7 @@ xmltodict==0.13.0 # via moto yarl==1.9.2 # via aiohttp -zipp==3.15.0 +zipp==3.16.2 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/sdk/python/requirements/py3.9-requirements.txt b/sdk/python/requirements/py3.9-requirements.txt index 53b0ff0e58..7d30fd3452 100644 --- a/sdk/python/requirements/py3.9-requirements.txt +++ b/sdk/python/requirements/py3.9-requirements.txt @@ -1,10 +1,10 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # pip-compile --output-file=sdk/python/requirements/py3.9-requirements.txt # -anyio==3.7.0 +anyio==4.0.0 # via # httpcore # starlette @@ -15,16 +15,17 @@ attrs==23.1.0 # via # bowler # jsonschema + # referencing bowler==0.9.0 # via feast (setup.py) -certifi==2023.5.7 +certifi==2023.7.22 # via # httpcore # httpx # requests -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via requests -click==8.1.3 +click==8.1.7 # via # bowler # dask @@ -35,39 +36,43 @@ cloudpickle==2.2.1 # via dask colorama==0.4.6 # via feast (setup.py) -dask==2023.5.1 +dask==2023.9.1 # via feast (setup.py) -dill==0.3.6 +dill==0.3.7 # via feast (setup.py) -exceptiongroup==1.1.1 +exceptiongroup==1.1.3 # via anyio -fastapi==0.95.2 +fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.1 +fastavro==1.8.2 # via # feast (setup.py) # pandavro fissix==21.11.13 # via bowler -fsspec==2023.5.0 +fsspec==2023.9.0 # via dask -greenlet==2.0.2 - # via sqlalchemy -grpcio==1.54.2 +grpcio==1.57.0 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection -grpcio-reflection==1.54.2 + # grpcio-tools +grpcio-health-checking==1.57.0 + # via feast (setup.py) +grpcio-reflection==1.57.0 # via feast (setup.py) -gunicorn==20.1.0 +grpcio-tools==1.57.0 + # via feast (setup.py) +gunicorn==21.2.0 # via feast (setup.py) h11==0.14.0 # via # httpcore # uvicorn -httpcore==0.17.2 +httpcore==0.17.3 # via httpx -httptools==0.5.0 +httptools==0.6.0 # via uvicorn httpx==0.24.1 # via feast (setup.py) @@ -76,32 +81,38 @@ idna==3.4 # anyio # httpx # requests -importlib-metadata==6.6.0 +importlib-metadata==6.8.0 # via dask jinja2==3.1.2 # via feast (setup.py) -jsonschema==4.17.3 +jsonschema==4.19.0 # via feast (setup.py) +jsonschema-specifications==2023.7.1 + # via jsonschema locket==1.0.0 # via partd markupsafe==2.1.3 # via jinja2 -mmh3==4.0.0 +mmh3==4.0.1 # via feast (setup.py) moreorless==0.4.0 # via bowler -mypy==1.3.0 +mypy==1.5.1 # via sqlalchemy mypy-extensions==1.0.0 # via mypy -numpy==1.24.3 +mypy-protobuf==3.1.0 + # via feast (setup.py) +numpy==1.25.2 # via # feast (setup.py) # pandas # pandavro # pyarrow packaging==23.1 - # via dask + # via + # dask + # gunicorn pandas==1.5.3 # via # feast (setup.py) @@ -110,36 +121,45 @@ pandavro==1.5.2 # via feast (setup.py) partd==1.4.0 # via dask -proto-plus==1.22.2 +proto-plus==1.22.3 # via feast (setup.py) -protobuf==4.23.2 +protobuf==4.23.3 # via # feast (setup.py) + # grpcio-health-checking # grpcio-reflection + # grpcio-tools + # mypy-protobuf # proto-plus pyarrow==11.0.0 # via feast (setup.py) -pydantic==1.10.8 +pydantic==1.10.12 # via # fastapi # feast (setup.py) -pygments==2.15.1 +pygments==2.16.1 # via feast (setup.py) -pyrsistent==0.19.3 - # via jsonschema python-dateutil==2.8.2 # via pandas python-dotenv==1.0.0 # via uvicorn -pytz==2023.3 +pytz==2023.3.post1 # via pandas pyyaml==6.0.1 # via # dask # feast (setup.py) # uvicorn +referencing==0.30.2 + # via + # jsonschema + # jsonschema-specifications requests==2.31.0 # via feast (setup.py) +rpds-py==0.10.2 + # via + # jsonschema + # referencing six==1.16.0 # via # pandavro @@ -149,15 +169,15 @@ sniffio==1.3.0 # anyio # httpcore # httpx -sqlalchemy[mypy]==1.4.48 +sqlalchemy[mypy]==1.4.49 # via feast (setup.py) -sqlalchemy2-stubs==0.0.2a34 +sqlalchemy2-stubs==0.0.2a35 # via sqlalchemy starlette==0.27.0 # via fastapi tabulate==0.9.0 # via feast (setup.py) -tenacity==8.2.2 +tenacity==8.2.3 # via feast (setup.py) toml==0.10.2 # via feast (setup.py) @@ -167,27 +187,34 @@ toolz==0.12.0 # via # dask # partd -tqdm==4.65.0 +tqdm==4.66.1 # via feast (setup.py) typeguard==2.13.3 # via feast (setup.py) -typing-extensions==4.6.3 +types-protobuf==4.24.0.1 + # via mypy-protobuf +typing-extensions==4.7.1 # via + # fastapi # mypy # pydantic # sqlalchemy2-stubs # starlette -urllib3==2.0.2 + # uvicorn +urllib3==2.0.4 # via requests -uvicorn[standard]==0.22.0 +uvicorn[standard]==0.23.2 # via feast (setup.py) uvloop==0.17.0 # via uvicorn volatile==2.1.0 # via bowler -watchfiles==0.19.0 +watchfiles==0.20.0 # via uvicorn websockets==11.0.3 # via uvicorn -zipp==3.15.0 +zipp==3.16.2 # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/setup.py b/setup.py index f10322ed8a..699d394940 100644 --- a/setup.py +++ b/setup.py @@ -46,8 +46,11 @@ "colorama>=0.3.9,<1", "dill~=0.3.0", "fastavro>=1.1.0,<2", - "grpcio>=1.47.0,<2", - "grpcio-reflection>=1.47.0,<2", + "grpcio>=1.56.2,<2", + "grpcio-tools>=1.56.2,<2", + "grpcio-reflection>=1.56.2,<2", + "grpcio-health-checking>=1.56.2,<2", + "mypy-protobuf==3.1", "Jinja2>=2,<4", "jsonschema", "mmh3", @@ -69,7 +72,7 @@ "toml>=0.10.0,<1", "tqdm>=4,<5", "typeguard==2.13.3", - "fastapi>=0.68.0,<1", + "fastapi>=0.68.0,<0.100", "uvicorn[standard]>=0.14.0,<1", "gunicorn", "dask>=2021.1.0", @@ -142,17 +145,16 @@ CI_REQUIRED = ( [ "build", + "virtualenv==20.23.0", "cryptography>=35.0,<42", - "flake8", + "flake8>=6.0.0,<6.1.0", "black>=22.6.0,<23", "isort>=5,<6", - "grpcio-tools>=1.47.0", - "grpcio-testing>=1.47.0", + "grpcio-testing>=1.56.2,<2", "minio==7.1.0", "mock==2.0.0", "moto", "mypy>=0.981,<0.990", - "mypy-protobuf==3.1", "avro==1.10.0", "gcsfs>=0.4.0,<=2022.01.0", "urllib3>=1.25.4,<2", @@ -170,7 +172,7 @@ "testcontainers>=3.5,<4", "adlfs==0.5.9", "firebase-admin>=5.2.0,<6", - "pre-commit", + "pre-commit<3.3.2", "assertpy==1.1", "pip-tools", "pybindgen", @@ -182,6 +184,7 @@ "types-requests", "types-setuptools", "types-tabulate", + "virtualenv<20.24.2" ] + GCP_REQUIRED + REDIS_REQUIRED @@ -380,8 +383,8 @@ def run(self): use_scm_version=use_scm_version, setup_requires=[ "setuptools_scm", - "grpcio>=1.47.0", - "grpcio-tools>=1.47.0", + "grpcio>=1.56.2,<2", + "grpcio-tools>=1.56.2,<2", "mypy-protobuf==3.1", "pybindgen==0.22.0", ], From 6474b4b0169dc9b3df8e8daecded2b1fad5ead58 Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 7 Sep 2023 11:51:00 +0700 Subject: [PATCH 07/19] fix: Add NUMERIC to bq_to_feast type map (#3719) Signed-off-by: Hai Nguyen --- sdk/python/feast/type_map.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index d246fb4054..3f49069066 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -528,6 +528,7 @@ def bq_to_feast_value_type(bq_type_as_str: str) -> ValueType: "DATETIME": ValueType.UNIX_TIMESTAMP, "TIMESTAMP": ValueType.UNIX_TIMESTAMP, "INTEGER": ValueType.INT64, + "NUMERIC": ValueType.INT64, "INT64": ValueType.INT64, "STRING": ValueType.STRING, "FLOAT": ValueType.DOUBLE, From 0049356b3243a5c16760541520640343023f6ea7 Mon Sep 17 00:00:00 2001 From: Breno Costa <35263725+breno-costa@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:31:28 +0200 Subject: [PATCH 08/19] chore: Widen redis requirement to make it easier to install alongside other requirements (#3755) * chore: wide redis dependency to make it easier to install alongside other requirements Signed-off-by: Breno Costa * update requirements Signed-off-by: Danny C --------- Signed-off-by: Breno Costa Signed-off-by: Danny C Co-authored-by: Danny C --- sdk/python/requirements/py3.10-ci-requirements.txt | 9 ++------- sdk/python/requirements/py3.8-ci-requirements.txt | 9 ++------- sdk/python/requirements/py3.9-ci-requirements.txt | 11 +++-------- setup.py | 2 +- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index cb72fdaa35..a59553b4ac 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -180,8 +180,6 @@ decorator==5.1.1 # ipython defusedxml==0.7.1 # via nbconvert -deprecated==1.2.14 - # via redis deprecation==2.1.0 # via testcontainers dill==0.3.7 @@ -577,7 +575,6 @@ packaging==23.1 # marshmallow # nbconvert # pytest - # redis # snowflake-connector-python # sphinx pandas==1.5.3 @@ -784,7 +781,7 @@ pyzmq==25.1.1 # ipykernel # jupyter-client # jupyter-server -redis==4.2.2 +redis==4.6.0 # via feast (setup.py) referencing==0.30.2 # via @@ -1065,9 +1062,7 @@ wheel==0.41.2 widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 - # via - # deprecated - # testcontainers + # via testcontainers xmltodict==0.13.0 # via moto yarl==1.9.2 diff --git a/sdk/python/requirements/py3.8-ci-requirements.txt b/sdk/python/requirements/py3.8-ci-requirements.txt index 9dfefc2108..b24172e890 100644 --- a/sdk/python/requirements/py3.8-ci-requirements.txt +++ b/sdk/python/requirements/py3.8-ci-requirements.txt @@ -184,8 +184,6 @@ decorator==5.1.1 # ipython defusedxml==0.7.1 # via nbconvert -deprecated==1.2.14 - # via redis deprecation==2.1.0 # via testcontainers dill==0.3.7 @@ -592,7 +590,6 @@ packaging==23.1 # marshmallow # nbconvert # pytest - # redis # snowflake-connector-python # sphinx pandas==1.5.3 @@ -802,7 +799,7 @@ pyzmq==25.1.1 # ipykernel # jupyter-client # jupyter-server -redis==4.2.2 +redis==4.6.0 # via feast (setup.py) referencing==0.30.2 # via @@ -1082,9 +1079,7 @@ wheel==0.41.2 widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 - # via - # deprecated - # testcontainers + # via testcontainers xmltodict==0.13.0 # via moto yarl==1.9.2 diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt index 3992303d00..ad19f9e8bd 100644 --- a/sdk/python/requirements/py3.9-ci-requirements.txt +++ b/sdk/python/requirements/py3.9-ci-requirements.txt @@ -180,8 +180,6 @@ decorator==5.1.1 # ipython defusedxml==0.7.1 # via nbconvert -deprecated==1.2.14 - # via redis deprecation==2.1.0 # via testcontainers dill==0.3.7 @@ -527,7 +525,7 @@ mypy-extensions==1.0.0 # via # black # mypy -mypy-protobuf==3.1 +mypy-protobuf==3.1.0 # via feast (setup.py) mysqlclient==2.2.0 # via feast (setup.py) @@ -584,7 +582,6 @@ packaging==23.1 # marshmallow # nbconvert # pytest - # redis # snowflake-connector-python # sphinx pandas==1.5.3 @@ -791,7 +788,7 @@ pyzmq==25.1.1 # ipykernel # jupyter-client # jupyter-server -redis==4.2.2 +redis==4.6.0 # via feast (setup.py) referencing==0.30.2 # via @@ -1077,9 +1074,7 @@ wheel==0.41.2 widgetsnbextension==4.0.8 # via ipywidgets wrapt==1.15.0 - # via - # deprecated - # testcontainers + # via testcontainers xmltodict==0.13.0 # via moto yarl==1.9.2 diff --git a/setup.py b/setup.py index 699d394940..573ab54d51 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ ] REDIS_REQUIRED = [ - "redis==4.2.2", + "redis>=4.2.2,<5", "hiredis>=2.0.0,<3", ] From f28ccc2b8f42bcca943d498ad583337d4cd70383 Mon Sep 17 00:00:00 2001 From: Malcolm Keyes <78397791+malcolmk181@users.noreply.github.com> Date: Thu, 7 Sep 2023 01:51:00 -0400 Subject: [PATCH 09/19] fix: Saved datasets no longer break CLI registry-dump command (#3717) fix: Access saved dataset proto correctly in list_saved_datasets Signed-off-by: Malcolm Keyes --- sdk/python/feast/infra/registry/proto_registry_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/registry/proto_registry_utils.py b/sdk/python/feast/infra/registry/proto_registry_utils.py index 2a275703db..c7eeea0f82 100644 --- a/sdk/python/feast/infra/registry/proto_registry_utils.py +++ b/sdk/python/feast/infra/registry/proto_registry_utils.py @@ -214,7 +214,7 @@ def list_saved_datasets( ) -> List[SavedDataset]: saved_datasets = [] for saved_dataset in registry_proto.saved_datasets: - if saved_dataset.project == project: + if saved_dataset.spec.project == project: saved_datasets.append(SavedDataset.from_proto(saved_dataset)) return saved_datasets From ed7535e23d490249ca7d224fb88e53b98d496ec0 Mon Sep 17 00:00:00 2001 From: Dani Date: Thu, 7 Sep 2023 08:18:12 +0200 Subject: [PATCH 10/19] feat: Enhance customization of Trino connections when using Trino-based Offline Stores (#3699) * feat: Enhance customization of Trino connections when using Trino-based Offline Stores Signed-off-by: boliri * docs: Add new connection parameters to Trino Offline Store's reference Signed-off-by: boliri --------- Signed-off-by: boliri --- docs/reference/offline-stores/trino.md | 41 +++++ .../trino_offline_store/tests/data_source.py | 5 + .../contrib/trino_offline_store/trino.py | 164 ++++++++++++++---- .../trino_offline_store/trino_queries.py | 49 +++--- .../trino_offline_store/trino_source.py | 10 ++ 5 files changed, 209 insertions(+), 60 deletions(-) diff --git a/docs/reference/offline-stores/trino.md b/docs/reference/offline-stores/trino.md index 446db620e3..fd437a7aa6 100644 --- a/docs/reference/offline-stores/trino.md +++ b/docs/reference/offline-stores/trino.md @@ -27,6 +27,47 @@ offline_store: catalog: memory connector: type: memory + user: trino + source: feast-trino-offline-store + http-scheme: https + ssl-verify: false + x-trino-extra-credential-header: foo=bar, baz=qux + + # enables authentication in Trino connections, pick the one you need + # if you don't need authentication, you can safely remove the whole auth block + auth: + # Basic Auth + type: basic + config: + username: foo + password: $FOO + + # Certificate + type: certificate + config: + cert-file: /path/to/cert/file + key-file: /path/to/key/file + + # JWT + type: jwt + config: + token: $JWT_TOKEN + + # OAuth2 (no config required) + type: oauth2 + + # Kerberos + type: kerberos + config: + config-file: /path/to/kerberos/config/file + service-name: foo + mutual-authentication: true + force-preemptive: true + hostname-override: custom-hostname + sanitize-mutual-error-response: true + principal: principal-name + delegate: true + ca_bundle: /path/to/ca/bundle/file online_store: path: data/online_store.db ``` diff --git a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/data_source.py b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/data_source.py index 67efa6a27f..a5aa53df7a 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/data_source.py +++ b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/data_source.py @@ -67,6 +67,11 @@ def __init__( catalog="memory", host="localhost", port=self.exposed_port, + source="trino-python-client", + http_scheme="http", + verify=False, + extra_credential=None, + auth=None, ) def teardown(self): diff --git a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino.py b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino.py index e0f73404eb..f662cda913 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino.py +++ b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino.py @@ -1,12 +1,18 @@ import uuid from datetime import date, datetime -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Union import numpy as np import pandas as pd import pyarrow -from pydantic import StrictStr -from trino.auth import Authentication +from pydantic import Field, FilePath, SecretStr, StrictBool, StrictStr, root_validator +from trino.auth import ( + BasicAuthentication, + CertificateAuthentication, + JWTAuthentication, + KerberosAuthentication, + OAuth2Authentication, +) from feast.data_source import DataSource from feast.errors import InvalidEntityType @@ -32,6 +38,87 @@ from feast.usage import log_exceptions_and_usage +class BasicAuthModel(FeastConfigBaseModel): + username: StrictStr + password: SecretStr + + +class KerberosAuthModel(FeastConfigBaseModel): + config: Optional[FilePath] = Field(default=None, alias="config-file") + service_name: Optional[StrictStr] = Field(default=None, alias="service-name") + mutual_authentication: StrictBool = Field( + default=False, alias="mutual-authentication" + ) + force_preemptive: StrictBool = Field(default=False, alias="force-preemptive") + hostname_override: Optional[StrictStr] = Field( + default=None, alias="hostname-override" + ) + sanitize_mutual_error_response: StrictBool = Field( + default=True, alias="sanitize-mutual-error-response" + ) + principal: Optional[StrictStr] + delegate: StrictBool = False + ca_bundle: Optional[FilePath] = Field(default=None, alias="ca-bundle-file") + + +class JWTAuthModel(FeastConfigBaseModel): + token: SecretStr + + +class CertificateAuthModel(FeastConfigBaseModel): + cert: FilePath = Field(default=None, alias="cert-file") + key: FilePath = Field(default=None, alias="key-file") + + +CLASSES_BY_AUTH_TYPE = { + "kerberos": { + "auth_model": KerberosAuthModel, + "trino_auth": KerberosAuthentication, + }, + "basic": { + "auth_model": BasicAuthModel, + "trino_auth": BasicAuthentication, + }, + "jwt": { + "auth_model": JWTAuthModel, + "trino_auth": JWTAuthentication, + }, + "oauth2": { + "auth_model": None, + "trino_auth": OAuth2Authentication, + }, + "certificate": { + "auth_model": CertificateAuthModel, + "trino_auth": CertificateAuthentication, + }, +} + + +class AuthConfig(FeastConfigBaseModel): + type: Literal["kerberos", "basic", "jwt", "oauth2", "certificate"] + config: Optional[Dict[StrictStr, Any]] + + @root_validator + def config_only_nullable_for_oauth2(cls, values): + auth_type = values["type"] + auth_config = values["config"] + if auth_type != "oauth2" and auth_config is None: + raise ValueError(f"config cannot be null for auth type '{auth_type}'") + + return values + + def to_trino_auth(self): + auth_type = self.type + trino_auth_cls = CLASSES_BY_AUTH_TYPE[auth_type]["trino_auth"] + + if auth_type == "oauth2": + return trino_auth_cls() + + model_cls = CLASSES_BY_AUTH_TYPE[auth_type]["auth_model"] + model = model_cls(**self.config) + return trino_auth_cls(**model.dict()) + + class TrinoOfflineStoreConfig(FeastConfigBaseModel): """Online store config for Trino""" @@ -47,6 +134,23 @@ class TrinoOfflineStoreConfig(FeastConfigBaseModel): catalog: StrictStr """ Catalog of the Trino cluster """ + user: StrictStr + """ User of the Trino cluster """ + + source: Optional[StrictStr] = "trino-python-client" + """ ID of the feast's Trino Python client, useful for debugging """ + + http_scheme: Literal["http", "https"] = Field(default="http", alias="http-scheme") + """ HTTP scheme that should be used while establishing a connection to the Trino cluster """ + + verify: StrictBool = Field(default=True, alias="ssl-verify") + """ Whether the SSL certificate emited by the Trino cluster should be verified or not """ + + extra_credential: Optional[StrictStr] = Field( + default=None, alias="x-trino-extra-credential-header" + ) + """ Specifies the HTTP header X-Trino-Extra-Credential, e.g. user1=pwd1, user2=pwd2 """ + connector: Dict[str, str] """ Trino connector to use as well as potential extra parameters. @@ -59,6 +163,16 @@ class TrinoOfflineStoreConfig(FeastConfigBaseModel): dataset: StrictStr = "feast" """ (optional) Trino Dataset name for temporary tables """ + auth: Optional[AuthConfig] + """ + (optional) Authentication mechanism to use when connecting to Trino. Supported options are: + - kerberos + - basic + - jwt + - oauth2 + - certificate + """ + class TrinoRetrievalJob(RetrievalJob): def __init__( @@ -162,9 +276,6 @@ def pull_latest_from_table_or_query( created_timestamp_column: Optional[str], start_date: datetime, end_date: datetime, - user: Optional[str] = None, - auth: Optional[Authentication] = None, - http_scheme: Optional[str] = None, ) -> TrinoRetrievalJob: assert isinstance(config.offline_store, TrinoOfflineStoreConfig) assert isinstance(data_source, TrinoSource) @@ -181,9 +292,7 @@ def pull_latest_from_table_or_query( timestamps.append(created_timestamp_column) timestamp_desc_string = " DESC, ".join(timestamps) + " DESC" field_string = ", ".join(join_key_columns + feature_name_columns + timestamps) - client = _get_trino_client( - config=config, user=user, auth=auth, http_scheme=http_scheme - ) + client = _get_trino_client(config=config) query = f""" SELECT @@ -216,17 +325,12 @@ def get_historical_features( registry: Registry, project: str, full_feature_names: bool = False, - user: Optional[str] = None, - auth: Optional[Authentication] = None, - http_scheme: Optional[str] = None, ) -> TrinoRetrievalJob: assert isinstance(config.offline_store, TrinoOfflineStoreConfig) for fv in feature_views: assert isinstance(fv.batch_source, TrinoSource) - client = _get_trino_client( - config=config, user=user, auth=auth, http_scheme=http_scheme - ) + client = _get_trino_client(config=config) table_reference = _get_table_reference_for_new_entity( catalog=config.offline_store.catalog, @@ -307,17 +411,12 @@ def pull_all_from_table_or_query( timestamp_field: str, start_date: datetime, end_date: datetime, - user: Optional[str] = None, - auth: Optional[Authentication] = None, - http_scheme: Optional[str] = None, ) -> RetrievalJob: assert isinstance(config.offline_store, TrinoOfflineStoreConfig) assert isinstance(data_source, TrinoSource) from_expression = data_source.get_table_query_string() - client = _get_trino_client( - config=config, user=user, auth=auth, http_scheme=http_scheme - ) + client = _get_trino_client(config=config) field_string = ", ".join( join_key_columns + feature_name_columns + [timestamp_field] ) @@ -378,21 +477,22 @@ def _upload_entity_df_and_get_entity_schema( # TODO: Ensure that the table expires after some time -def _get_trino_client( - config: RepoConfig, - user: Optional[str], - auth: Optional[Any], - http_scheme: Optional[str], -) -> Trino: - client = Trino( - user=user, - catalog=config.offline_store.catalog, +def _get_trino_client(config: RepoConfig) -> Trino: + auth = None + if config.offline_store.auth is not None: + auth = config.offline_store.auth.to_trino_auth() + + return Trino( host=config.offline_store.host, port=config.offline_store.port, + user=config.offline_store.user, + catalog=config.offline_store.catalog, + source=config.offline_store.source, + http_scheme=config.offline_store.http_scheme, + verify=config.offline_store.verify, + extra_credential=config.offline_store.extra_credential, auth=auth, - http_scheme=http_scheme, ) - return client def _get_entity_df_event_timestamp_range( diff --git a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_queries.py b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_queries.py index 97c61f78a6..50472407bc 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_queries.py +++ b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_queries.py @@ -1,7 +1,6 @@ from __future__ import annotations import datetime -import os import signal from dataclasses import dataclass from enum import Enum @@ -30,34 +29,27 @@ class QueryStatus(Enum): class Trino: def __init__( self, - host: Optional[str] = None, - port: Optional[int] = None, - user: Optional[str] = None, - catalog: Optional[str] = None, - auth: Optional[Any] = None, - http_scheme: Optional[str] = None, - source: Optional[str] = None, - extra_credential: Optional[str] = None, + host: str, + port: int, + user: str, + catalog: str, + source: Optional[str], + http_scheme: str, + verify: bool, + extra_credential: Optional[str], + auth: Optional[trino.Authentication], ): - self.host = host or os.getenv("TRINO_HOST") - self.port = port or os.getenv("TRINO_PORT") - self.user = user or os.getenv("TRINO_USER") - self.catalog = catalog or os.getenv("TRINO_CATALOG") - self.auth = auth or os.getenv("TRINO_AUTH") - self.http_scheme = http_scheme or os.getenv("TRINO_HTTP_SCHEME") - self.source = source or os.getenv("TRINO_SOURCE") - self.extra_credential = extra_credential or os.getenv("TRINO_EXTRA_CREDENTIAL") + self.host = host + self.port = port + self.user = user + self.catalog = catalog + self.source = source + self.http_scheme = http_scheme + self.verify = verify + self.extra_credential = extra_credential + self.auth = auth self._cursor: Optional[Cursor] = None - if self.host is None: - raise ValueError("TRINO_HOST must be set if not passed in") - if self.port is None: - raise ValueError("TRINO_PORT must be set if not passed in") - if self.user is None: - raise ValueError("TRINO_USER must be set if not passed in") - if self.catalog is None: - raise ValueError("TRINO_CATALOG must be set if not passed in") - def _get_cursor(self) -> Cursor: if self._cursor is None: headers = ( @@ -70,9 +62,10 @@ def _get_cursor(self) -> Cursor: port=self.port, user=self.user, catalog=self.catalog, - auth=self.auth, - http_scheme=self.http_scheme, source=self.source, + http_scheme=self.http_scheme, + verify=self.verify, + auth=self.auth, http_headers=headers, ).cursor() diff --git a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_source.py b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_source.py index f09b79069c..e618e8664e 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_source.py +++ b/sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_source.py @@ -227,10 +227,20 @@ def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: def get_table_column_names_and_types( self, config: RepoConfig ) -> Iterable[Tuple[str, str]]: + auth = None + if config.offline_store.auth is not None: + auth = config.offline_store.auth.to_trino_auth() + client = Trino( catalog=config.offline_store.catalog, host=config.offline_store.host, port=config.offline_store.port, + user=config.offline_store.user, + source=config.offline_store.source, + http_scheme=config.offline_store.http_scheme, + verify=config.offline_store.verify, + extra_credential=config.offline_store.extra_credential, + auth=auth, ) if self.table: table_schema = client.execute_query( From a8aeee9981fe75ffdc234d63c58cc99c5d3013db Mon Sep 17 00:00:00 2001 From: feast-ci-bot Date: Thu, 7 Sep 2023 06:53:29 +0000 Subject: [PATCH 11/19] chore(release): release 0.34.0 # [0.34.0](https://github.com/feast-dev/feast/compare/v0.33.0...v0.34.0) (2023-09-07) ### Bug Fixes * Add NUMERIC to bq_to_feast type map ([#3719](https://github.com/feast-dev/feast/issues/3719)) ([6474b4b](https://github.com/feast-dev/feast/commit/6474b4b0169dc9b3df8e8daecded2b1fad5ead58)) * Fix python unit tests ([#3734](https://github.com/feast-dev/feast/issues/3734)) ([e81684d](https://github.com/feast-dev/feast/commit/e81684d4f7916c986fa8e6cf06c2918951469799)) * Handle unknown postgres source types gracefully ([#3634](https://github.com/feast-dev/feast/issues/3634)) ([d7041f4](https://github.com/feast-dev/feast/commit/d7041f4cce813d349e9016da55d65a65c1ec2355)) * Pin protobuf version to avoid seg fault on some machines ([028cc20](https://github.com/feast-dev/feast/commit/028cc20a28118bd31deca8965782d5ad25f74300)) * Remove unwanted excessive splitting of gcs path, so expected gcs parquet paths are returned from BigQueryRetrievalJob.to_remote_storage() ([#3730](https://github.com/feast-dev/feast/issues/3730)) ([f2c5988](https://github.com/feast-dev/feast/commit/f2c59885e31f3f238dbd9c13cd1ba168e3233a9d)) * Run store.plan() only when need it. ([#3708](https://github.com/feast-dev/feast/issues/3708)) ([7bc7c47](https://github.com/feast-dev/feast/commit/7bc7c47b4507310850474290131c03fb6d480834)) * Saved datasets no longer break CLI registry-dump command ([#3717](https://github.com/feast-dev/feast/issues/3717)) ([f28ccc2](https://github.com/feast-dev/feast/commit/f28ccc2b8f42bcca943d498ad583337d4cd70383)) * Update py3.8 ci requirements for cython 3.0 release ([#3735](https://github.com/feast-dev/feast/issues/3735)) ([1695c13](https://github.com/feast-dev/feast/commit/1695c13fa8f48fdc2b5e627837043c5eea0914a9)) ### Features * Enhance customization of Trino connections when using Trino-based Offline Stores ([#3699](https://github.com/feast-dev/feast/issues/3699)) ([ed7535e](https://github.com/feast-dev/feast/commit/ed7535e23d490249ca7d224fb88e53b98d496ec0)) * Implement gRPC server to ingest streaming features ([#3687](https://github.com/feast-dev/feast/issues/3687)) ([a3fcd1f](https://github.com/feast-dev/feast/commit/a3fcd1f369bdf07174b5ecf2a49ca9864cf145d4)) --- CHANGELOG.md | 20 +++++++++++++++++++ infra/charts/feast-feature-server/Chart.yaml | 2 +- infra/charts/feast-feature-server/README.md | 4 ++-- infra/charts/feast-feature-server/values.yaml | 2 +- infra/charts/feast/Chart.yaml | 2 +- infra/charts/feast/README.md | 6 +++--- .../feast/charts/feature-server/Chart.yaml | 4 ++-- .../feast/charts/feature-server/README.md | 4 ++-- .../feast/charts/feature-server/values.yaml | 2 +- .../charts/transformation-service/Chart.yaml | 4 ++-- .../charts/transformation-service/README.md | 4 ++-- .../charts/transformation-service/values.yaml | 2 +- infra/charts/feast/requirements.yaml | 4 ++-- java/pom.xml | 2 +- sdk/python/feast/ui/package.json | 2 +- sdk/python/feast/ui/yarn.lock | 8 ++++---- ui/package.json | 2 +- 17 files changed, 47 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 585be439ba..f6e5a430f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +# [0.34.0](https://github.com/feast-dev/feast/compare/v0.33.0...v0.34.0) (2023-09-07) + + +### Bug Fixes + +* Add NUMERIC to bq_to_feast type map ([#3719](https://github.com/feast-dev/feast/issues/3719)) ([6474b4b](https://github.com/feast-dev/feast/commit/6474b4b0169dc9b3df8e8daecded2b1fad5ead58)) +* Fix python unit tests ([#3734](https://github.com/feast-dev/feast/issues/3734)) ([e81684d](https://github.com/feast-dev/feast/commit/e81684d4f7916c986fa8e6cf06c2918951469799)) +* Handle unknown postgres source types gracefully ([#3634](https://github.com/feast-dev/feast/issues/3634)) ([d7041f4](https://github.com/feast-dev/feast/commit/d7041f4cce813d349e9016da55d65a65c1ec2355)) +* Pin protobuf version to avoid seg fault on some machines ([028cc20](https://github.com/feast-dev/feast/commit/028cc20a28118bd31deca8965782d5ad25f74300)) +* Remove unwanted excessive splitting of gcs path, so expected gcs parquet paths are returned from BigQueryRetrievalJob.to_remote_storage() ([#3730](https://github.com/feast-dev/feast/issues/3730)) ([f2c5988](https://github.com/feast-dev/feast/commit/f2c59885e31f3f238dbd9c13cd1ba168e3233a9d)) +* Run store.plan() only when need it. ([#3708](https://github.com/feast-dev/feast/issues/3708)) ([7bc7c47](https://github.com/feast-dev/feast/commit/7bc7c47b4507310850474290131c03fb6d480834)) +* Saved datasets no longer break CLI registry-dump command ([#3717](https://github.com/feast-dev/feast/issues/3717)) ([f28ccc2](https://github.com/feast-dev/feast/commit/f28ccc2b8f42bcca943d498ad583337d4cd70383)) +* Update py3.8 ci requirements for cython 3.0 release ([#3735](https://github.com/feast-dev/feast/issues/3735)) ([1695c13](https://github.com/feast-dev/feast/commit/1695c13fa8f48fdc2b5e627837043c5eea0914a9)) + + +### Features + +* Enhance customization of Trino connections when using Trino-based Offline Stores ([#3699](https://github.com/feast-dev/feast/issues/3699)) ([ed7535e](https://github.com/feast-dev/feast/commit/ed7535e23d490249ca7d224fb88e53b98d496ec0)) +* Implement gRPC server to ingest streaming features ([#3687](https://github.com/feast-dev/feast/issues/3687)) ([a3fcd1f](https://github.com/feast-dev/feast/commit/a3fcd1f369bdf07174b5ecf2a49ca9864cf145d4)) + # [0.33.0](https://github.com/feast-dev/feast/compare/v0.32.0...v0.33.0) (2023-08-14) diff --git a/infra/charts/feast-feature-server/Chart.yaml b/infra/charts/feast-feature-server/Chart.yaml index 3ab493fc5f..6631d37784 100644 --- a/infra/charts/feast-feature-server/Chart.yaml +++ b/infra/charts/feast-feature-server/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: feast-feature-server description: Feast Feature Server in Go or Python type: application -version: 0.33.0 +version: 0.34.0 keywords: - machine learning - big data diff --git a/infra/charts/feast-feature-server/README.md b/infra/charts/feast-feature-server/README.md index e175f78510..ad88d08217 100644 --- a/infra/charts/feast-feature-server/README.md +++ b/infra/charts/feast-feature-server/README.md @@ -1,6 +1,6 @@ # Feast Python / Go Feature Server Helm Charts -Current chart version is `0.33.0` +Current chart version is `0.34.0` ## Installation @@ -30,7 +30,7 @@ See [here](https://github.com/feast-dev/feast/tree/master/examples/python-helm-d | fullnameOverride | string | `""` | | | image.pullPolicy | string | `"IfNotPresent"` | | | image.repository | string | `"feastdev/feature-server"` | Docker image for Feature Server repository | -| image.tag | string | `"0.33.0"` | The Docker image tag (can be overwritten if custom feature server deps are needed for on demand transforms) | +| image.tag | string | `"0.34.0"` | The Docker image tag (can be overwritten if custom feature server deps are needed for on demand transforms) | | imagePullSecrets | list | `[]` | | | livenessProbe.initialDelaySeconds | int | `30` | | | livenessProbe.periodSeconds | int | `30` | | diff --git a/infra/charts/feast-feature-server/values.yaml b/infra/charts/feast-feature-server/values.yaml index e9d09796a3..d46f1b685b 100644 --- a/infra/charts/feast-feature-server/values.yaml +++ b/infra/charts/feast-feature-server/values.yaml @@ -9,7 +9,7 @@ image: repository: feastdev/feature-server pullPolicy: IfNotPresent # image.tag -- The Docker image tag (can be overwritten if custom feature server deps are needed for on demand transforms) - tag: 0.33.0 + tag: 0.34.0 imagePullSecrets: [] nameOverride: "" diff --git a/infra/charts/feast/Chart.yaml b/infra/charts/feast/Chart.yaml index b16165808f..e0f530e05e 100644 --- a/infra/charts/feast/Chart.yaml +++ b/infra/charts/feast/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 description: Feature store for machine learning name: feast -version: 0.33.0 +version: 0.34.0 keywords: - machine learning - big data diff --git a/infra/charts/feast/README.md b/infra/charts/feast/README.md index 846e840236..fff6d0261b 100644 --- a/infra/charts/feast/README.md +++ b/infra/charts/feast/README.md @@ -8,7 +8,7 @@ This repo contains Helm charts for Feast Java components that are being installe ## Chart: Feast -Feature store for machine learning Current chart version is `0.33.0` +Feature store for machine learning Current chart version is `0.34.0` ## Installation @@ -65,8 +65,8 @@ See [here](https://github.com/feast-dev/feast/tree/master/examples/java-demo) fo | Repository | Name | Version | |------------|------|---------| | https://charts.helm.sh/stable | redis | 10.5.6 | -| https://feast-helm-charts.storage.googleapis.com | feature-server(feature-server) | 0.33.0 | -| https://feast-helm-charts.storage.googleapis.com | transformation-service(transformation-service) | 0.33.0 | +| https://feast-helm-charts.storage.googleapis.com | feature-server(feature-server) | 0.34.0 | +| https://feast-helm-charts.storage.googleapis.com | transformation-service(transformation-service) | 0.34.0 | ## Values diff --git a/infra/charts/feast/charts/feature-server/Chart.yaml b/infra/charts/feast/charts/feature-server/Chart.yaml index 931c34f7c9..bfb33b6140 100644 --- a/infra/charts/feast/charts/feature-server/Chart.yaml +++ b/infra/charts/feast/charts/feature-server/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 description: "Feast Feature Server: Online feature serving service for Feast" name: feature-server -version: 0.33.0 -appVersion: v0.33.0 +version: 0.34.0 +appVersion: v0.34.0 keywords: - machine learning - big data diff --git a/infra/charts/feast/charts/feature-server/README.md b/infra/charts/feast/charts/feature-server/README.md index 653b9dfb0c..f768a46cd5 100644 --- a/infra/charts/feast/charts/feature-server/README.md +++ b/infra/charts/feast/charts/feature-server/README.md @@ -1,6 +1,6 @@ # feature-server -![Version: 0.33.0](https://img.shields.io/badge/Version-0.33.0-informational?style=flat-square) ![AppVersion: v0.33.0](https://img.shields.io/badge/AppVersion-v0.33.0-informational?style=flat-square) +![Version: 0.34.0](https://img.shields.io/badge/Version-0.34.0-informational?style=flat-square) ![AppVersion: v0.34.0](https://img.shields.io/badge/AppVersion-v0.34.0-informational?style=flat-square) Feast Feature Server: Online feature serving service for Feast @@ -17,7 +17,7 @@ Feast Feature Server: Online feature serving service for Feast | envOverrides | object | `{}` | Extra environment variables to set | | image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | | image.repository | string | `"feastdev/feature-server-java"` | Docker image for Feature Server repository | -| image.tag | string | `"0.33.0"` | Image tag | +| image.tag | string | `"0.34.0"` | Image tag | | ingress.grpc.annotations | object | `{}` | Extra annotations for the ingress | | ingress.grpc.auth.enabled | bool | `false` | Flag to enable auth | | ingress.grpc.class | string | `"nginx"` | Which ingress controller to use | diff --git a/infra/charts/feast/charts/feature-server/values.yaml b/infra/charts/feast/charts/feature-server/values.yaml index 2403e79645..24b8da1e39 100644 --- a/infra/charts/feast/charts/feature-server/values.yaml +++ b/infra/charts/feast/charts/feature-server/values.yaml @@ -5,7 +5,7 @@ image: # image.repository -- Docker image for Feature Server repository repository: feastdev/feature-server-java # image.tag -- Image tag - tag: 0.33.0 + tag: 0.34.0 # image.pullPolicy -- Image pull policy pullPolicy: IfNotPresent diff --git a/infra/charts/feast/charts/transformation-service/Chart.yaml b/infra/charts/feast/charts/transformation-service/Chart.yaml index 7a4e0fd7d1..5d8f157a48 100644 --- a/infra/charts/feast/charts/transformation-service/Chart.yaml +++ b/infra/charts/feast/charts/transformation-service/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 description: "Transformation service: to compute on-demand features" name: transformation-service -version: 0.33.0 -appVersion: v0.33.0 +version: 0.34.0 +appVersion: v0.34.0 keywords: - machine learning - big data diff --git a/infra/charts/feast/charts/transformation-service/README.md b/infra/charts/feast/charts/transformation-service/README.md index 0d217edf04..cf8c7eaae8 100644 --- a/infra/charts/feast/charts/transformation-service/README.md +++ b/infra/charts/feast/charts/transformation-service/README.md @@ -1,6 +1,6 @@ # transformation-service -![Version: 0.33.0](https://img.shields.io/badge/Version-0.33.0-informational?style=flat-square) ![AppVersion: v0.33.0](https://img.shields.io/badge/AppVersion-v0.33.0-informational?style=flat-square) +![Version: 0.34.0](https://img.shields.io/badge/Version-0.34.0-informational?style=flat-square) ![AppVersion: v0.34.0](https://img.shields.io/badge/AppVersion-v0.34.0-informational?style=flat-square) Transformation service: to compute on-demand features @@ -13,7 +13,7 @@ Transformation service: to compute on-demand features | envOverrides | object | `{}` | Extra environment variables to set | | image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | | image.repository | string | `"feastdev/feature-transformation-server"` | Docker image for Transformation Server repository | -| image.tag | string | `"0.33.0"` | Image tag | +| image.tag | string | `"0.34.0"` | Image tag | | nodeSelector | object | `{}` | Node labels for pod assignment | | podLabels | object | `{}` | Labels to be added to Feast Serving pods | | replicaCount | int | `1` | Number of pods that will be created | diff --git a/infra/charts/feast/charts/transformation-service/values.yaml b/infra/charts/feast/charts/transformation-service/values.yaml index b7fdcb6659..6af9e569ea 100644 --- a/infra/charts/feast/charts/transformation-service/values.yaml +++ b/infra/charts/feast/charts/transformation-service/values.yaml @@ -5,7 +5,7 @@ image: # image.repository -- Docker image for Transformation Server repository repository: feastdev/feature-transformation-server # image.tag -- Image tag - tag: 0.33.0 + tag: 0.34.0 # image.pullPolicy -- Image pull policy pullPolicy: IfNotPresent diff --git a/infra/charts/feast/requirements.yaml b/infra/charts/feast/requirements.yaml index 5f855ef527..b3236b0322 100644 --- a/infra/charts/feast/requirements.yaml +++ b/infra/charts/feast/requirements.yaml @@ -1,12 +1,12 @@ dependencies: - name: feature-server alias: feature-server - version: 0.33.0 + version: 0.34.0 condition: feature-server.enabled repository: https://feast-helm-charts.storage.googleapis.com - name: transformation-service alias: transformation-service - version: 0.33.0 + version: 0.34.0 condition: transformation-service.enabled repository: https://feast-helm-charts.storage.googleapis.com - name: redis diff --git a/java/pom.xml b/java/pom.xml index 0aa87a6fea..245d2d7cc6 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -35,7 +35,7 @@ - 0.33.0 + 0.34.0 https://github.com/feast-dev/feast UTF-8 diff --git a/sdk/python/feast/ui/package.json b/sdk/python/feast/ui/package.json index 8372280882..80956440ab 100644 --- a/sdk/python/feast/ui/package.json +++ b/sdk/python/feast/ui/package.json @@ -6,7 +6,7 @@ "@elastic/datemath": "^5.0.3", "@elastic/eui": "^55.0.1", "@emotion/react": "^11.9.0", - "@feast-dev/feast-ui": "0.33.0", + "@feast-dev/feast-ui": "0.34.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.2.0", "@testing-library/user-event": "^13.5.0", diff --git a/sdk/python/feast/ui/yarn.lock b/sdk/python/feast/ui/yarn.lock index 28bfee06d1..6fb431576a 100644 --- a/sdk/python/feast/ui/yarn.lock +++ b/sdk/python/feast/ui/yarn.lock @@ -1452,10 +1452,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@feast-dev/feast-ui@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@feast-dev/feast-ui/-/feast-ui-0.33.0.tgz#4b8f8c5376103cac1ae0f25d6c9d359e4022707a" - integrity sha512-w+wa+YpFOIbxr4zTmGPuPITQhWfYIWjv7OmL7r/9yLbtQzp/6sKKChGYZwxi6pUu1ECsRc47uuSyhYllav3InQ== +"@feast-dev/feast-ui@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@feast-dev/feast-ui/-/feast-ui-0.34.0.tgz#21799c119da936814ff47e1d1d297258ecfda098" + integrity sha512-ErrnmaFPMgnmnTpBpn7T0oiC3cA+atE5yKBmoMFXBsK6aplu18aF53QBX4JJFKMKF+A2BcO37veeXm1k01SJQQ== dependencies: "@elastic/datemath" "^5.0.3" "@elastic/eui" "^55.0.1" diff --git a/ui/package.json b/ui/package.json index b81276d825..dc16a1e7a6 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "@feast-dev/feast-ui", - "version": "0.33.0", + "version": "0.34.0", "private": false, "files": [ "dist" From 68a87379c42567f338d86cb2be90520cc6d4bfb6 Mon Sep 17 00:00:00 2001 From: Danny Chiao Date: Thu, 7 Sep 2023 02:09:31 -0700 Subject: [PATCH 12/19] fix: Fix warnings from deprecated paths and update default log level (#3757) --- sdk/python/feast/cli.py | 6 ++-- .../feast/infra/offline_stores/file_source.py | 2 +- sdk/python/feast/proto_json.py | 8 ++--- sdk/python/feast/ui_server.py | 31 ++++++++++--------- .../requirements/py3.10-ci-requirements.txt | 3 ++ .../requirements/py3.10-requirements.txt | 6 +++- .../requirements/py3.8-ci-requirements.txt | 4 ++- .../requirements/py3.8-requirements.txt | 7 +++-- .../requirements/py3.9-ci-requirements.txt | 7 ++++- .../requirements/py3.9-requirements.txt | 10 ++++-- setup.py | 2 ++ 11 files changed, 56 insertions(+), 30 deletions(-) diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 8c2c326b59..53c346b6eb 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -18,10 +18,10 @@ from typing import List, Optional import click -import pkg_resources import yaml from colorama import Fore, Style from dateutil import parser +from importlib_metadata import version as importlib_version from pygments import formatters, highlight, lexers from feast import utils @@ -68,7 +68,7 @@ def format_options(self, ctx: click.Context, formatter: click.HelpFormatter): ) @click.option( "--log-level", - default="info", + default="warning", help="The logging level. One of DEBUG, INFO, WARNING, ERROR, and CRITICAL (case-insensitive).", ) @click.option( @@ -122,7 +122,7 @@ def version(): """ Display Feast SDK version """ - print(f'Feast SDK Version: "{pkg_resources.get_distribution("feast")}"') + print(f'Feast SDK Version: "{importlib_version("feast")}"') @cli.command() diff --git a/sdk/python/feast/infra/offline_stores/file_source.py b/sdk/python/feast/infra/offline_stores/file_source.py index d8522fb445..ac824b359f 100644 --- a/sdk/python/feast/infra/offline_stores/file_source.py +++ b/sdk/python/feast/infra/offline_stores/file_source.py @@ -158,7 +158,7 @@ def get_table_column_names_and_types( # Adding support for different file format path # based on S3 filesystem if filesystem is None: - schema = ParquetDataset(path).schema + schema = ParquetDataset(path, use_legacy_dataset=False).schema if hasattr(schema, "names") and hasattr(schema, "types"): # Newer versions of pyarrow doesn't have this method, # but this field is good enough. diff --git a/sdk/python/feast/proto_json.py b/sdk/python/feast/proto_json.py index a0a4dce86b..41d2afa55a 100644 --- a/sdk/python/feast/proto_json.py +++ b/sdk/python/feast/proto_json.py @@ -1,13 +1,13 @@ import uuid from typing import Any, Callable, Type -import pkg_resources from google.protobuf.json_format import ( # type: ignore _WKTJSONMETHODS, ParseError, _Parser, _Printer, ) +from importlib_metadata import version as importlib_version from packaging import version from feast.protos.feast.serving.ServingService_pb2 import FeatureList @@ -118,7 +118,7 @@ def from_json_object_updated( # https://github.com/feast-dev/feast/issues/2484 Certain feast users need a higher version of protobuf but the # parameters of `from_json_object` changes in feast 3.20.1. This change gives users flexibility to use earlier versions. - current_version = pkg_resources.get_distribution("protobuf").version + current_version = importlib_version("protobuf") if version.parse(current_version) < version.parse("3.20"): _patch_proto_json_encoding(Value, to_json_object, from_json_object) else: @@ -168,7 +168,7 @@ def from_json_object( # https://github.com/feast-dev/feast/issues/2484 Certain feast users need a higher version of protobuf but the # parameters of `from_json_object` changes in feast 3.20.1. This change gives users flexibility to use earlier versions. - current_version = pkg_resources.get_distribution("protobuf").version + current_version = importlib_version("protobuf") if version.parse(current_version) < version.parse("3.20"): _patch_proto_json_encoding(RepeatedValue, to_json_object, from_json_object) else: @@ -221,7 +221,7 @@ def from_json_object_updated( # https://github.com/feast-dev/feast/issues/2484 Certain feast users need a higher version of protobuf but the # parameters of `from_json_object` changes in feast 3.20.1. This change gives users flexibility to use earlier versions. - current_version = pkg_resources.get_distribution("protobuf").version + current_version = importlib_version("protobuf") if version.parse(current_version) < version.parse("3.20"): _patch_proto_json_encoding(FeatureList, to_json_object, from_json_object) else: diff --git a/sdk/python/feast/ui_server.py b/sdk/python/feast/ui_server.py index e750f280ad..9950eea070 100644 --- a/sdk/python/feast/ui_server.py +++ b/sdk/python/feast/ui_server.py @@ -2,7 +2,7 @@ import threading from typing import Callable, Optional -import pkg_resources +import importlib_resources import uvicorn from fastapi import FastAPI, Response from fastapi.middleware.cors import CORSMiddleware @@ -51,20 +51,21 @@ def shutdown_event(): async_refresh() - ui_dir = pkg_resources.resource_filename(__name__, "ui/build/") - # Initialize with the projects-list.json file - with open(ui_dir + "projects-list.json", mode="w") as f: - projects_dict = { - "projects": [ - { - "name": "Project", - "description": "Test project", - "id": project_id, - "registryPath": f"{root_path}/registry", - } - ] - } - f.write(json.dumps(projects_dict)) + ui_dir_ref = importlib_resources.files(__name__) / "ui/build/" + with importlib_resources.as_file(ui_dir_ref) as ui_dir: + # Initialize with the projects-list.json file + with ui_dir.joinpath("projects-list.json").open(mode="w") as f: + projects_dict = { + "projects": [ + { + "name": "Project", + "description": "Test project", + "id": project_id, + "registryPath": f"{root_path}/registry", + } + ] + } + f.write(json.dumps(projects_dict)) @app.get("/registry") def read_registry(): diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index a59553b4ac..3192a15472 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -366,7 +366,10 @@ imagesize==1.4.1 importlib-metadata==6.8.0 # via # dask + # feast (setup.py) # great-expectations +importlib-resources==6.0.1 + # via feast (setup.py) iniconfig==2.0.0 # via pytest ipykernel==6.25.2 diff --git a/sdk/python/requirements/py3.10-requirements.txt b/sdk/python/requirements/py3.10-requirements.txt index 4140bea9d0..0ebc485d6d 100644 --- a/sdk/python/requirements/py3.10-requirements.txt +++ b/sdk/python/requirements/py3.10-requirements.txt @@ -82,7 +82,11 @@ idna==3.4 # httpx # requests importlib-metadata==6.8.0 - # via dask + # via + # dask + # feast (setup.py) +importlib-resources==6.0.1 + # via feast (setup.py) jinja2==3.1.2 # via feast (setup.py) jsonschema==4.19.0 diff --git a/sdk/python/requirements/py3.8-ci-requirements.txt b/sdk/python/requirements/py3.8-ci-requirements.txt index b24172e890..a5acaf55d4 100644 --- a/sdk/python/requirements/py3.8-ci-requirements.txt +++ b/sdk/python/requirements/py3.8-ci-requirements.txt @@ -370,6 +370,7 @@ importlib-metadata==6.8.0 # via # build # dask + # feast (setup.py) # great-expectations # jupyter-client # jupyter-lsp @@ -379,6 +380,7 @@ importlib-metadata==6.8.0 # sphinx importlib-resources==6.0.1 # via + # feast (setup.py) # jsonschema # jsonschema-specifications # jupyterlab @@ -533,7 +535,7 @@ mypy-extensions==1.0.0 # via # black # mypy -mypy-protobuf==3.1 +mypy-protobuf==3.1.0 # via feast (setup.py) mysqlclient==2.2.0 # via feast (setup.py) diff --git a/sdk/python/requirements/py3.8-requirements.txt b/sdk/python/requirements/py3.8-requirements.txt index 636f886133..cccff07b7d 100644 --- a/sdk/python/requirements/py3.8-requirements.txt +++ b/sdk/python/requirements/py3.8-requirements.txt @@ -82,9 +82,12 @@ idna==3.4 # httpx # requests importlib-metadata==6.8.0 - # via dask + # via + # dask + # feast (setup.py) importlib-resources==6.0.1 # via + # feast (setup.py) # jsonschema # jsonschema-specifications jinja2==3.1.2 @@ -105,7 +108,7 @@ mypy==1.5.1 # via sqlalchemy mypy-extensions==1.0.0 # via mypy -mypy-protobuf==3.1 +mypy-protobuf==3.1.0 # via feast (setup.py) numpy==1.24.4 # via diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt index ad19f9e8bd..121a768abc 100644 --- a/sdk/python/requirements/py3.9-ci-requirements.txt +++ b/sdk/python/requirements/py3.9-ci-requirements.txt @@ -367,6 +367,7 @@ importlib-metadata==6.8.0 # via # build # dask + # feast (setup.py) # great-expectations # jupyter-client # jupyter-lsp @@ -374,6 +375,8 @@ importlib-metadata==6.8.0 # jupyterlab-server # nbconvert # sphinx +importlib-resources==6.0.1 + # via feast (setup.py) iniconfig==2.0.0 # via pytest ipykernel==6.25.2 @@ -1080,7 +1083,9 @@ xmltodict==0.13.0 yarl==1.9.2 # via aiohttp zipp==3.16.2 - # via importlib-metadata + # via + # importlib-metadata + # importlib-resources # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/sdk/python/requirements/py3.9-requirements.txt b/sdk/python/requirements/py3.9-requirements.txt index 7d30fd3452..a95bcd3524 100644 --- a/sdk/python/requirements/py3.9-requirements.txt +++ b/sdk/python/requirements/py3.9-requirements.txt @@ -82,7 +82,11 @@ idna==3.4 # httpx # requests importlib-metadata==6.8.0 - # via dask + # via + # dask + # feast (setup.py) +importlib-resources==6.0.1 + # via feast (setup.py) jinja2==3.1.2 # via feast (setup.py) jsonschema==4.19.0 @@ -214,7 +218,9 @@ watchfiles==0.20.0 websockets==11.0.3 # via uvicorn zipp==3.16.2 - # via importlib-metadata + # via + # importlib-metadata + # importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/setup.py b/setup.py index 573ab54d51..cd202c9c06 100644 --- a/setup.py +++ b/setup.py @@ -79,6 +79,8 @@ "bowler", # Needed for automatic repo upgrades # FastAPI does not correctly pull starlette dependency on httpx see thread(https://github.com/tiangolo/fastapi/issues/5656). "httpx>=0.23.3", + "importlib-resources>=6.0.0,<7", + "importlib_metadata>=6.8.0,<7" ] GCP_REQUIRED = [ From 8717e9bf0fd253454982b9c9e9527c4d41906e9c Mon Sep 17 00:00:00 2001 From: Nikita Demashov Date: Thu, 7 Sep 2023 21:24:55 +0300 Subject: [PATCH 13/19] fix: Set keepalives_idle None by default (#3756) set keepalives_idle None by default Co-authored-by: Nikita Demashov --- sdk/python/feast/infra/utils/postgres/postgres_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/utils/postgres/postgres_config.py b/sdk/python/feast/infra/utils/postgres/postgres_config.py index 9fbaed474d..a4ebb456ef 100644 --- a/sdk/python/feast/infra/utils/postgres/postgres_config.py +++ b/sdk/python/feast/infra/utils/postgres/postgres_config.py @@ -25,4 +25,4 @@ class PostgreSQLConfig(FeastConfigBaseModel): sslkey_path: Optional[StrictStr] = None sslcert_path: Optional[StrictStr] = None sslrootcert_path: Optional[StrictStr] = None - keepalives_idle: int = 0 + keepalives_idle: Optional[int] = None From 774ed33a067bf9bf087520325b72f4f4d194106a Mon Sep 17 00:00:00 2001 From: Danny C Date: Thu, 7 Sep 2023 13:26:00 -0700 Subject: [PATCH 14/19] fix: Pin numpy version to avoid spammy deprecation messages Signed-off-by: Danny C --- docs/SUMMARY.md | 1 + docs/reference/online-stores/README.md | 4 +++ .../requirements/py3.10-ci-requirements.txt | 26 +++++++++---------- .../requirements/py3.10-requirements.txt | 12 ++++----- .../requirements/py3.8-requirements.txt | 10 +++---- .../requirements/py3.9-ci-requirements.txt | 26 +++++++++---------- .../requirements/py3.9-requirements.txt | 12 ++++----- setup.py | 2 +- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 9b22d1e286..c80ded2adf 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -90,6 +90,7 @@ * [SQLite](reference/online-stores/sqlite.md) * [Snowflake](reference/online-stores/snowflake.md) * [Redis](reference/online-stores/redis.md) + * [Dragonfly](reference/online-stores/dragonfly.md) * [Datastore](reference/online-stores/datastore.md) * [DynamoDB](reference/online-stores/dynamodb.md) * [Bigtable](reference/online-stores/bigtable.md) diff --git a/docs/reference/online-stores/README.md b/docs/reference/online-stores/README.md index 2fdfd50f7c..f86e6f6a1d 100644 --- a/docs/reference/online-stores/README.md +++ b/docs/reference/online-stores/README.md @@ -18,6 +18,10 @@ Please see [Online Store](../../getting-started/architecture-and-components/onli [redis.md](redis.md) {% endcontent-ref %} +{% content-ref url="dragonfly.md" %} +[dragonfly.md](dragonfly.md) +{% endcontent-ref %} + {% content-ref url="datastore.md" %} [datastore.md](datastore.md) {% endcontent-ref %} diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index 3192a15472..cf1f4e938e 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -58,7 +58,7 @@ attrs==23.1.0 # referencing avro==1.10.0 # via feast (setup.py) -azure-core==1.29.3 +azure-core==1.29.4 # via # adlfs # azure-identity @@ -86,11 +86,11 @@ black==22.12.0 # via feast (setup.py) bleach==6.0.0 # via nbconvert -boto3==1.28.42 +boto3==1.28.43 # via # feast (setup.py) # moto -botocore==1.31.42 +botocore==1.31.43 # via # boto3 # moto @@ -172,7 +172,7 @@ dask==2023.9.1 # via feast (setup.py) db-dtypes==1.1.1 # via google-cloud-bigquery -debugpy==1.6.7.post1 +debugpy==1.7.0 # via ipykernel decorator==5.1.1 # via @@ -208,7 +208,7 @@ executing==1.2.0 # via stack-data fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.2 +fastavro==1.8.3 # via # feast (setup.py) # pandavro @@ -307,7 +307,7 @@ great-expectations==0.15.50 # via feast (setup.py) grpc-google-iam-v1==0.12.6 # via google-cloud-bigtable -grpcio==1.57.0 +grpcio==1.58.0 # via # feast (setup.py) # google-api-core @@ -319,15 +319,15 @@ grpcio==1.57.0 # grpcio-status # grpcio-testing # grpcio-tools -grpcio-health-checking==1.57.0 +grpcio-health-checking==1.58.0 # via feast (setup.py) -grpcio-reflection==1.57.0 +grpcio-reflection==1.58.0 # via feast (setup.py) -grpcio-status==1.57.0 +grpcio-status==1.58.0 # via google-api-core -grpcio-testing==1.57.0 +grpcio-testing==1.58.0 # via feast (setup.py) -grpcio-tools==1.57.0 +grpcio-tools==1.58.0 # via feast (setup.py) gunicorn==21.2.0 # via feast (setup.py) @@ -545,7 +545,7 @@ notebook-shim==0.2.3 # via # jupyterlab # notebook -numpy==1.25.2 +numpy==1.24.4 # via # altair # db-dtypes @@ -723,7 +723,7 @@ pyproject-hooks==1.0.0 # via build pyspark==3.4.1 # via feast (setup.py) -pytest==7.4.1 +pytest==7.4.2 # via # feast (setup.py) # pytest-benchmark diff --git a/sdk/python/requirements/py3.10-requirements.txt b/sdk/python/requirements/py3.10-requirements.txt index 0ebc485d6d..9ee910bf00 100644 --- a/sdk/python/requirements/py3.10-requirements.txt +++ b/sdk/python/requirements/py3.10-requirements.txt @@ -44,7 +44,7 @@ exceptiongroup==1.1.3 # via anyio fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.2 +fastavro==1.8.3 # via # feast (setup.py) # pandavro @@ -52,17 +52,17 @@ fissix==21.11.13 # via bowler fsspec==2023.9.0 # via dask -grpcio==1.57.0 +grpcio==1.58.0 # via # feast (setup.py) # grpcio-health-checking # grpcio-reflection # grpcio-tools -grpcio-health-checking==1.57.0 +grpcio-health-checking==1.58.0 # via feast (setup.py) -grpcio-reflection==1.57.0 +grpcio-reflection==1.58.0 # via feast (setup.py) -grpcio-tools==1.57.0 +grpcio-tools==1.58.0 # via feast (setup.py) gunicorn==21.2.0 # via feast (setup.py) @@ -107,7 +107,7 @@ mypy-extensions==1.0.0 # via mypy mypy-protobuf==3.1 # via feast (setup.py) -numpy==1.25.2 +numpy==1.24.4 # via # feast (setup.py) # pandas diff --git a/sdk/python/requirements/py3.8-requirements.txt b/sdk/python/requirements/py3.8-requirements.txt index cccff07b7d..4011ce804f 100644 --- a/sdk/python/requirements/py3.8-requirements.txt +++ b/sdk/python/requirements/py3.8-requirements.txt @@ -44,7 +44,7 @@ exceptiongroup==1.1.3 # via anyio fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.2 +fastavro==1.8.3 # via # feast (setup.py) # pandavro @@ -52,17 +52,17 @@ fissix==21.11.13 # via bowler fsspec==2023.9.0 # via dask -grpcio==1.57.0 +grpcio==1.58.0 # via # feast (setup.py) # grpcio-health-checking # grpcio-reflection # grpcio-tools -grpcio-health-checking==1.57.0 +grpcio-health-checking==1.58.0 # via feast (setup.py) -grpcio-reflection==1.57.0 +grpcio-reflection==1.58.0 # via feast (setup.py) -grpcio-tools==1.57.0 +grpcio-tools==1.58.0 # via feast (setup.py) gunicorn==21.2.0 # via feast (setup.py) diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt index 121a768abc..3a453153e5 100644 --- a/sdk/python/requirements/py3.9-ci-requirements.txt +++ b/sdk/python/requirements/py3.9-ci-requirements.txt @@ -58,7 +58,7 @@ attrs==23.1.0 # referencing avro==1.10.0 # via feast (setup.py) -azure-core==1.29.3 +azure-core==1.29.4 # via # adlfs # azure-identity @@ -86,11 +86,11 @@ black==22.12.0 # via feast (setup.py) bleach==6.0.0 # via nbconvert -boto3==1.28.42 +boto3==1.28.43 # via # feast (setup.py) # moto -botocore==1.31.42 +botocore==1.31.43 # via # boto3 # moto @@ -172,7 +172,7 @@ dask==2023.9.1 # via feast (setup.py) db-dtypes==1.1.1 # via google-cloud-bigquery -debugpy==1.6.7.post1 +debugpy==1.7.0 # via ipykernel decorator==5.1.1 # via @@ -208,7 +208,7 @@ executing==1.2.0 # via stack-data fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.2 +fastavro==1.8.3 # via # feast (setup.py) # pandavro @@ -307,7 +307,7 @@ great-expectations==0.15.50 # via feast (setup.py) grpc-google-iam-v1==0.12.6 # via google-cloud-bigtable -grpcio==1.57.0 +grpcio==1.58.0 # via # feast (setup.py) # google-api-core @@ -319,15 +319,15 @@ grpcio==1.57.0 # grpcio-status # grpcio-testing # grpcio-tools -grpcio-health-checking==1.57.0 +grpcio-health-checking==1.58.0 # via feast (setup.py) -grpcio-reflection==1.57.0 +grpcio-reflection==1.58.0 # via feast (setup.py) -grpcio-status==1.57.0 +grpcio-status==1.58.0 # via google-api-core -grpcio-testing==1.57.0 +grpcio-testing==1.58.0 # via feast (setup.py) -grpcio-tools==1.57.0 +grpcio-tools==1.58.0 # via feast (setup.py) gunicorn==21.2.0 # via feast (setup.py) @@ -552,7 +552,7 @@ notebook-shim==0.2.3 # via # jupyterlab # notebook -numpy==1.25.2 +numpy==1.24.4 # via # altair # db-dtypes @@ -730,7 +730,7 @@ pyproject-hooks==1.0.0 # via build pyspark==3.4.1 # via feast (setup.py) -pytest==7.4.1 +pytest==7.4.2 # via # feast (setup.py) # pytest-benchmark diff --git a/sdk/python/requirements/py3.9-requirements.txt b/sdk/python/requirements/py3.9-requirements.txt index a95bcd3524..432ac4cbe6 100644 --- a/sdk/python/requirements/py3.9-requirements.txt +++ b/sdk/python/requirements/py3.9-requirements.txt @@ -44,7 +44,7 @@ exceptiongroup==1.1.3 # via anyio fastapi==0.99.1 # via feast (setup.py) -fastavro==1.8.2 +fastavro==1.8.3 # via # feast (setup.py) # pandavro @@ -52,17 +52,17 @@ fissix==21.11.13 # via bowler fsspec==2023.9.0 # via dask -grpcio==1.57.0 +grpcio==1.58.0 # via # feast (setup.py) # grpcio-health-checking # grpcio-reflection # grpcio-tools -grpcio-health-checking==1.57.0 +grpcio-health-checking==1.58.0 # via feast (setup.py) -grpcio-reflection==1.57.0 +grpcio-reflection==1.58.0 # via feast (setup.py) -grpcio-tools==1.57.0 +grpcio-tools==1.58.0 # via feast (setup.py) gunicorn==21.2.0 # via feast (setup.py) @@ -107,7 +107,7 @@ mypy-extensions==1.0.0 # via mypy mypy-protobuf==3.1.0 # via feast (setup.py) -numpy==1.25.2 +numpy==1.24.4 # via # feast (setup.py) # pandas diff --git a/setup.py b/setup.py index cd202c9c06..f7b1ff0417 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ "Jinja2>=2,<4", "jsonschema", "mmh3", - "numpy>=1.22,<3", + "numpy>=1.22,<1.25", "pandas>=1.4.3,<2", # For some reason pandavro higher than 1.5.* only support pandas less than 1.3. "pandavro~=1.5.0", From 709c7098dc28a35dd488f5079d3787cf1f74ec03 Mon Sep 17 00:00:00 2001 From: Jiwon Park Date: Wed, 13 Sep 2023 03:29:16 +0900 Subject: [PATCH 15/19] feat: Apply cache to load proto registry for performance (#3702) * fix: Remove unused parameter Signed-off-by: Jiwon Park * feat: Apply cache to load proto registry for performance Signed-off-by: Jiwon Park * fix: Fix update key when cache missed Signed-off-by: phil-park --------- Signed-off-by: Jiwon Park Signed-off-by: phil-park --- .../infra/registry/proto_registry_utils.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/registry/proto_registry_utils.py b/sdk/python/feast/infra/registry/proto_registry_utils.py index c7eeea0f82..e93f513b69 100644 --- a/sdk/python/feast/infra/registry/proto_registry_utils.py +++ b/sdk/python/feast/infra/registry/proto_registry_utils.py @@ -1,4 +1,5 @@ import uuid +from functools import wraps from typing import List, Optional from feast import usage @@ -23,6 +24,26 @@ from feast.stream_feature_view import StreamFeatureView +def registry_proto_cache(func): + cache_key = None + cache_value = None + + @wraps(func) + def wrapper(registry_proto: RegistryProto, project: str): + nonlocal cache_key, cache_value + + key = tuple([id(registry_proto), registry_proto.version_id, project]) + + if key == cache_key: + return cache_value + else: + cache_value = func(registry_proto, project) + cache_key = key + return cache_value + + return wrapper + + def init_project_metadata(cached_registry_proto: RegistryProto, project: str): new_project_uuid = f"{uuid.uuid4()}" usage.set_current_project_uuid(new_project_uuid) @@ -137,8 +158,9 @@ def get_validation_reference( raise ValidationReferenceNotFound(name, project=project) +@registry_proto_cache def list_feature_services( - registry_proto: RegistryProto, project: str, allow_cache: bool = False + registry_proto: RegistryProto, project: str ) -> List[FeatureService]: feature_services = [] for feature_service_proto in registry_proto.feature_services: @@ -147,6 +169,7 @@ def list_feature_services( return feature_services +@registry_proto_cache def list_feature_views( registry_proto: RegistryProto, project: str ) -> List[FeatureView]: @@ -157,6 +180,7 @@ def list_feature_views( return feature_views +@registry_proto_cache def list_request_feature_views( registry_proto: RegistryProto, project: str ) -> List[RequestFeatureView]: @@ -169,6 +193,7 @@ def list_request_feature_views( return feature_views +@registry_proto_cache def list_stream_feature_views( registry_proto: RegistryProto, project: str ) -> List[StreamFeatureView]: @@ -181,6 +206,7 @@ def list_stream_feature_views( return stream_feature_views +@registry_proto_cache def list_on_demand_feature_views( registry_proto: RegistryProto, project: str ) -> List[OnDemandFeatureView]: @@ -193,6 +219,7 @@ def list_on_demand_feature_views( return on_demand_feature_views +@registry_proto_cache def list_entities(registry_proto: RegistryProto, project: str) -> List[Entity]: entities = [] for entity_proto in registry_proto.entities: @@ -201,6 +228,7 @@ def list_entities(registry_proto: RegistryProto, project: str) -> List[Entity]: return entities +@registry_proto_cache def list_data_sources(registry_proto: RegistryProto, project: str) -> List[DataSource]: data_sources = [] for data_source_proto in registry_proto.data_sources: @@ -209,6 +237,7 @@ def list_data_sources(registry_proto: RegistryProto, project: str) -> List[DataS return data_sources +@registry_proto_cache def list_saved_datasets( registry_proto: RegistryProto, project: str ) -> List[SavedDataset]: @@ -219,6 +248,7 @@ def list_saved_datasets( return saved_datasets +@registry_proto_cache def list_validation_references( registry_proto: RegistryProto, project: str ) -> List[ValidationReference]: @@ -231,6 +261,7 @@ def list_validation_references( return validation_references +@registry_proto_cache def list_project_metadata( registry_proto: RegistryProto, project: str ) -> List[ProjectMetadata]: From fa600fe3c4b1d5fdd383a9367511ac5616ee7a32 Mon Sep 17 00:00:00 2001 From: snowron <35639836+snowron@users.noreply.github.com> Date: Tue, 12 Sep 2023 22:13:30 +0300 Subject: [PATCH 16/19] feat: Add materialize and materialize-incremental rest endpoints (#3761) * resolve #3760 Signed-off-by: snowron * format feature_server.py Signed-off-by: snowron --------- Signed-off-by: snowron --- sdk/python/feast/feature_server.py | 43 +++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/feature_server.py b/sdk/python/feast/feature_server.py index 3abca1d6e8..7c638dd248 100644 --- a/sdk/python/feast/feature_server.py +++ b/sdk/python/feast/feature_server.py @@ -1,9 +1,11 @@ import json import traceback import warnings +from typing import List, Optional import gunicorn.app.base import pandas as pd +from dateutil import parser from fastapi import FastAPI, HTTPException, Request, Response, status from fastapi.logger import logger from fastapi.params import Depends @@ -11,7 +13,7 @@ from pydantic import BaseModel import feast -from feast import proto_json +from feast import proto_json, utils from feast.data_source import PushMode from feast.errors import PushSourceNotFoundException from feast.protos.feast.serving.ServingService_pb2 import GetOnlineFeaturesRequest @@ -31,6 +33,17 @@ class PushFeaturesRequest(BaseModel): to: str = "online" +class MaterializeRequest(BaseModel): + start_ts: str + end_ts: str + feature_views: Optional[List[str]] = None + + +class MaterializeIncrementalRequest(BaseModel): + end_ts: str + feature_views: Optional[List[str]] = None + + def get_app(store: "feast.FeatureStore"): proto_json.patch() @@ -134,6 +147,34 @@ def write_to_online_store(body=Depends(get_body)): def health(): return Response(status_code=status.HTTP_200_OK) + @app.post("/materialize") + def materialize(body=Depends(get_body)): + try: + request = MaterializeRequest(**json.loads(body)) + store.materialize( + utils.make_tzaware(parser.parse(request.start_ts)), + utils.make_tzaware(parser.parse(request.end_ts)), + request.feature_views, + ) + except Exception as e: + # Print the original exception on the server side + logger.exception(traceback.format_exc()) + # Raise HTTPException to return the error message to the client + raise HTTPException(status_code=500, detail=str(e)) + + @app.post("/materialize-incremental") + def materialize_incremental(body=Depends(get_body)): + try: + request = MaterializeIncrementalRequest(**json.loads(body)) + store.materialize_incremental( + utils.make_tzaware(parser.parse(request.end_ts)), request.feature_views + ) + except Exception as e: + # Print the original exception on the server side + logger.exception(traceback.format_exc()) + # Raise HTTPException to return the error message to the client + raise HTTPException(status_code=500, detail=str(e)) + return app From 6a728fe66db0286ea10301d1fe693d6dcba4e4f4 Mon Sep 17 00:00:00 2001 From: Nicholas Zeolla Date: Wed, 13 Sep 2023 20:17:40 +0100 Subject: [PATCH 17/19] feat: Add support for `table_create_disposition` in bigquery job for offline store (#3762) * Add bigquery table create disposition to offline store Signed-off-by: Nick Zeolla * linting Signed-off-by: Nick Zeolla --------- Signed-off-by: Nick Zeolla --- sdk/python/feast/infra/offline_stores/bigquery.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/bigquery.py b/sdk/python/feast/infra/offline_stores/bigquery.py index 5913b60f62..86c587c7fd 100644 --- a/sdk/python/feast/infra/offline_stores/bigquery.py +++ b/sdk/python/feast/infra/offline_stores/bigquery.py @@ -19,7 +19,7 @@ import pandas as pd import pyarrow import pyarrow.parquet -from pydantic import StrictStr, validator +from pydantic import ConstrainedStr, StrictStr, validator from pydantic.typing import Literal from tenacity import Retrying, retry_if_exception_type, stop_after_delay, wait_fixed @@ -72,6 +72,13 @@ def get_http_client_info(): return http_client_info.ClientInfo(user_agent=get_user_agent()) +class BigQueryTableCreateDisposition(ConstrainedStr): + """Custom constraint for table_create_disposition. To understand more, see: + https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobConfigurationLoad.FIELDS.create_disposition""" + + values = {"CREATE_NEVER", "CREATE_IF_NEEDED"} + + class BigQueryOfflineStoreConfig(FeastConfigBaseModel): """Offline store config for GCP BigQuery""" @@ -95,6 +102,9 @@ class BigQueryOfflineStoreConfig(FeastConfigBaseModel): gcs_staging_location: Optional[str] = None """ (optional) GCS location used for offloading BigQuery results as parquet files.""" + table_create_disposition: Optional[BigQueryTableCreateDisposition] = None + """ (optional) Specifies whether the job is allowed to create new tables. The default value is CREATE_IF_NEEDED.""" + @validator("billing_project_id") def project_id_exists(cls, v, values, **kwargs): if v and not values["project_id"]: @@ -324,6 +334,7 @@ def write_logged_features( job_config = bigquery.LoadJobConfig( source_format=bigquery.SourceFormat.PARQUET, schema=arrow_schema_to_bq_schema(source.get_schema(registry)), + create_disposition=config.offline_store.table_create_disposition, time_partitioning=bigquery.TimePartitioning( type_=bigquery.TimePartitioningType.DAY, field=source.get_log_timestamp_column(), @@ -384,6 +395,7 @@ def offline_write_batch( job_config = bigquery.LoadJobConfig( source_format=bigquery.SourceFormat.PARQUET, schema=arrow_schema_to_bq_schema(pa_schema), + create_disposition=config.offline_store.table_create_disposition, write_disposition="WRITE_APPEND", # Default but included for clarity ) From 04804a0cc32caafc583a0754898b257780b80c45 Mon Sep 17 00:00:00 2001 From: Danny Chiao Date: Thu, 21 Sep 2023 12:48:52 -0700 Subject: [PATCH 18/19] chore: Updating docs to fix some outdated assets (e.g. release process) Signed-off-by: Danny Chiao --- .github/auto_assign.yml | 1 - CODEOWNERS | 11 +- README.md | 4 +- community/governance.excalidraw | 913 --------------------------- community/governance.md | 33 +- community/governance.png | Bin 258405 -> 0 bytes community/maintainers.md | 37 +- docs/README.md | 2 +- docs/community.md | 39 -- docs/getting-started/faq.md | 2 +- docs/getting-started/quickstart.md | 2 - docs/project/contributing.md | 14 +- docs/project/development-guide.md | 7 - docs/project/release-process.md | 18 +- docs/reference/feast-cli-commands.md | 2 - docs/roadmap.md | 1 - examples/quickstart/quickstart.ipynb | 1 - infra/templates/README.md.jinja2 | 3 +- sdk/python/feast/cli.py | 2 - ui/CONTRIBUTING.md | 2 +- 20 files changed, 35 insertions(+), 1059 deletions(-) delete mode 100644 community/governance.excalidraw delete mode 100644 community/governance.png diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index 18151f7454..2a6cff517c 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -9,7 +9,6 @@ assignees: - woop - tsotnet - achals - - adchia - felixwang9817 # A number of assignees to add to the pull request diff --git a/CODEOWNERS b/CODEOWNERS index 259c13ea3f..4eae6d3524 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,16 +2,16 @@ # for more info about CODEOWNERS file # Core Interfaces -/sdk/python/feast/infra/offline_stores/offline_store.py @feast-dev/maintainers @chhabrakadabra @mavysavydav @sfc-gh-madkins +/sdk/python/feast/infra/offline_stores/offline_store.py @feast-dev/maintainers @sfc-gh-madkins /sdk/python/feast/infra/online_stores/online_store.py @feast-dev/maintainers @DvirDukhan /sdk/python/feast/infra/materialization_engine/batch_materialization_engine.py @feast-dev/maintainers @whoahbot @sfc-gh-madkins # ==== Offline Stores ==== # Core utils -/sdk/python/feast/infra/offline_stores/offline_utils.py @feast-dev/maintainers @chhabrakadabra @mavysavydav @sfc-gh-madkins +/sdk/python/feast/infra/offline_stores/offline_utils.py @feast-dev/maintainers @sfc-gh-madkins # BigQuery -/sdk/python/feast/infra/offline_stores/offline_store.py @feast-dev/maintainers @chhabrakadabra @mavysavydav +/sdk/python/feast/infra/offline_stores/offline_store.py @feast-dev/maintainers # Snowflake /sdk/python/feast/infra/offline_stores/snowflake* @sfc-gh-madkins @@ -47,8 +47,3 @@ # AWS Lambda /sdk/python/feast/infra/materialization/contrib/aws_lambda/ @achals - -# ==== Web UI ==== -/ui/ @adchia -/sdk/python/feast/ui/ @adchia -/sdk/python/feast/ui_server.py @adchia diff --git a/README.md b/README.md index f2c9348b1c..6a851d0d41 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Feast allows ML platform teams to: * **Avoid data leakage** by generating point-in-time correct feature sets so data scientists can focus on feature engineering rather than debugging error-prone dataset joining logic. This ensure that future feature values do not leak to models during training. * **Decouple ML from data infrastructure** by providing a single data access layer that abstracts feature storage from feature retrieval, ensuring models remain portable as you move from training models to serving models, from batch models to realtime models, and from one data infra system to another. -Please see our [documentation](https://docs.feast.dev/) for more information about the project, or sign up for an [email newsletter](https://feast.dev/). +Please see our [documentation](https://docs.feast.dev/) for more information about the project. ## 📐 Architecture ![](docs/assets/feast_marchitecture.png) @@ -145,7 +145,6 @@ pprint(feature_vector) The list below contains the functionality that contributors are planning to develop for Feast. * We welcome contribution to all items in the roadmap! -* Have questions about the roadmap? Go to the Slack channel to ask on #feast-development. * **Data Sources** * [x] [Snowflake source](https://docs.feast.dev/reference/data-sources/snowflake) @@ -214,7 +213,6 @@ Please refer to the official documentation at [Documentation](https://docs.feast * [Tutorials](https://docs.feast.dev/tutorials/tutorials-overview) * [Running Feast with Snowflake/GCP/AWS](https://docs.feast.dev/how-to-guides/feast-snowflake-gcp-aws) * [Change Log](https://github.com/feast-dev/feast/blob/master/CHANGELOG.md) - * [Slack (#Feast)](https://slack.feast.dev/) ## 👋 Contributing Feast is a community project and is still under active development. Please have a look at our contributing and development guides if you want to contribute to the project: diff --git a/community/governance.excalidraw b/community/governance.excalidraw deleted file mode 100644 index f4c8dad9a4..0000000000 --- a/community/governance.excalidraw +++ /dev/null @@ -1,913 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "type": "rectangle", - "version": 620, - "versionNonce": 853777363, - "isDeleted": false, - "id": "pr0yIJcUDXb4nFgowH9_r", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 409.5, - "y": 620.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 194, - "height": 83, - "seed": 1695250557, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "YfmPferxgVKoP70zGfYDK", - "type": "text" - }, - { - "id": "YfmPferxgVKoP70zGfYDK", - "type": "text" - }, - { - "type": "text", - "id": "YfmPferxgVKoP70zGfYDK" - }, - { - "id": "IsihlXUGDSklv2RsxX6wO", - "type": "arrow" - }, - { - "id": "G5s2AUFJ730fyPsIbA8xP", - "type": "arrow" - }, - { - "id": "j9ZVC3ZgHTsAGe3hJQecp", - "type": "arrow" - } - ], - "updated": 1662582134601, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 623, - "versionNonce": 328400605, - "isDeleted": false, - "id": "YfmPferxgVKoP70zGfYDK", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 414.5, - "y": 649.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 184, - "height": 25, - "seed": 1575229907, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582134601, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "CODEOWNERS", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "pr0yIJcUDXb4nFgowH9_r", - "originalText": "CODEOWNERS" - }, - { - "type": "rectangle", - "version": 756, - "versionNonce": 1648798067, - "isDeleted": false, - "id": "XDy4VWWtJ9sd6hzPJDdFe", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 409.5, - "y": 779.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 194, - "height": 83, - "seed": 1925179667, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "gUz4p_oPytb5-ejbYb81N", - "type": "text" - }, - { - "id": "gUz4p_oPytb5-ejbYb81N", - "type": "text" - }, - { - "id": "gUz4p_oPytb5-ejbYb81N", - "type": "text" - }, - { - "type": "text", - "id": "gUz4p_oPytb5-ejbYb81N" - }, - { - "id": "G5s2AUFJ730fyPsIbA8xP", - "type": "arrow" - } - ], - "updated": 1662582134601, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 781, - "versionNonce": 1240013629, - "isDeleted": false, - "id": "gUz4p_oPytb5-ejbYb81N", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 414.5, - "y": 808.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 184, - "height": 25, - "seed": 140322205, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582134601, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Contributors", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "XDy4VWWtJ9sd6hzPJDdFe", - "originalText": "Contributors" - }, - { - "type": "text", - "version": 463, - "versionNonce": 2109720179, - "isDeleted": false, - "id": "AYJKq2RGJrSIpbfiJOf_4", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 526, - "y": 517.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 274, - "height": 75, - "seed": 1616513981, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582134602, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "1. organize contributors\n2. influence roadmap\n3. own direction of an area", - "baseline": 68, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "1. organize contributors\n2. influence roadmap\n3. own direction of an area" - }, - { - "type": "rectangle", - "version": 776, - "versionNonce": 519656573, - "isDeleted": false, - "id": "z5LT5d710gSTA9DjwiL3O", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 70, - "angle": 0, - "x": 1013.7117834394903, - "y": 187.5000000000001, - "strokeColor": "#000000", - "backgroundColor": "#4c6ef5", - "width": 132, - "height": 682, - "seed": 1424710877, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "RscqyQXicYOkFsE_zvran", - "type": "text" - }, - { - "id": "J7IG4T5j15pB3b_K0Cpd9", - "type": "arrow" - }, - { - "id": "XEohLLmfFl0L9Wi2ew5AU", - "type": "arrow" - }, - { - "id": "o3Pp-94PORhEiEauRcZW_", - "type": "arrow" - }, - { - "type": "text", - "id": "RscqyQXicYOkFsE_zvran" - }, - { - "id": "j9ZVC3ZgHTsAGe3hJQecp", - "type": "arrow" - }, - { - "id": "Klq-VJGZiolZnGuaNJ8k9", - "type": "arrow" - } - ], - "updated": 1662582138112, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 896, - "versionNonce": 1733426643, - "isDeleted": false, - "id": "RscqyQXicYOkFsE_zvran", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1018.7117834394903, - "y": 476.0000000000001, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 122, - "height": 105, - "seed": 1202400115, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582138113, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "Feast \nGitHub \nproject", - "baseline": 95, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "z5LT5d710gSTA9DjwiL3O", - "originalText": "Feast GitHub project" - }, - { - "id": "IsihlXUGDSklv2RsxX6wO", - "type": "arrow", - "x": 506.997671158975, - "y": 619, - "width": 0, - "height": 132, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "strokeSharpness": "sharp", - "seed": 345290749, - "version": 680, - "versionNonce": 787007421, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - -132 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "pr0yIJcUDXb4nFgowH9_r", - "focus": 0.005130630504896713, - "gap": 1.5 - }, - "endBinding": { - "elementId": "TBYpmrW2OsKEqbpZfEeJg", - "focus": 0.461338833375829, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "G5s2AUFJ730fyPsIbA8xP", - "type": "arrow", - "x": 506.9985864097345, - "y": 776, - "width": 0.9914493467237548, - "height": 71, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "strokeSharpness": "sharp", - "seed": 241364467, - "version": 241, - "versionNonce": 649485971, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -0.9914493467237548, - -71 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "XDy4VWWtJ9sd6hzPJDdFe", - "focus": 0.011569796958606356, - "gap": 3.5 - }, - "endBinding": { - "elementId": "pr0yIJcUDXb4nFgowH9_r", - "focus": 0.011204382815075232, - "gap": 1.5 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "TBYpmrW2OsKEqbpZfEeJg", - "type": "rectangle", - "x": 409.5, - "y": 188, - "width": 361.99999999999994, - "height": 298, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 30, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 1515491581, - "version": 231, - "versionNonce": 593345661, - "isDeleted": false, - "boundElements": [ - { - "id": "IsihlXUGDSklv2RsxX6wO", - "type": "arrow" - }, - { - "id": "Klq-VJGZiolZnGuaNJ8k9", - "type": "arrow" - } - ], - "updated": 1662582134602, - "link": null, - "locked": false - }, - { - "id": "YEEHpa4RXaR8G9YW55v25", - "type": "rectangle", - "x": 428.5, - "y": 398, - "width": 163.61445783132532, - "height": 70.00000000000001, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 932648787, - "version": 319, - "versionNonce": 398988755, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "8iyUZwSph5yMVrXehf6vg" - }, - { - "id": "o3Pp-94PORhEiEauRcZW_", - "type": "arrow" - }, - { - "id": "IsihlXUGDSklv2RsxX6wO", - "type": "arrow" - } - ], - "updated": 1662582134602, - "link": null, - "locked": false - }, - { - "id": "8iyUZwSph5yMVrXehf6vg", - "type": "text", - "x": 433.5, - "y": 422.5, - "width": 153.61445783132532, - "height": 21, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 1803538003, - "version": 365, - "versionNonce": 952837341, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "text": "Area maintainers", - "fontSize": 16.697223677317968, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 15, - "containerId": "YEEHpa4RXaR8G9YW55v25", - "originalText": "Area maintainers" - }, - { - "type": "rectangle", - "version": 355, - "versionNonce": 1753998195, - "isDeleted": false, - "id": "Wh-PpzmGy1bWJko0akD-a", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 429.8072289156627, - "y": 257.1185567010309, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 161, - "height": 68.88144329896907, - "seed": 1844448573, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "OJCS1hAx71BD6u1jesJzR", - "type": "text" - }, - { - "type": "text", - "id": "OJCS1hAx71BD6u1jesJzR" - }, - { - "id": "o3Pp-94PORhEiEauRcZW_", - "type": "arrow" - } - ], - "updated": 1662582134602, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 409, - "versionNonce": 556564797, - "isDeleted": false, - "id": "OJCS1hAx71BD6u1jesJzR", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 434.8072289156627, - "y": 271.55927835051546, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 151, - "height": 40, - "seed": 852504851, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582134602, - "link": null, - "locked": false, - "fontSize": 16.413043478260864, - "fontFamily": 1, - "text": "Project \nmaintainers", - "baseline": 34, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Wh-PpzmGy1bWJko0akD-a", - "originalText": "Project maintainers" - }, - { - "id": "o3Pp-94PORhEiEauRcZW_", - "type": "arrow", - "x": 510.1952932956257, - "y": 396.60017389144207, - "width": 0.34508644012566947, - "height": 69.20034778288408, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 1889236627, - "version": 572, - "versionNonce": 918879507, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0.34508644012566947, - -69.20034778288408 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "YEEHpa4RXaR8G9YW55v25", - "focus": -0.0035794947090358044, - "gap": 1.3998261085579315 - }, - "endBinding": { - "elementId": "Wh-PpzmGy1bWJko0akD-a", - "focus": -0.0051056226396315905, - "gap": 1.3998261085579884 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "4CHi-UfB3oI1PAfcFm2o_", - "type": "text", - "x": 528.5, - "y": 354.5, - "width": 218, - "height": 20, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 2054408115, - "version": 238, - "versionNonce": 1105416605, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "text": "break ties by majority vote", - "fontSize": 16, - "fontFamily": 1, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 14, - "containerId": null, - "originalText": "break ties by majority vote" - }, - { - "id": "gHvMhIQl4S1SxPE8kzHLx", - "type": "text", - "x": 431.8072289156627, - "y": 201.5, - "width": 157, - "height": 35, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "mcHoJ-dlfU3T8l_C93UPa" - ], - "strokeSharpness": "sharp", - "seed": 1597289651, - "version": 154, - "versionNonce": 1038738099, - "isDeleted": false, - "boundElements": null, - "updated": 1662582134602, - "link": null, - "locked": false, - "text": "Maintainers", - "fontSize": 28, - "fontFamily": 1, - "textAlign": "left", - "verticalAlign": "top", - "baseline": 25, - "containerId": null, - "originalText": "Maintainers" - }, - { - "type": "text", - "version": 545, - "versionNonce": 1478563325, - "isDeleted": false, - "id": "_qJ5MtLgnvmF1-EDKX6qg", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 533, - "y": 732.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 106, - "height": 25, - "seed": 1870614973, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582134602, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "review PRs", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "review PRs" - }, - { - "id": "j9ZVC3ZgHTsAGe3hJQecp", - "type": "arrow", - "x": 610.590909090909, - "y": 673.6931323855418, - "width": 394.7818338530517, - "height": 1.1368683772161603e-13, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "strokeSharpness": "sharp", - "seed": 1115132605, - "version": 594, - "versionNonce": 1612334739, - "isDeleted": false, - "boundElements": null, - "updated": 1662582138112, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 394.7818338530517, - 1.1368683772161603e-13 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "pr0yIJcUDXb4nFgowH9_r", - "gap": 7.0909090909090455, - "focus": 0.2817622261576348 - }, - "endBinding": { - "elementId": "z5LT5d710gSTA9DjwiL3O", - "gap": 9.339040495529549, - "focus": -0.4257863119810608 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "Klq-VJGZiolZnGuaNJ8k9", - "type": "arrow", - "x": 775.7385321100917, - "y": 334, - "width": 233.2672383568049, - "height": 0, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "#868e96", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "strokeSharpness": "sharp", - "seed": 632787667, - "version": 198, - "versionNonce": 1893334067, - "isDeleted": false, - "boundElements": null, - "updated": 1662582138112, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 233.2672383568049, - 0 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "TBYpmrW2OsKEqbpZfEeJg", - "gap": 4.238532110091741, - "focus": -0.020134228187919462 - }, - "endBinding": { - "elementId": "z5LT5d710gSTA9DjwiL3O", - "gap": 5.7060129725937765, - "focus": 0.5703812316715546 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "text", - "version": 651, - "versionNonce": 1375877757, - "isDeleted": false, - "id": "diazwl57WWW_7gfm7wMea", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 678, - "y": 637.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 262, - "height": 25, - "seed": 2077675699, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582152851, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "merge PRs if no objections", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "merge PRs if no objections" - }, - { - "type": "text", - "version": 658, - "versionNonce": 1558756051, - "isDeleted": false, - "id": "_T1wMHFNqfA6a8Ku2OLDl", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 840, - "y": 296.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 102, - "height": 25, - "seed": 1987233139, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662582148465, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "merge PRs", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "merge PRs" - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/community/governance.md b/community/governance.md index 89cf800bc8..087b3599db 100644 --- a/community/governance.md +++ b/community/governance.md @@ -42,11 +42,7 @@ A formal governance structure helps us to On a high level, the key moving parts of the community are: - **GitHub activity** (issues + pull requests) -- **Slack community** ([slack.feast.dev](slack.feast.dev)) - - `#feast-development` is where design discussions happen amongst contributors - - Other Slack channels exist for users to ask and answer questions. - **RFCs** ([drive folder](https://drive.google.com/drive/u/0/folders/1msUsgmDbVBaysmhBlg9lklYLLTMk4bC3)) for detailed discussions -- **Community calls** (biweekly) to discuss best practices, contributions, and announce changes - **Maintainer syncs** (monthly) for [maintainers](maintainers.md) to discuss project direction and health With this structure, users and contributors largely self-organize and contribute changes as per [lazy consensus](#lazy-consensus). If there is active opposition and unresolvable conflict, then maintainers step in to break ties or make decisions. @@ -61,10 +57,6 @@ Anyone interested in the project can join the community to: - contribute to the project design - participate in the decision-making process. -The general decision making workflow is as follows: - - - > **Note**: There may not always a corresponding CODEOWNER for the affected code, in which case the responsibility falls on other maintainers or contributors with write access to review + merge the PR # Roles And Responsibilities @@ -96,7 +88,7 @@ In addition to their actions as users, contributors may also find themselves doi * Writing, editing, translating or reviewing the documentation * Organizing events or evangelizing the project -Contributors engage with the project through the issue tracker and slack community, or by writing or editing documentation. They submit changes to the project itself via Pull Requests (PRs), which will be considered for inclusion in the project by existing maintainers (see next section). +Contributors engage with the project through the issue tracker or by writing or editing documentation. They submit changes to the project itself via Pull Requests (PRs), which will be considered for inclusion in the project by existing maintainers (see next section). Contributors should follow the following guides when creating PRs: - [Contribution Process](https://docs.feast.dev/project/contributing) @@ -116,27 +108,14 @@ Maintainers are community members who have shown that they are committed to Feas > **Note**: maintainers, like other contributors, must make changes to Feast via pull requests (with code review). This applies to all changes to documentation, code, configuration, governance, etc. -### Types of maintainers - -There are two kinds of maintainers - -1. **Project maintainers** control overall project organization and resolving disputes. They also - - Attend a regular maintainers sync - - Participate in strategic planning, approve changes to the governance model, and manage the copyrights within the project outputs. - - (optional) Attend community calls - - (optional) Planning project roadmaps and articulating vision - - (optional) Guide design decisions to reinforce key project values (e.g. simplicity) -2. **Area maintainers** own a specific technical area (which may span code modules), often specifically targeting a user journey or tech stack. They - - are generally point people in GitHub or Slack on discussions in that area (e.g. tagged in `#feast-development`) - - (optional) help drive roadmap decisions - -> **Note:** project maintainers may also be area maintainers, but this does not give their ideas increased weight over other area maintainers. - -Decisions that need tie breakers may require intervention via project maintainers majority consensus. +Maintainers control overall project organization and resolving disputes. They also +- Attend a regular maintainers sync +- Participate in strategic planning, approve changes to the governance model, and manage the copyrights within the project outputs. +- (optional) Planning project roadmaps and articulating vision +- (optional) Guide design decisions to reinforce key project values (e.g. simplicity) ### Optional maintainer responsibilities Other optional activites a maintainer (project or area maintainer) may participate in: - * Monitor email aliases and our Slack (#feast-general, #feast-development, #feast-beginners). * Perform code reviews for other maintainers and the community. The areas of specialization listed in [OWNERS.md](OWNERS.md) can be used to help with routing an issue/question to the right person. * Triage GitHub issues, applying [labels]([https://github.com/feast-dev/feast/labels](https://github.com/feast-dev/feast/labels)) to each new item. Labels are extremely useful for future issue follow ups. Adding labels is somewhat subjective, so please use your best judgment. * Triage build issues, filing issues for known flaky builds or bugs, fixing or finding someone to fix any master build breakages. diff --git a/community/governance.png b/community/governance.png deleted file mode 100644 index c2b00930e3a3d4e4676ab80817e0cb364ff84777..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 258405 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

V08LI?>v4AIPGz_w5sVZ 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�h-N1{lXjI=h`dtzq9k)%eKS>HQ1B%rk*69i`$8Uj_GFa4o@`9jE5 zj|=F{BSl8*LZ!*+7{5r0n-f3VeXK|rl>(oRy=!3nDDV#OM~KZicwLjW#AnIVg^3)2 zOmFHh%)WW&uYT9$SO9gCa*ilUvVeo_JBfdjhyMNb=Pt;G2iUs$!Np3WYDKMxTl2+Yc`z8l z`vUkjDKufqx`VHczUPtO3smG#8E$gN=izQ>2;*AQMESlK?bRdw4Q2!tz+7N02X-7h zxB@J51rfWFhR1@VwHk<|Zo?Uf|K~iTslJQQ3dM+odb!4I1ndHt?^+3%b0RGL$v;-H zL`L2#8R61dGYz_fuXB%y7Jg;IZiS>01KMtDO@bS~pQ*W&@bMt=7_Bez8wxhCjp2~A zIB(WVQ|pi?X160g@%K-Gu70%-@gVYgV47lt{TrRBQeVjWN0WpI!ApuV{P?tn+U~2( zBNBjLx3+Ap#jElO>f`rhe7b7YVl#u^_7N&b-57R%_&-7FH)KFFJ{JS6C!CcpDsy=; zuSkv7PT!gLm_HnhG=phfZlQ`Hw5fuQ4)(OoxUHuINfxoV9rJ+Egw~6M$Et z?m@w>y@U04K23pMTlm_WEomN-@pbgWZ9)?gvC6E7peOt~3!beE!NfJYXxQaJz81ht?e?)pEx6t4_^@d*MSR0TW{9Y)^n5 zM+9aJ)KH~u$JEh7#kz1!Gwp>=@)|;Z;8VFYZ2?>P=h_nqrjBGfvM#V7fAS&MC0^#zM|6(tDCdry! z%R@zrE~J#;g_QK8R!&kJn*MA6SBjWY%L?_**0ZQjvn@9v@JeY0MLos;1t$StxE_L* z=e0DODfyzQ{;ft+ zIY$^n*}4um+t&yH!N6rH*mYBxrMZBs!=xG8?e_`WFtKQi_8=$JOF5-OZyFqtkqr_w z@2xH;F%C@rRPs$ppv`$dD!Lkt$T(Z$VuawM%SqZ7(-k^*FZ5d)ZFC=e94W(2MbpNR zg6C;DX3P2Kqh~ebm28~y8e84+u(dAXbl42EfQ1*?Dq_J6!D)OdXQm||L zQWuf`y6-5yau}Y}CmiBipObdf)lmY?X19itIpSoFOFEZzt|?+3mfM0Ek#od=@%h2W zYRywYXZD5Qz1ywja!&d#i1)N!_Oz_Tx@`2G#J?lfAv-pmH>tBJLF08+BGL8MM3BlyiIyyWLxnHGK&2ZUC=ZHV} zV2%?BG4iGG?(Z8GVl*QHud}@8Y!%3NA2m%-z>J8#iXdInUZhmi>ksW0|NLU#{@*(< z#sq~TT0P2L!Ev$8Tfi;po&Ns~?C)1W`3ufmr1&`MxvmqbSRv6tPjDFLag0P%bSVE) z+E7_|MyWomAzm&u4W=u(aA_r-L@OMsyGc+vtuFMu4|^a$utM-J7I;@jdR=PvM+lum z3BUs^5QjRVVNpIuR?A&%MN#%&sgkV6=tY1#?=j46lNoTrQBCO00nQtf(l!j!pG(sP zR}vOtIy=5Sv6##8tyMdjRV4dcqXLc*%Ne_{6J$vlL`iH0c7Kn7H_R*+)f0nuRG}XO z0(!2;ZXWFg{O*EvniNz!SbwZ;`qXEH_O3{d&pNkeTVuzz*a%;egpV``j!oCvDfvsI zCkFz$;z(sK>23-Hql2@)&D(?(?8@u-Sk8r=KC_Go(-jy&2`i*L_ZO%eTBYR(kE&eUXlPQ^g^+S8$#$O zjJ4Wx9F74MS3G>Lr(FNK^AhvC|Fog12H6XJCaCpz>YH_WK>WhT59TKyxcV8$3qzp; z9^}Z$T1htHa>!Du{(6=uXqyy14t<^%M;cm*hKa_TwMIhiPNf`X3ce;`?1=WzEN|tM zh8X!_i&Ku{wfof^rsO2bZTQWgl=Yhjafn{8i`{~W*Ae5j(bPKHy{IB8BE=3Ng0X2( zXS0m@3B>SP@8$oES^!T(k;tbXxTK`KO+9k`neJY*fZww*q+Ev@iMQ-98U$ldSkpNi zB+lShjWA+iv|VtFPN)u>k(orXqRVT8^ug$wPRoXyYy-wmR25j#-N7F~>&!mVrlxdu zR6?qZq?Xi8`)$@#E;w>?=%$VD&Vy?{q>O}sPN^W?D#2v1yU6A^%)KI~asSw#6`b7S zZ8rtEUsZMJMG589xwROdDTFWA@9|9htq-wL82Io~^<`inrsejF{R99LCu-7d_da+1 zDZ7H7jxmnrs(wS(3oJQ$8X}5yYMYHqBA0dDvVh$2Y+G&mU^F)AQK7*ZZY*oN4=E>dn({g^nt${{C`S>S%{kBdf@Z1y8GX4mP!|ZaL0*49N<|VZ5(WdH5(^c~5Kz`S4>Y&Da`@B0y&l0X zUf+i5P`dY-N*G6LPSe({W4|TmW_OX_&&n9GW2H5M`5~=vF~oHI*_46eHFjhY9YETLTVG-nW8O!^CKmfK4D*?Lh4^u5GIHoG{dR6rF5cFKOK>vajZ=FbvA-RyOT zr}PFAA$1SV&U18_9u%?hsGJ9>(&U@akZnN&75!LUnwE0waA1lZCy?vd^$4Ahxtnc^r}~l&F8i+T+U_drb1RN)1mkHP9zgr;(3U)4UPj!{my4ixmfKzXkoM*Sf8kVtx(C!o5a%fABJHk?&>PgM~q>_!V2 zUFR)%emLBioQJo={MF%I(n+wNVh#y~0mZL`bzlFcw6jO|Wd@`LpKY`(Iss>Nh^lvRt-Z>9v-LO=xYSp(Evm8)9>UgH-U!~)42Ooa z3<=+JQ3Lr@;s3UxP#ZIp7!2PX8mKk-HS8%X@xU`xp)LR>A`zT{KQjHMyBbNP^hwuXTuBg#NcSMh~mF*P0%0+#4nGeBgahv;x z2y59S{mb9=g4#Pl6>n4*JY^$^+6xb=`biSFcE4F(+t_HG%$KkHE&FlqKNb6o0pT+f zV%KSQMS(%tCM3*@_Za0Twp!8&)G*M%xvRp;R&8u4^y_8@p%A|WjDbHo8i^Jt|7hq2 zAe?Z8p_ER_1k5)fN!vfrccIO8ak0KpnW1y1#@qABX5wsCSk;qa@WF-tSz}ipdyL!J z7##96CUeKO2yH?riDD-DTudt!edRi#+8Pu2nCu+# z5DtDbFd!$jA_0yjYe{MTQqaM~0F`*@yZDs#i0`EFm#|LCi*$n4N4wCf)(Q1TnJm=` zJiY!x@!y$2K~(TemkESW{=}IG*)DyvXY};@6<=h>qGm(3z_Iu9OG%xTnPShCGq>R(l2@~Zlwmz9|{fX?UEp{lLWs-)Rt5u zN}j6IbW6o&)r3j!h4`jd{lvXJiVp=i3j9MTb&Et9-WV=ZE=T>ZvJf}}3?1260)?w} za7-bQgG~wS52i}$5v$P)Pxv!re9LglYbGg8Z!Fa{VvCi)`Vpebl@J@-5w!k0f6(B1 zo;y_GMS!@u+TmcPQDcO_p0^55P&dKc8unC#e?HR~^Uc`8fTqS%oISKsx%{?Rm$2sq zsmyPjpsDX)KzZ>qRfKtFG07&v^=P)a)uPp^-aUif?6)jZTexsZ_n{~$;?|2be_*DO zpHv!Zk%XHJGlx;o0InlFEPFjMgs4GfT&6{F5Mcdg@-wiVJZBx?At*>%{~10A#_{Wl z#tt2t;e<7_3GSmXp6~+pBdK*yL9JIbw#H!Qy4^V{MjKy)xw}yV zt7ci;E$U1H)7#Mh^(lnrEJ!?2{OKD@I*(uP1RT5rB%E(RKQIEkEp)q#8{f~4p;&&+ z-Sv=ygXRtT3)GTQafJfdrEl&ZHZ{J`g4!}A3RUfi@<;o2=)d@nS7?T?i&^M~QkLjX z2^2*9I!FBXWY%oJ_B3(xx9JjFtpD9B)QsqXCV$on5k?Z{NYM2n*``b|v4c-bSGP+T zIj^Z+9Oig3d+{$aMa3d<=F=?gp=g;u@SbPahcJ8SiCK^#Z;utfB-pQk9Pzh}l;?R8 z>|+@u6fII=_U#(|;K!cAFprvIYVmgsG(4^WYt36Fn-+GX33K%Mc?%>cN!L82;R~5~ zokpY9w6)LI9L)m@cA6)NJl@h(E>mt6PsU z8GpO{gM?rD4KI@yEIve>Mf99>>%;giVq(GW2dlpE($bj8A9J7HuOhD3r(63!-aJ2x z0|S^SAIak@0);5 znm%FtAJ3aGRgM~zJ;q@B*^GfCM45H&H~?k)w?ZIsIuY^7d8$a+^&cb-=8y8iHU=2K z{!YYhnsv4d%J21qtkcb^fkS;#2Ct=J=mxL}TD#S#JxB_&rAJwRZT_cMkqJXe1%QIA^LG5ey}{|mK!yMUrI4NHWXuJqA3xw-Gma9!Lv^ZYkqm^z39l(Jk_ zd;%oh;;H1DkcC{Yno=!Gp!cvHrAZusm<&IGP#*-KPM^NFA{)DNTce%k@aUr)+HhGw zI5cY*eZluDawc+Dkd%V@iM|XA?zdg>^2a2`8V@Xa)%%cdtXm_yI! z=T{M`sweFbEoa%AKCTC)zwuX)c}t!)^>Ft8 zF?HTSO?GX!PePN93euz~z4zV%2&i=F9i?}W-UB>>(vjYSh#BxP*O7aZjEw;pqibb9T?sn z8jOvm{x~vl3aPDsJRytHfHk=oF_9#Hm>6UCt`v3`vGL!X8@BLY^OcX&NKV0>xdE89hfu+EV!ymbeNBg>NHZ#WS-T1`7S4WKus$NA3j%}ljOE?k@UmRbemo~&u(DXg(DF<7kn=`4| zgWaZF;IKqe5nd?_wLZ` z;@!H*QUK5%2&CYDiYUF9a)NF3zhC3(hivAMw!IZ6$Qn| zo01zeRMfq)L4P6ML}o%|EDN;{AaeC*lE1PbL4LThOqOSKX9(apARrPq)*Cq4@H5jv zt&dJE#S8x`)h^t|Hbr>dB#Zz6b{#mkg#i~SCxj1!e~;i)11HB67nlUmL!)?yKM*s* zdGU%s?zsZ*Ij?lDHjcRfPS29Qj05Av5Qs;agijcrtasx3TCy$1I|mt3&x0jGMeaup z%n?)Nr6JZ42 zEh#|&D*|a^M{UZ95O0!@6eHX$x?|Q3b?Vv2WUp~vWhgf@bV~wqN#vd$JserUfeiod zHyH#hPRR)Z*gt%SI1$ZdI(nw5UE>d1G)=H@Z%v64;lp8O9APZRJJU?4!+g`ry zQ^x>zDtu-XeK&))?JXe!2xtaQRyXP|sBm6u#)K34;ljJ!85i)A21tb0$B7VZ0XnQl z8S0nSZR|FL?I`cN`b_6I&DhD1&Jow5l6e<@8R_^#8EL5*%OANM1Jf5=RUv(Hg%!;G z@hBQZCCjX4H|Qai@u~w4aVJRP|YzzqrQW}8TwXpus~gx-8#>EOd~R3CJ_U?+~1uzvbxH^4ZR_!-p3Rg&3TF62d?cE0U7{-JC9z8 z4F*Ozm0E^0la@UxYB8$%sTDVdLV~Od(@RjEZ9E~(1I}9G-$EPsE3Arf?8;Lu z9^!#h;Q7mn*0AiGN}wG?WFzU4QyE#M#>e@1Va>EEU-$8ohq18`C6ie^ zB9rDrs!Dxt`E-<%U0IC*KU3vYPai7pK6EC3dj9vzSHOO1w(rDdP{3ulBd)Z z8k>E!v&xtAmzTAh>-OtxE6coTREifB}XVd z&!Ntl^BZVo_HP^K$De%(bi9XllnH+bWZ_B4w!E__cH&-ECsvF;SRCdXen}|el)~59LA{*f8nvwj;7zrb7y#0i}iXPceu7~UrQf$ zI_JB&S?p8RS_&`N9ysOw<0t2hG|+$c@+AQF9j|^YInjN zf28i$tWx1Ph4hh$so;OHlH~GNP@^=H@$Z`lSbX&|j^lFi)Bn#m36+)uY(+nRd9?zu z6P`~^e_I;IiEj|aUrYJL6x%wRp|Y+8$-j(@oN^#hJWL7AQqrF22h=;R z_Or#9M&6JRi5ZF#K|i=?2~KyN(LtmD`Ao4&MPtUE*4`<6;V%-Zwd6!$I3D$I+(%}O zBd5RGJ%@qyKG|Dg+A_T8mh|4u?=a#nx+F30$cd;%)+LYaro+}TWM6~u=98&8-KpO8 zw&*czJ?oPNaN(`D_ftVK^v7V0@;`v~VfeHy&ntr0fZ(3wEzaU|@* zvflQU!2k*JziaKkzblIfpi9iOp9Z8>vCGZ2hM?Q%Pp%eiS#uFr??2VRdE5yZ!GN}JCIs!0so#3p`VZj)f?iqCJ^qugLLf*w4*pB7{vB@RO) zP6&k=!(Qc^Uyhuc*l*+Ou&Dn{cY95*R)+%!<@_!-*@R|wBOF{EV3UNt3?QybWdQCa zj+)b0XG1~D8XXxX@-pOE5*XcooIjMknvnA*Wia6F4$e@JU>pxr=sTz>?Tzpnw`8#V z6UBscA7W{{eF^6qBvA5WlmUKFIUxhmU4>-$V4aBdAls0nPL2-h=p{mp_!vWB0jaoHSs1K}XHlDHeDVNVy~Y z0!EP`x=%Y5J=dIu(a_m29Rsru)Zgae4&2I9K1Z(FGDomggsoNaBu4dQr>; z^E`lTeyEO+JXReP;>H#x0e+l3ui}XP8f4QSXvyY<4yd{mdM#Da2(aaWrlWPxuihW( zmMQKrZ7~Sn(5&96sHcr*c6|2M_3tGk_|RpaRAIw+=@>yUNEjccKc#CnlaFiaIEi#- zfRD$a7r@*ybNEvV8_!>P=iqX|e(No`YSu^^8dN-j*6dnucK)xL^Pf*mD1gZ-o4UHW zn%xJ@b_(gC$q}7?0g0*MoheH|48Rr9>COMEAm&1&0-pRqwVx48miZJwxWVRvmG67w zc8P4Pv1I1QMNX7Ar}**HKVm*Be=8W1-JnMY6Vu)Ox^dX}_6#Kz_y6G9*Aw#sY17yj zNathnNw=>|xRuSE{eex-@6d%t#Iob~uM=n+SvXY&&k~RF5jwS;mh3df`FP+SY4Q)3 z|CJX1GkL;T$X&VDJE)XC8>rRf?RNk}G89!A=!j)6$=nP)xh7<=mdIkJE|0`Xh&LmY z{+tdhLF*3SWL_E7BdS%J1Rm!+Wq}4F29_AXp!T9mKz!Rk4mgz4T{7ebhtfLPNt=!| zedyignt+6ugd%PqHWNrw|7+~popCoob=h_nuvT{_BF>#QIFi_zP~L!9F{;uQtZcN* zMr8TGn$r}m0m*7|FR}jkLqGiJ3kxtO_q#lRv4b|E_sZ#40kKE7Jx4@fs|WEr$-9bU(xR(MFSs937YV9n_ z#2U`5uY(qdNOaYOSlb&jq*reSL^6d2#*?)sQ0-9oP-tX5BuSWZ2U0TQTthtlgg7JF zX0@#$DVrD=3h(f)dnR3A{P*nF*nA>7wWUMcjeP6qFJ^x1$F!H&M3GmC&YZDXgp`() z3I)N-iuLLCPXzO7s_Vr)?kurfz*Wu;63wCN(GU&z+ijUmUu>1JS3p+Kd5*KiIsIbd z|2taWy=DaHQvtY-I->k~?v2;5klq&U z*XKH*2v*Dg(k?xw({D2SL#Wv4nW;Yu#W)WZTo@)wN_`E$q==SQ?hfKIqII13Y}~K>u^#FWHU|v4TyWKtG4NpBo&VmlmPs{=S606W_UX` z8}#2KZ2l{j|0iNWP35dbz9#YKRro{SiTi!`!KjMM}DIlu&W~ z#NF908&0!}0uF298cHSYD?sw&{mLEne--Jx+g*5t;|UR>^&FKvni7(k*B^0b%vMRW zoRE{=?(bJ5Fh?nG^7R4m^J6ZtI>M1fr!^xQpwH< zHniSytzM;%)KNJeu=fvZ0cArDM_G9=NnN*q)XMS&{1Em>29zYYpST~XP@Ki$K70XG zc?e`H&Jwhus<~cwQ5Cx5woK!FuK17*$!r29v88#hql1h4E!ck7>ZsaszBF$4kd8(* zOKLB$z2bHbt3ouCx8In#gzuS6h{0CSCf9CncL_8N$Y>p7h4>_SeqDPu&$08Rn{9l0oDRzG6^EBGliwjibRqVu2qb`6QO z_aDCZvN9)(7SMXy*j?CtcKG?U{;$RWCS~K}=xdxYNFBZg2n3)M%wQl0Y6#{&CM}bf z05@%`34C&a$z2DGY!w=EbtWJVHCw8OqEuh$yVS+ogmg#SNshy*% z`p|Z1cI2EMFKLi7_9b`%JrbgqUMG%ht%n1%<4(*P$9ysHcClVXIi z)jhuUF9Wm~iyXbLUPZA9em`PveX{#lfuAyB{`chjuL^s_*{*fJp3D;{T=ngmOy(qH z-*$wFl%Mp@4&dP6q(nb(p;MQ0v#hr-`p=jJEC&FxdQct_y#1CJpFgqn=f@2S4HCc{ ziKa5UjLh4dZOiwT?9b?z{Cl5f(i{Jh$gCSA&1CCl12ibjf|6Mf0u}LvhpTf&Ey~uF zk!DynPzu`w^1UYG$)*LtvERKxU!4_;=-Kyi*QbgE-sXca?W1n6Ns>+d&0t>j0$f(IdT&4IzFGK7g4nRUYhQQ{zaweK~f3Qr#%_I({yjzNes!(?42TF4?2V&^=xaB8TPQ%QO`<`Q#nHG5jnLZ9m)mkbHx zerIX(pJ8&8oeHof3j#NrDI!q?BD^L}H!}Bhw8$X}c&L?Gl!X_&MbJgGqhnh17cXGd zH6Z&BCsh<^;%dA8S1S_iXn#PJ!2iS~5K1W*6HD?KU@qroUvxI5qL#lT8f*Rd0rEUC zPSvQFmx>-%7X^(wIEq9RLY{2gTT-TnT`}m52IcY4-|soHs|2z~_$+aKL`{9PZS+nE z=)Ykx|v&Sm+)YJfzUYcnOU0gPgU7|B&O!C$7AL zA~1bZG5$Rvs3sic6plt@Lm5nRkK)0`;pl#D4eo=7or|;-8p*YtqLDbFB|%0j6)c<$ z_x@dWtQi~r5y?MRNuoOsBP+rx1;Fsh7vot2IH-04d;&x&S0(Tab;Gl^Y9&Cv`hJve ze!ISm^(o)V^bHx~ za-@moX*Ud?SU$6(OV)TjeVrrEB}>Je$B=CeW?+@&Xhmi>5#)%KKQG>)1GH9#b4ilo zQ6p@dfa`o#BD^fwu*Wo<%lbpHjDMsfKYA;3vCGtf`mGckgQm?U_Zw{h4e`?D6jQGG z!V7)?T-LUM&WtrY)!~@#P5|DHih=H`i5aDYaR>hv zV+47zxD7|q`p)anAKz;7R=JwRCh{VAk!B6ow&!?MHHp{zev50p<5u#mC%!q4UZ-f) zvI_129}R0nQ1tvs#EG4Q9~ByacrPUj#2{g^x=&2QlE6R`a4 z4pZ!(R!oK-dSf#7B&hQ-vdJoSfynv=@H2~>55mOb;l?U&zzR`qKJA4RxR!lu-ODkM0Hox09UBZ|ZIQCa&4H_U+2_D&sJ-xA9cS@X>s~uzv)_19DGHe>4bJ4#& z>IoNfTW-9Wl?xBU@>t6gB-UGRj$;(eS_0k;)k!__1S+6^_H45XnHZbv#b7O+O)Lyr_g0? zbeNB^(}w8x*gWH99W4UYGqw+ANVFYD9W)S#%< zz(n7D8>T8@Lq|R~$oNuc9&6S@NGKMzgkcL387bJ>qvrYgWp|4yHVL4@F4LlU*{x-* z9DaY%A_tV-xEf+9YI<_fi-YZlM$gxTH9?Ri8K&i*pJ7mi81sVg`ktk?W%R>_M;HSm zizeycA6Z^UhZTlieBzxSbH=6^@F}W=-gO?M(3fy4cLbh5K%d^J--O)d>Tct#Rt1^l z21FMILrW)0*9QLP+KdQ_wtR*ql3F&8NkgyJ`U!|y%=&C|G^BG#DYCfvq~k_DBiwjK z%tgV^X;mPUVlUV&FK9NjUj1|ho$G|)=2L5CTB1&eStJqFNLiCha(9I$O1IxB3L?GY ziI?>PSyM~2;0{Hrr69yx!4Tj0rUeN%$rAIGkoI!WuIO>5#rvkm3!mSl704X7M%4G$n&N6|Y`k^r z=n2f4`H2~xZYh4oT*va(!TRsH|ZkK-}Jg{%2`A6!! zLhsJ7&&iPC{m`@dT=?Dg?gxR8x99msA*HilNsgEO;i02f*kNwC!7dMThOn#6`Ebl- zc;-66<(j|M`Pxxfi|rl!?3;uE<_@W#HYUdre*Kd}URMr5J4rL`v5Cbwb7oSceWmrs zt~e?k%tcF2mlAbOW~CJvli71uV(L9EM$F*kzTRUUyM_hR$q59{8yFezRMsjr-QVmP zVQ!3uf<-SHPaybOlku)H*GjSC^{uTtGH$fvF`%6i;Gyue@RINziKOAi>k7ujUt9S4 z)6${}pDk2}Q_i)xNs`Hvi79oQY?t@OD^G*0TgSSovM|op9HBT-*2jvu{2$$nFfF5CJ=$YVBkV$Bt zM*bs2n^mD0O}5)v`X(eYXyQlTo8J{T)j)ZSQVMX;%wgwc2t=(A+u>7Z_M8Gr#!6Gq zQpA}dFk@7v+J$wSDz#IEoFjlTk`3IW^isoC>z8t7IOoxQk5g%VTf>p!WkR zXl1N|tv%cSr-M7o#%dZ2@oDLeh78jw-1k6A7fu@c!AXb`7@rxVdX_6l6s3bwy~$Zh=#;#`+F>MpdkNroCbl ze!Bu6Ssomnvcj(W`NI3NQfF+byLx|Uj88f69k!DFu4z_FhHAr|zz$ngtLjwibl9g1 z#2A~|Xor&9Ml<4V7XT{LET`U4+JBZa7pk9kiWT-61w5Uy1Hyk`&yZ6m@l*18$`^#`mVJjU?k1B6Gu(ap^%gKGZYWV?mT5 zhZd=&xSiS^oL9GfbuEbAr^RADwvO|Nue0geqfMW?)8?%l=0CCNC9QwH{8Y>@L$-me zleLIgRQ4kiqp-p$5pHkCSF##q3;ts?Dn9d5WVlf#+f#5>5%Wsy|f1F+!iYm z7itT-XehK8z`?9t{HE?{y&2XHdz0@yAvF&o=GC#l9xk>n552-l$0A_`jfIvr1JW0J zRv2sxgCa_5D@|`?>ean>;=A52`4tZGsO;kSfFMRyk$029>!^WF;5jZxL@}u_HAiCm zRTAP=2P^i?YjM77)YINs*nNLqlWFp_B^Rd^8z(-`R;JVNmM?}e$gyddJy)OF!e`oi zbY_KA5;Z0=no0N1fIS=se9GN7oN+BL+`1z?2e70AYhFh`C!He49qHA31@s)9R1_E< z&@W>@o6a+D&JII>?GB)J=9jy&)9Pxj=9)$?Lky0@UzAh0wdtl z`LQ96ZVr7{GB;^ePiciE8)B{@nRfN_xhAEI{W;42bKSLJ#0GEO>$WZnr_j6BDxRNW zUxq^FN_eHcJQQ)i->q^eTn|65x(c9<%)PsXW4fo_Szflo3?>67yzPUzp{uY+Vt!-c z!?nl8JUJs1F|ASiTm&6(m*x$M`14H7#j>oqx2r=n z{~{HsP%wD1J=W_i3ly|P_m#f5js33S45za`D*^ziyj+aVMfDbtib5bhG1XfR*+Dl-^G zEQN-P>l`rR74C4xJC6|}$2n;a^n+5^sC_n|g-l7gzp9njD#e<%t1$kGv%n25IPcTO ztWIs%itU8hNgICR?QL_u^cPMe5drU^ zpDQX9;QYt2;>0hMtUQ^^bpBLGQQb`8D{{S7_LY8KCSDi_tEEdp?msF^GUg;JwX)sy zAWar97Ot_#ZtOV9H{e@6>VbZQ4mL%RNxy{5#g>Jy){R5{qnn(af^GTDfQHiAcwm=b z3YdwbbEfXguA`hqeXlk~i#JsT;9gT6M9Vqy*fAn|5DZ6-Dy0X8Q+lQ;=^l6rp+Cj$5eoOk zRN(HWG2ziP1ok9=k$9&8S^XWi77Jc zScvVw34CiD-vf)h*PWQ%Rh!qd<)>Za$T zaH--#*f9J|mEKMCUDvw6!;g($s-eGN3Fk)FDjOSyVq$ zc2&fccbrDh-VcQd$2FhZv5B8Z3!!4)$?mG7x%x+cUH~Ix36r?$s~WFJVlrS|F-02XWzu$ z$Wf`Kf*ka8soO+Z~e37%|--P)1dA=Ap<`sf5r2M-s~BbX(AeX zt>d*};aQ7~etU&Mk9Mwj71c~>{-tr* z@5V3eP;b(sb8KR1*JDAvrt&RDV}+VotaWfTd$c_Yf9`k30AnG+qp9$nVk_6st2pXL z#-IS24PXI)eIvvU0B_`}$(;rk15QX=niMMTFOp2E3iAbZAVS9l-fUBNhgeO@es6G4 z!$akXWa*wPC>y{BD^+gg8JGym!^tL(2(Xn;et2IQp21FO8HAvj-r8j|%6d z`5=2}^2AO?X0iZcefJ&*d!AMd8_jXnc0rQ78`KwF)juf9A$WD8nABXIEgm8p6*F2> zFnxm}Wl0K0_o|ucIwa3FcZcqrEwF_%UX*fNk?lNz{83Yw3jOmPhmA&9m0~kX04z6- zMy>GqlUntAhg{@(47hYCS5k(Y3~f)fX<3+b?zN) z%sL9Ca)tg!69h&o5<97oYVq`Sgx~aCyW#Ocac}`YM44x3pyp464@f-%l%?(qE;~z~ zg39AlyAJVv>(KC<5u@%m^+Spe_st26y%GcMl%l$)h<0BC2X$W>7sP$}kuk0x|#fw}M!fWyo&J zW}kKnB>CZNzxb`v&6F{_;OeRU!Y!aH?|zUU7S_&{GKe zbW{xP5Gn0x!RF6+Hsfs6f7~|XadtLky4v6{TRA$tUKlR568^y=+y`--(vcpF7*J=q zoq!ySf_jBF7_tbyOva-i${FBGtE4gR-s?d%?QjyXxRy8_C!Xt)0k9Hi1Gex}Al;Dr z)53xiXmX~U#N;^NFzW}jU5>a2da@>4KT|)enBZjLjT&2U6FSTq`XR$H-o+3&()-Z@ zFxghf#3DCMj zqK;?vXTA*;pKD$*3WOzD5H4Ml*q=60Xqa0NtLEVxqc9RZ=lvO&K=}Q{z0`bGQYi3- zvz05n_IglZJAOPBJB`9|@e%)J0bnZ_56D?}K#O}&PTRAQBQT5gAL1ru!zV*Sb>Mw^ z?4AX>I5h^P3WS}+**)D@9R;L~`4O?}=t2CK-b@bP<<62RvGd-!^VR=+#pq zGwn(QT5pf?`qv1p4NfuUMY*SKHyG*sX^nTQ8LMR=pf&G>NuE(+1KtWo0!%l4x@z@o0V;O{Hr9w=otPtO(iF3w2xM;k{baHzb13$rjKI(g_{vZ9e@7i5#bxzq=nhZJzFiPC50(mPLVz~CvxI;uytVI z5&*{3C54ykxMPX$GZTc=1g&`6b}e=gT2rEp5x|9`l0g{$K=?yZdMG7Dm7XEQ1PPp0 zxSl`DAn|Lli>7V07v8Q%MMm95gK!4J!{_4{TCf<8Z%%&XkU!Ex>2@uTnfuo@T>`JB z#X|?Z`aV{+@OHYC5OS|3fs0nAK0*GpQ(564-OE6SBHOR$9YN3{ZCPZX+jWF_&~9- z>!%Ed0qzUht+{Pa1eSD)1C&-jS&{Wt_Wr9H8L5LOxlg&PVqZs8Yr*<(cwcw&%bemT zWiLC(uyY(=Gw1unz7I7%`}*c|oip5yG}3DADX zS3Fy_n5>JZoAxSNr>N@7@KD$V?+adz_!B-VcMwUUknWi924iWkk62tFNF#(xIU-68d8&g zBaOkAE79Y93RKcf|6Zf0D~>9#LwC2lGb)QmYPX;G&Svd5)r7jCb*`7-xDAP-HQDU; zx(&KyC4ast8Dz+RWT+WDdvzRQfxWE4hNYw(%xw09($|SB^>#J&^FfRL(j@dNi;IlM zHyZr(pZXD;EjkuOFH_6Ka`-JCF_=o22EWHT#~rFaHd1XazA9Q3e#X2aJn7*w-s4%8 z@-SP?StM5d`$BTzTz1n+*!z93s7j9cS4s@+`#4($s{U@7yKaKXkZx~P;`t7YXC^>W)#J(I?^pMkh#*LS2pv+R}ckj%j#jVo2fV|NF)~aeY zuIuTtPbwgObN(SU$ZCJeN_zLZqSf6Z%JQv8N@<9Vq;Pys{F+_G#ZltPnu+f5-ibjG z=hsC^N5OQhRwC!?y7*7$ygh$@zIkcpp;huX?cIt5OnNjYGjFRu$*@EK?0lT-{i&7X zea70OBG%VdOuv3u783l{mf_S^ah@|rEYCGMN;B<_d;nwbQ*>Rt)hR0_RluwbOm&sbGrQazGwy&AtzmHnu&Z?<3~n z=tKW4hgb%VJWnNHS(;&I3;;be;_uEUQ?44_=v{dq1%B2yS}K43{W%;fR4?@m?PeCG zoK!#WSPBgPoQ7N3dDL@WgvCHOCS*IGh+a0Dwio(4>=TXv52=n$?6ezdh4kbjc zeTi)NxBn|8eWJ8t(U+67JRWCXj1m+XYMI&||L!;U4s-qq`i{BmU1X&w5HxDdnKXH5 zGvWk_1eNNi3`4;Hgxu$HsdZwflrP+hs&fXEVoVznv9YMXLQD=0T7CJ}Nhd|t1x~*y z^Uh8i&h^s1=i=+d^LO=fqb1gZi67WoawwW8B=n!6p9B6-Z=haO$1%YpqUcVo3IBt|E&;_$HS7iDQGlwZ%1L35S;AaobZVwEqeDNo zudN^W(f4JEs&W(ieI-x+i|<7N^OJ-+`%*%JI)NT#6^0#eP@WSv&@8r^w}P*Zyt6Ztdze?^lLF5JbYmClG(Q^ox_la z)M=dFUyrkwmwLm9V=3r3xi?^ZDhD=fXj3+e1MMIfk$OJ_z&E5d>HyI^@up^}~s0jz&tWvy=#ICJ_} zmx*hD#taGfC$FZ4(XF8EWM&ZW0hXyk!h!Ps^Ui8t5GipqzFj=A4aBJE)cxh&`uu zJUcw9s6tGM!vgbHJf3#Tb+>-eg;nRCXGblQ62%28RlHj7I+-x~C$`2?!WU-&O2%9@ zl@$5o%LMZto@|V%t?QGjH=xrFmjx`*#N+d5Vt=B2swlfn>N}g&c)D>;TQNq zIVKc(F*;eP?PwwYXGr_Hx6u%j{AJdLNBG%!(U!v?C68`pukN^*q;}C~@$4i)}k^Ij;EjYgdttq zRZJ|ucJB3P%kItZc?AjX04HFO0{y_7{eqIf$(_wIo4S(2hexh54WE zuI)ZuxZL|{yn-3c8@(kXD12J+<=8xYfy#$}>h73^oE3_xnGE@U9)%;o{j0$D{PAfj z_?ez)EPMiD{#t;MYm-%!$n8R_kF%HbE4xHzp5z&+LMNFI(8ik4UtW^}x}oQpjIYmQ zSY!Q2HG1mOXc*Q$0GAS9AeZU^6uH*}AX*ZRMt`<%gstOUxyBJ)7t0lnO8zwh zFm6;TyW{iQQcq3AC&zp%e+pIQM%i6f)RAV%s9yqMb*b~?F1Ri2V~<5+3n0uj7+cwI zl$-nZc!lvzvKHI=ostEF#*aHoo10-~5SJE`R$QM>j_lfmlA70@(8 z1=9O8hjYe*C*O49^JJ_5JzteCtIOfRq4uvbj{hQ-5)Q-iaY5Lt?DBa zv|fi`45j`hUjpnRAG}Kj{D_cDiyM1)8v}RieH)e>kj9nlivqGXaP${W-X8CXgW)J) zQ*cnEHuJ^5N~=t7aae(??!;f+jBx@lpFrM6me^=dzZ4%-pZW~XxB@@zp)5?niYesY z#09DxEE<6?m?-_zRvwjiqi~Vea&MgJyhrwKs5l$B0>Ae|r-3#Y2sTXQl(z$Xpsv`t z>uz_++&ZOlWcE0B3v~6h=qC-atq&#&|J1hieQ;Kfql*ab;=eUyZ}|4{^5|>7qsl)` z+Y2lTCfYR?8Es9`foSP_tyh!_B4HtxnEFw1Q}$3<%+O-4`e&#+97f2zeCUiy0YOpk;4J!vVY1`pLh&^p8xhTHD|X#eJYMfzmzbLEI`!_ zSO9df(zoAzEz^4w@lX=b;EUt;_ZQ5cpb`t^sf-U>TlK&f832x*L;H-urcb)G2!$u_ zd-B%XIAnm7{^l)YtK-*Qh1yKds+_dCH+YspA%$Cs9~k~|Cq+7XxCL%2{_5V>p15=Q$ z+0!1LJ!PcFskB=c;Ysj-W8+-Z^M-MQ^KPw(oMxo&0MYi8uWR18b62p>Kx3`VAa#SUx(6l$*S8v{5A||Q;>GH9W-4E zev~i__m>|b>I#60;@}R_9`8TR4z~d;_D8FfpTLG>$p1$iQjBu zzB0y3P6kA%9==$#W2PD@tv`#bA`f-$S*Y$fSYN)4Q}}Cpy9e99zvd#|vCH)|MWd=5 zn${rU7mzPV&-vL$o!sSgr26#4lHar%4|dw^qUFocdok*MRvD{ezSWgcM!)0}5j7Vs zbn}0-qQqCvuiKGB_A=FPc@$XZM$Ydn3C;k)p&e8fTq|S#)Sg8LlJHFHFYc0=i4H9E zd#DcUQY&?|onn-#lHY5kW!Ik<{1!;uvm!@bh{8l&eylnDO}`%A)Lx{c#ae`JkG#{s zcqloP?!n0H+$PqOc{V^tE^A5tRp%d!@@IbE$NvN+5t7ln$O_6Z?%<#G+DD>rcL~9E zpY4iHR>f@IfrIRKsUjKaXgzydGO<9WT#^7h)zEp7Lool86*6H|6sK`m(_cO@aK`dY z&$^dAgYf;?YN$!iJ0lnlN;Y= zQ;#z7B-RfXA&N{TOEvMmG!_sPC$FtiG_dR!QXrNbGb!ETELTYwS);PaKH*e^$UpvL z9Q>rey&HBypm68qQc>ma7P{9#kmiL5-BWKCme1R6R}rr!3Jau4OG_Lx=YKBzO9hd! zYI2M^^_a_8xwLi9x2nnJQ;oJH*W z#h~E@Uo+RECu>f^&cNN#*|U6{G5xv@9iPZOWNX-RW3rSw0>wG@eaFF!ey>}hn@}a0 z+fOJ~{ynaaO8r>t#vf{|dTxVDY2XR1SR~{+jnX|fJWKkk73_w+&nyZnI7wcrMkbzF zj#R0zH#x?rb%Cd)CVcN@7+H3>Gtcjx_P-wikED)T9{jA>DKLGap*016^l9)v4?S7M z?u5Mg<w$mH5x=bTwl-2hGl0w~!V%+1$po8puAXg6AP#%Vm&M)qP1YhjMldhn|M z(|m)H;rr}oRe?4O#8SVwQaLE)ZrGIE-=mutZ!x=R#LBb6uBa&)4)VYWI}t>?L6f56 zkbvaZ?U%8_^;7fJ&q!a<7g+etR_+cYe=&EMe>MMEy)lVaO*=H@Lpe9Zak@qz3bE2_ zZGb+E&7OZ<^j%9YY@UiEn$qrexpAS75vFjzN-I|)I&A+rfAuZuZC5~j>cVS8H^26) z->LiCF-lsqoz;jNGdDqA(!a9p7e$)o4=6+JthxT6<5QHXnq!`KPp|%4rlm%@bEZ zooIYilx45$@$V5PEn_M7idd_TWG`D+he zk8|#G&V66k^Lmq6?SYCYz6Zov^R7b78|!#fjLI$_3|;X;yi1GiOP)U~XhA%}@mCj_ z@?Jjj1=T40`i;TI@Q7rQ8d}oelCFF7w=6KCRJlAl}P`K1pENU(wZ@#vcQl#&&wp^z}bPBxgh#Exly5mfybIMvK zh`Ht?1Aq61PC}}(wMAw!DfN|U+W@EU?^+vMtwnWRI-S*#z8rKMjJ`&sr3?VsZ)3gH z@gBp^TrhTTjM@q-N?drCopzZ_W!)cH};}~+&YoJe=($2Az5+`_FZ$LyyMzBQ}*Ny&z&tKlR`J<*iNZl#qP$o zQWP)z?7eV7o|}tWr9y4ikW7rPQi|y^*a)Acs$y`ZF>xqcOO3(TNn~N{L={FjfKe{r z=}UE4Uw`tJGCcSAgg74m{mrZn?TGF74k8=*sRg!L>33Oo{SS`w zqP_UZdh4);V`!*`X}DE5(_LCC{O+c)BMk^EXsTKp26&H0mjlzP8C6d%VI6iC2MCCn zj>H$@ki;|V=E&&o%NI+&DF%G8d>}dNGeik!*?0;qiH4$t;U9B<3Qbjl3>UK$10CzFHK2bnH5&rZNs=`ULlwl$i^O=Qph*zF5ZKFejv0Ti+ zMZ5kvRJTxatTQOfU@DO!H@X=pv{}{j8e(-eZqzuvQ*g6HX(heIb@zn)308^(P!HaF1{&`zM+)Y6(8RI6@WL3g5==(OYC2&8lKQ$tmFE9(QT!E;i zt~JF;L-jNA_!aQb$VSV3P41nx?kSyqkD;%$%XIPnD|fAlZq|#(3CmJ= zrK@*|CC+kssQdr)Uvma@OLUvE=zb={jfU9f;DC49xj`++5p_>(8mrA;eE@$3jJS)4 zdj)RvO^~E!tIJ%;q;NPKm4`FSA1z5B0k0e?BL`xp-i^P4a=K{b|+hYOa^7(+P0F$>L>LfdY-1dFu7wLUOn zsQcy_w#{n9shJ9Ty zL{Yz^?lLTTsNq=nInPc^Ek)l%OT}O+rsh?j>xP?yfXG6xGo)YV_B?ef)47t=_+m4- zvQW))t=Dnts}+|vxbmTejqJ5P;>>IQ=D_RZ?m}JYuj^)Zf3OR zyEh9xn5}!B>J8h16AiwtVC9bX;l-HVsf*LaMyBD?{2APR(R=CrvpB~j<(8HD`mC>o zO!l`qqmD%C_E(=PIkv21rOFCMO^1&1X*r}3krqGRz7JKZ4UdDs1)+v1KL=2SEwLCYO2uHzq*wHP7omX zV&-NfTZ4ubk(cR$z23ceg!nTmN_AZn?&UhfhWd#wuvL8`l--Ycm>{}!(zAU7@a4KQiZ=}UFkm#|t%{dp9O_q?q%ax5ct?~& zD82fX<`k7k))3=Yw!c=xe-ubZ@KrPvv(9CZi3vS+_K~lJA%!MSpvsE*mVykN_@@bo}d*dOcy$l|NYxMf~B={c8 zq}u@J8GnTjD2x0FmkNB=Se`;LwdK2yXbbKk*W$z=+OF9a`gvIE3r;-2J(4V0BF)4h ztD*J0rsLSv%EzDwJ84Sb4JIPl*+vcX%K^ankXk^HH$Uted7ko@X(91O2m)LqGN@9iQmv;3iNM-x;9}2TF5=}o>tjp?46CmvSy^*BnW`Xk-QBO;! zx#*A5&(fPEr@UeRs;x0ZHV8cG>X(a3Atl86aH3oO4UCdE9K##JnDCkh<7a!um=`mg zJMAC1lue=QVjSdN@RF7@;K)`DDC4M@^fhdk;|sO%De|`)Dc1tzvQs!R&pAI?2Gz%wRI!0U!U7LqL{dpbsy~N!^%k}7Jbns&LLGKIZ!?d3Ul#FUC{ycEC z+O{19b@vs5S>wPA*KN_zpY=-*shv_i;$$5KNWC~|xrSsA9P4!W3(PzEyJAUKok3Oo zI)?l-*~4wkgNc|?Y*JyDmVQ%Tq*KPWF*8PoKZ8N>Cskjo`p1Gl8<;gch52hu6hrhY zZq+wY%hi>{`GkOjpp=S2rJG!al_jx*h54$$83FlQtO&C_IpF8-?b!Cg?ZcxOul5LL zc}*?Khlo!e8Y@H%x6zsyp9SBjd^sEETjrUgZs;+md%&48SyrqJjM)J7kfH=G9A(x0 zb&0M0xxS({6q)E0sg@_HP9g?fY2q4TbfT!{(IhopaN+Kr7~Vq*PqHF(HvjtWs1Fw$ zjn4!fBtm}r-W`|iI`Fte?}B6gcO!etu)=WZntK%8(+|U6q`Y`oV%LieyYESn@qhTF zL<#!h==OK#BK;4?%v=$?0-P|0wCcvgerCqXk?p3Me2Z~-8%4?&S*^`^9jd$_bp zvKyEi3)rPDaC#(Oe&fXT<@9&0Z|PlnMTEdW_8}T};Oq5L|4HQ$wHKA#dMvm8|8JGo8u7}V^BOv){4v zj1W~vTw{DtQ)c_7>;rF!g>`4EsTmIa5+e^>;v^m~ivHu7o@QphX!oTUb7732>O_x~ zYBuRq=DVO=$Io1aPf#yjpDrNgg~l@EUi!}1nJ?-~oz^VZTIz!Rdtt+|=rXiyk=gK! z!Rl^wy^%R*sD;LV&Cmit-t5Ks1;O+*d00?o8|kbQHniLJi;4IBe#kW1Iyj-{kHf89 zL`$3FLR^uMf`)BiVu~8?=g7c7>*C^`^Uvk4xn2zBgzRfMO(|q)zhz2Ya_A6CB^s;x zVe=Lp+|U4?tx$wykM%bt2&xNcym@T*Kv5%ej4Ut0xpLA>q@+o(=CG=yR|mIGIhb%y_W1MZ0jf#TZ=nsoG-;LDowAa!20x1;*og1^3Kc)}nt<(}(AmKYa-}J$q zDl|TWF=nIA>>D?CO?>-PFJ)s-Q6o0L>@*tUIAI4IY4WEp25V!8vbtE=2_oGm|9g-P z1hIZ&YQxS&lJ6jEwyZK9`>IQ0jgw9 z34H8vUJ7#oWCQFEYZaCP+>08uhct*-9^2m3KS?QXgf8oi8k{cV$WjsudnT?9Wog0f zRrzqcp*w$~)OhRet^y!wTlIJ+V^ED5)!{Dyj$)2{cIzSgM!d0n!KI6Et^!n(Ir10hnW`%Mr=R_|hN1P@X7xtI~dCE-Zi zO7BLA&mvwO`fGcz&#kqy^C5{hd?|vrpgS;V-^(~)v1mu~vn858>G#aV!t@MwOcobZ zGX+hTWR*pcw1^_lOE!Nvock+QzsI?_Y=r$%z5Wml-tr1vWaNgtp>BSA5OCnf6JV+& z+=gBxoW(H1z*Q!5clTO)l&TmesF)oMQBnoDHu;5qL&`)fY^D&FOgxq+x;Bx8SHclZ zaNO=ZfAD||Ai<`_8aR0xUfx5Dt%>@bSpjF7-HQ5_saeTLT4!SPfI-EuCDLR<)__W8cY#AD17SwW)j%1=7sAU~NpKhH{+N!tQ1ux@kf`isk8f)hn zsT@B|&!n;(x{+wVJwiXQfaL5jYFyG3Ytw9#M@@UTBd<8S{WN>98SHTNBaoHoTd<44t!5P0PLJcwar94zUMyna1DOo<_ds_< z!UW4qprfyWXRw+fg_*$>XSh_?c3tFRNLSv`s)%>W|WUPfTp*IWjqVbY@)2)|Muz3G(P z&?26{ncVSi?;b!~MWctMf>XQqtMDyvn6t5+*zI(UDH&YpD%9?fJpTpkHXRu-SQ~Ub z7&AyidEU}HtSCYwk6_!8A1*<9#vEkv1XY=72)^H(jJvSDSsx(k+-mhYZD;R3d~HZtkcJ^<&rG8C0D7Cqgb*?u>4xJpWz!`k zts(H*+KEg1FWsDvQJIdJL|9f3Xv* zCoVO0W_~_B5%nAaV2uF(H7!Q3x=>?pYGWg>3)TRuohFE9u-%_?R`gFw@#7*|{kq&! zNo*<;C=)~~wX5p=G*=wN>FRo?X-H+1U`jo#vKc5MaV|A%dhhIHJ0&bvEyFQIVWd-I zk(*9Y3{y~@m@DD4Fd1&9W9;XDQ_BG~yf$boJz|TH6~CRfd4UDq%;Eu1`V5zL`)|TF zZgpmrLGyu5}{8ZaVv-3M~ z-d{{+s%0m3XAd-1)FQ51V;OclhEtFzq-pz^)Kq|am0)Wu+G8<;aeNqU(r+7NgW8G1 z=NPf`cecUh42oryq?wdNuWBHECKRxClZ)pd<@BmmqDPJZ% zO)jZqG6y!Zv~rR?B>>7_=l}f7QW^RJz)Y{lo35Q?t4zyUj|U(4rAbY75W4I zX8BVrmRBxY80sJPqo}>}jtU`PZ1W;X&T_31zQHE8{Z(^Z)^2ApAywzaWVKHqNcTb@ zn7iIivy@u2>RzNILJ{^e#Xd!mHt18aVTgYBwAMp5KA99;{MHdX+m)9aONc4Q#DyY|Ky1I^LRgM-x~D_{atAqy4HZ z7m#Zg_en22+L7Sl$(yT0SxU1L%Ug|+atzjUYp9a+;_L)>c^KQ${>Q3a;F_#z!f$lx zlR!Z^B*OIZ*PAqN=soc7b!%vIO0dK6%R>ahD8PL*L3z?!0<<0bpg9$yp{YGY;d)Uj zI>%tut)eJLZ|v{CzxGEb<(a4fyLy4JSy2)Ur#GBuOqaO~e%b_!dOGi9h$__R`vbOq zekK?;1a!95oHw^Onk12B=yW`DNkD(!$KM1BdMP~#>W}|E5FJFQd~`oKlf*sy^+@$= ztBpyrwR&bc%?$dpC`Jz0w|yp;>^Dw_$MHqvTd!vV5MUSYvwl9w9BKsaB^;CeJirb$ z8~Xv*IEm+k{r2hty_X)zAk0ZE*LFqU-xUZc7ygRGVh?OtzftUDcTrVece})LN+GBW zls%kRgE_Zx;NtBAU*x^yV^8PxyQySk7nMEa7d^VAhv%^dwarr~hH{M;$7jY{O;FVR zdWAAb|6dpvtm%vP0=bXcZudL5xOxYCq)00S;3^Z><0ksU^o9<~R-yEucvq^lhi*}r zS-{&=jK1Cy4M>QNejLvqm1XP?x=qKLrwOe;;9F;whBgHCXI2|gIVQfFf|3rt=(@3# zR@;MG&0MzM2E47``C148wC&*h#>F+b#=jLOIQ%lG*TX|qv7Ca_8)+_op8>Lb^^J<~ zRj`XJR*_lZGm+&a?(|B@eeNt!PP5g%gV+!k8@0Ce^v8}Tx>kU`-IRk@PR-!82We~} zk!`@>@*msyP{^9hj#^|w^^`hIH)F8BfsgKEPb~H_glJo?tX!bv+!s3*h^*TCOq|?8 z2=KiOjiVVL9m02p$hWYK_~ENWw0rpfkh>-3arvil`m2_iM`7tl@e%xb4F~i7{`HW? z*}}ZzYH$H$6`v6(N@|P?yO1F zy3EUrnnc8LQs8f%nuCJ4xbx1RqYMr|_2sxFmJ1mY=V^uxKXvr?v)b|~M_R4BU6=%Q z0{WnpM^bgTn~@3(s&~I#RyN>&sSW4FHVm}~7rs@a0Y|A~a206V#K4lXeB8hN_^5PDVM*F`MvPZb!U<`zCid6AyQm2Sq z{e~W~T7Xc>Ydu4`mb2T01qWe$c9zS+m|5|!C+TFr;x@%AdGznXZa6|7i*U?bsR-7K z!uHmjalWt^`xS2K?(0s!JCr&=ho8p2WDw|TNT2JbpOl&W+Z@?qzs90y#IpX?oy69l zi+Mbt1EY4})+@8pmyGdkL9ezo9Y^uk~xj(4g_o5Vl8%2?`_5md*u3jR|m+hH)U|6tj$K8=TC^`u0}-XH5_rCj0#Mh|`R^TE;()>>t1HopMlrR)q`hA%E-Dc+g4_UIXSl`vz z5PBt6VY~-8QOgQSa$fS$N0*u2ivz@v((SaU?})W?{VBj@b<4O6!W*^`T;;#cQ@g;P z5Aj7={s%25Gbqcx>bqOr{T53X@d9IgoYW(?8AzETLoDSzigB5{44*V8gzzOF>NAOA zqVYZZ1tH5j`g!jGYfDm|?2O84djR9rUUkN+rCxdqc>GqMb2y!UNqoT_w zq^g_U;nAqV6ZMP{EwJRKcqqYgp{cY_ABRS!mJ_O}SJ6-yso487bjiA`B=g~XQouxU zq>Oa{OP6+=0k$kHzQqlOJbOQcyyrv|v*F}ck>9tR!}F-XuA>Ju=0^u5CLPhE-@cdK zGllSC2mZ;Qk{P^V?q`F@CzD``#9EN}WL`=~HIf5iHux}}WP)E>5j_@_&A!dRWDT0d zS)989ytF%5zSAm!4qPT^3Qzn7&$Py0Wb{(mK-+9dsZB>oGR%0~s|}SI+2-(fB&J8X z+_FPG{eQ1nGgeTA>h{%qpW!$yFh2jgNxi&46(^~mb1i2-7!c&OcIR2L6&q*Rv zmXtwT=#%BwA)}3sMG}Vn}NCP3Nm{&8=SbO89y8D8bpPf?}i4@*{3D z<^@NZ)(z454(wPz+G2@>gEVTZW3?4!=`hGdmXEgp_i^Yj>aN$y1FaD8vp!XZ#|L^j znRx7R$6v}wUk_?Pw5T_ZAGaeMsYFnJ9Hj|;G%E9;)3NYU9`#tHB`90$%qf@vki;=d)k2A&wm~J8}}&V)90O=_ab@M`I>jN_Z_f^qY`Iq9LM%rqDJL+ zEcQb(c@u}!mH;}Qrkj}G)Z3%;O$$aM@j2@^6VZ3;kK-fC)W&}-R)^?ZSwXWG>0D1o z-lp(c0^ZKpFT6_}c4Bo;ennt{eu8#yB#pFD z9bkO<0(;N9{iC`2U_7-G)>MYua`36L8ZL6<|2Ywj?r~-F**4Fq&#!D{AyO-(UdLUF z^wl!OC&$IVpNdPp^Wsw)O^@&R$ZQIQ?dFS0NAhJS*{)I}z_*IW2p5@khBn`y_o;y{;6Y2&-1a|kQ}@|y6n9a zuvFenJ>NGw{;ZwT?lFj~V+MmZ5SB`F?G9wRJRl?JC)nepP*Z+a>Bt$-=^-?#YhzWl z(+Tt57TJ-%+W$Uvji5|lOvor7#?rfNbjt`HKgJ_M{A>t97|kj+8cdMA&YPTgEU+N2 z-uyn`_&k&HM`$m&;=&`GQEGuIuou)NwR^`n5MBPqPnRk1yY#p1$UUVu8?^69qVajd zIM{;e%=!jUUfXM5t4vboxv7~vi$u7^+a+z4(A6w+S}A8sp_ueH5Iy$+eXK#%*LNry z^8UY#KCI9F?>F@6#VhVqW_EjlbF}$|?RuGrY(sLdZk+TZX3r()>I0YP>cy)fgef)I zNdi*N&tep+-0^d@jaz6gty7dgoh-f-Ci2B}6M@-D=n+w-4d{J5Yi=r76k=$&Q_O|? zQDo*F`^`|hVr^bdd3|Nc6{aSWWO!F>c3Ad-M&sa4p(ie-!TFLB4sXDv6djwFf5{KA z`eaV;{jlp;#n<*)vJ-E9HWC)!LPmLg4`aG~QLC;TL& zTB_!x&aB=*-H=?MnB>_|iYU=`@GkG4^|HqtS8`8*qAHWscV>>rIM})BGKlG~or`e4 zAzj8&pjT2_bkKGeHC+B}xhSPWMXjD$VbE%j2-~wco=~wb8))feL}ZY04245Pfw81vm)!5H3n&WzQ zVc7}HEx62MH(2FH7G&cKUY`t1bM+#6!T59z4^GKvwYblcsz<9K1qFFPABhbie_d>c zBFfl6=dMI4+gHy2o3>)tVNnhd1Un+y9#NpI*vIlTK0jD;tZKC}a(7pshnRdH^Dkz! zqSG=JBQ^Kd8!?ZiEbUh}y3?T^#w}$H)(zMb;ij-` zs#o|#N>LJ?xAE*#^bGmYdG^3j$TAM5c5{*NzUUT~`@y)@UkO%kcgtbxo4Zl{{5q8J zc`TFma*>fV8@XK8U2QBPYE|Dqfq-6GLd)23&VDP6<(0xoM^$k%OXvL&N6UHanR_vb zcb<4Q?=gJsdo>s!{?GAYo^=BE-Ko&q!-9Jz|3vZMy0o@S!7#R(iU1wOLeND=f}X6L zRQQ;j#9iTh{2koV{yBvdx!QgZs=lEPy=dLLBjZ+g z-;>7o09uXOLLeyE&&aNrv#g9ffB#6 zzhRrm$f1o3pBX%?B?OF48}_#`ZR^}v{Le~UUfK-~eir$(wzM-(f`*N0ePfLbTef-S z@1OTd@Z=lIN9uv!PMVXpw*ng`YT%LD*7?@gF=`5ATE*NP9E^ zDZ*6;)=Dd*;@ARH=ZNnAhXr5?Fj>$}%yXtV-?S49YOJR4?01U5C|dMS%NtdN*@JEntnm>1DIT zbag&kLWJ&bBTDTN_jA`?piT~QpweEB)p0_RZT*dIs?6CI{&EL$<$ z@Suc9Yg_lYN#A)LpA0d_mnoKk&NK7|wyntRHNrtqcte%ak~V`-d1q zY3T`d?JGIMl*5?_@JH0~_Jh}&+$lhObFodes=!SFN4Q`Eo_Pu&Rm0)aPNL=YS|u6v z@(m_XP%|L^l_M)VW+ed+D`#bMRhd*HS0VS&W7BCD3p5Yf8MN~(u?s73 z{fk~@80P6FlTcS2Kke1OW= z5*>LdEM}7 zdum=;mB?kCBIR9h78{}dxq1^$S)musY=YEjuBY_pSmW|q@>R_tuD5+(ws>v!(vnn4 zsM#s=DqQCAEM*vT@TWVKU7FhlTtI=wGIoh~{AND-HDfnFG*IvFXTDF2s=7!tiECDX z)K-3}`bY;*X1E_sY`6>k!8zljdZk@Tsk(!HMJnXi23Mp@zf85Rn-i~(YN08Ie z7+LO1T-$2K2rawqnJLrFpCq`wwprZq>^I5rVQU5&n9l{CRxe@oIdQTTl4IWHx5oe{ zbB0UoYP$7Z_}c9BuhlevjMcH4P&Tl%GqK>uW%cT3GiZlXaad;`M}is#q~g>2p4N@A zAi&AgR_?2VIM=wjjJlIwoZBGqlg})}uZ7Tbia-3?!1`JDUB9GtBa|Ig^4#;5--a8B zNk&N$Ff6f<-tT!1soRJEO8MNba8e|rYQIgoE!YIx%%VY!V4a*sLBPp#g+ReO@+RSd z7n+qNs44qFqbgqCef^$%GTf0!F;bEd5yck01(Dia)}FT2^5&v30d65o`a2Tck*}I5 zFN5XeH>Eb^A#Bhy-?KSo4wOrgWg_|s_>qFitN3@_IeZ#3Q`u}lbGgtbmL_I;hTO`L zjvjQK9(e|Mo64xy9xX?pLA;ZXV9NJ^fI+UoxPL=@WCXfp*)5yxpw8D=YyvM#4E|U% zj)Q-d9F*q*DB&6=e$oHDf0^erDNI8lEMZ--6(?zY6Fx`Kvn#Rv(efbZ>KmVwfUl}+ zOYqR3f`!me&M%AxYe91QU%)+u!aFyHJvwc|C))%hMBV<5=#g|fcKT*TtD135FTy`^ zHDr|oKXOf^&XVkpO-FSBrZ_Ju7nn({RSYUy`wn9X)chT3ivddzxzy;!T)i%$nvF0f zHcNc6Yy972rTD7;-Bm-)4}n4l z_EhTJ`rZ*mdzMC#2Bm1Ge%zg3H~K+&muV#od-{2;lDB)43^6> zg1HGcP$XmVVtn(2;^kwL#{E(%vgy2e-2B`zD;w!&@=*RVBmUDC8KG11kNLTulk1WY z6f_pN#qz{_((GA%{2Q_ySVIX*FJIwXzl6zX%bJ7n&?U7}!9nIoY3u#1DqHsWVZS7F zn;1}*)OuXxw|kWz{?iBs1p}gy%CZf>OdV>HX`)1)QB`Ym^fk``W@z(!nFuWq89yRaz&8nPpR%ZrmR7p!9HpZB9j zFCO6LA=&rZxte~EKpxh*xEizbAu{F8W}nciUs3Ns|67gNRfFJD%mR~7D_8kEvnsakN12*jUBVWpMT=2WD)0W( z<^5h83J069EHitXNuM8*v@Min0)i4k=sAYv1GjL7utS2eLnFEt}wN?3ENBi&oq*Xtr9_97mg7 zEqCZieJ=4Nc*i}RR1e1@CSk-#d*KNZ1vI*?rP}J#*t5E_#sv8ffY75XbKL-=n6Gas zXjrZEhtfi|Mg6U~;7BX)9C-u7tPKrQ%A7!+F=L?j``)bQK~ zD;q^eTiMR33^l1#{dpk^R6*jx#d1{rd@o{|cB*ksEPaZ6Dv#jtM1L8I+gKrD*_2t( zO&8~XY7RC`Al46NO6};*j@9pkzNI4Zj1((ovsHX+kn9}_TZLH*m}s(*2TJb)tPbiOtcXDDn3?0G4M`(nLH;7uOTaTtz6@ThKPuaEUa zu*>{a5Fvec$H7#(*B%>-sAhnG5UOVWbzwlZ0wL+2##7H6v?PdDk?8SEMuRXK0|dUTRf|Q1niK9o>2jNO**RZ)qv5s7_v=U zCHgw-L`oOizn8yEtNL4mve2M$eW$Dd`>$9d;J+?F3wU5qc4x&mW++n2t@yctc4U%G z>U)Vx(#B3HyP|pR^hX2$uuSk1(B+;25vdo`!iU~J<6nagzIU9^5j3pb-#MW{;_jwP zuEgtPs>sPU^&})5ElIC&RowU~N0ZYVcV9rIFV$4lrEYJsDU))i>B4gP~s656Tli;+fER?K{wl+ca_ z+~nrqB%FM~@3ms*k0fv4g&c2HgvMS+j1gYlJq-~2^R$FpljdJg0o0RTY^q5+@hW}ve@E_P4=(Y&qPYw#unV~wSKjT0bMv=vJY?qFxZaiicbolx zJ-!j|Wj9}T;gbd>g<^)wQy{>dy~imt$Kr>g9MnAA9pXh)uraw z>yMF)-rY{qGPjpWWGSS=V2v<))KPusn5|2Lu!dGQ07fLN`Vn*0CmUr_8=WfC@Qjl` z&RY6q>|RUf2EqkWot?}?N*-L?{9rES69#jJ9$QfTFP*b(Z=0#$1umT@Nsio_YPFU) zFfz^i605)X85yta1XS7u7Xub(0~IaZG1qw?>A#7`+2{k6_({Rc$gu!Djd)M`)kI%Q zxf4#c8j%HUlxBm=3s7D-{31HevzzAdky?)%0y*CQuvlLlH{FBJ5%H`=&gz+gwo^-C zrF<5JpG{H%Rzb}(%-epAb!yq&`BxL*zxr9M|Mngw!1au7rcZ2gJuJtL!$|IFF(b5* zR}61|6_iYLBTL?^F_Wll%ysVZXt)1h{J-81|55W}*HQOMa7m6cM=hCVpSBr8 zA6MO!aGA4enh9K|OZq2P5Vpw~d;{2)ah56VE%}+SgO6B~ zVxZ}*&F8Af1)Ld|<>}NnZ<)hmA{50Dlh40&NA+mT;|-EuL495w!q5m;sV_4@S$6kpIT}71Wkf(Wb5b(S?&RKp9r;Gok=+_XylyAX^(>PndrK zyS$K%h{KzfkKPsg!X)zGgm%GHj{^F16K<*ZLiNE$aM5b% z9*%n=IdtoH!dG>}SwDJ=d&LS$1N6F_e=RmsE|pyZ+WMvNSE&((Re<1=!gATkY-dq+ z9K(bP^7KjVoL5V6F;Ooo8#mG&utV2v+e(`OnGGB`%@1pxLULt9t1~K?h_!gX5t${l zC@Gxl``hw%H`GDquK!9H-CI4g0I($D;zE=@Xf!a3o+||F!Oq*IM;nG({^hKdVO4hx zP*>zSo$EeQ<%q-+o5-$*{T0JuF2f_ex}Sthx>G{QEUs`I>!+46_F4ZrUH_?>H@?8Rt3U>|LPWkx} zopx}Mus3vt|1sF*7O-f)VaC)}i}%pHkvk+_uUxD;Z_2~@5FzX;pw4LuYEv({_akG8 z!!;*O!>EmQggCzZ?8b$yOFe|ADhDy-j63MlS*z=EEV{u7`J0?I{lo9&U~z!w?tvxl zB^I$-1+DuV8iJLFc`eQa)`q17w_ehHt3RD}j7zJhW`-TE`K-rCi^OFQ9)*jyI3dc<`eougOnZu`P_MWJ8A%Eu;k2 zOv6nILVGOe-QEENVG;ZlUEEIy<^GCuh9uFb8yWES#P;7*gnF#EHLfXk*ndNK@-LP5 z$o_JQL%=DvgHpc55Sv}&JM^w~&ZRv7{O1#AvgG{_NW+ZZMcr&c{`J_&>RCp|GxM9v zohml8kH#)m=r@_=w=cP!iv7s{)Wy%C6`-=C@)WjEbaqm_I^CSNT zZUE&S?}|=c^%IpM28Sm8<^fzI-LN?w{I@dL)SJ>UI@2Gcd2D-%4zd3^Q{bz|Al%D# zg4Do8(l;v>w%-}xg?xDAxFPl_CbG3J`L!!bLj6HE#p5>A4l0lsEZJ`KNeSN(e=XEh zt;|zV4V|`@!NV=f0lzJ*VxIa&rwQm|TW|}0_~jzej6yei9_$d&XKAttsyWPO*750b zQGN$Lu(vv20i>Np+wW&SS(+2GGX?)A$DlB3GPkXOgO+548@)5ePCozR6o5i z;`U`C9|l^^ncGjV4t)T)|Kf)@Hwawev+6oiLGYB5qWe})7MD&=b{T&J-_#|1;DMk;rBreIysv@t&^O3DVWOJfob_T zl1&VjoByR2yJ^u5f3qrTK#)4FZoKz^ccmNAViCJrSi+ms?D*srmL$GiT2{%3_6fS% z&)57OT`1mKf~1EXV1Ywg;O&y4WMmB8OYebf9QYtMKuZ_o5?ebYRVIbaUywp>Fkk!_ zc}_;NcUuj(S@2piS-=KvS;xu@V+)5A6#ci+4=Ne(Xn0!v`~G?$s%2mKEVpCJZX>H< z(Rb&*H?fs}THY=322K1?PHu@g3L4&5<0{J)PIUv^hWy@gf3@EkdL zmhWgZpqPrZXEfKF3&%Y#8(Oq=^_4FaWiD>aViiyW@|F-wOK^+K^Rf$(tn;*lZq}OXf=^ zC%K+!ojWq+2AaNn`w4eutwK6*R#BMDu8N3GV*eG9T?5rosc86t8K;46vQfyQh3&I0 zOetWLQ4!~;a>rQh2LLmXt{iqx@N540nd3F{lrKm_=z>wYRtZ zX1DI>zllXn*n;xvw2nz&;{c9uG)1L<9^I8RU`|>&ThG8BzSyK+4zH4@weve)i6GnD z5sg3%JYbSeUVj<~&a58Fo*VurlnS6q(I$8FNn>EIUmWp2^&D#>u%g6v;5jBC(!1JE z8wXyMowfUY-CpMp^68zpJwExlot6`E+l7u0UN7d>!?*qV=XGYD(z9Jg79~4ssAG@CWbyrSHGZ_FSlPf-ozKLk`WG@X}NB%N%N)puy;A%b5G^c;3{j^ zgxZPF8el{`831N^JaCo(lDNQez$skjZqS8jZR9m)0ZY?qC6VXqRhfae!ntH9z1+n% z&iJdliETZOM?X&=7^P|FLRi{p)S7o+TM2N)*91Dqgb?&e5GnEl5EWH(8K%P0!0Z@9 zd}-=bs;j*QGxQLPSH)s=$9JC@Ew+f(5liCPzQ1~vWAeT(ySf*G;tmt=<&??b5;61b^fZeMd=y`_iB z=&)_(zKQucf^vBIS1;|ifX>Rpzvi-RtBSqe{|Y6@q?&rH{RA&?=6hCs&Rfx=Qa_?s z_LDDKhsnIrD$Wi<2N;yebbhP+d>nzzfcoBy4ox#9^ZorKSy)9;PB3qh=7SDUK}q>A zBiD%uEn}iDvJ9;-JTlm4q)fmi&|3d(A>ozhi;0(C5){9Lh2i2pR&-a&3xnjxiVca5 z3>}FI6!CrdiGdcj&MIc}LX{{C=egXs%$Bm&qt4}^fGMfBp7NpG;P&j|n`tSV9FGf= z;@r;qi^dKi=fk3Ae*XRbh~h;Hx|%uBO}j8scF~pzX2?aBrk3G6qEX3gDR2oD2Yf>$ z6`Xl)6_)aX7my+qX*CQKC*Z!j;kzWD&McnLba!vYah@*%Pz15siGgHx3DrX;a|Mlp zs4u7G{+xmhSt68>a1%h!8)WX*!)Pm(S8?kXZD+TUqn2QObN3uj+tH+SfM)jmXaB$= ze>kf9czPCynK4y{{eR1v3kBJ-FgG6(zHk5#XN`JtF8XsOr=qGXjkjtN~^Qx%u+Q$_9FD9&thZ|6JcI%30rQEyz(_ zBNVc%i`D%HxDN4~(B`*U>{>E{A4J-@>$YFioLbWGG>MDXVfkYH2|c!v&&5{9a@EYo z^66bt3 zLp8p-h2am#h#&Y@*@b-)laG&dMn>1DvO_eF2q!c#+w@5xU6gb|!{fvgsc(zlN@@M? zI`;8L8M)Q@9(P{!>0c-vvR6HAwt+gcFZ zdw{M=ZStPI)zW<9fGm>8XBE1-ZYf6YAV)wn1rDyAR&icom_JIZ`kpru+R$FM2hqHe zVÐ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 L8tYf+IsNf(^|&9| diff --git a/community/maintainers.md b/community/maintainers.md index cdf78b150c..e66dbeb762 100644 --- a/community/maintainers.md +++ b/community/maintainers.md @@ -7,36 +7,25 @@ See [Governance](governance.md) for what each maintainer type is In alphabetical order | Name | GitHub Username | Email | Organization | -| -------------- | ---------------- | --------------------------- | ------------------ | -| Abhin Chhabra | `chhabrakadabra` | chhabra.abhin@gmail.com | Shopify | +| -------------- | ---------------- |-----------------------------| ------------------ | | Achal Shah | `achals` | achals@gmail.com | Tecton | -| Danny Chiao | `adchia` | d.chiao@gmail.com | Tecton | -| David Liu | `mavysavydav` | davidyliuliu@gmail.com | Twitter | | Felix Wang | `felixwang9817` | wangfelix98@gmail.com | Tecton | | Kevin Zhang | `kevjumba` | kevin.zhang.13499@gmail.com | Tecton | -| Matt Delacour | `MattDelac` | mdelacour@hey.com | (formerly) Shopify | | Miles Adkins | `sfc-gh-madkins` | miles.adkins@snowflake.com | Snowflake | | Willem Pienaar | `woop` | will.pienaar@gmail.com | Tecton | | Zhiling Chen | `zhilingc` | chnzhlng@gmail.com | GetGround | -## Area maintainers - -Generally, with contribution questions here, default to `#feast-development` in the [slack.feast.dev](slack.feast.dev) Slack channel, but these may be folks for you to tag in messages - -| Area | Description | Name | -| -------------------- | -------------------------------------------------------------------------- | --------------------------------------------- | -| Data ingestion | ingesting batch + stream data into the online store (materialization) | Achal Shah,
Felix Wang,
Kevin Zhang | -| Developer experience | tooling, testing, documentation, tutorials | Achal Shah | -| Feature serving | optimization, caching, deployment patterns, batch retrieval, range queries | Dvir Dukhan | -| Ops | general deployment concerns, CI/CD, versioning | Keith Adler,
Danny Chiao,
Felix Wang | -| Web UI | i.e. `feast ui` output | Danny Chiao,
David Liu | - ## Emeritus Maintainers -| Name | GitHub Username | Email | Organization | -| ------------------- | --------------- | --------------------------- | ------------ | -| Oleg Avdeev | oavdeev | oleg.v.avdeev@gmail.com | Tecton | -| Oleksii Moskalenko | pyalex | moskalenko.alexey@gmail.com | Tecton | -| Jay Parthasarthy | jparthasarthy | jparthasarthy@gmail.com | Tecton | -| Pradithya Aria Pura | pradithya | pradithya.aria@gmail.com | Gojek | -| Tsotne Tabidze | tsotnet | tsotnet@gmail.com | Tecton | \ No newline at end of file +| Name | GitHub Username | Email | Organization | +|---------------------|-----------------|-----------------------------|-------------------| +| Oleg Avdeev | oavdeev | oleg.v.avdeev@gmail.com | Tecton | +| Oleksii Moskalenko | pyalex | moskalenko.alexey@gmail.com | Tecton | +| Jay Parthasarthy | jparthasarthy | jparthasarthy@gmail.com | Tecton | +| Danny Chiao | adchia | danny@tecton.ai | Tecton | +| Pradithya Aria Pura | pradithya | pradithya.aria@gmail.com | Gojek | +| Tsotne Tabidze | tsotnet | tsotnet@gmail.com | Tecton | +| Abhin Chhabra | chhabrakadabra | chhabra.abhin@gmail.com | Shopify | +| Danny Chiao | adchia | danny@tecton.ai | Tecton | +| David Liu | mavysavydav | davidyliuliu@gmail.com | Twitter | +| Matt Delacour | MattDelac | mdelacour@hey.com | Shopify | diff --git a/docs/README.md b/docs/README.md index a305c4aecd..66c7548440 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,7 +57,7 @@ Many companies have used Feast to power real-world ML use cases such as: ## How can I get started? {% hint style="info" %} -The best way to learn Feast is to use it. Join our [Slack channel](http://slack.feast.dev) and head over to our [Quickstart](getting-started/quickstart.md) and try it out! +The best way to learn Feast is to use it. Head over to our [Quickstart](getting-started/quickstart.md) and try it out! {% endhint %} Explore the following resources to get started with Feast: diff --git a/docs/community.md b/docs/community.md index 098b6b3f90..21cca702bf 100644 --- a/docs/community.md +++ b/docs/community.md @@ -4,13 +4,6 @@ * [GitHub Repository](https://github.com/feast-dev/feast/): Find the complete Feast codebase on GitHub. * [Community Governance Doc](https://github.com/feast-dev/feast/blob/master/community): See the governance model of Feast, including who the maintainers are and how decisions are made. -* [Slack](https://slack.feast.dev): Feel free to ask questions or say hello! This is the main place where maintainers and contributors brainstorm and where users ask questions or discuss best practices. - * Feast users should join `#feast-general` or `#feast-beginners` to ask questions - * Feast developers / contributors should join `#feast-development` -* [Mailing list](https://groups.google.com/d/forum/feast-dev): We have both a user and developer mailing list. - * Feast users should join [feast-discuss@googlegroups.com](mailto:feast-discuss@googlegroups.com) group by clicking [here](https://groups.google.com/g/feast-discuss). - * Feast developers / contributors should join [feast-dev@googlegroups.com](mailto:feast-dev@googlegroups.com) group by clicking [here](https://groups.google.com/d/forum/feast-dev). -* [Community Calendar](https://calendar.google.com/calendar/u/0?cid=ZTFsZHVhdGM3MDU3YTJucTBwMzNqNW5rajBAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ): Includes community calls and design meetings. * [Google Folder](https://drive.google.com/drive/u/0/folders/1jgMHOPDT2DvBlJeO9LCM79DP4lm4eOrR): This folder is used as a central repository for all Feast resources. For example: * Design proposals in the form of Request for Comments (RFC). * User surveys and meeting minutes. @@ -19,36 +12,4 @@ ## How can I get help? -* **Slack:** Need to speak to a human? Come ask a question in our Slack channel (link above). * **GitHub Issues:** Found a bug or need a feature? [Create an issue on GitHub](https://github.com/feast-dev/feast/issues/new). -* **StackOverflow:** Need to ask a question on how to use Feast? We also monitor and respond to [StackOverflow](https://stackoverflow.com/questions/tagged/feast). - -## Community Calls - -### General community call (biweekly) -We have a user and contributor community call every two weeks (US & EU friendly). - -{% hint style="info" %} -Please join the above Feast user groups in order to see calendar invites to the community calls -{% endhint %} - -#### Frequency (every 2 weeks) - -* Tuesday 10:00 am to 10:30 am PST - -#### Links - -* Zoom: [https://zoom.us/j/6325193230](https://zoom.us/j/6325193230) -* Meeting notes (incl recordings): [https://bit.ly/feast-notes](https://bit.ly/feast-notes) - -### Developers call (biweekly) -We also have a `#feast-development` community call every two weeks, where we discuss contributions + brainstorm best practices. - -#### Frequency (every 2 weeks) - -* Tuesday 8:00 am to 8:30 am PST - -#### Links - -* Meeting notes (incl recordings): [Feast Development Biweekly](https://docs.google.com/document/d/1zUbIWFWjaBEVlToOdupnmKQwgAtFYx41sPoEEEdd2io/edit#) -* Zoom: [https://zoom.us/j/93657748160?pwd=K3ZpdzhqejgrcXNhc3BlSjFMdzUxdz09](https://zoom.us/j/93657748160?pwd=K3ZpdzhqejgrcXNhc3BlSjFMdzUxdz09) diff --git a/docs/getting-started/faq.md b/docs/getting-started/faq.md index a511ddb0dc..9b7eb834bf 100644 --- a/docs/getting-started/faq.md +++ b/docs/getting-started/faq.md @@ -3,7 +3,7 @@ {% hint style="info" %} **Don't see your question?** -We encourage you to ask questions on [Slack](https://slack.feast.dev) or [GitHub](https://github.com/feast-dev/feast). Even better, once you get an answer, add the answer to this FAQ via a [pull request](../project/development-guide.md)! +We encourage you to ask questions on [GitHub](https://github.com/feast-dev/feast). Even better, once you get an answer, add the answer to this FAQ via a [pull request](../project/development-guide.md)! {% endhint %} ## Getting started diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index b30bdb585c..d10e8a174a 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -555,9 +555,7 @@ show up in the upcoming concepts + architecture + tutorial pages as well. ## Next steps -* Join the [email newsletter](https://feast.dev/) to get new updates on Feast / feature stores. * Read the [Concepts](concepts/) page to understand the Feast data model. * Read the [Architecture](architecture-and-components/) page. * Check out our [Tutorials](../tutorials/tutorials-overview/) section for more examples on how to use Feast. * Follow our [Running Feast with Snowflake/GCP/AWS](../how-to-guides/feast-snowflake-gcp-aws/) guide for a more in-depth tutorial on using Feast. -* Join other Feast users and contributors in [Slack](https://slack.feast.dev) and become part of the community! diff --git a/docs/project/contributing.md b/docs/project/contributing.md index 9a3e3e1a3e..cded378951 100644 --- a/docs/project/contributing.md +++ b/docs/project/contributing.md @@ -2,21 +2,15 @@ ## Getting started After familiarizing yourself with the documentation, the simplest way to get started is to: -1. Join the `#feast-development` [Slack channel](https://tectonfeast.slack.com/archives/C01NTDB88QK), where contributors discuss ideas and PRs -2. Join our Google Groups in order to get access to RFC folders + get invites to community calls. See [community](../community.md) for more details. -3. Setup your developer environment by following [development guide](development-guide.md). -4. Either create a [GitHub issue](https://github.com/feast-dev/feast/issues) or make a draft PR (following [development guide](development-guide.md)) to get the ball rolling! +1. Setup your developer environment by following [development guide](development-guide.md). +2. Either create a [GitHub issue](https://github.com/feast-dev/feast/issues) or make a draft PR (following [development guide](development-guide.md)) to get the ball rolling! ## Decision making process *See [governance](../../community/governance.md) for more details here* We follow a process of [lazy consensus](http://community.apache.org/committers/lazyConsensus.html). If you believe you know what the project needs then just start development. As long as there is no active opposition and the PR has been approved by maintainers or CODEOWNERS, contributions will be merged. -We use our `#feast-development` [Slack channel](https://tectonfeast.slack.com/archives/C01NTDB88QK), [GitHub issues](https://github.com/feast-dev/feast/issues), and [GitHub pull requests](https://github.com/feast-dev/feast/pulls) to communicate development ideas. - -The general decision making workflow is as follows: - - +We use our [GitHub issues](https://github.com/feast-dev/feast/issues), and [GitHub pull requests](https://github.com/feast-dev/feast/pulls) to communicate development ideas. > **Note**: There may not always a corresponding CODEOWNER for the affected code, in which case the responsibility falls on other maintainers or contributors with write access to review + merge the PR @@ -30,7 +24,7 @@ See also [Making a pull request](development-guide.md#making-a-pull-request) for ## Resources -- [Community](../community.md) for other ways to get involved with the community (e.g. joining community calls) +- [Community](../community.md) for other ways to get involved with the community - [Development guide](development-guide.md) for tips on how to contribute - [Feast GitHub issues](https://github.com/feast-dev/feast/issues) to see what others are working on - [Feast RFCs](https://drive.google.com/drive/u/0/folders/1msUsgmDbVBaysmhBlg9lklYLLTMk4bC3) for a folder of previously written RFCs \ No newline at end of file diff --git a/docs/project/development-guide.md b/docs/project/development-guide.md index 69a1ab298a..931d0243d2 100644 --- a/docs/project/development-guide.md +++ b/docs/project/development-guide.md @@ -51,13 +51,6 @@ The compatibility policy for Feast can be found [here](compatibility.md), and sh ## Community See [Contribution process](./contributing.md) and [Community](../community.md) for details on how to get more involved in the community. -A quick few highlights: -- [RFCs](https://drive.google.com/drive/u/0/folders/0AAe8j7ZK3sxSUk9PVA) -- [Community Slack](https://slack.feast.dev/) -- [Feast Dev Mailing List](https://groups.google.com/g/feast-dev) -- [Community Calendar](https://calendar.google.com/calendar/u/0?cid=ZTFsZHVhdGM3MDU3YTJucTBwMzNqNW5rajBAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) - - Includes biweekly community calls at 10AM PST - ## Making a pull request We use the convention that the assignee of a PR is the person with the next action. diff --git a/docs/project/release-process.md b/docs/project/release-process.md index 0aa7d3fb5b..d3ff34bbc3 100644 --- a/docs/project/release-process.md +++ b/docs/project/release-process.md @@ -16,14 +16,14 @@ If you were cutting Feast 0.22.3, for example, you might do: After this step, you will have all the changes you need in the branch. -### 2. Pre-release verification +### 2. Pre-release verification (currently broken) A lot of things can go wrong. One of the most common is getting the wheels to build correctly (and not accidentally building dev wheels from improper tagging or local code changes during the release process). Another possible failure is that the Docker images might not build correctly. We verify the building the wheels and Docker images in **your fork** of Feast, not the main feast-dev/feast repo. -#### For minor releases (e.g. v0.22.0) +#### 2a. Verifying minor releases (e.g. v0.22.0) 1. Merge upstream master changes into your **fork**. Make sure you are running the workflow off of your fork! 2. Create a tag manually for the release on your fork. For example, if you are doing a release for version 0.22.0, create a tag by doing the following. - Checkout master branch and run `git tag v0.22.0`. @@ -37,17 +37,7 @@ We verify the building the wheels and Docker images in **your fork** of Feast, n 5. Run the workflow off of the tag you just created(`v0.22.0` in this case, **not** the master branch) and verify that the workflow worked (i.e ensure that all jobs are green). -#### For patch releases (e.g. v0.22.3) -You should already have checked out the existing minor release branch from step 1 (e.g. `v0.22-branch`). -1. Push the minor release branch to your fork (`git push -u origin `). -2. Add a patch release tag (e.g `v0.22.1`) by running `git tag `. - > This is important. If you don't have a tag, then the wheels you build will be **dev wheels**, which we can't - > push. The release process will automatically produce a tag for you via Semantic Release. -3. Push tags to your **origin branch** (not the upstream feast-dev/feast branch) with `git push origin `. -4. Kick off `build_wheels` workflow in your fork in the same way as is detailed in the last section, running the - workflow from this tag you just pushed up. - -### 3. Release for Python and Java SDK +### 2. Release for Python and Java SDK 1. Generate a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) or retrieve your saved personal access token. * The personal access token should have all of the permissions under the `repo` checkbox. 2. Access the `Actions` tab on the main `feast-dev/feast` repo and find the `release` action. @@ -81,7 +71,7 @@ so it helps to have a high level overview. See https://github.com/feast-dev/feas #### 4c: Update documentation -In the Feast Gitbook (ask [Danny Chiao](https://tectonfeast.slack.com/team/U029405HFEU) in Slack for access): +In the Feast Gitbook: 1. Create a new space within the Feast collection 2. Go to the overflow menu on the top -> Synchronize with Git 1. Specify GitHub as the provider diff --git a/docs/reference/feast-cli-commands.md b/docs/reference/feast-cli-commands.md index 38e85843d4..7bdea19e61 100644 --- a/docs/reference/feast-cli-commands.md +++ b/docs/reference/feast-cli-commands.md @@ -11,8 +11,6 @@ Usage: feast [OPTIONS] COMMAND [ARGS]... For more information, see our public docs at https://docs.feast.dev/ - For any questions, you can reach us at https://slack.feast.dev/ - Options: -c, --chdir TEXT Switch to a different feature repository directory before executing the given subcommand. diff --git a/docs/roadmap.md b/docs/roadmap.md index d5dc88005b..a04ede7c99 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -3,7 +3,6 @@ The list below contains the functionality that contributors are planning to develop for Feast. * We welcome contribution to all items in the roadmap! -* Have questions about the roadmap? Go to the Slack channel to ask on #feast-development. * **Data Sources** * [x] [Snowflake source](https://docs.feast.dev/reference/data-sources/snowflake) diff --git a/examples/quickstart/quickstart.ipynb b/examples/quickstart/quickstart.ipynb index 6e07d3e23b..f84457ac02 100644 --- a/examples/quickstart/quickstart.ipynb +++ b/examples/quickstart/quickstart.ipynb @@ -1066,7 +1066,6 @@ "- Read the [Concepts](https://docs.feast.dev/getting-started/concepts/) page to understand the Feast data model and architecture.\n", "- Check out our [Tutorials](https://docs.feast.dev/tutorials/tutorials-overview) section for more examples on how to use Feast.\n", "- Follow our [Running Feast with Snowflake/GCP/AWS](https://docs.feast.dev/how-to-guides/feast-snowflake-gcp-aws) guide for a more in-depth tutorial on using Feast.\n", - "- Join other Feast users and contributors in [Slack](https://slack.feast.dev/) and become part of the community!" ] } ], diff --git a/infra/templates/README.md.jinja2 b/infra/templates/README.md.jinja2 index 47779d4eb7..1cce08ecfa 100644 --- a/infra/templates/README.md.jinja2 +++ b/infra/templates/README.md.jinja2 @@ -25,7 +25,7 @@ Feast allows ML platform teams to: * **Avoid data leakage** by generating point-in-time correct feature sets so data scientists can focus on feature engineering rather than debugging error-prone dataset joining logic. This ensure that future feature values do not leak to models during training. * **Decouple ML from data infrastructure** by providing a single data access layer that abstracts feature storage from feature retrieval, ensuring models remain portable as you move from training models to serving models, from batch models to realtime models, and from one data infra system to another. -Please see our [documentation](https://docs.feast.dev/) for more information about the project, or sign up for an [email newsletter](https://feast.dev/). +Please see our [documentation](https://docs.feast.dev/) for more information about the project. ## 📐 Architecture ![](docs/assets/feast_marchitecture.png) @@ -149,7 +149,6 @@ Please refer to the official documentation at [Documentation](https://docs.feast * [Tutorials](https://docs.feast.dev/tutorials/tutorials-overview) * [Running Feast with Snowflake/GCP/AWS](https://docs.feast.dev/how-to-guides/feast-snowflake-gcp-aws) * [Change Log](https://github.com/feast-dev/feast/blob/master/CHANGELOG.md) - * [Slack (#Feast)](https://slack.feast.dev/) ## 👋 Contributing Feast is a community project and is still under active development. Please have a look at our contributing and development guides if you want to contribute to the project: diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 53c346b6eb..3153f02e51 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -86,8 +86,6 @@ def cli( Feast CLI For more information, see our public docs at https://docs.feast.dev/ - - For any questions, you can reach us at https://slack.feast.dev/ """ ctx.ensure_object(dict) ctx.obj["CHDIR"] = Path.cwd() if chdir is None else Path(chdir).absolute() diff --git a/ui/CONTRIBUTING.md b/ui/CONTRIBUTING.md index 970bd3676c..3c13759e26 100644 --- a/ui/CONTRIBUTING.md +++ b/ui/CONTRIBUTING.md @@ -91,7 +91,7 @@ The Feast UI is published as a module to NPM and can be found here: https://www. ### Requirements To publish a new version of the module, you will need: -- to be part of the @feast-dev team in NPM. Ask `#feast-development` on http://slack.feast.dev to add you if necessary. +- to be part of the @feast-dev team in NPM. - to [login to your NPM account on the command line](https://docs.npmjs.com/cli/v8/commands/npm-adduser). ### Steps for Publishing From 2192e6527fa10f1580e4dd8f350e05e45af981b7 Mon Sep 17 00:00:00 2001 From: James Crabtree <77012363+james-crabtree-sp@users.noreply.github.com> Date: Thu, 21 Sep 2023 14:53:10 -0500 Subject: [PATCH 19/19] feat: Add support for in_cluster config and additional labels for bytewax materialization (#3754) * SAASMLOPS-734 bytewax in-cluster config, custom labels, fix worker image deps Signed-off-by: James Crabtree * SAASMLOPS-769 make max parallelism configurable Signed-off-by: James Crabtree --------- Signed-off-by: James Crabtree --- .../bytewax/bytewax_materialization_engine.py | 25 +++++++++++-------- setup.py | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/sdk/python/feast/infra/materialization/contrib/bytewax/bytewax_materialization_engine.py b/sdk/python/feast/infra/materialization/contrib/bytewax/bytewax_materialization_engine.py index b222128bbb..21b7a5da1f 100644 --- a/sdk/python/feast/infra/materialization/contrib/bytewax/bytewax_materialization_engine.py +++ b/sdk/python/feast/infra/materialization/contrib/bytewax/bytewax_materialization_engine.py @@ -61,6 +61,12 @@ class BytewaxMaterializationEngineConfig(FeastConfigBaseModel): include_security_context_capabilities: bool = True """ (optional) Include security context capabilities in the init and job container spec """ + labels: dict = {} + """ (optional) additional labels to append to kubernetes objects """ + + max_parallelism: int = 10 + """ (optional) Maximum number of pods (default 10) allowed to run in parallel per job""" + class BytewaxMaterializationEngine(BatchMaterializationEngine): def __init__( @@ -82,7 +88,7 @@ def __init__( self.online_store = online_store # TODO: Configure k8s here - k8s_config.load_kube_config() + k8s_config.load_config() self.k8s_client = client.api_client.ApiClient() self.v1 = client.CoreV1Api(self.k8s_client) @@ -196,14 +202,13 @@ def _create_configuration_map(self, job_id, paths, feature_view, namespace): {"paths": paths, "feature_view": feature_view.name} ) + labels = {"feast-bytewax-materializer": "configmap"} configmap_manifest = { "kind": "ConfigMap", "apiVersion": "v1", "metadata": { "name": f"feast-{job_id}", - "labels": { - "feast-bytewax-materializer": "configmap", - }, + "labels": {**labels, **self.batch_engine_config.labels}, }, "data": { "feature_store.yaml": feature_store_configuration, @@ -260,27 +265,25 @@ def _create_job_definition(self, job_id, namespace, pods, env): "drop": ["ALL"], } + job_labels = {"feast-bytewax-materializer": "job"} + pod_labels = {"feast-bytewax-materializer": "pod"} job_definition = { "apiVersion": "batch/v1", "kind": "Job", "metadata": { "name": f"dataflow-{job_id}", "namespace": namespace, - "labels": { - "feast-bytewax-materializer": "job", - }, + "labels": {**job_labels, **self.batch_engine_config.labels}, }, "spec": { "ttlSecondsAfterFinished": 3600, "completions": pods, - "parallelism": pods, + "parallelism": min(pods, self.batch_engine_config.max_parallelism), "completionMode": "Indexed", "template": { "metadata": { "annotations": self.batch_engine_config.annotations, - "labels": { - "feast-bytewax-materializer": "pod", - }, + "labels": {**pod_labels, **self.batch_engine_config.labels}, }, "spec": { "restartPolicy": "Never", diff --git a/setup.py b/setup.py index f7b1ff0417..047100f03e 100644 --- a/setup.py +++ b/setup.py @@ -98,7 +98,7 @@ "hiredis>=2.0.0,<3", ] -AWS_REQUIRED = ["boto3>=1.17.0,<2", "docker>=5.0.2"] +AWS_REQUIRED = ["boto3>=1.17.0,<2", "docker>=5.0.2", "s3fs"] BYTEWAX_REQUIRED = ["bytewax==0.15.1", "docker>=5.0.2", "kubernetes<=20.13.0"]