malevich-coretools 0.2.2__py3-none-any.whl → 0.2.3__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.

Potentially problematic release.


This version of malevich-coretools might be problematic. Click here for more details.

@@ -1 +1,2 @@
1
1
  from .utils import * # noqa: F403
2
+ from .tools import vast_settings
@@ -103,6 +103,8 @@ class UserApp(BaseModel):
103
103
  outputId: Optional[Alias.Id]
104
104
  cfg: Optional[Alias.Json]
105
105
  image: JsonImage
106
+ platform: str
107
+ platformSettings: Optional[str]
106
108
  collectionsFrom: Optional[Dict[str, str]]
107
109
  extraCollectionsFrom: Optional[Dict[str, str]]
108
110
 
@@ -391,3 +393,4 @@ class AppFunctionsInfo(BaseModel):
391
393
  schemes: Dict[str, str] = dict()
392
394
  inits: Dict[str, InitInfo] = dict()
393
395
  logs: Optional[str] = None
396
+ instanceInfo: Optional[str] = None # json with info about instance
@@ -12,6 +12,7 @@ LONG_SLEEP_TIME = 1 # second
12
12
  WAIT_RESULT_TIMEOUT = 60 * 60 # hour
13
13
  AIOHTTP_TIMEOUT = aiohttp.ClientTimeout(total=60 * 10) # 10 min
14
14
  AIOHTTP_TIMEOUT_MINI = aiohttp.ClientTimeout(total=60 * 5) # 5 min
15
+ POSSIBLE_APPS_PLATFORMS = {"base", "vast"}
15
16
  # endpoints
16
17
 
17
18
  def with_wait(url, wait) -> str:
