Skip to content

Commit edb7bb7

Browse files
authored
Release/2.8.x (#115)
2 parents b936d73 + a82f6a2 commit edb7bb7

37 files changed

+1143
-535
lines changed

ads/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*--
33

4-
# Copyright (c) 2020, 2022 Oracle and/or its affiliates.
4+
# Copyright (c) 2020, 2023 Oracle and/or its affiliates.
55
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
66

77
from __future__ import print_function, division, absolute_import
@@ -26,6 +26,7 @@
2626
from ads.feature_engineering.accessor.dataframe_accessor import ADSDataFrameAccessor
2727
from ads.common import auth
2828
from ads.common.auth import set_auth
29+
from ads.common.config import Config
2930

3031
os.environ["GIT_PYTHON_REFRESH"] = "quiet"
3132

ads/ads_version.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "2.8.2"
2+
"version": "2.8.3"
33
}

ads/common/config.py

+100-18
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8; -*-
33

4-
# Copyright (c) 2022 Oracle and/or its affiliates.
4+
# Copyright (c) 2022, 2023 Oracle and/or its affiliates.
55
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
66

77
import os
88
from collections import defaultdict
99
from configparser import ConfigParser
1010
from copy import copy
1111
from enum import Enum
12-
from typing import Callable, Dict, List, Optional, Tuple, Union, Any
12+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
13+
from urllib.parse import urlparse
1314

1415
import fsspec
1516
import yaml
17+
1618
from ads.common import auth as authutil
1719
from ads.common.decorator.argument_to_case import ArgumentCase, argument_to_case
1820

