From adf77d9453aec1e8e5d59baac359ae447e1b72ff Mon Sep 17 00:00:00 2001 From: jayy-77 <1427jay@email.com> Date: Tue, 17 Feb 2026 15:09:53 +0530 Subject: [PATCH] Improve error handling in config loading and exceptions for better clarity on missing or malformed config keys. --- README.rst | 30 ++++++++++++++++++++++++------ examples/quickstart_identity.py | 24 ++++++++++++++++++++++++ src/oci/config.py | 10 ++++++++-- src/oci/exceptions.py | 19 ++++++++++++++++++- 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 examples/quickstart_identity.py diff --git a/README.rst b/README.rst index 16154c7dc4..e8a1467a00 100644 --- a/README.rst +++ b/README.rst @@ -9,16 +9,14 @@ This is the Python SDK for Oracle Cloud Infrastructure. Supported Python version __ https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/pythonsdk.htm#pythonsdk_topic-supported_python_versions +Quickstart +========== + .. code-block:: pycon >>> import oci - # Set up config - >>> config = oci.config.from_file( - ... "~/.oci/config", - ... "DEFAULT") - # Create a service client + >>> config = oci.config.from_file() >>> identity = oci.identity.IdentityClient(config) - # Get the current user >>> user = identity.get_user(config["user"]).data >>> print(user) { @@ -31,6 +29,26 @@ __ https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/pythonsdk.htm#pythonsd "time_created": "2016-08-30T23:46:44.680000+00:00" } +If you need to load a different profile or config file: + +.. code-block:: pycon + + >>> config = oci.config.from_file("~/.oci/config", "DEFAULT") + +You can also point the SDK at a config file via environment variable: + +.. code-block:: sh + + OCI_CONFIG_FILE=~/.oci/config python your_script.py + +Instance principals (running on an OCI compute instance) can be used without a config file: + +.. code-block:: python + + import oci + signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner() + identity = oci.identity.IdentityClient(config={}, signer=signer) + The project is open source and maintained by Oracle Corp. The home page for the project is `here`__. __ https://docs.oracle.com/en-us/iaas/tools/python/latest/index.html diff --git a/examples/quickstart_identity.py b/examples/quickstart_identity.py new file mode 100644 index 0000000000..f1fcf068b8 --- /dev/null +++ b/examples/quickstart_identity.py @@ -0,0 +1,24 @@ +import oci + + +def main() -> None: + config = oci.config.from_file() + oci.config.validate_config(config) + + identity = oci.identity.IdentityClient(config) + tenancy_id = config["tenancy"] + + compartments = identity.list_compartments( + compartment_id=tenancy_id, + compartment_id_in_subtree=True, + access_level="ACCESSIBLE", + ).data + + print("Accessible compartments:") + for c in compartments: + print(f"- {c.name} ({c.id})") + + +if __name__ == "__main__": + main() + diff --git a/src/oci/config.py b/src/oci/config.py index 01b0d9aa4a..2dea8e33ba 100644 --- a/src/oci/config.py +++ b/src/oci/config.py @@ -107,7 +107,13 @@ def from_file(file_location=DEFAULT_LOCATION, profile_name=DEFAULT_PROFILE): raise ConfigFileNotFound("Could not find config file at {}, please follow the instructions in the link to setup the config file https://docs.cloud.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm".format(expanded_file_location)) if profile_name not in parser: - raise ProfileNotFound("Profile '{}' not found in config file {} ".format(profile_name, expanded_file_location) + CONFIG_FILE_DEBUG_INFORMATION_LOG) + available_profiles = ", ".join(parser.sections()) if parser.sections() else "(none)" + raise ProfileNotFound( + "Profile '{}' not found in config file {}. Available profiles: {}. ".format( + profile_name, expanded_file_location, available_profiles + ) + + CONFIG_FILE_DEBUG_INFORMATION_LOG + ) config = dict(DEFAULT_CONFIG) config.update(parser[profile_name]) @@ -136,7 +142,7 @@ def validate_config(config, **kwargs): validator_function(config) return - """Raises ValueError if required fields are missing or malformed.""" + """Raises InvalidConfig if required fields are missing or malformed.""" errors = {} for required_key in REQUIRED: fallback_key = REQUIRED_FALLBACKS.get(required_key) diff --git a/src/oci/exceptions.py b/src/oci/exceptions.py index 5f40746d05..dd6fcb60cf 100644 --- a/src/oci/exceptions.py +++ b/src/oci/exceptions.py @@ -101,7 +101,24 @@ def __init__(self, errors): self.errors = errors def __str__(self): - return str(self.errors) + if not isinstance(self.errors, dict): + return str(self.errors) + + missing = sorted([k for k, v in self.errors.items() if v == "missing"]) + malformed = sorted([k for k, v in self.errors.items() if v == "malformed"]) + other = {k: v for k, v in self.errors.items() if v not in ("missing", "malformed")} + + parts = ["Invalid OCI config."] + if missing: + parts.append("Missing required keys: {}".format(", ".join(missing))) + if malformed: + parts.append("Malformed values for keys: {}".format(", ".join(malformed))) + if other: + parts.append("Other issues: {}".format(other)) + parts.append( + "Config reference: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm" + ) + return " ".join(parts) class InvalidAlloyConfig(ClientError):