• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

georgia-tech-db / eva / e6605954-d2af-406f-a2e4-075433fee53b

16 Sep 2023 06:13PM UTC coverage: 80.158% (+10.2%) from 69.982%
e6605954-d2af-406f-a2e4-075433fee53b

push

circle-ci

Chitti-Ankith
lint fixes

8 of 8 new or added lines in 1 file covered. (100.0%)

9522 of 11879 relevant lines covered (80.16%)

1.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.89
/evadb/utils/generic_utils.py
1
# coding=utf-8
2
# Copyright 2018-2023 EvaDB
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
import hashlib
2✔
16
import importlib
2✔
17
import inspect
2✔
18
import os
2✔
19
import pickle
2✔
20
import shutil
2✔
21
import sys
2✔
22
import uuid
2✔
23
from pathlib import Path
2✔
24
from typing import List
2✔
25
from urllib.parse import urlparse
2✔
26

27
from aenum import AutoEnum, unique
2✔
28

29
from evadb.configuration.constants import EvaDB_INSTALLATION_DIR
2✔
30
from evadb.utils.logging_manager import logger
2✔
31

32

33
def validate_kwargs(
2✔
34
    kwargs,
35
    allowed_keys: List[str],
36
    required_keys: List[str],
37
    error_message="Keyword argument not understood:",
38
):
39
    """Checks that all keyword arguments are in the set of allowed keys."""
40
    if required_keys is None:
1✔
41
        required_keys = allowed_keys
×
42
    for kwarg in kwargs:
1✔
43
        if kwarg not in allowed_keys:
1✔
44
            raise TypeError(error_message, kwarg)
45

46
    missing_keys = [key for key in required_keys if key not in kwargs]
1✔
47
    assert len(missing_keys) == 0, f"Missing required keys, {missing_keys}"
1✔
48

49

50
def str_to_class(class_path: str):
2✔
51
    """
52
    Convert string representation of a class path to Class
53

54
    Arguments:
55
        class_path (str): absolute path of import
56

57
    Returns:
58
        type: A Class for given path
59
    """
60
    assert class_path is not None, "Class path is not found"
1✔
61
    module_path, class_name = class_path.rsplit(".", 1)
1✔
62
    module = importlib.import_module(module_path)
1✔
63
    return getattr(module, class_name)
1✔
64

65

66
def load_function_class_from_file(filepath, classname=None):
2✔
67
    """
68
    Load a class from a Python file. If the classname is not specified, the function will check if there is only one class in the file and load that. If there are multiple classes, it will raise an error.
69

70
    Args:
71
        filepath (str): The path to the Python file.
72
        classname (str, optional): The name of the class to load. If not specified, the function will try to load a class with the same name as the file. Defaults to None.
73

74
    Returns:
75
        The class instance.
76

77
    Raises:
78
        RuntimeError: If the class name is not found or there is more than one class in the file.
79
    """
80
    try:
2✔
81
        abs_path = Path(filepath).resolve()
2✔
82
        spec = importlib.util.spec_from_file_location(abs_path.stem, abs_path)
2✔
83
        module = importlib.util.module_from_spec(spec)
2✔
84
        spec.loader.exec_module(module)
2✔
85
    except Exception as e:
86
        err_msg = f"Couldn't load function from {filepath} : {str(e)}. This might be due to a missing Python package, or because the function implementation file does not exist, or it is not a valid Python file."
87
        raise RuntimeError(err_msg)
88

89
    # Try to load the specified class by name
90
    if classname and hasattr(module, classname):
2✔
91
        return getattr(module, classname)
2✔
92

93
    # If class name not specified, check if there is only one class in the file
94
    classes = [
1✔
95
        obj
96
        for _, obj in inspect.getmembers(module, inspect.isclass)
97
        if obj.__module__ == module.__name__
98
    ]
99
    if len(classes) != 1:
1✔
100
        raise RuntimeError(
101
            f"{filepath} contains {len(classes)} classes, please specify the correct class to load by naming the function with the same name in the CREATE query."
102
        )
103
    return classes[0]
1✔
104

105

106
def is_gpu_available() -> bool:
2✔
107
    """
108
    Checks if the system has GPUS available to execute tasks
109
    Returns:
110
        [bool] True if system has GPUs, else False
111
    """
112
    try:
2✔
113
        import torch
2✔
114

115
        return torch.cuda.is_available()
2✔
116
    except ImportError:
117
        return False
