vastdb 0.0.5.2__py3-none-any.whl → 0.1.0__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.
Files changed (39) hide show
  1. vast_flatbuf/tabular/GetTableStatsResponse.py +45 -1
  2. vast_flatbuf/tabular/VipRange.py +56 -0
  3. vastdb/__init__.py +7 -0
  4. vastdb/bucket.py +77 -0
  5. vastdb/errors.py +158 -0
  6. vastdb/{api.py → internal_commands.py} +283 -747
  7. vastdb/schema.py +77 -0
  8. vastdb/session.py +48 -0
  9. vastdb/table.py +480 -0
  10. vastdb/tests/conftest.py +46 -0
  11. vastdb/tests/test_imports.py +125 -0
  12. vastdb/tests/test_projections.py +41 -0
  13. vastdb/tests/test_sanity.py +83 -0
  14. vastdb/tests/test_schemas.py +45 -0
  15. vastdb/tests/test_tables.py +608 -0
  16. vastdb/transaction.py +55 -0
  17. vastdb/util.py +77 -0
  18. vastdb-0.1.0.dist-info/METADATA +38 -0
  19. {vastdb-0.0.5.2.dist-info → vastdb-0.1.0.dist-info}/RECORD +23 -24
  20. vast_protobuf/substrait/__init__.py +0 -0
  21. vast_protobuf/substrait/algebra_pb2.py +0 -1344
  22. vast_protobuf/substrait/capabilities_pb2.py +0 -46
  23. vast_protobuf/substrait/ddl_pb2.py +0 -57
  24. vast_protobuf/substrait/extended_expression_pb2.py +0 -49
  25. vast_protobuf/substrait/extensions/__init__.py +0 -0
  26. vast_protobuf/substrait/extensions/extensions_pb2.py +0 -89
  27. vast_protobuf/substrait/function_pb2.py +0 -168
  28. vast_protobuf/substrait/parameterized_types_pb2.py +0 -181
  29. vast_protobuf/substrait/plan_pb2.py +0 -67
  30. vast_protobuf/substrait/type_expressions_pb2.py +0 -198
  31. vast_protobuf/substrait/type_pb2.py +0 -350
  32. vast_protobuf/tabular/__init__.py +0 -0
  33. vast_protobuf/tabular/rpc_pb2.py +0 -344
  34. vastdb/v2.py +0 -108
  35. vastdb-0.0.5.2.dist-info/METADATA +0 -47
  36. {vast_protobuf → vastdb/tests}/__init__.py +0 -0
  37. {vastdb-0.0.5.2.dist-info → vastdb-0.1.0.dist-info}/LICENSE +0 -0
  38. {vastdb-0.0.5.2.dist-info → vastdb-0.1.0.dist-info}/WHEEL +0 -0
  39. {vastdb-0.0.5.2.dist-info → vastdb-0.1.0.dist-info}/top_level.txt +0 -0
@@ -45,7 +45,39 @@ class GetTableStatsResponse(object):
45
45
  return bool(self._tab.Get(flatbuffers.number_types.BoolFlags, o + self._tab.Pos))
46
46
  return False
47
47
 
48
- def Start(builder): builder.StartObject(3)
48
+ # GetTableStatsResponse
49
+ def AddressType(self):
50
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10))
51
+ if o != 0:
52
+ return self._tab.String(o + self._tab.Pos)
53
+ return None
54
+
55
+ # GetTableStatsResponse
56
+ def Vips(self, j):
57
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12))
58
+ if o != 0:
59
+ x = self._tab.Vector(o)
60
+ x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4
61
+ x = self._tab.Indirect(x)
62
+ from vast_flatbuf.tabular.VipRange import VipRange
63
+ obj = VipRange()
64
+ obj.Init(self._tab.Bytes, x)
65
+ return obj
66
+ return None
67
+
68
+ # GetTableStatsResponse
69
+ def VipsLength(self):
70
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12))
71
+ if o != 0:
72
+ return self._tab.VectorLen(o)
73
+ return 0
74
+
75
+ # GetTableStatsResponse
76
+ def VipsIsNone(self):
77
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12))
78
+ return o == 0
79
+
80
+ def Start(builder): builder.StartObject(5)
49
81
  def GetTableStatsResponseStart(builder):
