Other — RedBaron 0.8 documentation

# Other

List of other features of RedBaron.

## .parent

Every node and node list have a `.parent` attribute that points to the parent node or node list. If the node doesn’t have a parent node (for example the node list returned when constructing a new instance using the `RedBaron` class), the `parent` attribute is set at `None`. A new node or node list created using `.copy()` always have its `parent` attribute set at `None`.

The attribute on which the node is assigned on the parent node is store in the `on_attribute` attribute. `on_attribute` is set at `"root"` if the parent is a RedBaron instance.

    In [1]: red = RedBaron("a = 1 + caramba")

    In [2]: red.help()
    0 -----------------------------------------------------
    AssignmentNode()
      # identifiers: assign, assignment, assignment_, assignmentnode
      operator=''
      target ->
        NameNode()
          # identifiers: name, name_, namenode
          value='a'
      annotation ->
        None
      value ->
        BinaryOperatorNode()
          # identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
          value='+'
          first ->
            IntNode() ...
          second ->
            NameNode() ...

    In [3]: red.parent

    In [4]: red.on_attribute

    In [5]: red[0].parent
    Out[5]: 0   a = 1 + caramba

    In [6]: red[0].on_attribute
    Out[6]: 'root'

    In [7]: red[0].target.parent
    Out[7]: a = 1 + caramba

    In [8]: red[0].target.on_attribute
    Out[8]: 'target'

    In [9]: red[0].value.parent
    Out[9]: a = 1 + caramba

    In [10]: red[0].value.on_attribute
    Out[10]: 'value'

    In [11]: red[0].value.first.parent
    Out[11]: 1 + caramba

    In [12]: red[0].value.first.on_attribute
    Out[12]: 'first'

## .parent_find()

A helper method that allow you to do the equivalent of the `.find()` method but in the chain of the parents of the node. This is the equivalent of doing: `while node has a parent: if node.parent match query: return node.parent, else: node = node.parent`. It returns `None` if no parent match the query.

    In [13]: red = RedBaron("def a():\n    with b:\n        def c():\n            pass")

    In [14]: red.help()
    0 -----------------------------------------------------
    DefNode()
      # identifiers: def, def_, defnode, funcdef, funcdef_
      # default test value: name
      async=False
      name='a'
      return_annotation ->
        None
      decorators ->
      arguments ->
      value ->
        * WithNode()
            # identifiers: with, with_, withnode
            async=False
            contexts ->
              * WithContextItemNode() ...
            value ->
              * DefNode() ...

    In [15]: r = red.pass_

    In [16]: r
    Out[16]: pass

    In [17]: r.parent
    Out[17]:
    def c():
                pass

    In [18]: r.parent_find('def')
    Out[18]:
    def c():
                pass

    In [19]: r.parent_find('def', name='a')
    Out[19]:
    def a():
        with b:
            def c():
                pass

    In [20]: r.parent_find('def', name='dont_exist')

## .next .previous .next_recursive .previous_recursive .next_generator() .previous_generator()

In a similar fashion, nodes have a `.next` and `.previous` attributes that point to the next or previous node if the node is located in a node list. They are set at `None` if there is not adjacent node or if the node is not in a node list. A node list will never have a `.next` or `.previous` node, so those attributes will always be set at `None`.

Nodes also have a `.next_generator()` and `.previous_generator()` if you want to iterate on the neighbours of the node.

