Skip to content

Commit a53b6b5

Browse files
authored
Allow override env-variables in load_catalog (#45)
* swap preference in merge_config * typing
1 parent 0644664 commit a53b6b5

File tree

4 files changed

+70
-3
lines changed

4 files changed

+70
-3
lines changed

pyiceberg/utils/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def merge_config(lhs: RecursiveDict, rhs: RecursiveDict) -> RecursiveDict:
4343
new_config[rhs_key] = merge_config(lhs_value, rhs_value)
4444
else:
4545
# Take the non-null value, with precedence on rhs
46-
new_config[rhs_key] = lhs_value or rhs_value
46+
new_config[rhs_key] = rhs_value or lhs_value
4747
else:
4848
# New key
4949
new_config[rhs_key] = rhs_value

tests/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.

tests/catalog/test_rest.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717
# pylint: disable=redefined-outer-name,unused-argument
18+
import os
19+
from typing import cast
20+
from unittest import mock
1821
from uuid import UUID
1922

2023
import pytest
2124
from requests_mock import Mocker
2225

2326
import pyiceberg
24-
from pyiceberg.catalog import PropertiesUpdateSummary, Table
27+
from pyiceberg.catalog import PropertiesUpdateSummary, Table, load_catalog
2528
from pyiceberg.catalog.rest import RestCatalog
2629
from pyiceberg.exceptions import (
2730
NamespaceAlreadyExistsError,
@@ -38,12 +41,14 @@
3841
from pyiceberg.table.snapshots import Operation, Snapshot, Summary
3942
from pyiceberg.table.sorting import SortField, SortOrder
4043
from pyiceberg.transforms import IdentityTransform, TruncateTransform
44+
from pyiceberg.typedef import RecursiveDict
4145
from pyiceberg.types import (
4246
BooleanType,
4347
IntegerType,
4448
NestedField,
4549
StringType,
4650
)
51+
from pyiceberg.utils.config import Config
4752

4853
TEST_URI = "https://iceberg-test-catalog/"
4954
TEST_CREDENTIALS = "client:secret"
@@ -943,3 +948,41 @@ def test_request_session_with_ssl_client_cert() -> None:
943948
# Missing namespace
944949
RestCatalog("rest", **catalog_properties) # type: ignore
945950
assert "Could not find the TLS certificate file, invalid path: path_to_client_cert" in str(e.value)
951+
952+
953+
EXAMPLE_ENV = {"PYICEBERG_CATALOG__PRODUCTION__URI": TEST_URI}
954+
955+
956+
@mock.patch.dict(os.environ, EXAMPLE_ENV)
957+
@mock.patch("pyiceberg.catalog.Config.get_catalog_config")
958+
def test_catalog_from_environment_variables(catalog_config_mock: mock.Mock, rest_mock: Mocker) -> None:
959+
env_config: RecursiveDict = Config._from_environment_variables({})
960+
catalog_config_mock.return_value = cast(RecursiveDict, env_config.get("catalog")).get("production")
961+
catalog = cast(RestCatalog, load_catalog("production"))
962+
assert catalog.uri == TEST_URI
963+
964+
965+
@mock.patch.dict(os.environ, EXAMPLE_ENV)
966+
@mock.patch("pyiceberg.catalog._ENV_CONFIG.get_catalog_config")
967+
def test_catalog_from_environment_variables_override(catalog_config_mock: mock.Mock, rest_mock: Mocker) -> None:
968+
rest_mock.get(
969+
"https://other-service.io/api/v1/config",
970+
json={"defaults": {}, "overrides": {}},
971+
status_code=200,
972+
)
973+
env_config: RecursiveDict = Config._from_environment_variables({})
974+
975+
catalog_config_mock.return_value = cast(RecursiveDict, env_config.get("catalog")).get("production")
976+
catalog = cast(RestCatalog, load_catalog("production", uri="https://other-service.io/api"))
977+
assert catalog.uri == "https://other-service.io/api"
978+
979+
980+
def test_catalog_from_parameters_empty_env(rest_mock: Mocker) -> None:
981+
rest_mock.get(
982+
"https://other-service.io/api/v1/config",
983+
json={"defaults": {}, "overrides": {}},
984+
status_code=200,
985+
)
986+
987+
catalog = cast(RestCatalog, load_catalog("production", uri="https://other-service.io/api"))
988+
assert catalog.uri == "https://other-service.io/api"

tests/utils/test_config.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
import pytest
2121
from strictyaml import as_document
2222

23-
from pyiceberg.utils.config import Config, _lowercase_dictionary_keys
23+
from pyiceberg.typedef import RecursiveDict
24+
from pyiceberg.utils.config import Config, _lowercase_dictionary_keys, merge_config
2425

2526
EXAMPLE_ENV = {"PYICEBERG_CATALOG__PRODUCTION__URI": "https://service.io/api"}
2627

@@ -54,3 +55,10 @@ def test_lowercase_dictionary_keys() -> None:
5455
uppercase_keys = {"UPPER": {"NESTED_UPPER": {"YES"}}}
5556
expected = {"upper": {"nested_upper": {"YES"}}}
5657
assert _lowercase_dictionary_keys(uppercase_keys) == expected # type: ignore
58+
59+
60+
def test_merge_config() -> None:
61+
lhs: RecursiveDict = {"common_key": "abc123"}
62+
rhs: RecursiveDict = {"common_key": "xyz789"}
63+
result = merge_config(lhs, rhs)
64+
assert result["common_key"] == rhs["common_key"]

0 commit comments

Comments
 (0)