# Copyright 2019 The Feast Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import glob
import json
import os
import pathlib
import re
import shutil
import subprocess
import sys
from distutils.cmd import Command
from distutils.dir_util import copy_tree
from pathlib import Path
from subprocess import CalledProcessError

from setuptools import Extension, find_packages

try:
    from setuptools import setup
    from setuptools.command.build_ext import build_ext as _build_ext
    from setuptools.command.build_py import build_py
    from setuptools.command.develop import develop
    from setuptools.command.install import install

except ImportError:
    from distutils.command.build_ext import build_ext as _build_ext
    from distutils.command.build_py import build_py
    from distutils.core import setup

NAME = "feast"
DESCRIPTION = "Python SDK for Feast"
URL = "https://github.com/feast-dev/feast"
AUTHOR = "Feast"
REQUIRES_PYTHON = ">=3.8.0"

REQUIRED = [
    "click>=7.0.0,<9.0.0",
    "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",
    "Jinja2>=2,<4",
    "jsonschema",
    "mmh3",
    "numpy>=1.22,<3",
    "pandas>=1.4.3,<2",
    "pandavro~=1.5.0",  # For some reason pandavro higher than 1.5.* only support pandas less than 1.3.
    "protobuf<5,>3.20",
    "proto-plus>=1.20.0,<2",
    "pyarrow>=4,<9",
    "pydantic>=1,<2",
    "pygments>=2.12.0,<3",
    "PyYAML>=5.4.0,<7",
    "requests",
    "SQLAlchemy[mypy]>1,<2",
    "tabulate>=0.8.0,<1",
    "tenacity>=7,<9",
    "toml>=0.10.0,<1",
    "tqdm>=4,<5",
    "typeguard",
    "fastapi>=0.68.0,<1",
    "uvicorn[standard]>=0.14.0,<1",
    "dask>=2021.1.0",
    "bowler",  # Needed for automatic repo upgrades
    "httpx>=0.23.3",  # FastAPI does not correctly pull starlette dependency on httpx see thread(https://github.com/tiangolo/fastapi/issues/5656).
]

GCP_REQUIRED = [
    "google-api-core>=1.23.0,<3",
    "googleapis-common-protos>=1.52.0,<2",
    "google-cloud-bigquery[pandas]>=2,<4",
    "google-cloud-bigquery-storage >= 2.0.0,<3",
    "google-cloud-datastore>=2.1.0,<3",
    "google-cloud-storage>=1.34.0,<3",
    "google-cloud-bigtable>=2.11.0,<3",
]

REDIS_REQUIRED = [
    "redis==4.2.2",
    "hiredis>=2.0.0,<3",
]

AWS_REQUIRED = ["boto3>=1.17.0,<=1.20.23", "docker>=5.0.2", "s3fs>=0.4.0,<=2022.01.0"]

BYTEWAX_REQUIRED = ["bytewax==0.13.1", "docker>=5.0.2", "kubernetes<=20.13.0"]

SNOWFLAKE_REQUIRED = [
    "snowflake-connector-python[pandas]>=2.7.3,<3",
    # `pyOpenSSL==22.1.0` requires `cryptography<39,>=38.0.0`, which is incompatible
    # with `snowflake-connector-python[pandas]==2.8.0`, which depends on
    # `cryptography<37.0.0,>=3.1.0`.
    "pyOpenSSL<22.1.0",
]

SPARK_REQUIRED = [
    "pyspark>=3.0.0,<4",
]

TRINO_REQUIRED = [
    "trino>=0.305.0,<0.400.0", "regex"
]

POSTGRES_REQUIRED = [
    "psycopg2-binary>=2.8.3,<3",
]

MYSQL_REQUIRED = ["mysqlclient", "pymysql", "types-PyMySQL"]

HBASE_REQUIRED = [
    "happybase>=1.2.0,<3",
]

CASSANDRA_REQUIRED = [
    "cassandra-driver>=3.24.0,<4",
]

GE_REQUIRED = ["great_expectations>=0.14.0,<0.15.0"]

GO_REQUIRED = [
    "cffi~=1.15.0",
]

AZURE_REQUIRED = [
    "azure-storage-blob>=0.37.0",
    "azure-identity>=1.6.1",
    "SQLAlchemy>=1.4.19",
    "pyodbc>=4.0.30",
    "pymssql",
]

ROCKSET_REQUIRED = [
    "rockset>=1.0.3",
]

CI_REQUIRED = (
    [
        "build",
        "cryptography>=35.0,<36",
        "flake8",
        "black>=22.6.0,<23",
        "isort>=5,<6",
        "grpcio-tools>=1.47.0",
        "grpcio-testing>=1.47.0",
        "minio==7.1.0",
        "mock==2.0.0",
        "moto<4",
        "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",
        "psutil==5.9.0",
        "py>=1.11.0",  # https://github.com/pytest-dev/pytest/issues/10420
        "pytest>=6.0.0,<8",
        "pytest-cov",
        "pytest-xdist",
        "pytest-benchmark>=3.4.1,<4",
        "pytest-lazy-fixture==0.6.3",
        "pytest-timeout==1.4.2",
        "pytest-ordering~=0.6.0",
        "pytest-mock==1.10.4",
        "Sphinx>4.0.0,<7",
        "testcontainers>=3.5,<4",
        "adlfs==0.5.9",
        "firebase-admin>=5.2.0,<6",
        "pre-commit",
        "assertpy==1.1",
        "pip-tools",
        "pybindgen",
        "types-protobuf~=3.19.22",
        "types-python-dateutil",
        "types-pytz",
        "types-PyYAML",
        "types-redis",
        "types-requests",
        "types-setuptools",
        "types-tabulate",
    ]
    + GCP_REQUIRED
    + REDIS_REQUIRED
    + AWS_REQUIRED
    + BYTEWAX_REQUIRED
    + SNOWFLAKE_REQUIRED
    + SPARK_REQUIRED
    + POSTGRES_REQUIRED
    + MYSQL_REQUIRED
    + TRINO_REQUIRED
    + GE_REQUIRED
    + HBASE_REQUIRED
    + CASSANDRA_REQUIRED
    + AZURE_REQUIRED
    + ROCKSET_REQUIRED
)


# rtd builds fail because of mysql not being installed in their environment.
# We can add mysql there, but it's not strictly needed. This will be faster for builds.
DOCS_REQUIRED = CI_REQUIRED.copy()
for _r in MYSQL_REQUIRED:
    DOCS_REQUIRED.remove(_r)

DEV_REQUIRED = ["mypy-protobuf==3.1", "grpcio-testing~=1.0"] + CI_REQUIRED

# Get git repo root directory
repo_root = str(pathlib.Path(__file__).resolve().parent)

# README file from Feast repo root directory
README_FILE = os.path.join(repo_root, "README.md")
with open(README_FILE, "r", encoding="utf8") as f:
    LONG_DESCRIPTION = f.read()

# Add Support for parsing tags that have a prefix containing '/' (ie 'sdk/go') to setuptools_scm.
# Regex modified from default tag regex in:
# https://github.com/pypa/setuptools_scm/blob/2a1b46d38fb2b8aeac09853e660bcd0d7c1bc7be/src/setuptools_scm/config.py#L9
TAG_REGEX = re.compile(
    r"^(?:[\/\w-]+)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$"
)

# Only set use_scm_version if git executable exists (setting this variable causes pip to use git under the hood)
if shutil.which("git"):
    use_scm_version = {"root": ".", "relative_to": __file__, "tag_regex": TAG_REGEX}
else:
    use_scm_version = None

PROTO_SUBDIRS = ["core", "serving", "types", "storage"]
PYTHON_CODE_PREFIX = "sdk/python"


class BuildPythonProtosCommand(Command):
    description = "Builds the proto files into Python files."
    user_options = [
        ("inplace", "i", "Write generated proto files to source directory."),
    ]

    def initialize_options(self):
        self.python_protoc = [
            sys.executable,
            "-m",
            "grpc_tools.protoc",
        ]  # find_executable("protoc")
        self.proto_folder = os.path.join(repo_root, "protos")
        self.sub_folders = PROTO_SUBDIRS
        self.build_lib = None
        self.inplace = 0

    def finalize_options(self):
        self.set_undefined_options("build", ("build_lib", "build_lib"))

    @property
    def python_folder(self):
        if self.inplace:
            return os.path.join(
                os.path.dirname(__file__) or os.getcwd(), "sdk/python/feast/protos"
            )

        return os.path.join(self.build_lib, "feast/protos")

    def _generate_python_protos(self, path: str):
        proto_files = glob.glob(os.path.join(self.proto_folder, path))
        Path(self.python_folder).mkdir(parents=True, exist_ok=True)
        subprocess.check_call(
            self.python_protoc
            + [
                "-I",
                self.proto_folder,
                "--python_out",
                self.python_folder,
                "--grpc_python_out",
                self.python_folder,
                "--mypy_out",
                self.python_folder,
            ]
            + proto_files
        )

    def run(self):
        for sub_folder in self.sub_folders:
            self._generate_python_protos(f"feast/{sub_folder}/*.proto")
            # We need the __init__ files for each of the generated subdirs
            # so that they are regular packages, and don't need the `--namespace-packages` flags
            # when being typechecked using mypy.
            with open(f"{self.python_folder}/feast/{sub_folder}/__init__.py", "w"):
                pass

        with open(f"{self.python_folder}/__init__.py", "w"):
            pass
        with open(f"{self.python_folder}/feast/__init__.py", "w"):
            pass

        for path in Path(self.python_folder).rglob("*.py"):
            for folder in self.sub_folders:
                # Read in the file
                with open(path, "r") as file:
                    filedata = file.read()

                # Replace the target string
                filedata = filedata.replace(
                    f"from feast.{folder}", f"from feast.protos.feast.{folder}"
                )

                # Write the file out again
                with open(path, "w") as file:
                    file.write(filedata)


def _generate_path_with_gopath():
    go_path = subprocess.check_output(["go", "env", "GOPATH"]).decode("utf-8")
    go_path = go_path.strip()
    path_val = os.getenv("PATH")
    path_val = f"{path_val}:{go_path}/bin"

    return path_val


def _ensure_go_and_proto_toolchain():
    try:
        version = subprocess.check_output(["go", "version"])
    except Exception as e:
        raise RuntimeError("Unable to find go toolchain") from e

    semver_string = re.search(r"go[\S]+", str(version)).group().lstrip("go")
    parts = semver_string.split(".")
    if not (int(parts[0]) >= 1 and int(parts[1]) >= 16):
        raise RuntimeError(f"Go compiler too old; expected 1.16+ found {semver_string}")

    path_val = _generate_path_with_gopath()

    try:
        subprocess.check_call(["protoc-gen-go", "--version"], env={"PATH": path_val})
        subprocess.check_call(
            ["protoc-gen-go-grpc", "--version"], env={"PATH": path_val}
        )
    except Exception as e:
        raise RuntimeError("Unable to find go/grpc extensions for protoc") from e


class BuildGoProtosCommand(Command):
    description = "Builds the proto files into Go files."
    user_options = []

    def initialize_options(self):
        self.go_protoc = [
            sys.executable,
            "-m",
            "grpc_tools.protoc",
        ]  # find_executable("protoc")
        self.proto_folder = os.path.join(repo_root, "protos")
        self.go_folder = os.path.join(repo_root, "go/protos")
        self.sub_folders = PROTO_SUBDIRS
        self.path_val = _generate_path_with_gopath()

    def finalize_options(self):
        pass

    def _generate_go_protos(self, path: str):
        proto_files = glob.glob(os.path.join(self.proto_folder, path))

        try:
            subprocess.check_call(
                self.go_protoc
                + [
                    "-I",
                    self.proto_folder,
                    "--go_out",
                    self.go_folder,
                    "--go_opt=module=github.com/feast-dev/feast/go/protos",
                    "--go-grpc_out",
                    self.go_folder,
                    "--go-grpc_opt=module=github.com/feast-dev/feast/go/protos",
                ]
                + proto_files,
                env={"PATH": self.path_val},
            )
        except CalledProcessError as e:
            print(f"Stderr: {e.stderr}")
            print(f"Stdout: {e.stdout}")

    def run(self):
        go_dir = Path(repo_root) / "go" / "protos"
        go_dir.mkdir(exist_ok=True)
        for sub_folder in self.sub_folders:
            self._generate_go_protos(f"feast/{sub_folder}/*.proto")


class BuildCommand(build_py):
    """Custom build command."""

    def run(self):
        self.run_command("build_python_protos")
        if os.getenv("COMPILE_GO", "false").lower() == "true":
            _ensure_go_and_proto_toolchain()
            self.run_command("build_go_protos")

        self.run_command("build_ext")
        build_py.run(self)


class DevelopCommand(develop):
    """Custom develop command."""

    def run(self):
        self.reinitialize_command("build_python_protos", inplace=1)
        self.run_command("build_python_protos")
        if os.getenv("COMPILE_GO", "false").lower() == "true":
            _ensure_go_and_proto_toolchain()
            self.run_command("build_go_protos")

        develop.run(self)


