Beispiel #1
0
def _make_new_list(requirements):
    """Make a flat Python list, using parso nodes.

    Args:
        requirements (iter[:class:`parso.python.tree.String`]):
            The Rez requirements that'll be used to generate the new
            list. No nodes related to punctuation should be used here.
            Just requirements.

    Returns:
        :class:`parso.python.tree.PythonNode`: The generated list.

    """
    nodes = []

    for requirement in requirements:
        nodes.append(requirement)
        nodes.append(tree.Operator(",", (0, 0)))

    prefix = "\n"

    if not nodes:
        prefix = ""

    return tree.PythonNode(
        "atom",
        [tree.Operator("[", (0, 0))] + nodes +
        [tree.Operator("]", (0, 0), prefix=prefix)],
    )
Beispiel #2
0
def _make_intersphinx_pairs(pairs):
    """Convert key/value pairs into something that parso can convert into dict key/values.

    Args:
        pairs (iter[tuple[str, str or tuple[str, str or NoneType]]):
            The intersphinx key/value mapping. Keys are usually the
            name of Rez packages and each value is the URL to the API
            documentation that we need to link to.

    Returns:
        list[:class:`parso.python.tree.Node`]:
            The key/value pairs, flattened into parso objects.

    """
    output = []

    for row, (key, value) in enumerate(sorted(pairs)):
        if isinstance(key, six.string_types):
            key = '"{key}"'.format(key=key)

        if isinstance(value, six.string_types):
            value = '"{value}"'.format(value=value)

        key_length = len(key)
        value_length = len(value)
        output.append(tree.String(key, (row, 0), prefix="\n    "))
        output.append(tree.Operator(":", (row, key_length)))
        output.append(tree.String(str(value), (row, key_length + len(":")), prefix=" "))
        output.append(tree.Operator(",", (row, key_length + len(":") + value_length)))

    return output
def _make_tests_node(data):
    """Create the final "tests" data that will be written to a package.py file.

    Important:
        This function technically doesn't create a "correct"
        parso dict. For the sake of simplicity, some corners were
        cut. For example, value data is converted into a raw
        :class:`parso.python.tree.String`. It's not technically
        "correct" but since this function is called just before writing
        the data into disk and not further modified, it's okay.

    Args:
        data (iter[str, :class:`parso.python.tree.PythonBaseNode`]):
            The key / value information that will be converted into a
            parso dict.

    Returns:
        :class:`parso.python.tree.PythonNode`:
            An atom PythonNode which has a full dictionary "tests"
            defined. This will later override an existing "tests"
            attribute in a package.py or be appended to the file as an
            assignment.

    """
    nodes = []

    for key, value in data:
        nodes.extend([
            tree.String(_escape(key), (0, 0), prefix="\n    "),
            tree.Operator(":", (0, 0)),
        ])

        if isinstance(value, collections.MutableMapping):
            nodes.append(_make_dict_nodes(sorted(value.items()),
                                          prefix="    "))
        elif hasattr(value, "get_code"):
            if hasattr(value, "prefix"):
                value.prefix = " "

            nodes.append(value)
        else:
            nodes.append(tree.String(_escape_all(value), (0, 0), prefix=" "))

        nodes.append(tree.Operator(",", (0, 0)))

    base = tree.PythonNode("dictorsetmaker", nodes)

    return tree.PythonNode(
        "atom",
        [
            tree.Operator("{", (0, 0)), base,
            tree.Operator("}", (0, 0), prefix="\n")
        ],
    )
def make_replacement_nodes(parts, prefix):
    """Convert namespace tokens into parso nodes.

    Args:
        parts (list[str]):
            The namespace import to convert. e.g. ["foo", "bar", "etc"].
        prefix (str):
            A string that will get placed before the joined result of
            `parts`. Usually, this is just a single " ".

    Returns:
        list[:class:`parso.python.tree.Name` or :class:`parso.python.tree.Operator`]:
            The converted nodes of `parts`.

    """
    new = []

    for index, name in enumerate(parts):
        new.append(tree.Name(name, (0, 0)))

        if index + 1 < len(parts):
            new.append(tree.Operator(".", (0, 0)))

    new[0].prefix = prefix

    return new