Nodes have also a `.next_recursive` and `.previous_recursive` attribute. It is similar to the non recursive function but differ in the fact that, when using `.next_recursive` on a node at the end of the list, it points to the first adjacent node that exist in the parent hierarchy.

    In [21]: red = RedBaron("[1, 2, 3]; a = 1")

    In [22]: list = red[0]

    In [23]: print(list.next)
    ;

    In [24]: list.help()
    ListNode()
      # identifiers: list, list_, listnode
      value ->
        * IntNode()
            # identifiers: int, int_, intnode
            value='1'
        * IntNode()
            # identifiers: int, int_, intnode
            value='2'
        * IntNode()
            # identifiers: int, int_, intnode
            value='3'

    In [25]: assign = red[2]

    In [26]: assign.help()
    AssignmentNode()
      # identifiers: assign, assignment, assignment_, assignmentnode
      operator=''
      target ->
        NameNode()
          # identifiers: name, name_, namenode
          value='a'
      annotation ->
        None
      value ->
        IntNode()
          # identifiers: int, int_, intnode
          value='1'

    In [27]: list.value[2].help(deep=1)
    IntNode()
      # identifiers: int, int_, intnode
      value='3'

## .next_intuitive/.previous_intuitive

Due to its tree nature, navigating in the FST might not behave as the user expect it. For example: doing a `.next` on a `TryNode` will not return the first `ExceptNode` (or `FinallyNode`) but will return the node after the try-excepts-else-finally node because it is a full node in itself in the FST.

See for yourself:

    In [28]: red = RedBaron("try:\n    pass\nexcept:\n    pass\nafter")

    In [29]: red.try_
    Out[29]:
    try:
        pass
    except:
        pass

    In [30]: red.try_.next
    Out[30]: after

    In [31]: red.help()
    0 -----------------------------------------------------
    TryNode()
      # identifiers: try, try_, trynode
      else ->
        None
      finally ->
        None
      value ->
        * PassNode()
            # identifiers: pass, pass_, passnode
      excepts ->
        * ExceptNode()
            # identifiers: except, except_, exceptnode
            delimiter=''
            exception ->
              None
            target ->
              None
            value ->
              * PassNode() ...
    1 -----------------------------------------------------
    NameNode()
      # identifiers: name, name_, namenode
      value='after'

To solve this issue `.next_intuitive` and `.previous_intuitive` have been introduced:

    In [32]: red
    Out[32]:
    0   try:
            pass
        except:
            pass

    1   after

    In [33]: red.try_.next_intuitive
    Out[33]:
    except:
        pass

    In [34]: red.try_.next_intuitive.next_intuitive
    Out[34]: after

This also applies to `IfNode`, `ElifNode`, `ElseNode`, `ForNode` and `WhileNode` (both of the last one can have an else statement). This also works coming from nodes outsides of those previous nodes.

For `IfNode`, `ElifNode` and `ElseNode` inside an `IfelseblockNode`:

    In [35]: red = RedBaron("before\nif a:\n    pass\nelif b:\n    pass\nelse:\n    pass\nafter")

    In [36]: red
    Out[36]:
    0   before
    1   if a:
            pass
        elif b:
            pass
        else:
            pass

    2   after

    In [37]: red[1].help()
    IfelseblockNode()
      # identifiers: ifelseblock, ifelseblock_, ifelseblocknode
      value ->
        * IfNode()
            # identifiers: if, if_, ifnode
            test ->
              NameNode() ...
            value ->
              * PassNode() ...
        * ElifNode()
            # identifiers: elif, elif_, elifnode
            test ->
              NameNode() ...
            value ->
              * PassNode() ...
        * ElseNode()
            # identifiers: else, else_, elsenode
            value ->
              * PassNode() ...

    In [38]: red[1]
    Out[38]:
    if a:
        pass
    elif b:
        pass
    else:
        pass

    In [39]: red.if_.next
    Out[39]:
    elif b:
        pass

    In [40]: red.if_.next_intuitive
    Out[40]:
    elif b:
        pass

    In [41]: red.if_.next_intuitive.next_intuitive
    Out[41]:
    else:
        pass

    In [42]: red.if_.next_intuitive.next_intuitive.next_intuitive
    Out[42]: after

    In [43]: red.if_.next_intuitive.next_intuitive.next_intuitive.next_intuitive

Warning

There is a subtlety: `IfelseblockNode` is unaffected by this behavior: you have to use `next_intuitive` or `previous_intuitive` on `IfNode`, `ElifNode` and `ElseNode` inside IfelseblockNode.

