String Handling in Python 3 Test Scripts

String Handling in Python 3 Test Scripts

With Python 2 not seeing any further development or bug fixes after January 1st 2020, requests for custom Squish packages containing Python 3 support have gained a lot more traction. Since the next major Squish release will ship with Python 3, we’ll have a short look at Python 3-specific behavior in the context of Squish GUI tests. The most obvious change in behavior is how Python handles strings and how individual characters are represented inside a string.

Strings in Python

In Python 2, handling of strings was rather simple. Script writers usually did not have to take care about where these strings came from (network, files from other machines, etc.) or how their internal representation (also known as “encoding”) would look like. Most of the time, everything would just work, except for cases where it wouldn’t.

In Python 3, the authors of the language acknowledged that strings can come from different kinds of sources. Also, the Python 2 behavior of “working most of the time, but not always” may cause problems that are hard to track down.

The result is that in Python 3, strings now care about this internal representation, details of which would far exceed this article. Thankfully, the Python 3 documentation explains everything in its Unicode HOWTO.

Strings in Squish API

Traditionally, Squish always expected test scripts to be UTF-8 encoded. In fact, the Squish IDE by default creates all text files — and even the recently added Script-based Object Map — in this encoding. For literal strings in single- or double-quotes, this likely means that they are already in a format that is safely read and understood by Python 3. The following will just work fine:

someObject.title = '😀'  # Set a string property to a grinning face emoji

For data from any other source, like network connections or files read inside the test script, one extra step will be needed in Python 3. Most Python functions will provide such data as bytes instead of str. This means they will first have to be converted to str before passing them on to anything in the Squish API that expects a string.

This can be achieved by calling bytes.decode(encoding):

with open('input.txt') as f:
  message = f.read()
type(someObject, message)  # does not work in Python 3 anymore
type(someObject, message.decode('utf-8'))  # works in Python 2 and 3

Strings in GUI Toolkits

In addition to string types in Python, Squish also deals with string types from the GUI toolkit of the AUT. Typical types can be QString for Qt-based AUTs or java.lang.String in the case of Java-based AUTs. Conversion between these string types is usually done automatically inside test scripts. In some cases, however, an explicit conversion from the toolkit string type into the script language string type is needed or is at least helpful:

# Look up a QLineEdit inside a Qt AUT
lineEdit = waitForObject(names.surname_LineEdit)
# QLineEdit.text is a QString
test.compare(className(lineEdit.text), "QString")
# Comparing toolkit strings with Python strings works fine
test.compare(lineEdit.text, "Hello")
# Explicit conversion to Python str is also possible
test.compare(str(lineEdit.text), "Hello")

Summary

String handling of Python inside Squish test scripts does not change a lot between Python 2 and 3. In most cases, the transition between Python versions should be transparent as long as all test script files are properly encoded as UTF-8. For the remaining cases, Python 3 offers functions to convert between bytes and str when necessary.

Stefan Gehn joined froglogic in 2010 after finishing his Master's degree in computer science. He's mostly leaping around everything that involves Qt and Linux and has been squashing bugs left and right in several other areas of Squish ever since.

6 Comments

  1. Frieder Schüler 6 months ago

    I would rather open the file with the correct encoding, instead of converting all the strings afterwards:

    open(path_to_file, mode=’r’, encoding=’utf-8′)

    In Python 2 there is no encoding parameter for the built-in open() function, but you can use the codecs.open() function instead of the built-in codecs module.

    • Author
      Stefan Gehn 6 months ago

      You are right. When reading a file the code can be indeed simplified to what you wrote.

      There may still be other cases like low-level socket operations where Python always returns bytes that need to be converted into str first, in order to pass them to a Squish function expecting a string.

    • Frerich Raabe 6 months ago

      I believe that opening the file in binary mode would have worked better to demonstrate the point: you get a `str` in Python 2, but `bytes` in Python 3.

  2. Joel 6 months ago

    Stefan, thanks for this post.
    When you write “next major Squish release will ship with Python 3”, are you refering to 6.6 or 7.0?
    Is 6.6 close to be released?

    • Author
      Stefan Gehn 6 months ago

      The next Squish release will likely be versioned “6.6”.
      Due to other upcoming releases and the overall situation I cannot really make any estimates on when it will be released though.

      • Joel 6 months ago

        Thanks!

Leave a reply

Your email address will not be published. Required fields are marked *

*