50
82
  """This method is deprecated. Please switch to Start."""
51
83
  return Start(builder)
@@ -61,6 +93,18 @@ def AddIsExternalRowidAlloc(builder, isExternalRowidAlloc): builder.PrependBoolS
61
93
  def GetTableStatsResponseAddIsExternalRowidAlloc(builder, isExternalRowidAlloc):
62
94
  """This method is deprecated. Please switch to AddIsExternalRowidAlloc."""
63
95
  return AddIsExternalRowidAlloc(builder, isExternalRowidAlloc)
96
+ def AddAddressType(builder, addressType): builder.PrependUOffsetTRelativeSlot(3, flatbuffers.number_types.UOffsetTFlags.py_type(addressType), 0)
97
+ def GetTableStatsResponseAddAddressType(builder, addressType):
98
+ """This method is deprecated. Please switch to AddAddressType."""
99
+ return AddAddressType(builder, addressType)
100
+ def AddVips(builder, vips): builder.PrependUOffsetTRelativeSlot(4, flatbuffers.number_types.UOffsetTFlags.py_type(vips), 0)
101
+ def GetTableStatsResponseAddVips(builder, vips):
102
+ """This method is deprecated. Please switch to AddVips."""
103
+ return AddVips(builder, vips)
104
+ def StartVipsVector(builder, numElems): return builder.StartVector(4, numElems, 4)
105
+ def GetTableStatsResponseStartVipsVector(builder, numElems):
106
+ """This method is deprecated. Please switch to Start."""
107
+ return StartVipsVector(builder, numElems)
64
108
  def End(builder): return builder.EndObject()
65
109
  def GetTableStatsResponseEnd(builder):
66
110
  """This method is deprecated. Please switch to End."""
