We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
feast cli minor bug
## Expected Behavior 

`feast teardown --help` should list the options to this subcommand like

> (feast) ➜  examples git:(br_jsd_mlflow_feast_example) ✗ feast init --help
> Usage: feast init [OPTIONS] [PROJECT_DIRECTORY]
> 
>   Create a new Feast repository
> 
> Options:
>   -m, --minimal               Create an empty project repository
>   -t, --template [local|gcp]  Specify a template for the created project
>   --help                      Show this message and exit.

## Current Behavior

> (base) ➜  delta-lake git:(master) ✗ feast teardown --help
> Usage: feast teardown [OPTIONS]
> 
>   Tear down deployed feature store infrastructure
> 
> Options:
>   --help  Show this message and exit.

I reckon this is a generic message if the subcommand has not options. For example, `feast apply --help` exhibits similar 
behavior.

## Steps to reproduce

`feast teardown --help`

### Specifications

- Version: 0.10.3
- Platform: MacOS Mojave
- Subsystem:



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sdk/python/feast/cli.py`
Content:
```
1 # Copyright 2019 The Feast Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     https://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import logging
16 from datetime import datetime
17 from pathlib import Path
18 from typing import List
19 
20 import click
21 import pkg_resources
22 import yaml
23 
24 from feast.errors import FeastObjectNotFoundException, FeastProviderLoginError
25 from feast.feature_store import FeatureStore
26 from feast.repo_config import load_repo_config
27 from feast.repo_operations import (
28     apply_total,
29     cli_check_repo,
30     generate_project_name,
31     init_repo,
32     registry_dump,
33     teardown,
34 )
35 from feast.telemetry import Telemetry
36 
37 _logger = logging.getLogger(__name__)
38 DATETIME_ISO = "%Y-%m-%dT%H:%M:%s"
39 
40 
41 @click.group()
42 @click.option(
43     "--chdir",
44     "-c",
45     help="Switch to a different feature repository directory before executing the given subcommand.",
46 )
47 @click.pass_context
48 def cli(ctx: click.Context, chdir: str):
49     """
50     Feast CLI
51 
52     For more information, see our public docs at https://docs.feast.dev/
53 
54     For any questions, you can reach us at https://slack.feast.dev/
55     """
56     ctx.ensure_object(dict)
57     ctx.obj["CHDIR"] = Path.cwd() if chdir is None else Path(chdir).absolute()
58     pass
59 
60 
61 @cli.command()
62 def version():
63     """
64     Display Feast SDK version
65     """
66     print(f'Feast SDK Version: "{pkg_resources.get_distribution("feast")}"')
67 
68 
69 @cli.group(name="entities")
70 def entities_cmd():
71     """
72     Access entities
73     """
74     pass
75 
76 
77 @entities_cmd.command("describe")
78 @click.argument("name", type=click.STRING)
79 @click.pass_context
80 def entity_describe(ctx: click.Context, name: str):
81     """
82     Describe an entity
83     """
84     repo = ctx.obj["CHDIR"]
85     cli_check_repo(repo)
86     store = FeatureStore(repo_path=str(repo))
87 
88     try:
89         entity = store.get_entity(name)
90     except FeastObjectNotFoundException as e:
91         print(e)
92         exit(1)
93 
94     print(
95         yaml.dump(
96             yaml.safe_load(str(entity)), default_flow_style=False, sort_keys=False
97         )
98     )
99 
100 
101 @entities_cmd.command(name="list")
102 @click.pass_context
103 def entity_list(ctx: click.Context):
104     """
105     List all entities
106     """
107     repo = ctx.obj["CHDIR"]
108     cli_check_repo(repo)
109     store = FeatureStore(repo_path=str(repo))
110     table = []
111     for entity in store.list_entities():
112         table.append([entity.name, entity.description, entity.value_type])
113 
114     from tabulate import tabulate
115 
116     print(tabulate(table, headers=["NAME", "DESCRIPTION", "TYPE"], tablefmt="plain"))
117 
118 
119 @cli.group(name="feature-views")
120 def feature_views_cmd():
121     """
122     Access feature views
123     """
124     pass
125 
126 
127 @feature_views_cmd.command("describe")
128 @click.argument("name", type=click.STRING)
129 @click.pass_context
130 def feature_view_describe(ctx: click.Context, name: str):
131     """
132     Describe a feature view
133     """
134     repo = ctx.obj["CHDIR"]
135     cli_check_repo(repo)
136     store = FeatureStore(repo_path=str(repo))
137 
138     try:
139         feature_view = store.get_feature_view(name)
140     except FeastObjectNotFoundException as e:
141         print(e)
142         exit(1)
143 
144     print(
145         yaml.dump(
146             yaml.safe_load(str(feature_view)), default_flow_style=False, sort_keys=False
147         )
148     )
149 
150 
151 @feature_views_cmd.command(name="list")
152 @click.pass_context
153 def feature_view_list(ctx: click.Context):
154     """
155     List all feature views
156     """
157     repo = ctx.obj["CHDIR"]
158     cli_check_repo(repo)
159     store = FeatureStore(repo_path=str(repo))
160     table = []
161     for feature_view in store.list_feature_views():
162         table.append([feature_view.name, feature_view.entities])
163 
164     from tabulate import tabulate
165 
166     print(tabulate(table, headers=["NAME", "ENTITIES"], tablefmt="plain"))
167 
168 
169 @cli.command("apply")
170 @click.pass_context
171 def apply_total_command(ctx: click.Context):
172     """
173     Create or update a feature store deployment
174     """
175     repo = ctx.obj["CHDIR"]
176     cli_check_repo(repo)
177     repo_config = load_repo_config(repo)
178     tele = Telemetry()
179     tele.log("apply")
180     try:
181         apply_total(repo_config, repo)
182     except FeastProviderLoginError as e:
183         print(str(e))
184 
185 
186 @cli.command("teardown")
187 @click.pass_context
188 def teardown_command(ctx: click.Context):
189     """
190     Tear down deployed feature store infrastructure
191     """
192     repo = ctx.obj["CHDIR"]
193     cli_check_repo(repo)
194     repo_config = load_repo_config(repo)
195     tele = Telemetry()
196     tele.log("teardown")
197 
198     teardown(repo_config, repo)
199 
200 
201 @cli.command("registry-dump")
202 @click.pass_context
203 def registry_dump_command(ctx: click.Context):
204     """
205     Print contents of the metadata registry
206     """
207     repo = ctx.obj["CHDIR"]
208     cli_check_repo(repo)
209     repo_config = load_repo_config(repo)
210     tele = Telemetry()
211     tele.log("registry-dump")
212 
213     registry_dump(repo_config, repo_path=repo)
214 
215 
216 @cli.command("materialize")
217 @click.argument("start_ts")
218 @click.argument("end_ts")
219 @click.option(
220     "--views", "-v", help="Feature views to materialize", multiple=True,
221 )
222 @click.pass_context
223 def materialize_command(
224     ctx: click.Context, start_ts: str, end_ts: str, views: List[str]
225 ):
226     """
227     Run a (non-incremental) materialization job to ingest data into the online store. Feast
228     will read all data between START_TS and END_TS from the offline store and write it to the
229     online store. If you don't specify feature view names using --views, all registered Feature
230     Views will be materialized.
231 
232     START_TS and END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'
233     """
234     repo = ctx.obj["CHDIR"]
235     cli_check_repo(repo)
236     store = FeatureStore(repo_path=str(repo))
237     store.materialize(
238         feature_views=None if not views else views,
239         start_date=datetime.fromisoformat(start_ts),
240         end_date=datetime.fromisoformat(end_ts),
241     )
242 
243 
244 @cli.command("materialize-incremental")
245 @click.argument("end_ts")
246 @click.option(
247     "--views", "-v", help="Feature views to incrementally materialize", multiple=True,
248 )
249 @click.pass_context
250 def materialize_incremental_command(ctx: click.Context, end_ts: str, views: List[str]):
251     """
252     Run an incremental materialization job to ingest new data into the online store. Feast will read
253     all data from the previously ingested point to END_TS from the offline store and write it to the
254     online store. If you don't specify feature view names using --views, all registered Feature
255     Views will be incrementally materialized.
256 
257     END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'
258     """
259     repo = ctx.obj["CHDIR"]
260     cli_check_repo(repo)
261     store = FeatureStore(repo_path=str(repo))
262     store.materialize_incremental(
263         feature_views=None if not views else views,
264         end_date=datetime.fromisoformat(end_ts),
265     )
266 
267 
268 @cli.command("init")
269 @click.argument("PROJECT_DIRECTORY", required=False)
270 @click.option(
271     "--minimal", "-m", is_flag=True, help="Create an empty project repository"
272 )
273 @click.option(
274     "--template",
275     "-t",
276     type=click.Choice(["local", "gcp"], case_sensitive=False),
277     help="Specify a template for the created project",
278     default="local",
279 )
280 def init_command(project_directory, minimal: bool, template: str):
281     """Create a new Feast repository"""
282     if not project_directory:
283         project_directory = generate_project_name()
284 
285     if minimal:
286         template = "minimal"
287 
288     tele = Telemetry()
289     tele.log("init")
290     init_repo(project_directory, template)
291 
292 
293 if __name__ == "__main__":
294     cli()
295
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py
--- a/sdk/python/feast/cli.py
+++ b/sdk/python/feast/cli.py
@@ -38,6 +38,19 @@
 DATETIME_ISO = "%Y-%m-%dT%H:%M:%s"
 
 
