示例#1
0
    def _process_star(cls, red, stars, skip_lineno=False):
        """
        _process_star is designed to replace from X import * methods.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param stars: List of redbaron nodes that matched for this proc.
        :type stars: list
        """
        mappings = {}
        for star in stars:
            from_import = star.parent
            binding = from_import.value[0]
            second_level_modules = None
            if len(star.parent.value) > 1:
                second_level_modules = [star.parent.value[1].dumps()]
            if len(star.parent.value) > 2:
                pass

            children = cls._get_children(binding.dumps(), second_level_modules)
            if second_level_modules is None:
                second_level_modules = children
            text = "from {binding} import {slm}".format(
                binding="Qt",
                slm=", ".join([name for name in second_level_modules]))

            change(logger=EXPAND_STARS_LOG,
                   node=star.parent,
                   replacement=text,
                   skip_lineno=skip_lineno)
            mappings.update(children)
            # star.replace(
            #     text
            # )
        return mappings
示例#2
0
    def _process_to_methods(red, objects, skip_lineno=False, **kwargs):
        """
        Attempts at fixing the "toString" "toBool" "toPyObject" etc
        PyQt4-apiv1.0 helper methods.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        for node in objects:
            raw = node.parent.dumps()
            changed = _conversion_methods.to_methods(raw)
            if changed != raw:
                change(
                    logger=PSEP_LOG,
                    node=node.parent,
                    replacement=changed,
                    skip_lineno=skip_lineno,
                )

                node.parent.replace(changed)
                continue
示例#3
0
def _convert_root_name_imports(red, aliases, skip_lineno=False):
    """
    _convert_root_name_imports is a function that should be used in cases
    where the original code just imported the python binding and did not
    import any second level modules.

    For example:
    ```
    import PySide

    ```
    :param red: The redbaron ast.
    :type red: redbaron.RedBaron
    :param aliases: Aliases is the replacement information that is build
        automatically from qt_py_convert.
    :type aliases: dict
    :param skip_lineno: An optional performance flag. By default, when the
        script replaces something, it will tell you which line it is
        replacing on. This can be useful for tracking the places that
        changes occurred. When you turn this flag on however, it will not
        show the line numbers. This can give great performance increases
        because redbaron has trouble calculating the line number sometimes.
    :type skip_lineno: bool
    """
    def filter_function(value):
        """A filter delegate for our red.find_all function."""
        return value.dumps().startswith("Qt.")

    matches = red.find_all("AtomTrailersNode", value=filter_function)
    matches += red.find_all("DottedNameNode", value=filter_function)
    lstrip_qt_regex = re.compile(r"^Qt\.", )

    if matches:
        MAIN_LOG.debug(
            color_text(
                text="====================================",
                color=ANSI.colors.purple,
            ))
        MAIN_LOG.debug(
            color_text(
                text="Replacing top level binding imports.",
                color=ANSI.colors.purple,
                style=ANSI.styles.underline,
            ))

    for node in matches:
        name = lstrip_qt_regex.sub("", node.dumps(), count=1)

        root_name = name.split(".")[0]
        if root_name in COMMON_MODULES:
            aliases["root_aliases"].add(root_name)
            change(logger=MAIN_LOG,
                   node=node,
                   replacement=name,
                   skip_lineno=skip_lineno)
            node.replace(name)
        else:
            MAIN_LOG.warning(
                "Unknown second level module from the Qt package \"{}\"".
                format(color_text(text=root_name, color=ANSI.colors.orange)))
示例#4
0
    def _process_qstringlist(red, objects, skip_lineno=False, **kwargs):
        """
        _process_qstringlist is designed to replace QStringList code.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        # TODO: Find different usage cases of QStringList.
        #       Probably just need support for construction and isinstance.
        # Replace each node
        for node in objects:
            raw = node.parent.dumps()
            changed = re.sub(r"((?:QtCore\.)?QStringList)", "list", raw)
            if changed != raw:
                change(
                    logger=PSEP_LOG,
                    node=node.parent,
                    replacement=changed,
                    skip_lineno=skip_lineno,
                )

                node.parent.replace(changed)
