Last active
September 15, 2020 23:22
-
-
Save pauleveritt/5a1b1b2f019dd3352b0677ec3baa0e41 to your computer and use it in GitHub Desktop.
Using an alias for a PEP 593 Annotation with multiple arguments.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Using an alias for a PEP 593 Annotation with multiple arguments. | |
I'm rewriting my injector. | |
I currently put the injector information in dataclass field metadata. | |
I'd like to allow other callables such as functions, so I'm looking at PEP 593 Annotations, similar to what [the inject package](https://github.com/alecthomas/injector/blob/master/injector/__init__.py#L1116) does. | |
My injection though needs zero or more arguments, sort of like an RxPY pipeline. | |
# Update 1: Changed from Markdown science fiction to using code in a running test | |
""" | |
def test_handle_field_injected_customer_experiment(this_container, this_injector, this_injector2): | |
this_container.register_singleton(Customer(), Customer) | |
InjectT = TypeVar('InjectT') | |
def simple_factory(customer: Customer): | |
return customer | |
result = this_injector(target=simple_factory) | |
assert 'Base Customer' == result.name | |
# ============================================================== | |
# Let's start using Annotated so we can bring in the other | |
# features from the current injector. | |
def annotated_factory(customer: Annotated[Customer, _inject_marker]): | |
return customer | |
result = this_injector(target=annotated_factory) | |
assert 'Base Customer' == result.name | |
# ============================================================== | |
# The current injector lets the return type be different than | |
# the lookup type. We want the result to be FrenchCustomer, | |
# but ask the injector for the registered Customer. | |
def frenchcustomer_factory(customer: Annotated[FrenchCustomer, Customer, _inject_marker]): | |
# The injector looked up "Customer" which | |
return customer | |
result = this_injector2(target=frenchcustomer_factory) | |
assert 'French Customer' == result.name | |
# ============================================================== | |
# The current injector also lets you pluck off just an | |
# attribute, which is handy for building a props-based | |
# component system. | |
def attr_factory(customer_name: Annotated[str, Customer, Attr('name'), _inject_marker]): | |
# The injector looked up "Customer" which | |
return f'Name: {customer_name}' | |
result = this_injector2(target=attr_factory) | |
assert 'Name: French Customer' == result | |
# ============================================================== | |
# That's getting a little noisy. Would be nice to use a TypeAlias | |
# to at least cut down that last part. But the alias fails at | |
# runtime with: | |
# TypeError: Too many parameters for typing.Annotated[~InjectT, | |
# <object object at 0x10a4ce8c0>]; actual 3, expected 1 | |
# | |
# mypy fails with: | |
# tests/test_593_injector.py:177: error: Bad number of arguments for type alias, expected: 1, given: 3 | |
# tests/test_593_injector.py:177: error: Invalid type comment or annotation | |
# tests/test_593_injector.py:177: note: Suggestion: use Attr[...] instead of Attr(...) | |
Injected = Annotated[InjectT, _inject_marker] | |
def injected_factory(customer_name: Injected[str, Customer, Attr('name')]): | |
# The injector looked up "Customer" which | |
return f'Name: {customer_name}' | |
result = this_injector2(target=injected_factory) | |
assert 'Name: French Customer' == result | |
@gvanrossum Yes, I'm basing this on mypy at first. I was really hoping to not post a tracker question, as it might just be noise at this point. The error is about the generic getting too many arguments. I will link to something in a repo that shows passing/failing tests.
Perhaps I shouldn't worry about using Annotated
without a marker saying its for my system. I'm only doing the alias to avoid that.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Are you using mypy? You could post this in the mypy tracker -- or in the typing tracker, if it's independent from which type checker is used. (Have you tried others? You don't show any error messages, you just say "if fails" -- the errors usually give some hints as to what's wrong.)
I suspect that the type alias creates a straightforward generic type, just like
I don't think variadic aliases are supported, not even with PEP 613's
TypeAlias
.