Bug 84922 - Problems with output format of low-level API function get_args_list()
Summary: Problems with output format of low-level API function get_args_list()
Status: RESOLVED NOTOURBUG
Alias: None
Product: dbus
Classification: Unclassified
Component: python (show other bugs)
Version: 1.8
Hardware: x86 (IA32) Linux (All)
: medium normal
Assignee: Simon McVittie
QA Contact: D-Bus Maintainers
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-10-11 22:36 UTC by andi3
Modified: 2014-10-13 14:17 UTC (History)
0 users

See Also:
i915 platform:
i915 features:


Attachments

Description andi3 2014-10-11 22:36:09 UTC
Here is a python script (meant to catch messages sent from elsewhere, e. g. notify-send) that has two "forks" built-in:

import glib, dbus
from dbus.mainloop.glib import DBusGMainLoop

def recv_msg_works(bus, msg):
#the only working version
   cnt = 0
   for str in msg.get_args_list(byte_arrays=True):
     while cnt < 5:
       if cnt == 4: 
         print (str)
       cnt += 1
       break

def recv_msg_fails_var1(bus, msg):
#non-working version #1
   [st] = msg.get_args_list(byte_arrays=True)
   print st[4]                      # chokes!
   print "test1"                    # never reached


def recv_msg_fails_var2(bus, msg):
#non working version #2
   [first,second,third,fourth] = msg.get_args_list(byte_arrays=True)
   print "4th msg = %s" % fourth    # chokes!
   print "test2"                    # never reached
   

DBusGMainLoop(set_as_default=True)

bus = dbus.SessionBus()
bus.add_match_string_non_blocking("eavesdrop=true, interface='org.freedesktop.Notifications', member='Notify'")
bus.add_message_filter(recv_msg_works)
# bus.add_message_filter(recv_msg_fails_var1)
# bus.add_message_filter(recv_msg_fails_var2)

glib.MainLoop().run()


At first glance, this might induce the response "back to Python school".

However, since the official API documentation (which I've scoured multiple times) of get_args_list() *claims* to output a true Python list, also the two other variants should work.

But they do not.

Instead, this will make THE WHOLE SCRIPT CHOKE.
No, there is _no_ complaint from Python that anything was syntactically wrong.
Debugging the variants will, however, reveal the fact that neither the "test1" nor the "test2" dummy lines will ever be output.
This is because it chokes *exactly* at the get_args_list() line. I can say that for sure now.

Now my question:
Why does this require this silly for loop if it should be assumed that msg.get_args_list will get the WHOLE list in one go?

I have a suspicion that something is internally wrong or returned the wrong way.
This IS supposed to be a list, so ordinary list operations *ought to* work.

Compare:

arg = ['one','two','three','four']

print "last arg = %s " % arg[3]

It should not matter if the list is defined manually or retrieved from a function like get_args_list().
Or the documentation should add an important part why (and above all: how) the returned list should be treated specially.
But from the current documentation, I could not read anything like that.
Comment 1 Simon McVittie 2014-10-13 11:32:45 UTC
If your working version is correct (i.e. prints what you want it to print), then neither of your non-working versions are correct.

(In reply to andi3 from comment #0)
> (meant to catch messages sent from elsewhere, e. g.
> notify-send)

What is in these messages, including the types?

(As in: "print repr(msg.get_args_list(byte_arrays=True))")

> This IS supposed to be a list, so ordinary list operations *ought to* work.

It is a real Python list like [], not just an iterable object or object with a list-like API. The source code is in dbus_py_Message_get_args_list() in _dbus_bindings/message-get-args.c if you don't believe me.

(In reply to andi3 from comment #0)
>    cnt = 0
>    for str in msg.get_args_list(byte_arrays=True):
>      while cnt < 5:
>        if cnt == 4: 
>          print (str)
>        cnt += 1
>        break

This would be appropriate if the message has 5 or more arguments and you want to print the fifth (i.e. args[4] in zero-based numbering).

(As a side note, str is the name of a built-in type, so it seems an inadvisably confusing choice for a variable name... but it works.)

> def recv_msg_fails_var1(bus, msg):
> #non-working version #1
>    [st] = msg.get_args_list(byte_arrays=True)

I suspect this is not the code you intended to write. It will fail unless the message has precisely one argument:

>>> [st] = [1, 2, 3, 4, 5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

> def recv_msg_fails_var2(bus, msg):
> #non working version #2
>    [first,second,third,fourth] = msg.get_args_list(byte_arrays=True)
>    print "4th msg = %s" % fourth    # chokes!

This will fail unless the message has precisely four arguments, which seems highly suspicious when your working code assumes at least five arguments:

>>> [first, second, third, fourth] = [1, 2, 3, 4, 5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

I don't think this is the code you intended to write either.

The simplest correct implementation would be:

def untested_but_should_work(bus, msg):
   args = msg.get_args_list(byte_arrays=True)
   print args[4]
   print "^ there you go"

> At first glance, this might induce the response "back to Python school".

You said it, not me :-)

> No, there is _no_ complaint from Python that anything was syntactically
> wrong.

You should get the exception logged to the logging subsystem (or, if you haven't configured logging, to stderr) by the code mentioned in this comment: <https://bugs.freedesktop.org/show_bug.cgi?id=9980#c3>.

If not, you could try this:

    try:
        ... what you wrote ...
    except:
        import traceback
        traceback.print_exc()

and with any luck you'll see the traceback and ValueError.
Comment 2 andi3 2014-10-13 14:11:18 UTC
Simon,

Thanks for the in-depth reply.

def untested_but_should_work(bus, msg):
   args = msg.get_args_list(byte_arrays=True)
   print args[4]
   print "^ there you go"

--

Yes, of course it should work but in reality it _doesn't_ work.
(You may test it and report me back: maybe it works for you. But if you do, please make sure you test the whole script with your modified def, not just this short def (which _may_ work standalone, but knowing that is of no purpose)

This is my problem: it will always choke at the middle line if written that way, and not get to print the "^ there you go".
And this is what I'm still trying to figure out why this does not work.

>What is in these messages, including the types?

It's the product of 

$ notify-send <summary> <message>

Summary is sent to /dev/null and that is ok, since we are required to supply the summary to notify-send, but we only want the message part.


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.