hirundo 0.1.7__tar.gz → 0.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. hirundo-0.1.8/PKG-INFO +176 -0
  2. hirundo-0.1.8/README.md +115 -0
  3. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/__init__.py +1 -1
  4. hirundo-0.1.8/hirundo/_env.py +26 -0
  5. hirundo-0.1.8/hirundo/_http.py +14 -0
  6. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/_iter_sse_retrying.py +2 -2
  7. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/cli.py +75 -16
  8. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/dataset_optimization.py +111 -57
  9. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/git.py +11 -9
  10. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/logger.py +3 -1
  11. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/storage.py +17 -17
  12. hirundo-0.1.8/hirundo.egg-info/PKG-INFO +176 -0
  13. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo.egg-info/SOURCES.txt +1 -0
  14. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo.egg-info/requires.txt +1 -1
  15. {hirundo-0.1.7 → hirundo-0.1.8}/pyproject.toml +4 -2
  16. hirundo-0.1.7/PKG-INFO +0 -118
  17. hirundo-0.1.7/README.md +0 -57
  18. hirundo-0.1.7/hirundo/_env.py +0 -15
  19. hirundo-0.1.7/hirundo.egg-info/PKG-INFO +0 -118
  20. {hirundo-0.1.7 → hirundo-0.1.8}/LICENSE +0 -0
  21. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/__main__.py +0 -0
  22. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/_constraints.py +0 -0
  23. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/_headers.py +0 -0
  24. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/_timeouts.py +0 -0
  25. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo/enum.py +0 -0
  26. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo.egg-info/dependency_links.txt +0 -0
  27. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo.egg-info/entry_points.txt +0 -0
  28. {hirundo-0.1.7 → hirundo-0.1.8}/hirundo.egg-info/top_level.txt +0 -0
  29. {hirundo-0.1.7 → hirundo-0.1.8}/setup.cfg +0 -0
