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

Initial custom attributes work #1144

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Conversation

Tinche
Copy link
Member

@Tinche Tinche commented Jun 8, 2023

This PR enables other libraries to define their own Attribute classes (replacing attrs.Attribute), and hence to define their own fields() function.

Why does this need to be in attrs?

Because the next step will be to add support for this to the attrs Mypy plugin, and we need to design this feature with that in mind.

Having Mypy support for this will actually enable statically checking things that were previously impossible in Python, so if we do it right, it might be a big deal.

For example, as far as I know no ORM in Python today can statically check queries. We may change this.

Since this is a feature for library authors (not end users directly), we may consider designing it so it is easily supportable in Mypy rather than easy to use (as funny as that sounds).

My personal motivation is that my codebases are pretty heavily typed, but I keep running into untypeable things to do with databases. I would love to plug the hole, even partially.

This is a continuation of #934.

Use cases

cattrs customization

Right now, here's how a cattrs structuring function can be customized:

from cattrs.gen import override, make_dict_structure_fn

make_dict_structure_fn(MyClass, converter, a=override(omit=True))

Here's how it could be:

from cattrs.gen import make_dict_structure_fn, fields as f

make_dict_structure_fn(MyClass, converter, f(MyClass).a.omit())

Why is it better?

A more fluent API, better errors, statically checked.

cattrs validation

cattrs has no validation right now, here's how it could work.

from cattrs.v import fields as f

validator = f(MyClass).a.gt(5) & f(MyClass).b.len(min=1)

validator(MyClass(1, [])

PocketMongo query builder

The current approach to creating a Mongo query condition:

find_one(Model, {"user_id": uid})

How it could be:

from pocketmongo import q, find_one

find_one(q(Model).user_id.equal(uid))
# Mypy error if `user_id` is misspelled (field doesn't exist)
# Mypy error if `uid` is not the same type as `user_id`

Why is it better:

A more fluent API. Statically checked.

This might end up being a model for attrs-based ORMs going forward.

Problems

  • Pyright probably won't support this, but that's just something we have to live with. Mypy supporting it will add plenty of value.

  • I'm not sure about handling nested attributes yet. If class A has an attribute b, typed as class B, and class B has a field called c. f(A).b.c should probably work, but it maybe requires special casing? That's TBD, but sounds like mostly a Mypy issue.

@Tinche Tinche requested a review from hynek June 8, 2023 00:35
@hynek hynek marked this pull request as draft March 16, 2024 15:24
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.

None yet

1 participant