diff --git a/include/lsst/afw/table/KeyBase.h b/include/lsst/afw/table/KeyBase.h index 25acba7c72..fac105a03c 100644 --- a/include/lsst/afw/table/KeyBase.h +++ b/include/lsst/afw/table/KeyBase.h @@ -17,8 +17,6 @@ class BaseRecord; template class KeyBase { public: - static bool const HAS_NAMED_SUBFIELDS = false; - KeyBase() = default; KeyBase(KeyBase const &) = default; KeyBase(KeyBase &&) = default; @@ -31,8 +29,6 @@ class KeyBase { template class KeyBase > { public: - static bool const HAS_NAMED_SUBFIELDS = false; - KeyBase() = default; KeyBase(KeyBase const &) = default; KeyBase(KeyBase &&) = default; @@ -53,8 +49,6 @@ class KeyBase > { template <> class KeyBase { public: - static bool const HAS_NAMED_SUBFIELDS = false; - KeyBase() = default; KeyBase(KeyBase const &) = default; KeyBase(KeyBase &&) = default; diff --git a/python/lsst/afw/table/_base.py b/python/lsst/afw/table/_base.py index 248dad909e..edd4f8d5eb 100644 --- a/python/lsst/afw/table/_base.py +++ b/python/lsst/afw/table/_base.py @@ -32,11 +32,11 @@ class BaseRecord: # noqa: F811 def extract(self, *patterns, **kwargs): - """Extract a dictionary of {: } in which the field names - match the given shell-style glob pattern(s). + """Extract a dictionary of {: } in which the field + names match the given shell-style glob pattern(s). - Any number of glob patterns may be passed; the result will be the union of all - the result of each glob considered separately. + Any number of glob patterns may be passed; the result will be the union + of all the result of each glob considered separately. Parameters ---------- @@ -46,11 +46,6 @@ def extract(self, *patterns, **kwargs): to be reused to extract values from multiple records. This keyword is incompatible with any position arguments and the regex, sub, and ordered keyword arguments. - split : `bool` - If `True`, fields with named subfields (e.g. points) will be split - into separate items in the dict; instead of {"point": - lsst.geom.Point2I(2,3)}, for instance, you'd get {"point.x": - 2, "point.y": 3}. Default is `False`. regex : `str` or `re` pattern object A regular expression to be used in addition to any glob patterns passed as positional arguments. Note that this will be compared @@ -64,22 +59,12 @@ def extract(self, *patterns, **kwargs): order of the `Schema`. Default is `False`. """ d = kwargs.pop("items", None) - split = kwargs.pop("split", False) if d is None: d = self.schema.extract(*patterns, **kwargs).copy() elif kwargs: kwargsStr = ", ".join(kwargs.keys()) raise ValueError(f"Unrecognized keyword arguments for extract: {kwargsStr}") - # must use list because we might be adding/deleting elements - for name, schemaItem in list(d.items()): - key = schemaItem.key - if split and key.HAS_NAMED_SUBFIELDS: - for subname, subkey in zip(key.subfields, key.subkeys): - d[f"{name}.{subname}"] = self.get(subkey) - del d[name] - else: - d[name] = self.get(schemaItem.key) - return d + return {name: self.get(schemaItem.key) for name, schemaItem in d.items()} def __repr__(self): return f"{type(self)}\n{self}" diff --git a/python/lsst/afw/table/_schema.cc b/python/lsst/afw/table/_schema.cc index bf971a9c31..86c13c7bdc 100644 --- a/python/lsst/afw/table/_schema.cc +++ b/python/lsst/afw/table/_schema.cc @@ -314,7 +314,6 @@ void declareSchemaType(WrapperCollection &wrappers) { // KeyBase wrappers.wrapType(PyKeyBase(wrappers.module, ("KeyBase" + suffix).c_str()), [](auto &mod, auto &cls) { - cls.def_readonly_static("HAS_NAMED_SUBFIELDS", &KeyBase::HAS_NAMED_SUBFIELDS); declareKeyBaseSpecializations(cls); }); diff --git a/src/table/KeyBase.cc b/src/table/KeyBase.cc index e317138ce7..a0c69e428a 100644 --- a/src/table/KeyBase.cc +++ b/src/table/KeyBase.cc @@ -16,14 +16,6 @@ Key::Element> KeyBase::getStorage() const { return detail::Access::extractElement(*this, 0); } -bool const KeyBase::HAS_NAMED_SUBFIELDS; - -template -bool const KeyBase::HAS_NAMED_SUBFIELDS; - -template -bool const KeyBase>::HAS_NAMED_SUBFIELDS; - template std::vector KeyBase>::extractVector(BaseRecord const &record) const { Key> const *self = static_cast> const *>(this); diff --git a/src/table/Schema.cc b/src/table/Schema.cc index 6718758cc9..d09f91a16b 100644 --- a/src/table/Schema.cc +++ b/src/table/Schema.cc @@ -106,203 +106,32 @@ class ItemFunctors { namespace detail { -//----- Finding a SchemaItem by field name ------------------------------------------------------------------ - -// This is easier to understand if you start reading from the bottom of this section, with -// SchemaImpl::find(std::string const &), then work your way up. - -namespace { - -// Given a SchemaItem for a regular field, look for a subfield with the given name. -// Return the index of the subfield (>= 0) on success, -1 on failure. -template -inline int findNamedSubfield( - SchemaItem const &item, std::string const &name, char delimiter, - boost::mpl::true_ * // whether a match is possible based on the type of T; computed by caller -) { - if (name.size() <= item.field.getName().size()) return -1; - - if ( // compare invocation is equivalent to "name.startswith(item.field.getName())" in Python - name.compare(0, item.field.getName().size(), item.field.getName()) == 0 && - name[item.field.getName().size()] == delimiter) { - int const position = item.field.getName().size() + 1; - int const size = name.size() - position; - int const nElements = item.field.getElementCount(); - for (int i = 0; i < nElements; ++i) { - if (name.compare(position, size, Key::subfields[i]) == 0) { - return i; - } - } - } - return -1; -} - -// This is an overload of findNamedSubfield that always fails; it's called when we -// know from the type of the field and subfield that they're incompatible. -template -inline int findNamedSubfield( - SchemaItem const &item, std::string const &name, char delimiter, - boost::mpl::false_ * // whether a match is possible based on the type of T; computed by caller -) { - return -1; -} - -// Given a SchemaItem and a subfield index, make a new SchemaItem that corresponds to that -// subfield and put it in the result smart pointer. -template -inline void makeSubfieldItem( - SchemaItem const &item, int index, char delimiter, std::unique_ptr > &result, - boost::mpl::true_ * // whether a match is possible based on the types of T and U; computed by caller -) { - result.reset(new SchemaItem(detail::Access::extractElement(item.key, index), - Field(join(item.field.getName(), Key::subfields[index], delimiter), - item.field.getDoc(), item.field.getUnits()))); -} - -// An overload of makeSubfieldItem that always fails because we know T and U aren't compatible. -template -inline void makeSubfieldItem( - SchemaItem const &item, int index, char delimiter, std::unique_ptr > &result, - boost::mpl::false_ * // whether a match is possible based on the types of T and U; computed by caller -) {} - -// This is a Variant visitation functor used to extract subfield items by name. -// For example, if we have a Point field "a", if we search the Schema for "a.x", -// we want to return a SchemaItem that makes it look like "a.x" is a full-fledged -// field in its own right. -template -struct ExtractItemByName : public boost::static_visitor<> { - explicit ExtractItemByName(std::string const &name_, char delimiter_) - : delimiter(delimiter_), name(name_) {} - - template - void operator()(SchemaItem const &item) const { - // We want to find out if 'item' has a subfield whose fully-qualified name matches our - // name data member. But we also know that the subfield needs to have type U, and that - // the field needs to have named subfields. - // This typedef is boost::mpl::true_ if all the above is true, and boost::mpl::false_ otherwise. - typedef typename boost::mpl::and_::Element>, - boost::mpl::bool_::HAS_NAMED_SUBFIELDS> >::type - IsMatchPossible; - // We use that type to dispatch one of the two overloads of findNamedSubfield. - int n = findNamedSubfield(item, name, delimiter, (IsMatchPossible *)0); - // If we have a match, we call another overloaded template to make the subfield. - if (n >= 0) makeSubfieldItem(item, n, delimiter, result, (IsMatchPossible *)0); - } - - char delimiter; - std::string name; // name we're looking for - mutable std::unique_ptr > result; // where we put the result to signal that we're done -}; - -} // namespace - -// Here's the driver for the find-by-name algorithm. template SchemaItem SchemaImpl::find(std::string const &name) const { NameMap::const_iterator i = _names.lower_bound(name); - if (i != _names.end()) { - if (i->first == name) { - // got an exact match; we're done if it has the right type, and dead if it doesn't. - try { - return boost::get const>(_items[i->second]); - } catch (boost::bad_get &err) { - throw LSST_EXCEPT(lsst::pex::exceptions::TypeError, - (boost::format("Field '%s' does not have the given type.") % name).str()); - } + if (i != _names.end() && i->first == name) { + // got an exact match; we're done if it has the right type, and dead if it doesn't. + try { + return boost::get>(_items[i->second]); + } catch (boost::bad_get &err) { + throw LSST_EXCEPT(lsst::pex::exceptions::TypeError, + (boost::format("Field '%s' does not have the given type.") % name).str()); } } - // We didn't get an exact match, but we might be searching for "a.x/a_x" and "a" might be a point field. - // Because the names are sorted, we know we overshot it, so we work backwards. - ExtractItemByName extractor(name, getDelimiter()); - while (i != _names.begin()) { - --i; - boost::apply_visitor(extractor, _items[i->second]); // see if the current item is a match - if (extractor.result) return *extractor.result; - } throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError, - (boost::format("Field or subfield with name '%s' not found with type '%s'.") % name % + (boost::format("Field with name '%s' not found with type '%s'.") % name % Field::getTypeString()) .str()); } -//----- Finding a SchemaItem by key ------------------------------------------------------------------------- - -// This is easier to understand if you start reading from the bottom of this section, with -// SchemaImpl::find(Key const &), then work your way up. - -namespace { - -// Given a SchemaItem for a regular field, look for a subfield with the given Key -// Return the index of the subfield (>= 0) on success, -1 on failure. -template -inline int findKeySubfield( - SchemaItem const &item, Key const &key, - boost::mpl::true_ * // whether a match is possible based on the types of T and U; computed by caller -) { - int n = (key.getOffset() - item.key.getOffset()) / sizeof(U); - if (n >= 0 && n < item.key.getElementCount()) { - return n; - } - return -1; -} - -// This is an overload of findKeySubfield that always fails; it's called when we -// know from the type of the field and subfield that they're incompatible. -template -inline int findKeySubfield( - SchemaItem const &item, Key const &key, - boost::mpl::false_ * // whether a match is possible based on the types of T and U; computed by caller -) { - return -1; -} - -// This is a Variant visitation functor used to extract subfield items by key. -template -struct ExtractItemByKey : public boost::static_visitor<> { - explicit ExtractItemByKey(Key const &key_, char delimiter_) : delimiter(delimiter_), key(key_) {} - - template - void operator()(SchemaItem const &item) const { - // We want to find out if 'item' has a subfield whose matches our key data member. - // But we also know that the subfield needs to have type U. - // This typedef is boost::mpl::true_ if the above is true, and boost::mpl::false_ otherwise. - typedef typename boost::mpl::and_::Element>, - boost::mpl::bool_::HAS_NAMED_SUBFIELDS> >::type - IsMatchPossible; - // We use that type to dispatch one of the two overloads of findKeySubfield. - int n = findKeySubfield(item, key, (IsMatchPossible *)0); - // If we have a match, we call another overloaded template to make the subfield. - // (this is the same makeSubfieldItem used in ExtractItemByName, so it's defined up there) - if (n >= 0) makeSubfieldItem(item, n, delimiter, result, (IsMatchPossible *)0); - } - - char delimiter; - Key key; - mutable std::unique_ptr > result; -}; - -} // namespace - -// Here's the driver for the find-by-key algorithm. It's pretty similar to the find-by-name algorithm. template SchemaItem SchemaImpl::find(Key const &key) const { OffsetMap::const_iterator i = _offsets.lower_bound(key.getOffset()); - if (i != _offsets.end()) { - if (i->first == key.getOffset()) { - try { - return boost::get const>(_items[i->second]); - } catch (boost::bad_get &err) { - // just swallow the exception; this might be a subfield key that points to the beginning. - } - } - // We didn't get an exact match, but we might be searching for a subfield. - // Because the offsets are sorted, we know we overshot it, so we work backwards. - ExtractItemByKey extractor(key, getDelimiter()); - while (i != _offsets.begin()) { - --i; - boost::apply_visitor(extractor, _items[i->second]); - if (extractor.result) return *extractor.result; + if (i != _offsets.end() && i->first == key.getOffset()) { + try { + return boost::get>(_items[i->second]); + } catch (boost::bad_get &err) { + // just swallow the exception; this might be a subfield key that points to the beginning. } } throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError,