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

Alias for a PEP 593 Annotation with multiple args fails with TypeError: Too many parameters #753

Closed
pauleveritt opened this issue Sep 15, 2020 · 3 comments
Labels
topic: feature Discussions about new features for Python's type annotations

Comments

@pauleveritt
Copy link

(Per a suggestion, moving a conversation from a gist to here.

I have a type-hinting-based injector that I would like to rewrite to use PEP 593 annotations. I currently put the injector hints in dataclass field metadata. Annotations would make it work with functions, namedtuple etc. Similar to what the inject package does. My injection though needs zero or more arguments.

Here's code from a test that fails at the end, when switching to an alias. Exception info is in the comment.

Perhaps I should either get rid of the marker or find some other way to determine it's one of my annotations? Or presume I'm the only consumer of annotations?

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
@srittau
Copy link
Collaborator

srittau commented Nov 4, 2021

@pauleveritt I am sorry for the very late answer, but could you condense your example. I assume the problem are the following lines, where mypy complains?

    Injected = Annotated[InjectT, _inject_marker]

    def injected_factory(customer_name: Injected[str, Customer, Attr('name')]): ...

What exactly would you suggest to be the solution?

@pauleveritt
Copy link
Author

Hi, thanks for coming back on this. I agree, the example is way too long.

FWIW, I worked around it by having a single object in the second spot, which then collected the "pipeline" and behaved like a marker. Here's an example

I think this could be closed, as this is a reasonable solution for me.

@srittau srittau added the topic: feature Discussions about new features for Python's type annotations label Nov 4, 2021
@srittau
Copy link
Collaborator

srittau commented Nov 4, 2021

Thanks, I will close this. #779 might also be related.

@srittau srittau closed this as completed Nov 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: feature Discussions about new features for Python's type annotations
Projects
None yet
Development

No branches or pull requests

2 participants