wmill 1.503.3__tar.gz → 1.505.0__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.
Potentially problematic release.
This version of wmill might be problematic. Click here for more details.
- {wmill-1.503.3 → wmill-1.505.0}/PKG-INFO +1 -1
- {wmill-1.503.3 → wmill-1.505.0}/pyproject.toml +1 -1
- {wmill-1.503.3 → wmill-1.505.0}/wmill/client.py +51 -12
- {wmill-1.503.3 → wmill-1.505.0}/README.md +0 -0
- {wmill-1.503.3 → wmill-1.505.0}/wmill/__init__.py +0 -0
- {wmill-1.503.3 → wmill-1.505.0}/wmill/py.typed +0 -0
- {wmill-1.503.3 → wmill-1.505.0}/wmill/s3_reader.py +0 -0
- {wmill-1.503.3 → wmill-1.505.0}/wmill/s3_types.py +0 -0
|
@@ -11,7 +11,8 @@ import time
|
|
|
11
11
|
import warnings
|
|
12
12
|
import json
|
|
13
13
|
from json import JSONDecodeError
|
|
14
|
-
from typing import Dict, Any, Union, Literal
|
|
14
|
+
from typing import Dict, Any, Union, Literal, Optional
|
|
15
|
+
import re
|
|
15
16
|
|
|
16
17
|
import httpx
|
|
17
18
|
|
|
@@ -312,6 +313,7 @@ class Windmill:
|
|
|
312
313
|
return result_text
|
|
313
314
|
|
|
314
315
|
def get_variable(self, path: str) -> str:
|
|
316
|
+
path = parse_variable_syntax(path) or path
|
|
315
317
|
if self.mocked_api is not None:
|
|
316
318
|
variables = self.mocked_api["variables"]
|
|
317
319
|
try:
|
|
@@ -326,6 +328,7 @@ class Windmill:
|
|
|
326
328
|
return self.get(f"/w/{self.workspace}/variables/get_value/{path}").json()
|
|
327
329
|
|
|
328
330
|
def set_variable(self, path: str, value: str, is_secret: bool = False) -> None:
|
|
331
|
+
path = parse_variable_syntax(path) or path
|
|
329
332
|
if self.mocked_api is not None:
|
|
330
333
|
self.mocked_api["variables"][path] = value
|
|
331
334
|
return
|
|
@@ -358,6 +361,7 @@ class Windmill:
|
|
|
358
361
|
path: str,
|
|
359
362
|
none_if_undefined: bool = False,
|
|
360
363
|
) -> dict | None:
|
|
364
|
+
path = parse_resource_syntax(path) or path
|
|
361
365
|
if self.mocked_api is not None:
|
|
362
366
|
resources = self.mocked_api["resources"]
|
|
363
367
|
try:
|
|
@@ -391,6 +395,7 @@ class Windmill:
|
|
|
391
395
|
path: str,
|
|
392
396
|
resource_type: str,
|
|
393
397
|
):
|
|
398
|
+
path = parse_resource_syntax(path) or path
|
|
394
399
|
if self.mocked_api is not None:
|
|
395
400
|
self.mocked_api["resources"][path] = value
|
|
396
401
|
return
|
|
@@ -485,6 +490,7 @@ class Windmill:
|
|
|
485
490
|
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
|
|
486
491
|
initiate an S3 connection from DuckDB
|
|
487
492
|
"""
|
|
493
|
+
s3_resource_path = parse_resource_syntax(s3_resource_path) or s3_resource_path
|
|
488
494
|
try:
|
|
489
495
|
raw_obj = self.post(
|
|
490
496
|
f"/w/{self.workspace}/job_helpers/v2/duckdb_connection_settings",
|
|
@@ -506,6 +512,7 @@ class Windmill:
|
|
|
506
512
|
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
|
|
507
513
|
initiate an S3 connection from Polars
|
|
508
514
|
"""
|
|
515
|
+
s3_resource_path = parse_resource_syntax(s3_resource_path) or s3_resource_path
|
|
509
516
|
try:
|
|
510
517
|
raw_obj = self.post(
|
|
511
518
|
f"/w/{self.workspace}/job_helpers/v2/polars_connection_settings",
|
|
@@ -527,6 +534,7 @@ class Windmill:
|
|
|
527
534
|
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
|
|
528
535
|
initiate an S3 connection using boto3
|
|
529
536
|
"""
|
|
537
|
+
s3_resource_path = parse_resource_syntax(s3_resource_path) or s3_resource_path
|
|
530
538
|
try:
|
|
531
539
|
s3_resource = self.post(
|
|
532
540
|
f"/w/{self.workspace}/job_helpers/v2/s3_resource_info",
|
|
@@ -540,7 +548,7 @@ class Windmill:
|
|
|
540
548
|
"Could not generate Boto3 S3 connection settings from the provided resource"
|
|
541
549
|
) from e
|
|
542
550
|
|
|
543
|
-
def load_s3_file(self, s3object: S3Object, s3_resource_path: str | None) -> bytes:
|
|
551
|
+
def load_s3_file(self, s3object: S3Object | str, s3_resource_path: str | None) -> bytes:
|
|
544
552
|
"""
|
|
545
553
|
Load a file from the workspace s3 bucket and returns its content as bytes.
|
|
546
554
|
|
|
@@ -552,11 +560,12 @@ class Windmill:
|
|
|
552
560
|
file_content = my_obj_content.decode("utf-8")
|
|
553
561
|
'''
|
|
554
562
|
"""
|
|
563
|
+
s3object = parse_s3_object(s3object)
|
|
555
564
|
with self.load_s3_file_reader(s3object, s3_resource_path) as file_reader:
|
|
556
565
|
return file_reader.read()
|
|
557
566
|
|
|
558
567
|
def load_s3_file_reader(
|
|
559
|
-
self, s3object: S3Object, s3_resource_path: str | None
|
|
568
|
+
self, s3object: S3Object | str, s3_resource_path: str | None
|
|
560
569
|
) -> BufferedReader:
|
|
561
570
|
"""
|
|
562
571
|
Load a file from the workspace s3 bucket and returns the bytes stream.
|
|
@@ -569,6 +578,7 @@ class Windmill:
|
|
|
569
578
|
print(file_reader.read())
|
|
570
579
|
'''
|
|
571
580
|
"""
|
|
581
|
+
s3object = parse_s3_object(s3object)
|
|
572
582
|
reader = S3BufferedReader(
|
|
573
583
|
f"{self.workspace}",
|
|
574
584
|
self.client,
|
|
@@ -580,7 +590,7 @@ class Windmill:
|
|
|
580
590
|
|
|
581
591
|
def write_s3_file(
|
|
582
592
|
self,
|
|
583
|
-
s3object: S3Object | None,
|
|
593
|
+
s3object: S3Object | str | None,
|
|
584
594
|
file_content: BufferedReader | bytes,
|
|
585
595
|
s3_resource_path: str | None,
|
|
586
596
|
content_type: str | None = None,
|
|
@@ -603,6 +613,7 @@ class Windmill:
|
|
|
603
613
|
client.write_s3_file(s3_obj, my_file)
|
|
604
614
|
'''
|
|
605
615
|
"""
|
|
616
|
+
s3object = parse_s3_object(s3object)
|
|
606
617
|
# httpx accepts either bytes or "a bytes generator" as content. If it's a BufferedReader, we need to convert it to a generator
|
|
607
618
|
if isinstance(file_content, BufferedReader):
|
|
608
619
|
content_payload = bytes_generator(file_content)
|
|
@@ -644,12 +655,12 @@ class Windmill:
|
|
|
644
655
|
raise Exception("Could not write file to S3") from e
|
|
645
656
|
return S3Object(s3=response["file_key"])
|
|
646
657
|
|
|
647
|
-
def sign_s3_objects(self, s3_objects: list[S3Object]) -> list[S3Object]:
|
|
658
|
+
def sign_s3_objects(self, s3_objects: list[S3Object | str]) -> list[S3Object]:
|
|
648
659
|
return self.post(
|
|
649
|
-
f"/w/{self.workspace}/apps/sign_s3_objects", json={"s3_objects": s3_objects}
|
|
660
|
+
f"/w/{self.workspace}/apps/sign_s3_objects", json={"s3_objects": list(map(parse_s3_object, s3_objects))}
|
|
650
661
|
).json()
|
|
651
662
|
|
|
652
|
-
def sign_s3_object(self, s3_object: S3Object) -> S3Object:
|
|
663
|
+
def sign_s3_object(self, s3_object: S3Object | str) -> S3Object:
|
|
653
664
|
return self.post(
|
|
654
665
|
f"/w/{self.workspace}/apps/sign_s3_objects",
|
|
655
666
|
json={"s3_objects": [s3_object]},
|
|
@@ -1027,7 +1038,7 @@ def boto3_connection_settings(s3_resource_path: str = "") -> Boto3ConnectionSett
|
|
|
1027
1038
|
|
|
1028
1039
|
|
|
1029
1040
|
@init_global_client
|
|
1030
|
-
def load_s3_file(s3object: S3Object, s3_resource_path: str | None = None) -> bytes:
|
|
1041
|
+
def load_s3_file(s3object: S3Object | str, s3_resource_path: str | None = None) -> bytes:
|
|
1031
1042
|
"""
|
|
1032
1043
|
Load the entire content of a file stored in S3 as bytes
|
|
1033
1044
|
"""
|
|
@@ -1038,7 +1049,7 @@ def load_s3_file(s3object: S3Object, s3_resource_path: str | None = None) -> byt
|
|
|
1038
1049
|
|
|
1039
1050
|
@init_global_client
|
|
1040
1051
|
def load_s3_file_reader(
|
|
1041
|
-
s3object: S3Object, s3_resource_path: str | None = None
|
|
1052
|
+
s3object: S3Object | str, s3_resource_path: str | None = None
|
|
1042
1053
|
) -> BufferedReader:
|
|
1043
1054
|
"""
|
|
1044
1055
|
Load the content of a file stored in S3
|
|
@@ -1050,7 +1061,7 @@ def load_s3_file_reader(
|
|
|
1050
1061
|
|
|
1051
1062
|
@init_global_client
|
|
1052
1063
|
def write_s3_file(
|
|
1053
|
-
s3object: S3Object | None,
|
|
1064
|
+
s3object: S3Object | str | None,
|
|
1054
1065
|
file_content: BufferedReader | bytes,
|
|
1055
1066
|
s3_resource_path: str | None = None,
|
|
1056
1067
|
content_type: str | None = None,
|
|
@@ -1075,7 +1086,7 @@ def write_s3_file(
|
|
|
1075
1086
|
|
|
1076
1087
|
|
|
1077
1088
|
@init_global_client
|
|
1078
|
-
def sign_s3_objects(s3_objects: list[S3Object]) -> list[S3Object]:
|
|
1089
|
+
def sign_s3_objects(s3_objects: list[S3Object | str]) -> list[S3Object]:
|
|
1079
1090
|
"""
|
|
1080
1091
|
Sign S3 objects to be used by anonymous users in public apps
|
|
1081
1092
|
Returns a list of signed s3 tokens
|
|
@@ -1084,7 +1095,7 @@ def sign_s3_objects(s3_objects: list[S3Object]) -> list[S3Object]:
|
|
|
1084
1095
|
|
|
1085
1096
|
|
|
1086
1097
|
@init_global_client
|
|
1087
|
-
def sign_s3_object(s3_object: S3Object) -> S3Object:
|
|
1098
|
+
def sign_s3_object(s3_object: S3Object| str) -> S3Object:
|
|
1088
1099
|
"""
|
|
1089
1100
|
Sign S3 object to be used by anonymous users in public apps
|
|
1090
1101
|
Returns a signed s3 object
|
|
@@ -1336,3 +1347,31 @@ def task(*args, **kwargs):
|
|
|
1336
1347
|
return f(args[0], None)
|
|
1337
1348
|
else:
|
|
1338
1349
|
return lambda x: f(x, kwargs.get("tag"))
|
|
1350
|
+
|
|
1351
|
+
def parse_resource_syntax(s: str) -> Optional[str]:
|
|
1352
|
+
"""Parse resource syntax from string."""
|
|
1353
|
+
if s is None:
|
|
1354
|
+
return None
|
|
1355
|
+
if s.startswith("$res:"):
|
|
1356
|
+
return s[5:]
|
|
1357
|
+
if s.startswith("res://"):
|
|
1358
|
+
return s[6:]
|
|
1359
|
+
return None
|
|
1360
|
+
|
|
1361
|
+
def parse_s3_object(s3_object: S3Object | str) -> S3Object:
|
|
1362
|
+
"""Parse S3 object from string or S3Object format."""
|
|
1363
|
+
if isinstance(s3_object, str):
|
|
1364
|
+
match = re.match(r'^s3://([^/]*)/(.*)$', s3_object)
|
|
1365
|
+
if match:
|
|
1366
|
+
return S3Object(s3=match.group(2) or "", storage=match.group(1) or None)
|
|
1367
|
+
return S3Object(s3="")
|
|
1368
|
+
else:
|
|
1369
|
+
return s3_object
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
|
|
1373
|
+
def parse_variable_syntax(s: str) -> Optional[str]:
|
|
1374
|
+
"""Parse variable syntax from string."""
|
|
1375
|
+
if s.startswith("var://"):
|
|
1376
|
+
return s[6:]
|
|
1377
|
+
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|