But, if you do a `next_intuitive` or `previous_intuitive` or a node around `IfelseblockNode` it will jump to the first or last node inside the `IfelseblockNode`.

See this example

    In [44]: red = RedBaron("before\nif a:\n    pass\nelif b:\n    pass\nelse:\n    pass\nafter")

    In [45]: red[1].ifelseblock.next_intuitive  # similar to .next
    Out[45]: after

    In [46]: red[1].ifelseblock.next.previous  # this is the IfelseblockNode
    Out[46]:
    if a:
        pass
    elif b:
        pass
    else:
        pass

    In [47]: red[1].ifelseblock.next.previous_intuitive  # this is the ElseNode
    Out[47]:
    else:
        pass

    In [48]: red[1].ifelseblock.previous.next  # this is the IfelseblockNode
    Out[48]:
    if a:
        pass
    elif b:
        pass
    else:
        pass

    In [49]: red[1].ifelseblock.previous.next_intuitive  # this is the IfNode
    Out[49]:
    if a:
        pass

For `ForNode`:

    In [50]: red = RedBaron("for a in b:\n    pass\nelse:\n    pass\nafter")

    In [51]: red
    Out[51]:
    0   for a in b:
            pass
        else:
            pass

    1   after

    In [52]: red[0].help()
    ForNode()
      # identifiers: for, for_, fornode
      async=False
      iterator ->
        NameNode()
          # identifiers: name, name_, namenode
          value='a'
      target ->
        NameNode()
          # identifiers: name, name_, namenode
          value='b'
      else ->
        ElseNode()
          # identifiers: else, else_, elsenode
          value ->
            * PassNode() ...
      value ->
        * PassNode()
            # identifiers: pass, pass_, passnode

    In [53]: red.for_
    Out[53]:
    for a in b:
        pass
    else:
        pass

    In [54]: red.for_.next
    Out[54]: after

    In [55]: red.for_.next_intuitive
    Out[55]:
    else:
        pass

    In [56]: red.for_.next_intuitive.next_intuitive
    Out[56]: after

For `WhileNode`:

    In [57]: red = RedBaron("while a:\n    pass\nelse:\n    pass\nafter")

    In [58]: red
    Out[58]:
    0   while a:
            pass
        else:
            pass

    1   after

    In [59]: red[0].help()
    WhileNode()
      # identifiers: while, while_, whilenode
      test ->
        NameNode()
          # identifiers: name, name_, namenode
          value='a'
      else ->
        ElseNode()
          # identifiers: else, else_, elsenode
          value ->
            * PassNode() ...
      value ->
        * PassNode()
            # identifiers: pass, pass_, passnode

    In [60]: red.while_
    Out[60]:
    while a:
        pass
    else:
        pass

    In [61]: red.while_.next
    Out[61]: after

    In [62]: red.while_.next_intuitive
    Out[62]:
    else:
        pass

    In [63]: red.while_.next_intuitive.next_intuitive
    Out[63]: after

## .root

Every node have the `.root` attribute (property) that returns the root node in which this node is located:

    In [64]: red = RedBaron("def a(): return 42")

    In [65]: red.int_
    Out[65]: 42

    In [66]: assert red.int_.root is red

## .index_on_parent

Every node have the `.index_on_parent` attribute (property) that returns the index at which this node is store in its parent node list. If the node isn’t stored in a node list, it returns `None`. If the node is stored in a proxy list (Proxy List), it’s the index in the proxy list that is returned. to get the unproxified index use .index_on_parent_raw.

    In [67]: red = RedBaron("a = [1, 2, 3]")

    In [68]: red[0].value.value
    Out[68]:
    0   1
    1   2
    2   3

    In [69]: red[0].value.value[2]
    Out[69]: 3

    In [70]: red[0].value.value[2].index_on_parent
    Out[70]: 2

    In [71]: red[0].value
    Out[71]: [1, 2, 3]

    In [72]: red[0].value.index_on_parent