示例#5
0
    def _process_qstring(red, objects, skip_lineno=False, **kwargs):
        """
        _process_qstring is designed to replace QString code.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        # Replace each node
        for node in objects:
            raw = node.parent.dumps()
            changed = re.sub(r"((?:QtCore\.)?QString(?:\.fromUtf8)?)",
                             text_type.__name__, raw)
            if changed != raw:
                change(
                    logger=PSEP_LOG,
                    node=node.parent,
                    replacement=changed,
                    skip_lineno=skip_lineno,
                )

                node.parent.replace(changed)
示例#6
0
 def _no_second_level_module(node, _child_parts, skip_lineno=False):
     replacement = "import Qt"
     change(
         logger=IMPORTS_LOG,
         node=node,
         replacement=replacement,
         skip_lineno=skip_lineno
     )
     node.replace(replacement)
示例#7
0
    def _process_qvariant(red, objects, skip_lineno=False, **kwargs):
        """
        _process_qvariant is designed to replace QVariant code.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        qvariant_expr = re.compile(
            r"(?:QtCore\.)?QVariant(?P<is_instance>\((?P<value>.*)\))?")

        # Replace each node
        for node in objects:
            raw = node.parent.dumps()
            matched = qvariant_expr.search(raw)
            if matched:
                if not matched.groupdict()["is_instance"]:
                    # We have the usage of a QVariant Class object.
                    # This leads to an invalid statement and cannot be
                    #   resolved in api 2.0.
                    # We are adding it to warnings and continuing on.
                    ErrorClass.from_node(node=node,
                                         reason="""
As of api v2.0, there is no concept of a "QVariant" object.
Usage of the class object directly cannot be translated into something that \
can consistantly be relied on.

You will probably want to remove the usage of this entirely.""")
                    continue
                else:  # If it was used as an instance (most cases).

                    def replacement(match):
                        """regex sub function"""
                        # Some edge case logic here.
                        # Was having issues replacing the following code:
                        # return QtCore.QVariant()
                        # There was no parameter...So now that becomes:
                        # return None
                        if not match.groupdict()["value"]:
                            return "None"
                        return match.groupdict()["value"]

                    # We have an instance of a QVariant used.
                    changed = qvariant_expr.sub(replacement, raw)

                if changed != raw:
                    change(
                        logger=PSEP_LOG,
                        node=node.parent,
                        replacement=changed.strip(" "),
                        skip_lineno=skip_lineno,
                    )
                    node.parent.replace(changed.strip(" "))
示例#8
0
    def _process_import(cls, red, objects, skip_lineno=False):
        """
        _process_import is designed to replace from import methods.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        binding_aliases = ALIAS_DICT
        mappings = {}

        # Replace each node
        for node, binding in objects:
            from_import_parts = cls._get_import_parts(node, binding)
            if len(from_import_parts) and from_import_parts[0]:
                second_level_module = from_import_parts[0]
            else:
                cls._no_second_level_module(node.parent,
                                            from_import_parts,
                                            skip_lineno=skip_lineno)
                binding_aliases["bindings"].add(binding)
                for target in node.parent.targets:
                    binding_aliases["root_aliases"].add(target.value)
                continue

            for _from_as_name in node.parent.targets:
                if _from_as_name.type in IGNORED_IMPORT_TARGETS:
                    continue
                if _from_as_name.type == "star":
                    # TODO: Make this a flag and make use the expand module.
                    _, star_mappings = stars_process(red)
                    mappings.update(star_mappings)
                else:
                    key = _from_as_name.target or _from_as_name.value
                    value = ".".join(
                        from_import_parts) + "." + _from_as_name.value
                    mappings[key] = value

            replacement = "from Qt import {key}".format(
                key=second_level_module)
            change(logger=FROM_IMPORTS_LOG,
                   node=node.parent,
                   replacement=replacement,
                   skip_lineno=skip_lineno)
            node.parent.replace(replacement)
            binding_aliases["bindings"].add(binding)
            for target in node.parent.targets:
                binding_aliases["root_aliases"].add(target.value)
            if binding not in binding_aliases:
                binding_aliases[binding] = set()
            binding_aliases[binding] = binding_aliases[binding].union(
                set([target.value for target in node.parent.targets]))
        return binding_aliases, mappings
示例#9
0
    def _no_second_level_module(node, _parts, skip_lineno=False):
        text = "from Qt import {key}".format(
            key=", ".join([target.value for target in node.targets]))

        change(logger=FROM_IMPORTS_LOG,
               node=node,
               replacement=text,
               skip_lineno=skip_lineno)

        node.replace(text)
示例#10
0
    def _process_qsignal(red,
                         objects,
                         skip_lineno=False,
                         explicit_signals_flag=False):
        """
        _process_qsignal is designed to replace QSignal code.
        It calls out to the _qsignal module and can fix disconnects, connects,
        and emits.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        for node in objects:
            raw = node.parent.dumps()

            if "disconnect" in raw:
                changed = _qsignal.process_disconnect(
                    raw, explicit=explicit_signals_flag)
                if changed != raw:
                    change(
                        logger=PSEP_LOG,
                        node=node.parent,
                        replacement=changed,
                        skip_lineno=skip_lineno,
                    )

                    node.parent.replace(changed)
                    continue
            if "connect" in raw:
                changed = _qsignal.process_connect(
                    raw, explicit=explicit_signals_flag)
                if changed != raw:
                    change(
                        logger=PSEP_LOG,
                        node=node.parent,
                        replacement=changed,
                        skip_lineno=skip_lineno,
                    )
                    node.parent.replace(changed)
                    continue
            if "emit" in raw:
                changed = _qsignal.process_emit(raw,
                                                explicit=explicit_signals_flag)
                if changed != raw:

                    change(
                        logger=PSEP_LOG,
                        node=node.parent,
                        replacement=changed,
                        skip_lineno=skip_lineno,
                    )
                    node.parent.replace(changed)
                    continue
示例#11
0
    def _process_import(cls, red, objects, skip_lineno=False):
        """
        _process_import is designed to replace import methods.

        :param red: redbaron process. Unused in this method.
        :type red: redbardon.RedBaron
        :param objects: List of redbaron nodes that matched for this proc.
        :type objects: list
        :param skip_lineno: Global "skip_lineno" flag.
        :type skip_lineno: bool
        """
        binding_aliases = ALIAS_DICT
        mappings = {}

        # Replace each node
        for node, binding in objects:
            for child_index, child in enumerate(node):
                _child_name = cls._build_child_name(child)
                _child_as_name = child.target
                if _child_name.split(".")[0] not in __supported_bindings__ \
                        and _child_as_name not in __supported_bindings__:
                    # Only one of our multi import node's children is relevant.
                    continue

                _child_parts = _child_name.replace(binding, "")
                _child_parts = _child_parts.lstrip(".").split(".")

                # Check to see if there is a second level module
                if len(_child_parts) and _child_parts[0]:
                    second_level_module = _child_parts[0]
                else:
                    if len(node) == 1:
                        # Only one in the import: "import PySide"
                        cls._no_second_level_module(node.parent, _child_parts)
                    else:
                        # Multiple in the import: "import PySide, os"
                        node_parent = node.parent
                        node.pop(child_index)
                        repl = node.parent.dumps() + "\nimport Qt"

                        change(
                            logger=IMPORTS_LOG,
                            node=node_parent,
                            replacement=repl,
                            skip_lineno=skip_lineno
                        )

                        node.parent.replace(repl)
                    if _child_as_name:
                        mappings[_child_as_name] = "Qt"
                    else:
                        mappings[_child_name] = "Qt"
                        binding_aliases["bindings"].add(binding)
                    continue

                mappings[_child_as_name or _child_name] = ".".join(
                    _child_parts
                )

                change(
                    logger=IMPORTS_LOG,
                    node=node.parent,
                    replacement="from Qt import {key}".format(
                        key=second_level_module
                    ),
                    skip_lineno=skip_lineno
                )

                node.parent.replace(
                    "from Qt import {key}".format(key=second_level_module)
                )
                binding_aliases["bindings"].add(binding)
                binding_aliases["root_aliases"].add(second_level_module)
                if binding not in binding_aliases:
                    binding_aliases[binding] = set()
                binding_aliases[binding].add(second_level_module)
        return binding_aliases, mappings
