Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Preserve frame types though ehrQL operations
We define tables in ehrQL using subclasses of `EventFrame` and `PatientFrame` e.g. ```py @table class event(EventFrame): date = Series(datetime.date) code = Series(SNOMEDCTCode) ``` This gives us two big advantages: * auto-complete for column names; * the option to define [table-specific helper][1] methods. Previously however, as soon as you performed any kind of operation on one of these tables (i.e. called a method) you'd get back a plain `EventFrame` or `PatientFrame` with no column auto-completion and no helper methods. This PR ensures that we return the appropriate frame subclass from all methods. This also allows us to remove the `__getattr__` magic from the `BaseFrame` class. **The nasty bits** Returning an appropriate type in all cases requires two bits of trickery in the form of dynamic class compilation: 1. When we call `sort_by()` we need the result to have, as well as its existing methods, the `get_first/last_for_patient()` methods. So we construct a subclass which mixes in the necessary methods. 2. When we call one of the `get_first/last_for_patient()` methods we need to get back a `PatientFrame`. This should have all the columns defined on the original frame, but none of the methods. We introspect the class definition to extract all the columns and construct a new `PatientFrame` with those columns included. **Static auto-complete** The above gives us auto-complete in a dynamic context like an IPython session where code is actually executed. We also get a limited form of static (type-based) auto-complete in VSCode. Previously, this worked only on the original frame and this PR extends this so that it persists through `where/except_where` calls. However it won't persist through `sort_by` or `get_first/last_for_patient()`. After reasonably extensive investigation (which I need to write up in [this ticket][2]) I don't _think_ our ideal behaviour is acheivable in Pylance (VSCode's type checker) as things currently stand. But I don't think anything in this PR makes things worse in that regard. [1]: #1021 [2]: #506
- Loading branch information