Coverage for python/lsst/daf/butler/_butlerConfig.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22"""
23Configuration classes specific to the Butler
24"""
25from __future__ import annotations
27__all__ = ("ButlerConfig",)
29import copy
30import os.path
31from typing import (
32 Optional
33)
35from .core import (
36 ButlerURI,
37 Config,
38 DatastoreConfig,
39 StorageClassConfig,
40)
41from .registry import RegistryConfig
42from .transfers import RepoTransferFormatConfig
44CONFIG_COMPONENT_CLASSES = (RegistryConfig, StorageClassConfig,
45 DatastoreConfig, RepoTransferFormatConfig)
48class ButlerConfig(Config):
49 """Contains the configuration for a `Butler`
51 The configuration is read and merged with default configurations for
52 the particular classes. The defaults are read according to the rules
53 outlined in `ConfigSubset`. Each component of the configuration associated
54 with a configuration class reads its own defaults.
56 Parameters
57 ----------
58 other : `str`, `Config`, optional
59 Path to butler configuration YAML file or a directory containing a
60 "butler.yaml" file. If `None` the butler will
61 be configured based entirely on defaults read from the environment
62 or from ``searchPaths``.
63 No defaults will be read if a `ButlerConfig` is supplied directly.
64 searchPaths : `list` or `tuple`, optional
65 Explicit additional paths to search for defaults. They should
66 be supplied in priority order. These paths have higher priority
67 than those read from the environment in
68 `ConfigSubset.defaultSearchPaths()`. They are only read if ``other``
69 refers to a configuration file or directory.
70 """
72 def __init__(self, other=None, searchPaths=None):
74 self.configDir: Optional[ButlerURI] = None
76 # If this is already a ButlerConfig we assume that defaults
77 # have already been loaded.
78 if other is not None and isinstance(other, ButlerConfig):
79 super().__init__(other)
80 # Ensure that the configuration directory propagates
81 self.configDir = copy.copy(other.configDir)
82 return
84 if isinstance(other, str):
85 # This will only allow supported schemes
86 uri = ButlerURI(other)
87 if uri.isLocal:
88 # Check explicitly that we have a directory
89 if os.path.isdir(uri.ospath):
90 other = uri.join("butler.yaml")
91 else:
92 # For generic URI assume that we have a directory
93 # if the basename does not have a file extension
94 # This heuristic is needed since we can not rely on
95 # external users to include the trailing / and we
96 # can't always check that the remote resource is a directory.
97 if not uri.dirLike and not uri.getExtension():
98 # Force to a directory and add the default config name
99 uri = ButlerURI(other, forceDirectory=True).join("butler.yaml")
100 other = uri
102 # Create an empty config for us to populate
103 super().__init__()
105 # Read the supplied config so that we can work out which other
106 # defaults to use.
107 butlerConfig = Config(other)
109 configFile = butlerConfig.configFile
110 if configFile is not None:
111 uri = ButlerURI(configFile)
112 self.configDir = uri.dirname()
114 # A Butler config contains defaults defined by each of the component
115 # configuration classes. We ask each of them to apply defaults to
116 # the values we have been supplied by the user.
117 for configClass in CONFIG_COMPONENT_CLASSES:
118 # Only send the parent config if the child
119 # config component is present (otherwise it assumes that the
120 # keys from other components are part of the child)
121 localOverrides = None
122 if configClass.component in butlerConfig:
123 localOverrides = butlerConfig
124 config = configClass(localOverrides, searchPaths=searchPaths)
125 # Re-attach it using the global namespace
126 self.update({configClass.component: config})
127 # Remove the key from the butlerConfig since we have already
128 # merged that information.
129 if configClass.component in butlerConfig:
130 del butlerConfig[configClass.component]
132 # Now that we have all the defaults we can merge the externally
133 # provided config into the defaults.
134 # Not needed if there is never information in a butler config file
135 # not present in component configurations
136 self.update(butlerConfig)