-
Notifications
You must be signed in to change notification settings - Fork 431
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
Add type hints to the generic database handler #1858
Add type hints to the generic database handler #1858
Conversation
@DavidMStraub, one complication is that when these methods are overloaded in a proxy (like I think with the correct type hints, we could catch such errors. |
Exactly! I have been bitten by this before. I suspect there are similar, more hidden cases. |
2b130b1
to
2508db8
Compare
@DavidMStraub In Use orjson + encoder @dsblank suggests something like:
If I were to make a call like But if I were to call it with assuming myperson is an object of type Person |
No, this will not work and not throw an error. The correct way to tell the type checker would be using from typing import overload
def data_to_object(data):
# actual implementation here
@overload
def data_to_object(data: PersonData) -> Person:
...
@overload
def data_to_object(data: FamilyData) -> Family:
... Possibly this could be combined with |
But the question is what you are trying to achieve. I think it boils down to type narrowing for a factory method. So I would do: from typing import TypeVar, Type
T = TypeVar("T", bound="BasicPrimaryObject")
class BasicPrimaryObject:
@classmethod
def from_data(cls: Type[T], data) -> T:
raise NotImplementedError
class Person(BasicPrimaryObject):
@classmethod
def from_data(cls: Type["Person"], data: PersonData) -> "Person":
# implement
class Family(BasicPrimaryObject):
@classmethod
def from_data(cls: Type["Family"], data: FamilyData) -> "Family":
# implement Now, |
What I am trying to achieve is clarity and explicitness and type checking in the code, like there is in gen/db/generic get_person_from_handle. I would much prefer replicated get_person_from_data, consistently like all the other get_foo_from_bar in gen/db/generic. I wanted to understand whether the single dispatch approach would work, because that is being advocated elsewhere, but I don't like it because it is not as clear and explicit as get_person-from_data etc. I would write the overload approach as myfamily = get_object_from_data(somefamilydata), but this does not make it explicit in the call that we know that the type of object is Family.. As far as the Family.get_object_from_data(somefamilydata) approach is concerned, AIUI this would distribute the code among the different primary objects, which may not be as desirable as having the db related code in one place. And it is not as consistent as myfamily = get_object_from_data(somefamilydata) for db conversion. Overall though presumably there is some overhead in having Python resolve which method to apply, as opposed to writing in the call which method to use! |
I am with you on this. Explicit is better than implicit, and if something is difficult to type-hint, it probably means that it's not explicit enough.
But an alternative to having separate functions is to have class methods like in my example. These are just factory methods, so it's natural they live with the classes.
That is something I would absolutely not worry about. Let's have a consistent and maintainable design. This will not be the speed bottleneck. |
I marked this PR as ready for review - I think we should merge our type hints bit by bit, otherwise we will run into the issue again that we have a huge PR that will take a long time to merge and will be difficult to disentangle from other changes. There is no problem with having only some modules annotated, as long as the type checker passes (which is assured by CI). |
The A We could consider creating to_data/from_data methods as newer forms of the serialize/unserialize methods. There is already a create class method which could be updated to use |
12c3e9a
to
b71e3f2
Compare
This is work in progress adding type hints to
gramps.gen
and defining Gramps-specific types in the process.