sunholo 0.81.0__py3-none-any.whl → 0.81.2__py3-none-any.whl
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.
- sunholo/auth/refresh.py +15 -11
- sunholo/gcs/add_file.py +1 -1
- sunholo/gcs/download_url.py +44 -24
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/METADATA +2 -2
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/RECORD +9 -9
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/WHEEL +0 -0
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/entry_points.txt +0 -0
- {sunholo-0.81.0.dist-info → sunholo-0.81.2.dist-info}/top_level.txt +0 -0
sunholo/auth/refresh.py
CHANGED
|
@@ -4,17 +4,17 @@ import os
|
|
|
4
4
|
import google.auth
|
|
5
5
|
from google.auth.transport import requests
|
|
6
6
|
from ..utils.gcp import is_running_on_gcp
|
|
7
|
-
|
|
8
|
-
|
|
9
7
|
from ..custom_logging import log
|
|
10
8
|
|
|
11
9
|
def get_default_email():
|
|
12
|
-
|
|
13
|
-
log.error("Could not refresh the credentials properly.")
|
|
14
|
-
return None
|
|
10
|
+
|
|
15
11
|
# https://stackoverflow.com/questions/64234214/how-to-generate-a-blob-signed-url-in-google-cloud-run
|
|
16
12
|
|
|
17
|
-
gcs_credentials, project_id =
|
|
13
|
+
gcs_credentials, project_id = refresh_credentials()
|
|
14
|
+
|
|
15
|
+
if gcs_credentials is None:
|
|
16
|
+
log.error("Could not refresh the credentials properly.")
|
|
17
|
+
return None
|
|
18
18
|
|
|
19
19
|
service_account_email = getattr(gcs_credentials, 'service_account_email', None)
|
|
20
20
|
# If you use a service account credential, you can use the embedded email
|
|
@@ -26,7 +26,7 @@ def get_default_email():
|
|
|
26
26
|
return None
|
|
27
27
|
|
|
28
28
|
log.info(f"Found default email: {service_account_email=} for {project_id=}")
|
|
29
|
-
return service_account_email
|
|
29
|
+
return service_account_email, gcs_credentials
|
|
30
30
|
|
|
31
31
|
def get_default_creds():
|
|
32
32
|
gcs_credentials = None
|
|
@@ -36,20 +36,24 @@ def get_default_creds():
|
|
|
36
36
|
return gcs_credentials, project_id
|
|
37
37
|
|
|
38
38
|
def refresh_credentials():
|
|
39
|
+
"""
|
|
40
|
+
Need to refresh to get a valid email/token for signing URLs from a default service account
|
|
41
|
+
"""
|
|
39
42
|
if not is_running_on_gcp():
|
|
40
43
|
log.debug("Not running on Google Cloud so no credentials available for GCS.")
|
|
41
|
-
return
|
|
44
|
+
return None, None
|
|
42
45
|
|
|
43
46
|
gcs_credentials, project_id = get_default_creds()
|
|
44
47
|
|
|
45
48
|
if not gcs_credentials.token or gcs_credentials.expired or not gcs_credentials.valid:
|
|
46
49
|
try:
|
|
47
|
-
|
|
50
|
+
r = requests.Request()
|
|
51
|
+
gcs_credentials.refresh(r)
|
|
48
52
|
|
|
49
|
-
return
|
|
53
|
+
return gcs_credentials, project_id
|
|
50
54
|
|
|
51
55
|
except Exception as e:
|
|
52
56
|
log.error(f"Failed to refresh gcs credentials: {e}")
|
|
53
57
|
|
|
54
|
-
return
|
|
58
|
+
return None, None
|
|
55
59
|
|
sunholo/gcs/add_file.py
CHANGED
sunholo/gcs/download_url.py
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional, Tuple, TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from PIL import Image
|
|
5
|
+
from google.cloud.storage.bucket import Bucket
|
|
6
|
+
|
|
1
7
|
import os
|
|
2
8
|
from urllib.parse import quote
|
|
3
9
|
from datetime import datetime, timedelta
|
|
@@ -6,25 +12,20 @@ from datetime import datetime, timedelta
|
|
|
6
12
|
from google.auth.exceptions import RefreshError
|
|
7
13
|
|
|
8
14
|
try:
|
|
9
|
-
from google.cloud import storage
|
|
15
|
+
from google.cloud import storage
|
|
10
16
|
except ImportError:
|
|
11
17
|
storage = None
|
|
12
18
|
|
|
13
19
|
from ..custom_logging import log
|
|
14
|
-
from ..
|
|
15
|
-
from ..auth.refresh import refresh_credentials, get_default_creds, get_default_email
|
|
20
|
+
from ..auth.refresh import refresh_credentials, get_default_email
|
|
16
21
|
from io import BytesIO
|
|
17
22
|
try:
|
|
18
23
|
from PIL import Image
|
|
19
24
|
except ImportError:
|
|
20
25
|
Image = None
|
|
21
26
|
|
|
22
|
-
gcs_credentials = None
|
|
23
|
-
project_id = None
|
|
24
|
-
gcs_client = None
|
|
25
|
-
gcs_bucket_cache = {}
|
|
26
27
|
|
|
27
|
-
def get_image_from_gcs(gs_uri: str):
|
|
28
|
+
def get_image_from_gcs(gs_uri: str) -> Image.Image: # type: ignore
|
|
28
29
|
"""Converts image bytes from GCS to a PIL Image object."""
|
|
29
30
|
image_bytes = get_bytes_from_gcs(gs_uri)
|
|
30
31
|
if not Image:
|
|
@@ -35,7 +36,7 @@ def get_image_from_gcs(gs_uri: str):
|
|
|
35
36
|
except IOError as e:
|
|
36
37
|
raise ValueError("Unable to open image from bytes:", e)
|
|
37
38
|
|
|
38
|
-
def get_bytes_from_gcs(gs_uri):
|
|
39
|
+
def get_bytes_from_gcs(gs_uri) -> Optional[bytes]:
|
|
39
40
|
"""
|
|
40
41
|
Downloads a file from Google Cloud Storage and returns its bytes.
|
|
41
42
|
|
|
@@ -73,22 +74,39 @@ def get_bytes_from_gcs(gs_uri):
|
|
|
73
74
|
log.error(f"Error downloading file from GCS: {str(err)}")
|
|
74
75
|
return None
|
|
75
76
|
|
|
77
|
+
gcs_bucket_cache = {}
|
|
78
|
+
def get_bucket(bucket_name: str) -> Optional[Bucket]:
|
|
79
|
+
"""
|
|
80
|
+
Gets a Cloud Storage bucket and initialised GCS client
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
bucket_name: Name of the bucket
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# Prepare global variables for client reuse
|
|
85
|
+
Returns:
|
|
86
|
+
|
|
87
|
+
"""
|
|
81
88
|
if storage:
|
|
82
89
|
gcs_client = storage.Client()
|
|
90
|
+
else:
|
|
91
|
+
raise ImportError("google storage pip required - install via `pip install sunholo[gcp]`")
|
|
83
92
|
|
|
84
|
-
def get_bucket(bucket_name):
|
|
85
93
|
if bucket_name not in gcs_bucket_cache:
|
|
86
94
|
gcs_bucket_cache[bucket_name] = gcs_client.get_bucket(bucket_name)
|
|
87
95
|
return gcs_bucket_cache[bucket_name]
|
|
88
96
|
|
|
89
|
-
def sign_gcs_url(bucket_name:str, object_name:str, expiry_secs = 86400):
|
|
97
|
+
def sign_gcs_url(bucket_name:str, object_name:str, expiry_secs:int = 86400) -> Optional[str]:
|
|
98
|
+
"""
|
|
99
|
+
Creates a signed URL so that users can download a file from Google Cloud Storage without authentication
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
bucket_name: Name of the bucket where the object lies
|
|
103
|
+
object_name: Object within the bucket
|
|
104
|
+
expiry_secs: How long the link will be valid - default 24hrs
|
|
90
105
|
|
|
91
|
-
|
|
106
|
+
Returns:
|
|
107
|
+
str: The signed URL or None if not avialable
|
|
108
|
+
"""
|
|
109
|
+
service_account_email, gcs_credentials = get_default_email()
|
|
92
110
|
|
|
93
111
|
expires = datetime.now() + timedelta(seconds=expiry_secs)
|
|
94
112
|
|
|
@@ -105,8 +123,8 @@ def sign_gcs_url(bucket_name:str, object_name:str, expiry_secs = 86400):
|
|
|
105
123
|
return url
|
|
106
124
|
except RefreshError:
|
|
107
125
|
log.info("Refreshing gcs_credentials due to token expiration.")
|
|
108
|
-
|
|
109
|
-
if
|
|
126
|
+
credentials, token = refresh_credentials()
|
|
127
|
+
if credentials:
|
|
110
128
|
return sign_gcs_url(bucket_name, object_name, expiry_secs)
|
|
111
129
|
log.error("Failed to refresh gcs credentials")
|
|
112
130
|
return None
|
|
@@ -115,7 +133,7 @@ def sign_gcs_url(bucket_name:str, object_name:str, expiry_secs = 86400):
|
|
|
115
133
|
return None
|
|
116
134
|
|
|
117
135
|
|
|
118
|
-
def construct_download_link(source_uri: str) ->
|
|
136
|
+
def construct_download_link(source_uri: str) -> Tuple[str, str, bool]:
|
|
119
137
|
"""Creates a viewable Cloud Storage web browser link from a gs:// URI."""
|
|
120
138
|
if not source_uri.startswith("gs://"):
|
|
121
139
|
return source_uri, source_uri, False # Return the URI as is if it doesn't start with gs://
|
|
@@ -132,7 +150,7 @@ def construct_download_link(source_uri: str) -> tuple[str, str, bool]:
|
|
|
132
150
|
return construct_download_link_simple(bucket_name, object_name)
|
|
133
151
|
|
|
134
152
|
|
|
135
|
-
def construct_download_link_simple(bucket_name:str, object_name:str) ->
|
|
153
|
+
def construct_download_link_simple(bucket_name:str, object_name:str) -> Tuple[str, str, bool]:
|
|
136
154
|
"""Creates a viewable Cloud Storage web browser link from a gs:// URI.
|
|
137
155
|
|
|
138
156
|
Args:
|
|
@@ -142,12 +160,14 @@ def construct_download_link_simple(bucket_name:str, object_name:str) -> tuple[st
|
|
|
142
160
|
A URL that directly access the object in the Cloud Storage web browser.
|
|
143
161
|
"""
|
|
144
162
|
|
|
145
|
-
|
|
163
|
+
if object_name.startswith("gs://"):
|
|
164
|
+
public_url = object_name.replace("gs://", "https://storage.cloud.google.com")
|
|
165
|
+
else:
|
|
166
|
+
public_url = f"https://storage.cloud.google.com/{bucket_name}/{quote(object_name)}"
|
|
146
167
|
filename = os.path.basename(object_name)
|
|
147
|
-
|
|
148
|
-
return public_url, filename, signed
|
|
168
|
+
return public_url, filename, False
|
|
149
169
|
|
|
150
|
-
def parse_gs_uri(gs_uri: str) ->
|
|
170
|
+
def parse_gs_uri(gs_uri: str) -> Tuple[str, str]:
|
|
151
171
|
"""Parses a gs:// URI into the bucket name and object name.
|
|
152
172
|
|
|
153
173
|
Args:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.81.
|
|
3
|
+
Version: 0.81.2
|
|
4
4
|
Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
|
|
5
5
|
Home-page: https://github.com/sunholo-data/sunholo-py
|
|
6
|
-
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.81.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.81.2.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -19,7 +19,7 @@ sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,3
|
|
|
19
19
|
sunholo/archive/archive.py,sha256=PxVfDtO2_2ZEEbnhXSCbXLdeoHoQVImo4y3Jr2XkCFY,1204
|
|
20
20
|
sunholo/auth/__init__.py,sha256=TeP-OY0XGxYV_8AQcVGoh35bvyWhNUcMRfhuD5l44Sk,91
|
|
21
21
|
sunholo/auth/gcloud.py,sha256=PdbwkuTdRi4RKBmgG9uwsReegqC4VG15_tw5uzmA7Fs,298
|
|
22
|
-
sunholo/auth/refresh.py,sha256=
|
|
22
|
+
sunholo/auth/refresh.py,sha256=6AEWX87G3I9BCqrgGJjHGrrWABBXHuaGDKU9ZEcVeXM,2017
|
|
23
23
|
sunholo/auth/run.py,sha256=zZWRIxfG93eZMCE-feiHRQTLMcHz4U6-yseGgZfu1LI,2776
|
|
24
24
|
sunholo/azure/__init__.py,sha256=S1WQ5jndzNgzhSBh9UpX_yw7hRVm3hCzkAWNxUdK4dA,48
|
|
25
25
|
sunholo/azure/auth.py,sha256=Y3fDqFLYwbsIyi5hS5L-3hYnwrLWVL96yPng5Sj5c2c,2236
|
|
@@ -78,9 +78,9 @@ sunholo/discovery_engine/get_ai_search_chunks.py,sha256=VPzdYoBP_E6Bko0KpX656QiI
|
|
|
78
78
|
sunholo/embedder/__init__.py,sha256=sI4N_CqgEVcrMDxXgxKp1FsfsB4FpjoXgPGkl4N_u4I,44
|
|
79
79
|
sunholo/embedder/embed_chunk.py,sha256=MCbTePWjUbIRVDFFhHJ94BvOZvIom62-mTr0PmfQyt0,6951
|
|
80
80
|
sunholo/gcs/__init__.py,sha256=SZvbsMFDko40sIRHTHppA37IijvJTae54vrhooEF5-4,90
|
|
81
|
-
sunholo/gcs/add_file.py,sha256=
|
|
81
|
+
sunholo/gcs/add_file.py,sha256=wkBQBfnjUbItnRNGiG9oBr7Jf2QfLpZf2nA5zT435ss,7107
|
|
82
82
|
sunholo/gcs/download_folder.py,sha256=ijJTnS595JqZhBH8iHFErQilMbkuKgL-bnTCMLGuvlA,1614
|
|
83
|
-
sunholo/gcs/download_url.py,sha256=
|
|
83
|
+
sunholo/gcs/download_url.py,sha256=Al7IbNXiaZ-zV09GHhUE9hx013lghJtE9nW8SJx_sas,5938
|
|
84
84
|
sunholo/gcs/metadata.py,sha256=oQLcXi4brsZ74aegWyC1JZmhlaEV270HS5_UWtAYYWE,898
|
|
85
85
|
sunholo/invoke/__init__.py,sha256=bELcqIjzKvaupcIN5OQmDgGx_8jARtH9T6PCe8UgcvE,99
|
|
86
86
|
sunholo/invoke/direct_vac_func.py,sha256=wvrYDZNLoLeO_uQiqRdGUlhwjhLr05dVNBST9TxwwBA,4478
|
|
@@ -134,9 +134,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
|
|
|
134
134
|
sunholo/vertex/memory_tools.py,sha256=pgSahVDh7GPEulu3nl-w0jb5lTClb4TCnVxPnMokNZY,7533
|
|
135
135
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
|
136
136
|
sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
|
|
137
|
-
sunholo-0.81.
|
|
138
|
-
sunholo-0.81.
|
|
139
|
-
sunholo-0.81.
|
|
140
|
-
sunholo-0.81.
|
|
141
|
-
sunholo-0.81.
|
|
142
|
-
sunholo-0.81.
|
|
137
|
+
sunholo-0.81.2.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
138
|
+
sunholo-0.81.2.dist-info/METADATA,sha256=qIVU2f03S9E678dvZfzckg3hw8bkVTGQ-8ytTlesoek,7348
|
|
139
|
+
sunholo-0.81.2.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
140
|
+
sunholo-0.81.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
141
|
+
sunholo-0.81.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
142
|
+
sunholo-0.81.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|