maxframe 1.1.0__cp311-cp311-win_amd64.whl → 1.1.1__cp311-cp311-win_amd64.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.

Potentially problematic release.


This version of maxframe might be problematic. Click here for more details.

Binary file
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from .config import AttributeDict, option_context, options
15
+ from .config import AttributeDict, option_context, options, update_wlm_quota_settings
maxframe/config/config.py CHANGED
@@ -28,6 +28,8 @@ except ImportError:
28
28
 
29
29
  available_timezones = lambda: all_timezones
30
30
 
31
+ import logging
32
+
31
33
  from ..utils import get_python_tag
32
34
  from .validators import (
33
35
  ValidatorType,
@@ -43,6 +45,8 @@ from .validators import (
43
45
  is_valid_cache_path,
44
46
  )
45
47
 
48
+ logger = logging.getLogger(__name__)
49
+
46
50
  _DEFAULT_REDIRECT_WARN = "Option {source} has been replaced by {target} and might be removed in a future release."
47
51
  _DEFAULT_MAX_ALIVE_SECONDS = 3 * 24 * 3600
48
52
  _DEFAULT_MAX_IDLE_SECONDS = 3600
@@ -499,3 +503,22 @@ class OptionsProxy:
499
503
 
500
504
 
501
505
  options = OptionsProxy()
506
+
507
+
508
+ def update_wlm_quota_settings(session_id: str, engine_settings: Dict[str, Any]):
509
+ engine_quota = engine_settings.get("odps.task.wlm.quota", None)
510
+ session_quota = options.session.quota_name or None
511
+ if engine_quota != session_quota and engine_quota:
512
+ logger.warning(
513
+ "[Session=%s] Session quota (%s) is different to SubDag engine quota (%s)",
514
+ session_id,
515
+ session_quota,
516
+ engine_quota,
517
+ )
518
+ # TODO(renxiang): overwrite or not overwrite
519
+ return
520
+
521
+ if session_quota:
522
+ engine_settings["odps.task.wlm.quota"] = session_quota
523
+ elif "odps.task.wlm.quota" in engine_settings:
524
+ engine_settings.pop("odps.task.wlm.quota")
@@ -18,7 +18,14 @@ import threading
18
18
 
19
19
  import pytest
20
20
 
21
- from ..config import Config, is_integer, is_string, option_context, options
21
+ from ..config import (
22
+ Config,
23
+ is_integer,
24
+ is_string,
25
+ option_context,
26
+ options,
27
+ update_wlm_quota_settings,
28
+ )
22
29
 
23
30
 
24
31
  def test_config_context():
@@ -101,3 +108,15 @@ def test_config_copy():
101
108
 
102
109
  target_cfg.update(src_cfg_dict)
103
110
  assert target_cfg.a.b.c == 1
111
+
112
+
113
+ def test_update_wlm_quota_settings():
114
+ with option_context({}):
115
+ options.session.quota_name = "quota1"
116
+ engine_settings = {}
117
+ update_wlm_quota_settings("session_id", engine_settings)
118
+ assert engine_settings["odps.task.wlm.quota"] == "quota1"
119
+ options.session.quota_name = None
120
+ update_wlm_quota_settings("session_id", engine_settings)
121
+ # TODO(renxiang): overwrite or not overwrite
122
+ assert "odps.task.wlm.quota" in engine_settings
maxframe/conftest.py CHANGED
@@ -40,10 +40,14 @@ def _get_odps_env(test_config: ConfigParser, section_name: str) -> ODPS:
40
40
  access_id = test_config.get(section_name, "access_id")
41
41
  except NoOptionError:
42
42
  access_id = test_config.get("odps", "access_id")
43
+ if not access_id:
44
+ access_id = os.getenv("ACCESS_ID")
43
45
  try:
44
46
  secret_access_key = test_config.get(section_name, "secret_access_key")
45
47
  except NoOptionError:
46
48
  secret_access_key = test_config.get("odps", "secret_access_key")
49
+ if not secret_access_key:
50
+ secret_access_key = os.getenv("SECRET_ACCESS_KEY")
47
51
  try:
48
52
  project = test_config.get(section_name, "project")
49
53
  except NoOptionError:
@@ -119,8 +123,10 @@ def oss_config():
119
123
  old_cache_url = options.object_cache_url
120
124
 
121
125
  try:
122
- oss_access_id = config.get("oss", "access_id")
123
- oss_secret_access_key = config.get("oss", "secret_access_key")
126
+ oss_access_id = config.get("oss", "access_id") or os.getenv("ACCESS_ID")
127
+ oss_secret_access_key = config.get("oss", "secret_access_key") or os.getenv(
128
+ "SECRET_ACCESS_KEY"
129
+ )
124
130
  oss_bucket_name = config.get("oss", "bucket_name")
125
131
  oss_endpoint = config.get("oss", "endpoint")
126
132
  oss_rolearn = config.get("oss", "rolearn")
@@ -37,6 +37,7 @@ from ...serialization.serializables import (
37
37
  SeriesField,
38
38
  StringField,
39
39
  )
40
+ from ...utils import is_empty
40
41
  from ..utils import parse_index
41
42
  from .core import ColumnPruneSupportedDataSourceMixin, IncrementalIndexDatasource
42
43
 
@@ -250,7 +251,7 @@ class DataFrameReadODPSQuery(
250
251
  self.columns = columns
251
252
 
252
253
  def __call__(self, chunk_bytes=None, chunk_size=None):
253
- if not self.index_columns:
254
+ if is_empty(self.index_columns):
254
255
  index_value = parse_index(pd.RangeIndex(0))
255
256
  elif len(self.index_columns) == 1:
256
257
  index_value = parse_index(
@@ -34,6 +34,7 @@ from ...serialization.serializables import (
34
34
  SeriesField,
35
35
  StringField,
36
36
  )
37
+ from ...utils import is_empty
37
38
  from ..core import DataFrame # noqa: F401
38
39
  from ..utils import parse_index
39
40
  from .core import ColumnPruneSupportedDataSourceMixin, IncrementalIndexDatasource
@@ -76,7 +77,7 @@ class DataFrameReadODPSTable(
76
77
  self.columns = columns
77
78
 
78
79
  def __call__(self, shape, chunk_bytes=None, chunk_size=None):
79
- if not self.index_columns:
80
+ if is_empty(self.index_columns):
80
81
  if np.isnan(shape[0]):
81
82
  index_value = parse_index(pd.RangeIndex(0))
82
83
  else:
@@ -238,7 +239,8 @@ def read_odps_table(
238
239
  partitions = [partitions]
239
240
 
240
241
  append_partitions = append_partitions or any(
241
- pt.name in (columns or ()) for pt in (table.table_schema.partitions or ())
242
+ pt.name in (columns if not is_empty(columns) else ())
243
+ for pt in (table.table_schema.partitions or ())
242
244
  )
243
245
  op = DataFrameReadODPSTable(
244
246
  table_name=table.full_table_name,
@@ -19,5 +19,5 @@ from .schema import (
19
19
  odps_schema_to_pandas_dtypes,
20
20
  pandas_to_odps_schema,
21
21
  )
22
- from .tableio import HaloTableIO, ODPSTableIO
22
+ from .tableio import HaloTableIO, ODPSTableIO, TunnelTableIO
23
23
  from .volumeio import ODPSVolumeReader, ODPSVolumeWriter
@@ -14,10 +14,12 @@
14
14
 
15
15
  from typing import Any, Tuple, Union
16
16
 
17
+ import numpy as np
17
18
  import pandas as pd
18
19
  import pyarrow as pa
19
20
 
20
21
  from ...core import OutputType
22
+ from ...lib.version import parse as parse_version
21
23
  from ...protocol import DataFrameTableMeta
22
24
  from ...tensor.core import TENSOR_TYPE
23
25
  from ...typing_ import ArrowTableType, PandasObjectTypes
@@ -109,7 +111,26 @@ def pandas_to_arrow(
109
111
  df = pd.DataFrame([[df]], columns=names)
110
112
  else: # this could never happen # pragma: no cover
111
113
  raise ValueError(f"Does not support meta type {table_meta.type!r}")
112
- pa_table = pa.Table.from_pandas(df, nthreads=nthreads, preserve_index=False)
114
+
115
+ try:
116
+ pa_table = pa.Table.from_pandas(df, nthreads=nthreads, preserve_index=False)
117
+ except pa.ArrowTypeError as ex: # pragma: no cover
118
+ late_np_version = parse_version(np.__version__) >= parse_version("1.20")
119
+ early_pa_version = parse_version(pa.__version__) <= parse_version("4.0")
120
+ if (
121
+ late_np_version
122
+ and early_pa_version
123
+ and "Did not pass numpy.dtype object" in str(ex)
124
+ ):
125
+ raise TypeError(
126
+ "Potential dependency conflict. Try update to pyarrow>4.0 "
127
+ "or downgrade to numpy<1.20. Details can be seen at "
128
+ "https://github.com/numpy/numpy/issues/17913. "
129
+ f"Raw error message: {ex!r}"
130
+ ).with_traceback(ex.__traceback__) from None
131
+ else:
132
+ raise
133
+
113
134
  if table_datetime_cols:
114
135
  col_names = pa_table.schema.names
115
136
  col_datas = []
@@ -15,6 +15,7 @@
15
15
  import os
16
16
  import time
17
17
  from abc import ABC, abstractmethod
18
+ from collections import OrderedDict
18
19
  from contextlib import contextmanager
19
20
  from typing import Dict, List, Optional, Union
20
21
 
@@ -25,7 +26,7 @@ from odps.apis.storage_api import (
25
26
  TableBatchScanResponse,
26
27
  TableBatchWriteResponse,
27
28
  )
28
- from odps.tunnel import TableTunnel
29
+ from odps.tunnel import TableDownloadSession, TableDownloadStatus, TableTunnel
29
30
  from odps.types import OdpsSchema, PartitionSpec, timestamp_ntz
30
31
  from odps.utils import call_with_retry
31
32
 
@@ -36,12 +37,13 @@ except ImportError:
36
37
 
37
38
  from ...config import options
38
39
  from ...env import ODPS_STORAGE_API_ENDPOINT
39
- from ...utils import sync_pyodps_options
40
+ from ...utils import is_empty, sync_pyodps_options
40
41
  from .schema import odps_schema_to_arrow_schema
41
42
 
42
43
  PartitionsType = Union[List[str], str, None]
43
44
 
44
45
  _DEFAULT_ROW_BATCH_SIZE = 4096
46
+ _DOWNLOAD_ID_CACHE_SIZE = 100
45
47
 
46
48
 
47
49
  class ODPSTableIO(ABC):
@@ -65,7 +67,11 @@ class ODPSTableIO(ABC):
65
67
  ) -> OdpsSchema:
66
68
  final_cols = []
67
69
 
68
- columns = columns or [col.name for col in table_schema.simple_columns]
70
+ columns = (
71
+ columns
72
+ if not is_empty(columns)
73
+ else [col.name for col in table_schema.simple_columns]
74
+ )
69
75
  if partition_columns is True:
70
76
  partition_columns = [c.name for c in table_schema.partitions]
71
77
  else:
@@ -215,6 +221,46 @@ class TunnelMultiPartitionReader:
215
221
 
216
222
 
217
223
  class TunnelTableIO(ODPSTableIO):
224
+ _down_session_ids = OrderedDict()
225
+
226
+ @classmethod
227
+ def create_download_sessions(
228
+ cls,
229
+ odps_entry: ODPS,
230
+ full_table_name: str,
231
+ partitions: List[Optional[str]] = None,
232
+ ) -> Dict[Optional[str], TableDownloadSession]:
233
+ table = odps_entry.get_table(full_table_name)
234
+ tunnel = TableTunnel(odps_entry)
235
+ parts = (
236
+ [partitions]
237
+ if partitions is None or isinstance(partitions, str)
238
+ else partitions
239
+ )
240
+ part_to_session = dict()
241
+ for part in parts:
242
+ part_key = (full_table_name, part)
243
+ down_session = None
244
+
245
+ if part_key in cls._down_session_ids:
246
+ down_id = cls._down_session_ids[part_key]
247
+ down_session = tunnel.create_download_session(
248
+ table, async_mode=True, partition_spec=part, download_id=down_id
249
+ )
250
+ if down_session.status != TableDownloadStatus.Normal:
251
+ down_session = None
252
+
253
+ if down_session is None:
254
+ down_session = tunnel.create_download_session(
255
+ table, async_mode=True, partition_spec=part
256
+ )
257
+
258
+ while len(cls._down_session_ids) >= _DOWNLOAD_ID_CACHE_SIZE:
259
+ cls._down_session_ids.popitem(False)
260
+ cls._down_session_ids[part_key] = down_session.id
261
+ part_to_session[part] = down_session
262
+ return part_to_session
263
+
218
264
  @contextmanager
219
265
  def open_reader(
220
266
  self,
@@ -241,21 +287,15 @@ class TunnelTableIO(ODPSTableIO):
241
287
  or (reverse_range and start is None)
242
288
  ):
243
289
  with sync_pyodps_options():
244
- table = self._odps.get_table(full_table_name)
245
- tunnel = TableTunnel(self._odps)
246
- parts = (
247
- [partitions]
248
- if partitions is None or isinstance(partitions, str)
249
- else partitions
290
+ tunnel_sessions = self.create_download_sessions(
291
+ self._odps, full_table_name, partitions
292
+ )
293
+ part_to_down_id = {
294
+ pt: session.id for (pt, session) in tunnel_sessions.items()
295
+ }
296
+ total_records = sum(
297
+ session.count for session in tunnel_sessions.values()
250
298
  )
251
- part_to_down_id = dict()
252
- total_records = 0
253
- for part in parts:
254
- down_session = tunnel.create_download_session(
255
- table, async_mode=True, partition_spec=part
256
- )
257
- part_to_down_id[part] = down_session.id
258
- total_records += down_session.count
259
299
 
260
300
  count = None
261
301
  if start is not None or stop is not None:
Binary file
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import weakref
16
- from collections import defaultdict
16
+ from collections import OrderedDict
17
17
  from typing import Any, Dict, List, Optional, Tuple, Type
18
18
 
19
19
  import msgpack
@@ -98,14 +98,18 @@ class SerializableMeta(type):
98
98
  non_primitive_fields.append(v)
99
99
 
100
100
  # count number of fields for every base class
101
- cls_to_primitive_field_count = defaultdict(lambda: 0)
102
- cls_to_non_primitive_field_count = defaultdict(lambda: 0)
101
+ cls_to_primitive_field_count = OrderedDict()
102
+ cls_to_non_primitive_field_count = OrderedDict()
103
103
  for field_name in field_order:
104
104
  cls_hash = field_to_cls_hash[field_name]
105
105
  if field_name in primitive_field_names:
106
- cls_to_primitive_field_count[cls_hash] += 1
106
+ cls_to_primitive_field_count[cls_hash] = (
107
+ cls_to_primitive_field_count.get(cls_hash, 0) + 1
108
+ )
107
109
  else:
108
- cls_to_non_primitive_field_count[cls_hash] += 1
110
+ cls_to_non_primitive_field_count[cls_hash] = (
111
+ cls_to_non_primitive_field_count.get(cls_hash, 0) + 1
112
+ )
109
113
 
110
114
  slots = set(properties.pop("__slots__", set()))
111
115
  slots.update(properties_field_slot_names)
@@ -120,9 +124,11 @@ class SerializableMeta(type):
120
124
  properties["_FIELD_ORDER"] = field_order
121
125
  properties["_FIELD_TO_NAME_HASH"] = field_to_cls_hash
122
126
  properties["_PRIMITIVE_FIELDS"] = primitive_fields
123
- properties["_CLS_TO_PRIMITIVE_FIELD_COUNT"] = dict(cls_to_primitive_field_count)
127
+ properties["_CLS_TO_PRIMITIVE_FIELD_COUNT"] = OrderedDict(
128
+ cls_to_primitive_field_count
129
+ )
124
130
  properties["_NON_PRIMITIVE_FIELDS"] = non_primitive_fields
125
- properties["_CLS_TO_NON_PRIMITIVE_FIELD_COUNT"] = dict(
131
+ properties["_CLS_TO_NON_PRIMITIVE_FIELD_COUNT"] = OrderedDict(
126
132
  cls_to_non_primitive_field_count
127
133
  )
128
134
  properties["__slots__"] = tuple(slots)
@@ -296,21 +302,51 @@ class SerializableSerializer(Serializer):
296
302
  else:
297
303
  field.set(obj, value)
298
304
 
305
+ @classmethod
306
+ def _prune_server_fields(
307
+ cls,
308
+ client_cls_to_field_count: Optional[Dict[int, int]],
309
+ server_cls_to_field_count: Dict[int, int],
310
+ server_fields: list,
311
+ ) -> list:
312
+ if not client_cls_to_field_count: # pragma: no cover
313
+ # todo remove this branch when all versions below v0.1.0b5 is eliminated
314
+ return server_fields
315
+ if set(client_cls_to_field_count.keys()) == set(
316
+ server_cls_to_field_count.keys()
317
+ ):
318
+ return server_fields
319
+ ret_server_fields = []
320
+ server_pos = 0
321
+ for cls_hash, count in server_cls_to_field_count.items():
322
+ if cls_hash in client_cls_to_field_count:
323
+ ret_server_fields.extend(server_fields[server_pos : server_pos + count])
324
+ server_pos += count
325
+ return ret_server_fields
326
+
299
327
  @classmethod
300
328
  def _set_field_values(
301
329
  cls,
302
330
  obj: Serializable,
303
331
  values: List[Any],
304
- client_cls_to_field_count: Optional[Dict[str, int]],
332
+ client_cls_to_field_count: Optional[Dict[int, int]],
305
333
  is_primitive: bool = True,
306
334
  ):
307
335
  obj_class = type(obj)
308
336
  if is_primitive:
309
337
  server_cls_to_field_count = obj_class._CLS_TO_PRIMITIVE_FIELD_COUNT
310
- server_fields = obj_class._PRIMITIVE_FIELDS
338
+ server_fields = cls._prune_server_fields(
339
+ client_cls_to_field_count,
340
+ server_cls_to_field_count,
341
+ obj_class._PRIMITIVE_FIELDS,
342
+ )
311
343
  else:
312
344
  server_cls_to_field_count = obj_class._CLS_TO_NON_PRIMITIVE_FIELD_COUNT
313
- server_fields = obj_class._NON_PRIMITIVE_FIELDS
345
+ server_fields = cls._prune_server_fields(
346
+ client_cls_to_field_count,
347
+ server_cls_to_field_count,
348
+ obj_class._NON_PRIMITIVE_FIELDS,
349
+ )
314
350
 
315
351
  legacy_to_new_hash = {
316
352
  c._LEGACY_NAME_HASH: c._NAME_HASH
@@ -221,7 +221,10 @@ def test_compatible_serializable(set_is_ci):
221
221
  _ref_val = ReferenceField("ref_val", "MySimpleSerializable")
222
222
  _dict_val = DictField("dict_val")
223
223
 
224
- class MySubSerializable(MySimpleSerializable):
224
+ class MyMidSerializable(MySimpleSerializable):
225
+ _i_bool_val = Int64Field("i_bool_val", default=True)
226
+
227
+ class MySubSerializable(MyMidSerializable):
225
228
  _m_int_val = Int64Field("m_int_val", default=250)
226
229
  _m_str_val = StringField("m_str_val", default="SUB_STR")
227
230
 
@@ -48,7 +48,7 @@ def vstack(tup):
48
48
 
49
49
  Examples
50
50
  --------
51
- >>> import mars.tensor as mt
51
+ >>> import maxframe.tensor as mt
52
52
 
53
53
  >>> a = mt.array([1, 2, 3])
54
54
  >>> b = mt.array([2, 3, 4])
maxframe/utils.py CHANGED
@@ -1127,3 +1127,9 @@ def sync_pyodps_options():
1127
1127
 
1128
1128
  def str_to_bool(s: Optional[str]) -> Optional[bool]:
1129
1129
  return s.lower().strip() in ("true", "1") if s is not None else None
1130
+
1131
+
1132
+ def is_empty(val):
1133
+ if isinstance(val, (pd.DataFrame, pd.Series, pd.Index)):
1134
+ return val.empty
1135
+ return not bool(val)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: maxframe
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: MaxFrame operator-based data analyze framework
5
5
  Requires-Dist: numpy<2.0.0,>=1.19.0
6
6
  Requires-Dist: pandas>=1.0.0
@@ -11,11 +11,11 @@ Requires-Dist: msgpack>=1.0.0
11
11
  Requires-Dist: traitlets>=5.0
12
12
  Requires-Dist: cloudpickle<3.0.0,>=1.5.0
13
13
  Requires-Dist: pyyaml>=5.1
14
+ Requires-Dist: pickle5; python_version < "3.8"
14
15
  Requires-Dist: tornado>=6.0
15
16
  Requires-Dist: defusedxml>=0.5.0
16
17
  Requires-Dist: tqdm>=4.1.0
17
- Requires-Dist: importlib-metadata>=1.4
18
- Requires-Dist: pickle5; python_version < "3.8"
18
+ Requires-Dist: importlib_metadata>=1.4
19
19
  Provides-Extra: dev
20
20
  Requires-Dist: black>=22.3.0; extra == "dev"
21
21
  Requires-Dist: flake8>=5.0.4; extra == "dev"
@@ -1,9 +1,9 @@
1
1
  maxframe/__init__.py,sha256=RujkARDvD6mhFpR330UQ0UfogvIdae5EjR1euFpTiYA,1036
2
- maxframe/_utils.cp311-win_amd64.pyd,sha256=LNCWqE3sHoxLD8a8dt0wwB5ZJFaY_N_W4MOnd6SL5g0,306176
2
+ maxframe/_utils.cp311-win_amd64.pyd,sha256=ljsG5RJgNgnl-XMSSbQvdG6VIm63KWWcDjlRsQcksV0,306176
3
3
  maxframe/_utils.pxd,sha256=_qHN-lCY1FQgDFIrrqA79Ys0SBdonp9kXRMS93xKSYk,1187
4
4
  maxframe/_utils.pyx,sha256=_3p6aJEJ6WZYLcNZ6o4DxoxsxqadTlJXFlgDeFPxqUQ,17564
5
5
  maxframe/codegen.py,sha256=8CvmD-1elhT9hK5O3jadhq7G-3AVf7Tb1MNOJcGJWVA,18267
6
- maxframe/conftest.py,sha256=O821qk5aADhwAF2-VMDFuIc7Ym_f-iDXXThapRZ86Tw,6247
6
+ maxframe/conftest.py,sha256=YDTh7eJqk19UhZE73-18oKPUxlT9ke7S-kUui4rfeJc,6489
7
7
  maxframe/env.py,sha256=xY4wjMWIJ4qLsFAQ5F-X5CrVR7dDSWiryPXni0YSK5c,1435
8
8
  maxframe/errors.py,sha256=nhQVjRbH5EsyLZhyAufvHpMhfDN6eR8vcrv4sjaI7p8,1000
9
9
  maxframe/extension.py,sha256=XKgK2b42i-jfnLc0lBPiBMsGA6HMQ4a12mJc09tMAFc,2752
@@ -13,12 +13,12 @@ maxframe/protocol.py,sha256=TM6jnUsNy634YaI-Ed-alX48ebtGSyFJwM8XvmlGPbg,19526
13
13
  maxframe/session.py,sha256=wftB7_3CHU9ljdgS9b-Gz6PNiUToVSScwSG-x3dlu3I,37710
14
14
  maxframe/typing_.py,sha256=EsgH2QO4VF0TRdjshKyL0Tmgiy3Ow8doTwEuam5nStE,1220
15
15
  maxframe/udf.py,sha256=bzzpmXVz7BioiNW2RxmKEad2E4X5mJpEfKYmLjGlLGA,5317
16
- maxframe/utils.py,sha256=RYAcum5Y1YgmzotPzTrSuqvRONSQlC7XXhbQqkr6XtA,35679
17
- maxframe/config/__init__.py,sha256=AHo3deaCm1JnbbRX_udboJEDYrYytdvivp9RFxJcumI,671
18
- maxframe/config/config.py,sha256=PjTqbLPW2e8DxgMuz9vmY1lxcSdTWaSxhCsheMeWomI,16011
16
+ maxframe/utils.py,sha256=jCZkpT6OtjIlTYArmRiT-WDsh7sGQb_cxbyVGXBiK94,35817
17
+ maxframe/config/__init__.py,sha256=2VB189TzomdYUKOdSvyb5trVPOXVYX6bS1DzDv5U5YA,698
18
+ maxframe/config/config.py,sha256=2rqp6JPNrUW6FJAwmAxZal3512e8T3uKLupAldxWaMg,16804
19
19
  maxframe/config/validators.py,sha256=vs82UJa4xdY7tvbM7U4vOtXDJHrfdAOTvDkbrFtuGLU,2599
20
20
  maxframe/config/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
21
- maxframe/config/tests/test_config.py,sha256=FWQZ6KBUG_jY1-KaR-GKXl7khhlTbuLlk3uaEV8koM8,2839
21
+ maxframe/config/tests/test_config.py,sha256=oi_7HOVIEDARpNnflDFAC5tkoP3Kk188VXU5Or-ewpI,3405
22
22
  maxframe/config/tests/test_validators.py,sha256=kz9Yxiqvl_BJq2CT61JTzfpRyrjm6lFrjqIfqeld_yo,1274
23
23
  maxframe/core/__init__.py,sha256=uNccOOfZ1aCxvUsz8ic5kObQkrBgfUv8ExU7OScyW1w,1508
24
24
  maxframe/core/base.py,sha256=43qZ_sJFgW857qhT1jqgsjdAsBy9tzCeqeX62CPm4vM,4691
@@ -33,7 +33,7 @@ maxframe/core/entity/utils.py,sha256=454RYVbTMVW_8KnfDqUPec4kz1p98izVTC2OrzhOkao
33
33
  maxframe/core/entity/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
34
34
  maxframe/core/entity/tests/test_objects.py,sha256=8OAMgphXRVri3UewnCYoXfS3lJ_53yxFmR3rv2pWiMY,1392
35
35
  maxframe/core/graph/__init__.py,sha256=IRTkHcVzI57tPXZ3agJJb-atpoD-9Jk4vtcRj3XQXzg,782
36
- maxframe/core/graph/core.cp311-win_amd64.pyd,sha256=iGGd9Fy7X7yPE4Au0R4h2hCAA6tnEtQp98EaY-ajXaU,251392
36
+ maxframe/core/graph/core.cp311-win_amd64.pyd,sha256=PdlGalLtOQmT7N3ZSkGYw4M367D2FEg6HRfWh8ogPAg,250880
37
37
  maxframe/core/graph/core.pyx,sha256=J619C6xfVCF8NQ8oeinhICYSg-w5ecX-dnBI7NB-6n0,16390
38
38
  maxframe/core/graph/entity.py,sha256=7DJtHi7Xum_Yp16caiQd2AMZd34PWDPy7kocjWhgl3o,5010
39
39
  maxframe/core/graph/builder/__init__.py,sha256=fL_blRc623cJ5CQTiFG1d4OK8M97FcFqGbX9chwbUQs,655
@@ -118,8 +118,8 @@ maxframe/dataframe/datasource/from_records.py,sha256=ygpKOMXZnDdWzGxMxQ4KdGv-tJF
118
118
  maxframe/dataframe/datasource/from_tensor.py,sha256=mShHYi0fZcG7ZShFVgIezaphh8tSFqR9-nQMm5YKIhw,15146
119
119
  maxframe/dataframe/datasource/index.py,sha256=X_NShW67nYJGxaWp3qOrvyInNkz9L-XHjbApU4fHoes,4518
120
120
  maxframe/dataframe/datasource/read_csv.py,sha256=IvQihmpcZIdzSD7ziX92aTAHNyP5WnTgd2cZz_h43sQ,24668
121
- maxframe/dataframe/datasource/read_odps_query.py,sha256=Uao52Qg9o1hZAORIw5Sfphx9bNhd5BWg4EoXHq_2Bn8,13861
122
- maxframe/dataframe/datasource/read_odps_table.py,sha256=9cMQZ3pdJbL54xHJE2o884iTrwv6VDRkjfehdOa6SOI,9594
121
+ maxframe/dataframe/datasource/read_odps_query.py,sha256=KRk2dAw_8XbrkYzB8AynVUP1PXTartRSOwfg2zgm1QY,13898
122
+ maxframe/dataframe/datasource/read_odps_table.py,sha256=fcH-ZkaO-Ln8QyvjZQNWCoUiKXk-hhTzaai-uQpHgE8,9667
123
123
  maxframe/dataframe/datasource/read_parquet.py,sha256=SZPrWoax2mwMBNvRk_3lkS72pZLe-_X_GwQ1JROBMs4,14952
124
124
  maxframe/dataframe/datasource/series.py,sha256=elQVupKETh-hUHI2fTu8TRxBE729Vyrmpjx17XlRV-8,1964
125
125
  maxframe/dataframe/datasource/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
@@ -289,10 +289,10 @@ maxframe/io/objects/core.py,sha256=rhEYmS8Ty_cqJfjvTiPU-KaUVnr9DwPSIZEaPMZDH2U,4
289
289
  maxframe/io/objects/tensor.py,sha256=NQaYIItNYaeM0EcJ6TFejNWSSpXKSJjb6eI_ZKzXf6w,2749
290
290
  maxframe/io/objects/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
291
291
  maxframe/io/objects/tests/test_object_io.py,sha256=9SSR8XCDoFIuAj6kafRQ9fKW68pvg8pT2gmqFY8BIfk,3899
292
- maxframe/io/odpsio/__init__.py,sha256=xJbh1eHNI0D3xJG22VM3AHwwiwMIPldtGEJjildPsxc,925
293
- maxframe/io/odpsio/arrow.py,sha256=Q83DRXCsg1HEuv821Y39i3bdddGHPh-ToKj9ZJ2Tx9g,5231
292
+ maxframe/io/odpsio/__init__.py,sha256=7n5ZtsGR4f7B8yA7YPQodd1g6ecvjg5P4baomogUgsA,940
293
+ maxframe/io/odpsio/arrow.py,sha256=PRZb1hMDcN1yRT8WmZNFI-YCwkj3kxyNryTwLgNsUyE,6073
294
294
  maxframe/io/odpsio/schema.py,sha256=HRlz_RqiN86pW6kV2jh-fcrrwRPLfkZtWmUHTTVLVrs,13367
295
- maxframe/io/odpsio/tableio.py,sha256=N-YW_ynkrpHKXzrc4sE0Yj26kqqrRhtX-70az-t4EH4,21679
295
+ maxframe/io/odpsio/tableio.py,sha256=vDRJW5rELwSC2aB3L4LbCNMFtBE27ZG7SxpK97m6V5U,23116
296
296
  maxframe/io/odpsio/volumeio.py,sha256=L-lp6vVGSASmpnsVtA1IC-j5iEOLXEUzXnr82bByveE,3035
297
297
  maxframe/io/odpsio/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
298
298
  maxframe/io/odpsio/tests/test_arrow.py,sha256=yeDWFzsm2IMS-k6jimlQ7uim5T6WW1Anuy8didaf4cs,3194
@@ -332,7 +332,7 @@ maxframe/learn/utils/core.py,sha256=WNDISxPFsWmjkwHwEvjVGCkAOkIftqzEQFPA_KWr7FY,
332
332
  maxframe/lib/__init__.py,sha256=_PB28W40qku6YiT8fJYqdmEdRMQfelOwGeksCOZJfCc,657
333
333
  maxframe/lib/compression.py,sha256=QQpNK79iUC9zck74I0HKMhapSRnLBXtTRyS91taEVIc,1497
334
334
  maxframe/lib/functools_compat.py,sha256=2LTrkSw5i-z5E9XCtZzfg9-0vPrYxicKvDjnnNrAL1Q,2697
335
- maxframe/lib/mmh3.cp311-win_amd64.pyd,sha256=mM09OEoxm-yi5jq_zhKlih5Cq23SR2r0Cgslru0Xtt4,17920
335
+ maxframe/lib/mmh3.cp311-win_amd64.pyd,sha256=2JHSE12UEQGNgEg1c948G2p2w2y6qhTF4efSKeGcnbg,17920
336
336
  maxframe/lib/mmh3.pyi,sha256=pkzO6SUUukCty3X-WFsuokWBtIPWyxEa06ypF9liruI,1537
337
337
  maxframe/lib/version.py,sha256=VOVZu3KHS53YUsb_vQsT7AyHwcCWAgc-3bBqV5ANcbQ,18941
338
338
  maxframe/lib/wrapped_pickle.py,sha256=Akx-qhMMJJ6VVzQYrcVO_umFjx0IR-Yzb1XqyOS1Mio,3976
@@ -386,7 +386,7 @@ maxframe/remote/core.py,sha256=ELCUvg-_ZdTax_exFqm6-XWUXukT5qalKYBeDB-mV0k,6733
386
386
  maxframe/remote/run_script.py,sha256=k93-vaFLUanWoBRai4-78DX_SLeZ8_rbbxcCtOIXZO8,3677
387
387
  maxframe/serialization/__init__.py,sha256=UmDfKNohvk3StL-87QYmQTcQ2g6dRHSk49VFQS73VB0,963
388
388
  maxframe/serialization/arrow.py,sha256=OMeDjLcPgagqzokG7g3Vhwm6Xw1j-Kph1V2QsIwi6dw,3513
389
- maxframe/serialization/core.cp311-win_amd64.pyd,sha256=8a9UgDfEDRnkbyXdw1uWL34o9L_RAla1_5wVudldyzU,423424
389
+ maxframe/serialization/core.cp311-win_amd64.pyd,sha256=FOqTSPY8KFQKr0IvwoB7J9fzAi07nIKcHLoXCrjfWAw,423424
390
390
  maxframe/serialization/core.pxd,sha256=KioRiFhr5DTuqXnS2imJ3djWfSv2IAmhnz-kAFPgU6A,1548
391
391
  maxframe/serialization/core.pyi,sha256=ELDG44v8O7A7RfURExMfVEJENuaEYHTwgJ-vzlH2Vh4,2206
392
392
  maxframe/serialization/core.pyx,sha256=TKEAyaJJAUV20vR_uI93jA6B8mhTt7uoOtMdw0vL5q4,36686
@@ -396,12 +396,12 @@ maxframe/serialization/numpy.py,sha256=ENrFKl24mtYyO1vZRLwHvMD0r4z_UI7J2-yNlmfWS
396
396
  maxframe/serialization/pandas.py,sha256=PMp999KR9xnTFSgRTgepbsD7nGfObNwaYeK4UGJ_T4U,8725
397
397
  maxframe/serialization/scipy.py,sha256=fGwQ5ZreymrMT8g7TneATfFdKFF7YPNZQqgWgMa3J8M,2498
398
398
  maxframe/serialization/serializables/__init__.py,sha256=rlQhIaSAVzz4KYkc5shEHFZDPd6WDMPkxalU76yjJ3M,1406
399
- maxframe/serialization/serializables/core.py,sha256=LARyWxWwPK-eknPHrMGauPG1GJpfjl-mKI50bQSWuz4,16583
399
+ maxframe/serialization/serializables/core.py,sha256=1YjMfOi3kOCcOaiax1v8ScfXn7gwol70JwXcouaMj5w,17972
400
400
  maxframe/serialization/serializables/field.py,sha256=DVott3HAbne4UvN-heSFS9gSl0wCxV5RssS738FCjzk,16639
401
401
  maxframe/serialization/serializables/field_type.py,sha256=NtzY4Zi87futV3Er3tD1qK8UpI7MXOC09h2Uipq9RRA,15525
402
402
  maxframe/serialization/serializables/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
403
403
  maxframe/serialization/serializables/tests/test_field_type.py,sha256=uG87-bdG8xGmjrubEHCww1ZKmRupSvnNKnZoV2SnwYM,4502
404
- maxframe/serialization/serializables/tests/test_serializable.py,sha256=ZzQcsL82r6l0Ny80MdBXJGohoZ2UcEgdA_5bxEjHX6E,10447
404
+ maxframe/serialization/serializables/tests/test_serializable.py,sha256=CfzLa635DR-9xMdNaGhWohzoICFs3pyKCljPYDQEkr4,10568
405
405
  maxframe/serialization/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
406
406
  maxframe/serialization/tests/test_serial.py,sha256=D1cCXylizxdQm6tBwVSVDs7v8ZZJokv7jHN_-nrqf7U,12956
407
407
  maxframe/tensor/__init__.py,sha256=3XN24f4B0eBroWt7XW33_vJCXlbcJj83zgnmAJpz1dQ,4910
@@ -544,7 +544,7 @@ maxframe/tensor/indexing/tests/test_indexing.py,sha256=uatTFiNkYnyqibYa2-NleUWyF
544
544
  maxframe/tensor/merge/__init__.py,sha256=mzXzvbNObPYmvWY3PCu3yFvV82Tqqaxav5DX_Iv2UN8,703
545
545
  maxframe/tensor/merge/concatenate.py,sha256=RRyP-fqUGfMeP3Zo2wgfZcnF-u8dCCIOpr_0jI5Fft8,3314
546
546
  maxframe/tensor/merge/stack.py,sha256=lZJs8M1JYBdYXnalj6-6ltdJKKslZ1Z-ydkzvGlzzkY,4277
547
- maxframe/tensor/merge/vstack.py,sha256=RDlTE2A51b1ku7ep1J0p-bq8EQHUkiXLa0rVHNWOLBI,2338
547
+ maxframe/tensor/merge/vstack.py,sha256=T9lGknzxA62FQJUEoPieZ7iFjCcvgkqfR8zFD9ZMG2Y,2342
548
548
  maxframe/tensor/merge/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
549
549
  maxframe/tensor/merge/tests/test_merge.py,sha256=tI4xeJPpME68buemIzTga9mTQiMoqFc9v4__Hi40kzg,2291
550
550
  maxframe/tensor/misc/__init__.py,sha256=C6VvY-dnO0_8Z1ArbzhT0I0ClFNT2LGfSgPJdICUexo,1185
@@ -656,20 +656,20 @@ maxframe/tests/test_utils.py,sha256=5Z2cym1tlpnF73f4I3WC1eBocsbNEDGzDErJL_fKWx8,
656
656
  maxframe/tests/utils.py,sha256=lrUZul-tUjc-S7HtJDZs5PcrsA4Xecd96xZmNQ1WX-A,5532
657
657
  maxframe_client/__init__.py,sha256=hIVOnxj6AoN2zIMxQCzRb10k0LSoYS_DrQevXO9KPBg,705
658
658
  maxframe_client/conftest.py,sha256=UWWMYjmohHL13hLl4adb0gZPLRdBVOYVvsFo6VZruI0,658
659
- maxframe_client/fetcher.py,sha256=hfzgxDolyI7JtykCpr5Xq3lAQCJGbL2hXaRLXlMlCf0,9477
659
+ maxframe_client/fetcher.py,sha256=cIt1R2k0YV5IKxDQ276x5Dzwk3vFKFyDH3nzRzb6SH0,9445
660
660
  maxframe_client/clients/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
661
661
  maxframe_client/clients/framedriver.py,sha256=GdF_VGr2n4QZTbHy-p_IAaUrHLD1t7DYA-g3BtnylAA,4678
662
662
  maxframe_client/session/__init__.py,sha256=9zFCd3zkSADESAFc4SPoQ2nkvRwsIhhpNNO2TtSaWbU,854
663
663
  maxframe_client/session/consts.py,sha256=USDwwz79zFjhlp2MGz5Th6cCuZaYRLAo491tUanZdDs,1430
664
664
  maxframe_client/session/graph.py,sha256=CEavpl_zmz_nZ2TXI4moRxpdYsMZMokV63889qI6l_4,4501
665
- maxframe_client/session/odps.py,sha256=4Fez9gIKdw_HCHRWujmWUe1ZalSsabClVQnp5mzuJto,24803
666
- maxframe_client/session/task.py,sha256=AYd8DyvmAtYqW_O0_eyMC076O1VG6LvDP7MbpqHzlEg,12175
665
+ maxframe_client/session/odps.py,sha256=HlJ25wt0DLzMrhQVt-izEfrfSEejGMD8D08_x4Xcdto,25586
666
+ maxframe_client/session/task.py,sha256=6uljv5T-VEGKoWDA-E7iSaQMkimQON2vBaunTNLl8HQ,12347
667
667
  maxframe_client/session/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
668
- maxframe_client/session/tests/test_task.py,sha256=kEGse7pEY9pC_Bg2kwFBFILIxIe_84_cPfp-p6MhI7I,3223
668
+ maxframe_client/session/tests/test_task.py,sha256=pzluiWktDx5e7XfJKsasNMAJk83l5RIGbrphwy__yAU,4815
669
669
  maxframe_client/tests/__init__.py,sha256=29eM5D4knhYwe3TF42naTuC5b4Ym3VeH4rK8KpdLWNY,609
670
670
  maxframe_client/tests/test_fetcher.py,sha256=lYtn4NuntN-_A7rMd10e2y_CEUp7zelTij441NTFP6M,4265
671
671
  maxframe_client/tests/test_session.py,sha256=Rn144kIFfJ-ppWNgQ8psLTo0Z_zOk7q3xZCtdiaK_OM,11542
672
- maxframe-1.1.0.dist-info/METADATA,sha256=iOvh1zxFdLLOm6Q8tlQnTVGtQQoHl6T0KtQxVNf7bXQ,3126
673
- maxframe-1.1.0.dist-info/WHEEL,sha256=pa6Q1C4C_AVXqyAePbbb0HCsKIpaeBhf3-2iCk_ZSEA,102
674
- maxframe-1.1.0.dist-info/top_level.txt,sha256=64x-fc2q59c_vXwNUkehyjF1vb8JWqFSdYmUqIFqoTM,31
675
- maxframe-1.1.0.dist-info/RECORD,,
672
+ maxframe-1.1.1.dist-info/METADATA,sha256=SjFIBOd43-2F8ZPWIvamXD2pDQUA9ciTHsW_FevDxK8,3126
673
+ maxframe-1.1.1.dist-info/WHEEL,sha256=nkBcd8Ko0v5sEcSagm2-x_RVrb8gBSkTa8VFFZ0Mr1o,101
674
+ maxframe-1.1.1.dist-info/top_level.txt,sha256=64x-fc2q59c_vXwNUkehyjF1vb8JWqFSdYmUqIFqoTM,31
675
+ maxframe-1.1.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp311-cp311-win_amd64
5
5
 
@@ -20,7 +20,6 @@ import pandas as pd
20
20
  import pyarrow as pa
21
21
  from odps import ODPS
22
22
  from odps.models import ExternalVolume
23
- from odps.tunnel import TableTunnel
24
23
 
25
24
  from maxframe.core import OBJECT_TYPE
26
25
  from maxframe.dataframe.core import DATAFRAME_TYPE
@@ -28,6 +27,7 @@ from maxframe.io.objects import get_object_io_handler
28
27
  from maxframe.io.odpsio import (
29
28
  ODPSTableIO,
30
29
  ODPSVolumeReader,
30
+ TunnelTableIO,
31
31
  arrow_to_pandas,
32
32
  build_dataframe_table_meta,
33
33
  odps_schema_to_pandas_dtypes,
@@ -136,11 +136,10 @@ class ODPSTableFetcher(ToThreadMixin, ResultFetcher):
136
136
  dtypes = odps_schema_to_pandas_dtypes(table.table_schema)
137
137
  tileable.refresh_from_dtypes(dtypes)
138
138
 
139
- tunnel = TableTunnel(self._odps_entry)
140
- total_records = 0
141
- for part_spec in part_specs:
142
- session = tunnel.create_download_session(table, part_spec)
143
- total_records += session.count
139
+ part_sessions = TunnelTableIO.create_download_sessions(
140
+ self._odps_entry, info.full_table_name, part_specs
141
+ )
142
+ total_records = sum(session.count for session in part_sessions.values())
144
143
 
145
144
  new_shape_list = list(tileable.shape)
146
145
  new_shape_list[0] = total_records
@@ -84,10 +84,21 @@ class MaxFrameServiceCaller(metaclass=abc.ABCMeta):
84
84
  def get_settings_to_upload(self) -> Dict[str, Any]:
85
85
  sql_settings = (odps_options.sql.settings or {}).copy()
86
86
  sql_settings.update(options.sql.settings or {})
87
-
88
87
  quota_name = options.session.quota_name or getattr(
89
88
  odps_options, "quota_name", None
90
89
  )
90
+ quota_settings = {
91
+ sql_settings.get("odps.task.wlm.quota", None),
92
+ options.spe.task.settings.get("odps.task.wlm.quota", None),
93
+ options.pythonpack.task.settings.get("odps.task.wlm.quota", None),
94
+ quota_name,
95
+ }.difference([None])
96
+ if len(quota_settings) >= 2:
97
+ raise ValueError(
98
+ "Quota settings are conflicting: %s" % ", ".join(sorted(quota_settings))
99
+ )
100
+ elif len(quota_settings) == 1:
101
+ quota_name = quota_settings.pop()
91
102
  lifecycle = options.session.table_lifecycle or odps_options.lifecycle
92
103
  temp_lifecycle = (
93
104
  options.session.temp_table_lifecycle or odps_options.temp_lifecycle
@@ -332,6 +343,11 @@ class MaxFrameSession(ToThreadMixin, IsolatedAsyncSession):
332
343
  self._last_settings = copy.deepcopy(new_settings)
333
344
  return new_settings
334
345
 
346
+ if self._last_settings.get("session.quota_name", None) != new_settings.get(
347
+ "session.quota_name", None
348
+ ):
349
+ raise ValueError("Quota name cannot be changed after sessions are created")
350
+
335
351
  update = dict()
336
352
  for k in new_settings.keys():
337
353
  old_item = self._last_settings.get(k)
@@ -126,10 +126,13 @@ class MaxFrameInstanceCaller(MaxFrameServiceCaller):
126
126
 
127
127
  def _create_maxframe_task(self) -> MaxFrameTask:
128
128
  task = MaxFrameTask(name=self._task_name, major_version=self._major_version)
129
+ mf_settings = self.get_settings_to_upload()
129
130
  mf_opts = {
130
- "odps.maxframe.settings": json.dumps(self.get_settings_to_upload()),
131
+ "odps.maxframe.settings": json.dumps(mf_settings),
131
132
  "odps.maxframe.output_format": self._output_format,
132
133
  }
134
+ if mf_settings.get("session.quota_name", None):
135
+ mf_opts["odps.task.wlm.quota"] = mf_settings["session.quota_name"]
133
136
  if mf_version:
134
137
  mf_opts["odps.maxframe.client_version"] = mf_version
135
138
  task.update_settings(mf_opts)
@@ -11,17 +11,20 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
-
15
14
  import json
16
15
  import os
17
16
 
18
17
  import mock
18
+ import pytest
19
19
  from defusedxml import ElementTree
20
20
  from odps import ODPS
21
21
  from odps import options as odps_options
22
22
 
23
+ from maxframe import options
24
+ from maxframe.config import option_context
25
+
23
26
  from ...session.consts import MAXFRAME_OUTPUT_JSON_FORMAT
24
- from ...session.task import MaxFrameInstanceCaller, MaxFrameTask
27
+ from ...session.task import MaxFrameInstanceCaller, MaxFrameTask, MaxFrameTaskSession
25
28
 
26
29
  expected_file_dir = os.path.join(os.path.dirname(__file__), "expected-data")
27
30
 
@@ -79,3 +82,33 @@ def test_maxframe_instance_caller_creating_session():
79
82
  finally:
80
83
  odps_options.priority = old_priority
81
84
  odps_options.get_priority = old_get_priority
85
+
86
+
87
+ @pytest.mark.asyncio
88
+ async def test_session_quota_flag_valid():
89
+ def mock_create(self, task: MaxFrameTask, **kwargs):
90
+ assert task.properties["settings"]
91
+ task_settings = json.loads(task.properties["settings"])
92
+ assert task_settings["odps.task.wlm.quota"] == "session_quota"
93
+
94
+ with mock.patch.multiple(
95
+ target="maxframe_client.session.task.MaxFrameInstanceCaller",
96
+ _wait_instance_task_ready=mock.DEFAULT,
97
+ get_session=mock.DEFAULT,
98
+ get_logview_address=mock.DEFAULT,
99
+ ), mock.patch("odps.models.instances.BaseInstances.create", mock_create):
100
+ with option_context({"session.quota_name": "session_quota"}):
101
+ with pytest.raises(ValueError):
102
+ options.sql.settings["odps.task.wlm.quota"] = "session_quota2"
103
+ await MaxFrameTaskSession.init(
104
+ address="test", odps_entry=ODPS.from_environments()
105
+ )
106
+ options.sql.settings["odps.task.wlm.quota"] = "session_quota"
107
+ mf_task_session = await MaxFrameTaskSession.init(
108
+ address="test", odps_entry=ODPS.from_environments()
109
+ )
110
+ with pytest.raises(ValueError):
111
+ options.sql.settings["odps.task.wlm.quota"] = "session_quota2"
112
+ mf_task_session._get_diff_settings()
113
+ options.sql.settings["odps.task.wlm.quota"] = "session_quota"
114
+ mf_task_session._get_diff_settings()