## .index_on_parent_raw

Same as .index_on_parent except that it always return the unproxified whether the node is stored in a proxy list or not.

    In [73]: red = RedBaron("a = [1, 2, 3]")

    In [74]: red[0].value.value.node_list
    Out[74]:
    0   1
    1   ,
    2   2
    3   ,
    4   3

    In [75]: red[0].value.value.node_list[2]
    Out[75]: 2

    In [76]: red[0].value.value.node_list[2].index_on_parent_raw
    Out[76]: 2

## .filtered()

Node list comes with a small helper function: `.filtered()` that returns a tuple containing the “significant” node (nodes that aren’t comma node, dot node, space node or endl node).

    In [77]: red = RedBaron("[1, 2, 3]")

    In [78]: red[0].value
    Out[78]:
    0   1
    1   2
    2   3

    In [79]: red[0].value.filtered()
    Out[79]: (1, 2, 3)

Note: the fact that it’s a tuple that is returned will probably evolve in the future into a node list proxy or something like that, I just don’t have the time to do something better right now.

## .indentation

Every node has the property `.indentation` that will return the indentation level of the node:

    In [80]: red = RedBaron("while a:\n    pass")

    In [81]: red[0].indentation
    Out[81]: ''

    In [82]: red[0].test.indentation
    Out[82]: ''

    In [83]: red.pass_.indentation
    Out[83]: '    '

    In [84]: red = RedBaron("while a: pass")

    In [85]: red.pass_.indentation
    Out[85]: ''

## .increase_indentation() and .decrease_indentation()

Those 2 methods allow you to change the indentation of a part of the tree. They expect the number of spaces to add or to remove as first argument.

    In [86]: red = RedBaron("def a():\n    if plop:\n        pass")

    In [87]: red
    Out[87]:
    0   def a():
            if plop:
                pass

    In [88]: red[0].value.increase_indentation(15)

    In [89]: red
    Out[89]:
    0   def a():
                           if plop:
                               pass

    In [90]: red[0].value.decrease_indentation(15)

    In [91]: red
    Out[91]:
    0   def a():
            if plop:
                pass

## .to_python()

Warning

Since RedBaron calls ast.literal_eval it can only parse the python code parsed by the python version you are using.

For example if you are using a python version inferior to 3.6, to_python will crash on 100_000 because it is only supported since python 3.6

This method safely evaluate the current selected nodes. It wraps ast.literal_eval, therefor, and for security reasons, it only works on a subset of python: numbers, strings, lists, dicts, tuples, boolean and `None`. Of course, using this method on a list/dict/tuple containing values that aren’t listed here will raise a `ValueError`.

    In [92]: RedBaron("42")[0].value  # string
    Out[92]: '42'

    In [93]: RedBaron("42")[0].to_python()  # python int
    Out[93]: 42

    In [94]: RedBaron("'a' 'b'")[0].dumps()
    Out[94]: "'a' 'b'"

    In [95]: RedBaron("'a' 'b'")[0].to_python()
    Out[95]: 'ab'

    In [96]: RedBaron("u'unicode string'")[0].to_python()
    Out[96]: u'unicode string'

    In [97]: RedBaron("[1, 2, 3]")[0].to_python()
    Out[97]: [1, 2, 3]

    In [98]: RedBaron("(1, 2, 3)")[0].to_python()
    Out[98]: (1, 2, 3)

    In [99]: RedBaron("{'foo': 'bar'}")[0].to_python()
    Out[99]: {'foo': 'bar'}

    In [100]: RedBaron("False")[0].to_python()
    Out[100]: False

    In [101]: RedBaron("True")[0].to_python()
    Out[101]: True

    In [102]: print(RedBaron("None")[0].to_python())
    None

## .path()

Every node has a `path()` method that will return a `Path` object to it. Every path object has a `.node` attribute that point to the node and a `.to_baron_path` that returns a Baron Path namedtuple.

    In [103]: red = RedBaron("while a:\n    pass")

    In [104]: red.pass_
    Out[104]: pass

    In [105]: path = red.pass_.path()

    In [106]: path
    Out[106]: <Path(PassNode(pass) @ [0, 'value', 1]) object at 139751515763664>

    In [107]: path.node
    Out[107]: pass

    In [108]: path.to_baron_path()
    Out[108]: [0, 'value', 1]

## Path class

RedBaron provides a Path class that represent a path to a node.

class `redbaron.` `Path`(node)

  Holds the path to a FST node

  Path(node): path coming from the node’s root Path.from_baron_path(node, path): path going down the node following the given path

  Note that the second argument “path” is a baron path, i.e. list of keys that can be given for example by redbaron.Path(node).to_baron_path()

  The second form is useful when converting a path given by baron to a redbaron node

## .map .filter .apply

RedBaron nodes list have 3 helper methods `.map`, `.filter` and `.apply` quite similar to python builtins (except for apply). The main difference is that they return a node list instance instead of a python buildin list.

- `.map` takes a callable (like a lambda or a function) that receive a node as first argument, this callable is applied on every node of the node list and a node list containing the return of those applies will be returned.
- `.filter` works like `.map` but instead of returning a node list of the return of the callable, it returns a node list that contains the nodes for which the callable returned `True` (or something considered `True` in python)
- `.apply` works like `.map` but instead of returning the result of the callable, it returns to original node.

    In [109]: red = RedBaron("[1, 2, 3]")

    In [110]: red('int')
    Out[110]:
    0   1
    1   2
    2   3

    In [111]: red('int').map(lambda x: x.to_python() + 1)
    Out[111]:
    0   2
    1   3
    2   4

    In [112]: red('int').filter(lambda x: x.to_python() % 2 == 0)
    Out[112]: 0   2

    In [113]: red = RedBaron("a()\nb()\nc(x=y)")

    In [114]: red('call')
    Out[114]:
    0   ()
    1   ()
    2   (x=y)

    # FIXME
    # red('call').map(lambda x: x.append_value("answer=42"))
    In [115]: red('call')
    Out[115]:
    0   ()
    1   ()
    2   (x=y)

    In [116]: red = RedBaron("a()\nb()\nc(x=y)")

    # FIXME
    # red('call').apply(lambda x: x.append_value("answer=42"))

## .replace()

`.replace()` is a method that allow to replace in place a node by another one. Like every operation of this nature, you can pass a string, a dict, a list of length one or a node instance.

    In [117]: red = RedBaron("a()\nb()\nc(x=y)")

    In [118]: red[2].replace("1 + 2")

    In [119]: red
    Out[119]:
    0   a()
    1   b()
    2   1 + 2

    In [120]: red[-1].replace("plop")

    In [121]: red
    Out[121]:
    0   a()
    1   b()
    2   plop

## .edit()

Helper method that allow to edit the code of the current node into an editor. The result is parsed and replace the code of the current node.

    In [122]: red = RedBaron("def a(): return 42")

    # should be used like this: (I can't execute this code here, obviously)
    # red.return_.edit()

By default, the editor is taken from the variable `EDITOR` in the environment variables. If this variable is not present, nano is used. You can use a different editor this way: `node.edit(editor="vim")`.

## .absolute_bounding_box

The absolute bounding box of a node represents its top-left and bottom-right position relative to the fst’s root node. The position is given as a tuple `(line, column)` with both starting at 1.

    In [123]: red = RedBaron("def a(): return 42")

    In [124]: red.funcdef.value.absolute_bounding_box
    Out[124]: BoundingBox (Position (1, 10), Position (2, 0))

You can also get the bounding box of “string” nodes like the left parenthesis in the example above by giving the attribute’s name to the `get_absolute_bounding_box_of_attribute()` method:

    In [125]: red.funcdef.get_absolute_bounding_box_of_attribute('(')
    Out[125]: BoundingBox (Position (1, 6), Position (1, 6))

This is impossible to do without giving the attribute’s name as an argument since the left parenthesis is not a redbaron Node.

## .bounding_box

Every node has the `bounding_box` property which holds the top-left and bottom-right position of the node. Compared to the `absolute_bounding_box` property, it assumes the node is the root node so the top-left position is always `(1, 1)`.

    In [126]: red = RedBaron("def a(): return 42")

    In [127]: red.funcdef.value.absolute_bounding_box
    Out[127]: BoundingBox (Position (1, 10), Position (2, 0))

    In [128]: red.funcdef.value.bounding_box
    Out[128]: BoundingBox (Position (1, 1), Position (2, 0))

## .find_by_position()

You can find which node is located at a given line and column:

    In [129]: red = RedBaron("def a(): return 42")

    In [130]: red.find_by_position((1, 5))
    Out[130]: def a(): return 42

    In [131]: red.find_by_position((1, 6)) # '(' is not a redbaron node
    Out[131]: def a(): return 42

## .at()

Returns first node at specific line

    In [132]: red = RedBaron("def a():\n return 42")

    In [133]: red.at(1) # Gives DefNode
    Out[133]:
    def a():
     return 42

    In [134]: red.at(2) # Gives ReturnNode
    Out[134]: return 42

## Node.from_fst()

`Node.from_fst()` is a helper class method that takes an FST node and return a RedBaron node instance. Except if you need to go down at a low level or that RedBaron doesn’t provide the helper you need, you shouldn’t use it.

    In [135]: from redbaron import Node

    In [136]: Node.from_fst({"type": "name", "value": "a"})
    Out[136]: a

`Node.from_fst()` takes 2 optional keywords arguments: `parent` and `on_attribute` that should respectively be RedBaron node instance (the parent node) and a string (the attribute of the parent node on which this node is stored). See .parent doc for a better understanding of those 2 parameters.

    In [137]: red = RedBaron("[1,]")

    In [138]: new_name = Node.from_fst({"type": "name", "value": "a"}, parent=red[0], on_attribute="value")

    In [139]: red[0].value.append(new_name)

## NodeList.from_fst()

Similarly to `Node.from_fst()`, `NodeList.from_fst()` is a helper class method that takes an FST node list and return a RedBaron node list instance. Similarly, you probably don’t need to go so low level.

    In [140]: from redbaron import NodeList

    In [141]: NodeList.from_fst([{"type": "name", "value": "a"}, {'first_formatting': [], 'type': 'comma', 'second_formatting': [{'type': 'space', 'value': ' '}]}, {"type": "name", "value": "b"}])
    Out[141]:
    0   a
    1   ,
    2   b

## .insert_before .insert_after

One thing you often wants to do is to insert things just after or before the node you’ve just got via query. Those helpers are here for that:

    In [142]: red = RedBaron("foo = 42\nprint('bar')\n")

    In [143]: red
    Out[143]:
    0   foo = 42
    1   print('bar')

    In [144]: red.print_.insert_before("baz")

    In [145]: red
    Out[145]:
    0   foo = 42
    1   baz
    2   print('bar')

    In [146]: red.print_.insert_after("foobar")

    In [147]: red
    Out[147]:
    0   foo = 42
    1   baz
    2   print('bar')
    3   foobar

Additionally, you can give an optional argument `offset` to insert more than one line after or before:

    In [148]: red = RedBaron("foo = 42\nprint('bar')\n")

    In [149]: red
    Out[149]:
    0   foo = 42
    1   print('bar')

    In [150]: red.print_.insert_before("baz", offset=1)

    In [151]: red
    Out[151]:
    0   baz
    1   foo = 42
    2   print('bar')

    In [152]: red[0].insert_after("foobar", offset=1)

    In [153]: red
    Out[153]:
    0   baz
    1   foo = 42
    2   foobar
    3   print('bar')