Hide keyboard shortcuts

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/>. 

21 

22""" 

23Configuration classes specific to the Butler 

24""" 

25 

26__all__ = ("ButlerConfig",) 

27 

28import os.path 

29 

30from .core import ( 

31 ButlerURI, 

32 CompositesConfig, 

33 Config, 

34 DatastoreConfig, 

35 DimensionConfig, 

36 RepoTransferFormatConfig, 

37 StorageClassConfig, 

38) 

39from .registry import RegistryConfig 

40 

41CONFIG_COMPONENT_CLASSES = (RegistryConfig, StorageClassConfig, 

42 DatastoreConfig, CompositesConfig, DimensionConfig, 

43 RepoTransferFormatConfig) 

44 

45 

46class ButlerConfig(Config): 

47 """Contains the configuration for a `Butler` 

48 

49 The configuration is read and merged with default configurations for 

50 the particular classes. The defaults are read according to the rules 

51 outlined in `ConfigSubset`. Each component of the configuration associated 

52 with a configuration class reads its own defaults. 

53 

54 Parameters 

55 ---------- 

56 other : `str`, `Config`, optional 

57 Path to butler configuration YAML file or a directory containing a 

58 "butler.yaml" file. If `None` the butler will 

59 be configured based entirely on defaults read from the environment 

60 or from ``searchPaths``. 

61 No defaults will be read if a `ButlerConfig` is supplied directly. 

62 searchPaths : `list` or `tuple`, optional 

63 Explicit additional paths to search for defaults. They should 

64 be supplied in priority order. These paths have higher priority 

65 than those read from the environment in 

66 `ConfigSubset.defaultSearchPaths()`. They are only read if ``other`` 

67 refers to a configuration file or directory. 

68 """ 

69 

70 def __init__(self, other=None, searchPaths=None): 

71 

72 self.configDir = None 

73 

74 # If this is already a ButlerConfig we assume that defaults 

75 # have already been loaded. 

76 if other is not None and isinstance(other, ButlerConfig): 

77 super().__init__(other) 

78 # Ensure that the configuration directory propagates 

79 self.configDir = other.configDir 

80 return 

81 

82 if isinstance(other, str): 

83 uri = ButlerURI(other) 

84 if uri.scheme == "file" or not uri.scheme: 

85 if os.path.isdir(uri.ospath): 

86 other = os.path.join(uri.ospath, "butler.yaml") 

87 elif uri.scheme == "s3": 

88 if not uri.dirLike and "." not in uri.basename(): 

89 uri = ButlerURI(other, forceDirectory=True) 

90 uri.updateFile("butler.yaml") 

91 other = uri.geturl() 

92 else: 

93 raise ValueError(f"Unrecognized URI scheme: {uri.scheme}") 

94 

95 # Create an empty config for us to populate 

96 super().__init__() 

97 

98 # Read the supplied config so that we can work out which other 

99 # defaults to use. 

100 butlerConfig = Config(other) 

101 

102 configFile = butlerConfig.configFile 

103 if configFile is not None: 

104 uri = ButlerURI(configFile) 

105 if uri.scheme == "s3": 

106 uri.updateFile("") 

107 self.configDir = uri.geturl() 

108 else: 

109 self.configDir = os.path.dirname(os.path.abspath(configFile)) 

110 

111 # A Butler config contains defaults defined by each of the component 

112 # configuration classes. We ask each of them to apply defaults to 

113 # the values we have been supplied by the user. 

114 for configClass in CONFIG_COMPONENT_CLASSES: 

115 # Only send the parent config if the child 

116 # config component is present (otherwise it assumes that the 

117 # keys from other components are part of the child) 

118 localOverrides = None 

119 if configClass.component in butlerConfig: 

120 localOverrides = butlerConfig 

121 config = configClass(localOverrides, searchPaths=searchPaths) 

122 # Re-attach it using the global namespace 

123 self.update({configClass.component: config}) 

124 # Remove the key from the butlerConfig since we have already 

125 # merged that information. 

126 if configClass.component in butlerConfig: 

127 del butlerConfig[configClass.component] 

128 

129 # Now that we have all the defaults we can merge the externally 

130 # provided config into the defaults. 

131 # Not needed if there is never information in a butler config file 

132 # not present in component configurations 

133 self.update(butlerConfig)