@@ -0,0 +1 @@
1
+ from .vast import vast_settings
@@ -0,0 +1,306 @@
1
+ from typing import Dict, Optional, Union, Any
2
+ import re
3
+ import json
4
+
5
+ # from https://raw.githubusercontent.com/vast-ai/vast-python/master/vast.py
6
+ def parse_query(query_str: str, res: Dict = None) -> Dict:
7
+ """
8
+ Basically takes a query string (like the ones in the examples of commands for the search__offers function) and
9
+ processes it into a dict of URL parameters to be sent to the server.
10
+
11
+ :param str query_str:
12
+ :param Dict res:
13
+ :return Dict:
14
+ """
15
+ if res is None: res = {}
16
+ if type(query_str) == list:
17
+ query_str = " ".join(query_str)
18
+ query_str = query_str.strip()
19
+ opts = re.findall("([a-zA-Z0-9_]+)( *[=><!]+| +(?:[lg]te?|nin|neq|eq|not ?eq|not ?in|in) )?( *)(\[[^\]]+\]|[^ ]+)?( *)", query_str)
20
+
21
+ #print(opts)
22
+ # res = {}
23
+ op_names = {
24
+ ">=": "gte",
25
+ ">": "gt",
26
+ "gt": "gt",
27
+ "gte": "gte",
28
+ "<=": "lte",
29
+ "<": "lt",
30
+ "lt": "lt",
31
+ "lte": "lte",
32
+ "!=": "neq",
33
+ "==": "eq",
34
+ "=": "eq",
35
+ "eq": "eq",
36
+ "neq": "neq",
37
+ "noteq": "neq",
38
+ "not eq": "neq",
39
+ "notin": "notin",
40
+ "not in": "notin",
41
+ "nin": "notin",
42
+ "in": "in",
43
+ };
44
+
45
+ field_alias = {
46
+ "cuda_vers": "cuda_max_good",
47
+ "display_active": "gpu_display_active",
48
+ "reliability": "reliability2",
49
+ "dlperf_usd": "dlperf_per_dphtotal",
50
+ "dph": "dph_total",
51
+ "flops_usd": "flops_per_dphtotal",
52
+ };
53
+
54
+ field_multiplier = {
55
+ "cpu_ram": 1000,
56
+ "gpu_ram": 1000,
57
+ "duration": 24.0 * 60.0 * 60.0,
58
+ }
59
+
60
+ fields = {
61
+ "bw_nvlink",
62
+ "compute_cap",
63
+ "cpu_cores",
64
+ "cpu_cores_effective",
65
+ "cpu_ram",
66
+ "cuda_max_good",
67
+ "datacenter",
68
+ "direct_port_count",
69
+ "driver_version",
70
+ "disk_bw",
71
+ "disk_space",
72
+ "dlperf",
73
+ "dlperf_per_dphtotal",
74
+ "dph_total",
75
+ "duration",
76
+ "external",
77
+ "flops_per_dphtotal",
78
+ "gpu_display_active",
79
+ # "gpu_ram_free_min",
80
+ "gpu_mem_bw",
81
+ "gpu_name",
82
+ "gpu_ram",
83
+ "gpu_display_active",
84
+ "has_avx",
85
+ "host_id",
86
+ "id",
87
+ "inet_down",
88
+ "inet_down_cost",
89
+ "inet_up",
90
+ "inet_up_cost",
91
+ "machine_id",
92
+ "min_bid",
93
+ "mobo_name",
94
+ "num_gpus",
95
+ "pci_gen",
96
+ "pcie_bw",
97
+ "reliability2",
98
+ "rentable",
99
+ "rented",
100
+ "storage_cost",
101
+ "static_ip",
102
+ "total_flops",
103
+ "verification",
104
+ "verified",
105
+ "geolocation"
106
+ };
107
+
108
+ joined = "".join("".join(x) for x in opts)
109
+ if joined != query_str:
110
+ raise ValueError(
111
+ "Unconsumed text. Did you forget to quote your query? " + repr(joined) + " != " + repr(query_str))
112
+
113
+ for field, op, _, value, _ in opts:
114
+ value = value.strip(",[]")
115
+ v = res.setdefault(field, {})
116
+ op = op.strip()
117
+ op_name = op_names.get(op)
118
+
119
+ if field in field_alias:
120
+ field = field_alias[field]
121
+
122
+ if (field == "driver_version") and ('.' in value):
123
+ value = numeric_version(value)
124
+
125
+ if not field in fields:
126
+ print("Warning: Unrecognized field: {}, see list of recognized fields.".format(field), file=sys.stderr);
127
+ if not op_name:
128
+ raise ValueError("Unknown operator. Did you forget to quote your query? " + repr(op).strip("u"))
129
+ if op_name in ["in", "notin"]:
130
+ value = [x.strip() for x in value.split(",") if x.strip()]
131
+ if not value:
132
+ raise ValueError("Value cannot be blank. Did you forget to quote your query? " + repr((field, op, value)))
133
+ if not field:
134
+ raise ValueError("Field cannot be blank. Did you forget to quote your query? " + repr((field, op, value)))
135
+ if value in ["?", "*", "any"]:
136
+ if op_name != "eq":
137
+ raise ValueError("Wildcard only makes sense with equals.")
138
+ if field in v:
139
+ del v[field]
140
+ if field in res:
141
+ del res[field]
142
+ continue
143
+
144
+ if field in field_multiplier:
145
+ value = float(value) * field_multiplier[field]
146
+
147
+ if isinstance(value, str):
148
+ v[op_name] = value.replace('_', ' ')
149
+ else:
150
+ v[op_name] = value
151
+
152
+ else:
153
+ #print(value)
154
+ if value == 'true':
155
+ v[op_name] = True
156
+ elif value == 'False':
157
+ v[op_name] = False
158
+ elif isinstance(value, str):
159
+ v[op_name] = value.replace('_', ' ')
160
+ else:
161
+ #v[op_name] = [v.replace('_', ' ') for v in value]
162
+ v[op_name] = value
163
+
164
+ res[field] = v;
165
+
166
+ return res
167
+
168
+ # from https://raw.githubusercontent.com/vast-ai/vast-python/master/vast.py
169
+ def parse(search_query: str, show_order: str = "score-", no_default: bool = False, instance_type = "on-demand") -> Dict[str, Any]:
170
+ """Query syntax:
171
+
172
+ query = comparison comparison...
173
+ comparison = field op value
174
+ field = <name of a field>
175
+ op = one of: <, <=, ==, !=, >=, >, in, notin
176
+ value = <bool, int, float, etc> | 'any'
177
+ bool: True, False
178
+
179
+ note: to pass '>' and '<' on the command line, make sure to use quotes
180
+ note: to encode a string query value (ie for gpu_name), replace any spaces ' ' with underscore '_'
181
+
182
+
183
+ Examples:
184
+
185
+ # search for somewhat reliable single RTX 3090 instances, filter out any duplicates or offers that conflict with our existing stopped instances
186
+ ./vast search offers 'reliability > 0.98 num_gpus=1 gpu_name=RTX_3090 rented=False'
187
+
188
+ # search for datacenter gpus with minimal compute_cap and total_flops
189
+ ./vast search offers 'compute_cap > 610 total_flops > 5 datacenter=True'
190
+
191
+ # search for reliable machines with at least 4 gpus, unverified, order by num_gpus, allow duplicates
192
+ ./vast search offers 'reliability > 0.99 num_gpus>=4 verified=False rented=any' -o 'num_gpus-'
193
+
194
+
195
+ Available fields:
196
+
197
+ Name Type Description
198
+
199
+ bw_nvlink float bandwidth NVLink
200
+ compute_cap: int cuda compute capability*100 (ie: 650 for 6.5, 700 for 7.0)
201
+ cpu_cores: int # virtual cpus
202
+ cpu_cores_effective: float # virtual cpus you get
203
+ cpu_ram: float system RAM in gigabytes
204
+ cuda_vers: float machine max supported cuda version (based on driver version)
205
+ datacenter: bool show only datacenter offers
206
+ direct_port_count int open ports on host's router
207
+ disk_bw: float disk read bandwidth, in MB/s
208
+ disk_space: float disk storage space, in GB
209
+ dlperf: float DL-perf score (see FAQ for explanation)
210
+ dlperf_usd: float DL-perf/$
211
+ dph: float $/hour rental cost
212
+ driver_version string machine's nvidia driver version as 3 digit string ex. "535.86.05"
213
+ duration: float max rental duration in days
214
+ external: bool show external offers in addition to datacenter offers
215
+ flops_usd: float TFLOPs/$
216
+ geolocation: string Two letter country code. Works with operators =, !=, in, not in (e.g. geolocation not in [XV,XZ])
217
+ gpu_mem_bw: float GPU memory bandwidth in GB/s
218
+ gpu_name: string GPU model name (no quotes, replace spaces with underscores, ie: RTX_3090 rather than 'RTX 3090')
219
+ gpu_ram: float GPU RAM in GB
220
+ gpu_frac: float Ratio of GPUs in the offer to gpus in the system
221
+ gpu_display_active: bool True if the GPU has a display attached
222
+ has_avx: bool CPU supports AVX instruction set.
223
+ id: int instance unique ID
224
+ inet_down: float internet download speed in Mb/s
225
+ inet_down_cost: float internet download bandwidth cost in $/GB
226
+ inet_up: float internet upload speed in Mb/s
227
+ inet_up_cost: float internet upload bandwidth cost in $/GB
228
+ machine_id int machine id of instance
229
+ min_bid: float current minimum bid price in $/hr for interruptible
230
+ num_gpus: int # of GPUs
231
+ pci_gen: float PCIE generation
232
+ pcie_bw: float PCIE bandwidth (CPU to GPU)
233
+ reliability: float machine reliability score (see FAQ for explanation)
234
+ rentable: bool is the instance currently rentable
235
+ rented: bool allow/disallow duplicates and potential conflicts with existing stopped instances
236
+ storage_cost: float storage cost in $/GB/month
237
+ static_ip: bool is the IP addr static/stable
238
+ total_flops: float total TFLOPs from all GPUs
239
+ verified: bool is the machine verified
240
+ """
241
+ field_alias = {
242
+ "cuda_vers": "cuda_max_good",
243
+ "reliability": "reliability2",
244
+ "dlperf_usd": "dlperf_per_dphtotal",
245
+ "dph": "dph_total",
246
+ "flops_usd": "flops_per_dphtotal",
247
+ };
248
+
249
+ try:
250
+
251
+ if no_default:
252
+ query = {}
253
+ else:
254
+ query = {"verified": {"eq": True}, "external": {"eq": False}, "rentable": {"eq": True}, "rented": {"eq": False}}
255
+ #query = {"verified": {"eq": True}, "external": {"eq": False}, "rentable": {"eq": True} }
256
+
257
+ if search_query is not None:
258
+ query = parse_query(search_query, query)
259
+
260
+ order = []
261
+ for name in show_order.split(","):
262
+ name = name.strip()
263
+ if not name: continue
264
+ direction = "asc"
265
+ if name.strip("-") != name:
266
+ direction = "desc"
267
+ field = name.strip("-");
268
+ if field in field_alias:
269
+ field = field_alias[field];
270
+ order.append([field, direction])
271
+
272
+ query["order"] = order
273
+ query["type"] = instance_type
274
+ # For backwards compatibility, support --type=interruptible option
275
+ if query["type"] == 'interruptible':
276
+ query["type"] = 'bid'
277
+ except ValueError as e:
278
+ print("Error: ", e)
279
+ return None
280
+ return query
281
+
282
+ def vast_settings(id: Optional[str] = None, query: Optional[Union[str, Dict[str, Any]]] = None) -> str:
283
+ """
284
+ Platform settings for create app:\n
285
+ by nothing - use default query inside\n
286
+ by offer id - try use this id for app\n
287
+ by query - use this query inside\n
288
+
289
+ Examples:\n
290
+ vast_settings()\n
291
+ vast_settings(id="<id, that found with vast (vastai search offers)>")\n
292
+ vast_settings(query={"dph": {"lt": "0.1"}})\n
293
+ vast_settings(query="dph<0.1")\n
294
+ """
295
+ res = {}
296
+ if id is not None:
297
+ assert query is None, "id and query set"
298
+ res["id"] = id
299
+ elif query is not None:
300
+ if isinstance(query, str):
301
+ res["query"] = parse(query, no_default=True) # run only with this options
302
+ else:
303
+ assert isinstance(query, dict), "wrong query type: it should be str or dict"
304
+ res["query"] = query
305
+ return json.dumps(res)
306
+
@@ -8,7 +8,7 @@ import malevich_coretools.funcs.funcs as f
8
8
  import malevich_coretools.funcs.helpers as fh