118

119

120
def get_gpu_count() -> int:
2✔
121
    """
122
    Check number of GPUs through Torch.
123
    """
124
    try:
1✔
125
        import torch
1✔
126

127
        return torch.cuda.device_count()
1✔
128
    except ImportError:
129
        return 0
130

131

132
def generate_file_path(dataset_location: str, name: str = "") -> Path:
2✔
133
    """Generates a arbitrary file_path(md5 hash) based on the a random salt
134
    and name
135

136
    Arguments:
137
        dataset_location(str): parent directory where a file needs to be created
138
        name (str): Input file_name.
139

140
    Returns:
141
        Path: pathlib.Path object
142

143
    """
144
    dataset_location = Path(dataset_location)
2✔
145
    dataset_location.mkdir(parents=True, exist_ok=True)
2✔
146

147
    salt = uuid.uuid4().hex
2✔
148
    file_name = hashlib.md5(salt.encode() + name.encode()).hexdigest()
2✔
149
    path = dataset_location / file_name
2✔
150
    return path.resolve()
2✔
151

152

153
def get_size(obj, seen=None):
2✔
154
    """Recursively finds size of objects
155
    https://goshippo.com/blog/measure-real-size-any-python-object/
156
    """
157
    size = sys.getsizeof(obj)
2✔
158
    if seen is None:
2✔
159
        seen = set()
2✔
160
    obj_id = id(obj)
2✔
161
    if obj_id in seen:
2✔
162
        return 0
2✔
163
    # Important mark as seen *before* entering recursion to gracefully handle
164
    # self-referential objects
165
    seen.add(obj_id)
2✔
166
    if isinstance(obj, dict):
2✔
167
        size += sum([get_size(v, seen) for v in obj.values()])
2✔
168
        size += sum([get_size(k, seen) for k in obj.keys()])
2✔
169
    elif hasattr(obj, "__dict__"):
2✔
170
        size += get_size(obj.__dict__, seen)
2✔
171
    elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
2✔
172
        size += sum([get_size(i, seen) for i in obj])
2✔
173
    return size
2✔
174

175

176
def get_str_hash(s: str) -> str:
2✔
177
    return hashlib.md5(s.encode("utf-8")).hexdigest()
×
178

179

180
def get_file_checksum(fname: str) -> str:
2✔
181
    """Compute checksum of the file contents
182

183
    Args:
184
        fname (str): file path
185

186
    Returns:
187
        str: hash string representing the checksum of the file content
188
    """
189
    hash_md5 = hashlib.md5()
2✔
190
    with open(fname, "rb") as f:
2✔
191
        for chunk in iter(lambda: f.read(4096), b""):
2✔
192
            hash_md5.update(chunk)
2✔
193
    return hash_md5.hexdigest()
2✔
194

195

196
def parse_config_yml():
2✔
197
    """
198
    Parses the 'evadb.yml' file and returns the config object.
199
    """
200
    import yaml
2✔
201

202
    f = open(Path(EvaDB_INSTALLATION_DIR) / "evadb.yml", "r+")
2✔
203
    config_obj = yaml.load(f, Loader=yaml.FullLoader)
2✔
204
    return config_obj
2✔
205

206

207
def is_postgres_uri(db_uri):
2✔
208
    """
209
    Determines if the db_uri is that of postgres.
210

211
    Args:
212
        db_uri (str) : db_uri to parse
213
    """
214
    parsed_uri = urlparse(db_uri)
2✔
215
    return parsed_uri.scheme == "postgres" or parsed_uri.scheme == "postgresql"
2✔
216

217

218
class PickleSerializer(object):
2✔
219
    @classmethod
2✔
220
    def serialize(cls, data):
2✔
221
        return pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
2✔
222

223
    @classmethod
2✔
224
    def deserialize(cls, data):
2✔
225
        return pickle.loads(data)
2✔
226

227

228
@unique
2✔
229
class EvaDBEnum(AutoEnum):
2✔
230
    def __str__(self):
2✔
231
        return self.name
2✔
232

233

234
def remove_directory_contents(dir_path):
2✔
235
    if os.path.exists(dir_path):
2✔
236
        for filename in os.listdir(dir_path):
2✔
237
            file_path = os.path.join(dir_path, filename)
2✔
238
            try:
2✔
239
                if os.path.isfile(file_path) or os.path.islink(file_path):
2✔
240
                    os.unlink(file_path)
2✔
241
                elif os.path.isdir(file_path):
2✔
242
                    shutil.rmtree(file_path)
2✔
243
            except Exception as e:
244
                logger.warning(f"Failed to delete {file_path}. Reason: {str(e)}")
245

246

247
def find_nearest_word(word, word_list):
2✔
248
    from thefuzz import process
1✔
249

250
    nearest_word_and_score = process.extractOne(word, word_list)
1✔
251
    nearest_word = nearest_word_and_score[0]
1✔
252

253
    return nearest_word
1✔
254

255

256
##############################
257
## TRY TO IMPORT PACKAGES
258
##############################
259

260

261
def try_to_import_ray():
2✔
262
    try:
2✔
263
        import ray  # noqa: F401
2✔
264
        from ray.util.queue import Queue  # noqa: F401
2✔
265
    except ImportError:
266
        raise ValueError(
267
            """Could not import ray python package.
268
                Please install it with `pip install ray`."""
269
        )
270

271

272
def try_to_import_forecast():
2✔
273
    try:
2✔
274
        from statsforecast import StatsForecast  # noqa: F401
2✔
275
    except ImportError:
276
        raise ValueError(
277
            """Could not import StatsForecast python package.
278
                Please install it with `pip install statsforecast`."""
279
        )
280

281

282
def is_ray_available() -> bool:
2✔
283
    try:
2✔
284
        try_to_import_ray()
2✔
285
        return True
2✔
286
    except ValueError:  # noqa: E722
287
        return False
288

289

290
def is_ray_enabled_and_installed(ray_enabled: bool) -> bool:
2✔
291
    ray_installed = is_ray_available()
2✔
292
    return ray_enabled and ray_installed
2✔
293

294

295
##############################
296
## MODEL TRAIN FRAMEWORK
297
##############################
298

299

300
def try_to_import_ludwig():
2✔
301
    try:
2✔
302
        import ludwig  # noqa: F401
2✔
303
        from ludwig.automl import auto_train  # noqa: F401
2✔
304
    except ImportError:
305
        raise ValueError(
306
            """Could not import ludwig.
307
                Please install it with `pip install ludwig[full]`."""
308
        )
309

310

311
def is_ludwig_available() -> bool:
2✔
312
    try:
2✔
313
        try_to_import_ludwig()
2✔
314
        return True
2✔
315
    except ValueError:  # noqa: E722
316
        return False
317

318

319
def is_forecast_available() -> bool:
2✔
320
    try:
2✔
321
        try_to_import_forecast()
2✔
322
        return True
2✔
323
    except ValueError:  # noqa: E722
324
        return False
325

326

327
##############################
328
## VISION
329
##############################
330

331

332
def try_to_import_pillow():
2✔
333
    try:
2✔
334
        import PIL  # noqa: F401
2✔
335
    except ImportError:
336
        raise ValueError(
337
            """Could not import pillow python package.
338
                Please install it with `pip install pillow`."""
339
        )
340

341

342
def try_to_import_torch():
2✔
343
    try:
2✔
344
        import torch  # noqa: F401
2✔
345
    except ImportError:
346
        raise ValueError(
347
            """Could not import torch python package.
348
                Please install them with `pip install torch`."""
349
        )
350

351

352
def try_to_import_torchvision():
2✔
353
    try:
2✔
354
        import torchvision  # noqa: F401
2✔
355
    except ImportError:
356
        raise ValueError(
357
            """Could not import torchvision python package.
358
                Please install them with `pip install torchvision`."""
359
        )
360

361

362
def try_to_import_cv2():
2✔
363
    try:
2✔
364
        import cv2  # noqa: F401
2✔
365
    except ImportError:
366
        raise ValueError(
367
            """Could not import cv2 python package.
368
                Please install it with `pip install opencv-python`."""
369
        )
370

371

372
def try_to_import_timm():
2✔
373
    try:
×
374
        import timm  # noqa: F401
×
375
    except ImportError:
376
        raise ValueError(
377
            """Could not import timm python package.
378
                Please install them with `pip install timm`."""
379
        )
380

381

382
def try_to_import_kornia():
2✔
383
    try:
2✔
384
        import kornia  # noqa: F401
2✔
385
    except ImportError:
386
        raise ValueError(
387
            """Could not import kornia python package.
388
                Please install it with `pip install kornia`."""
389
        )
390

391

392
def try_to_import_decord():
2✔
393
    try:
2✔
394
        import decord  # noqa: F401
