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.