示例#12
0
def _cleanup_imports(red, aliases, mappings, skip_lineno=False):
    """
    _cleanup_imports fixes the imports.
    Initially changing them as per the following:
    >>> from PyQt4 import QtGui, QtCore
    to 
    >>> from Qt import QtGui, QtCore
    for each binding.
    It doesn't have enough knowledge of your script at this point to know if 
      you need QtWidgets or if the ones you import are all used. 
    This will get reflected at the end.

    :param red: The redbaron ast.
    :type red: redbaron.RedBaron
    :param aliases: Aliases is the replacement information that is build
        automatically from qt_py_convert.
    :type aliases: dict
    :param mappings: Mappings is information about the bindings that are used.
    :type mappings: dict
    :param skip_lineno: An optional performance flag. By default, when the
        script replaces something, it will tell you which line it is
        replacing on. This can be useful for tracking the places that
        changes occurred. When you turn this flag on however, it will not
        show the line numbers. This can give great performance increases
        because redbaron has trouble calculating the line number sometimes.
    :type skip_lineno: bool
    """
    replaced = False
    deletion_index = []
    imps = red.find_all("FromImportNode")
    imps += red.find_all("ImportNode")

    MAIN_LOG.debug(
        color_text(text="===========================", color=ANSI.colors.blue))
    MAIN_LOG.debug(
        color_text(
            text="Consolidating Import lines.",
            color=ANSI.colors.blue,
            style=ANSI.styles.underline,
        ))

    for child in imps:
        for value in child.value:
            value_str = value.value
            try:
                value_str = value_str.dumps()
            except AttributeError:
                pass
            if value.value == "Qt" or value_str in __suplimentary_bindings__:
                if not replaced:
                    names = filter(
                        lambda a: True if a in COMMON_MODULES else False,
                        aliases["used"],
                    )
                    if not names:  # Attempt to build names from input aliases.
                        members = filter(
                            lambda a: True if a in mappings else False,
                            aliases["root_aliases"],
                        )
                        names = []
                        for member in members:
                            names.append(mappings[member].split(".")[0])

                    if not names:
                        MAIN_LOG.warning(
                            color_text(text="We have found no usages of Qt in "
                                       "this script despite you previously"
                                       " having imported the binding.\nIf "
                                       "you think this is in error, "
                                       "please let us know and submit an "
                                       "issue ticket with the example you "
                                       "think is wrong.",
                                       color=ANSI.colors.green))
                        child.parent.remove(child)
                        continue
                    # What we want to replace to.
                    replace_text = "from Qt import {key}".format(
                        key=", ".join(names))

                    cleaning_message = color_text(text="Cleaning",
                                                  color=ANSI.colors.green)
                    cleaning_message += (
                        " imports from: \"{original}\" to \"{replacement}\"")
                    change(msg=cleaning_message,
                           logger=MAIN_LOG,
                           node=child,
                           replacement=replace_text,
                           skip_lineno=skip_lineno)

                    child.replace(replace_text)
                    replaced = True
                else:
                    deleting_message = "{name} \"{orig}\"".format(
                        orig=str(child).strip("\n"),
                        name=color_text(text="Deleting",
                                        color=ANSI.colors.red))
                    change(msg=deleting_message,
                           logger=MAIN_LOG,
                           node=child,
                           replacement="",
                           skip_lineno=skip_lineno)
                    child.parent.remove(child)
            else:
                pass
    for child in reversed(deletion_index):
        MAIN_LOG.debug("Deleting {node}".format(node=child))
        child.parent.remove(child)