+class NoOptionDefaultFormat(click.Command):
+    def format_options(self, ctx: click.Context, formatter: click.HelpFormatter):
+        """Writes all the options into the formatter if they exist."""
+        opts = []
+        for param in self.get_params(ctx):
+            rv = param.get_help_record(ctx)
+            if rv is not None:
+                opts.append(rv)
+        if opts:
+            with formatter.section("Options(No current command options)"):
+                formatter.write_dl(opts)
+
+
 @click.group()
 @click.option(
     "--chdir",
@@ -166,7 +179,7 @@
     print(tabulate(table, headers=["NAME", "ENTITIES"], tablefmt="plain"))
 
 
-@cli.command("apply")
+@cli.command("apply", cls=NoOptionDefaultFormat)
 @click.pass_context
 def apply_total_command(ctx: click.Context):
     """
@@ -183,7 +196,7 @@
         print(str(e))
 
 
-@cli.command("teardown")
+@cli.command("teardown", cls=NoOptionDefaultFormat)
 @click.pass_context
 def teardown_command(ctx: click.Context):
     """

{"golden_diff": "diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py\n--- a/sdk/python/feast/cli.py\n+++ b/sdk/python/feast/cli.py\n@@ -38,6 +38,19 @@\n DATETIME_ISO = \"%Y-%m-%dT%H:%M:%s\"\n \n \n+class NoOptionDefaultFormat(click.Command):\n+    def format_options(self, ctx: click.Context, formatter: click.HelpFormatter):\n+        \"\"\"Writes all the options into the formatter if they exist.\"\"\"\n+        opts = []\n+        for param in self.get_params(ctx):\n+            rv = param.get_help_record(ctx)\n+            if rv is not None:\n+                opts.append(rv)\n+        if opts:\n+            with formatter.section(\"Options(No current command options)\"):\n+                formatter.write_dl(opts)\n+\n+\n @click.group()\n @click.option(\n     \"--chdir\",\n@@ -166,7 +179,7 @@\n     print(tabulate(table, headers=[\"NAME\", \"ENTITIES\"], tablefmt=\"plain\"))\n \n \n-@cli.command(\"apply\")\n+@cli.command(\"apply\", cls=NoOptionDefaultFormat)\n @click.pass_context\n def apply_total_command(ctx: click.Context):\n     \"\"\"\n@@ -183,7 +196,7 @@\n         print(str(e))\n \n \n-@cli.command(\"teardown\")\n+@cli.command(\"teardown\", cls=NoOptionDefaultFormat)\n @click.pass_context\n def teardown_command(ctx: click.Context):\n     \"\"\"\n", "issue": "feast cli minor bug\n## Expected Behavior \r\n\r\n`feast teardown --help` should list the options to this subcommand like\r\n\r\n> (feast) \u279c  examples git:(br_jsd_mlflow_feast_example) \u2717 feast init --help\r\n> Usage: feast init [OPTIONS] [PROJECT_DIRECTORY]\r\n> \r\n>   Create a new Feast repository\r\n> \r\n> Options:\r\n>   -m, --minimal               Create an empty project repository\r\n>   -t, --template [local|gcp]  Specify a template for the created project\r\n>   --help                      Show this message and exit.\r\n\r\n## Current Behavior\r\n\r\n> (base) \u279c  delta-lake git:(master) \u2717 feast teardown --help\r\n> Usage: feast teardown [OPTIONS]\r\n> \r\n>   Tear down deployed feature store infrastructure\r\n> \r\n> Options:\r\n>   --help  Show this message and exit.\r\n\r\nI reckon this is a generic message if the subcommand has not options. For example, `feast apply --help` exhibits similar \r\nbehavior.\r\n\r\n## Steps to reproduce\r\n\r\n`feast teardown --help`\r\n\r\n### Specifications\r\n\r\n- Version: 0.10.3\r\n- Platform: MacOS Mojave\r\n- Subsystem:\r\n\r\n\n", "before_files": [{"content": "# Copyright 2019 The Feast Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import List\n\nimport click\nimport pkg_resources\nimport yaml\n\nfrom feast.errors import FeastObjectNotFoundException, FeastProviderLoginError\nfrom feast.feature_store import FeatureStore\nfrom feast.repo_config import load_repo_config\nfrom feast.repo_operations import (\n    apply_total,\n    cli_check_repo,\n    generate_project_name,\n    init_repo,\n    registry_dump,\n    teardown,\n)\nfrom feast.telemetry import Telemetry\n\n_logger = logging.getLogger(__name__)\nDATETIME_ISO = \"%Y-%m-%dT%H:%M:%s\"\n\n\n@click.group()\n@click.option(\n    \"--chdir\",\n    \"-c\",\n    help=\"Switch to a different feature repository directory before executing the given subcommand.\",\n)\n@click.pass_context\ndef cli(ctx: click.Context, chdir: str):\n    \"\"\"\n    Feast CLI\n\n    For more information, see our public docs at https://docs.feast.dev/\n\n    For any questions, you can reach us at https://slack.feast.dev/\n    \"\"\"\n    ctx.ensure_object(dict)\n    ctx.obj[\"CHDIR\"] = Path.cwd() if chdir is None else Path(chdir).absolute()\n    pass\n\n\n@cli.command()\ndef version():\n    \"\"\"\n    Display Feast SDK version\n    \"\"\"\n    print(f'Feast SDK Version: \"{pkg_resources.get_distribution(\"feast\")}\"')\n\n\n@cli.group(name=\"entities\")\ndef entities_cmd():\n    \"\"\"\n    Access entities\n    \"\"\"\n    pass\n\n\n@entities_cmd.command(\"describe\")\n@click.argument(\"name\", type=click.STRING)\n@click.pass_context\ndef entity_describe(ctx: click.Context, name: str):\n    \"\"\"\n    Describe an entity\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n\n    try:\n        entity = store.get_entity(name)\n    except FeastObjectNotFoundException as e:\n        print(e)\n        exit(1)\n\n    print(\n        yaml.dump(\n            yaml.safe_load(str(entity)), default_flow_style=False, sort_keys=False\n        )\n    )\n\n\n@entities_cmd.command(name=\"list\")\n@click.pass_context\ndef entity_list(ctx: click.Context):\n    \"\"\"\n    List all entities\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    table = []\n    for entity in store.list_entities():\n        table.append([entity.name, entity.description, entity.value_type])\n\n    from tabulate import tabulate\n\n    print(tabulate(table, headers=[\"NAME\", \"DESCRIPTION\", \"TYPE\"], tablefmt=\"plain\"))\n\n\n@cli.group(name=\"feature-views\")\ndef feature_views_cmd():\n    \"\"\"\n    Access feature views\n    \"\"\"\n    pass\n\n\n@feature_views_cmd.command(\"describe\")\n@click.argument(\"name\", type=click.STRING)\n@click.pass_context\ndef feature_view_describe(ctx: click.Context, name: str):\n    \"\"\"\n    Describe a feature view\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n\n    try:\n        feature_view = store.get_feature_view(name)\n    except FeastObjectNotFoundException as e:\n        print(e)\n        exit(1)\n\n    print(\n        yaml.dump(\n            yaml.safe_load(str(feature_view)), default_flow_style=False, sort_keys=False\n        )\n    )\n\n\n@feature_views_cmd.command(name=\"list\")\n@click.pass_context\ndef feature_view_list(ctx: click.Context):\n    \"\"\"\n    List all feature views\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    table = []\n    for feature_view in store.list_feature_views():\n        table.append([feature_view.name, feature_view.entities])\n\n    from tabulate import tabulate\n\n    print(tabulate(table, headers=[\"NAME\", \"ENTITIES\"], tablefmt=\"plain\"))\n\n\n@cli.command(\"apply\")\n@click.pass_context\ndef apply_total_command(ctx: click.Context):\n    \"\"\"\n    Create or update a feature store deployment\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"apply\")\n    try:\n        apply_total(repo_config, repo)\n    except FeastProviderLoginError as e:\n        print(str(e))\n\n\n@cli.command(\"teardown\")\n@click.pass_context\ndef teardown_command(ctx: click.Context):\n    \"\"\"\n    Tear down deployed feature store infrastructure\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"teardown\")\n\n    teardown(repo_config, repo)\n\n\n@cli.command(\"registry-dump\")\n@click.pass_context\ndef registry_dump_command(ctx: click.Context):\n    \"\"\"\n    Print contents of the metadata registry\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"registry-dump\")\n\n    registry_dump(repo_config, repo_path=repo)\n\n\n@cli.command(\"materialize\")\n@click.argument(\"start_ts\")\n@click.argument(\"end_ts\")\n@click.option(\n    \"--views\", \"-v\", help=\"Feature views to materialize\", multiple=True,\n)\n@click.pass_context\ndef materialize_command(\n    ctx: click.Context, start_ts: str, end_ts: str, views: List[str]\n):\n    \"\"\"\n    Run a (non-incremental) materialization job to ingest data into the online store. Feast\n    will read all data between START_TS and END_TS from the offline store and write it to the\n    online store. If you don't specify feature view names using --views, all registered Feature\n    Views will be materialized.\n\n    START_TS and END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    store.materialize(\n        feature_views=None if not views else views,\n        start_date=datetime.fromisoformat(start_ts),\n        end_date=datetime.fromisoformat(end_ts),\n    )\n\n\n@cli.command(\"materialize-incremental\")\n@click.argument(\"end_ts\")\n@click.option(\n    \"--views\", \"-v\", help=\"Feature views to incrementally materialize\", multiple=True,\n)\n@click.pass_context\ndef materialize_incremental_command(ctx: click.Context, end_ts: str, views: List[str]):\n    \"\"\"\n    Run an incremental materialization job to ingest new data into the online store. Feast will read\n    all data from the previously ingested point to END_TS from the offline store and write it to the\n    online store. If you don't specify feature view names using --views, all registered Feature\n    Views will be incrementally materialized.\n\n    END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    store.materialize_incremental(\n        feature_views=None if not views else views,\n        end_date=datetime.fromisoformat(end_ts),\n    )\n\n\n@cli.command(\"init\")\n@click.argument(\"PROJECT_DIRECTORY\", required=False)\n@click.option(\n    \"--minimal\", \"-m\", is_flag=True, help=\"Create an empty project repository\"\n)\n@click.option(\n    \"--template\",\n    \"-t\",\n    type=click.Choice([\"local\", \"gcp\"], case_sensitive=False),\n    help=\"Specify a template for the created project\",\n    default=\"local\",\n)\ndef init_command(project_directory, minimal: bool, template: str):\n    \"\"\"Create a new Feast repository\"\"\"\n    if not project_directory:\n        project_directory = generate_project_name()\n\n    if minimal:\n        template = \"minimal\"\n\n    tele = Telemetry()\n    tele.log(\"init\")\n    init_repo(project_directory, template)\n\n\nif __name__ == \"__main__\":\n    cli()\n", "path": "sdk/python/feast/cli.py"}], "after_files": [{"content": "# Copyright 2019 The Feast Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport logging\nfrom datetime import datetime\nfrom pathlib import Path\nfrom typing import List\n\nimport click\nimport pkg_resources\nimport yaml\n\nfrom feast.errors import FeastObjectNotFoundException, FeastProviderLoginError\nfrom feast.feature_store import FeatureStore\nfrom feast.repo_config import load_repo_config\nfrom feast.repo_operations import (\n    apply_total,\n    cli_check_repo,\n    generate_project_name,\n    init_repo,\n    registry_dump,\n    teardown,\n)\nfrom feast.telemetry import Telemetry\n\n_logger = logging.getLogger(__name__)\nDATETIME_ISO = \"%Y-%m-%dT%H:%M:%s\"\n\n\nclass NoOptionDefaultFormat(click.Command):\n    def format_options(self, ctx: click.Context, formatter: click.HelpFormatter):\n        \"\"\"Writes all the options into the formatter if they exist.\"\"\"\n        opts = []\n        for param in self.get_params(ctx):\n            rv = param.get_help_record(ctx)\n            if rv is not None:\n                opts.append(rv)\n        if opts:\n            with formatter.section(\"Options(No current command options)\"):\n                formatter.write_dl(opts)\n\n\n@click.group()\n@click.option(\n    \"--chdir\",\n    \"-c\",\n    help=\"Switch to a different feature repository directory before executing the given subcommand.\",\n)\n@click.pass_context\ndef cli(ctx: click.Context, chdir: str):\n    \"\"\"\n    Feast CLI\n\n    For more information, see our public docs at https://docs.feast.dev/\n\n    For any questions, you can reach us at https://slack.feast.dev/\n    \"\"\"\n    ctx.ensure_object(dict)\n    ctx.obj[\"CHDIR\"] = Path.cwd() if chdir is None else Path(chdir).absolute()\n    pass\n\n\n@cli.command()\ndef version():\n    \"\"\"\n    Display Feast SDK version\n    \"\"\"\n    print(f'Feast SDK Version: \"{pkg_resources.get_distribution(\"feast\")}\"')\n\n\n@cli.group(name=\"entities\")\ndef entities_cmd():\n    \"\"\"\n    Access entities\n    \"\"\"\n    pass\n\n\n@entities_cmd.command(\"describe\")\n@click.argument(\"name\", type=click.STRING)\n@click.pass_context\ndef entity_describe(ctx: click.Context, name: str):\n    \"\"\"\n    Describe an entity\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n\n    try:\n        entity = store.get_entity(name)\n    except FeastObjectNotFoundException as e:\n        print(e)\n        exit(1)\n\n    print(\n        yaml.dump(\n            yaml.safe_load(str(entity)), default_flow_style=False, sort_keys=False\n        )\n    )\n\n\n@entities_cmd.command(name=\"list\")\n@click.pass_context\ndef entity_list(ctx: click.Context):\n    \"\"\"\n    List all entities\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    table = []\n    for entity in store.list_entities():\n        table.append([entity.name, entity.description, entity.value_type])\n\n    from tabulate import tabulate\n\n    print(tabulate(table, headers=[\"NAME\", \"DESCRIPTION\", \"TYPE\"], tablefmt=\"plain\"))\n\n\n@cli.group(name=\"feature-views\")\ndef feature_views_cmd():\n    \"\"\"\n    Access feature views\n    \"\"\"\n    pass\n\n\n@feature_views_cmd.command(\"describe\")\n@click.argument(\"name\", type=click.STRING)\n@click.pass_context\ndef feature_view_describe(ctx: click.Context, name: str):\n    \"\"\"\n    Describe a feature view\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n\n    try:\n        feature_view = store.get_feature_view(name)\n    except FeastObjectNotFoundException as e:\n        print(e)\n        exit(1)\n\n    print(\n        yaml.dump(\n            yaml.safe_load(str(feature_view)), default_flow_style=False, sort_keys=False\n        )\n    )\n\n\n@feature_views_cmd.command(name=\"list\")\n@click.pass_context\ndef feature_view_list(ctx: click.Context):\n    \"\"\"\n    List all feature views\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    table = []\n    for feature_view in store.list_feature_views():\n        table.append([feature_view.name, feature_view.entities])\n\n    from tabulate import tabulate\n\n    print(tabulate(table, headers=[\"NAME\", \"ENTITIES\"], tablefmt=\"plain\"))\n\n\n@cli.command(\"apply\", cls=NoOptionDefaultFormat)\n@click.pass_context\ndef apply_total_command(ctx: click.Context):\n    \"\"\"\n    Create or update a feature store deployment\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"apply\")\n    try:\n        apply_total(repo_config, repo)\n    except FeastProviderLoginError as e:\n        print(str(e))\n\n\n@cli.command(\"teardown\", cls=NoOptionDefaultFormat)\n@click.pass_context\ndef teardown_command(ctx: click.Context):\n    \"\"\"\n    Tear down deployed feature store infrastructure\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"teardown\")\n\n    teardown(repo_config, repo)\n\n\n@cli.command(\"registry-dump\")\n@click.pass_context\ndef registry_dump_command(ctx: click.Context):\n    \"\"\"\n    Print contents of the metadata registry\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    repo_config = load_repo_config(repo)\n    tele = Telemetry()\n    tele.log(\"registry-dump\")\n\n    registry_dump(repo_config, repo_path=repo)\n\n\n@cli.command(\"materialize\")\n@click.argument(\"start_ts\")\n@click.argument(\"end_ts\")\n@click.option(\n    \"--views\", \"-v\", help=\"Feature views to materialize\", multiple=True,\n)\n@click.pass_context\ndef materialize_command(\n    ctx: click.Context, start_ts: str, end_ts: str, views: List[str]\n):\n    \"\"\"\n    Run a (non-incremental) materialization job to ingest data into the online store. Feast\n    will read all data between START_TS and END_TS from the offline store and write it to the\n    online store. If you don't specify feature view names using --views, all registered Feature\n    Views will be materialized.\n\n    START_TS and END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    store.materialize(\n        feature_views=None if not views else views,\n        start_date=datetime.fromisoformat(start_ts),\n        end_date=datetime.fromisoformat(end_ts),\n    )\n\n\n@cli.command(\"materialize-incremental\")\n@click.argument(\"end_ts\")\n@click.option(\n    \"--views\", \"-v\", help=\"Feature views to incrementally materialize\", multiple=True,\n)\n@click.pass_context\ndef materialize_incremental_command(ctx: click.Context, end_ts: str, views: List[str]):\n    \"\"\"\n    Run an incremental materialization job to ingest new data into the online store. Feast will read\n    all data from the previously ingested point to END_TS from the offline store and write it to the\n    online store. If you don't specify feature view names using --views, all registered Feature\n    Views will be incrementally materialized.\n\n    END_TS should be in ISO 8601 format, e.g. '2021-07-16T19:20:01'\n    \"\"\"\n    repo = ctx.obj[\"CHDIR\"]\n    cli_check_repo(repo)\n    store = FeatureStore(repo_path=str(repo))\n    store.materialize_incremental(\n        feature_views=None if not views else views,\n        end_date=datetime.fromisoformat(end_ts),\n    )\n\n\n@cli.command(\"init\")\n@click.argument(\"PROJECT_DIRECTORY\", required=False)\n@click.option(\n    \"--minimal\", \"-m\", is_flag=True, help=\"Create an empty project repository\"\n)\n@click.option(\n    \"--template\",\n    \"-t\",\n    type=click.Choice([\"local\", \"gcp\"], case_sensitive=False),\n    help=\"Specify a template for the created project\",\n    default=\"local\",\n)\ndef init_command(project_directory, minimal: bool, template: str):\n    \"\"\"Create a new Feast repository\"\"\"\n    if not project_directory:\n        project_directory = generate_project_name()\n\n    if minimal:\n        template = \"minimal\"\n\n    tele = Telemetry()\n    tele.log(\"init\")\n    init_repo(project_directory, template)\n\n\nif __name__ == \"__main__\":\n    cli()\n", "path": "sdk/python/feast/cli.py"}]}