UncountablePythonSDK 0.0.176__py3-none-any.whl → 0.0.177__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.
docs/conf.py CHANGED
@@ -7,6 +7,8 @@
7
7
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
8
 
9
9
  import datetime
10
+ import importlib
11
+ import inspect
10
12
 
11
13
  from docutils import nodes # type: ignore[import-untyped]
12
14
  from sphinx.addnodes import pending_xref # type: ignore[import-not-found]
@@ -63,6 +65,20 @@ favicons = [
63
65
  ]
64
66
 
65
67
 
68
+ def _resolve_alias_to_real_module(target_module: str) -> str:
69
+ if "." not in target_module:
70
+ return target_module
71
+ parent_name, attr_name = target_module.rsplit(".", 1)
72
+ try:
73
+ parent = importlib.import_module(parent_name)
74
+ except ImportError:
75
+ return target_module
76
+ aliased = getattr(parent, attr_name, None)
77
+ if not inspect.ismodule(aliased) or aliased.__name__ == target_module:
78
+ return target_module
79
+ return aliased.__name__
80
+
81
+
66
82
  def _hook_missing_reference(
67
83
  _app: Sphinx, _env: BuildEnvironment, node: pending_xref, contnode: nodes.Text
68
84
  ) -> nodes.reference | None:
@@ -80,6 +96,10 @@ def _hook_missing_reference(
80
96
  return None
81
97
 
82
98
  target_module, target_name = target.rsplit(".", 1)
99
+ real_module = _resolve_alias_to_real_module(target_module)
100
+ if real_module != target_module:
101
+ target_module = real_module
102
+ target = f"{target_module}.{target_name}"
83
103
 
84
104
  # construct relative path from current doc page to target page
85
105
  relative_segments_to_root = [".." for _ in current_doc.split("/")]
@@ -17,6 +17,7 @@ from uncountable.types import uploader_t
17
17
  from .types import AuthDetailsAll, AuthDetailsApiKey
18
18
 
19
19
  _CHUNK_SIZE = 5 * 1024 * 1024 # s3 requires 5MiB minimum
20
+ _PARSED_FILE_DATA_FILENAME = "parsed_file_data.json"
20
21
 
21
22
 
22
23
  class FileUploadType(StrEnum):
@@ -51,17 +52,27 @@ class UploaderFileUpload:
51
52
  FileUpload = MediaFileUpload | DataFileUpload | UploaderFileUpload
52
53
 
53
54
 
54
- @dataclass(kw_only=True)
55
- class _ParsedFileDataWrapper:
56
- parsed_file_data: list[uploader_t.ParsedFileData]
57
-
58
-
59
55
  @dataclass(kw_only=True)
60
56
  class FileBytes:
61
57
  name: str
62
58
  bytes_data: BytesIO
63
59
 
64
60
 
61
+ def _build_parsed_file_data_bytes(
62
+ *, parsed_file_data: list[uploader_t.ParsedFileData]
63
+ ) -> FileBytes:
64
+ buffer = BytesIO()
65
+ buffer.write(b'{"parsed_file_data": [')
66
+ for index, file_data in enumerate(parsed_file_data):
67
+ if index > 0:
68
+ buffer.write(b", ")
69
+ serialized_file_data = serialize_for_storage(file_data)
70
+ buffer.write(json.dumps(serialized_file_data).encode("utf-8"))
71
+ buffer.write(b"]}")
72
+ buffer.seek(0)
73
+ return FileBytes(name=_PARSED_FILE_DATA_FILENAME, bytes_data=buffer)
74
+
75
+
65
76
  @contextmanager
66
77
  def file_upload_data(file_upload: FileUpload) -> Generator[FileBytes, None, None]:
67
78
  match file_upload:
@@ -73,15 +84,9 @@ def file_upload_data(file_upload: FileUpload) -> Generator[FileBytes, None, None
73
84
  case DataFileUpload():
74
85
  yield FileBytes(name=file_upload.name, bytes_data=file_upload.data)
75
86
  case UploaderFileUpload():
76
- wrapper = _ParsedFileDataWrapper(
87
+ yield _build_parsed_file_data_bytes(
77
88
  parsed_file_data=file_upload.parsed_file_data
78
89
  )
79
- serialized = serialize_for_storage(wrapper)
80
- json_bytes = json.dumps(serialized).encode("utf-8")
81
- yield FileBytes(
82
- name="parsed_file_data.json",
83
- bytes_data=BytesIO(json_bytes),
84
- )
85
90
 
86
91
 
87
92
  @dataclass(kw_only=True)
@@ -135,7 +140,7 @@ class FileUploader:
135
140
  case DataFileUpload():
136
141
  attributes["file_name"] = file_upload.name
137
142
  case UploaderFileUpload():
138
- attributes["file_name"] = "parsed_file_data.json"
143
+ attributes["file_name"] = _PARSED_FILE_DATA_FILENAME
139
144
  case _:
140
145
  assert_never(file_upload)
141
146
  with push_scope_optional(
@@ -13,6 +13,7 @@ from uncountable.core.query.column import (
13
13
  Column,
14
14
  ColumnFilter,
15
15
  EnumColumn,
16
+ JsonColumn,
16
17
  NullableColumn,
17
18
  PrimaryKeyColumn,
18
19
  get_column_base,
@@ -72,8 +73,16 @@ class QueryBuilder[QueryRowT: QueryRow]:
72
73
  column.identifier for column in column_definitions.values()
73
74
  )
74
75
  self._property_names: tuple[str, ...] = tuple(column_definitions.keys())
76
+ json_property_names = {
77
+ name
78
+ for name, column in column_definitions.items()
79
+ if isinstance(column, JsonColumn)
80
+ }
75
81
  self._parser: CachedParser = CachedParser(
76
- serial_class(unconverted_keys={*self._property_names})(
82
+ serial_class(
83
+ unconverted_keys={*self._property_names},
84
+ unconverted_values=json_property_names,
85
+ )(
77
86
  make_dataclass(
78
87
  cls_name=self._model.__name__,
79
88
  fields=[
@@ -465,6 +465,27 @@ class NullableTextsColumn[
465
465
  # IMPROVE: Implement "Not Exists" filter (when supported in SDK)
466
466
 
467
467
 
468
+ class JsonColumn(Column[base_t.JsonValue, NullableColumn]):
469
+ __hash__ = Column.__hash__
470
+
471
+ def __init__(self, identifier: listing_t.ColumnIdentifier) -> None:
472
+ super().__init__(
473
+ identifier=identifier,
474
+ nullable=NullableColumn,
475
+ value_type=(
476
+ dict[str, base_t.JsonValue] | list[base_t.JsonValue] | base_t.JsonScalar
477
+ ),
478
+ )
479
+
480
+ def __ne__(self, other: None) -> ColumnFilter: # type: ignore[override]
481
+ return ColumnFilter(
482
+ identifier=self.identifier,
483
+ filter=lambda identifier: listing_t.FilterSpecExists(column=identifier),
484
+ )
485
+
486
+ # IMPROVE: Implement "Not Exists" filter (when supported in SDK)
487
+
488
+
468
489
  class PrimaryKeyColumn(
469
490
  Column[base_t.ObjectId, NonNullableColumn],
470
491
  ):
@@ -684,6 +705,10 @@ class IdColumn[
684
705
  column: NullableTextsColumn[BaseNullableT],
685
706
  ) -> NullableTextsColumn[BaseNullableT]: ...
686
707
 
708
+ # Json
709
+ @overload
710
+ def __rshift__(self, column: JsonColumn) -> JsonColumn: ...
711
+
687
712
  # Ids
688
713
  @overload
689
714
  def __rshift__[
@@ -734,6 +759,7 @@ class IdColumn[
734
759
  | NumericColumn[BaseNullableT]
735
760
  | TextsColumn[BaseNullableT]
736
761
  | NullableTextsColumn[BaseNullableT]
762
+ | JsonColumn
737
763
  | IdsColumn[BaseNullableT]
738
764
  | IdColumn[BaseNullableT],
739
765
  ) -> (
@@ -768,6 +794,7 @@ class IdColumn[
768
794
  | NullableTextsColumn[BaseNullableT]
769
795
  | NullableTextsColumn[NonNullableColumn | NullableColumn]
770
796
  | NullableTextsColumn[NullableColumn]
797
+ | JsonColumn
771
798
  | IdsColumn[BaseNullableT]
772
799
  | IdsColumn[NonNullableColumn | NullableColumn]
773
800
  | IdsColumn[NullableColumn]
@@ -869,6 +896,8 @@ class IdColumn[
869
896
  if self.nullable is NonNullableColumn
870
897
  else NullableColumn,
871
898
  )
899
+ case JsonColumn():
900
+ return JsonColumn(identifier=transitive_identifier)
872
901
  case IdsColumn():
873
902
  return IdsColumn(
874
903
  id_type=column.id_type,
@@ -48,6 +48,7 @@ __all__: list[str] = [
48
48
  "condition_parameter_matches": "Condition Parameter Matches",
49
49
  "condition_parameter_rule": "Condition Parameter Rule",
50
50
  "condition_parameter_value": "Condition Parameter Value",
51
+ "config_transfer_export": "Config Transfer Export",
51
52
  "constraint": "Constraint",
52
53
  "constraint_set": "Constraint Set",
53
54
  "control_type": "Control Type",
@@ -299,6 +300,7 @@ class EntityType(StrEnum):
299
300
  CONDITION_PARAMETER_MATCHES = "condition_parameter_matches"
300
301
  CONDITION_PARAMETER_RULE = "condition_parameter_rule"
301
302
  CONDITION_PARAMETER_VALUE = "condition_parameter_value"
303
+ CONFIG_TRANSFER_EXPORT = "config_transfer_export"
302
304
  CONSTRAINT = "constraint"
303
305
  CONSTRAINT_SET = "constraint_set"
304
306
  CONTROL_TYPE = "control_type"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.176
3
+ Version: 0.0.177
4
4
  Summary: Uncountable SDK
5
5
  Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
6
6
  Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
@@ -1,6 +1,6 @@
1
1
  docs/.flowmarkignore,sha256=gmJziQDpI3zbfHFgM73BE9iaLn04Oqya_7QCjJ749o8,548
2
2
  docs/.gitignore,sha256=_ebkZUcwfvfnGEJ95rfj1lxoBNd6EE9ZvtOc7FsbfFE,7
3
- docs/conf.py,sha256=Ky-_Y76T7pwN2aBG-dSF79Av70e7ASgcOXEdQ1qyor4,3542
3
+ docs/conf.py,sha256=KivJNvQWboXqJ07NMgcYY5tsV6CoIfyTpZh8jdn_5Qk,4239
4
4
  docs/index.md,sha256=g4Yi5831fEkywYkkcFohYLkKzSI91SOZF7DxKsm9zgI,3193
5
5
  docs/justfile,sha256=WymCEQ6W2A8Ak79iUPmecmuaUNN2htb7STUrz5K7ELE,273
6
6
  docs/requirements.txt,sha256=8aqtVaNV4AlzUZ7nAM8kxOF1ccAcujhQ_ayU9Whksf8,172
@@ -120,11 +120,11 @@ uncountable/core/__init__.py,sha256=RFv0kO6rKFf1PtBPu83hCGmxqkJamRtsgQ9_-ztw7tA,
120
120
  uncountable/core/async_batch.py,sha256=9pYGFzVCQXt8059qFHgutweGIFPquJ5Xfq6NT5P-1K0,1206
121
121
  uncountable/core/client.py,sha256=XSAPfcSU51d3MgAZOuT6FMrdrM452KQfWzNHjLOGSMY,16549
122
122
  uncountable/core/environment.py,sha256=Z9vu7JtnSDgQB_KKcZnjTFNyARXjRr_PDW9krwxNNAo,1132
123
- uncountable/core/file_upload.py,sha256=hvxz6lq1Hv4nK51bux8W_Tl22_UNBtd8U21OWteXWjI,5730
123
+ uncountable/core/file_upload.py,sha256=IisvIBdm2f4CJTRQ9Awi5JuI16PSERjXrzZgmrUiCHY,5960
124
124
  uncountable/core/types.py,sha256=s2CjqYJpsmbC7xMwxxT7kJ_V9bwokrjjWVVjpMcQpKI,333
125
125
  uncountable/core/query/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- uncountable/core/query/builder.py,sha256=bnsHdeV6Pe2gf2kpbVDtY8_cQmmp51CMprLfNjymN8Q,8243
127
- uncountable/core/query/column.py,sha256=Xxvp9C4PkHtcSI5NGAt90NBI4-xpNwpvRJr6f1L51hA,44285
126
+ uncountable/core/query/builder.py,sha256=LmPHfi0wdNGVn1hdYxTAU1MTsIDlVc_lgWBZFVVmZR8,8510
127
+ uncountable/core/query/column.py,sha256=q5Yy41oS8MNrNaHAZVoftooJExPymm2tmCTF-BAPsoY,45231
128
128
  uncountable/core/query/row.py,sha256=dy68YKvMz336nyOsj_g2oNTLKBuUnKRUz5n99sApfT0,1600
129
129
  uncountable/core/query/types.py,sha256=xZgc0_O1PIxZoJWQhxkg3qnSvHj3yt-bv9QX1UCOFGw,481
130
130
  uncountable/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -196,7 +196,7 @@ uncountable/types/curves_t.py,sha256=WIML06C67yC4KWdPJR2HV_Ac7eBFTDRDUL3YgeXbFOs
196
196
  uncountable/types/data.py,sha256=u2isf4XEug3Eu-xSIoqGaCQmW2dFaKBHCkP_WKYwwBc,500
197
197
  uncountable/types/data_t.py,sha256=U_dIh5GcyaJlkrHEI4Qf4TOo1uJX07PYO70sYzzKtTo,2615
198
198
  uncountable/types/entity.py,sha256=Zclk1LYcRaYrMDhqyCjMSLEg0fE6_q8LHvV22Qvscgs,566
199
- uncountable/types/entity_t.py,sha256=v2nuly-LbmFoSBHFtd7apIs2OcMUyXpX5AvYIXweMOc,27562
199
+ uncountable/types/entity_t.py,sha256=fVDQyZvpBHp6llk05entEf2ODOVcCsEGlkE1IyDfY2o,27676
200
200
  uncountable/types/experiment_groups.py,sha256=qUpFOx1AKgzaT_4khCOv5Xs6jwiQGbvHH-GUh3v1nv4,288
201
201
  uncountable/types/experiment_groups_t.py,sha256=QjZBdjnbsD1D-VpPSSV6hBg-Q7mujjKvYtdMewtXNVU,712
202
202
  uncountable/types/exports.py,sha256=VMmxUO2PpV1Y63hZ2AnVor4H-B6aswJ7YpSru_u89lU,334
@@ -414,7 +414,7 @@ uncountable/types/api/uploader/complete_async_parse.py,sha256=Os1HAubxTlbut6Gylj
414
414
  uncountable/types/api/uploader/invoke_uploader.py,sha256=QU1KmAFgGHSWskZEKfaAww8rCj-hVNP0i4kMQE3x3Fc,1822
415
415
  uncountable/types/api/user/__init__.py,sha256=gCgbynxG3jA8FQHzercKtrHKHkiIKr8APdZYUniAor8,55
416
416
  uncountable/types/api/user/get_current_user_info.py,sha256=BT7bGp5SOHz3XdKWhhdZ6acnInafT2PgdJ-H4YH3W7c,1181
417
- uncountablepythonsdk-0.0.176.dist-info/METADATA,sha256=s0UZYChC9s08XuTnFwHFzr4j6RYWBqgwTMemmUggQTk,2175
418
- uncountablepythonsdk-0.0.176.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
419
- uncountablepythonsdk-0.0.176.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
420
- uncountablepythonsdk-0.0.176.dist-info/RECORD,,
417
+ uncountablepythonsdk-0.0.177.dist-info/METADATA,sha256=gvqE0J2wNYcjjbTIjMPWR8fF1tB5p0Z2DCG0Z4-tiFc,2175
418
+ uncountablepythonsdk-0.0.177.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
419
+ uncountablepythonsdk-0.0.177.dist-info/top_level.txt,sha256=1UVGjAU-6hJY9qw2iJ7nCBeEwZ793AEN5ZfKX9A1uj4,31
420
+ uncountablepythonsdk-0.0.177.dist-info/RECORD,,