def _make_dict_nodes(data, prefix=""):
    """Create a parso node that represents the inner key / value pairs of a Python dict.

    In terms of the work needed to generate a Rez "tests" attribute,
    this function is used mainly to create the "inner dict" that a
    "tests" attribute frequently defines.

    Args:
        data (iter[tuple[str, str or list or :class:`parso.python.tree.PythonBaseNode`]]):

    Returns:
        :class:`parso.python.tree.PythonNode`:
            A node that defines the inside of a parso dict. This node
            is not equivalent to an actual dict and still needs to be
            wrapped with another :class:`parso.python.tree.PythonNode`
            to be valid.

    """
    nodes = []

    for key, item in data:
        item = _escape_all(item)

        nodes.append(
            tree.String(_escape(key), (0, 0),
                        prefix="\n    {prefix}".format(prefix=prefix)))
        nodes.append(tree.Operator(":", (0, 0)))
        nodes.append(
            tree.PythonNode(
                "atom",
                [
                    tree.String(item, (0, 0), prefix=" "),
                    tree.Operator(",", (0, 0))
                ],
            ), )

    return tree.PythonNode(
        "atom",
        [
            tree.Operator("{", (0, 0), prefix=" "),
            tree.PythonNode("dictorsetmaker", nodes),
            tree.Operator("}", (0, 0), prefix="\n    "),
        ],
    )
Beispiel #6
0
def _make_intersphinx_dict(pairs):
    """Create a parso literal that defines ``intersphinx_mapping``.

    Args:
        pairs (list[:class:`parso.python.tree.Node`]):
            The key / value dict pairs that will be added into a new
            ``intersphinx_mapping`` dict literal.

    Returns:
        :class:`parso.python.tree.PythonNode`: The generated dict.

    """
    return tree.PythonNode(
        "atom",
        [
            tree.Operator("{", (0, 0), prefix=" "),
            tree.PythonNode("dictorsetmaker", pairs),
            tree.Operator("}", (len(pairs), 0), prefix="\n"),
        ],
    )
Beispiel #7
0
 def convert_leaf(self, pgen_grammar, type, value, prefix, start_pos):
     # print('leaf', repr(value), token.tok_name[type])
     if type == NAME:
         if value in pgen_grammar.keywords:
             return tree.Keyword(value, start_pos, prefix)
         else:
             return tree.Name(value, start_pos, prefix)
     elif type == STRING:
         return tree.String(value, start_pos, prefix)
     elif type == NUMBER:
         return tree.Number(value, start_pos, prefix)
     elif type == NEWLINE:
         return tree.Newline(value, start_pos, prefix)
     elif type == ENDMARKER:
         return tree.EndMarker(value, start_pos, prefix)
     else:
         return tree.Operator(value, start_pos, prefix)
Beispiel #8
0
def _separate_with_commas(nodes):
    """Add a comma between every parso node, starting at the first element.

    Args:
        nodes (list): Some objects that will get parso nodes inserted between its elements.

    Returns:
        list[:class:`parso.python.Tree.PythonBaseNode`]:
            The original `nodes` plus comma operators.

    """
    nodes = copy.deepcopy(nodes)

    for index in reversed(range(1, len(nodes))):
        nodes.insert(index, tree.Operator(",", (0, 0)))

    return nodes
