New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DM-17451: raise when columnviews access undefined keys #431
Conversation
c1d1b4a
to
8fa4541
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A handful of questions and comments.
Two broader points:
-
I would rebase squash the tests with the new C++ code, since they clearly go together.
-
It may be out of scope, but I feel like
testColumnView
and_testFluxSlot
have too much in them right now: it's hard to keep track of what is going on and what should or shouldn't be available at any given point. It may be too annoying given all the other code to manage the catalogs, but I wonder if you couldn't put them in their own test case, so that the success/failure condition is more obvious?
if (!key.isValid()) { | ||
throw LSST_EXCEPT( | ||
pex::exceptions::LogicError, | ||
"Key is not valid (if this is a SourceCatalog, make sure slot aliases have been set up)."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why SourceCatalog
here? Can slots not be set on SimpleCatalog
s? What other types of catalogs can or cannot have slots?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slots are for SourceTable
s (or Catalog
s) only (see e.g. https://github.com/lsst/afw/blob/master/include/lsst/afw/table/slots.h#L24).
for keyType in ["Angle", "ArrayB", "ArrayD", "ArrayF", "ArrayI", | ||
"ArrayU", "B", "D", "F", "Flag", "I", "L", "U"]: | ||
# Default-constructed key is invalid | ||
invalidKey = getattr(lsst.afw.table, f"Key{keyType}")() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat use of format strings. I need to use those more.
tests/test_simpleTable.py
Outdated
# Default-constructed key is invalid | ||
invalidKey = getattr(lsst.afw.table, f"Key{keyType}")() | ||
self.assertFalse(invalidKey.isValid()) | ||
self.assertRaises(lsst.pex.exceptions.LogicError, catalog.get, invalidKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the context manager version easier to understand the intent:
with self.assertRaises(LogicError):
catalog.get(invalidKey)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is just taste. Personally, I prefer the non-context-manager version when testing the result of a simple function call; the context manager is obviously better when dealing with more involved logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The testing guide prefers the context manager. Currently about 90% of the LSST code base complies with that statement (and I definitely prefer the context manage for code readability).
https://developer.lsst.io/python/testing.html?highlight=assertraises
@@ -69,6 +69,11 @@ std::shared_ptr<BaseTable> BaseColumnView::getTable() const { return _impl->tabl | |||
|
|||
template <typename T> | |||
typename ndarray::ArrayRef<T, 1> const BaseColumnView::operator[](Key<T> const &key) const { | |||
if (!key.isValid()) { | |||
throw LSST_EXCEPT( | |||
pex::exceptions::LogicError, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this raise LogicError
or KeyError
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reckon LogicError
, for consistency with the behaviour when accessing an invalid key on a single record. I was actually surprised that was a LogicError
, but I think changing it on this ticket is out of scope.
@@ -639,7 +643,8 @@ def _testFluxSlot(self, slotName): | |||
type=np.float64, doc="flux uncertainty") | |||
flagKey = schema.addField("%s_flag" % (baseName,), | |||
type="Flag", doc="flux flag") | |||
table = lsst.afw.table.SourceTable.make(schema) | |||
catalog = lsst.afw.table.SourceCatalog(schema) | |||
table = catalog.table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish someone would explain to me the difference between an Table and a Catalog...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roughy speaking (and I'm sure Jim or somebody else who knows better will be along to correct me in a moment):
- Tables are (confusingly named) record factories.
- Catalogs are containers of records.
In this particular case, we were previously only testing access to slots on individual records, so we only needed a table (a factory to create those records). I extended the test to test that the slot worked properly on a catalog (ie, a collection of records) too; obviously, that means we need to create the catalog for testing.
tests/test_sourceTable.py
Outdated
self.catalog.table.schema.getAliasMap().erase("slot_Centroid") | ||
self.catalog.table.schema.getAliasMap().erase("slot_Shape") | ||
for quantity in ["X", "Y", "Ixx", "Iyy", "Ixy"]: | ||
self.assertRaises(lsst.pex.exceptions.LogicError, getattr(self.catalog, f"get{quantity}")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again with the with
. Please add a comment above this explaining what this test is doing: it took me a bit to figure it out.
# And we should be able to delete the slot, breaking the mapping. | ||
table.schema.getAliasMap().erase("slot_%s" % (slotName,)) | ||
self.assertFalse(getattr(table, "get%sSlot" % (slotName,))().isValid()) | ||
self.assertNotEqual(getattr(table, "get%sSlot" % (slotName,))().getMeasKey(), instFluxKey) | ||
self.assertNotEqual(getattr(table, "get%sSlot" % (slotName,))().getErrKey(), errKey) | ||
self.assertNotEqual(getattr(table, "get%sSlot" % (slotName,))().getFlagKey(), flagKey) | ||
|
||
with self.assertRaises(lsst.pex.exceptions.LogicError): | ||
getattr(catalog, f"get{instFluxName}")() | ||
with self.assertRaises(lsst.pex.exceptions.LogicError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should these two raise? I would have thought they would work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are referring to keys which are defined as part of the slot. When the slot is erased (at line 675, above), all the keys defined as part of it are invalidated. Accessing via those invalid keys raises.
I'll add a comment to that effect.
8fa4541
to
8e3445a
Compare
8e3445a
to
0f740b8
Compare
No description provided.