9
9
  from malevich_coretools.abstract import * # noqa: F403
10
10
  from malevich_coretools.secondary import Config, to_json
11
- from malevich_coretools.secondary.const import WAIT_RESULT_TIMEOUT
11
+ from malevich_coretools.secondary.const import POSSIBLE_APPS_PLATFORMS, WAIT_RESULT_TIMEOUT
12
12
  from malevich_coretools.secondary.helpers import rand_str
13
13
 
14
14
  # config
@@ -392,11 +392,12 @@ def get_app_real(id: str, *, auth: Optional[AUTH]=None, conn_url: Optional[str]=
392
392
  return f.get_userApps_realId(id, auth=auth, conn_url=conn_url)
393
393
 
394
394
 
395
- def create_app(app_id: str, processor_id: str, input_id: Optional[str]=None, output_id: Optional[str]=None, app_cfg: Optional[Union[Dict[str, Any], Alias.Json]] = None, image_ref: Optional[str] = None, image_auth: Optional[AUTH]=None, collections_from: Optional[Dict[str, str]]=None, extra_collections_from: Optional[Dict[str, str]]=None, wait: bool = True, *, auth: Optional[AUTH]=None, conn_url: Optional[str]=None) -> Alias.Id:
395
+ def create_app(app_id: str, processor_id: str, input_id: Optional[str]=None, output_id: Optional[str]=None, app_cfg: Optional[Union[Dict[str, Any], Alias.Json]] = None, image_ref: Optional[str] = None, image_auth: Optional[AUTH]=None, platform: str = "base", platform_settings: Optional[str] = None, collections_from: Optional[Dict[str, str]]=None, extra_collections_from: Optional[Dict[str, str]]=None, wait: bool = True, *, auth: Optional[AUTH]=None, conn_url: Optional[str]=None) -> Alias.Id:
396
396
  """create app\n
397
397
  \"app_cfg\" must be json or dict or None\n
398
398
  \"image_ref\" automatically generated by id, but this is not always True, it is better to set it\n
399
399
  return \"id\""""
400
+ assert platform in POSSIBLE_APPS_PLATFORMS, f"wrong platform: {platform}, possible platforms: {POSSIBLE_APPS_PLATFORMS}"
400
401
  app_cfg_json = None if app_cfg is None else to_json(app_cfg)
401
402
  image_user = image_auth[0] if image_auth is not None else Config.USERNAME
402
403
  image_token = image_auth[1] if image_auth is not None else Config.TOKEN
@@ -407,14 +408,15 @@ def create_app(app_id: str, processor_id: str, input_id: Optional[str]=None, out
407
408
  if Config.WITH_WARNINGS:
408
409
  Config.logger.warning(f"assumed image ref: {image_ref}")
409
410
  json_image = JsonImage(ref=image_ref, user=image_user, token=image_token)
410
- app = UserApp(appId=app_id, inputId=input_id, processorId=processor_id, outputId=output_id, cfg=app_cfg_json, image=json_image, collectionsFrom=collections_from, extraCollectionsFrom=extra_collections_from)
411
+ app = UserApp(appId=app_id, inputId=input_id, processorId=processor_id, outputId=output_id, cfg=app_cfg_json, image=json_image, platform=platform, platformSettings=platform_settings, collectionsFrom=collections_from, extraCollectionsFrom=extra_collections_from)
411
412
  return f.post_userApps(app, wait=wait, auth=auth, conn_url=conn_url)
412
413
 
413
414
 
414
- def update_app(id: str, app_id: str, processor_id: str, input_id: Optional[str]=None, output_id: Optional[str]=None, app_cfg: Optional[Union[Dict[str, Any], Alias.Json]] = None, image_ref: Optional[str] = None, image_auth: Optional[AUTH]=None, collections_from: Optional[Dict[str, str]]=None, extra_collections_from: Optional[Dict[str, str]]=None, wait: bool = True, *, auth: Optional[AUTH]=None, conn_url: Optional[str]=None) -> Alias.Info:
415
+ def update_app(id: str, app_id: str, processor_id: str, input_id: Optional[str]=None, output_id: Optional[str]=None, app_cfg: Optional[Union[Dict[str, Any], Alias.Json]] = None, image_ref: Optional[str] = None, image_auth: Optional[AUTH]=None, platform: str = "base", platform_settings: Optional[str] = None, collections_from: Optional[Dict[str, str]]=None, extra_collections_from: Optional[Dict[str, str]]=None, wait: bool = True, *, auth: Optional[AUTH]=None, conn_url: Optional[str]=None) -> Alias.Info:
415
416
  """update app by \"id\"\n
416
417
  \"app_cfg\" must be json or dict or None\n
417
418
  \"image_ref\" automatically generated by id, but this is not always True, it is better to set it"""
419
+ assert platform in POSSIBLE_APPS_PLATFORMS, f"wrong platform: {platform}, possible platforms: {POSSIBLE_APPS_PLATFORMS}"
418
420
  app_cfg_json = None if app_cfg is None else to_json(app_cfg)
419
421
  image_user = image_auth[0] if image_auth is not None else Config.USERNAME
420
422
  image_token = image_auth[1] if image_auth is not None else Config.TOKEN
@@ -425,7 +427,7 @@ def update_app(id: str, app_id: str, processor_id: str, input_id: Optional[str]=
425
427
  if Config.WITH_WARNINGS:
426
428
  Config.logger.warning(f"assumed image ref: {image_ref}")
427
429
  json_image = JsonImage(ref=image_ref, user=image_user, token=image_token)
428
- app = UserApp(appId=app_id, inputId=input_id, processorId=processor_id, outputId=output_id, cfg=app_cfg_json, image=json_image, collectionsFrom=collections_from, extraCollectionsFrom=extra_collections_from)
430
+ app = UserApp(appId=app_id, inputId=input_id, processorId=processor_id, outputId=output_id, cfg=app_cfg_json, image=json_image, platform=platform, platformSettings=platform_settings, collectionsFrom=collections_from, extraCollectionsFrom=extra_collections_from)
429
431
  return f.post_userApps_id(id, app, wait=wait, auth=auth, conn_url=conn_url)
430
432
 
431
433
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: malevich-coretools
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Author: Andrew Pogrebnoj
5
5
  Author-email: andrew@onjulius.co
6
6
  License-File: LICENSE
@@ -0,0 +1,18 @@
1
+ malevich_coretools/__init__.py,sha256=-JwDbXfkEcEYt-MaDDHvUHI8jQLj0RqkVjvr92WL-jw,68
2
+ malevich_coretools/utils.py,sha256=ge2xAykEnZ7P7s6cm2jsx05ruN8OPxgSzFXAwz2BtPw,52367
3
+ malevich_coretools/abstract/__init__.py,sha256=ZWtP4gFLpNVFXzlMKXEK4iMnUqqMg07pL8yKGSJd6QI,38
4
+ malevich_coretools/abstract/abstract.py,sha256=coe2Lmp9totwEfnQh_l1oEcSa6p7bdVCXdS7O4Gx94A,8221
5
+ malevich_coretools/funcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ malevich_coretools/funcs/funcs.py,sha256=ltgFnCeye1Mc0ErMwDqHX7qOTuJYVCY9QScWiGbPDkg,27291
7
+ malevich_coretools/funcs/helpers.py,sha256=Y8v6uyawYl2_G0XmhmiVLqGIUzvxnkpKF0ODA9vrB3Y,1276
8
+ malevich_coretools/secondary/__init__.py,sha256=048HqvG36_1WdDVZK_RuECmaf14Iq2fviUysG1inlaE,78
9
+ malevich_coretools/secondary/config.py,sha256=b7RysQTkPKib8FEhfm3uU7TPvz8vgWLlqakvZN5NFs4,391
10
+ malevich_coretools/secondary/const.py,sha256=jBIynrBTA-MmL07ob270ihS01sHRYRHbdcOlljy0FgU,7523
11
+ malevich_coretools/secondary/helpers.py,sha256=yQiaG_Q_5sw04RFpZccoSryPBuoA25BpSmvzSyQtwK0,2937
12
+ malevich_coretools/tools/__init__.py,sha256=87oIj4h-inVh8yuPW9vtTLrEZ6zNp6yD6jClSmsU4AE,32
13
+ malevich_coretools/tools/vast.py,sha256=gAGJNBPdiLTzQZfZHlx-1hqMMcnoD2M6dmEYgFvl37I,9515
14
+ malevich_coretools-0.2.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
15
+ malevich_coretools-0.2.3.dist-info/METADATA,sha256=avZDIWtWk3kf3nnkyyYRmCGt4Lw2GHKOgMpLRW6-2Uo,236
16
+ malevich_coretools-0.2.3.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
17
+ malevich_coretools-0.2.3.dist-info/top_level.txt,sha256=wDX3s1Tso0otBPNrFRfXqyNpm48W4Bp5v6JfbITO2Z8,19
18
+ malevich_coretools-0.2.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.41.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- malevich_coretools/__init__.py,sha256=zdIcHs3T_NZ8HYWts-O7OpBEWHIu779QDZMGF5HRCLg,35
2
- malevich_coretools/utils.py,sha256=d6SGJqdk1iYqAO6cltAO0c0HSg4YUeyhqo1xPGPgaVc,51852
3
- malevich_coretools/abstract/__init__.py,sha256=ZWtP4gFLpNVFXzlMKXEK4iMnUqqMg07pL8yKGSJd6QI,38
4
- malevich_coretools/abstract/abstract.py,sha256=ElmocG9HAIir65Ja8eaXCr6uze_4k64C8cdxoZTM37U,8095
5
- malevich_coretools/funcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- malevich_coretools/funcs/funcs.py,sha256=ltgFnCeye1Mc0ErMwDqHX7qOTuJYVCY9QScWiGbPDkg,27291
7
- malevich_coretools/funcs/helpers.py,sha256=Y8v6uyawYl2_G0XmhmiVLqGIUzvxnkpKF0ODA9vrB3Y,1276
8
- malevich_coretools/secondary/__init__.py,sha256=048HqvG36_1WdDVZK_RuECmaf14Iq2fviUysG1inlaE,78
9
- malevich_coretools/secondary/config.py,sha256=b7RysQTkPKib8FEhfm3uU7TPvz8vgWLlqakvZN5NFs4,391
10
- malevich_coretools/secondary/const.py,sha256=_QsiuvKy7ct4woHKrln_crXkQG0EgpJ8EnOGb428kKA,7480
11
- malevich_coretools/secondary/helpers.py,sha256=yQiaG_Q_5sw04RFpZccoSryPBuoA25BpSmvzSyQtwK0,2937
12
- malevich_coretools-0.2.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- malevich_coretools-0.2.2.dist-info/METADATA,sha256=ZpMpNbYJcEW3Lrrx2ggnZ6ZruO6PTfQ5LVOw8DGgAGE,236
14
- malevich_coretools-0.2.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
15
- malevich_coretools-0.2.2.dist-info/top_level.txt,sha256=wDX3s1Tso0otBPNrFRfXqyNpm48W4Bp5v6JfbITO2Z8,19
16
- malevich_coretools-0.2.2.dist-info/RECORD,,