[Tutor] Error Python version 3.6 does not support this syntax.

Cameron Simpson cs at cskk.id.au
Thu Nov 29 17:18:47 EST 2018


On 30Nov2018 02:19, srinivasan <srinivasan.rns at gmail.com> wrote:
>Thanks a lot for your quick responses, again the below line seems to
>be throwing the same error, is that should I again decode the line
>where am facing the issue to str? or could you please let me if there
>is any alternative solution for the same or workaround in python 3.6?
>
>Code Snippet:
>
>    def parse_device_info(self, info_string):
>        """Parse a string corresponding to a device."""
>        device = {}
>        block_list = ["[\x1b[0;", "removed"]
>        string_valid = not any(keyword in info_string for keyword in
>block_list) ---------------------------> Again this line seems to be
>the same issue
[...]
>    def get_paired_devices(self):
>        """Return a list of tuples of paired devices."""
>        try:
>            out = self.get_output("paired-devices")
>        except BluetoothctlError as e:
>            print(e)
>            return None
>        else:
>            paired_devices = []
>            for line in out:
>                device = self.parse_device_info(line)
[...]

Your problem is basicly that reading from command output gets you bytes 
data, not text (str) data. This is because pipes transfer bytes; that 
the command may issue text simply means that those bytes are an encoding 
of the text.

Your entire process treats the output as text, because the commands 
issue textual output.

Therefore, the most sensible thing to do at this point is to _decode_ 
the bytes into text as soon as you get them from the command, and then 
the rest of your programme can work in text (str) from then on.

So I would be inclined to change:

    for line in out:
        device = self.parse_device_info(line)

into (untested):

    for line_b in out:
        line = line_b.decode(errors='replace')
        device = self.parse_device_info(line)

That will turn line_b (a bytes object holding the line) into text before 
you try to do any parsing. From that point onward, everything is text 
(str) and you do not need to put any bytes->str stuff elsewhere in your 
programme.

Some remarks:

That decode line above uses the default bytes->str decoding, which is 
'utf-8'. That is probably how your system works, but if it is not you 
need to adjust accordingly. If the command ussues pure ASCII you'll be 
fine regardless.

The decode uses "errors='replace'", which means that if the bytes data 
are _not_ correct UTF-8 encoded text, the decoder will put some 
replacement characters in the result instead of raising an exception.  
This simplifies your code, and since you're parsing the output anyway 
for infomation the replacements should show up to your eye. The default 
decode mode is 'strict', which would raise an exception on an invalid 
encoding.

Purists might decode the output stream ("out") before the for-loop, but 
in most encodings including UTF-8, you can split on newlines (byte code 
10, ASCII NL) safely anyway, so we just split first and decode each 
"bytes" line individually.

In the loop I deliberately iterate over "line_b", and have the decoded 
text in "line", instead of doing something like:

    for line in out:
        line = line.decode(....)

That way I am always sure that I intend to talk about bytes in one place 
(line_b) and text (line) in another. Having variable that might contain 
bytes _or_ text leads to confusion and difficult debugging.

The core takeaway here is that you want to keep in mind whether you're 
working in bytes or text (str). Keep the division clean, that way all 
you other code can be written appropriately. So: the command pipe output 
is bytes. COnvert it to text before passing to your text parsing code.  
That way all the parsing code can work in text (str) and have no weird 
conversion logic.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list