# Copyright (c) 2025 The University of Washington
#
# This file is part of rapidtools.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# You should have received a copy of the BSD 3-Clause License along with
# rapidtools. If not, see <http://www.opensource.org/licenses/>.
#
# Contributors:
# Barbaros Cetiner
#
# Last updated:
# 09-28-2026
"""
Configuration utilities for the rapidtools package.
Centralizes logging formats, HTTP request defaults (headers, timeouts, retry
policy), mask-type definitions, and default colormaps so that every module
shares a single, consistent configuration surface.
Example:
>>> from rapidtools.config import MaskType, get_configured_session
>>> session = get_configured_session(retries=3)
>>> session.adapters['https://'].max_retries.total
3
>>> MaskType.SEMANTIC == 'semantic'
True
"""
import logging
import sys
import warnings
from collections.abc import Iterable
from enum import StrEnum
from typing import Any
import requests
from requests.adapters import HTTPAdapter, Retry
# Logging Configuration:
LOG_FORMAT = '%(asctime)s- %(levelname)s: %(message)s'
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
# Network Configuration:
REQUESTS_HEADERS = {
'User-Agent': (
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/131.0.0.0 Safari/537.36'
),
'Accept': (
'text/html,application/xhtml+xml,application/xml;'
'q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8'
),
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
}
DEFAULT_RETRY_TOTAL = 5
DEFAULT_BACKOFF = 1
DEFAULT_STATUS_FORCELIST = [429, 500, 502, 503, 504]
# Only idempotent methods are retried automatically. A POST that reached the
# server may already have been processed (and billed), so the API clients opt
# in through API_RETRY_METHODS, limited to API_RETRY_STATUSES: the statuses
# that mean the provider did not process the request (rate limited,
# unavailable, or overloaded; 529 is Anthropic's overload status).
DEFAULT_ALLOWED_METHODS = ['HEAD', 'GET', 'OPTIONS']
API_RETRY_METHODS = ['HEAD', 'GET', 'OPTIONS', 'POST']
API_RETRY_STATUSES = [429, 503, 529]
REQUESTS_TIMEOUT_VAL = 30
# Mask & Segmentation Configuration:
class MaskType(StrEnum):
"""
Central definition for available mask types.
Because this is a :class:`~enum.StrEnum`, members compare equal to their
plain string values and can be passed anywhere a string is expected.
Attributes:
SEMANTIC: One label per pixel class (``'semantic'``).
INSTANCE: One label per distinct object instance (``'instance'``).
Example:
>>> from rapidtools.config import MaskType
>>> MaskType('instance') is MaskType.INSTANCE
True
>>> [m.value for m in MaskType]
['semantic', 'instance']
"""
SEMANTIC = 'semantic'
INSTANCE = 'instance'
DEFAULT_SEMANTIC_CMAP = 'tab20'
DEFAULT_INSTANCE_CMAP = 'nipy_spectral'
# Helper functions:
[docs]
def resolve_alias(
kwargs: dict[str, Any],
new_name: str,
*old_names: str,
default: Any = None,
stacklevel: int = 3,
) -> Any:
"""
Pop a deprecated keyword alias from ``kwargs`` and return the value.
Used while parameter names are being harmonized: components accept the
new name (e.g. ``save_directory``) and still honour the old one (e.g.
``output_dir``) with a :class:`DeprecationWarning`.
Args:
kwargs: The ``**kwargs`` dict received by the constructor (mutated).
new_name: The canonical parameter name, for the warning message.
*old_names: Deprecated aliases to look for, in priority order.
default: Value returned when no alias is present.
stacklevel: Passed to :func:`warnings.warn`.
Returns:
Any: The alias value, or ``default``.
Raises:
TypeError: If ``kwargs`` still holds unrelated keys afterwards
(mirrors the error a normal signature would raise).
Example:
>>> from rapidtools.config import resolve_alias
>>> kwargs = {'output_dir': 'tiles'}
>>> resolve_alias(kwargs, 'save_directory', 'output_dir') # warns
'tiles'
>>> kwargs
{}
"""
value = default
for old in old_names:
if old in kwargs:
value = kwargs.pop(old)
warnings.warn(
f"'{old}' is deprecated; use '{new_name}' instead.",
DeprecationWarning,
stacklevel=stacklevel,
)
if kwargs:
unexpected = ', '.join(sorted(kwargs))
raise TypeError(f'Unexpected keyword argument(s): {unexpected}')
return value