Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-1519

Non-namespaced enums raising SystemError

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P3: Somewhat important
    • Resolution: Done
    • Affects Version/s: 5.15.2
    • Fix Version/s: None
    • Component/s: PySide
    • Labels:
      None
    • Environment:
      Ubuntu Linux 18.04
      macOS 11.2
    • Platform/s:
      All
    • Commits:
      6629810389d0d546a9306d7a65b4c2c20bd5b9a6 (pyside/pyside-setup/dev) 6d342a1857af6ae3a48c1b18e4d3d2730488c942 (pyside/tqtc-pyside-setup/tqtc/lts-5.15)

      Description

      As part of our build process for our Python bindings, a custom script creates a .pyi file for our module. Part of this is invoking PySide2.support.signature.lib.enum_sig.HintingEnumerator(formatter).module(module_name) with the name of our module as argument.
      Since our update to v5.15.2, this call raises a SystemError with the error message being implement enum instances at module level.
      The error is caused by four enums in a bog-standard C++ header file. I cannot share the complete file, but I've included a shortened version that includes examples of everything included to be an accurate representation. You'll find the enums close to the bottom.

      defaults.h
      #ifndef DEFAULTS_H
      #define DEFAULTS_H
      
      #include <QList>
      
      class QString;
      class QBitArray;
      class QVariant;
      class QByteArray;
      
      typedef QList<QByteArray> ArrayList;
      
      #define PING_PERIOD 120000
      
      #define DEFAULT_DOMAIN "client.tld"
      #define DEFAULT_SYSAP_NAME "foo"
      
      #define DEFAULT_LANGUAGE "en"
      
      #define MAX_STRING_SIZE_IN_BYTE 51     //!< without terminating symbol
      #define ELLIPSIS_CHARACTER      0x2026 //!< three dots as one symbol in utf-8
      
      #define RF_AUTO_UPDATE_SOFTWARE_ID  (0x0253U)
      #define RF_BATTERY_AUTO_UPDATE_SOFTWARE_ID  (0x0A7F)
      #define RF_JABIL_AUTO_UPDATE_KEYPAD_SOFTWARE_ID  (0x0D07U)
      #define RF_JABIL_AUTO_UPDATE_RTC_SOFTWARE_ID  (0x0106FU)
      
      #ifndef SOFTWARE_VERSION
      #define SOFTWARE_VERSION "2.4.0"
      #endif
      #ifndef BUILD_REVISION
      #define BUILD_REVISION "1"
      #endif
      const int EMULATED_DEVICE_FILE_VERSION = 1;
      #define EMULATED_DEVICE_FILE_MAGIC "EDEV"
      const int PROJECT_DATA_VERSION = 2;
      
      #define RF_DEFAULT_HEARTBEAT_INTERVAL (5 * 60)
      #define RF_NORMAL_HEARTBEAT_INTERVAL (10 * 60)
      #define RF_UPDATE_HEARTBEAT_INTERVAL (60 * 60)
      #define RF_MINUMUM_HEARTBEAT_INTERVAL (60)
      
      #define TASK_SUCCESS        0
      #define TASK_ERROR          1
      #define TASK_TIMEOUT        2
      #define TASK_XMLERROR       3
      #define TASK_SUPERFLUOUS    4
      #define TASK_SUCCESS_REBOOT 5
      #define TASK_TRY_AGAIN      6
      #define TASK_NEED_UPDATE    7
      #define TASK_REALLY_DEFECT  8
      
      #define SIZE_AS_STRING(A) (QString("size:")+QString::number((A).size()))
      
      #if QT_VERSION < QT_VERSION_CHECK(5, 1, 0)
      
      #define MANUFACTURER_FROM_SERIALNR(S, OK) \
          (ushort)((S).left(4).toUInt(&(OK), 16))
      
      #define PRODUCTIONNR_FROM_SERIALNR(S, OK) \
          (uint)((S).right(8).toUInt(&(OK), 16))
      
      #else
      
      #define MANUFACTURER_FROM_SERIALNR(S, OK) \
          (ushort)((S).leftRef(4).toUInt(&(OK), 16))
      
      #define PRODUCTIONNR_FROM_SERIALNR(S, OK) \
          (uint)((S).rightRef(8).toUInt(&(OK), 16))
      
      #endif
      
      enum UartTransportMode{
        LL_MODE,
        LTL_MODE,
        APL_MODE
      };
      
      enum UartPriority {
        UART_PRIORITY_HIGH,
        UART_PRIORITY_LOW,
        UART_PRIORITY_SYSTEM
      };
      
      enum UserPermissions
      {
        up_invalid = 0,
        up_vbus =            (1U << 0),
        up_timer =           (1U << 1),
        up_monitor =         (1U << 2),
        up_setdp =           (1U << 3),
        up_config =          (1U << 4),
        up_action =          (1U << 5),
        up_myclient =           (1U << 6),
        up_network =         (1U << 7),
        up_users =           (1U << 8),
        up_safety =          (1U << 9),
        up_install =         (1U << 10),
      
        up_hasChanged =      (1U << 31)
      };
      
      /* tx power tables channel 11 to 26 */
      #define TX_POWERTABLE_DEFAULT    "09 09 09 09 09 09 09 09 09 09 09 09 09 09 09 09"
      #define TX_POWERTABLE_USA            "00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 0C"
      
      #define INVALID_DEPENDENCY_INDEX static_cast<unsigned short>(0xFFFFu)
      
      /* LED palette indices - the actual color definitions are in system/Led.cpp, colorPalette */
      enum class LedColor : quint8 {
        Black = 0,
        Red,
        Green,
        Blue,
        Orange,
        Cyan,
        Purple,
        White,
      
        _NumEntries
      };
      
      
      constexpr LedColor ACCESS_COLOR_REMOVABLE_MEDIA = LedColor::Green;
      
      // datapoint direction indicator for better readability than a random "true" or "false" in a call
      enum class DPDirection {
        Input,
        Output
      };
      
      #endif // DEFAULTS_H
      

      Our code defines heaps of other enums, none of which cause an error. However, these five seem to be the only ones to not be defined inside any namespace.
      Removing these enums from our typesystem rids us of the error, but is not an acceptable workaround. The relevant excerpt from the typesystem:

      Typesystem excerpt including the enums from default.h
      <!-- sublib/sublibcommon/defines.h -->
      <enum-type name="UartTransportMode"/>
      <enum-type name="UartPriority"/>
      <enum-type name="UserPermissions"/>
      <enum-type name="LedColor"/>
      <enum-type name="DPDirection"/>
      

      For reference, here is the important bit of the custom script:

      Python script raising the error
      def main():
          args = parseCmdLineArgs()
      
          if args.add_ld_library_path and not args.internal:
              add_ld_library_paths(args)
      
          module_name = "fooBindings"
      
          tmp_out_file = args.out + ".tmp"
          temporary_dir = None
          try:
              with open(tmp_out_file, "w") as outfile:
                  outfile.write("# -*- coding: utf-8 -*-\n")
                  outfile.write("\n")
                  outfile.write("# WARNING: This file is auto-generated, all modifications will be overwritten\n")
                  outfile.write("\n")
                  outfile.write("import PySide2\n")
                  outfile.write("import PySide2.QtCore\n")
                  outfile.write("import shiboken2 as Shiboken\n")
                  outfile.write("import typing\n")
                  formatter = Formatter(outfile=outfile)
      
                  temporary_dir = tempfile.mkdtemp(dir=args.input_dir)
                  library_dir = temporary_dir
                  bindings_path = os.path.join(temporary_dir, 'fooBindings.so')
                  if platform.system() == 'Darwin':
                      addLocalRpathToPythonBindings(input_path=os.path.join(args.input_dir, 'fooBindings.so'), output_path=bindings_path)
                  else:
                      shutil.copyfile(os.path.join(args.input_dir, 'fooBindings.so'), bindings_path)
      
                  # Force importing from --input-dir, not from an installed location, otherwise we may catch
                  # an old file.
                  # NOTE: This must occur AFTER all import statements, but BEFORE the call to module() before,
                  #       which imports the specified module (only)
                  orig_sys_path = sys.path
                  sys.path = [library_dir]
                  PySide2.support.signature.lib.enum_sig.HintingEnumerator(formatter).module(module_name)
                  sys.path = orig_sys_path
                  outfile.write("\n")
          finally:
              if temporary_dir:
                  shutil.rmtree(temporary_dir)
          if not os.path.exists(args.out) or not filecmp.cmp(args.out, tmp_out_file):
              if os.path.exists(args.out):
                  os.remove(args.out)
              os.rename(tmp_out_file, args.out)
          else:
              os.remove(tmp_out_file)
      

      add_ld_library_paths and addLocalRpathToPythonBindings are helpers to add necessary information into the binary and shouldn't be of relevance. If you do need them, tell me. The Formatter class mentioned is also part of this script, same deal.

        Attachments

        For Gerrit Dashboard: PYSIDE-1519
        # Subject Branch Project Status CR V

          Activity

            People

            Assignee:
            ctismer Christian Tismer
            Reporter:
            snkauoe Sebastian Kaupe
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Gerrit Reviews

                There are no open Gerrit changes