hirundo-0.1.8/PKG-INFO ADDED
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.1
2
+ Name: hirundo
3
+ Version: 0.1.8
4
+ Summary: This package is used to interface with Hirundo's platform. It provides a simple API to optimize your ML datasets.
5
+ Author-email: Hirundo <dev@hirundo.io>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024, Hirundo
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+
16
+ Project-URL: Homepage, https://github.com/Hirundo-io/hirundo-client
17
+ Keywords: dataset,machine learning,data science,data engineering
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: pyyaml>=6.0.1
25
+ Requires-Dist: types-PyYAML>=6.0.12
26
+ Requires-Dist: pydantic>=2.7.1
27
+ Requires-Dist: twine>=5.0.0
28
+ Requires-Dist: python-dotenv>=1.0.1
29
+ Requires-Dist: types-requests>=2.31.0
30
+ Requires-Dist: typer>=0.12.3
31
+ Requires-Dist: httpx>=0.27.0
32
+ Requires-Dist: stamina>=24.2.0
33
+ Requires-Dist: httpx-sse>=0.4.0
34
+ Requires-Dist: pandas>=2.2.2
35
+ Requires-Dist: tqdm>=4.66.5
36
+ Provides-Extra: dev
37
+ Requires-Dist: pyyaml>=6.0.1; extra == "dev"
38
+ Requires-Dist: types-PyYAML>=6.0.12; extra == "dev"
39
+ Requires-Dist: pydantic>=2.7.1; extra == "dev"
40
+ Requires-Dist: twine>=5.0.0; extra == "dev"
41
+ Requires-Dist: python-dotenv>=1.0.1; extra == "dev"
42
+ Requires-Dist: types-requests>=2.31.0; extra == "dev"
43
+ Requires-Dist: types-setuptools>=69.5.0; extra == "dev"
44
+ Requires-Dist: typer>=0.12.3; extra == "dev"
45
+ Requires-Dist: httpx>=0.27.0; extra == "dev"
46
+ Requires-Dist: stamina>=24.2.0; extra == "dev"
47
+ Requires-Dist: httpx-sse>=0.4.0; extra == "dev"
48
+ Requires-Dist: pytest>=8.2.0; extra == "dev"
49
+ Requires-Dist: pytest-asyncio>=0.23.6; extra == "dev"
50
+ Requires-Dist: uv; extra == "dev"
51
+ Requires-Dist: pre-commit>=3.7.1; extra == "dev"
52
+ Requires-Dist: ruff==0.6.5; extra == "dev"
53
+ Requires-Dist: bumpver; extra == "dev"
54
+ Provides-Extra: docs
55
+ Requires-Dist: sphinx>=7.4.7; extra == "docs"
56
+ Requires-Dist: sphinx-autobuild>=2024.4.16; extra == "docs"
57
+ Requires-Dist: sphinx-click>=5.0.1; extra == "docs"
58
+ Requires-Dist: autodoc_pydantic>=2.2.0; extra == "docs"
59
+ Requires-Dist: furo; extra == "docs"
60
+ Requires-Dist: sphinx-multiversion; extra == "docs"
61
+
62
+ # Hirundo
63
+
64
+ This package exposes access to Hirundo APIs for dataset optimization for Machine Learning.
65
+
66
+ Dataset optimization is currently available for datasets labelled for classification and object detection.
67
+
68
+
69
+ Support dataset storage integrations include:
70
+ - Google Cloud (GCP) Storage
71
+ - Amazon Web Services (AWS) S3
72
+ - Git LFS (Large File Storage) repositories (e.g. GitHub or HuggingFace)
73
+
74
+ Optimizing a classification dataset
75
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76
+
77
+ Currently ``hirundo`` requires a CSV file with the following columns (all columns are required):
78
+ - ``image_path``: The location of the image within the dataset ``root``
79
+ - ``label``: The label of the image, i.e. which the class that was annotated for this image
80
+
81
+ And outputs a CSV with the same columns and:
82
+ - ``suspect_level``: mislabel suspect level
83
+ - ``suggested_label``: suggested label
84
+ - ``suggested_label_conf``: suggested label confidence
85
+
86
+ Optimizing an object detection (OD) dataset
87
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88
+
89
+ Currently ``hirundo`` requires a CSV file with the following columns (all columns are required):
90
+ - ``image_path``: The location of the image within the dataset ``root``
91
+ - ``bbox_id``: The index of the bounding box within the dataset. Used to indicate label suspects
92
+ - ``label``: The label of the image, i.e. which the class that was annotated for this image
93
+ - ``x1``, ``y1``, ``x2``, ``y2``: The bounding box coordinates of the object within the image
94
+
95
+ And outputs a CSV with the same columns and:
96
+ - ``suspect_level``: object mislabel suspect level
97
+ - ``suggested_label``: suggested object label
98
+ - ``suggested_label_conf``: suggested object label confidence
99
+
100
+ Note: This Python package must be used alongside a Hirundo server, either the SaaS platform, a custom VPC deployment or an on-premises installation.
101
+
102
+
103
+ ## Installation
104
+
105
+ You can install the codebase with a simple `pip install hirundo` to install the latest version of this package. If you prefer to install from the Git repository and/or need a specific version or branch, you can simply clone the repository, check out the relevant commit and then run `pip install .` to install that version. A full list of dependencies can be found in `requirements.txt`, but these will be installed automatically by either of these commands.
106
+
107
+ ## Usage
108
+
109
+ Classification example:
110
+ ```
111
+ from hirundo.dataset_optimization import OptimizationDataset
112
+ from hirundo.enum import LabellingType
113
+ from hirundo.storage import StorageIntegration, StorageLink, StorageTypes
114
+
115
+ test_dataset = OptimizationDataset(
116
+ name="TEST-GCP cifar 100 classification dataset",
117
+ labelling_type=LabellingType.SingleLabelClassification,
118
+ dataset_storage=StorageLink(
119
+ storage_integration=StorageIntegration(
120
+ name="cifar100bucket",
121
+ type=StorageTypes.GCP,
122
+ gcp=StorageGCP(
123
+ bucket_name="cifar100bucket",
124
+ project="Hirundo-global",
125
+ credentials_json=json.loads(os.environ["GCP_CREDENTIALS"]),
126
+ ),
127
+ ),
128
+ path="/pytorch-cifar/data",
129
+ ),
130
+ dataset_metadata_path="cifar100.csv",
131
+ classes=cifar100_classes,
132
+ )
133
+
134
+ test_dataset.run_optimization()
135
+ results = test_dataset.check_run()
136
+ print(results)
137
+ ```
138
+
139
+
140
+ Object detection example:
141
+
142
+ ```
143
+ from hirundo.dataset_optimization import OptimizationDataset
144
+ from hirundo.enum import LabellingType
145
+ from hirundo.storage import StorageIntegration, StorageLink, StorageTypes
146
+
147
+ test_dataset = OptimizationDataset(
148
+ name=f"TEST-HuggingFace-BDD-100k-validation-OD-validation-dataset{unique_id}",
149
+ labelling_type=LabellingType.ObjectDetection,
150
+ dataset_storage=StorageLink(
151
+ storage_integration=StorageIntegration(
152
+ name=f"BDD-100k-validation-dataset{unique_id}",
153
+ type=StorageTypes.GIT,
154
+ git=StorageGit(
155
+ repo=GitRepo(
156
+ name=f"BDD-100k-validation-dataset{unique_id}",
157
+ repository_url="https://git@hf.co/datasets/hirundo-io/bdd100k-validation-only",
158
+ ),
159
+ branch="main",
160
+ ),
161
+ ),
162
+ path="/BDD100K Val from Hirundo.zip/bdd100k",
163
+ ),
164
+ dataset_metadata_path="bdd100k.csv",
165
+ )
166
+
167
+ test_dataset.run_optimization()
168
+ results = test_dataset.check_run()
169
+ print(results)
170
+ ```
171
+
172
+ Note: Currently we only support the main CPython release 3.9, 3.10 and 3.11. PyPy support may be introduced in the future.
173
+
174
+ ## Further documentation
175
+
176
+ To learn about mroe how to use this library, please visit the [http://docs.hirundo.io/](documentation) or see the Google Colab examples.
@@ -0,0 +1,115 @@
1
+ # Hirundo
2
+
3
+ This package exposes access to Hirundo APIs for dataset optimization for Machine Learning.
4
+
5
+ Dataset optimization is currently available for datasets labelled for classification and object detection.
6
+
7
+
8
+ Support dataset storage integrations include:
9
+ - Google Cloud (GCP) Storage
10
+ - Amazon Web Services (AWS) S3
11
+ - Git LFS (Large File Storage) repositories (e.g. GitHub or HuggingFace)
12
+
13
+ Optimizing a classification dataset
14
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15
+
16
+ Currently ``hirundo`` requires a CSV file with the following columns (all columns are required):
17
+ - ``image_path``: The location of the image within the dataset ``root``
18
+ - ``label``: The label of the image, i.e. which the class that was annotated for this image
19
+
20
+ And outputs a CSV with the same columns and:
21
+ - ``suspect_level``: mislabel suspect level
22
+ - ``suggested_label``: suggested label
23
+ - ``suggested_label_conf``: suggested label confidence
24
+
25
+ Optimizing an object detection (OD) dataset
26
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27
+
28
+ Currently ``hirundo`` requires a CSV file with the following columns (all columns are required):
29
+ - ``image_path``: The location of the image within the dataset ``root``
30
+ - ``bbox_id``: The index of the bounding box within the dataset. Used to indicate label suspects
31
+ - ``label``: The label of the image, i.e. which the class that was annotated for this image
32
+ - ``x1``, ``y1``, ``x2``, ``y2``: The bounding box coordinates of the object within the image
33
+
34
+ And outputs a CSV with the same columns and:
35
+ - ``suspect_level``: object mislabel suspect level
36
+ - ``suggested_label``: suggested object label
37
+ - ``suggested_label_conf``: suggested object label confidence
38
+
39
+ Note: This Python package must be used alongside a Hirundo server, either the SaaS platform, a custom VPC deployment or an on-premises installation.
40
+
41
+
42
+ ## Installation
43
+
44
+ You can install the codebase with a simple `pip install hirundo` to install the latest version of this package. If you prefer to install from the Git repository and/or need a specific version or branch, you can simply clone the repository, check out the relevant commit and then run `pip install .` to install that version. A full list of dependencies can be found in `requirements.txt`, but these will be installed automatically by either of these commands.
45
+
46
+ ## Usage
47
+
48
+ Classification example:
49
+ ```
50
+ from hirundo.dataset_optimization import OptimizationDataset
51
+ from hirundo.enum import LabellingType
52
+ from hirundo.storage import StorageIntegration, StorageLink, StorageTypes
53
+
54
+ test_dataset = OptimizationDataset(
55
+ name="TEST-GCP cifar 100 classification dataset",
56
+ labelling_type=LabellingType.SingleLabelClassification,
57
+ dataset_storage=StorageLink(
58
+ storage_integration=StorageIntegration(
59
+ name="cifar100bucket",
60
+ type=StorageTypes.GCP,
61
+ gcp=StorageGCP(
62
+ bucket_name="cifar100bucket",
63
+ project="Hirundo-global",
64
+ credentials_json=json.loads(os.environ["GCP_CREDENTIALS"]),
65
+ ),
66
+ ),
67
+ path="/pytorch-cifar/data",
68
+ ),
69
+ dataset_metadata_path="cifar100.csv",
70
+ classes=cifar100_classes,
71
+ )
72
+
73
+ test_dataset.run_optimization()
74
+ results = test_dataset.check_run()
75
+ print(results)
76
+ ```
77
+
78
+
79
+ Object detection example:
80
+
81
+ ```
82
+ from hirundo.dataset_optimization import OptimizationDataset
83
+ from hirundo.enum import LabellingType
84
+ from hirundo.storage import StorageIntegration, StorageLink, StorageTypes
85
+
86
+ test_dataset = OptimizationDataset(
87
+ name=f"TEST-HuggingFace-BDD-100k-validation-OD-validation-dataset{unique_id}",
88
+ labelling_type=LabellingType.ObjectDetection,
89
+ dataset_storage=StorageLink(
90
+ storage_integration=StorageIntegration(
91
+ name=f"BDD-100k-validation-dataset{unique_id}",
92
+ type=StorageTypes.GIT,
93
+ git=StorageGit(
94
+ repo=GitRepo(
95
+ name=f"BDD-100k-validation-dataset{unique_id}",
96
+ repository_url="https://git@hf.co/datasets/hirundo-io/bdd100k-validation-only",
97
+ ),
98
+ branch="main",
99
+ ),
100
+ ),
101
+ path="/BDD100K Val from Hirundo.zip/bdd100k",
102
+ ),
103
+ dataset_metadata_path="bdd100k.csv",
104
+ )
105
+
106
+ test_dataset.run_optimization()
107
+ results = test_dataset.check_run()
108
+ print(results)
109
+ ```
110
+
111
+ Note: Currently we only support the main CPython release 3.9, 3.10 and 3.11. PyPy support may be introduced in the future.
112
+
113
+ ## Further documentation
114
+
115
+ To learn about mroe how to use this library, please visit the [http://docs.hirundo.io/](documentation) or see the Google Colab examples.
@@ -32,4 +32,4 @@ __all__ = [
32
32
  "StorageIntegration",
33
33
  ]
34
34
 
35
- __version__ = "0.1.7"
35
+ __version__ = "0.1.8"
@@ -0,0 +1,26 @@
1
+ import enum
2
+ import os
3
+ from pathlib import Path
4
+
5
+ from dotenv import load_dotenv
6
+
7
+
8
+ class EnvLocation(enum.Enum):
9
+ DOTENV = Path.cwd() / ".env"
10
+ HOME = Path.home() / ".hirundo.conf"
11
+
12
+
13
+ if os.path.exists(EnvLocation.DOTENV.value):
14
+ load_dotenv(EnvLocation.DOTENV.value)
15
+ elif os.path.exists(EnvLocation.HOME.value):
16
+ load_dotenv(EnvLocation.HOME.value)
17
+
18
+ API_HOST = os.getenv("API_HOST", "https://api.hirundo.io")
19
+ API_KEY = os.getenv("API_KEY")
20
+
21
+
22
+ def check_api_key():
23
+ if not API_KEY:
24
+ raise ValueError(
25
+ "API_KEY is not set. Please run `hirundo setup` to set the API key"
26
+ )
@@ -0,0 +1,14 @@
1
+ from requests import Response
2
+
3
+ import hirundo.logger
4
+
5
+ logger = hirundo.logger.get_logger(__name__)
6
+
7
+
8
+ def raise_for_status_with_reason(response: Response):
9
+ try:
10
+ response.reason = response.json().get("reason", None)
11
+ except Exception as e:
12
+ logger.debug("Failed to parse response as JSON: %s", e)
13
+
14
+ response.raise_for_status()
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import time
3
+ import typing
3
4
  from collections.abc import AsyncGenerator, Generator
4
- from typing import Union
5
5
 
6
6
  import httpx
7
7
  from httpx_sse import ServerSentEvent, aconnect_sse, connect_sse
@@ -13,7 +13,7 @@ def iter_sse_retrying(
13
13
  client: httpx.Client,
14
14
  method: str,
15
15
  url: str,
16
- headers: Union[dict[str, str], None] = None,
16
+ headers: typing.Optional[dict[str, str]] = None,
17
17
  ) -> Generator[ServerSentEvent, None, None]:
18
18
  if headers is None:
19
19
  headers = {}
@@ -1,11 +1,14 @@
1
+ import os
1
2
  import re
2
3
  import sys
4
+ import typing
5
+ from pathlib import Path
3
6
  from typing import Annotated
4
7
  from urllib.parse import urlparse
5
8
 
6
9
  import typer
7
10
 
8
- from hirundo._env import API_HOST
11
+ from hirundo._env import API_HOST, EnvLocation
9
12
 
10
13
  docs = "sphinx" in sys.modules
11
14
  hirundo_epilog = (
@@ -23,7 +26,9 @@ app = typer.Typer(
23
26
  )
24
27
 
25
28
 
26
- def upsert_env(var_name: str, var_value: str):
29
+ def _upsert_env(
30
+ dotenv_filepath: typing.Union[str, Path], var_name: str, var_value: str
31
+ ):
27
32
  """
28
33
  Change an environment variable in the .env file.
29
34
  If the variable does not exist, it will be added.
@@ -32,18 +37,30 @@ def upsert_env(var_name: str, var_value: str):
32
37
  var_name: The name of the environment variable to change.
33
38
  var_value: The new value of the environment variable.
34
39
  """
35
- dotenv = "./.env"
36
40
  regex = re.compile(rf"^{var_name}=.*$")
37
- with open(dotenv) as f:
38
- lines = f.readlines()
41
+ lines = []
42
+ if os.path.exists(dotenv_filepath):
43
+ with open(dotenv_filepath) as f:
44
+ lines = f.readlines()
39
45
 
40
- with open(dotenv, "w") as f:
46
+ with open(dotenv_filepath, "w") as f:
41
47
  f.writelines(line for line in lines if not regex.search(line) and line != "\n")
42
48
 
43
- with open(dotenv, "a") as f:
49
+ with open(dotenv_filepath, "a") as f:
44
50
  f.writelines(f"\n{var_name}={var_value}")
45
51
 
46
52
 
53
+ def upsert_env(var_name: str, var_value: str):
54
+ if os.path.exists(EnvLocation.DOTENV.value):
55
+ # If a `.env` file exists, re-use it
56
+ _upsert_env(EnvLocation.DOTENV.value, var_name, var_value)
57
+ return EnvLocation.DOTENV.name
58
+ else:
59
+ # Create a `.hirundo.conf` file with environment variables in the home directory
60
+ _upsert_env(EnvLocation.HOME.value, var_name, var_value)
61
+ return EnvLocation.HOME.name
62
+
63
+
47
64
  def fix_api_host(api_host: str):
48
65
  if not api_host.startswith("http") and not api_host.startswith("https"):
49
66
  api_host = f"https://{api_host}"
@@ -72,8 +89,15 @@ def setup_api_key(
72
89
  Setup the API key for the Hirundo client library.
73
90
  Values are saved to a .env file in the current directory for use by the library in requests.
74
91
  """
75
- upsert_env("API_KEY", api_key)
76
- print("API key saved to .env for future use. Please do not share the .env file")
92
+ saved_to = upsert_env("API_KEY", api_key)
93
+ if saved_to == EnvLocation.HOME.name:
94
+ print(
95
+ "API key saved to ~/.hirundo.conf for future use. Please do not share the ~/.hirundo.conf file since it contains your secret API key."
96
+ )
97
+ elif saved_to == EnvLocation.DOTENV.name:
98
+ print(
99
+ "API key saved to local .env file for future use. Please do not share the .env file since it contains your secret API key."
100
+ )
77
101
 
78
102
 
79
103
  @app.command("change-remote", epilog=hirundo_epilog)
@@ -94,8 +118,13 @@ def change_api_remote(
94
118
  """
95
119
  api_host = fix_api_host(api_host)
96
120
 
97
- upsert_env("API_HOST", api_host)
98
- print("API host saved to .env for future use. Please do not share this file")
121
+ saved_to = upsert_env("API_HOST", api_host)
122
+ if saved_to == EnvLocation.HOME.name:
123
+ print(
124
+ "API host saved to ~/.hirundo.conf for future use. Please do not share the ~/.hirundo.conf file"
125
+ )
126
+ elif saved_to == EnvLocation.DOTENV.name:
127
+ print("API host saved to .env for future use. Please do not share this file")
99
128
 
100
129
 
101
130
  @app.command("setup", epilog=hirundo_epilog)
@@ -123,11 +152,41 @@ def setup(
123
152
  Setup the Hirundo client library.
124
153
  """
125
154
  api_host = fix_api_host(api_host)
126
- upsert_env("API_HOST", api_host)
127
- upsert_env("API_KEY", api_key)
128
- print(
129
- "API host and API key saved to .env for future use. Please do not share this file"
130
- )
155
+ api_host_saved_to = upsert_env("API_HOST", api_host)
156
+ api_key_saved_to = upsert_env("API_KEY", api_key)
157
+ if api_host_saved_to != api_key_saved_to:
158
+ print(
159
+ "API host and API key saved to different locations. This should not happen. Please report this issue."
160
+ )
161
+ if (
162
+ api_host_saved_to == EnvLocation.HOME.name
163
+ and api_key_saved_to == EnvLocation.DOTENV.name
164
+ ):
165
+ print(
166
+ "API host saved to ~/.hirundo.conf for future use. Please do not share the ~/.hirundo.conf file"
167
+ )
168
+ print(
169
+ "API key saved to local .env file for future use. Please do not share the .env file since it contains your secret API key."
170
+ )
171
+ elif (
172
+ api_host_saved_to == EnvLocation.DOTENV.name
173
+ and api_key_saved_to == EnvLocation.HOME.name
174
+ ):
175
+ print(
176
+ "API host saved to .env for future use. Please do not share this file"
177
+ )
178
+ print(
179
+ "API key saved to ~/.hirundo.conf for future use. Please do not share the ~/.hirundo.conf file since it contains your secret API key."
180
+ )
181
+ return
182
+ if api_host_saved_to == EnvLocation.HOME.name:
183
+ print(
184
+ "API host and API key saved to ~/.hirundo.conf for future use. Please do not share the ~/.hirundo.conf file since it contains your secret API key."
185
+ )
186
+ elif api_host_saved_to == EnvLocation.DOTENV.name:
187
+ print(
188
+ "API host and API key saved to .env for future use. Please do not share this file since it contains your secret API key."
189
+ )
131
190
 
132
191
 
133
192
  typer_click_object = typer.main.get_command(app)