Basics

RedBaron is very simple to use, you just need to import it and feed him with a string:

from redbaron import RedBaron

red = RedBaron("print('hello world!')")

But what you should be really doing is using RedBaron directly into a shell (I recommend IPython but bpython is cool too), it has been thought for it, like BeautifulSoup.

In [1]: from redbaron import RedBaron

In [2]: red = RedBaron("hello = 'Hello World!'\nprint(hello)")

In [3]: red
Out[3]: 
0   hello = 'Hello World!'
1   print(hello)

As you can see, when displayed in a shell, a RedBaron instance renders to the actual content so you easily see what you are doing when playing interactively with it (just like a BeautifulSoup instance).

There are 2 families of Node in RedBaron: NodeList and standalone Node. Since a Python program is a list of operations, RedBaron will always be a list. This is why when displayed you see integers on the left, those are the index in the list of the nodes of the right, so as expected:

In [4]: red[1]
Out[4]: print(hello)

You get the print Node that was located at 2. As you can see, here we are on a standalone Node, so we don’t get the list of indexes of the left.

.help()

Another useful function is .help(). It displays the RedBaron nodes tree helping you understand how is it composed and how you can use it:

In [5]: red[0]
Out[5]: hello = 'Hello World!'

In [6]: red[0].help()
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode()
      # identifiers: name, name_, namenode
      value='hello'
  annotation ->
    None
  value ->
    StringNode()
      # identifiers: string, string_, stringnode
      value="'Hello World!'"

Here, as you can see, hello = 'Hello World!' is an AssignmentNode and it has 2 attributes: target and value. Those 2 attributes are 2 other nodes, a NameNode for the variable hello and a StringNode for the string. Those 2 nodes each have one attribute value that is their content.

One rule with Baron: every node has a value attribute that contains its value (in case of a node with multiple data, value points to the most obvious one, for example, in a function definition it’s the body of the function). The only exceptions are nodes where it doesn’t make any sense, for example a PassNode (representing the keyword pass) simply doesn’t contain anything.

Like the repr, .help() has also a display showing index number when called on a NodeList:

In [7]: red.help()
0 -----------------------------------------------------
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode()
      # identifiers: name, name_, namenode
      value='hello'
  annotation ->
    None
  value ->
    StringNode()
      # identifiers: string, string_, stringnode
      value="'Hello World!'"
1 -----------------------------------------------------
EndlNode()
  # identifiers: endl, endl_, endlnode
  value='\n'
  indent=''
2 -----------------------------------------------------
PrintNode()
  # identifiers: print, print_, printnode
  destination ->
    None
  value ->
    * AssociativeParenthesisNode()
        # identifiers: associative_parenthesis, associative_parenthesis_, associativeparenthesis, associativeparenthesisnode
        value ->
          NameNode() ...

The best way to understand how .help() works is to remember that RedBaron is mapping from Baron FST which is JSON. This means that RedBaron node can be composed of either: string, bool, numbers, list or other nodes and the key are always string.

helpers

Some nodes come with helpers method, .help() displays them when they are present:

In [8]: red = RedBaron("import a, b, c as d")

In [9]: red.help(deep=1)
0 -----------------------------------------------------
ImportNode()
  # identifiers: import, import_, importnode
  # helpers: modules, names
  value ->
    * DottedAsNameNode() ...
    * DottedAsNameNode() ...
    * DottedAsNameNode() ...

You can read their documentation using the ? magic of ipython:

In [10]: print(red[0].names.__doc__)  # you can do "red[0].names?" in IPython shell
return a list of string of new names inserted in the python context

In [11]: red[0].names()
Out[11]: ['a', 'b', 'd']

In [12]: print(red[0].modules.__doc__)
return a list of string of modules imported

In [13]: red[0].modules()
Out[13]: ['a', 'b', 'c']

If you come with cool helpers, don’t hesitate to propose them in a pull request!

deep

.help() accept a deep argument on how far in the tree it should show the .help() of subnode. By default its value is 2. You can pass the value True if you want to display the whole tree.

In [14]: red = RedBaron("a = b if c else d")

In [15]: red.help()
0 -----------------------------------------------------
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode()
      # identifiers: name, name_, namenode
      value='a'
  annotation ->
    None
  value ->
    TernaryOperatorNode()
      # identifiers: ternary_operator, ternary_operator_, ternaryoperator, ternaryoperatornode
      first ->
        NameNode() ...
      value ->
        NameNode() ...
      second ->
        NameNode() ...

In [16]: red.help(0)
0 -----------------------------------------------------
AssignmentNode() ...

In [17]: red.help(deep=1)  # you can name the argument too
0 -----------------------------------------------------
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode() ...
  annotation ->
    None
  value ->
    TernaryOperatorNode() ...

In [18]: red.help(True)
0 -----------------------------------------------------
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode()
      # identifiers: name, name_, namenode
      value='a'
  annotation ->
    None
  value ->
    TernaryOperatorNode()
      # identifiers: ternary_operator, ternary_operator_, ternaryoperator, ternaryoperatornode
      first ->
        NameNode()
          # identifiers: name, name_, namenode
          value='b'
      value ->
        NameNode()
          # identifiers: name, name_, namenode
          value='c'
      second ->
        NameNode()
          # identifiers: name, name_, namenode
          value='d'

with_formatting

.help() accepts the option with_formatting that is set at False by default. If set at True it will also display the attributes responsible for holding the formatting of the node (they are always node list):

In [19]: red.help(with_formatting=True)
0 -----------------------------------------------------
AssignmentNode()
  # identifiers: assign, assignment, assignment_, assignmentnode
  operator=''
  target ->
    NameNode()
      # identifiers: name, name_, namenode
      value='a'
  annotation ->
    None
  value ->
    TernaryOperatorNode()
      # identifiers: ternary_operator, ternary_operator_, ternaryoperator, ternaryoperatornode
      first ->
        NameNode() ...
      value ->
        NameNode() ...
      second ->
        NameNode() ...
      first_formatting ->
        * SpaceNode() ...
      second_formatting ->
        * SpaceNode() ...
      third_formatting ->
        * SpaceNode() ...
      fourth_formatting ->
        * SpaceNode() ...
  annotation_first_formatting ->
  annotation_second_formatting ->
  first_formatting ->
    * SpaceNode()
        # identifiers: space, space_, spacenode
        value=' '
  second_formatting ->
    * SpaceNode()
        # identifiers: space, space_, spacenode
        value=' '

Those attributes are always surrounding syntax element of Python like [](),.{} or keywords. You should, normally, not have a lot of reasons to play with them. You can find a detailed version of each nodes here: Nodes References Page.

nodes structure

Nodes can have 3 kind of attributes (which can be accessed like normal object attributes):

  • data attributes, which are nearly always strings. They are shown with a = in .help(). .value here for example.
In [20]: red = RedBaron("variable")

In [21]: red[0].help()
NameNode()
  # identifiers: name, name_, namenode
  value='variable'

In [22]: red[0].value
Out[22]: 'variable'
  • node attributes, which are other nodes. They are shown with a -> followed by the name of the other node at the next line in .help(). .target and .value here for example.
In [23]: red = RedBaron("a = 1")

In [24]: red[0].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 [25]: red[0].target.help()
NameNode()
  # identifiers: name, name_, namenode
  value='a'
  • nodelist attributes, which are lists of other nodes. They are shown with a -> followed by a series of names of the other nodes starting with a * for every item of the list. .value here for example:
In [26]: red = RedBaron("[1, 2, 3]")

In [27]: red[0].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 [28]: red[0].value[0].help()
IntNode()
  # identifiers: int, int_, intnode
  value='1'

.dumps(), transform the tree into source code

To transform a RedBaron tree back into source code, use the .dumps() method. This will transform the current selection back into code.

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

In [30]: red.dumps()
Out[30]: 'a = 1'

In [31]: red[0].target.dumps()
Out[31]: 'a'

.fst(), transform the RedBaron tree into Baron FST

To transform a RedBaron tree into Baron Full Syntax Tree, just use the .fst() method. This will transform the current selection into FST.

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

In [33]: red.fst()
Out[33]: 
[{'annotation': {},
  'annotation_first_formatting': [],
  'annotation_second_formatting': [],
  'first_formatting': [{'type': 'space', 'value': ' '}],
  'operator': '',
  'second_formatting': [{'type': 'space', 'value': ' '}],
  'target': {'type': 'name', 'value': 'a'},
  'type': 'assignment',
  'value': {'section': 'number', 'type': 'int', 'value': '1'}}]

In [34]: red[0].target.fst()
Out[34]: {'type': 'name', 'value': 'a'}

While I don’t see a lot of occasions where you might need this, this will allow you to better understand how Baron and RedBaron are working.

.copy()

If you want to copy a RedBaron node you can use the .copy() method this way:

In [35]: red = RedBaron("a = b")

In [36]: red[0].target.copy()
Out[36]: a