@@ -287,6 +289,7 @@ def __init__(
287289
self._config_parser = ExtendedConfigParser(uri=self.uri, auth=self.auth)
288290

289291
def _on_change(self):
292+
"""This method will be called when config modified."""
290293
pass
291294

292295
def default(self) -> ConfigSection:
@@ -345,7 +348,7 @@ def section_set(
345348
The new config section will be added in case if it doesn't exist.
346349
Otherwise the existing config section will be merged with the new fields.
347350
348-
Paramaters
351+
Parameters
349352
----------
350353
key: str
351354
A key of a config section.
@@ -362,7 +365,7 @@ def section_set(
362365
Raises
363366
------
364367
ValueError
365-
If section with goven key is already exist and `replace` flag set to False.
368+
If section with given key is already exist and `replace` flag set to False.
366369
TypeError
367370
If input `info` has a wrong format.
368371
"""
@@ -389,7 +392,7 @@ def section_set(
389392
return self._config[key]
390393

391394
@argument_to_case(case=ArgumentCase.UPPER, arguments=["key"])
392-
def section_remove(self, key: str) -> None:
395+
def section_remove(self, key: str) -> "Config":
393396
"""Removes config section form config.
394397
395398
Parameters
@@ -404,26 +407,62 @@ def section_remove(self, key: str) -> None:
404407
"""
405408
self._config.pop(key, None)
406409
self._on_change()
410+
return self
411+
412+
def save(
413+
self,
414+
uri: Optional[str] = None,
415+
auth: Optional[Dict] = None,
416+
force_overwrite: Optional[bool] = False,
417+
) -> "Config":
418+
"""Saves config to a config file.
407419
408-
def save(self) -> None:
409-
"""Saves config data to a config file.
420+
Parameters
421+
----------
422+
uri: (str, optional). Defaults to `~/.ads/config`.
423+
The path to the config file. Can be local or Object Storage file.
424+
auth: (Dict, optional). Defaults to None.
425+
The default authentication is set using `ads.set_auth` API. If you need to override the
426+
default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
427+
authentication signer and kwargs required to instantiate IdentityClient object.
428+
force_overwrite: (bool, optional). Defaults to `False`.
429+
Overwrites the config if exists.
410430
411431
Returns
412432
-------
413433
None
414434
Nothing
415435
"""
416-
self._config_parser.with_dict(self.to_dict()).save()
436+
uri = uri or self.uri
437+
auth = auth or self.auth or authutil.default_signer()
438+
self._config_parser.with_dict(self.to_dict()).save(
439+
uri=uri, auth=auth, force_overwrite=force_overwrite
440+
)
441+
return self
442+
443+
def load(self, uri: Optional[str] = None, auth: Optional[Dict] = None) -> "Config":
444+
"""Loads config from a config file.
417445
418-
def load(self) -> "Config":
419-
"""Loads config data from a config file.
446+
Parameters
447+
----------
448+
uri: (str, optional). Defaults to `~/.ads/config`.
449+
The path where the config file needs to be saved. Can be local or Object Storage file.
450+
auth: (Dict, optional). Defaults to None.
451+
The default authentication is set using `ads.set_auth` API. If you need to override the
452+
default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
453+
authentication signer and kwargs required to instantiate IdentityClient object.
420454
421455
Returns
422456
-------
423457
Config
424458
A config object.
425459
"""
426-
return self.with_dict(self._config_parser.read().to_dict())
460+
uri = uri or self.uri
461+
auth = auth or self.auth or authutil.default_signer()
462+
463+
return self.with_dict(
464+
self._config_parser.read(uri=uri, auth=auth).to_dict(), replace=True
465+
)
427466

428467
def with_dict(
429468
self,
@@ -507,23 +546,54 @@ def __init__(
507546
uri: (str, optional). Defaults to `~/.ads/config`.
508547
The path to the config file. Can be local or Object Storage file.
509548
auth: (Dict, optional). Defaults to None.
510-
The default authetication is set using `ads.set_auth` API. If you need to override the
549+
The default authentication is set using `ads.set_auth` API. If you need to override the
511550
default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
512551
authentication signer and kwargs required to instantiate IdentityClient object.
513552
"""
514553
super().__init__(default_section="EXCLUDE_DEFAULT_SECTION")
515554
self.auth = auth or authutil.default_signer()
516555
self.uri = uri
517556

518-
def save(self) -> None:
557+
def save(
558+
self,
559+
uri: Optional[str] = None,
560+
auth: Optional[Dict] = None,
561+
force_overwrite: Optional[bool] = False,
562+
) -> None:
519563
"""Saves the config to the file.
520564
565+
Parameters
566+
----------
567+
uri: (str, optional). Defaults to `~/.ads/config`.
568+
The path to the config file. Can be local or Object Storage file.
569+
auth: (Dict, optional). Defaults to None.
570+
The default authentication is set using `ads.set_auth` API. If you need to override the
571+
default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
572+
authentication signer and kwargs required to instantiate IdentityClient object.
573+
force_overwrite: (bool, optional). Defaults to `False`.
574+
Overwrites the config if exists.
575+
521576
Returns
522577
-------
523578
None
524-
nothing
579+
580+
Raise
581+
-----
582+
FileExistsError
583+
In case if file exists and force_overwrite is false.
525584
"""
526-
with fsspec.open(self.uri, mode="w", **self.auth) as f:
585+
uri = uri or self.uri
586+
auth = auth or self.auth or authutil.default_signer()
587+
588+
if not force_overwrite:
589+
dst_path_scheme = urlparse(uri).scheme or "file"
590+
if fsspec.filesystem(dst_path_scheme, **auth).exists(uri):
591+
raise FileExistsError(
592+
f"The `{uri}` exists. Set `force_overwrite` to True "
593+
"if you wish to overwrite."
594+
)
595+
596+
with fsspec.open(uri, mode="w", **auth) as f:
527597
self.write(f)
528598

529599
def to_dict(self) -> Dict[str, Any]:
@@ -532,19 +602,31 @@ def to_dict(self) -> Dict[str, Any]:
532602
Returns
533603
-------
534604
Dict[str, Any]
535-
Cofig in a dictionary format.
605+
Config in a dictionary format.
536606
"""
537607
return {s: dict(self[s]) for s in self.keys() if self[s]}
538608

539-
def read(self) -> "ExtendedConfigParser":
609+
def read(
610+
self, uri: Optional[str] = None, auth: Optional[Dict] = None
611+
) -> "ExtendedConfigParser":
540612
"""Reads config file.
541613
614+
uri: (str, optional). Defaults to `~/.ads/config`.
615+
The path to the config file. Can be local or Object Storage file.
616+
auth: (Dict, optional). Defaults to None.
617+
The default authentication is set using `ads.set_auth` API. If you need to override the
618+
default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
619+
authentication signer and kwargs required to instantiate IdentityClient object.
620+
542621
Returns
543622
-------
544623
ExtendedConfigParser
545624
Config parser object.
546625
"""
547-
with fsspec.open(self.uri, "r", **self.auth) as f:
626+
uri = uri or self.uri
627+
auth = auth or self.auth or authutil.default_signer()
628+
629+
with fsspec.open(uri, "r", **auth) as f:
548630
self.read_string(f.read())
549631
return self
550632

ads/common/oci_datascience.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8; -*-
33

4-
# Copyright (c) 2021, 2022 Oracle and/or its affiliates.
4+
# Copyright (c) 2021, 2023 Oracle and/or its affiliates.
55
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
66

77
import oci.data_science
88
from ads.common.oci_mixin import OCIModelMixin
9+
from ads.common.decorator.utils import class_or_instance_method
910

1011

1112
class OCIDataScienceMixin(OCIModelMixin):
12-
@classmethod
13+
@class_or_instance_method
1314
def init_client(cls, **kwargs) -> oci.data_science.DataScienceClient:
1415
return cls._init_client(client=oci.data_science.DataScienceClient, **kwargs)
1516

ads/common/oci_logging.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import oci.logging
1313
import oci.loggingsearch
14+
import oci.exceptions
1415
from ads.common.decorator.utils import class_or_instance_method
1516
from ads.common.oci_mixin import OCIModelMixin, OCIWorkRequestMixin
1617
from ads.common.oci_resource import OCIResource, ResourceNotFoundError
@@ -370,12 +371,21 @@ def _search_logs(
370371
time_end=self.format_datetime(time_end),
371372
search_query=search_query,
372373
)
373-
response = self.search_client.search_logs(
374-
search_details, limit=LIMIT_PER_REQUEST
375-
)
376-
records = response.data.results
377-
logger.debug("%d logs received.", len(records))
378-
if len(records) >= LIMIT_PER_REQUEST:
374+
result_too_large = False
375+
try:
376+
response = self.search_client.search_logs(
377+
search_details, limit=LIMIT_PER_REQUEST
378+
)
379+
records = response.data.results
380+
logger.debug("%d logs received.", len(records))
381+
except oci.exceptions.ServiceError as ex:
382+
if ex.status == 400 and "search result is too large" in ex.message:
383+
logger.debug(ex.message)
384+
records = []
385+
result_too_large = True
386+
else:
387+
raise oci.exceptions.ServiceError from ex
388+
if result_too_large or len(records) >= LIMIT_PER_REQUEST:
379389
mid = time_start + (time_end - time_start) / 2
380390
# The log search API used RFC3339 time format.
381391
# The minimum time unit of RFC3339 format is 1000 microseconds.

ads/common/utils.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -1259,12 +1259,13 @@ def copy_from_uri(
12591259
to_path = temp_dir
12601260
else:
12611261
unpack_path = None
1262+
12621263
fs = fsspec.filesystem(scheme, **auth)
1263-
try:
1264-
fs.get(uri, to_path, recursive=True)
1265-
except IsADirectoryError:
1264+
1265+
if not (uri.endswith("/") or fs.isdir(uri)) and os.path.isdir(to_path):
12661266
to_path = os.path.join(to_path, os.path.basename(str(uri).rstrip("/")))
1267-
fs.get(uri, to_path, recursive=True)
1267+
1268+
fs.get(uri, to_path, recursive=True)
12681269

12691270
if unpack_path:
12701271
shutil.unpack_archive(to_path, unpack_path)

0 commit comments

Comments
 (0)