Skip to content
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

make compatible with sqlalchemy #5

Merged
merged 3 commits into from
Oct 2, 2023
Merged

Conversation

beheh
Copy link
Contributor

@beheh beheh commented Sep 19, 2023

This PR was motivated by an issue I faced while I was trying to use TypeIDs as primary keys in a SQLAlchemy-based project, because SQLAlchemy was unable to index rows by their id (TypeError: unhashable type: 'TypeID').

This PR does a few things:

  1. it makes TypeIDs hashable1, so they can be used by SQLAlchemy as dictionary keys
  2. it adds a TypeID.uuid property for easy extraction of the UUID for storage in SQLAlchemy
  3. it adds examples/sqlalchemy.py with a simple example on how to use a TypeID as a SQLAlchemy column type

Implementation details

This PR makes TypeID hashable1 by implementing a __hash__ function that wraps prefix and suffix. Hashing the prefix will simply hash the string (or None), and hashing the suffix will simply defer to the UUID hash implementation2. This implementation should have no interaction with the TypeID spec, because this type of hashing is only relevant within Python specifically to interoperate with other Python types. To my knowledge there is no common cross-language definition of the hashing algorithm.

Footnotes

  1. https://docs.python.org/3.11/glossary.html#term-hashable 2

  2. https://github.com/python/cpython/blob/afa7b0d743d9b7172d58d1e4c28da8324b9be2eb/Lib/uuid.py#L268-L269

@akhundMurad
Copy link
Owner

Hello @beheh.
Thanks for your contribution!
I will check this pull request in a while.
Also, it would be great to build a project in order to demonstrate the integration of TypeID and SqlAlchemy.

@beheh
Copy link
Contributor Author

beheh commented Sep 20, 2023

Sure! While I'm not sure yet whether my project will be open source, I'll post my current version here for now:

from typeid import TypeID, base32, from_uuid as typeid_from_uuid

def get_uuid_from_type_id(type_id: TypeID) -> UUID:
    return UUID(bytes=bytes(base32.decode(type_id.suffix)))

class TypeIDType(types.TypeDecorator):
    impl = types.Uuid

    cache_ok = True

    prefix: Optional[str]

    def __init__(self, prefix: Optional[str], *args, **kwargs):
        self.prefix = prefix
        super().__init__(*args, **kwargs)

    def process_bind_param(self, value: TypeID, dialect):
        if self.prefix is None:
            assert value.prefix is None
        else:
            assert value.prefix == self.prefix

        return get_uuid_from_type_id(value)

    def process_result_value(self, value, dialect):
        return typeid_from_uuid(value, self.prefix)

This TypeDecorator can then be used as a column type, storing the suffix in the database a UUID field, and then we simply assert the prefix during conversion into the DB. One thing you may notice is that I'm manually extracting the UUID - I didn't find a way to do this using typeid-python's API.

@akhundMurad
Copy link
Owner

Looks nice!
Could you please:

  1. Create an issue regarding the UUID extraction through built-in API.
  2. Add this example to examples/sqlalchemy.py

I presume these steps would positively affect our project 😌

@akhundMurad
Copy link
Owner

@beheh Sorry, forgot to tag you 😅

@beheh beheh changed the title Make TypeID hashable make compatible for sqlalchemy Sep 25, 2023
@beheh beheh marked this pull request as draft September 25, 2023 09:47
@beheh beheh changed the title make compatible for sqlalchemy make compatible with sqlalchemy Sep 26, 2023
@akhundMurad
Copy link
Owner

Hello @beheh
Are you planning to add some new changes to this MR?
I am asking because you marked it as draft.

@beheh beheh marked this pull request as ready for review October 2, 2023 09:48
@beheh
Copy link
Contributor Author

beheh commented Oct 2, 2023

@akhundMurad I was away from my machine and pushed my remaining code now. I've unmarked as draft.

@akhundMurad akhundMurad merged commit 88d058c into akhundMurad:main Oct 2, 2023
3 checks passed
@beheh beheh deleted the hashable branch October 2, 2023 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants