Esempio n. 1
0
def find_definition_root_nodes(attribute, graph, inclusive=False):
    """Find the root of a function definition.

    If the function has decorator(s) above it, return the first node
    that defines all of them.

    Args:
        attribute (str):
            The name of a Python attribute. e.g. "tests", "help", etc.
        graph (:class:`parso.python.tree.PythonBaseNode`):
            The base node to check from. Usually this represents a Python module.
        inclusive (bool, optional):
            If True, include `graph` as the first item to be iterated
            over. If False, only iterate over `graph`'s children,
            recursively. Default is False.

    """
    items = node_seek.iter_nested_children(graph)

    if inclusive:
        items = itertools.chain([graph], items)

    nodes = []

    for child in items:
        if isinstance(child, tree.Function) and child.name == attribute:
            nodes.append(child)
        elif _is_decorator_wrapper(child) and find_definition_nodes(
                attribute, node_seek.iter_nested_children(child)):
            nodes.append(child)

    return nodes
def _flatten_node(node):
    """Remove all prefix / whitespace information from a parso node + its children.

    :class:`TestsAdapter` is responsible for setting + appending
    whitespace for all of the parso nodes that eventually get written
    to disk. But existing data may already have whitespace prefix data.
    So this function sets that all back to zero, to make it easier to
    append a prefix to existing nodes.

    Args:
        node (:class:`parso.python.tree.PythonBaseNode`):
            A parso object that presumably has child nodes. These
            children may have whitespace - every child's whitespace will
            be removed.

    Returns:
        :class:`parso.python.tree.PythonBaseNode`:
            A copy of `node` whose children have no defined whitespace.

    """
    node = copy.deepcopy(node)

    if hasattr(node, "prefix"):
        node.prefix = ""

    for child in node_seek.iter_nested_children(node):
        if hasattr(child, "prefix"):
            child.prefix = ""

            break

    return node
Esempio n. 3
0
def _kill_suffix(node):
    """Remove any trailing newlines from `node`."""
    for child in reversed(list(node_seek.iter_nested_children(node))):
        if isinstance(child, tree.Newline):
            child.value = ""

            return
Esempio n. 4
0
def find_assignment_nodes(attribute, graph, inclusive=False):
    """Get a parso node each time a certain Python attribute is declared and given a value.

    Args:
        attribute (str):
            The name of a Python attribute. e.g. "tests", "help", etc.
        graph (:class:`parso.python.tree.PythonBaseNode`):
            The root node that will be checked. Usually, this is a
            :class:`parso.python.tree.Module` but it doesn't have to be.
        inclusive (bool, optional):
            If True, include `graph` as the first item to be iterated
            over. If False, only iterate over `graph`'s children,
            recursively. Default is False.

    Returns:
        set[:class:`parso.python.tree.PythonBaseNode`]: The found assignments, if any.

    """
    nodes = []

    items = node_seek.iter_nested_children(graph)

    if inclusive:
        items = itertools.chain([graph], items)

    for child in items:
        if isinstance(child, tree.ExprStmt):
            for name in child.get_defined_names():
                if name.value == attribute:
                    column = name.start_pos[1]

                    if column == 0:
                        nodes.append(child)

    return nodes
def _get_tail_children(nodes):
    """Get the tail namespaces of a from-import.

    In other words, from "from foo import bar, thing", get the "bar" and "thing".

    Args:
        nodes (iter[:class:`parso.python.tree.PythonBaseNode`]):
            All parso objects that represent a Python from-import's
            tail. It may contain commas and other syntax-specific parts.
            But this will all be filtered out before being returned.

    Returns:
        list[:class:`parso.python.tree.PythonBaseNode`]: The found tail nodes.

    """
    children = []

    for node in nodes:
        if isinstance(node, tree.Name):
            children.append(node)
        else:
            for child in node_seek.iter_nested_children(node):
                if isinstance(child, tree.Name) and not _is_alias_name(child):
                    children.append(child)

    return children
Esempio n. 6
0
def _get_inner_list_entries(node):
    """Find the literal "requires" entries of a node.

    Args:
        node (:class:`parso.python.tree.PythonBaseNode`):
            A parso object that (we assume) has 0+ child list parso
            objects. Each child found is returned.

    Returns:
        list[:class:`parso.python.tree.PythonNode`]:
            The found key / value "requires" attribute entries, if any.

    """
    entries = []

    for child in node_seek.iter_nested_children(node):
        if not _is_list_root_definition(child):
            continue

        if any(child_ for child_ in child.children
               if isinstance(child_, tree.PythonNode)):
            continue

        entries.append(child)

    return entries
