LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
_GenericMap.py
Go to the documentation of this file.
1# This file is part of afw.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
21
22__all__ = ["GenericMap", "MutableGenericMap"]
23
24from collections.abc import Mapping, MutableMapping
25
26from lsst.utils import TemplateMeta
27from ._typehandling import GenericMapS, MutableGenericMapS
28
29
30class GenericMap(metaclass=TemplateMeta):
31 """An abstract `~collections.abc.Mapping` for use when sharing a
32 map between C++ and Python.
33
34 For compatibility with C++, ``GenericMap`` has the following
35 restrictions:
36
37 - all keys must be of the same type
38 - values must be built-in types or subclasses of
39 `lsst.afw.typehandling.Storable`. Almost any user-defined class in
40 C++ or Python can have `~lsst.afw.typehandling.Storable` as a mixin.
41
42 As a safety precaution, `~lsst.afw.typehandling.Storable` objects that are
43 added from C++ may be copied when you retrieve them from Python, making it
44 impossible to modify them in-place. This issue does not affect objects that
45 are added from Python, or objects that are always passed by
46 :cpp:class:`shared_ptr` in C++.
47 """
48
49 def __repr__(self):
50 className = type(self).__name__
51 return className + "({" + ", ".join("%r: %r" % (key, value) for key, value in self.items()) + "})"
52
53 # Support equality with any Mapping, including dict
54 # Not clear why Mapping.__eq__ doesn't work
55 def __eq__(self, other):
56 if len(self) != len(other):
57 return False
58
59 for key, value in self.items():
60 try:
61 if (value != other[key]):
62 return False
63 except KeyError:
64 return False
65 return True
66
67 # Easier than making GenericMap actually inherit from Mapping
68 keys = Mapping.keys
69 values = Mapping.values
70 items = Mapping.items
71
72
73GenericMap.register(str, GenericMapS)
74Mapping.register(GenericMapS)
75
76
78 """An abstract `~collections.abc.MutableMapping` for use when sharing a
79 map between C++ and Python.
80
81 For compatibility with C++, ``MutableGenericMap`` has the following
82 restrictions:
83
84 - all keys must be of the same type
85 - values must be built-in types or subclasses of
86 `lsst.afw.typehandling.Storable`. Almost any user-defined class in
87 C++ or Python can have `~lsst.afw.typehandling.Storable` as a mixin.
88
89 As a safety precaution, `~lsst.afw.typehandling.Storable` objects that are
90 added from C++ may be copied when you retrieve them from Python, making it
91 impossible to modify them in-place. This issue does not affect objects that
92 are added from Python, or objects that are always passed by
93 :cpp:class:`shared_ptr` in C++.
94
95 Notes
96 -----
97 Key-type specializations of ``MutableGenericMap`` are available as, e.g.,
98 ``MutableGenericMap[str]``.
99 """
100
101 # Easier than making MutableGenericMap actually inherit from MutableMapping
102 setdefault = MutableMapping.setdefault
103 update = MutableMapping.update
104
105 # MutableMapping.pop relies on implementation details of MutableMapping
106 def pop(self, key, default=None):
107 try:
108 value = self[key]
109 del self[key]
110 return value
111 except KeyError:
112 if default is not None:
113 return default
114 else:
115 raise
116
117
118MutableGenericMap.register(str, MutableGenericMapS)
119MutableMapping.register(MutableGenericMapS)
120
121
122class AutoKeyMeta(TemplateMeta):
123 """A metaclass for abstract mappings whose key type is implied by their
124 constructor arguments.
125
126 This metaclass requires that the mapping have a `dict`-like constructor,
127 i.e., it takes a mapping or an iterable of key-value pairs as its first
128 positional parameter.
129
130 This class differs from `~lsst.utils.TemplateMeta` only in that the dtype
131 (or equivalent) constructor keyword is optional. If it is omitted, the
132 class will attempt to infer it from the first argument.
133 """
134
135 def __call__(cls, *args, **kwargs): # noqa N805, non-self first param
136 if len(cls.TEMPLATE_PARAMS) != 1:
137 raise ValueError("AutoKeyMeta requires exactly one template parameter")
138 dtypeKey = cls.TEMPLATE_PARAMS[0]
139 dtype = kwargs.get(dtypeKey, None)
140
141 # Try to infer dtype if not provided
142 if dtype is None and len(args) >= 1:
143 dtype = cls._guessKeyType(args[0])
144 if dtype is not None:
145 kwargs[dtypeKey] = dtype
146
147 return super().__call__(*args, **kwargs)
148
149 def _guessKeyType(cls, inputData): # noqa N805, non-self first param
150 """Try to infer the key type of a map from its input.
151
152 Parameters
153 ----------
154 inputData : `~collections.abc.Mapping` or iterable of pairs
155 Any object that can be passed to a `dict`-like constructor. Keys
156 are assumed homogeneous (if not, a
157 `~lsst.afw.typehandling.GenericMap` constructor will raise
158 `TypeError` no matter what key type, if any, is provided).
159
160 Returns
161 -------
162 keyType : `type`
163 The type of the keys in ``inputData``, or `None` if the type could
164 not be inferred.
165 """
166 if inputData:
167 firstKey = None
168 if isinstance(inputData, Mapping):
169 # mapping to copy
170 firstKey = iter(inputData.keys()).__next__()
171 elif not isinstance(inputData, str):
172 # iterable of key-value pairs
173 try:
174 firstKey = iter(inputData).__next__()[0]
175 except TypeError:
176 # Not an iterable of pairs
177 pass
178 if firstKey:
179 return type(firstKey)
180 # Any other input is either empty or an invalid input to dict-like constructors
181 return None