wmill 1.504.0__tar.gz → 1.505.1__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wmill
3
- Version: 1.504.0
3
+ Version: 1.505.1
4
4
  Summary: A client library for accessing Windmill server wrapping the Windmill client API
5
5
  Home-page: https://windmill.dev
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "wmill"
3
- version = "1.504.0"
3
+ version = "1.505.1"
4
4
  description = "A client library for accessing Windmill server wrapping the Windmill client API"
5
5
  license = "Apache-2.0"
6
6
  homepage = "https://windmill.dev"
@@ -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