Esempio n. 7
0
def _get_entries(node):
    """Get the existing "requires" assignment parso nodes.

    Args:
        node (:class:`parso.python.tree.ExprStmt`):
            A parso assignment node that defines "requires".
            e.g. Imagine "requires = ["foo-1"]", but as a single Python node.

    Returns:
        list[:class:`parso.python.tree.String`]: The existing requirements in `node`.

    """
    root_entries = _get_inner_list_entries(node)

    if not root_entries:
        return []

    entries = []

    for item in root_entries:
        for child in node_seek.iter_nested_children(item):
            if not isinstance(child, tree.Operator):
                entries.append(child)

    return entries
    def _get_inner_children(node):
        children = []

        for child in node_seek.iter_nested_children(node):
            if not _is_alias_name(child):
                children.append(child)

        return children
Esempio n. 9
0
def get_imports(graph, partial=False, namespaces=frozenset(), aliases=False):
    """Find every import in `graph`.

    Args:
        graph (:class:`parso.python.tree.Module`):
            Some parso node that may have Python imports iside of it.
        partial (bool, optional):
            If True, allow namespace replacement even if the user
            doesn't provide 100% of the namespace. If False, they must
            describe the entire import namespace before it can be
            renamed. Default is False.
        namespaces (set[str], optional):
            If `partial` is False, these dot-separated Python import
            namespaces are used to figure out if it's okay to replace an
            import or if any part of an import statement is missing. If
            no namespaces are given then `partial` must be set to True.
            Default: set().
        aliases (bool, optional):
            If True and replacing a namespace would cause Python
            statements to fail, auto-add an import alias to ensure
            backwards compatibility If False, don't add aliases. Default
            is False.

    Returns:
        :class:`.BaseAdapter`:
            Every import in `graph`, returned as a class that can easily
            query its data or replace it.

    """
    imports = set()
    processed = set(
    )  # This variable makes sure nodes are only ever parsed once

    # We reverse the nested children because parso nests its nodes
    # in ways that makes it hard to choose the correct adapter class
    # without starting from the bottom-level nodes and working our way
    # up each node's list of parents.
    #
    for child in reversed(list(node_seek.iter_nested_children(graph))):
        parents = node_seek.iter_parents(child)

        if any(parent for parent in parents if parent in processed):
            # Make sure that if a parent of `child` was parsed to not try to parse `child`
            continue

        adapter = import_registry.get_import_data(child,
                                                  partial=partial,
                                                  namespaces=namespaces,
                                                  aliases=aliases)

        if adapter:
            imports.add(adapter)

        processed.add(child)

    return imports
Esempio n. 10
0
    def is_valid(node):
        """bool: Check if `node` is compatible with this class."""
        if not isinstance(node, tree.ImportName):
            return False

        for child in node_seek.iter_nested_children(node):
            if isinstance(child, tree.Operator) and child.value == ",":
                return False

        return True
Esempio n. 11
0
 def test_nested(self):
     """Return all children of a node."""
     graph = parso.parse("something = 8")
     self.assertEqual(
         [
             graph.children[0],
             graph.children[0].children[0],
             graph.children[0].children[1],
             graph.children[0].children[2],
             graph.children[1],
         ],
         list(node_seek.iter_nested_children(graph)),
     )
def _replace_namespace(nodes, old_parts, new_parts, partial=False):
    """Change `nodes` into an import resembling the Python namespace defined by `new_parts`.

    If there's no common namespace between `nodes` and `old_parts` then
    this function does nothing.

    Args:
        node (:class:`parso.python.tree.ImportFrom`):
            A parso object that represents a Python import.
        old_parts (list[str]):
            The namespace that is expected to be all or part of the
            namespace that `node` defines.
        new_parts (list[str]):
            The namespace to replace `node` with. e.g. ["foo", "bar"].

    """

    def _is_fully_defined_by(names, parts):
        for index, name in enumerate(names):
            try:
                part = parts[index]
            except IndexError:
                return False

            if part != name:
                return False

        return True

    names = [
        child
        for node in nodes
        for child in itertools.chain([node], node_seek.iter_nested_children(node))
        if isinstance(child, tree.Name)
    ]
    prefix = node_seek.get_node_with_first_prefix(names[0]).prefix
    start, end = import_helper.get_replacement_indices(names, old_parts)

    if start == -1 or end == -1:
        # There's nothing to replace, in this case
        return

    if not partial and not _is_fully_defined_by(
        [name.value for name in names], old_parts
    ):
        return

    new_nodes = import_helper.make_replacement_nodes(new_parts, prefix)
    names[0].parent.children[start : end + 1] = new_nodes