Beispiel #9
0
    def _get_entries(assignment):
        """Find every help key + value entry from some "help" attribute.

        If `assignment` is invalid, this function may return an empty list.

        Args:
            assignment (:class:`parso.python.tree.ExprStmt`):
                The node that assigns the "help" attribute, which will
                be modified.

        Returns:
            list[:class:`parso.python.tree.PythonNode`]:
                The list node that is a parso "list of list of strs".

        """
        root = _get_list_root(assignment)

        if root:
            return _get_inner_list_entries(root)

        string_root = _get_str_root(assignment)

        if not string_root:
            return []

        if string_root.get_code().strip() in ("''", '""'):
            return []

        node = tree.PythonNode(
            "testlist_comp",
            [
                tree.String(
                    '"{rez_configuration.DEFAULT_HELP_LABEL}"'.format(
                        rez_configuration=rez_configuration),
                    (0, 0),
                ),
                tree.Operator(",", (0, 0)),
                string_root,
            ],
        )

        for child in node.children:
            child.parent = node

        return [node]
    def _make_import(namespace_parts, prefix="", alias=None):
        """Make a new from-import node and insert it into an existing parso graph.

        Args:
            namespace_parts (list[str]):
                The import that will be converted into a parso from-import node.
            prefix (str, optional):
                The leading whitespace (indentation) placed before the new import.

        Returns:
            :class:`parso.python.tree.ImportFrom`: A newly generated import statement.

        """
        base_names = namespace_parts[:-1]
        base_count = len(base_names)
        base_nodes = []

        if base_count > 1:
            for is_last, name in iterbot.iter_is_last(base_names):
                base_nodes.append(tree.Name(name, (0, 0)))

                if not is_last:
                    base_nodes.append(tree.Operator(".", (0, 0)))

            base_nodes[0].prefix = " "
            base = tree.PythonNode("dotted_name", base_nodes)
        else:
            base = tree.Name(base_names[0], (0, 0), prefix=" ")

        tail = tree.Name(namespace_parts[-1], (0, 0), prefix=" ")

        if alias:
            tail = tree.PythonNode(
                "import_as_name",
                [tail, tree.Keyword("as", (0, 0), prefix=" "), alias])

        return tree.ImportFrom([
            tree.Keyword("from", (0, 0), prefix=prefix),
            base,
            tree.Keyword("import", (0, 0), prefix=" "),
            tail,
        ])
Beispiel #11
0
    def modify_with_existing(  # pylint: disable=arguments-differ
            cls, graph, data, append=False):
        """Add `data` to a parso node `graph`.

        Reference:
            https://github.com/nerdvegas/rez/wiki/Package-Definition-Guide#help

        Args:
            graph (:class:`parso.python.Tree.PythonBaseNode`):
                Some node that may assignment an attribute called
                "tests". If "tests" assignment exists, it gets
                overwritten. If no assignment exists then a new
                assignment is appended to the end of the file.
            data (list[list[str]] or str or :class:`parso.python.tree.PythonNode`):
                Any values that'd typically define a Rez "tests"
                attribute. Basically anything is allowed, as long the
                Rez package schema considers it valid.
            append (bool, optional):
                If False, anything in `data` will override the objects
                in `graph` if there are any conflicts between the two.
                If True, the conflicts are ignored and `data` is just
                added to `graph` is if no conflict exists. Default is False.

        Returns:
            str:
                The modified Python source code. It should resemble the
                source code of `graph` plus any serialized `data`.

        """
        assignments = parso_utility.find_assignment_nodes(
            "help", graph) or parso_utility.find_definition_root_nodes(
                "help", graph)

        try:
            assignment = assignments[-1]
        except IndexError:
            assignment = None

        if isinstance(data, tree.BaseNode):
            graph = convention.insert_or_append_raw_node(
                data, graph, assignment, "help")

            return graph.get_code()

        help_data = parso.parse(json.dumps(
            data, cls=encoder.BuiltinEncoder)).children[0]

        if (assignment and not _get_list_root(assignment)
                and isinstance(help_data, tree.String)):
            help_data.prefix = " "
            graph = convention.insert_or_append(help_data, graph, assignment,
                                                "help")

            return graph.get_code()

        if _get_list_root(assignment) and isinstance(help_data, tree.String):
            raise ValueError(
                'You may not add string "{help_data}" '
                'because assignment "{assignment}" is a list.'.format(
                    help_data=help_data.get_code(),
                    assignment=assignment.get_code()))

        help_data = _get_inner_list_entries(help_data)

        entries = []

        if assignment:
            entries = cls._get_entries(assignment)

        if not append:
            entries = cls._resolve_entries(entries, help_data)
        else:
            entries.extend(help_data)

        entries = [
            tree.PythonNode("atom", [
                tree.Operator("[", (0, 0)), entry,
                tree.Operator("]", (0, 0))
            ]) for entry in entries
        ]
        entries = _separate_with_commas(entries)

        node = tree.PythonNode(
            "atom",
            [tree.Operator("[", (0, 0))] + entries +
            [tree.Operator(",", (0, 0)),
             tree.Operator("]", (0, 0))],
        )

        node = _apply_formatting(node)

        graph = convention.insert_or_append(node, graph, assignment, "help")

        return graph.get_code()