示例#13
0
def _convert_body(red, aliases, mappings, skip_lineno=False):
    """
    _convert_body is  one of the first conversion functions to run on the
    redbaron ast.
    It finds the NameNode's or the AtomTrailersNode+DottedNameNodes and will
    run them through the filter expressions built off of the values in
    mappings.
    If found, it will replace the source value with the destination value in
    mappings.

    :param red: The redbaron ast.
    :type red: redbaron.RedBaron
    :param aliases: Aliases is the replacement information that is build
        automatically from qt_py_convert.
    :type aliases: dict
    :param mappings: Mappings is information about the bindings that are used.
    :type mappings: dict
    :param skip_lineno: An optional performance flag. By default, when the
        script replaces something, it will tell you which line it is
        replacing on. This can be useful for tracking the places that
        changes occurred. When you turn this flag on however, it will not
        show the line numbers. This can give great performance increases
        because redbaron has trouble calculating the line number sometimes.
    :type skip_lineno: bool
    """
    def expression_factory(expr_key):
        """
        expression_factory is a function factory for building a regex.match
        function for a specific key that we found in misplaced_mappings
        """
        regex = re.compile(r"{value}(?:[\.\[\(].*)?$".format(value=expr_key),
                           re.DOTALL)

        def expression_filter(value):
            """
            Basic filter function matching for red.find_all against a regex
            previously created from the factory
            ."""
            return regex.match(value.dumps())

        return expression_filter

    # Body of the function
    for key in sorted(mappings, key=len):
        MAIN_LOG.debug(
            color_text(
                text="-" * len(key),
                color=ANSI.colors.teal,
            ))
        MAIN_LOG.debug(
            color_text(
                text=key,
                color=ANSI.colors.teal,
                style=ANSI.styles.underline,
            ))
        if "." in key:
            filter_function = expression_factory(key)
            matches = red.find_all("AtomTrailersNode", value=filter_function)
            matches += red.find_all("DottedNameNode", value=filter_function)
        else:
            matches = red.find_all("NameNode", value=key)
        if matches:
            for node in matches:
                # Dont replace imports, we already did that.
                parent_is_import = node.parent_find("ImportNode")
                parent_is_fimport = node.parent_find("FromImportNode")
                if not parent_is_import and not parent_is_fimport:
                    # If the node's parent has dot syntax. Make sure we are
                    # the first one. Reasoning: We are relying on namespacing,
                    # so we don't want to turn bob.foo.cat into bob.foo.bear.
                    # Because bob.foo.cat might not be equal to the mike.cat
                    # that we meant to change.
                    if hasattr(node.parent,
                               "type") and node.parent.type == "atomtrailers":
                        if not node.parent.value[0] == node:
                            continue

                    if key != mappings[key]:
                        replacement = node.dumps().replace(key, mappings[key])
                        change(logger=MAIN_LOG,
                               node=node,
                               replacement=replacement,
                               skip_lineno=skip_lineno)
                        if mappings[key].split(".")[0] in COMMON_MODULES:
                            aliases["used"].add(mappings[key].split(".")[0])

                        node.replace(replacement)
                    else:
                        if node.dumps().split(".")[0] in COMMON_MODULES:
                            aliases["used"].add(node.dumps().split(".")[0])
示例#14
0
def _convert_attributes(red, aliases, skip_lineno=False):
    """
    _convert_attributes converts all AtomTrailersNodes and DottenNameNodes to 
      the Qt5/PySide2 api matching Qt.py..
    This means that anything that was using QtGui but is now using QtWidgets 
      will be updated for example.
    It does not do any api v1 - api v2 conversion or specific 
      misplaced_mapping changes.

    :param red: The redbaron ast.
    :type red: redbaron.RedBaron
    :param aliases: Aliases is the replacement information that is build
        automatically from qt_py_convert.
    :type aliases: dict
    :param skip_lineno: An optional performance flag. By default, when the
        script replaces something, it will tell you which line it is
        replacing on. This can be useful for tracking the places that
        changes occurred. When you turn this flag on however, it will not
        show the line numbers. This can give great performance increases
        because redbaron has trouble calculating the line number sometimes.
    :type skip_lineno: bool
    """
    # Compile our expressions
    # Our expressions are basically as follows:
    # From:
    #   <Any Qt SLM>.<any_member of A>
    # To:
    #   <A>.<\back reference to the member matched>
    # Where A is the specific Qt SecondLevelModule that we are building this
    #   expression for.
    #
    # Also sorry this is longer than 79 chars..
    # It gets harder to read the more I try to make it more readable.
    expressions = [
        (
            re.compile(
                r"^(?P<module>{modules})\.(?P<widget>(?:{widgets})(?:[.\[(].*)?)$"
                .format(  # Regular expression
                    modules="|".join(
                        re.escape(name) for name in Qt._common_members.keys()),
                    widgets="|".join(
                        re.escape(widget)
                        for widget in Qt._common_members[module_name])),
                re.MULTILINE),
            module_name) for module_name in Qt._common_members
    ]

    def finder_function_factory(exprs):
        """Basic function factory. Used as a find_all delegate for red."""
        def finder_function(value):
            """The filter for our red.find_all function."""
            return any(
                [expression.match(value.dumps()) for expression, mod in exprs])

        return finder_function

    mappings = {}
    # Find any AtomTrailersNode that matches any of our expressions.
    nodes = red.find_all("AtomTrailersNode",
                         value=finder_function_factory(expressions))
    nodes += red.find_all("DottedNameNode",
                          value=finder_function_factory(expressions))
    header_written = False
    for node in nodes:
        orig_node_str = node.dumps()
        added_module = False
        for expr, module_ in expressions:
            modified = expr.sub(
                r"{module}.\2".format(module=module_),
                orig_node_str,
            )

            if modified != orig_node_str:
                mappings[orig_node_str] = modified
                aliases["used"].add(module_)
                added_module = True
                if not header_written:
                    MAIN_LOG.debug(
                        color_text(
                            text="=========================",
                            color=ANSI.colors.orange,
                        ))
                    MAIN_LOG.debug(
                        color_text(text="Parsing AtomTrailersNodes",
                                   color=ANSI.colors.orange,
                                   style=ANSI.styles.underline))
                    header_written = True

                repl = str(node).replace(
                    str(node.value[0]).strip("\n"), module_)

                change(logger=Qt4_Qt5_LOG,
                       node=node,
                       replacement=repl,
                       skip_lineno=skip_lineno)
                # Only replace the first node part of the statement.
                # This allows us to keep any child nodes that have already
                # been gathered attached to the main node tree.

                # This was the cause of a bug in our internal code.
                # http://dd-git.d2.com/ahughes/qt_py_convert/issues/19

                # A node that had child nodes that needed replacements on the
                # same line would cause an issue if we replaced the entire
                # line the first replacement. The other replacements on that
                # line would not stick because they would be replacing to an
                # orphaned tree.
                node.value[0].replace(module_)
                break
            # else:
            #     if orig_node_str.split(".")[0] in COMMON_MODULES:
            #         aliases["used"].add(orig_node_str.split(".")[0])
        if not added_module:
            aliases["used"].add(orig_node_str.split(".")[0])
    return mappings