Esempio n. 13
0
def _get_str_root(node):
    """Find the first node that could be considered the root of a "help" attribute.

    Args:
        node (:class:`parso.python.tree.ExprStmt`):
            A node that defines a "help" attribute and should contain a
            string help URL or file path.

    Returns:
        :class:`parso.python.tree.PythonNode` or NoneType:
            The URL / file-path node, if any.

    """
    for child in node_seek.iter_nested_children(node):
        if isinstance(child, tree.String):
            return child

    return None
Esempio n. 14
0
def _get_list_root(node):
    """Find the first node that could be considered the root of a "help" attribute.

    Args:
        node (:class:`parso.python.tree.ExprStmt`):
            A node that defines a "help" attribute and should contain a
            list of list of strings.

    Returns:
        :class:`parso.python.tree.PythonNode` or NoneType:
            The top of a list of list of strings, if any.

    """
    for child in node_seek.iter_nested_children(node):
        if _is_list_root_definition(child):
            return child

    return None
Esempio n. 15
0
def _apply_formatting(node):
    """Get a copy of `node` that has "human-readable" whitespace.

    Args:
        node (:class:`parso.python.Tree.PythonBaseNode`):
            A parso object that represents the top-level "list of list
            of strings" that defines a Rez help attribute.

    Returns:
        :class:`parso.python.Tree.PythonBaseNode`: The copy of `node` that has nice newlines.

    """
    def _needs_space(node):
        if isinstance(node, tree.Operator) and node.value in ("[", "]"):
            return False

        if isinstance(node, tree.String):
            return False

        return True  # pragma: no cover

    def _iter_inner_entries(node):
        for child in node_seek.iter_nested_children(node):
            if isinstance(child, tree.PythonNode) and child.type == "atom":
                yield child

    node = copy.deepcopy(node)

    for child in node_seek.iter_nested_children(node):
        if isinstance(child, tree.Operator) and child.value == ",":
            child.prefix = ""
        elif hasattr(child, "prefix") and _needs_space(child):
            child.prefix = " "  # pragma: no cover

    for child in _iter_inner_entries(node):
        opening_brace = child.children[0]
        opening_brace.prefix = "\n    "

    node.children[-1].prefix = "\n"

    return node
def _get_dict_maker_root(node):
    """Find the inner parso PythonNode that defines a Python dict.

    Args:
        node (:class:`parso.python.tree.PythonBaseNode`):
            A parso node that may or may not have a "dictorsetmaker"
            node as one of its children. Usually, this will actually be
            an "atom" type :class:`parso.python.tree.PythonNode`

    Returns:
        :class:`parso.python.tree.PythonNode` or NoneType:
            Get the inner dict, if any exists in `node`.

    """
    for child in node_seek.iter_nested_children(node):
        if isinstance(child,
                      tree.PythonNode) and child.type == "dictorsetmaker":
            # If this happens, it means that `node` is a non-empty dict
            return child

    return None
def _is_empty_dict(node):
    """Check if the given parso `node` represents an initialized-but-empty assignment.

    Args:
        node (:class:`parso.python.tree.ExprStmt`): The assignment node to check.

    Returns:
        bool: Return False if `node` is a non-empty dict. Otherwise, return True.

    """
    def _is_close(node):
        if not isinstance(node, tree.Operator):
            return False  # pragma: no cover

        return node.value == ")"

    def _is_open(node):
        if not isinstance(node, tree.Operator):
            return False  # pragma: no cover

        return node.value == "("

    for child in node_seek.iter_nested_children(node):
        if (isinstance(child, tree.PythonNode) and child.type == "atom"
                and len(child.children) == 2 and child.children[0].value == "{"
                and child.children[-1].value == "}"):
            # If this happens, it means that `node` is an empty dict
            return True

        if isinstance(child, tree.Name) and child.value == "dict":
            # Check if `child` is `dict()`
            maybe_open = child.get_next_leaf()

            if _is_open(maybe_open) and _is_close(maybe_open.get_next_leaf()):
                return True

    return False  # pragma: no cover
Esempio n. 18
0
 def test_empty(self):
     """Return no children because the node has no children."""
     graph = parso.parse("something = 8")
     name_node = graph.get_first_leaf()
     self.assertEqual([], list(node_seek.iter_nested_children(name_node)))
Esempio n. 19
0
 def _iter_inner_entries(node):
     for child in node_seek.iter_nested_children(node):
         if isinstance(child, tree.PythonNode) and child.type == "atom":
             yield child