@@ -0,0 +1,56 @@
1
+ # automatically generated by the FlatBuffers compiler, do not modify
2
+
3
+ # namespace: tabular
4
+
5
+ import flatbuffers
6
+ from flatbuffers.compat import import_numpy
7
+ np = import_numpy()
8
+
9
+ class VipRange(object):
10
+ __slots__ = ['_tab']
11
+
12
+ @classmethod
13
+ def GetRootAs(cls, buf, offset=0):
14
+ n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
15
+ x = VipRange()
16
+ x.Init(buf, n + offset)
17
+ return x
18
+
19
+ @classmethod
20
+ def GetRootAsVipRange(cls, buf, offset=0):
21
+ """This method is deprecated. Please switch to GetRootAs."""
22
+ return cls.GetRootAs(buf, offset)
23
+ # VipRange
24
+ def Init(self, buf, pos):
25
+ self._tab = flatbuffers.table.Table(buf, pos)
26
+
27
+ # VipRange
28
+ def StartAddress(self):
29
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
30
+ if o != 0:
31
+ return self._tab.String(o + self._tab.Pos)
32
+ return None
33
+
34
+ # VipRange
35
+ def AddressCount(self):
36
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
37
+ if o != 0:
38
+ return self._tab.Get(flatbuffers.number_types.Uint16Flags, o + self._tab.Pos)
39
+ return 0
40
+
41
+ def Start(builder): builder.StartObject(2)
42
+ def VipRangeStart(builder):
43
+ """This method is deprecated. Please switch to Start."""
44
+ return Start(builder)
45
+ def AddStartAddress(builder, startAddress): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(startAddress), 0)
46
+ def VipRangeAddStartAddress(builder, startAddress):
47
+ """This method is deprecated. Please switch to AddStartAddress."""
48
+ return AddStartAddress(builder, startAddress)
49
+ def AddAddressCount(builder, addressCount): builder.PrependUint16Slot(1, addressCount, 0)
50
+ def VipRangeAddAddressCount(builder, addressCount):
51
+ """This method is deprecated. Please switch to AddAddressCount."""
52
+ return AddAddressCount(builder, addressCount)
53
+ def End(builder): return builder.EndObject()
54
+ def VipRangeEnd(builder):
55
+ """This method is deprecated. Please switch to End."""
56
+ return End(builder)
vastdb/__init__.py CHANGED
@@ -0,0 +1,7 @@
1
+ """VAST Database Python SDK."""
2
+
3
+ from . import session
4
+
5
+ # A helper function, useful as a short-hand for Session c-tor: `session = vastdb.connect(...)`
6
+ connect = session.Session
7
+ connect.__name__ = 'connect'
vastdb/bucket.py ADDED
@@ -0,0 +1,77 @@
1
+ """VAST Database bucket.
2
+
3
+ VAST S3 buckets can be used to create Database schemas and tables.
4
+ It is possible to list and access VAST snapshots generated over a bucket.
5
+ """
6
+
7
+ from . import errors, schema, transaction
8
+
9
+ from dataclasses import dataclass
10
+ import logging
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass
16
+ class Snapshot:
17
+ """VAST bucket-level snapshot."""
18
+
19
+ name: str
20
+ bucket: "Bucket"
21
+
22
+
23
+ @dataclass
24
+ class Bucket:
25
+ """VAST bucket."""
26
+
27
+ name: str
28
+ tx: "transaction.Transaction"
29
+
30
+ def create_schema(self, path: str) -> "schema.Schema":
31
+ """Create a new schema (a container of tables) under this bucket."""
32
+ self.tx._rpc.api.create_schema(self.name, path, txid=self.tx.txid)
33
+ log.info("Created schema: %s", path)
34
+ return self.schema(path)
35
+
36
+ def schema(self, path: str) -> "schema.Schema":
37
+ """Get a specific schema (a container of tables) under this bucket."""
38
+ s = self.schemas(path)
39
+ log.debug("schema: %s", s)
40
+ if not s:
41
+ raise errors.MissingSchema(self.name, path)
42
+ assert len(s) == 1, f"Expected to receive only a single schema, but got: {len(s)}. ({s})"
43
+ log.debug("Found schema: %s", s[0].name)
44
+ return s[0]
45
+
46
+ def schemas(self, name: str = None) -> ["schema.Schema"]:
47
+ """List bucket's schemas."""
48
+ schemas = []
49
+ next_key = 0
50
+ exact_match = bool(name)
51
+ log.debug("list schemas param: schema=%s, exact_match=%s", name, exact_match)
52
+ while True:
53
+ bucket_name, curr_schemas, next_key, is_truncated, _ = \
54
+ self.tx._rpc.api.list_schemas(bucket=self.name, next_key=next_key, txid=self.tx.txid,
55
+ name_prefix=name, exact_match=exact_match)
56
+ if not curr_schemas:
57
+ break
58
+ schemas.extend(curr_schemas)
59
+ if not is_truncated:
60
+ break
61
+
62
+ return [schema.Schema(name=name, bucket=self) for name, *_ in schemas]
63
+
64
+ def snapshots(self) -> [Snapshot]:
65
+ """List bucket's snapshots."""
66
+ snapshots = []
67
+ next_key = 0
68
+ while True:
69
+ curr_snapshots, is_truncated, next_key = \
70
+ self.tx._rpc.api.list_snapshots(bucket=self.name, next_token=next_key)
71
+ if not curr_snapshots:
72
+ break
73
+ snapshots.extend(curr_snapshots)
74
+ if not is_truncated:
75
+ break
76
+
77
+ return [Snapshot(name=snapshot, bucket=self) for snapshot in snapshots]
vastdb/errors.py ADDED
@@ -0,0 +1,158 @@
1
+ import logging
2
+ import requests
3
+ import xml.etree.ElementTree
4
+
5
+ from enum import Enum
6
+ from dataclasses import dataclass
7
+
8
+
9
+ class HttpStatus(Enum):
10
+ SUCCESS = 200
11
+ BAD_REQUEST = 400
12
+ FOBIDDEN = 403
13
+ NOT_FOUND = 404
14
+ METHOD_NOT_ALLOWED = 405
15
+ REQUEST_TIMEOUT = 408
16
+ CONFLICT = 409
17
+ INTERNAL_SERVER_ERROR = 500
18
+ NOT_IMPLEMENTED = 501
19
+ SERVICE_UNAVAILABLE = 503
20
+
21
+
22
+ log = logging.getLogger(__name__)
23
+
24
+
25
+ @dataclass
26
+ class HttpError(Exception):
27
+ code: str
28
+ message: str
29
+ url: str
30
+ status: int # HTTP status
31
+ headers: requests.structures.CaseInsensitiveDict # HTTP response headers
32
+
33
+ def __post_init__(self):
34
+ self.args = [vars(self)]
35
+
36
+
37
+ class NotFound(HttpError):
38
+ pass
39
+
40
+
41
+ class Forbidden(HttpError):
42
+ pass
43
+
44
+
45
+ class BadRequest(HttpError):
46
+ pass
47
+
48
+
49
+ class MethodNotAllowed(HttpError):
50
+ pass
51
+
52
+
53
+ class RequestTimeout(HttpError):
54
+ pass
55
+
56
+
57
+ class Conflict(HttpError):
58
+ pass
59
+
60
+
61
+ class InternalServerError(HttpError):
62
+ pass
63
+
64
+
65
+ class NotImplemented(HttpError):
66
+ pass
67
+
68
+
69
+ class ServiceUnavailable(HttpError):
70
+ pass
71
+
72
+
73
+ class UnexpectedError(HttpError):
74
+ pass
75
+
76
+
77
+ @dataclass
78
+ class ImportFilesError(Exception):
79
+ message: str
80
+ error_dict: dict
81
+
82
+
83
+ class InvalidArgument(Exception):
84
+ pass
85
+
86
+
87
+ class Missing(Exception):
88
+ pass
89
+
90
+
91
+ @dataclass
92
+ class MissingBucket(Missing):
93
+ bucket: str
94
+
95
+
96
+ @dataclass
97
+ class MissingSchema(Missing):
98
+ bucket: str
99
+ schema: str
100
+
101
+
102
+ @dataclass
103
+ class MissingTable(Missing):
104
+ bucket: str
105
+ schema: str
106
+ table: str
107
+
108
+
109
+ @dataclass
110
+ class MissingProjection(Missing):
111
+ bucket: str
112
+ schema: str
113
+ table: str
114
+ projection: str
115
+
116
+
117
+ ERROR_TYPES_MAP = {
118
+ HttpStatus.BAD_REQUEST: BadRequest,
119
+ HttpStatus.FOBIDDEN: Forbidden,
120
+ HttpStatus.NOT_FOUND: NotFound,
121
+ HttpStatus.METHOD_NOT_ALLOWED: MethodNotAllowed,
122
+ HttpStatus.REQUEST_TIMEOUT: RequestTimeout,
123
+ HttpStatus.CONFLICT: Conflict,
124
+ HttpStatus.INTERNAL_SERVER_ERROR: InternalServerError,
125
+ HttpStatus.NOT_IMPLEMENTED: NotImplemented,
126
+ HttpStatus.SERVICE_UNAVAILABLE: ServiceUnavailable,
127
+ }
128
+
129
+
130
+ def from_response(res: requests.Response):
131
+ if res.status_code == HttpStatus.SUCCESS.value:
132
+ return None
133
+
134
+ log.debug("response: url='%s', code=%s, headers=%s, body='%s'", res.request.url, res.status_code, res.headers, res.text)
135
+ # try to parse S3 XML response for the error details:
136
+ code = None
137
+ message = None
138
+ if res.text:
139
+ try:
140
+ root = xml.etree.ElementTree.fromstring(res.text)
141
+ code = root.find('Code')
142
+ code = code.text if code is not None else None
143
+ message = root.find('Message')
144
+ message = message.text if message is not None else None
145
+ except xml.etree.ElementTree.ParseError:
146
+ log.debug("invalid XML: %r", res.text)
147
+
148
+ kwargs = dict(
149
+ code=code,
150
+ message=message,
151
+ url=res.request.url,
152
+ status=res.status_code,
153
+ headers=res.headers,
154
+ )
155
+ log.warning("RPC failed: %s", kwargs)
156
+ status = HttpStatus(res.status_code)
157
+ error_type = ERROR_TYPES_MAP.get(status, UnexpectedError)
158
+ raise error_type(**kwargs)