class build_ext(_build_ext):
    def finalize_options(self) -> None:
        super().finalize_options()
        if os.getenv("COMPILE_GO", "false").lower() == "false":
            self.extensions = [e for e in self.extensions if not self._is_go_ext(e)]

    def _is_go_ext(self, ext: Extension):
        return any(
            source.endswith(".go") or source.startswith("github")
            for source in ext.sources
        )

    def build_extension(self, ext: Extension):
        print(f"Building extension {ext}")
        if not self._is_go_ext(ext):
            # the base class may mutate `self.compiler`
            compiler = copy.deepcopy(self.compiler)
            self.compiler, compiler = compiler, self.compiler
            try:
                return _build_ext.build_extension(self, ext)
            finally:
                self.compiler, compiler = compiler, self.compiler

        bin_path = _generate_path_with_gopath()
        go_env = json.loads(
            subprocess.check_output(["go", "env", "-json"]).decode("utf-8").strip()
        )

        print(f"Go env: {go_env}")
        print(f"CWD: {os.getcwd()}")

        destination = os.path.dirname(os.path.abspath(self.get_ext_fullpath(ext.name)))
        subprocess.check_call(
            ["go", "install", "golang.org/x/tools/cmd/goimports"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            ["go", "get", "github.com/go-python/gopy@v0.4.4"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            ["go", "install", "github.com/go-python/gopy"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            [
                "gopy",
                "build",
                "-output",
                destination,
                "-vm",
                sys.executable,
                "--build-tags",
                "cgo,ccalloc",
                "--dynamic-link=True",
                "-no-make",
                *ext.sources,
            ],
            env={
                "PATH": bin_path,
                "CGO_LDFLAGS_ALLOW": ".*",
                **go_env,
            },
        )

    def copy_extensions_to_source(self):
        build_py = self.get_finalized_command("build_py")
        for ext in self.extensions:
            fullname = self.get_ext_fullname(ext.name)
            modpath = fullname.split(".")
            package = ".".join(modpath[:-1])
            package_dir = build_py.get_package_dir(package)

            src_dir = dest_dir = package_dir

            if src_dir.startswith(PYTHON_CODE_PREFIX):
                src_dir = package_dir[len(PYTHON_CODE_PREFIX) :]
            src_dir = src_dir.lstrip("/")

            src_dir = os.path.join(self.build_lib, src_dir)

            # copy whole directory
            print(f"Copying from {src_dir} to {dest_dir}")
            copy_tree(src_dir, dest_dir)


setup(
    name=NAME,
    author=AUTHOR,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    long_description_content_type="text/markdown",
    python_requires=REQUIRES_PYTHON,
    url=URL,
    packages=find_packages(
        where=PYTHON_CODE_PREFIX, exclude=("java", "infra", "sdk/python/tests", "ui")
    ),
    package_dir={"": PYTHON_CODE_PREFIX},
    install_requires=REQUIRED,
    # https://stackoverflow.com/questions/28509965/setuptools-development-requirements
    # Install dev requirements with: pip install -e .[dev]
    extras_require={
        "dev": DEV_REQUIRED,
        "ci": CI_REQUIRED,
        "gcp": GCP_REQUIRED,
        "aws": AWS_REQUIRED,
        "bytewax": BYTEWAX_REQUIRED,
        "redis": REDIS_REQUIRED,
        "snowflake": SNOWFLAKE_REQUIRED,
        "spark": SPARK_REQUIRED,
        "trino": TRINO_REQUIRED,
        "postgres": POSTGRES_REQUIRED,
        "azure": AZURE_REQUIRED,
        "mysql": MYSQL_REQUIRED,
        "ge": GE_REQUIRED,
        "hbase": HBASE_REQUIRED,
        "go": GO_REQUIRED,
        "docs": DOCS_REQUIRED,
        "cassandra": CASSANDRA_REQUIRED,
    },
    include_package_data=True,
    license="Apache",
    classifiers=[
        # Trove classifiers
        # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
        "License :: OSI Approved :: Apache Software License",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.7",
    ],
    entry_points={"console_scripts": ["feast=feast.cli:cli"]},
    use_scm_version=use_scm_version,
    setup_requires=[
        "setuptools_scm",
        "grpcio>=1.47.0",
        "grpcio-tools>=1.47.0",
        "mypy-protobuf==3.1",
        "pybindgen==0.22.0",
    ],
    cmdclass={
        "build_python_protos": BuildPythonProtosCommand,
        "build_go_protos": BuildGoProtosCommand,
        "build_py": BuildCommand,
        "develop": DevelopCommand,
        "build_ext": build_ext,
    },
    ext_modules=[
        Extension(
            "feast.embedded_go.lib._embedded",
            ["github.com/feast-dev/feast/go/embedded"],
        )
    ],
)

setup.py
# Copyright 2019 The Feast Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import glob
import json
import os
import pathlib
import re
import shutil
import subprocess
import sys
from distutils.cmd import Command
from distutils.dir_util import copy_tree
from pathlib import Path
from subprocess import CalledProcessError

from setuptools import Extension, find_packages

try:
    from setuptools import setup
    from setuptools.command.build_ext import build_ext as _build_ext
    from setuptools.command.build_py import build_py
    from setuptools.command.develop import develop
    from setuptools.command.install import install

except ImportError:
    from distutils.command.build_ext import build_ext as _build_ext
    from distutils.command.build_py import build_py
    from distutils.core import setup

NAME = "feast"
DESCRIPTION = "Python SDK for Feast"
URL = "https://github.com/feast-dev/feast"
AUTHOR = "Feast"
REQUIRES_PYTHON = ">=3.8.0"

REQUIRED = [
    "click>=7.0.0,<9.0.0",
    "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",
    "Jinja2>=2,<4",
    "jsonschema",
    "mmh3",
    "numpy>=1.22,<3",
    "pandas>=1.4.3,<2",
    "pandavro~=1.5.0",  # For some reason pandavro higher than 1.5.* only support pandas less than 1.3.
    "protobuf<5,>3.20",
    "proto-plus>=1.20.0,<2",
    "pyarrow>=4,<9",
    "pydantic>=1,<2",
    "pygments>=2.12.0,<3",
    "PyYAML>=5.4.0,<7",
    "requests",
    "SQLAlchemy[mypy]>1,<2",
    "tabulate>=0.8.0,<1",
    "tenacity>=7,<9",
    "toml>=0.10.0,<1",
    "tqdm>=4,<5",
    "typeguard",
    "fastapi>=0.68.0,<1",
    "uvicorn[standard]>=0.14.0,<1",
    "dask>=2021.1.0",
    "bowler",  # Needed for automatic repo upgrades
    "httpx>=0.23.3",  # FastAPI does not correctly pull starlette dependency on httpx see thread(https://github.com/tiangolo/fastapi/issues/5656).
]

GCP_REQUIRED = [
    "google-api-core>=1.23.0,<3",
    "googleapis-common-protos>=1.52.0,<2",
    "google-cloud-bigquery[pandas]>=2,<4",
    "google-cloud-bigquery-storage >= 2.0.0,<3",
    "google-cloud-datastore>=2.1.0,<3",
    "google-cloud-storage>=1.34.0,<3",
    "google-cloud-bigtable>=2.11.0,<3",
]

REDIS_REQUIRED = [
    "redis==4.2.2",
    "hiredis>=2.0.0,<3",
]

AWS_REQUIRED = ["boto3>=1.17.0,<=1.20.23", "docker>=5.0.2", "s3fs>=0.4.0,<=2022.01.0"]

BYTEWAX_REQUIRED = ["bytewax==0.13.1", "docker>=5.0.2", "kubernetes<=20.13.0"]

SNOWFLAKE_REQUIRED = [
    "snowflake-connector-python[pandas]>=2.7.3,<3",
    # `pyOpenSSL==22.1.0` requires `cryptography<39,>=38.0.0`, which is incompatible
    # with `snowflake-connector-python[pandas]==2.8.0`, which depends on
    # `cryptography<37.0.0,>=3.1.0`.
    "pyOpenSSL<22.1.0",
]

SPARK_REQUIRED = [
    "pyspark>=3.0.0,<4",
]

TRINO_REQUIRED = [
    "trino>=0.305.0,<0.400.0", "regex"
]

POSTGRES_REQUIRED = [
    "psycopg2-binary>=2.8.3,<3",
]

MYSQL_REQUIRED = ["mysqlclient", "pymysql", "types-PyMySQL"]

HBASE_REQUIRED = [
    "happybase>=1.2.0,<3",
]

CASSANDRA_REQUIRED = [
    "cassandra-driver>=3.24.0,<4",
]

GE_REQUIRED = ["great_expectations>=0.15.41,<0.16.0"]

GO_REQUIRED = [
    "cffi~=1.15.0",
]

AZURE_REQUIRED = [
    "azure-storage-blob>=0.37.0",
    "azure-identity>=1.6.1",
    "SQLAlchemy>=1.4.19",
    "pyodbc>=4.0.30",
    "pymssql",
]

ROCKSET_REQUIRED = [
    "rockset>=1.0.3",
]

CI_REQUIRED = (
    [
        "build",
        "cryptography>=35.0,<36",
        "flake8",
        "black>=22.6.0,<23",
        "isort>=5,<6",
        "grpcio-tools>=1.47.0",
        "grpcio-testing>=1.47.0",
        "minio==7.1.0",
        "mock==2.0.0",
        "moto<4",
        "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",
        "psutil==5.9.0",
        "py>=1.11.0",  # https://github.com/pytest-dev/pytest/issues/10420
        "pytest>=6.0.0,<8",
        "pytest-cov",
        "pytest-xdist",
        "pytest-benchmark>=3.4.1,<4",
        "pytest-lazy-fixture==0.6.3",
        "pytest-timeout==1.4.2",
        "pytest-ordering~=0.6.0",
        "pytest-mock==1.10.4",
        "Sphinx>4.0.0,<7",
        "testcontainers>=3.5,<4",
        "adlfs==0.5.9",
        "firebase-admin>=5.2.0,<6",
        "pre-commit",
        "assertpy==1.1",
        "pip-tools",
        "pybindgen",
        "types-protobuf~=3.19.22",
        "types-python-dateutil",
        "types-pytz",
        "types-PyYAML",
        "types-redis",
        "types-requests",
        "types-setuptools",
        "types-tabulate",
    ]
    + GCP_REQUIRED
    + REDIS_REQUIRED
    + AWS_REQUIRED
    + BYTEWAX_REQUIRED
    + SNOWFLAKE_REQUIRED
    + SPARK_REQUIRED
    + POSTGRES_REQUIRED
    + MYSQL_REQUIRED
    + TRINO_REQUIRED
    + GE_REQUIRED
    + HBASE_REQUIRED
    + CASSANDRA_REQUIRED
    + AZURE_REQUIRED
    + ROCKSET_REQUIRED
)


# rtd builds fail because of mysql not being installed in their environment.
# We can add mysql there, but it's not strictly needed. This will be faster for builds.
DOCS_REQUIRED = CI_REQUIRED.copy()
for _r in MYSQL_REQUIRED:
    DOCS_REQUIRED.remove(_r)

DEV_REQUIRED = ["mypy-protobuf==3.1", "grpcio-testing~=1.0"] + CI_REQUIRED

# Get git repo root directory
repo_root = str(pathlib.Path(__file__).resolve().parent)

# README file from Feast repo root directory
README_FILE = os.path.join(repo_root, "README.md")
with open(README_FILE, "r", encoding="utf8") as f:
    LONG_DESCRIPTION = f.read()

# Add Support for parsing tags that have a prefix containing '/' (ie 'sdk/go') to setuptools_scm.
# Regex modified from default tag regex in:
# https://github.com/pypa/setuptools_scm/blob/2a1b46d38fb2b8aeac09853e660bcd0d7c1bc7be/src/setuptools_scm/config.py#L9
TAG_REGEX = re.compile(
    r"^(?:[\/\w-]+)?(?P<version>[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$"
)

# Only set use_scm_version if git executable exists (setting this variable causes pip to use git under the hood)
if shutil.which("git"):
    use_scm_version = {"root": ".", "relative_to": __file__, "tag_regex": TAG_REGEX}
else:
    use_scm_version = None

PROTO_SUBDIRS = ["core", "serving", "types", "storage"]
PYTHON_CODE_PREFIX = "sdk/python"


class BuildPythonProtosCommand(Command):
    description = "Builds the proto files into Python files."
    user_options = [
        ("inplace", "i", "Write generated proto files to source directory."),
    ]

    def initialize_options(self):
        self.python_protoc = [
            sys.executable,
            "-m",
            "grpc_tools.protoc",
        ]  # find_executable("protoc")
        self.proto_folder = os.path.join(repo_root, "protos")
        self.sub_folders = PROTO_SUBDIRS
        self.build_lib = None
        self.inplace = 0

    def finalize_options(self):
        self.set_undefined_options("build", ("build_lib", "build_lib"))

    @property
    def python_folder(self):
        if self.inplace:
            return os.path.join(
                os.path.dirname(__file__) or os.getcwd(), "sdk/python/feast/protos"
            )

        return os.path.join(self.build_lib, "feast/protos")

    def _generate_python_protos(self, path: str):
        proto_files = glob.glob(os.path.join(self.proto_folder, path))
        Path(self.python_folder).mkdir(parents=True, exist_ok=True)
        subprocess.check_call(
            self.python_protoc
            + [
                "-I",
                self.proto_folder,
                "--python_out",
                self.python_folder,
                "--grpc_python_out",
                self.python_folder,
                "--mypy_out",
                self.python_folder,
            ]
            + proto_files
        )

    def run(self):
        for sub_folder in self.sub_folders:
            self._generate_python_protos(f"feast/{sub_folder}/*.proto")
            # We need the __init__ files for each of the generated subdirs
            # so that they are regular packages, and don't need the `--namespace-packages` flags
            # when being typechecked using mypy.
            with open(f"{self.python_folder}/feast/{sub_folder}/__init__.py", "w"):
                pass

        with open(f"{self.python_folder}/__init__.py", "w"):
            pass
        with open(f"{self.python_folder}/feast/__init__.py", "w"):
            pass

        for path in Path(self.python_folder).rglob("*.py"):
            for folder in self.sub_folders:
                # Read in the file
                with open(path, "r") as file:
                    filedata = file.read()

                # Replace the target string
                filedata = filedata.replace(
                    f"from feast.{folder}", f"from feast.protos.feast.{folder}"
                )

                # Write the file out again
                with open(path, "w") as file:
                    file.write(filedata)


def _generate_path_with_gopath():
    go_path = subprocess.check_output(["go", "env", "GOPATH"]).decode("utf-8")
    go_path = go_path.strip()
    path_val = os.getenv("PATH")
    path_val = f"{path_val}:{go_path}/bin"

    return path_val


def _ensure_go_and_proto_toolchain():
    try:
        version = subprocess.check_output(["go", "version"])
    except Exception as e:
        raise RuntimeError("Unable to find go toolchain") from e

    semver_string = re.search(r"go[\S]+", str(version)).group().lstrip("go")
    parts = semver_string.split(".")
    if not (int(parts[0]) >= 1 and int(parts[1]) >= 16):
        raise RuntimeError(f"Go compiler too old; expected 1.16+ found {semver_string}")

    path_val = _generate_path_with_gopath()

    try:
        subprocess.check_call(["protoc-gen-go", "--version"], env={"PATH": path_val})
        subprocess.check_call(
            ["protoc-gen-go-grpc", "--version"], env={"PATH": path_val}
        )
    except Exception as e:
        raise RuntimeError("Unable to find go/grpc extensions for protoc") from e


class BuildGoProtosCommand(Command):
    description = "Builds the proto files into Go files."
    user_options = []

    def initialize_options(self):
        self.go_protoc = [
            sys.executable,
            "-m",
            "grpc_tools.protoc",
        ]  # find_executable("protoc")
        self.proto_folder = os.path.join(repo_root, "protos")
        self.go_folder = os.path.join(repo_root, "go/protos")
        self.sub_folders = PROTO_SUBDIRS
        self.path_val = _generate_path_with_gopath()

    def finalize_options(self):
        pass

    def _generate_go_protos(self, path: str):
        proto_files = glob.glob(os.path.join(self.proto_folder, path))

        try:
            subprocess.check_call(
                self.go_protoc
                + [
                    "-I",
                    self.proto_folder,
                    "--go_out",
                    self.go_folder,
                    "--go_opt=module=github.com/feast-dev/feast/go/protos",
                    "--go-grpc_out",
                    self.go_folder,
                    "--go-grpc_opt=module=github.com/feast-dev/feast/go/protos",
                ]
                + proto_files,
                env={"PATH": self.path_val},
            )
        except CalledProcessError as e:
            print(f"Stderr: {e.stderr}")
            print(f"Stdout: {e.stdout}")

    def run(self):
        go_dir = Path(repo_root) / "go" / "protos"
        go_dir.mkdir(exist_ok=True)
        for sub_folder in self.sub_folders:
            self._generate_go_protos(f"feast/{sub_folder}/*.proto")


class BuildCommand(build_py):
    """Custom build command."""

    def run(self):
        self.run_command("build_python_protos")
        if os.getenv("COMPILE_GO", "false").lower() == "true":
            _ensure_go_and_proto_toolchain()
            self.run_command("build_go_protos")

        self.run_command("build_ext")
        build_py.run(self)


class DevelopCommand(develop):
    """Custom develop command."""

    def run(self):
        self.reinitialize_command("build_python_protos", inplace=1)
        self.run_command("build_python_protos")
        if os.getenv("COMPILE_GO", "false").lower() == "true":
            _ensure_go_and_proto_toolchain()
            self.run_command("build_go_protos")

        develop.run(self)


class build_ext(_build_ext):
    def finalize_options(self) -> None:
        super().finalize_options()
        if os.getenv("COMPILE_GO", "false").lower() == "false":
            self.extensions = [e for e in self.extensions if not self._is_go_ext(e)]

    def _is_go_ext(self, ext: Extension):
        return any(
            source.endswith(".go") or source.startswith("github")
            for source in ext.sources
        )

    def build_extension(self, ext: Extension):
        print(f"Building extension {ext}")
        if not self._is_go_ext(ext):
            # the base class may mutate `self.compiler`
            compiler = copy.deepcopy(self.compiler)
            self.compiler, compiler = compiler, self.compiler
            try:
                return _build_ext.build_extension(self, ext)
            finally:
                self.compiler, compiler = compiler, self.compiler

        bin_path = _generate_path_with_gopath()
        go_env = json.loads(
            subprocess.check_output(["go", "env", "-json"]).decode("utf-8").strip()
        )

        print(f"Go env: {go_env}")
        print(f"CWD: {os.getcwd()}")

        destination = os.path.dirname(os.path.abspath(self.get_ext_fullpath(ext.name)))
        subprocess.check_call(
            ["go", "install", "golang.org/x/tools/cmd/goimports"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            ["go", "get", "github.com/go-python/gopy@v0.4.4"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            ["go", "install", "github.com/go-python/gopy"],
            env={"PATH": bin_path, **go_env},
        )
        subprocess.check_call(
            [
                "gopy",
                "build",
                "-output",
                destination,
                "-vm",
                sys.executable,
                "--build-tags",
                "cgo,ccalloc",
                "--dynamic-link=True",
                "-no-make",
                *ext.sources,
            ],
            env={
                "PATH": bin_path,
                "CGO_LDFLAGS_ALLOW": ".*",
                **go_env,
            },
        )

    def copy_extensions_to_source(self):
        build_py = self.get_finalized_command("build_py")
        for ext in self.extensions:
            fullname = self.get_ext_fullname(ext.name)
            modpath = fullname.split(".")
            package = ".".join(modpath[:-1])
            package_dir = build_py.get_package_dir(package)

            src_dir = dest_dir = package_dir

            if src_dir.startswith(PYTHON_CODE_PREFIX):
                src_dir = package_dir[len(PYTHON_CODE_PREFIX) :]
            src_dir = src_dir.lstrip("/")

            src_dir = os.path.join(self.build_lib, src_dir)

            # copy whole directory
            print(f"Copying from {src_dir} to {dest_dir}")
            copy_tree(src_dir, dest_dir)


setup(
    name=NAME,
    author=AUTHOR,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    long_description_content_type="text/markdown",
    python_requires=REQUIRES_PYTHON,
    url=URL,
    packages=find_packages(
        where=PYTHON_CODE_PREFIX, exclude=("java", "infra", "sdk/python/tests", "ui")
    ),
    package_dir={"": PYTHON_CODE_PREFIX},
    install_requires=REQUIRED,
    # https://stackoverflow.com/questions/28509965/setuptools-development-requirements
    # Install dev requirements with: pip install -e .[dev]
    extras_require={
        "dev": DEV_REQUIRED,
        "ci": CI_REQUIRED,
        "gcp": GCP_REQUIRED,
        "aws": AWS_REQUIRED,
        "bytewax": BYTEWAX_REQUIRED,
        "redis": REDIS_REQUIRED,
        "snowflake": SNOWFLAKE_REQUIRED,
        "spark": SPARK_REQUIRED,
        "trino": TRINO_REQUIRED,
        "postgres": POSTGRES_REQUIRED,
        "azure": AZURE_REQUIRED,
        "mysql": MYSQL_REQUIRED,
        "ge": GE_REQUIRED,
        "hbase": HBASE_REQUIRED,
        "go": GO_REQUIRED,
        "docs": DOCS_REQUIRED,
        "cassandra": CASSANDRA_REQUIRED,
    },
    include_package_data=True,
    license="Apache",
    classifiers=[
        # Trove classifiers
        # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
        "License :: OSI Approved :: Apache Software License",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.7",
    ],
    entry_points={"console_scripts": ["feast=feast.cli:cli"]},
    use_scm_version=use_scm_version,
    setup_requires=[
        "setuptools_scm",
        "grpcio>=1.47.0",
        "grpcio-tools>=1.47.0",
        "mypy-protobuf==3.1",
        "pybindgen==0.22.0",
    ],
    cmdclass={
        "build_python_protos": BuildPythonProtosCommand,
        "build_go_protos": BuildGoProtosCommand,
        "build_py": BuildCommand,
        "develop": DevelopCommand,
        "build_ext": build_ext,
    },
    ext_modules=[
        Extension(
            "feast.embedded_go.lib._embedded",
            ["github.com/feast-dev/feast/go/embedded"],
        )
    ],
)

setup.py
diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt
index 274a0dbc257..2ee998b85c7 100644
--- a/sdk/python/requirements/py3.10-ci-requirements.txt
+++ b/sdk/python/requirements/py3.10-ci-requirements.txt
@@ -24,15 +24,25 @@ aiosignal==1.3.1
     # via aiohttp
 alabaster==0.7.13
     # via sphinx
-altair==4.2.2
+altair==4.2.0
     # via great-expectations
 anyio==3.6.2
     # via
     #   httpcore
+    #   jupyter-server
     #   starlette
     #   watchfiles
 appdirs==1.4.4
     # via fissix
+argon2-cffi==21.3.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+argon2-cffi-bindings==21.2.0
+    # via argon2-cffi
+arrow==1.2.3
+    # via isoduration
 asn1crypto==1.5.1
     # via
     #   oscrypto
@@ -65,16 +75,20 @@ azure-identity==1.12.0
     # via
     #   adlfs
     #   feast (setup.py)
-azure-storage-blob==12.14.1
+azure-storage-blob==12.15.0
     # via
     #   adlfs
     #   feast (setup.py)
-babel==2.11.0
+babel==2.12.1
     # via sphinx
 backcall==0.2.0
     # via ipython
+beautifulsoup4==4.11.2
+    # via nbconvert
 black==22.12.0
     # via feast (setup.py)
+bleach==6.0.0
+    # via nbconvert
 boto3==1.20.23
     # via
     #   feast (setup.py)
@@ -110,6 +124,7 @@ certifi==2022.12.7
     #   snowflake-connector-python
 cffi==1.15.1
     # via
+    #   argon2-cffi-bindings
     #   azure-datalake-store
     #   cryptography
     #   snowflake-connector-python
@@ -137,7 +152,9 @@ colorama==0.4.6
     # via
     #   feast (setup.py)
     #   great-expectations
-coverage[toml]==7.1.0
+comm==0.1.2
+    # via ipykernel
+coverage[toml]==7.2.1
     # via pytest-cov
 cryptography==35.0.0
     # via
@@ -153,16 +170,18 @@ cryptography==35.0.0
     #   snowflake-connector-python
     #   types-pyopenssl
     #   types-redis
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
-dataclasses==0.6
-    # via great-expectations
 db-dtypes==1.0.5
     # via google-cloud-bigquery
+debugpy==1.6.6
+    # via ipykernel
 decorator==5.1.1
     # via
     #   gcsfs
     #   ipython
+defusedxml==0.7.1
+    # via nbconvert
 deprecated==1.2.13
     # via redis
 deprecation==2.1.0
@@ -190,11 +209,11 @@ executing==1.2.0
     # via stack-data
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
-fastjsonschema==2.16.2
+fastjsonschema==2.16.3
     # via nbformat
 filelock==3.9.0
     # via
@@ -206,6 +225,8 @@ fissix==21.11.13
     # via bowler
 flake8==6.0.0
     # via feast (setup.py)
+fqdn==1.5.1
+    # via jsonschema
 frozenlist==1.3.3
     # via
     #   aiohttp
@@ -234,9 +255,9 @@ google-api-core[grpc]==2.11.0
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-api-python-client==2.79.0
+google-api-python-client==2.80.0
     # via firebase-admin
-google-auth==2.16.1
+google-auth==2.16.2
     # via
     #   gcsfs
     #   google-api-core
@@ -250,11 +271,11 @@ google-auth-httplib2==0.1.0
     # via google-api-python-client
 google-auth-oauthlib==1.0.0
     # via gcsfs
-google-cloud-bigquery[pandas]==3.5.0
+google-cloud-bigquery[pandas]==3.6.0
     # via feast (setup.py)
-google-cloud-bigquery-storage==2.18.1
+google-cloud-bigquery-storage==2.19.0
     # via feast (setup.py)
-google-cloud-bigtable==2.15.0
+google-cloud-bigtable==2.17.0
     # via feast (setup.py)
 google-cloud-core==2.3.2
     # via
@@ -263,7 +284,7 @@ google-cloud-core==2.3.2
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-cloud-datastore==2.13.2
+google-cloud-datastore==2.14.0
     # via feast (setup.py)
 google-cloud-firestore==2.10.0
     # via firebase-admin
@@ -284,7 +305,7 @@ googleapis-common-protos[grpc]==1.58.0
     #   google-api-core
     #   grpc-google-iam-v1
     #   grpcio-status
-great-expectations==0.14.13
+great-expectations==0.15.50
     # via feast (setup.py)
 greenlet==2.0.2
     # via sqlalchemy
@@ -332,6 +353,7 @@ identify==2.5.18
 idna==3.4
     # via
     #   anyio
+    #   jsonschema
     #   requests
     #   rfc3986
     #   snowflake-connector-python
@@ -342,20 +364,42 @@ importlib-metadata==6.0.0
     # via great-expectations
 iniconfig==2.0.0
     # via pytest
-ipython==8.10.0
+ipykernel==6.21.2
+    # via
+    #   ipywidgets
+    #   nbclassic
+    #   notebook
+ipython==8.11.0
+    # via
+    #   great-expectations
+    #   ipykernel
+    #   ipywidgets
+ipython-genutils==0.2.0
+    # via
+    #   nbclassic
+    #   notebook
+ipywidgets==8.0.4
     # via great-expectations
 isodate==0.6.1
-    # via msrest
+    # via
+    #   azure-storage-blob
+    #   msrest
+isoduration==20.11.0
+    # via jsonschema
 isort==5.12.0
     # via feast (setup.py)
 jedi==0.18.2
     # via ipython
-jinja2==3.0.3
+jinja2==3.1.2
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-server
     #   moto
+    #   nbclassic
+    #   nbconvert
+    #   notebook
     #   sphinx
 jmespath==0.10.0
     # via
@@ -364,31 +408,70 @@ jmespath==0.10.0
 jsonpatch==1.32
     # via great-expectations
 jsonpointer==2.3
-    # via jsonpatch
-jsonschema==4.17.3
+    # via
+    #   jsonpatch
+    #   jsonschema
+jsonschema[format-nongpl]==4.17.3
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-events
     #   nbformat
+jupyter-client==8.0.3
+    # via
+    #   ipykernel
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   notebook
 jupyter-core==5.2.0
-    # via nbformat
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   nbformat
+    #   notebook
+jupyter-events==0.6.3
+    # via jupyter-server
+jupyter-server==2.3.0
+    # via
+    #   nbclassic
+    #   notebook-shim
+jupyter-server-terminals==0.4.4
+    # via jupyter-server
+jupyterlab-pygments==0.2.2
+    # via nbconvert
+jupyterlab-widgets==3.0.5
+    # via ipywidgets
 kubernetes==20.13.0
     # via feast (setup.py)
 locket==1.0.0
     # via partd
+makefun==1.15.1
+    # via great-expectations
 markupsafe==2.1.2
     # via
     #   jinja2
     #   moto
+    #   nbconvert
+marshmallow==3.19.0
+    # via great-expectations
 matplotlib-inline==0.1.6
-    # via ipython
+    # via
+    #   ipykernel
+    #   ipython
 mccabe==0.7.0
     # via flake8
 minio==7.1.0
     # via feast (setup.py)
 mistune==2.0.5
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   nbconvert
 mmh3==3.0.0
     # via feast (setup.py)
 mock==2.0.0
@@ -406,9 +489,7 @@ msal-extensions==1.0.0
 msgpack==1.0.4
     # via cachecontrol
 msrest==0.7.1
-    # via
-    #   azure-storage-blob
-    #   msrestazure
+    # via msrestazure
 msrestazure==0.6.4
     # via adlfs
 multidict==6.0.4
@@ -429,10 +510,34 @@ mypy-protobuf==3.1
     # via feast (setup.py)
 mysqlclient==2.1.1
     # via feast (setup.py)
+nbclassic==0.5.2
+    # via notebook
+nbclient==0.7.2
+    # via nbconvert
+nbconvert==7.2.9
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 nbformat==5.7.3
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   notebook
+nest-asyncio==1.5.6
+    # via
+    #   ipykernel
+    #   nbclassic
+    #   notebook
 nodeenv==1.7.0
     # via pre-commit
+notebook==6.5.2
+    # via great-expectations
+notebook-shim==0.2.2
+    # via nbclassic
 numpy==1.24.2
     # via
     #   altair
@@ -456,6 +561,10 @@ packaging==23.0
     #   docker
     #   google-cloud-bigquery
     #   great-expectations
+    #   ipykernel
+    #   jupyter-server
+    #   marshmallow
+    #   nbconvert
     #   pytest
     #   redis
     #   sphinx
@@ -470,6 +579,8 @@ pandas==1.5.3
     #   snowflake-connector-python
 pandavro==1.5.2
     # via feast (setup.py)
+pandocfilters==1.5.0
+    # via nbconvert
 parso==0.8.3
     # via jedi
 partd==1.3.0
@@ -482,7 +593,7 @@ pexpect==4.8.0
     # via ipython
 pickleshare==0.7.5
     # via ipython
-pip-tools==6.12.2
+pip-tools==6.12.3
     # via feast (setup.py)
 platformdirs==3.0.0
     # via
@@ -495,9 +606,14 @@ ply==3.11
     # via thriftpy2
 portalocker==2.7.0
     # via msal-extensions
-pre-commit==3.0.4
+pre-commit==3.1.1
     # via feast (setup.py)
-prompt-toolkit==3.0.37
+prometheus-client==0.16.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+prompt-toolkit==3.0.38
     # via ipython
 proto-plus==1.22.2
     # via
@@ -525,11 +641,15 @@ protobuf==4.22.0
     #   mypy-protobuf
     #   proto-plus
 psutil==5.9.0
-    # via feast (setup.py)
+    # via
+    #   feast (setup.py)
+    #   ipykernel
 psycopg2-binary==2.9.5
     # via feast (setup.py)
 ptyprocess==0.7.0
-    # via pexpect
+    # via
+    #   pexpect
+    #   terminado
 pure-eval==0.2.2
     # via stack-data
 py==1.11.0
@@ -562,12 +682,14 @@ pydantic==1.10.5
     # via
     #   fastapi
     #   feast (setup.py)
+    #   great-expectations
 pyflakes==3.0.1
     # via flake8
 pygments==2.14.0
     # via
     #   feast (setup.py)
     #   ipython
+    #   nbconvert
     #   sphinx
 pyjwt[crypto]==2.6.0
     # via
@@ -584,7 +706,7 @@ pyopenssl==22.0.0
     # via
     #   feast (setup.py)
     #   snowflake-connector-python
-pyparsing==2.4.7
+pyparsing==3.0.9
     # via
     #   great-expectations
     #   httplib2
@@ -621,18 +743,21 @@ pytest-xdist==3.2.0
 python-dateutil==2.8.2
     # via
     #   adal
+    #   arrow
     #   botocore
     #   google-cloud-bigquery
     #   great-expectations
+    #   jupyter-client
     #   kubernetes
     #   moto
     #   pandas
     #   rockset
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
+python-json-logger==2.0.7
+    # via jupyter-events
 pytz==2022.7.1
     # via
-    #   babel
     #   great-expectations
     #   moto
     #   pandas
@@ -644,9 +769,17 @@ pyyaml==6.0
     # via
     #   dask
     #   feast (setup.py)
+    #   jupyter-events
     #   kubernetes
     #   pre-commit
     #   uvicorn
+pyzmq==25.0.0
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 redis==4.2.2
     # via feast (setup.py)
 regex==2022.10.31
@@ -681,8 +814,16 @@ requests-oauthlib==1.3.1
     #   msrest
 responses==0.22.0
     # via moto
+rfc3339-validator==0.1.4
+    # via
+    #   jsonschema
+    #   jupyter-events
 rfc3986[idna2008]==1.5.0
     # via httpx
+rfc3986-validator==0.1.1
+    # via
+    #   jsonschema
+    #   jupyter-events
 rockset==1.0.5
     # via feast (setup.py)
 rsa==4.9
@@ -695,10 +836,17 @@ s3transfer==0.5.2
     # via boto3
 scipy==1.10.1
     # via great-expectations
+send2trash==1.8.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 six==1.16.0
     # via
+    #   asttokens
     #   azure-core
     #   azure-identity
+    #   bleach
     #   cassandra-driver
     #   geomet
     #   google-auth
@@ -710,6 +858,7 @@ six==1.16.0
     #   msrestazure
     #   pandavro
     #   python-dateutil
+    #   rfc3339-validator
     #   thriftpy2
 sniffio==1.3.0
     # via
@@ -720,6 +869,8 @@ snowballstemmer==2.2.0
     # via sphinx
 snowflake-connector-python[pandas]==2.9.0
     # via feast (setup.py)
+soupsieve==2.4
+    # via beautifulsoup4
 sphinx==6.1.3
     # via feast (setup.py)
 sphinxcontrib-applehelp==1.0.4
@@ -744,14 +895,20 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
-termcolor==2.2.0
-    # via great-expectations
+terminado==0.17.1
+    # via
+    #   jupyter-server
+    #   jupyter-server-terminals
+    #   nbclassic
+    #   notebook
 testcontainers==3.7.1
     # via feast (setup.py)
 thriftpy2==0.4.16
     # via happybase
+tinycss2==1.2.1
+    # via nbconvert
 toml==0.10.2
     # via
     #   feast (setup.py)
@@ -769,22 +926,38 @@ toolz==0.12.0
     #   altair
     #   dask
     #   partd
+tornado==6.2
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+    #   terminado
 tqdm==4.64.1
     # via
     #   feast (setup.py)
     #   great-expectations
 traitlets==5.9.0
     # via
+    #   comm
+    #   ipykernel
     #   ipython
+    #   ipywidgets
+    #   jupyter-client
     #   jupyter-core
+    #   jupyter-events
+    #   jupyter-server
     #   matplotlib-inline
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
     #   nbformat
+    #   notebook
 trino==0.321.0
     # via feast (setup.py)
 typeguard==2.13.3
     # via feast (setup.py)
-types-docutils==0.19.1.6
-    # via types-setuptools
 types-protobuf==3.19.22
     # via
     #   feast (setup.py)
@@ -793,27 +966,28 @@ types-pymysql==1.0.19.5
     # via feast (setup.py)
 types-pyopenssl==23.0.0.4
     # via types-redis
-types-python-dateutil==2.8.19.8
+types-python-dateutil==2.8.19.10
     # via feast (setup.py)
 types-pytz==2022.7.1.2
     # via feast (setup.py)
 types-pyyaml==6.0.12.8
     # via feast (setup.py)
-types-redis==4.5.1.3
+types-redis==4.5.1.4
     # via feast (setup.py)
-types-requests==2.28.11.14
+types-requests==2.28.11.15
     # via feast (setup.py)
-types-setuptools==67.4.0.1
+types-setuptools==67.4.0.3
     # via feast (setup.py)
 types-tabulate==0.9.0.1
     # via feast (setup.py)
 types-toml==0.10.8.5
     # via responses
-types-urllib3==1.26.25.7
+types-urllib3==1.26.25.8
     # via types-requests
 typing-extensions==4.5.0
     # via
     #   azure-core
+    #   azure-storage-blob
     #   great-expectations
     #   mypy
     #   pydantic
@@ -825,6 +999,8 @@ tzlocal==4.2
     # via
     #   great-expectations
     #   trino
+uri-template==1.2.0
+    # via jsonschema
 uritemplate==4.1.1
     # via google-api-python-client
 urllib3==1.26.14
@@ -843,7 +1019,7 @@ uvicorn[standard]==0.20.0
     # via feast (setup.py)
 uvloop==0.17.0
     # via uvicorn
-virtualenv==20.19.0
+virtualenv==20.20.0
     # via pre-commit
 volatile==2.1.0
     # via bowler
@@ -851,9 +1027,16 @@ watchfiles==0.18.1
     # via uvicorn
 wcwidth==0.2.6
     # via prompt-toolkit
+webcolors==1.12
+    # via jsonschema
+webencodings==0.5.1
+    # via
+    #   bleach
+    #   tinycss2
 websocket-client==1.5.1
     # via
     #   docker
+    #   jupyter-server
     #   kubernetes
 websockets==10.4
     # via uvicorn
@@ -861,7 +1044,9 @@ werkzeug==2.1.2
     # via moto
 wheel==0.38.4
     # via pip-tools
-wrapt==1.14.1
+widgetsnbextension==4.0.5
+    # via ipywidgets
+wrapt==1.15.0
     # via
     #   aiobotocore
     #   deprecated
@@ -870,7 +1055,7 @@ xmltodict==0.13.0
     # via moto
 yarl==1.8.2
     # via aiohttp
-zipp==3.14.0
+zipp==3.15.0
     # 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 451ffa32537..1efd6fc61ed 100644
--- a/sdk/python/requirements/py3.10-requirements.txt
+++ b/sdk/python/requirements/py3.10-requirements.txt
@@ -35,13 +35,13 @@ cloudpickle==2.2.1
     # via dask
 colorama==0.4.6
     # via feast (setup.py)
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
 dill==0.3.6
     # via feast (setup.py)
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
@@ -123,7 +123,7 @@ pyrsistent==0.19.3
     # via jsonschema
 python-dateutil==2.8.2
     # via pandas
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
 pytz==2022.7.1
     # via pandas
@@ -153,7 +153,7 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
 toml==0.10.2
     # via feast (setup.py)
diff --git a/sdk/python/requirements/py3.8-ci-requirements.txt b/sdk/python/requirements/py3.8-ci-requirements.txt
index 4e94a670235..1afec16b406 100644
--- a/sdk/python/requirements/py3.8-ci-requirements.txt
+++ b/sdk/python/requirements/py3.8-ci-requirements.txt
@@ -24,15 +24,25 @@ aiosignal==1.3.1
     # via aiohttp
 alabaster==0.7.13
     # via sphinx
-altair==4.2.2
+altair==4.2.0
     # via great-expectations
 anyio==3.6.2
     # via
     #   httpcore
+    #   jupyter-server
     #   starlette
     #   watchfiles
 appdirs==1.4.4
     # via fissix
+argon2-cffi==21.3.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+argon2-cffi-bindings==21.2.0
+    # via argon2-cffi
+arrow==1.2.3
+    # via isoduration
 asn1crypto==1.5.1
     # via
     #   oscrypto
@@ -65,11 +75,11 @@ azure-identity==1.12.0
     # via
     #   adlfs
     #   feast (setup.py)
-azure-storage-blob==12.14.1
+azure-storage-blob==12.15.0
     # via
     #   adlfs
     #   feast (setup.py)
-babel==2.11.0
+babel==2.12.1
     # via sphinx
 backcall==0.2.0
     # via ipython
@@ -77,8 +87,12 @@ backports-zoneinfo==0.2.1
     # via
     #   pytz-deprecation-shim
     #   tzlocal
+beautifulsoup4==4.11.2
+    # via nbconvert
 black==22.12.0
     # via feast (setup.py)
+bleach==6.0.0
+    # via nbconvert
 boto3==1.20.23
     # via
     #   feast (setup.py)
@@ -114,6 +128,7 @@ certifi==2022.12.7
     #   snowflake-connector-python
 cffi==1.15.1
     # via
+    #   argon2-cffi-bindings
     #   azure-datalake-store
     #   cryptography
     #   snowflake-connector-python
@@ -141,7 +156,9 @@ colorama==0.4.6
     # via
     #   feast (setup.py)
     #   great-expectations
-coverage[toml]==7.1.0
+comm==0.1.2
+    # via ipykernel
+coverage[toml]==7.2.1
     # via pytest-cov
 cryptography==35.0.0
     # via
@@ -157,16 +174,18 @@ cryptography==35.0.0
     #   snowflake-connector-python
     #   types-pyopenssl
     #   types-redis
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
-dataclasses==0.6
-    # via great-expectations
 db-dtypes==1.0.5
     # via google-cloud-bigquery
+debugpy==1.6.6
+    # via ipykernel
 decorator==5.1.1
     # via
     #   gcsfs
     #   ipython
+defusedxml==0.7.1
+    # via nbconvert
 deprecated==1.2.13
     # via redis
 deprecation==2.1.0
@@ -194,11 +213,11 @@ executing==1.2.0
     # via stack-data
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
-fastjsonschema==2.16.2
+fastjsonschema==2.16.3
     # via nbformat
 filelock==3.9.0
     # via
@@ -210,6 +229,8 @@ fissix==21.11.13
     # via bowler
 flake8==6.0.0
     # via feast (setup.py)
+fqdn==1.5.1
+    # via jsonschema
 frozenlist==1.3.3
     # via
     #   aiohttp
@@ -238,9 +259,9 @@ google-api-core[grpc]==2.11.0
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-api-python-client==2.79.0
+google-api-python-client==2.80.0
     # via firebase-admin
-google-auth==2.16.1
+google-auth==2.16.2
     # via
     #   gcsfs
     #   google-api-core
@@ -254,11 +275,11 @@ google-auth-httplib2==0.1.0
     # via google-api-python-client
 google-auth-oauthlib==1.0.0
     # via gcsfs
-google-cloud-bigquery[pandas]==3.5.0
+google-cloud-bigquery[pandas]==3.6.0
     # via feast (setup.py)
-google-cloud-bigquery-storage==2.18.1
+google-cloud-bigquery-storage==2.19.0
     # via feast (setup.py)
-google-cloud-bigtable==2.15.0
+google-cloud-bigtable==2.17.0
     # via feast (setup.py)
 google-cloud-core==2.3.2
     # via
@@ -267,7 +288,7 @@ google-cloud-core==2.3.2
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-cloud-datastore==2.13.2
+google-cloud-datastore==2.14.0
     # via feast (setup.py)
 google-cloud-firestore==2.10.0
     # via firebase-admin
@@ -288,7 +309,7 @@ googleapis-common-protos[grpc]==1.58.0
     #   google-api-core
     #   grpc-google-iam-v1
     #   grpcio-status
-great-expectations==0.14.13
+great-expectations==0.15.50
     # via feast (setup.py)
 greenlet==2.0.2
     # via sqlalchemy
@@ -336,6 +357,7 @@ identify==2.5.18
 idna==3.4
     # via
     #   anyio
+    #   jsonschema
     #   requests
     #   rfc3986
     #   snowflake-connector-python
@@ -345,25 +367,49 @@ imagesize==1.4.1
 importlib-metadata==6.0.0
     # via
     #   great-expectations
+    #   jupyter-client
+    #   nbconvert
     #   sphinx
 importlib-resources==5.12.0
     # via jsonschema
 iniconfig==2.0.0
     # via pytest
-ipython==8.10.0
+ipykernel==6.21.2
+    # via
+    #   ipywidgets
+    #   nbclassic
+    #   notebook
+ipython==8.11.0
+    # via
+    #   great-expectations
+    #   ipykernel
+    #   ipywidgets
+ipython-genutils==0.2.0
+    # via
+    #   nbclassic
+    #   notebook
+ipywidgets==8.0.4
     # via great-expectations
 isodate==0.6.1
-    # via msrest
+    # via
+    #   azure-storage-blob
+    #   msrest
+isoduration==20.11.0
+    # via jsonschema
 isort==5.12.0
     # via feast (setup.py)
 jedi==0.18.2
     # via ipython
-jinja2==3.0.3
+jinja2==3.1.2
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-server
     #   moto
+    #   nbclassic
+    #   nbconvert
+    #   notebook
     #   sphinx
 jmespath==0.10.0
     # via
@@ -372,31 +418,70 @@ jmespath==0.10.0
 jsonpatch==1.32
     # via great-expectations
 jsonpointer==2.3
-    # via jsonpatch
-jsonschema==4.17.3
+    # via
+    #   jsonpatch
+    #   jsonschema
+jsonschema[format-nongpl]==4.17.3
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-events
     #   nbformat
+jupyter-client==8.0.3
+    # via
+    #   ipykernel
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   notebook
 jupyter-core==5.2.0
-    # via nbformat
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   nbformat
+    #   notebook
+jupyter-events==0.6.3
+    # via jupyter-server
+jupyter-server==2.3.0
+    # via
+    #   nbclassic
+    #   notebook-shim
+jupyter-server-terminals==0.4.4
+    # via jupyter-server
+jupyterlab-pygments==0.2.2
+    # via nbconvert
+jupyterlab-widgets==3.0.5
+    # via ipywidgets
 kubernetes==20.13.0
     # via feast (setup.py)
 locket==1.0.0
     # via partd
+makefun==1.15.1
+    # via great-expectations
 markupsafe==2.1.2
     # via
     #   jinja2
     #   moto
+    #   nbconvert
+marshmallow==3.19.0
+    # via great-expectations
 matplotlib-inline==0.1.6
-    # via ipython
+    # via
+    #   ipykernel
+    #   ipython
 mccabe==0.7.0
     # via flake8
 minio==7.1.0
     # via feast (setup.py)
 mistune==2.0.5
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   nbconvert
 mmh3==3.0.0
     # via feast (setup.py)
 mock==2.0.0
@@ -414,9 +499,7 @@ msal-extensions==1.0.0
 msgpack==1.0.4
     # via cachecontrol
 msrest==0.7.1
-    # via
-    #   azure-storage-blob
-    #   msrestazure
+    # via msrestazure
 msrestazure==0.6.4
     # via adlfs
 multidict==6.0.4
@@ -437,10 +520,34 @@ mypy-protobuf==3.1
     # via feast (setup.py)
 mysqlclient==2.1.1
     # via feast (setup.py)
+nbclassic==0.5.2
+    # via notebook
+nbclient==0.7.2
+    # via nbconvert
+nbconvert==7.2.9
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 nbformat==5.7.3
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   notebook
+nest-asyncio==1.5.6
+    # via
+    #   ipykernel
+    #   nbclassic
+    #   notebook
 nodeenv==1.7.0
     # via pre-commit
+notebook==6.5.2
+    # via great-expectations
+notebook-shim==0.2.2
+    # via nbclassic
 numpy==1.24.2
     # via
     #   altair
@@ -464,6 +571,10 @@ packaging==23.0
     #   docker
     #   google-cloud-bigquery
     #   great-expectations
+    #   ipykernel
+    #   jupyter-server
+    #   marshmallow
+    #   nbconvert
     #   pytest
     #   redis
     #   sphinx
@@ -478,6 +589,8 @@ pandas==1.5.3
     #   snowflake-connector-python
 pandavro==1.5.2
     # via feast (setup.py)
+pandocfilters==1.5.0
+    # via nbconvert
 parso==0.8.3
     # via jedi
 partd==1.3.0
@@ -490,7 +603,7 @@ pexpect==4.8.0
     # via ipython
 pickleshare==0.7.5
     # via ipython
-pip-tools==6.12.2
+pip-tools==6.12.3
     # via feast (setup.py)
 pkgutil-resolve-name==1.3.10
     # via jsonschema
@@ -505,9 +618,14 @@ ply==3.11
     # via thriftpy2
 portalocker==2.7.0
     # via msal-extensions
-pre-commit==3.0.4
+pre-commit==3.1.1
     # via feast (setup.py)
-prompt-toolkit==3.0.37
+prometheus-client==0.16.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+prompt-toolkit==3.0.38
     # via ipython
 proto-plus==1.22.2
     # via
@@ -535,11 +653,15 @@ protobuf==4.22.0
     #   mypy-protobuf
     #   proto-plus
 psutil==5.9.0
-    # via feast (setup.py)
+    # via
+    #   feast (setup.py)
+    #   ipykernel
 psycopg2-binary==2.9.5
     # via feast (setup.py)
 ptyprocess==0.7.0
-    # via pexpect
+    # via
+    #   pexpect
+    #   terminado
 pure-eval==0.2.2
     # via stack-data
 py==1.11.0
@@ -572,12 +694,14 @@ pydantic==1.10.5
     # via
     #   fastapi
     #   feast (setup.py)
+    #   great-expectations
 pyflakes==3.0.1
     # via flake8
 pygments==2.14.0
     # via
     #   feast (setup.py)
     #   ipython
+    #   nbconvert
     #   sphinx
 pyjwt[crypto]==2.6.0
     # via
@@ -594,7 +718,7 @@ pyopenssl==22.0.0
     # via
     #   feast (setup.py)
     #   snowflake-connector-python
-pyparsing==2.4.7
+pyparsing==3.0.9
     # via
     #   great-expectations
     #   httplib2
@@ -631,15 +755,19 @@ pytest-xdist==3.2.0
 python-dateutil==2.8.2
     # via
     #   adal
+    #   arrow
     #   botocore
     #   google-cloud-bigquery
     #   great-expectations
+    #   jupyter-client
     #   kubernetes
     #   moto
     #   pandas
     #   rockset
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
+python-json-logger==2.0.7
+    # via jupyter-events
 pytz==2022.7.1
     # via
     #   babel
@@ -654,9 +782,17 @@ pyyaml==6.0
     # via
     #   dask
     #   feast (setup.py)
+    #   jupyter-events
     #   kubernetes
     #   pre-commit
     #   uvicorn
+pyzmq==25.0.0
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 redis==4.2.2
     # via feast (setup.py)
 regex==2022.10.31
@@ -691,8 +827,16 @@ requests-oauthlib==1.3.1
     #   msrest
 responses==0.22.0
     # via moto
+rfc3339-validator==0.1.4
+    # via
+    #   jsonschema
+    #   jupyter-events
 rfc3986[idna2008]==1.5.0
     # via httpx
+rfc3986-validator==0.1.1
+    # via
+    #   jsonschema
+    #   jupyter-events
 rockset==1.0.5
     # via feast (setup.py)
 rsa==4.9
@@ -707,10 +851,17 @@ s3transfer==0.5.2
     # via boto3
 scipy==1.10.1
     # via great-expectations
+send2trash==1.8.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 six==1.16.0
     # via
+    #   asttokens
     #   azure-core
     #   azure-identity
+    #   bleach
     #   cassandra-driver
     #   geomet
     #   google-auth
@@ -722,6 +873,7 @@ six==1.16.0
     #   msrestazure
     #   pandavro
     #   python-dateutil
+    #   rfc3339-validator
     #   thriftpy2
 sniffio==1.3.0
     # via
@@ -732,6 +884,8 @@ snowballstemmer==2.2.0
     # via sphinx
 snowflake-connector-python[pandas]==2.9.0
     # via feast (setup.py)
+soupsieve==2.4
+    # via beautifulsoup4
 sphinx==6.1.3
     # via feast (setup.py)
 sphinxcontrib-applehelp==1.0.4
@@ -756,14 +910,20 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
-termcolor==2.2.0
-    # via great-expectations
+terminado==0.17.1
+    # via
+    #   jupyter-server
+    #   jupyter-server-terminals
+    #   nbclassic
+    #   notebook
 testcontainers==3.7.1
     # via feast (setup.py)
 thriftpy2==0.4.16
     # via happybase
+tinycss2==1.2.1
+    # via nbconvert
 toml==0.10.2
     # via
     #   feast (setup.py)
@@ -781,22 +941,38 @@ toolz==0.12.0
     #   altair
     #   dask
     #   partd
+tornado==6.2
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+    #   terminado
 tqdm==4.64.1
     # via
     #   feast (setup.py)
     #   great-expectations
 traitlets==5.9.0
     # via
+    #   comm
+    #   ipykernel
     #   ipython
+    #   ipywidgets
+    #   jupyter-client
     #   jupyter-core
+    #   jupyter-events
+    #   jupyter-server
     #   matplotlib-inline
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
     #   nbformat
+    #   notebook
 trino==0.321.0
     # via feast (setup.py)
 typeguard==2.13.3
     # via feast (setup.py)
-types-docutils==0.19.1.6
-    # via types-setuptools
 types-protobuf==3.19.22
     # via
     #   feast (setup.py)
@@ -805,28 +981,29 @@ types-pymysql==1.0.19.5
     # via feast (setup.py)
 types-pyopenssl==23.0.0.4
     # via types-redis
-types-python-dateutil==2.8.19.8
+types-python-dateutil==2.8.19.10
     # via feast (setup.py)
 types-pytz==2022.7.1.2
     # via feast (setup.py)
 types-pyyaml==6.0.12.8
     # via feast (setup.py)
-types-redis==4.5.1.3
+types-redis==4.5.1.4
     # via feast (setup.py)
-types-requests==2.28.11.14
+types-requests==2.28.11.15
     # via feast (setup.py)
-types-setuptools==67.4.0.1
+types-setuptools==67.4.0.3
     # via feast (setup.py)
 types-tabulate==0.9.0.1
     # via feast (setup.py)
 types-toml==0.10.8.5
     # via responses
-types-urllib3==1.26.25.7
+types-urllib3==1.26.25.8
     # via types-requests
 typing-extensions==4.5.0
     # via
     #   aioitertools
     #   azure-core
+    #   azure-storage-blob
     #   black
     #   great-expectations
     #   mypy
@@ -840,6 +1017,8 @@ tzlocal==4.2
     # via
     #   great-expectations
     #   trino
+uri-template==1.2.0
+    # via jsonschema
 uritemplate==4.1.1
     # via google-api-python-client
 urllib3==1.26.14
@@ -858,7 +1037,7 @@ uvicorn[standard]==0.20.0
     # via feast (setup.py)
 uvloop==0.17.0
     # via uvicorn
-virtualenv==20.19.0
+virtualenv==20.20.0
     # via pre-commit
 volatile==2.1.0
     # via bowler
@@ -866,9 +1045,16 @@ watchfiles==0.18.1
     # via uvicorn
 wcwidth==0.2.6
     # via prompt-toolkit
+webcolors==1.12
+    # via jsonschema
+webencodings==0.5.1
+    # via
+    #   bleach
+    #   tinycss2
 websocket-client==1.5.1
     # via
     #   docker
+    #   jupyter-server
     #   kubernetes
 websockets==10.4
     # via uvicorn
@@ -876,7 +1062,9 @@ werkzeug==2.1.2
     # via moto
 wheel==0.38.4
     # via pip-tools
-wrapt==1.14.1
+widgetsnbextension==4.0.5
+    # via ipywidgets
+wrapt==1.15.0
     # via
     #   aiobotocore
     #   deprecated
@@ -885,7 +1073,7 @@ xmltodict==0.13.0
     # via moto
 yarl==1.8.2
     # via aiohttp
-zipp==3.14.0
+zipp==3.15.0
     # via
     #   importlib-metadata
     #   importlib-resources
diff --git a/sdk/python/requirements/py3.8-requirements.txt b/sdk/python/requirements/py3.8-requirements.txt
index 0429a8313ce..c57baa0d27a 100644
--- a/sdk/python/requirements/py3.8-requirements.txt
+++ b/sdk/python/requirements/py3.8-requirements.txt
@@ -35,13 +35,13 @@ cloudpickle==2.2.1
     # via dask
 colorama==0.4.6
     # via feast (setup.py)
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
 dill==0.3.6
     # via feast (setup.py)
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
@@ -127,7 +127,7 @@ pyrsistent==0.19.3
     # via jsonschema
 python-dateutil==2.8.2
     # via pandas
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
 pytz==2022.7.1
     # via pandas
@@ -157,7 +157,7 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
 toml==0.10.2
     # via feast (setup.py)
@@ -189,5 +189,5 @@ watchfiles==0.18.1
     # via uvicorn
 websockets==10.4
     # via uvicorn
-zipp==3.14.0
+zipp==3.15.0
     # via importlib-resources
diff --git a/sdk/python/requirements/py3.9-ci-requirements.txt b/sdk/python/requirements/py3.9-ci-requirements.txt
index df472dd6b07..4e0ab11d366 100644
--- a/sdk/python/requirements/py3.9-ci-requirements.txt
+++ b/sdk/python/requirements/py3.9-ci-requirements.txt
@@ -24,15 +24,25 @@ aiosignal==1.3.1
     # via aiohttp
 alabaster==0.7.13
     # via sphinx
-altair==4.2.2
+altair==4.2.0
     # via great-expectations
 anyio==3.6.2
     # via
     #   httpcore
+    #   jupyter-server
     #   starlette
     #   watchfiles
 appdirs==1.4.4
     # via fissix
+argon2-cffi==21.3.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+argon2-cffi-bindings==21.2.0
+    # via argon2-cffi
+arrow==1.2.3
+    # via isoduration
 asn1crypto==1.5.1
     # via
     #   oscrypto
@@ -65,16 +75,20 @@ azure-identity==1.12.0
     # via
     #   adlfs
     #   feast (setup.py)
-azure-storage-blob==12.14.1
+azure-storage-blob==12.15.0
     # via
     #   adlfs
     #   feast (setup.py)
-babel==2.11.0
+babel==2.12.1
     # via sphinx
 backcall==0.2.0
     # via ipython
+beautifulsoup4==4.11.2
+    # via nbconvert
 black==22.12.0
     # via feast (setup.py)
+bleach==6.0.0
+    # via nbconvert
 boto3==1.20.23
     # via
     #   feast (setup.py)
@@ -110,6 +124,7 @@ certifi==2022.12.7
     #   snowflake-connector-python
 cffi==1.15.1
     # via
+    #   argon2-cffi-bindings
     #   azure-datalake-store
     #   cryptography
     #   snowflake-connector-python
@@ -137,7 +152,9 @@ colorama==0.4.6
     # via
     #   feast (setup.py)
     #   great-expectations
-coverage[toml]==7.1.0
+comm==0.1.2
+    # via ipykernel
+coverage[toml]==7.2.1
     # via pytest-cov
 cryptography==35.0.0
     # via
@@ -153,16 +170,18 @@ cryptography==35.0.0
     #   snowflake-connector-python
     #   types-pyopenssl
     #   types-redis
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
-dataclasses==0.6
-    # via great-expectations
 db-dtypes==1.0.5
     # via google-cloud-bigquery
+debugpy==1.6.6
+    # via ipykernel
 decorator==5.1.1
     # via
     #   gcsfs
     #   ipython
+defusedxml==0.7.1
+    # via nbconvert
 deprecated==1.2.13
     # via redis
 deprecation==2.1.0
@@ -190,11 +209,11 @@ executing==1.2.0
     # via stack-data
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
-fastjsonschema==2.16.2
+fastjsonschema==2.16.3
     # via nbformat
 filelock==3.9.0
     # via
@@ -206,6 +225,8 @@ fissix==21.11.13
     # via bowler
 flake8==6.0.0
     # via feast (setup.py)
+fqdn==1.5.1
+    # via jsonschema
 frozenlist==1.3.3
     # via
     #   aiohttp
@@ -234,9 +255,9 @@ google-api-core[grpc]==2.11.0
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-api-python-client==2.79.0
+google-api-python-client==2.80.0
     # via firebase-admin
-google-auth==2.16.1
+google-auth==2.16.2
     # via
     #   gcsfs
     #   google-api-core
@@ -250,11 +271,11 @@ google-auth-httplib2==0.1.0
     # via google-api-python-client
 google-auth-oauthlib==1.0.0
     # via gcsfs
-google-cloud-bigquery[pandas]==3.5.0
+google-cloud-bigquery[pandas]==3.6.0
     # via feast (setup.py)
-google-cloud-bigquery-storage==2.18.1
+google-cloud-bigquery-storage==2.19.0
     # via feast (setup.py)
-google-cloud-bigtable==2.15.0
+google-cloud-bigtable==2.17.0
     # via feast (setup.py)
 google-cloud-core==2.3.2
     # via
@@ -263,7 +284,7 @@ google-cloud-core==2.3.2
     #   google-cloud-datastore
     #   google-cloud-firestore
     #   google-cloud-storage
-google-cloud-datastore==2.13.2
+google-cloud-datastore==2.14.0
     # via feast (setup.py)
 google-cloud-firestore==2.10.0
     # via firebase-admin
@@ -284,7 +305,7 @@ googleapis-common-protos[grpc]==1.58.0
     #   google-api-core
     #   grpc-google-iam-v1
     #   grpcio-status
-great-expectations==0.14.13
+great-expectations==0.15.50
     # via feast (setup.py)
 greenlet==2.0.2
     # via sqlalchemy
@@ -332,6 +353,7 @@ identify==2.5.18
 idna==3.4
     # via
     #   anyio
+    #   jsonschema
     #   requests
     #   rfc3986
     #   snowflake-connector-python
@@ -341,23 +363,47 @@ imagesize==1.4.1
 importlib-metadata==6.0.0
     # via
     #   great-expectations
+    #   jupyter-client
+    #   nbconvert
     #   sphinx
 iniconfig==2.0.0
     # via pytest
-ipython==8.10.0
+ipykernel==6.21.2
+    # via
+    #   ipywidgets
+    #   nbclassic
+    #   notebook
+ipython==8.11.0
+    # via
+    #   great-expectations
+    #   ipykernel
+    #   ipywidgets
+ipython-genutils==0.2.0
+    # via
+    #   nbclassic
+    #   notebook
+ipywidgets==8.0.4
     # via great-expectations
 isodate==0.6.1
-    # via msrest
+    # via
+    #   azure-storage-blob
+    #   msrest
+isoduration==20.11.0
+    # via jsonschema
 isort==5.12.0
     # via feast (setup.py)
 jedi==0.18.2
     # via ipython
-jinja2==3.0.3
+jinja2==3.1.2
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-server
     #   moto
+    #   nbclassic
+    #   nbconvert
+    #   notebook
     #   sphinx
 jmespath==0.10.0
     # via
@@ -366,31 +412,70 @@ jmespath==0.10.0
 jsonpatch==1.32
     # via great-expectations
 jsonpointer==2.3
-    # via jsonpatch
-jsonschema==4.17.3
+    # via
+    #   jsonpatch
+    #   jsonschema
+jsonschema[format-nongpl]==4.17.3
     # via
     #   altair
     #   feast (setup.py)
     #   great-expectations
+    #   jupyter-events
     #   nbformat
+jupyter-client==8.0.3
+    # via
+    #   ipykernel
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   notebook
 jupyter-core==5.2.0
-    # via nbformat
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   nbformat
+    #   notebook
+jupyter-events==0.6.3
+    # via jupyter-server
+jupyter-server==2.3.0
+    # via
+    #   nbclassic
+    #   notebook-shim
+jupyter-server-terminals==0.4.4
+    # via jupyter-server
+jupyterlab-pygments==0.2.2
+    # via nbconvert
+jupyterlab-widgets==3.0.5
+    # via ipywidgets
 kubernetes==20.13.0
     # via feast (setup.py)
 locket==1.0.0
     # via partd
+makefun==1.15.1
+    # via great-expectations
 markupsafe==2.1.2
     # via
     #   jinja2
     #   moto
+    #   nbconvert
+marshmallow==3.19.0
+    # via great-expectations
 matplotlib-inline==0.1.6
-    # via ipython
+    # via
+    #   ipykernel
+    #   ipython
 mccabe==0.7.0
     # via flake8
 minio==7.1.0
     # via feast (setup.py)
 mistune==2.0.5
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   nbconvert
 mmh3==3.0.0
     # via feast (setup.py)
 mock==2.0.0
@@ -408,9 +493,7 @@ msal-extensions==1.0.0
 msgpack==1.0.4
     # via cachecontrol
 msrest==0.7.1
-    # via
-    #   azure-storage-blob
-    #   msrestazure
+    # via msrestazure
 msrestazure==0.6.4
     # via adlfs
 multidict==6.0.4
@@ -431,10 +514,34 @@ mypy-protobuf==3.1
     # via feast (setup.py)
 mysqlclient==2.1.1
     # via feast (setup.py)
+nbclassic==0.5.2
+    # via notebook
+nbclient==0.7.2
+    # via nbconvert
+nbconvert==7.2.9
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 nbformat==5.7.3
-    # via great-expectations
+    # via
+    #   great-expectations
+    #   jupyter-server
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
+    #   notebook
+nest-asyncio==1.5.6
+    # via
+    #   ipykernel
+    #   nbclassic
+    #   notebook
 nodeenv==1.7.0
     # via pre-commit
+notebook==6.5.2
+    # via great-expectations
+notebook-shim==0.2.2
+    # via nbclassic
 numpy==1.24.2
     # via
     #   altair
@@ -458,6 +565,10 @@ packaging==23.0
     #   docker
     #   google-cloud-bigquery
     #   great-expectations
+    #   ipykernel
+    #   jupyter-server
+    #   marshmallow
+    #   nbconvert
     #   pytest
     #   redis
     #   sphinx
@@ -472,6 +583,8 @@ pandas==1.5.3
     #   snowflake-connector-python
 pandavro==1.5.2
     # via feast (setup.py)
+pandocfilters==1.5.0
+    # via nbconvert
 parso==0.8.3
     # via jedi
 partd==1.3.0
@@ -484,7 +597,7 @@ pexpect==4.8.0
     # via ipython
 pickleshare==0.7.5
     # via ipython
-pip-tools==6.12.2
+pip-tools==6.12.3
     # via feast (setup.py)
 platformdirs==3.0.0
     # via
@@ -497,9 +610,14 @@ ply==3.11
     # via thriftpy2
 portalocker==2.7.0
     # via msal-extensions
-pre-commit==3.0.4
+pre-commit==3.1.1
     # via feast (setup.py)
-prompt-toolkit==3.0.37
+prometheus-client==0.16.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+prompt-toolkit==3.0.38
     # via ipython
 proto-plus==1.22.2
     # via
@@ -527,11 +645,15 @@ protobuf==4.22.0
     #   mypy-protobuf
     #   proto-plus
 psutil==5.9.0
-    # via feast (setup.py)
+    # via
+    #   feast (setup.py)
+    #   ipykernel
 psycopg2-binary==2.9.5
     # via feast (setup.py)
 ptyprocess==0.7.0
-    # via pexpect
+    # via
+    #   pexpect
+    #   terminado
 pure-eval==0.2.2
     # via stack-data
 py==1.11.0
@@ -564,12 +686,14 @@ pydantic==1.10.5
     # via
     #   fastapi
     #   feast (setup.py)
+    #   great-expectations
 pyflakes==3.0.1
     # via flake8
 pygments==2.14.0
     # via
     #   feast (setup.py)
     #   ipython
+    #   nbconvert
     #   sphinx
 pyjwt[crypto]==2.6.0
     # via
@@ -586,7 +710,7 @@ pyopenssl==22.0.0
     # via
     #   feast (setup.py)
     #   snowflake-connector-python
-pyparsing==2.4.7
+pyparsing==3.0.9
     # via
     #   great-expectations
     #   httplib2
@@ -623,18 +747,21 @@ pytest-xdist==3.2.0
 python-dateutil==2.8.2
     # via
     #   adal
+    #   arrow
     #   botocore
     #   google-cloud-bigquery
     #   great-expectations
+    #   jupyter-client
     #   kubernetes
     #   moto
     #   pandas
     #   rockset
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
+python-json-logger==2.0.7
+    # via jupyter-events
 pytz==2022.7.1
     # via
-    #   babel
     #   great-expectations
     #   moto
     #   pandas
@@ -646,9 +773,17 @@ pyyaml==6.0
     # via
     #   dask
     #   feast (setup.py)
+    #   jupyter-events
     #   kubernetes
     #   pre-commit
     #   uvicorn
+pyzmq==25.0.0
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 redis==4.2.2
     # via feast (setup.py)
 regex==2022.10.31
@@ -683,8 +818,16 @@ requests-oauthlib==1.3.1
     #   msrest
 responses==0.22.0
     # via moto
+rfc3339-validator==0.1.4
+    # via
+    #   jsonschema
+    #   jupyter-events
 rfc3986[idna2008]==1.5.0
     # via httpx
+rfc3986-validator==0.1.1
+    # via
+    #   jsonschema
+    #   jupyter-events
 rockset==1.0.5
     # via feast (setup.py)
 rsa==4.9
@@ -699,10 +842,17 @@ s3transfer==0.5.2
     # via boto3
 scipy==1.10.1
     # via great-expectations
+send2trash==1.8.0
+    # via
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
 six==1.16.0
     # via
+    #   asttokens
     #   azure-core
     #   azure-identity
+    #   bleach
     #   cassandra-driver
     #   geomet
     #   google-auth
@@ -714,6 +864,7 @@ six==1.16.0
     #   msrestazure
     #   pandavro
     #   python-dateutil
+    #   rfc3339-validator
     #   thriftpy2
 sniffio==1.3.0
     # via
@@ -724,6 +875,8 @@ snowballstemmer==2.2.0
     # via sphinx
 snowflake-connector-python[pandas]==2.9.0
     # via feast (setup.py)
+soupsieve==2.4
+    # via beautifulsoup4
 sphinx==6.1.3
     # via feast (setup.py)
 sphinxcontrib-applehelp==1.0.4
@@ -748,14 +901,20 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
-termcolor==2.2.0
-    # via great-expectations
+terminado==0.17.1
+    # via
+    #   jupyter-server
+    #   jupyter-server-terminals
+    #   nbclassic
+    #   notebook
 testcontainers==3.7.1
     # via feast (setup.py)
 thriftpy2==0.4.16
     # via happybase
+tinycss2==1.2.1
+    # via nbconvert
 toml==0.10.2
     # via
     #   feast (setup.py)
@@ -773,22 +932,38 @@ toolz==0.12.0
     #   altair
     #   dask
     #   partd
+tornado==6.2
+    # via
+    #   ipykernel
+    #   jupyter-client
+    #   jupyter-server
+    #   nbclassic
+    #   notebook
+    #   terminado
 tqdm==4.64.1
     # via
     #   feast (setup.py)
     #   great-expectations
 traitlets==5.9.0
     # via
+    #   comm
+    #   ipykernel
     #   ipython
+    #   ipywidgets
+    #   jupyter-client
     #   jupyter-core
+    #   jupyter-events
+    #   jupyter-server
     #   matplotlib-inline
+    #   nbclassic
+    #   nbclient
+    #   nbconvert
     #   nbformat
+    #   notebook
 trino==0.321.0
     # via feast (setup.py)
 typeguard==2.13.3
     # via feast (setup.py)
-types-docutils==0.19.1.6
-    # via types-setuptools
 types-protobuf==3.19.22
     # via
     #   feast (setup.py)
@@ -797,28 +972,29 @@ types-pymysql==1.0.19.5
     # via feast (setup.py)
 types-pyopenssl==23.0.0.4
     # via types-redis
-types-python-dateutil==2.8.19.8
+types-python-dateutil==2.8.19.10
     # via feast (setup.py)
 types-pytz==2022.7.1.2
     # via feast (setup.py)
 types-pyyaml==6.0.12.8
     # via feast (setup.py)
-types-redis==4.5.1.3
+types-redis==4.5.1.4
     # via feast (setup.py)
-types-requests==2.28.11.14
+types-requests==2.28.11.15
     # via feast (setup.py)
-types-setuptools==67.4.0.1
+types-setuptools==67.4.0.3
     # via feast (setup.py)
 types-tabulate==0.9.0.1
     # via feast (setup.py)
 types-toml==0.10.8.5
     # via responses
-types-urllib3==1.26.25.7
+types-urllib3==1.26.25.8
     # via types-requests
 typing-extensions==4.5.0
     # via
     #   aioitertools
     #   azure-core
+    #   azure-storage-blob
     #   black
     #   great-expectations
     #   mypy
@@ -832,6 +1008,8 @@ tzlocal==4.2
     # via
     #   great-expectations
     #   trino
+uri-template==1.2.0
+    # via jsonschema
 uritemplate==4.1.1
     # via google-api-python-client
 urllib3==1.26.14
@@ -850,7 +1028,7 @@ uvicorn[standard]==0.20.0
     # via feast (setup.py)
 uvloop==0.17.0
     # via uvicorn
-virtualenv==20.19.0
+virtualenv==20.20.0
     # via pre-commit
 volatile==2.1.0
     # via bowler
@@ -858,9 +1036,16 @@ watchfiles==0.18.1
     # via uvicorn
 wcwidth==0.2.6
     # via prompt-toolkit
+webcolors==1.12
+    # via jsonschema
+webencodings==0.5.1
+    # via
+    #   bleach
+    #   tinycss2
 websocket-client==1.5.1
     # via
     #   docker
+    #   jupyter-server
     #   kubernetes
 websockets==10.4
     # via uvicorn
@@ -868,7 +1053,9 @@ werkzeug==2.1.2
     # via moto
 wheel==0.38.4
     # via pip-tools
-wrapt==1.14.1
+widgetsnbextension==4.0.5
+    # via ipywidgets
+wrapt==1.15.0
     # via
     #   aiobotocore
     #   deprecated
@@ -877,7 +1064,7 @@ xmltodict==0.13.0
     # via moto
 yarl==1.8.2
     # via aiohttp
-zipp==3.14.0
+zipp==3.15.0
     # 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 c33622325cc..3e2c23fb164 100644
--- a/sdk/python/requirements/py3.9-requirements.txt
+++ b/sdk/python/requirements/py3.9-requirements.txt
@@ -35,13 +35,13 @@ cloudpickle==2.2.1
     # via dask
 colorama==0.4.6
     # via feast (setup.py)
-dask==2023.2.0
+dask==2023.3.0
     # via feast (setup.py)
 dill==0.3.6
     # via feast (setup.py)
 fastapi==0.92.0
     # via feast (setup.py)
-fastavro==1.7.1
+fastavro==1.7.2
     # via
     #   feast (setup.py)
     #   pandavro
@@ -123,7 +123,7 @@ pyrsistent==0.19.3
     # via jsonschema
 python-dateutil==2.8.2
     # via pandas
-python-dotenv==0.21.1
+python-dotenv==1.0.0
     # via uvicorn
 pytz==2022.7.1
     # via pandas
@@ -153,7 +153,7 @@ starlette==0.25.0
     # via fastapi
 tabulate==0.9.0
     # via feast (setup.py)
-tenacity==8.2.1
+tenacity==8.2.2
     # via feast (setup.py)
 toml==0.10.2
     # via feast (setup.py)
diff --git a/setup.py b/setup.py
index 581f04dcfbf..d9c949b30f6 100644
--- a/setup.py
+++ b/setup.py
@@ -127,7 +127,7 @@
     "cassandra-driver>=3.24.0,<4",
 ]
 
-GE_REQUIRED = ["great_expectations>=0.14.0,<0.15.0"]
+GE_REQUIRED = ["great_expectations>=0.15.41,<0.16.0"]
 
 GO_REQUIRED = [
     "cffi~=1.15.0",

Greate Expectations version a bit outdated
## Expected Behavior 
GX version works well with >=0.15.41

## Current Behavior
It got pinned between 0.14 and 0.15


### Specifications

- Version: 0.29.0

## Possible Solution
Bump GX version

