Discussion
Loading...

Discussion

  • About
  • Code of conduct
  • Privacy
  • Users
  • Instances
  • About Bonfire
clayote
@clayote@peoplemaking.games  ·  activity timestamp 3 months ago

I'm trying to add good #Python type annotations to #Lisien

Everything stored in it needs to be serializable in messagepack, so I've annotated all its function signatures with one of the type aliases

Key: TypeAlias = (
str | int | float | None | tuple["Key", ...] | frozenset["Key"]
)
Value: TypeAlias = (
Key
| dict[Key, "Value"]
| tuple["Value", ...]
| list["Value"]
| set["Value"]
| frozenset["Value"]
)

Works ok so far.

But now I'm trying to use NewType to distinguish generic keys from those that name a Lisien entity, such as a node, edge, or graph.

You can't use NewType on type annotations, so I have to make a class for it. I did it like this:

  • Copy link
  • Flag this post
  • Block
clayote
@clayote@peoplemaking.games replied  ·  activity timestamp 3 months ago
def is_valid_key(obj) -> TypeGuard[Key]:
return (
obj is None
or isinstance(obj, (str, int, float))
or (
isinstance(obj, (tuple, frozenset))
and all(is_valid_key(elem) for elem in obj)
)
)


class KeyClass:
def __new__(cls, that: Key) -> Key:
return that

def __instancecheck__(self, instance) -> bool:
return is_valid_key(instance)

Having to pick whether to use Key or KeyClass kinda sucks and I want to use just the class everywhere, but it seems like #PyCharm 's type checker can't tell that, when I annotate something KeyClass, that means it shouldn't complain when I pass in a string.

Is this a PyCharm limitation, or am I using types wrong?

  • Copy link
  • Flag this comment
  • Block
clayote
@clayote@peoplemaking.games replied  ·  activity timestamp 3 months ago

(note to self: actually run the code before you post it. just because it makes the type checker happy doesn't mean it runs)

  • Copy link
  • Flag this comment
  • Block
clayote
@clayote@peoplemaking.games replied  ·  activity timestamp 3 months ago

Well that's fairly bizarre. Seems like I have to do

_Key = str | int | float | None | tuple["_Key", ...] | frozenset["_Key"]
_KeyType = TypeVar("_KeyType", bound=_Key, covariant=True)


class Key(Generic[_KeyType]):
def new(cls, obj: _Key) -> Key:
if not is_valid_key(obj):
raise TypeError("Invalid key")
return obj

def instancecheck(self, instance) -> bool:
return is_valid_key(instance)

Even though instancecheck is alleged to be called to implement isinstance(obj, Key) it...isn't. Must be something about generics.

  • Copy link
  • Flag this comment
  • Block
Log in

bonfire.cafe

A space for Bonfire maintainers and contributors to communicate

bonfire.cafe: About · Code of conduct · Privacy · Users · Instances
Bonfire social · 1.0.0-rc.3.21 no JS en
Automatic federation enabled
  • Explore
  • About
  • Members
  • Code of Conduct
Home
Login