2✔
395
    except ImportError:
396
        raise ValueError(
397
            """Could not import decord python package.
398
                Please install it with `pip install eva-decord`."""
399
        )
400

401

402
def try_to_import_ultralytics():
2✔
403
    try:
2✔
404
        import ultralytics  # noqa: F401
2✔
405
    except ImportError:
406
        raise ValueError(
407
            """Could not import ultralytics python package.
408
                Please install it with `pip install ultralytics`."""
409
        )
410

411

412
def try_to_import_norfair():
2✔
413
    try:
2✔
414
        import norfair  # noqa: F401
2✔
415
    except ImportError:
416
        raise ValueError(
417
            """Could not import norfair python package.
418
                Please install it with `pip install norfair`."""
419
        )
420

421

422
##############################
423
## DOCUMENT
424
##############################
425

426

427
def try_to_import_transformers():
2✔
428
    try:
×
429
        import transformers  # noqa: F401
×
430
    except ImportError:
431
        raise ValueError(
432
            """Could not import transformers python package.
433
                Please install it with `pip install transformers`."""
434
        )
435

436

437
def try_to_import_facenet_pytorch():
2✔
438
    try:
2✔
439
        import facenet_pytorch  # noqa: F401
2✔
440
    except ImportError:
441
        raise ValueError(
442
            """Could not import facenet_pytorch python package.
443
                Please install it with `pip install facenet-pytorch`."""
444
        )
445

446

447
def try_to_import_openai():
2✔
448
    try:
×
449
        import openai  # noqa: F401
×
450
    except ImportError:
451
        raise ValueError(
452
            """Could not import openai python package.
453
                Please install them with `pip install openai`."""
454
        )
455

456

457
def try_to_import_langchain():
2✔
458
    try:
×
459
        import langchain  # noqa: F401
×
460
    except ImportError:
461
        raise ValueError(
462
            """Could not import langchain package.
463
                Please install it with `pip install langchain`."""
464
        )
465

466

467
##############################
468
## VECTOR STORES
469
##############################
470

471

472
def try_to_import_faiss():
2✔
473
    try:
2✔
474
        import faiss  # noqa: F401
2✔
475
    except ImportError:
476
        raise ValueError(
477
            """Could not import faiss python package.
478
                Please install it with `pip install faiss-cpu` or `pip install faiss-gpu`."""
479
        )
480

481

482
def try_to_import_qdrant_client():
2✔
483
    try:
2✔
484
        import qdrant_client  # noqa: F401
2✔
485
    except ImportError:
486
        raise ValueError(
487
            """Could not import qdrant_client python package.
488
                Please install it with `pip install qdrant_client`."""
489
        )
490

491

492
def try_to_import_pinecone_client():
2✔
493
    try:
×
494
        import pinecone  # noqa: F401
×
495
    except ImportError:
496
        raise ValueError(
497
            """Could not import pinecone_client python package.
498
                Please install it with 'pip install pinecone_client`."""
499
        )
500

501

502
def is_qdrant_available() -> bool:
2✔
503
    try:
2✔
504
        try_to_import_qdrant_client()
2✔
505
        return True
2✔
506
    except ValueError:  # noqa: E722
507
        return False
508

509

510
##############################
511
## UTILS
512
##############################
513

514

515
def try_to_import_moto():
2✔
516
    try:
×
517
        import boto3  # noqa: F401
×
518
    except ImportError:
519
        raise ValueError(
520
            """Could not import boto3 python package.
521
                Please install it with `pip install moto[s3]`."""
522
        )
523

524

525
def try_to_import_fitz():
2✔
526
    try:
1✔
527
        import fitz  # noqa: F401
1✔
528
    except ImportError:
529
        raise ValueError(
530
            """Could not import fitz python package.
531
                Please install it with `pip install pymupdfs`."""
532
        )
533

534

535
def string_comparison_case_insensitive(string_1, string_2) -> bool:
2✔
536
    """
537
    Case insensitive string comparison for two strings which gives
538
    a bool response whether the strings are the same or not
539

540
    Arguments:
541
        string_1 (str)
542
        string_2 (str)
543

544
    Returns:
545
        True/False (bool): Returns True if the strings are same, false otherwise
546
    """
547

548
    # Does not make sense in case of null strings
549
    if string_1 is None or string_2 is None:
2✔
550
        return False
2✔
551

552
    return string_1.lower() == string_2.lower()
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc