Coverage for python/lsst/daf/butler/_butlerConfig.py: 25%
Shortcuts 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
Shortcuts 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 os
30import copy
31from typing import (
32 Optional,
33 Sequence,
34 Union,
35)
37from .core import (
38 ButlerURI,
39 Config,
40 DatastoreConfig,
41 StorageClassConfig,
42)
43from .registry import RegistryConfig
44from .transfers import RepoTransferFormatConfig
46CONFIG_COMPONENT_CLASSES = (RegistryConfig, StorageClassConfig,
47 DatastoreConfig, RepoTransferFormatConfig)
50class ButlerConfig(Config):
51 """Contains the configuration for a `Butler`
53 The configuration is read and merged with default configurations for
54 the particular classes. The defaults are read according to the rules
55 outlined in `ConfigSubset`. Each component of the configuration associated
56 with a configuration class reads its own defaults.
58 Parameters
59 ----------
60 other : `str`, `Config`, `ButlerConfig`, optional
61 Path to butler configuration YAML file or a directory containing a
62 "butler.yaml" file. If `None` the butler will
63 be configured based entirely on defaults read from the environment
64 or from ``searchPaths``.
65 No defaults will be read if a `ButlerConfig` is supplied directly.
66 searchPaths : `list` or `tuple`, optional
67 Explicit additional paths to search for defaults. They should
68 be supplied in priority order. These paths have higher priority
69 than those read from the environment in
70 `ConfigSubset.defaultSearchPaths()`. They are only read if ``other``
71 refers to a configuration file or directory.
72 """
74 def __init__(self, other: Optional[Union[str, ButlerURI, Config]] = None,
75 searchPaths: Sequence[Union[str, ButlerURI]] = None):
77 self.configDir: Optional[ButlerURI] = None
79 # If this is already a ButlerConfig we assume that defaults
80 # have already been loaded.
81 if other is not None and isinstance(other, ButlerConfig):
82 super().__init__(other)
83 # Ensure that the configuration directory propagates
84 self.configDir = copy.copy(other.configDir)
85 return
87 if isinstance(other, (str, os.PathLike)):
88 # This will only allow supported schemes
89 uri = ButlerURI(other)
91 # We allow the butler configuration file to be left off the
92 # URI supplied by the user. If a directory-like URI is given
93 # we add the default configuration name.
95 # It's easy to miss a trailing / for remote URIs so try to guess
96 # we have been given a directory-like URI if there is no
97 # file extension. Local URIs do not need any guess work.
98 if not uri.isLocal and not uri.getExtension():
99 uri = ButlerURI(other, forceDirectory=True)
101 if uri.isdir():
102 # Could also be butler.json (for example in the butler
103 # server) but checking for existence will slow things
104 # down given that this might involve two checks and then
105 # the config read below would still do the read.
106 other = uri.join("butler.yaml")
108 # Create an empty config for us to populate
109 super().__init__()
111 # Read the supplied config so that we can work out which other
112 # defaults to use.
113 butlerConfig = Config(other)
115 configFile = butlerConfig.configFile
116 if configFile is not None:
117 uri = ButlerURI(configFile)
118 self.configFile = uri
119 self.configDir = uri.dirname()
121 # A Butler config contains defaults defined by each of the component
122 # configuration classes. We ask each of them to apply defaults to
123 # the values we have been supplied by the user.
124 for configClass in CONFIG_COMPONENT_CLASSES:
125 # Only send the parent config if the child
126 # config component is present (otherwise it assumes that the
127 # keys from other components are part of the child)
128 localOverrides = None
129 if configClass.component in butlerConfig:
130 localOverrides = butlerConfig
131 config = configClass(localOverrides, searchPaths=searchPaths)
132 # Re-attach it using the global namespace
133 self.update({configClass.component: config})
134 # Remove the key from the butlerConfig since we have already
135 # merged that information.
136 if configClass.component in butlerConfig:
137 del butlerConfig[configClass.component]
139 # Now that we have all the defaults we can merge the externally
140 # provided config into the defaults.
141 # Not needed if there is never information in a butler config file
142 # not present in component configurations
143 self.update(butlerConfig)