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

Signal.send(*senders) means you must omit sender= if using a single arg #44

Closed
tankorsmash opened this issue Oct 9, 2018 · 2 comments
Closed

Comments

@tankorsmash
Copy link

tankorsmash commented Oct 9, 2018

https://github.com/jek/blinker/blob/42aad6110a02a86a9a51a77c75c2d638a50656b7/blinker/base.py#L261

If you don't write the my_signal.send() call as blinker expects, you get a silent failure.

In my_signal.send(sender=MyObject()) means that sender is None. I'm on Python3, but I've rigged a simple example to show what is happening. The cases where you don't pass in a named arg it works, but otherwise sender sticks around in kwargs).

I'm not sure of a good solution but the way it works definitely caught me off guard.

In [2]: def foo(*sender, **kwargs):
   ...:     print(f"len: {len(sender)} and sender is: {sender}")
   ...:     print(f"kwargs is {kwargs}")
   ...:

In [3]: foo(123)
len: 1 and sender is: (123,)
kwargs is {}

In [4]: foo(sender=123)
len: 0 and sender is: ()
kwargs is {'sender': 123}

In [5]: foo([123, 'abc'])
len: 1 and sender is: ([123, 'abc'],)
kwargs is {}

Now, if you swap *sender, to sender=None, sender is always defined, even if the len() fails on an int.


In [6]: def bar(sender=None, **kwargs):
   ...:     print(f"len: {len(sender)} and sender is: {sender}")
   ...:     print(f"kwargs is {kwargs}")
   ...:

In [7]: bar(123)
TypeError                                 Traceback (most recent call last)
<ipython> in <module>()
----> 1 bar(123)

<ipython> in bar(sender, **kwargs)
      1 def bar(sender=None, **kwargs):
----> 2     print(f"len: {len(sender)} and sender is: {sender}")
      3     print(f"kwargs is {kwargs}")
      4

TypeError: object of type 'int' has no len()

In [8]: bar(sender=123)
TypeError                                 Traceback (most recent call last)
<ipython> in <module>()
----> 1 bar(sender=123)

<ipython> in bar(sender, **kwargs)
      1 def bar(sender=None, **kwargs):
----> 2     print(f"len: {len(sender)} and sender is: {sender}")
      3     print(f"kwargs is {kwargs}")
      4

TypeError: object of type 'int' has no len()

In [9]: bar([123, 'abc'])
len: 2 and sender is: [123, 'abc']
kwargs is {}

I'm not sure if there's a way to reconcile the two approaches, but I figure I might as well write it down.

@r-chris
Copy link

r-chris commented Apr 27, 2020

There is a relevant comment on how to update the send method once Python 3.8 positional arguments are available here:

#41 (comment)

Python 3.8 positional-only parameters finally bring a portable way of fixing the (*args, **kwargs) hack on the send() method. If I were still actively working in Python (sadly I am not), I would look at making 3.8 the preferred native source version for Blinker and move all prior versions (2.7 and <3.8) to compatibility fallbacks.

@davidism
Copy link
Member

my_signal.send(sender=MyObject()) means that sender is None

That's the intended behavior, to allow a keyword argument sender passed on to the receiver, separately from a single positional argument specifying the Blinker sender.

The implementation for this can be improved with Python 3.8 positional-only arguments, but it won't change the fact that you are not intended to pass the Blinker sender as a keyword argument.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

3 participants