Welcome to RedBaron’s documentation!¶
Introduction¶
RedBaron is a python library and tool powerful enough to be used into IPython solely that intent to make the process of writing code that modify source code as easy and as simple as possible. That include writing custom refactoring, generic refactoring, tools, IDE or directly modifying you source code into IPython with a higher and more powerful abstraction than the advanced texts modification tools that you find in advanced text editors and IDE.
RedBaron guaranteed you that it will only modify your code where you ask him
to. To achieve this, it is based on Baron
a lossless AST for
Python that guarantees the operation fst_to_code(code_to_fst(source_code)) == source_code
.
RedBaron API and feel is heavily inspired by BeautifulSoup. It tries to be simple and intuitive and that once you’ve get the basics principles, you are good without reading the doc for 80% of your operations.
A note about the examples¶
This documentation is full of example for nearly everything. But in fact, those aren’t really “example”: those are real life code that are executed at the compilation time of this documentation, this guaranteed the example you see to work exactly the same way for you.
Funny side effect: this make it possible to “break” this documentation.
Installation¶
pip install redbaron[pygments]
Or if you don’t want to have syntax highlight in your shell or don’t need it:
pip install redbaron
Basic usage¶
Simple API: give string, get string back.
In [1]: from redbaron import RedBaron
In [2]: red = RedBaron("some_value = 42")
In [3]: red.dumps() # get code back
Out[3]: 'some_value = 42'
Though to be used in IPython directly:
In [4]: red # direct feedback like BeautifulSoup, "0" here is the index of the node in our source code
Out[4]: 0 some_value = 42
In [5]: red.help() # helper function that describe nodes content so you don't have to read the doc
0 -----------------------------------------------------
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='some_value'
annotation ->
None
value ->
IntNode()
# identifiers: int, int_, intnode
value='42'
Easy nodes modifications, you already know how to code in python, so pass
python code (in a string) on the attribute you want to modify (wonder what
.value
is? look at the output of .help()
in the previous
example):
In [6]: red[0].value = "1 + 4"
In [7]: red
Out[7]: 0 some_value = 1 + 4
Easy queries, just like in BeautifulSoup:
In [8]: red.find("int", value=4)
In [9]: red.find_all("int") # can also be written red("int") like in BeautifulSoup
Out[9]:
0 1
1 4
Queries can be very powerful, you can test each attributes with value/lambda/regex/special syntax for regex/globs.
Now let’s pretend that we are editing a django settings.py (notice that we are extending our source code using the same API than the one of a python list since we are in a list of lines):
In [10]: red.extend(["\n", "INSTALLED_APPLICATIONS = (\n 'django',\n)"]) # here "\n" is to had a blank line
In [11]: red
Out[11]:
0 some_value = 1 + 4
1 INSTALLED_APPLICATIONS = (
'django',
)
And let’s install another django application! (again: same API than a python list)
In [12]: red.find("assignment", target=lambda x: x.dumps() == "INSTALLED_APPLICATIONS").value.append("'another_app'")
In [13]: red
Out[13]:
0 some_value = 1 + 4
1 INSTALLED_APPLICATIONS = (
'django',
'another_app',
)
Notice that the formatting of the tuple has been detected and respected when adding the new django application.
And let’s see the result of our work:
In [14]: print(red.dumps())
some_value = 1 + 4
INSTALLED_APPLICATIONS = (
'django',
'another_app',
)
Financial support¶
Baron and RedBaron are a very advanced piece of engineering that requires a lot of time of concentration to work on. Until the end of 2018, the development has been a full volunteer work mostly done by [Bram](https://github.com/psycojoker), but now, to reach the next level and bring those projects to the stability and quality you expect, we need your support.
You can join our contributors and sponsors on our transparent [OpenCollective](https://opencollective.com/redbaron), every contribution will count and will be mainly used to work on the projects stability and quality but also on continuing, on the side, the R&D side of those projects.
Our supporters¶
Become our first sponsor!
Table of content¶
Learn how to use RedBaron¶
This tutorial guides you through the big principles of RedBaron and highlights the most useful helpers and tricks. It is more or less a lighter version of the already existing documentation.
A reminder before starting:
- RedBaron doesn’t do static analysis and will never do (but it’s very likely that it will be combined with tools that do it, like astroid or rope, to bring static analysis into RedBaron or easy source code modification in the others)
The structure of this tutorial is similar to the documentation’s:
- basic principles and how to use it in a shell
- how to query the tree
- how to modify the tree
- how to play with list of things
- miscellaneous but useful stuff
Basic principles¶
Input and output with the source code in a string:
from redbaron import RedBaron
red = RedBaron("code source as a string")
red.dumps()
Input and output with the source code in a file:
from redbaron import RedBaron
with open("code.py", "r") as source_code:
red = RedBaron(source_code.read())
with open("code.py", "w") as source_code:
source_code.write(red.dumps())
Now that you know how to load your code into RedBaron, let’s talk about its principles:
- RedBaron represents the source code as a tree. This is because when you are writing source code (of any classical language), you are actually writing a tree structure in the source file.
- For example: in
1 + 2
the top node is+
, the left one is1
and the right one is2
. - In
(1 + 2) + 3
the top node is, again,+
, but the left one is actually(1 + 2)
which is again another+
node! This structure is a tree. - The classical approach to handle such a structure is to use an Abstract Syntax Tree (AST) (it is used by compilers and interpreters like cpython).
- RedBaron, by relying on Baron, uses a Full Syntax Tree (FST). It’s like an AST except it keeps every information, included formatting, and is then a lossless representation of the source code. Under the hood, the FST produced by Baron is in JSON and has been thought to be read and used by humans (although not as easily as RedBaron).
- So, when BeautifulSoup wraps the HTML datastructure into objects, RedBaron does the same thing for the FST datastructure and provides a nice way to interact with the source code.
Example of an AST for some language that looks like Go:
While you don’t have to do that to use RedBaron on a daily basis, seeing the produced FST can help your understand RedBaron better (every key that has “_formatting” in its name is formatting related):
In [1]: import json
In [2]: red = RedBaron("1+2")
In [3]: print(json.dumps(red.fst(), indent=4)) # json.dumps is used for pretty printing
[
{
"first_formatting": [],
"value": "+",
"second_formatting": [],
"second": {
"section": "number",
"type": "int",
"value": "2"
},
"type": "binary_operator",
"first": {
"section": "number",
"type": "int",
"value": "1"
}
}
]
Use it in a shell¶
Now that we stated the concept of the source code as a tree, let’s explore it.
First, like BeautifulSoup, when used in a shell RedBaron displays the currently selected source code, so you’ll have a direct idea of what you are working on:
In [4]: red = RedBaron("stuff = 1 + 2\nprint 'Beautiful result:', stuff ")
In [5]: red
Out[5]:
0 stuff = 1 + 2
1 print 'Beautiful result:', stuff
2 ' '
You might notice the 0
and the 1
on the left: those are the
indexes of the 2 nodes in the root of the source code. In fact, a source code is
a list of statements so the root node red
is a list. See by yourself:
In [6]: red[0]
Out[6]: stuff = 1 + 2
In [7]: red[1]
Out[7]: print 'Beautiful result:', stuff
But now, how to access the attributes? Since reading the doc for every node is boring, RedBaron comes with a helper method that shows you the underlying structure of the currently selected nodes:
In [8]: red[0]
Out[8]: stuff = 1 + 2
In [9]: red[0].help()
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
annotation ->
None
value ->
BinaryOperatorNode()
# identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
value='+'
first ->
IntNode() ...
second ->
IntNode() ...
The output might be a bit scary at first, but it’s simply showing you the underlying structure, mapped to Baron JSON’s one.
By the way, RedBaron has nice coloration features if you use ipython as your python shell.
Let’s take it step by step:
- We are on an
AssignmentNode
(something likea = b
) that has 3 attributes:operator
,target
andvalue
. - The
operator
is an empty string (it could have been a python operator like+
in a case likea += b
) target
points to another node, aNameNode
(you can see this thanks to the arrow->
instead of an equal sign=
)value
points to aBinaryOperatorNode
.
To get more information about all the existing kind of nodes, see the documentation: Nodes References Page.
Let’s try it:
In [10]: red[0]
Out[10]: stuff = 1 + 2
In [11]: red[0].operator
Out[11]: ''
In [12]: red[0].target
Out[12]: stuff
In [13]: red[0].value
Out[13]: 1 + 2
For now we saw attributes that are either strings or pointing to other
nodes, respectively called leafs and branches in the tree terminology.
The last kind of attributes that you will encounter are a special case
of the branch nodes: instead of pointing to a single node, they point to
a list of nodes. You can see this in the print statement’s value
attribute:
In [14]: red[1].help()
PrintNode()
# identifiers: print, print_, printnode
destination ->
None
value ->
* StringNode()
# identifiers: string, string_, stringnode
value="'Beautiful result:'"
* NameNode()
# identifiers: name, name_, namenode
value='stuff'
Notice the *
before StringNode
and NameNode
? It
indicates that they are items of a list. Look:
In [15]: red[1]
Out[15]: print 'Beautiful result:', stuff
In [16]: red[1].value
Out[16]:
0 'Beautiful result:'
1 stuff
In [17]: red[1].value[0]
Out[17]: 'Beautiful result:'
In [18]: red[1].value[1]
Out[18]: stuff
And if we show the help of the value attribute, we clearly see that there is a list of nodes.
In [19]: red[1].value.help()
0 -----------------------------------------------------
StringNode()
# identifiers: string, string_, stringnode
value="'Beautiful result:'"
1 -----------------------------------------------------
CommaNode()
# identifiers: comma, comma_, commanode
2 -----------------------------------------------------
NameNode()
# identifiers: name, name_, namenode
value='stuff'
This is similar for the root node, which is itself also a list of nodes:
In [20]: red.help()
0 -----------------------------------------------------
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
annotation ->
None
value ->
BinaryOperatorNode()
# identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
value='+'
first ->
IntNode() ...
second ->
IntNode() ...
1 -----------------------------------------------------
EndlNode()
# identifiers: endl, endl_, endlnode
value='\n'
indent=''
2 -----------------------------------------------------
PrintNode()
# identifiers: print, print_, printnode
destination ->
None
value ->
* StringNode()
# identifiers: string, string_, stringnode
value="'Beautiful result:'"
* NameNode()
# identifiers: name, name_, namenode
value='stuff'
3 -----------------------------------------------------
SpaceNode()
# identifiers: space, space_, spacenode
value=' '
And voilà, you now know how to navigate the tree by attributes without having to read any documentation!
If you’re curious about the identifiers
outputted by the
.help()
method, read on to the next section.
And one last thing: by default .help()
stops at a certain
“deepness level” and displays ...
instead of going further. To
avoid that, simply pass an integer that indicates the “deepness level”
you want, or give True
if you want to display the whole tree.
red.help(4)
red.help(True)
You can read the whole documentation of .help
here: .help()
Querying¶
Querying is inspired by BeautifulSoup. You have access to 2 methods:
.find
and .find_all
, accepting the same arguments. The
first one returns the first matched node and the second one returns
the list of all the matched nodes.
The first argument is a string that represent the kind of the node you
want to match on. The identifiers
section displayed by the
.help()
method shows you several strings you can use to identify
a kind of node. For example:
In [21]: red
Out[21]:
0 stuff = 1 + 2
1 print 'Beautiful result:', stuff
2 ' '
In [22]: red.help()
0 -----------------------------------------------------
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
annotation ->
None
value ->
BinaryOperatorNode()
# identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
value='+'
first ->
IntNode() ...
second ->
IntNode() ...
1 -----------------------------------------------------
EndlNode()
# identifiers: endl, endl_, endlnode
value='\n'
indent=''
2 -----------------------------------------------------
PrintNode()
# identifiers: print, print_, printnode
destination ->
None
value ->
* StringNode()
# identifiers: string, string_, stringnode
value="'Beautiful result:'"
* NameNode()
# identifiers: name, name_, namenode
value='stuff'
3 -----------------------------------------------------
SpaceNode()
# identifiers: space, space_, spacenode
value=' '
In [23]: red.find("assignment")
Out[23]: stuff = 1 + 2
In [24]: red.find("print")
Out[24]: print 'Beautiful result:', stuff
In [25]: red.find_all("int")
Out[25]:
0 1
1 2
Then, you can pass as many keyword arguments as you want. They will filter the returned list on the attributes of the node and keep only those matching all attributes:
In [26]: red.find("int", value=2)
The only special argument you can pass is recursive
that determine if
the query is done recursively. By default it is set at True
, just pass
recursive=False
to .find
or .find_all
to avoid that.
Queries are very powerful: you can pass lambdas, regexes, a short hand syntax for regexes and globs, a tuple of string instead of a string for the node kind, a global regex that receives the node (instead of a regex per attribute), etc. You can read all of that in the documentation: Querying.
Finally, .find
and .find_all
also have a shortcut syntax (exactly like
in BeautifulSoup):
In [27]: red.find("int")
Out[27]: 1
In [28]: red.int
Out[28]: 1
In [29]: red.find_all("int", value=2)
Out[29]:
In [30]: red("int", value=2)
Out[30]:
But be aware that if you do a
red.something_that_can_be_a_node_identifier
and this is also not an
attribute of a node, this will raise an AttributeError
.
Modification¶
Nodes modification is extremely simple in RedBaron: you just have to set the attribute of the node you want to modify with a string containing python source code. Just look by yourself:
In [31]: red
Out[31]:
0 stuff = 1 + 2
1 print 'Beautiful result:', stuff
2 ' '
In [32]: red[0].target = "something_else"
In [33]: red[0].value = "42 * 34"
In [34]: red
Out[34]:
0 something_else = 42 * 34
1 print 'Beautiful result:', stuff
2 ' '
In [35]: red[1].value = "'Hello World!'"
In [36]: red
Out[36]:
0 something_else = 42 * 34
1 print 'Hello World!'
2 ' '
Notice that this also works with complex attributes like the body of a function. Here RedBaron makes a lot of effort to correctly format your input so you can pass it pretty much anything:
In [37]: red = RedBaron("def a():\n pass")
In [38]: red[0].value = "1 + 1"
In [39]: red # correctly indented
Out[39]:
0 def a():
1 + 1
In [40]: red[0].value = "\n\n\n stuff\n"
In [41]: red # again
Out[41]:
0 def a():
stuff
And this works too for more complex situations where the node is indented and followed by another node whose indentation can’t be broken and other low level details that you don’t want to hear about (but if you wish too, this is detailed in the full documentation).
And voilà, easy source code modification! You can also pass RedBaron node
objects or Baron JSON FST that you have obtain is some way or another, for example by
using .copy()
:
In [42]: red = RedBaron("stuff = 1 + 2\nprint(stuff)")
In [43]: red
Out[43]:
0 stuff = 1 + 2
1 print(stuff)
In [44]: i = red[0].value.copy()
In [45]: red[1].value = i
In [46]: red
Out[46]:
0 stuff = 1 + 2
1 print1 + 2
You can also replace a node in place using the .replace()
method.
Warning: the .replace()
expects that the string you pass
represents a whole valid python program (so for example: .replace("*args,
**kwargs")
won’t work). This limitation should be raised in the future.
In [47]: red
Out[47]:
0 stuff = 1 + 2
1 print1 + 2
In [48]: red[0].value.replace("1234")
In [49]: red
Out[49]:
0 stuff = 1234
1 print1 + 2
This is generally very useful when working on queries. For example (a real life
example), here is the code to replace every print stuff
(prints
statement of one argument, an example with multiple arguments is left as an exercise to the
reader) with logger.debug(stuff)
:
red("print", value=lambda x: len(x) == 1).map(lambda x: x.replace("logger.debug(%s)" % x.value.dumps()))
(.map()
will be covered at the end of the tutorial but should speak for itself.)
You can read everything about modifications in RedBaron here: Modifying
Playing with list of nodes¶
The last big concept of RedBaron covered in this tutorial is how to handle list of nodes. The problem for
short is that, for a python developer, the list [1, 2, 3]
has 3 items
but it has 5 items in the FST world, because it needs to take into account
the commas. It is not sufficient to know that it is a comma separated
list because each comma can have a different formatting. This is
a pattern you find in every list of nodes, the separator being
either commas, dots (eg: a.b(c)[d]
) or end of line characters (for
lines of code).
Having to deal with those separators is extremely annoying and error prone, so, RedBaron offers an abstraction that hides all this for you! You just have to deal with those list of nodes like if they were regular python list and everything will be fine. See by yourself:
In [50]: red = RedBaron("[1, 2, 3]")
In [51]: red.help()
0 -----------------------------------------------------
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 [52]: red[0].value # see: no explicit commas to deal with
Out[52]:
0 1
1 2
2 3
In [53]: red[0].value.append("4")
In [54]: red # comma has been added for us
Out[54]: 0 [1, 2, 3, 4]
This abstraction is called a proxy list. They can even detect indentation style for comma separated lists:
In [55]: red = RedBaron("[\n 1,\n 2,\n 3,\n]")
In [56]: red
Out[56]:
0 [
1,
2,
3,
]
In [57]: red[0].value.append("caramba")
In [58]: red
Out[58]:
0 [
1,
2,
3,
caramba,
]
This also work with nodes separated by dots:
In [59]: red = RedBaron("a.b(c)[d]")
In [60]: red
Out[60]: 0 a.b(c)[d]
In [61]: red[0].value.extend(["e", "(f)", "[g:h]"])
In [62]: red
Out[62]: 0 a.b(c)[d].e(f)[g:h]
And lines of code (note that the blank lines are explicitly shown and it is intended as such, see the documentation for more information: Proxy List):
In [63]: red = RedBaron("a = 1\n\nprint(a)")
In [64]: red
Out[64]:
0 a = 1
1 '\n'
2 print(a)
In [65]: red.insert(1, "if a:\n print('a == 1')")
In [66]: red
Out[66]:
0 a = 1
1 if a:
print('a == 1')
2 '\n'
3 print(a)
The important things to remember are that:
- Every method and protocol of python lists (except
sort
andreversed
) works on proxy list. - And every node list in python is wrapped by a proxy list.
The raw list is stored on the .node_list
attribute of the proxy list:
In [67]: red = RedBaron("[1, 2, 3]")
In [68]: red[0].node_list
Out[68]:
0 1
1 ,
2 2
3 ,
4 3
Warning: the proxyfied list and the proxy list are only synced from the proxy list to the raw list. If you start to modify the raw list, don’t use the proxy list anymore or you’ll have strange bugs! This might change in the future.
One last thing: if the proxy list is stored on the .value
attribute,
you can directly call the methods on the holder node.
This is done because it is more intuitive, see by yourself:
red = RedBaron("[1, 2, 3]")
red[0].append("4") # is exactly the same than the next line
red[0].value.append("4")
Misc things¶
A short list of useful features of RedBaron:
.map
, a method of RedBaron lists that takes a callable (like a lambda or a function), apply it to every one of its members and returns a RedBaron list containing the result of the call.apply
same than.map
except it returns a RedBaron list of the nodes on which the callable has been applied (i.e. the members before the call instead of the members after the call) (for simplicity we uses theint
builtin function here, you might want to look atto_python
in the future for a more generic conversion operation)
In [69]: red = RedBaron("[1, 2, 3]")
In [70]: red("int").map(lambda x: int(x.value) + 42)
Out[70]:
0 43
1 44
2 45
In [71]: red("int").apply(lambda x: int(x.value) + 42)
Out[71]:
0 1
1 2
2 3
.filter
, another method of RedBaron list, it takes a callable and return a RedBaron list containing the nodes for which the callable has returned True (or something that is tested has True in python)
In [72]: red = RedBaron("[1, 2, 3]")
In [73]: red("int").filter(lambda x: int(x.value) % 2 == 1) # odd numbers
Out[73]:
0 1
1 3
.next
gives the node just after the current one if the node is in a list.previous
does the inverse.parent
gives the holder of this node
In [74]: red = RedBaron("[1, 2, 3]")
In [75]: red.int_
Out[75]: 1
In [76]: red.int_.next
Out[76]: ,
In [77]: red.int_.previous # None because nothing is behind it
In [78]: red.int_.parent
Out[78]: [1, 2, 3]
And you can find all the others various RedBaron features here: Other
Why is this important?¶
The usage of an FST might not be obvious at first sight so let’s consider a series of problems to illustrate it. Let’s say that you want to write a program that will:
- rename a variable in a source file… without clashing with things that are not a variable (example: stuff inside a string)
- inline a function/method
- extract a function/method from a series of line of code
- split a class into several classes
- split a file into several modules
- convert your whole code base from one ORM to another
- do custom refactoring operation not implemented by IDE/rope
- implement the class browser of smalltalk for python (the whole one where you can edit the code of the methods, not just showing code)
It is very likely that you will end up with the awkward feeling of writing clumsy weak code that is very likely to break because you didn’t thought about all the annoying special cases and the formatting keeps bothering you. You may end up playing with ast.py until you realize that it removes too much information to be suitable for those situations. You will probably ditch this task as simple too complicated and really not worth the effort. You are missing a good abstraction that will take care of all of the code structure and formatting for you so you can concentrate on your task.
The FST tries to be this abstraction. With it you can now work on a tree which represents your code with its formatting. Moreover, since it is the exact representation of your code, modifying it and converting it back to a string will give you back your code only modified where you have modified the tree.
Said in another way, what I’m trying to achieve with Baron is a paradigm change in which writing code that will modify code is now a realistic task that is worth the price (I’m not saying a simple task, but a realistic task, it’s still a complex task).
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.
Querying¶
As you have seen in the previous section, you can navigate into RedBaron tree
only using attribute access and index access on list of nodes with the use of
the .help()
method to know what you can do. However, RedBaron offers
way more powerful and convenient tools to do that.
.find()¶
To retrieve a single node, you can use the .find()
method by passing it
one of the identifiers listed in .help()
of node you want to get, this way:
In [1]: red = RedBaron("a = 1")
In [2]: red.help()
0 -----------------------------------------------------
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 [3]: red.find('NameNode').help()
NameNode()
# identifiers: name, name_, namenode
value='a'
In [4]: red.find('namenode').help() # identifiers are not case sensitive
NameNode()
# identifiers: name, name_, namenode
value='a'
In [5]: red.find('name')
Out[5]: a
This will recursively travel the tree and return the first node of that type.
You can also specify attributes of the node that you want to match:
In [6]: red = RedBaron("a = b")
In [7]: red.find('name').help()
NameNode()
# identifiers: name, name_, namenode
value='a'
In [8]: red.find('name', value='b').help()
NameNode()
# identifiers: name, name_, namenode
value='b'
If you don’t want a recursive approach but only on the first level on the current node or node list, you can pass recursive=False
to .find()
.
Like BeautifulSoup, RedBaron provides a shorthand to .find()
, you can
write the name of the target as an attribute of the node and this will do a .find()
in the same fashion:
In [9]: red = RedBaron("a = b")
In [10]: red.find('name')
Out[10]: a
In [11]: red.name
Out[11]: a
You might have noticed that some identifiers end with a _
, those are
for the case where the identifier might be a Python reserved keyword like
if
, or while
for example.
Be aware that if you do a red.something_that_can_be_a_node_identifier
and this is also not an attribute of a node, this will raise an
AttributeError
.
.find_all()¶
.find_all()
is extremely similar to .find()
except it returns a
node list containing all the matching queries instead of a single one. Like in
BeautifulSoup, __call__
is aliased to find_all
(meaning that if
you try to call the node this way node(some_arguments)
this will call
.find_all()
with the arguments).
In [12]: red = RedBaron("a = b")
In [13]: red.find_all("NameNode")
Out[13]:
0 a
1 b
In [14]: red.find_all("name")
Out[14]:
0 a
1 b
In [15]: red.findAll("name")
Out[15]:
0 a
1 b
In [16]: red.findAll("name", value="b")
Out[16]: 0 b
In [17]: red("name", value="b")
Out[17]: 0 b
.find_all()
also supports the option recursive=False
.
Advanced querying¶
.find()
and .find_all()
offer more powerful comparison mean
than just equality comparison.
Callable (lambda)¶
Instead of passing a string to test properties of the identifier of a node, you can pass a callable, like a lambda. It will receive the value as first argument:
In [18]: red = RedBaron("a = [1, 2, 3, 4]")
In [19]: red.find("int", value=lambda value: int(value) % 2 == 0)
Out[19]: 2
In [20]: red.find_all("int", value=lambda value: int(value) % 2 == 0)
Out[20]:
0 2
1 4
In [21]: red.find(lambda identifier: identifier == "comma")
Out[21]: ,
In [22]: red.find_all(lambda identifier: identifier == "comma")
Out[22]:
0 ,
1 ,
2 ,
Regex¶
Instead of passing a string to test properties of a node, you can pass a compiled regex:
In [23]: import re
In [24]: red = RedBaron("abcd = plop + pouf")
In [25]: red.find("name", value=re.compile("^p"))
Out[25]: plop
In [26]: red.find_all("name", value=re.compile("^p"))
Out[26]:
0 plop
1 pouf
In [27]: red.find(re.compile("^n"))
Out[27]: abcd
In [28]: red.find_all(re.compile("^n"))
Out[28]:
0 abcd
1 plop
2 pouf
Having to compile regex is boring, so you can use this shorthand syntax instead (prefixing a string with “re:”):
In [29]: red = RedBaron("abcd = plop + pouf")
In [30]: red.find("name", value="re:^p")
Out[30]: plop
In [31]: red.find_all("name", value="re:^p")
Out[31]:
0 plop
1 pouf
In [32]: red.find("re:^n")
Out[32]: abcd
In [33]: red.find_all("re:^n")
Out[33]:
0 abcd
1 plop
2 pouf
Globs¶
Same than in a shell, you can use globs by prefixing the string with “g:”:
In [34]: red = RedBaron("abcd = plop + pouf")
In [35]: red.find("name", value="g:p*")
Out[35]: plop
In [36]: red.find_all("name", value="g:p*")
Out[36]:
0 plop
1 pouf
In [37]: red.find("g:n*")
Out[37]: abcd
In [38]: red.find_all("g:n*")
Out[38]:
0 abcd
1 plop
2 pouf
In the background, the comparison is done using the fnmatch module of the standard lib.
List or tuple¶
You can pass a list as a shorthand to test if the tested attribute is in any of the member of the list/tuple:
In [39]: red = RedBaron("foo\nbar\nbaz")
In [40]: red.find("name", value=["foo", "baz"])
Out[40]: foo
In [41]: red.find("name", value=("foo", "baz"))
Out[41]: foo
In [42]: red("name", value=["foo", "baz"])
Out[42]:
0 foo
1 baz
In [43]: red("name", value=("foo", "baz"))
Out[43]:
0 foo
1 baz
In [44]: red = RedBaron("1\nstuff\n'string'\n")
In [45]: red.find(["int", "string"])
Out[45]: 1
In [46]: red(["int", "string"])
Out[46]:
0 1
1 'string'
*args and default value¶
You can also pass as many callable as args (without giving it a key) as you want, those callables will receive the node itself as first argument (and must return a value that will be tested as a boolable):
In [47]: red = RedBaron("a = [1, 2, 3, 4]")
In [48]: red.find("int", lambda node: int(node.value) % 2 == 0)
Out[48]: 2
In [49]: red.find_all("int", lambda node: int(node.value) % 2 == 0)
Out[49]:
0 2
1 4
In [50]: red.find("int", lambda node: int(node.value) % 2 == 0, lambda node: int(node.value) == 4)
Out[50]: 4
To ease the usage of RedBaron in ipython (and in general), you can pass any of
the previous testing methods (except the lambda) as the first argument of
*args, it will be tested against the default testing attribute which is the
“value” attribute by default. This mean that: red.find("name", "foo")
is the equivalent of red.find("name", value="foo")
.
If the default tested attribute is different, it will be shown in
.help()
. For now, the 2 only cases where this happens is on class node
and funcdef node where the attribute is “name”.
In [51]: red = RedBaron("foo\ndef bar(): pass\nbaz\ndef badger(): pass")
In [52]: red.find("name", "baz")
Out[52]: baz
In [53]: red.find("def", "bar")
Out[53]: def bar(): pass
In [54]: red.find("def").help()
DefNode()
# identifiers: def, def_, defnode, funcdef, funcdef_
# default test value: name
async=False
name='bar'
return_annotation ->
None
decorators ->
arguments ->
value ->
* PassNode()
# identifiers: pass, pass_, passnode
Modifying¶
Principle¶
When it comes to modifying the tree, the normal classical way would tell you to use the RedBaron nodes constructors, like this:
In [1]: from redbaron import RedBaron, NameNode
In [2]: red = RedBaron("a = 1")
In [3]: red[0].value
Out[3]: 1
In [4]: red[0].value = NameNode({'first_formatting': [{'type': 'space', 'value': ' '}], 'value': '+', 'second_formatting': [{'type': 'space', 'value': ' '}], 'second': {'section': 'number', 'type': 'int', 'value': '1'}, 'type': 'binary_operator', 'first': {'section': 'number', 'type': 'int', 'value': '1'}})
In [5]: red
Out[5]: 0 a = 1 + 1
As you can see, this is totally impracticable. So, to solve this problem, RedBaron adopt a simple logic: you already know how to code in python, so, just send python code in form of a string, RedBaron will takes care or parsing and injecting it into its tree. This give an extremely simple and intuitive API:
In [6]: red = RedBaron("a = 1")
In [7]: red[0].value
Out[7]: 1
In [8]: red[0].value = "1 + 1"
In [9]: red
Out[9]: 0 a = 1 + 1
The details on how you can modify every nodes can be found here: Nodes References Page.
Code block modifications¶
The modification of python code block (like the body of a function or a while loop) is also possible this way. RedBaron will takes care for you or formatting you input the right way (adding surrounding blank lines and settings the correct indentation for the every line).
Example:
In [10]: red = RedBaron("while True: pass")
In [11]: red[0].value = "plop"
In [12]: red
Out[12]:
0 while True:
plop
In [13]: red[0].value = " this_will_be_correctly_indented"
In [14]: red
Out[14]:
0 while True:
this_will_be_correctly_indented
You have the full list of cases handled on this page: Nodes References Page.
Details¶
As you might have already noticed, you can set attributes of a node with a string or a RedBaron node. This is also possible by directly passing FST.
Here is an IPython session illustrating all the possibilities (be sure to have read the “node structures” in basics to understand what is happening):
In [15]: from redbaron import RedBaron
In [16]: red = RedBaron("a = b")
Data attribute, no parsing¶
In [17]: red.name.help()
NameNode()
# identifiers: name, name_, namenode
value='a'
In [18]: red.name.value = "something_else"
In [19]: red
Out[19]: 0 something_else = b
Node attribute with a string: parsing with RedBaron¶
In [20]: red[0].help()
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='something_else'
annotation ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
In [21]: red[0].value = "42 * pouet"
In [22]: red
Out[22]: 0 something_else = 42 * pouet
Node attribute with FST data: transformation into RedBaron objects¶
In [23]: red[0].value = {"type": "name", "value": "pouet"}
In [24]: red
Out[24]: 0 something_else = pouet
List attribute with a string: parsing with RedBaron¶
In [25]: red = RedBaron("[1, 2, 3]")
In [26]: 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 [27]: red[0].value = "caramba"
In [28]: red
Out[28]: 0 [caramba]
In [29]: red[0].value = "4, 5, 6"
In [30]: red
Out[30]: 0 [4, 5, 6]
List node attribute with FST: transformation into RedBaron objects¶
In [31]: red[0].value = {"type": "name", "value": "pouet"}
In [32]: red
Out[32]: 0 [pouet]
In [33]: red[0].value = [{"type": "name", "value": "pouet"}]
In [34]: red
Out[34]: 0 [pouet]
List node attribute with mixed content: parsing/transformation depending of the context¶
In [35]: red[0].value = [{"type": "name", "value": "pouet"}, {"type": "comma", "first_formatting": [], "second_formatting": []}, "pouet ,", NameNode({"type": "name", "value": "plop"})]
In [36]: red
Out[36]: 0 [pouet,pouet ,plop]
Auto assignment of .parent and .on_attribute¶
When you modify an attribute of a node or a node list, RedBaron will take care
of setting the .parent
value of the new attribute to the corresponding
node.
This will be done if you set the attribute value using either a string
,
a fst node
, an instance of a node or a node list.
The same is done for .on_attribute
.
Next¶
To learn how to work with list of things in RedBaron read Proxy List.
Proxy List¶
Problem¶
For a python developer, the list [1, 2, 3]
has 3 members, which
is true in the python world, but in the “source code modification”
world, this list has 5 elements because you have to count the 2 commas.
Indeed each comma needs to be taken into account separately because they
can have a different formatting.
This makes things quite annoying to deal with because you have to think about the formatting too! For example, if you want to append an item to a list, you need to take care of a lot of details:
- if the list is empty you don’t have to put a comma
- otherwise yes
- but wait, what happens if there is a trailing comma?
- also, what to do if the list is declared in an indented way (with
"\n "
after every comma for example)? - etc…
And that’s only for a comma separated list of things: you also have the
same formatting details to care about for dot separated lists
(e.g. a.b.c().d[plop]
) and endl separated lists (a python code block,
or you whole source file).
You don’t want to have to deal with this.
Solution¶
To avoid you to deal with all this boring low level details, RedBaron implements “proxy lists”. This abstraction gives you the impression that the list of things you are dealing with behave the same way than in the python world while taking care of all the low level formatting details.
The “proxy lists” has the same API than a python list so they should be really intuitive to use.
For example:
In [1]: red = RedBaron("[1, 2, 3]")
In [2]: red[0].value.append("42")
In [3]: red
Out[3]: 0 [1, 2, 3, 42]
In [4]: del red[0].value[2]
In [5]: red
Out[5]: 0 [1, 2, 42]
There are, for now, 4 kind of proxy lists:
CommaProxyList
which handles comma separated listsDotProxyList
which handlesatomtrailers
(those kind of constructions:a.b[plop].c()
)LineProxyList
which handles lines of code (like the body of a function or the whole source code)DecoratorLineProxyList
which handles lists of decorators (they are nearly the same asLineProxyList
)
Be aware that the proxy list are set on the attribute that is a list, not on the node holding the list. See the ‘value’ attribute access in the examples below.
Usage¶
As said, proxy lists have the exact same API than python lists (at the exception
that they don’t implement the sort
and reverse
methods).
Every method accepts as input the same inputs that you can use to modify a node
in RedBaron. This means that you can pass a string containing source code,
an FST or a RedBaron node.
Here is a session demonstrating every method of a proxy list:
In [6]: red = RedBaron("[1, 2, 3]")
Please refer to python list documentation if you want to know the exact behavior or those methods (or send a patch to improve this documentation).
append¶
In [7]: red
Out[7]: 0 [1, 2, 3]
In [8]: red[0].value.append("plop")
In [9]: red
Out[9]: 0 [1, 2, 3, plop]
In [10]: red[0].value
Out[10]:
0 1
1 2
2 3
3 plop
insert¶
In [11]: red
Out[11]: 0 [1, 2, 3, plop]
In [12]: red[0].value.insert(1, "42")
In [13]: red
Out[13]: 0 [1, 42, 2, 3, plop]
In [14]: red[0].value
Out[14]:
0 1
1 42
2 2
3 3
4 plop
extend¶
In [15]: red
Out[15]: 0 [1, 42, 2, 3, plop]
In [16]: red[0].value.extend(["pif", "paf", "pouf"])
In [17]: red
Out[17]: 0 [1, 42, 2, 3, plop, pif, paf, pouf]
In [18]: red[0].value
Out[18]:
0 1
1 42
2 2
3 3
4 plop
5 pif
6 paf
7 pouf
pop¶
In [19]: red
Out[19]: 0 [1, 42, 2, 3, plop, pif, paf, pouf]
In [20]: red[0].value.pop()
In [21]: red
Out[21]: 0 [1, 42, 2, 3, plop, pif, paf]
In [22]: red[0].value
Out[22]:
0 1
1 42
2 2
3 3
4 plop
5 pif
6 paf
In [23]: red[0].value.pop(3)
In [24]: red
Out[24]: 0 [1, 42, 2, plop, pif, paf]
In [25]: red[0].value
Out[25]:
0 1
1 42
2 2
3 plop
4 pif
5 paf
__getitem__¶
In [26]: red
Out[26]: 0 [1, 42, 2, plop, pif, paf]
In [27]: red[0].value
Out[27]:
0 1
1 42
2 2
3 plop
4 pif
5 paf
In [28]: red[0].value[2]
Out[28]: 2
__setitem__¶
In [29]: red
Out[29]: 0 [1, 42, 2, plop, pif, paf]
In [30]: red[0].value[2] = "1 + 1"
In [31]: red
Out[31]: 0 [1, 42, 1 + 1, plop, pif, paf]
In [32]: red[0].value
Out[32]:
0 1
1 42
2 1 + 1
3 plop
4 pif
5 paf
remove¶
In [33]: red
Out[33]: 0 [1, 42, 1 + 1, plop, pif, paf]
In [34]: red[0].value.remove(red[0].value[2])
In [35]: red
Out[35]: 0 [1, 42, plop, pif, paf]
In [36]: red[0].value
Out[36]:
0 1
1 42
2 plop
3 pif
4 paf
index¶
In [37]: red
Out[37]: 0 [1, 42, plop, pif, paf]
In [38]: red[0].value
Out[38]:
0 1
1 42
2 plop
3 pif
4 paf
In [39]: red[0].value.index(red[0].value[2])
Out[39]: 2
count¶
In [40]: red
Out[40]: 0 [1, 42, plop, pif, paf]
In [41]: red[0].value
Out[41]:
0 1
1 42
2 plop
3 pif
4 paf
In [42]: red[0].value.count(red[0].value[2])
Out[42]: 1
len¶
In [43]: red
Out[43]: 0 [1, 42, plop, pif, paf]
In [44]: red[0].value
Out[44]:
0 1
1 42
2 plop
3 pif
4 paf
In [45]: len(red[0].value)
Out[45]: 5
__delitem__¶
In [46]: red
Out[46]: 0 [1, 42, plop, pif, paf]
In [47]: del red[0].value[2]
In [48]: red
Out[48]: 0 [1, 42, pif, paf]
In [49]: red[0].value
Out[49]:
0 1
1 42
2 pif
3 paf
in¶
In [50]: red
Out[50]: 0 [1, 42, pif, paf]
In [51]: red[0].value[2] in red[0].value
Out[51]: False
__iter__¶
In [52]: red
Out[52]: 0 [1, 42, pif, paf]
In [53]: for i in red[0].value:
....: print(i.dumps())
....:
1
42
pif
paf
__getslice__¶
In [54]: red
Out[54]: 0 [1, 42, pif, paf]
In [55]: red[0].value
Out[55]:
0 1
1 42
2 pif
3 paf
In [56]: red[0].value[2:4]
Out[56]:
0 pif
1 paf
__setslice__¶
In [57]: red
Out[57]: 0 [1, 42, pif, paf]
In [58]: red[0].value[2:4] = ["1 + 1", "a", "b", "c"]
In [59]: red
Out[59]: 0 [1, 42, 1 + 1, a, b, c]
In [60]: red[0].value
Out[60]:
0 1
1 42
2 1 + 1
3 a
4 b
5 c
__delslice__¶
In [61]: red
Out[61]: 0 [1, 42, 1 + 1, a, b, c]
In [62]: red[0].value[2:5]
Out[62]:
0 1 + 1
1 a
2 b
In [63]: del red[0].value[2:5]
In [64]: red
Out[64]: 0 [1, 42, c]
In [65]: red[0].value
Out[65]:
0 1
1 42
2 c
Access the unproxified node list¶
The unproxified node list is stored under the attribute node_list
of
the proxy list. Be aware that, for now, the proxy won’t detect if you
directly modify the unproxified node list, this will cause bugs if you modify
the unproxified list then use the proxy list directly. So, for now, only use
one or the other.
In [66]: red = RedBaron("[1, 2, 3]")
In [67]: red[0].value.node_list
Out[67]:
0 1
1 ,
2 2
3 ,
4 3
In [68]: red[0].value
Out[68]:
0 1
1 2
2 3
Omitting “.value”¶
For convenience, and because this is a super common typo error, if a node has a
proxy list on its .value
attribute, you can omit to access it and the
method access will be automatically redirect to it.
This means that the 2 next lines are equivalent:
In [69]: red[0]
Out[69]: [1, 2, 3]
In [70]: red[0].value.append("plop")
In [71]: red[0].append("plop")
CommaProxyList¶
CommaProxyList is the most generic and most obvious proxy list, all the examples above are made using it.
It is used everywhere where values are separated by commas.
DotProxyList¶
DotProxyList is nearly as generic as the CommaProxyList. The specific case of a
DotProxyList is that it is intelligent enough to not add a “.” before a “call”
((a, b=c, *d, **e)
) or a “getitem” ([foobar]
).
In [72]: red = RedBaron("a.b(c).d[e]")
In [73]: red[0].value
Out[73]:
0 a
1 b
2 (c)
3 d
4 [e]
In [74]: red[0].extend(["[stuff]", "f", "(g, h)"])
In [75]: red[0]
Out[75]: a.b(c).d[e][stuff].f(g, h)
In [76]: red[0].value
Out[76]:
0 a
1 b
2 (c)
3 d
4 [e]
5 [stuff]
6 f
7 (g, h)
It is used everywhere where values are separated by “.”.
You can see a complete example with a DotProxyList, like for the CommaProxyList, here: DotProxyList usage examples.
LineProxyList¶
LineProxyList is used to handle lines of code, it takes care to place the correct endl node between and to set the correct indentation and not to break the indentation of the next block (if there is one).
One particularity of LineProxyList is that it shows you explicitly the empty line (while other proxy lists never show you formatting). This is done because you’ll often want to be able to manage those blank lines because you want to put some space in your code or separate group of lines.
In [77]: red = RedBaron("while 42:\n stuff\n other_stuff\n\n there_is_an_empty_line_before_me")
In [78]: red
Out[78]:
0 while 42:
stuff
other_stuff
there_is_an_empty_line_before_me
In [79]: red[0].value
Out[79]:
0 stuff
1 other_stuff
2 '\n '
3 there_is_an_empty_line_before_me
In [80]: red[0].append("plouf")
In [81]: red
Out[81]:
0 while 42:
stuff
other_stuff
there_is_an_empty_line_before_me
plouf
In [82]: red[0].value
Out[82]:
0 stuff
1 other_stuff
2 '\n '
3 there_is_an_empty_line_before_me
4 plouf
You can see a complete example with a LineProxyList, like for the CommaProxyList, here: LineProxyList usage examples.
DecoratorLineProxyList¶
A DecoratorLineProxyList is exactly the same as a LineProxyList except it has a small modification to indent decorators correctly. Just think of it as a simple LineProxyList and everything will be fine.
Don’t forget to add the :file:`@` when you add a new decorator (omitting it will raise an exception).
Example:
In [83]: red = RedBaron("@plop\ndef stuff():\n pass\n")
In [84]: red
Out[84]:
0 @plop
def stuff():
pass
In [85]: red[0].decorators.append("@plouf")
In [86]: red[0].decorators
Out[86]:
0 @plop
1 @plouf
In [87]: red
Out[87]:
0 @plop
@plouf
def stuff():
pass
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 140441411937616>
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 returnedTrue
(or something consideredTrue
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')
Reference¶
This is the reference page for every node type encountered in RedBaron and their specificities.
Nodes References Page¶
TopClass¶
CodeBlockNode¶
CodeBlockNode is a type of node that has a body composed of indented code like the DefNode or the IfNode. Great care has been taken on the SetAttr of their value so you don’t have to take care about reindenting and other formatting details.
Demonstration:
In [1]: red = RedBaron("def function():\n pass\n")
In [2]: red
Out[2]:
0 def function():
pass
In [3]: red[0].value = "stuff" # first '\n' will be added, indentation will be set
In [4]: red
Out[4]:
0 def function():
stuff
In [5]: red[0].value = " bad_indent"
In [6]: red
Out[6]:
0 def function():
bad_indent
In [7]: red[0].value = " some\n stuff"
In [8]: red
Out[8]:
0 def function():
some
stuff
Some for indented cases:
In [9]: red = RedBaron("class A:\n def __init__():\n pass\n\n def plop():\n pass")
In [10]: red.def_.value = "not_indented"
In [11]: red
Out[11]:
0 class A:
def __init__():
not_indented
def plop():
pass
In [12]: red.def_.value = "\n badly_indented"
In [13]: red
Out[13]:
0 class A:
def __init__():
badly_indented
def plop():
pass
In [14]: red.def_.value = "some\nstuff\nfoo\nbar\n\npouet"
In [15]: red
Out[15]:
0 class A:
def __init__():
some
stuff
foo
bar
pouet
def plop():
pass
Nodes¶
ArgumentGeneratorComprehensionNode¶
A node representing generator passed as an argument during a function call.
In [16]: RedBaron("a(x for y in z)")[0].value[1].value[0].help(deep=True)
ArgumentGeneratorComprehensionNode()
# identifiers: argument_generator_comprehension, argument_generator_comprehension_, argumentgeneratorcomprehension, argumentgeneratorcomprehensionnode
result ->
NameNode()
# identifiers: name, name_, namenode
value='x'
generators ->
* ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='y'
target ->
NameNode()
# identifiers: name, name_, namenode
value='z'
ifs ->
SetAttr¶
In [17]: red = RedBaron("a(x for y in z)")
In [18]: red
Out[18]: 0 a(x for y in z)
In [19]: red[0].value[1].value[0].result = "pouet"
In [20]: red
Out[20]: 0 a(pouet for y in z)
In [21]: red[0].value[1].value[0].generators = "for artichaut in courgette"
In [22]: red
Out[22]: 0 a(pouet for artichaut in courgette)
AssertNode¶
A node representing the assert statement.
In [23]: RedBaron("assert test, message")[0].help(deep=True)
AssertNode()
# identifiers: assert, assert_, assertnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='test'
message ->
NameNode()
# identifiers: name, name_, namenode
value='message'
SetAttr¶
In [24]: red = RedBaron("assert some_test")
In [25]: red
Out[25]: 0 assert some_test
In [26]: red[0].value = "1 == caramba()"
In [27]: red
Out[27]: 0 assert 1 == caramba()
In [28]: red[0].message = "'foo bar'"
In [29]: red
Out[29]: 0 assert 1 == caramba(), 'foo bar'
In [30]: red[0].message = ""
In [31]: red
Out[31]: 0 assert 1 == caramba()
AssignmentNode¶
A node representing the assign operation in python (foo = bar
) and the
“augmented” assign (foo += bar
).
In [32]: RedBaron("a = b")[0].help(deep=True)
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator=''
target ->
NameNode()
# identifiers: name, name_, namenode
value='a'
annotation ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
In [33]: RedBaron("a += b")[0].help(deep=True)
AssignmentNode()
# identifiers: assign, assignment, assignment_, assignmentnode
operator='+'
target ->
NameNode()
# identifiers: name, name_, namenode
value='a'
annotation ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
SetAttr¶
Works as expected:
In [34]: red = RedBaron("a = b")
In [35]: red[0].first = "caramba"
In [36]: red
Out[36]: 0 a = b
In [37]: red[0].second = "42"
In [38]: red
Out[38]: 0 a = b
For the operator part, expected input should work:
In [39]: red = RedBaron("a = b")
In [40]: red[0].operator = "+="
In [41]: red
Out[41]: 0 a += b
In [42]: red[0].operator = "+" # equivalent to '+='
In [43]: red
Out[43]: 0 a += b
In [44]: red[0].operator = "-" # equivalent to '-='
In [45]: red
Out[45]: 0 a -= b
In [46]: red[0].operator = "=" # equivalent to '='
In [47]: red
Out[47]: 0 a = b
In [48]: red[0].operator = "/="
In [49]: red
Out[49]: 0 a /= b
In [50]: red[0].operator = "" # equivalent to '='
In [51]: red
Out[51]: 0 a = b
AssociativeParenthesisNode¶
This node represents a statement prioritised on another by being surrounded by
parenthesis. For e.g., the first part of this addition: (1 + 1) * 2
.
In [52]: RedBaron("(foo)")[0].help(deep=True)
AssociativeParenthesisNode()
# identifiers: associative_parenthesis, associative_parenthesis_, associativeparenthesis, associativeparenthesisnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='foo'
SetAttr¶
In [53]: red = RedBaron("(foo)")
In [54]: red
Out[54]: 0 (foo)
In [55]: red[0].value = "1 + 1"
In [56]: red
Out[56]: 0 (1 + 1)
AtomtrailersNode¶
This node represents a combination of NameNode, DotNode,
CallNode, GetitemNode sorted in a list. For e.g.:
a.b().c[d]
.
In [57]: RedBaron("a.b().c[d]")[0].help(deep=True)
AtomtrailersNode()
# identifiers: atomtrailers, atomtrailers_, atomtrailersnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='a'
* NameNode()
# identifiers: name, name_, namenode
value='b'
* CallNode()
# identifiers: call, call_, callnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='c'
* GetitemNode()
# identifiers: getitem, getitem_, getitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='d'
SetAttr¶
In [58]: red = RedBaron("a.b()")
In [59]: red
Out[59]: 0 a.b()
In [60]: red[0].value = "d.be"
In [61]: red
Out[61]: 0 d.be
BinaryNode¶
The node represents a binary number value.
In [62]: RedBaron("0b10101")[0].help(deep=True)
BinaryNode()
# identifiers: binary, binary_, binarynode
value='0b10101'
BinaryOperatorNode¶
The node represents a binary operator (an operator (e.g.: +
-
/
..) applied to 2 values) with its operands. For e.g.: 1 + 1
.
In [63]: RedBaron("1 + 1")[0].help(deep=True)
BinaryOperatorNode()
# identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
value='+'
first ->
IntNode()
# identifiers: int, int_, intnode
value='1'
second ->
IntNode()
# identifiers: int, int_, intnode
value='1'
SetAttr¶
In [64]: red = RedBaron("1 + 1")
In [65]: red
Out[65]: 0 1 + 1
In [66]: red[0].value = "*"
In [67]: red
Out[67]: 0 1 * 1
In [68]: red[0].first = "(1 + 1)"
In [69]: red
Out[69]: 0 (1 + 1) * 1
In [70]: red[0].second = "caramba"
In [71]: red
Out[71]: 0 (1 + 1) * caramba
BooleanOperatorNode¶
The node represents a boolean operator (an operator (e.g.: and
or
) applied to 2 values) with its operands. For e.g.: x and y
.
In [72]: RedBaron("x and y")[0].help(deep=True)
BooleanOperatorNode()
# identifiers: boolean_operator, boolean_operator_, booleanoperator, booleanoperatornode
value='and'
first ->
NameNode()
# identifiers: name, name_, namenode
value='x'
second ->
NameNode()
# identifiers: name, name_, namenode
value='y'
SetAttr¶
In [73]: red = RedBaron("x and y")
In [74]: red
Out[74]: 0 x and y
In [75]: red[0].value = "or"
In [76]: red
Out[76]: 0 x or y
In [77]: red[0].first = "plop"
In [78]: red
Out[78]: 0 plop or y
In [79]: red[0].second = "oupsi"
In [80]: red
Out[80]: 0 plop or oupsi
CallNode¶
A node representing a call (eg: a()
, here a
is called with no
arguments). It is always stored in an AtomtrailersNode or a
DecoratorNode.
In [81]: RedBaron("a(b, c=d)")[0].value[1].help(deep=True)
CallNode()
# identifiers: call, call_, callnode
value ->
* CallArgumentNode()
# identifiers: call_argument, call_argument_, callargument, callargumentnode
target ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
* CallArgumentNode()
# identifiers: call_argument, call_argument_, callargument, callargumentnode
target ->
NameNode()
# identifiers: name, name_, namenode
value='c'
value ->
NameNode()
# identifiers: name, name_, namenode
value='d'
SetAttr¶
SetAttr works as expected:
In [82]: red = RedBaron("a()")
In [83]: red[0].value[1].value = "b, c=d, *e, **f"
In [84]: red
Out[84]: 0 a(b, c=d, *e, **f)
CallArgumentNode¶
A node representing an argument or a named argument of a CallNode (other nodes that can be in a CallNode are ListArgumentNode and DictArgumentNode).
In [85]: RedBaron("a(b, c=d)")[0].value[1].value[0].help(deep=True)
CallArgumentNode()
# identifiers: call_argument, call_argument_, callargument, callargumentnode
target ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
In [86]: RedBaron("a(b, c=d)")[0].value[1].value[1].help(deep=True)
CallArgumentNode()
# identifiers: call_argument, call_argument_, callargument, callargumentnode
target ->
NameNode()
# identifiers: name, name_, namenode
value='c'
value ->
NameNode()
# identifiers: name, name_, namenode
value='d'
SetAttr¶
In [87]: red = RedBaron("a(b)")
In [88]: red
Out[88]: 0 a(b)
In [89]: red[0].value[1].value[0] = "stuff=foo"
In [90]: red
Out[90]: 0 a(stuff=foo)
ClassNode¶
A node representing a class definition.
In [91]: RedBaron("class SomeAwesomeName(A, B, C): pass")[0].help(deep=True)
ClassNode()
# identifiers: class, class_, classnode
# default test value: name
name='SomeAwesomeName'
parenthesis=True
decorators ->
inherit_from ->
* TupleNode()
# identifiers: tuple, tuple_, tuplenode
with_parenthesis=False
value ->
* NameNode()
# identifiers: name, name_, namenode
value='A'
* NameNode()
# identifiers: name, name_, namenode
value='B'
* NameNode()
# identifiers: name, name_, namenode
value='C'
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
ClassNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Most other attributes work as expected:
In [92]: red = RedBaron("class SomeAwesomeName(A, B, C): pass")
In [93]: red[0].name = "AnotherAwesomeName"
In [94]: red
Out[94]:
0 class AnotherAwesomeName(A, B, C): pass
In [95]: red[0].inherit_from = "object"
In [96]: red
Out[96]:
0 class AnotherAwesomeName(object): pass
CommaNode¶
A node representing a comma, this is the kind of formatting node that you might have to deal with if not enough high level helpers are available. They are generally present in call, function arguments definition and data structure sugar syntactic notation.
The comma node is responsible for holding the formatting around it.
In [97]: RedBaron("[1, 2, 3]")[0].value.node_list[1].help(deep=True)
CommaNode()
# identifiers: comma, comma_, commanode
ComparisonNode¶
The node represents a comparison operation, for e.g.: 42 > 30
.
In [98]: RedBaron("42 > 30")[0].help(deep=True)
ComparisonNode()
# identifiers: comparison, comparison_, comparisonnode
first ->
IntNode()
# identifiers: int, int_, intnode
value='42'
value ->
ComparisonOperatorNode()
# identifiers: comparison_operator, comparison_operator_, comparisonoperator, comparisonoperatornode
first='>'
second=''
second ->
IntNode()
# identifiers: int, int_, intnode
value='30'
SetAttr¶
In [99]: red = RedBaron("42 > 30")
In [100]: red
Out[100]: 0 42 > 30
In [101]: red[0].operator = "=="
In [102]: red
Out[102]: 0 42 > 30
In [103]: red[0].first = "(1 + 1)"
In [104]: red
Out[104]: 0 (1 + 1) > 30
In [105]: red[0].second = "caramba"
In [106]: red
Out[106]: 0 (1 + 1) > caramba
ComprehensionIfNode¶
The node represents “if” condition in a comprehension loop. It is always a member of a ComprehensionLoopNode.
In [107]: RedBaron("[x for x in x if condition]")[0].generators[0].ifs[0].help(deep=True)
ComprehensionIfNode()
# identifiers: comprehension_if, comprehension_if_, comprehensionif, comprehensionifnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='condition'
SetAttr¶
In [108]: red = RedBaron("[x for x in x if condition]")
In [109]: red
Out[109]: 0 [x for x in x if condition]
In [110]: red[0].generators[0].ifs[0].value = "True"
In [111]: red
Out[111]: 0 [x for x in x if True]
ComprehensionLoopNode¶
The node represents the loop part of a comprehension structure.
In [112]: RedBaron("[x for y in z]")[0].generators[0].help(deep=True)
ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='y'
target ->
NameNode()
# identifiers: name, name_, namenode
value='z'
ifs ->
SetAttr¶
In [113]: red = RedBaron("[x for y in z]")
In [114]: red
Out[114]: 0 [x for y in z]
In [115]: red[0].generators[0].target = "plop"
In [116]: red
Out[116]: 0 [x for y in plop]
In [117]: red[0].generators[0].iterator = "iter"
In [118]: red
Out[118]: 0 [x for iter in plop]
In [119]: red[0].generators[0].ifs = "if a if b"
In [120]: red
Out[120]: 0 [x for iter in plop if a if b]
DecoratorNode¶
A node representing an individual decorator (of a function or a class).
In [121]: RedBaron("@stuff.plop(*a)\ndef b(): pass")[0].decorators[0].help(deep=True)
DecoratorNode()
# identifiers: decorator, decorator_, decoratornode
value ->
DottedNameNode()
# identifiers: dotted_name, dotted_name_, dottedname, dottednamenode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='stuff'
* DotNode()
# identifiers: dot, dot_, dotnode
* NameNode()
# identifiers: name, name_, namenode
value='plop'
call ->
CallNode()
# identifiers: call, call_, callnode
value ->
* ListArgumentNode()
# identifiers: list_argument, list_argument_, listargument, listargumentnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='a'
annotation ->
None
SetAttr¶
In [122]: red = RedBaron("@stuff\ndef a(): pass")
In [123]: red
Out[123]:
0 @stuff
def a(): pass
In [124]: red[0].decorators[0].value = "a.b.c"
In [125]: red
Out[125]:
0 @a.b.c
def a(): pass
In [126]: red[0].decorators[0].call = "(*args)"
In [127]: red
Out[127]:
0 @a.b.c(*args)
def a(): pass
In [128]: red[0].decorators[0].call = ""
In [129]: red
Out[129]:
0 @a.b.c
def a(): pass
DefNode¶
A node representing a function definition.
In [130]: RedBaron("def stuff():\n pass\n")[0].help(deep=True)
DefNode()
# identifiers: def, def_, defnode, funcdef, funcdef_
# default test value: name
async=False
name='stuff'
return_annotation ->
None
decorators ->
arguments ->
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
DefNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Most other attributes works as expected:
In [131]: red = RedBaron("def stuff():\n body\n")
In [132]: red[0]
Out[132]:
def stuff():
body
In [133]: red[0].name = "awesome_function"
In [134]: red[0].arguments = "a, b=None, *c, **d"
In [135]: red
Out[135]:
0 def awesome_function(a, b=None, *c, **d):
body
Decorators might be a bit less intuitive:
In [136]: red = RedBaron("def stuff():\n body\n")
In [137]: red[0].decorators = "@foo(*plop)"
In [138]: red
Out[138]:
0 @foo(*plop)
def stuff():
body
In [139]: red[0].decorators = "@foo\n@bar.baz()"
In [140]: red
Out[140]:
0 @foo
@bar.baz()
def stuff():
body
In [141]: red[0].decorators = " @pouet" # SetAttr will take care of reindenting everything as expected
In [142]: red
Out[142]:
0 @pouet
def stuff():
body
New in 0.7.
Async is a boolean attribute that determine if a function is async:
In [143]: red = RedBaron("def stuff():\n body\n")
In [144]: red[0].async_
Out[144]: False
In [145]: red[0].async_ = True
In [146]: red
Out[146]:
0 async def stuff():
body
In [147]: red[0].async_ = False
In [148]: red
Out[148]:
0 def stuff():
body
Warning
As of python 3.7 async and await are now reserved keywords so don’t uses red.async, it works as expected but won’t make your code forward compatible.
New in 0.9
Return annotation management:
In [149]: red = RedBaron("def stuff():\n return 42\n")
In [150]: red
Out[150]:
0 def stuff():
return 42
In [151]: red[0].return_annotation = "Int"
In [152]: red
Out[152]:
0 def stuff() -> Int:
return 42
In [153]: red[0].return_annotation = ""
In [154]: red
Out[154]:
0 def stuff():
return 42
DefArgumentNode¶
A node representing an argument in a function definition.
In [155]: RedBaron("def a(b, c=d): pass")[0].arguments.help(deep=True)
0 -----------------------------------------------------
DefArgumentNode()
# identifiers: def_argument, def_argument_, defargument, defargumentnode
target ->
NameNode()
# identifiers: name, name_, namenode
value='b'
annotation ->
None
value ->
None
1 -----------------------------------------------------
CommaNode()
# identifiers: comma, comma_, commanode
2 -----------------------------------------------------
DefArgumentNode()
# identifiers: def_argument, def_argument_, defargument, defargumentnode
target ->
NameNode()
# identifiers: name, name_, namenode
value='c'
annotation ->
None
value ->
NameNode()
# identifiers: name, name_, namenode
value='d'
SetAttr¶
In [156]: red = RedBaron("def a(b): pass")
In [157]: red
Out[157]:
0 def a(b): pass
In [158]: red[0].arguments[0].name = "plop"
In [159]: red
Out[159]:
0 def a(b): pass
In [160]: red[0].arguments[0].value = "1 + 1"
In [161]: red
Out[161]:
0 def a(b=1 + 1): pass
New in 0.9
Annotations:
In [162]: red = RedBaron("def a(b): pass")
In [163]: red
Out[163]:
0 def a(b): pass
In [164]: red[0].arguments[0].annotation = "Int"
In [165]: red
Out[165]:
0 def a(b : Int): pass
In [166]: red[0].arguments[0].annotation
Out[166]: Int
In [167]: red
Out[167]:
0 def a(b : Int): pass
DelNode¶
A node representing a del
statement.
In [168]: RedBaron("del stuff")[0].help(deep=True)
DelNode()
# identifiers: del, del_, delnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
SetAttr¶
In [169]: red = RedBaron("del stuff")
In [170]: red
Out[170]: 0 del stuff
In [171]: red[0].value = "some, other, stuff"
In [172]: red
Out[172]: 0 del some, other, stuff
DictArgumentNode¶
A node representing a ‘kwargs’ defined in a function definition argument or used in a CallNode.
In [173]: RedBaron("a(**b)")[0].value[1].value[0].help(deep=True)
DictArgumentNode()
# identifiers: dict_argument, dict_argument_, dictargument, dictargumentnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
annotation ->
None
SetAttr¶
In [174]: red = RedBaron("a(**b)")
In [175]: red
Out[175]: 0 a(**b)
In [176]: red[0].value[1].value[0].value = "plop"
In [177]: red
Out[177]: 0 a(**plop)
New in 0.9
Annotations:
In [178]: red = RedBaron("def a(**b): pass")
In [179]: red
Out[179]:
0 def a(**b): pass
In [180]: red[0].arguments[0].annotation = "Int"
In [181]: red
Out[181]:
0 def a(**b : Int): pass
In [182]: red[0].arguments[0].annotation
Out[182]: Int
In [183]: red
Out[183]:
0 def a(**b : Int): pass
DictNode¶
A node representing python sugar syntactic notation for dict.
In [184]: RedBaron("{'a': 1, 'b': 2, 'c': 3}")[0].help(deep=True)
DictNode()
# identifiers: dict, dict_, dictnode
value ->
* DictitemNode()
# identifiers: dictitem, dictitem_, dictitemnode
key ->
StringNode()
# identifiers: string, string_, stringnode
value="'a'"
value ->
IntNode()
# identifiers: int, int_, intnode
value='1'
* DictitemNode()
# identifiers: dictitem, dictitem_, dictitemnode
key ->
StringNode()
# identifiers: string, string_, stringnode
value="'b'"
value ->
IntNode()
# identifiers: int, int_, intnode
value='2'
* DictitemNode()
# identifiers: dictitem, dictitem_, dictitemnode
key ->
StringNode()
# identifiers: string, string_, stringnode
value="'c'"
value ->
IntNode()
# identifiers: int, int_, intnode
value='3'
DictComprehensionNode¶
A node representing dictionary comprehension node.
In [185]: RedBaron("{a: b for c in d}")[0].help(deep=True)
DictComprehensionNode()
# identifiers: dict_comprehension, dict_comprehension_, dictcomprehension, dictcomprehensionnode
result ->
DictitemNode()
# identifiers: dictitem, dictitem_, dictitemnode
key ->
NameNode()
# identifiers: name, name_, namenode
value='a'
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
generators ->
* ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='c'
target ->
NameNode()
# identifiers: name, name_, namenode
value='d'
ifs ->
SetAttr¶
In [186]: red = RedBaron("{a: b for c in d}")
In [187]: red
Out[187]: 0 {a: b for c in d}
In [188]: red[0].result = "plop: poulpe"
In [189]: red
Out[189]: 0 {plop: poulpe for c in d}
In [190]: red[0].generators = "for zomg in wtf"
In [191]: red
Out[191]: 0 {plop: poulpe for zomg in wtf}
DottedAsNameNode¶
A node representing an argument to the import node.
In [192]: RedBaron("import a.b.c as d")[0].value[0].help(deep=True)
DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target='d'
value ->
* NameNode()
# identifiers: name, name_, namenode
value='a'
* NameNode()
# identifiers: name, name_, namenode
value='b'
* NameNode()
# identifiers: name, name_, namenode
value='c'
SetAttr¶
In [193]: red = RedBaron("import a.b.c as d")
In [194]: red
Out[194]: 0 import a.b.c as d
In [195]: red[0].value[0].value = "some.random.module"
In [196]: red
Out[196]: 0 import some.random.module as d
In [197]: red[0].value[0].target = "stuff"
In [198]: red
Out[198]: 0 import some.random.module as stuff
DotNode¶
A node representing a dot ‘.’, generally found in atom trailers (this kind of structure: ‘variable.another_variable(call)[getitem]’). This is the kind of formatting node that you might have to deal with if not enough high level helpers are available.
The dot node is responsible for holding the formatting around it.
In [199]: RedBaron("a.b")[0].value[1].help(deep=True)
NameNode()
# identifiers: name, name_, namenode
value='b'
ElifNode¶
A node representing an elif statement.
The ElifNode, like the IfNode or the ElseNode are stored in a IfelseblockNode.
In [200]: RedBaron("if a: pass\nelif b: pass")[0].value[1].help(deep=True)
ElifNode()
# identifiers: elif, elif_, elifnode
test ->
NameNode()
# identifiers: name, name_, namenode
value='b'
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
ElifNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Other attributes work as expected:
In [201]: red = RedBaron("if a: pass\nelif b: pass")
In [202]: red
Out[202]:
0 if a: pass
elif b: pass
In [203]: red[0].value[1].test = "1 + 1 == 11"
In [204]: red
Out[204]:
0 if a: pass
elif 1 + 1 == 11: pass
ElseNode¶
A node representing an else statement.
The ElseNode, like the IfNode or the ElifNode, is stored in a IfelseblockNode.
In [205]: RedBaron("if a: pass\nelse: pass")[0].value[1].help(deep=True)
ElseNode()
# identifiers: else, else_, elsenode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
ElifNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information.
EllipsisNode¶
A node representing “…”.
In [206]: RedBaron("def a(): ...").ellipsis.help(deep=True)
EllipsisNode()
# identifiers: ellipsis, ellipsis_, ellipsisnode
EndlNode¶
A node for the end line (‘n’, ‘rn’) component.
This node is responsible for holding the indentation AFTER itself. This node also handles formatting around it, CommentNode before an EndlNode will end up in the formatting key of an EndlNode 99% of the time (the exception is if the CommentNode is the last node of the file).
In [207]: RedBaron("\n")[0].help()
EndlNode()
# identifiers: endl, endl_, endlnode
value='\n'
indent=''
In [208]: RedBaron("# first node of the file\n# last node of the file").node_list.help()
0 -----------------------------------------------------
CommentNode()
# identifiers: comment, comment_, commentnode
value='# first node of the file'
1 -----------------------------------------------------
EndlNode()
# identifiers: endl, endl_, endlnode
value='\n'
indent=''
2 -----------------------------------------------------
CommentNode()
# identifiers: comment, comment_, commentnode
value='# last node of the file'
ExceptNode¶
A node representing an except statement (member of a TryNode).
In [209]: RedBaron("try: pass\nexcept FooBar: pass\nexcept Exception: pass\nelse: pass\nfinally: pass\n")[0].excepts[0].help(deep=True)
ExceptNode()
# identifiers: except, except_, exceptnode
delimiter=''
exception ->
NameNode()
# identifiers: name, name_, namenode
value='FooBar'
target ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
ExceptNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Other attributes work as expected:
In [210]: red = RedBaron("try: pass\nexcept: pass")
In [211]: red
Out[211]:
0 try: pass
except: pass
In [212]: red[0].excepts[0].exception = "plop"
In [213]: red
Out[213]:
0 try: pass
except plop: pass
In [214]: red[0].excepts[0].target = "stuff"
In [215]: red
Out[215]:
0 try: pass
except plop as stuff: pass
In [216]: red[0].excepts[0].exception = ""
In [217]: red
Out[217]:
0 try: pass
except: pass
# red[0].excepts[0].target = "stuff" <- would raise without a target
ExecNode¶
A node representing an exec statement.
In [218]: RedBaron("exec '1 + 1' in a, b")[0].help(deep=True)
ExecNode()
# identifiers: exec, exec_, execnode
value ->
StringNode()
# identifiers: string, string_, stringnode
value="'1 + 1'"
globals ->
NameNode()
# identifiers: name, name_, namenode
value='a'
locals ->
NameNode()
# identifiers: name, name_, namenode
value='b'
SetAttr¶
In [219]: red = RedBaron("exec 'stuff'")
In [220]: red
Out[220]: 0 exec 'stuff'
In [221]: red[0].value = 'some_code'
In [222]: red
Out[222]: 0 exec some_code
In [223]: red[0].globals = 'x'
In [224]: red
Out[224]: 0 exec some_code in x
In [225]: red[0].locals = 'y'
In [226]: red
Out[226]: 0 exec some_code in x, y
FinallyNode¶
A node representing a finally statement (member of a TryNode).
In [227]: RedBaron("try: pass\nexcept FooBar: pass\nexcept Exception: pass\nelse: pass\nfinally: pass\n").finally_.help(deep=True)
FinallyNode()
# identifiers: finally, finally_, finallynode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
FinallyNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information.
ForNode¶
A node representing a for loop.
In [228]: RedBaron("for i in b:\n pass")[0].help(deep=True)
ForNode()
# identifiers: for, for_, fornode
async=False
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='i'
target ->
NameNode()
# identifiers: name, name_, namenode
value='b'
else ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
ForNode is a CodeBlockNode which means its value attribute accepts a wide range
of values, see CodeBlockNode for more information. The else attributes
accept a great ranges of inputs, since else
is a reserved python
keyword, you need to access it using the else_
attribute. Other
attributes work as expected:
In [229]: red = RedBaron("for i in b: pass")
In [230]: red
Out[230]:
0 for i in b: pass
In [231]: red[0].iterator = "i, j, k"
In [232]: red
Out[232]:
0 for i, j, k in b: pass
In [233]: red[0].target = "[x for x in stuff if condition]"
In [234]: red
Out[234]:
0 for i, j, k in [x for x in stuff if condition]: pass
In [235]: red[0].else_ = "do_stuff"
In [236]: red
Out[236]:
0 for i, j, k in [x for x in stuff if condition]: pass
else:
do_stuff
In [237]: red[0].else_ = "else: foobar"
In [238]: red
Out[238]:
0 for i, j, k in [x for x in stuff if condition]: pass
else:
foobar
In [239]: red[0].else_ = " else:\n badly_indented_and_trailing\n\n\n\n"
In [240]: red
Out[240]:
0 for i, j, k in [x for x in stuff if condition]: pass
else:
badly_indented_and_trailing
New in 0.8.
Async is a boolean attribute that determine if a function is async:
In [241]: red = RedBaron("for a in b: pass")
In [242]: red[0].async_
Out[242]: False
In [243]: red[0].async_ = True
In [244]: red
Out[244]:
0 async for a in b: pass
In [245]: red[0].async_ = False
In [246]: red
Out[246]:
0 for a in b: pass
Warning
As of python 3.7 async and await are now reserved keywords so don’t uses red.async, it works as expected but won’t make your code forward compatible.
FromImportNode¶
A node representing a “from import” statement.
In [247]: RedBaron("from a import b")[0].help(deep=True)
FromImportNode()
# identifiers: from_import, from_import_, fromimport, fromimportnode
# helpers: full_path_modules, full_path_names, modules, names
value ->
* NameNode()
# identifiers: name, name_, namenode
value='a'
targets ->
* NameAsNameNode()
# identifiers: name_as_name, name_as_name_, nameasname, nameasnamenode
value='b'
target=''
SetAttr¶
In [248]: red = RedBaron("from a import b")
In [249]: red
Out[249]: 0 from a import b
In [250]: red[0].value = "some.module"
In [251]: red
Out[251]: 0 from some.module import b
In [252]: red[0].targets = "a as b, c as d, e"
In [253]: red
Out[253]: 0 from some.module import a as b, c as d, e
Helpers¶
To reduce the complexity, 2 helpers method are provided:
In [254]: red = RedBaron("from foo.bar import baz as stuff, plop")
In [255]: red[0].names() # names added to the context
Out[255]: ['stuff', 'plop']
In [256]: red[0].modules() # modules imported
Out[256]: ['baz', 'plop']
In [257]: red[0].full_path_names() # names added to the context with full path
Out[257]: ['foo.bar.stuff', 'foo.bar.plop']
In [258]: red[0].full_path_modules() # modules imported with full path
Out[258]: ['foo.bar.baz', 'foo.bar.plop']
GeneratorComprehensionNode¶
A node representing a generator comprehension node.
In [259]: RedBaron("(x for y in z)")[0].help(deep=True)
GeneratorComprehensionNode()
# identifiers: generator_comprehension, generator_comprehension_, generatorcomprehension, generatorcomprehensionnode
result ->
NameNode()
# identifiers: name, name_, namenode
value='x'
generators ->
* ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='y'
target ->
NameNode()
# identifiers: name, name_, namenode
value='z'
ifs ->
SetAttr¶
In [260]: red = RedBaron("(x for y in z)")
In [261]: red
Out[261]: 0 (x for y in z)
In [262]: red[0].result = "pouet"
In [263]: red
Out[263]: 0 (pouet for y in z)
In [264]: red[0].generators = "for artichaut in courgette"
In [265]: red
Out[265]: 0 (pouet for artichaut in courgette)
GetitemNode¶
A node representing a ‘get item’ access on a python object, in other words the ‘[stuff]’ in ‘some_object[stuff]’.
In [266]: RedBaron("a[b]")[0].value[1].help(deep=True)
GetitemNode()
# identifiers: getitem, getitem_, getitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
SetAttr¶
In [267]: red = RedBaron("a[b]")
In [268]: red
Out[268]: 0 a[b]
In [269]: red[0].value[1].value = "1 + 1"
In [270]: red
Out[270]: 0 a[1 + 1]
GlobalNode¶
A node representing a global statement.
In [271]: RedBaron("global a")[0].help(deep=True)
GlobalNode()
# identifiers: global, global_, globalnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='a'
SetAttr¶
In [272]: red = RedBaron("global a")
In [273]: red
Out[273]: 0 global a
In [274]: red[0].value = "stuff, plop"
In [275]: red
Out[275]: 0 global stuff, plop
IfNode¶
A node representing an if statement.
The IfNode, like the ElifNode or the ElseNode, is stored in an IfelseblockNode.
In [276]: RedBaron("if a: pass")[0].value[0].help(deep=True)
IfNode()
# identifiers: if, if_, ifnode
test ->
NameNode()
# identifiers: name, name_, namenode
value='a'
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
IfNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Other attributes work as expected:
In [277]: red = RedBaron("if a: pass")
In [278]: red
Out[278]:
0 if a: pass
In [279]: red[0].value[0].test = "1 + 1 == 11"
In [280]: red
Out[280]:
0 if 1 + 1 == 11: pass
IfelseblockNode¶
A node representing the conditional block composed of at least one if statement, zero or more elif statements and, at the end, an optional else statement. All those statements are stored in a list.
In [281]: RedBaron("if a: pass\nelif b: pass\nelse: pass\n")[0].help(deep=True)
IfelseblockNode()
# identifiers: ifelseblock, ifelseblock_, ifelseblocknode
value ->
* IfNode()
# identifiers: if, if_, ifnode
test ->
NameNode()
# identifiers: name, name_, namenode
value='a'
value ->
* PassNode()
# identifiers: pass, pass_, passnode
* ElifNode()
# identifiers: elif, elif_, elifnode
test ->
NameNode()
# identifiers: name, name_, namenode
value='b'
value ->
* PassNode()
# identifiers: pass, pass_, passnode
* ElseNode()
# identifiers: else, else_, elsenode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
Works as expected and is very flexible on its input:
- the input is automatically put at the correct indentation
- the input is automatically right strip
- if the statement is followed, the correct number of blanks lines are added: 2 when at the root of the file, 1 when indented
In [282]: red = RedBaron("if a: pass\n")
In [283]: red
Out[283]:
0 if a: pass
In [284]: red[0].value = "if a:\n pass\nelif b:\n pass\n\n\n"
In [285]: red
Out[285]:
0 if a:
pass
elif b:
pass
In [286]: red[0].value = " if a:\n pass"
In [287]: red
Out[287]:
0 if a:
pass
In [288]: red = RedBaron("if a:\n pass\n\n\nplop")
In [289]: red
Out[289]:
0 if a:
pass
1 plop
In [290]: red[0].value = " if a:\n pass"
In [291]: red
Out[291]:
0 if a:
pass
1 plop
In [292]: red = RedBaron("while True:\n if plop:\n break\n\n stuff")
In [293]: red
Out[293]:
0 while True:
if plop:
break
stuff
In [294]: red[0].value[1].value = "if a:\n pass\nelif b:\n pass\n\n\n"
In [295]: red
Out[295]:
0 while True:
if plop:
break
if a:
pass
elif b:
pass
ImportNode¶
A node representing the import statement of the python language.
Be careful, this node and its subnodes are way more complex than what you can expect.
In [296]: RedBaron("import foo")[0].help(deep=True)
ImportNode()
# identifiers: import, import_, importnode
# helpers: modules, names
value ->
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target=''
value ->
* NameNode()
# identifiers: name, name_, namenode
value='foo'
In [297]: RedBaron("import foo.bar.baz as stuff, another_thing.plop")[0].help(deep=True)
ImportNode()
# identifiers: import, import_, importnode
# helpers: modules, names
value ->
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target='stuff'
value ->
* NameNode()
# identifiers: name, name_, namenode
value='foo'
* NameNode()
# identifiers: name, name_, namenode
value='bar'
* NameNode()
# identifiers: name, name_, namenode
value='baz'
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target=''
value ->
* NameNode()
# identifiers: name, name_, namenode
value='another_thing'
* NameNode()
# identifiers: name, name_, namenode
value='plop'
SetAttr¶
Works as expected:
In [298]: red = RedBaron("import foo")
In [299]: red[0].value = "foo.bar.baz as plop, stuff, plop.dot"
In [300]: red
Out[300]: 0 import foo.bar.baz as plop, stuff, plop.dot
In [301]: red.help(deep=True)
0 -----------------------------------------------------
ImportNode()
# identifiers: import, import_, importnode
# helpers: modules, names
value ->
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target='plop'
value ->
* NameNode()
# identifiers: name, name_, namenode
value='foo'
* NameNode()
# identifiers: name, name_, namenode
value='bar'
* NameNode()
# identifiers: name, name_, namenode
value='baz'
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target=''
value ->
* NameNode()
# identifiers: name, name_, namenode
value='stuff'
* DottedAsNameNode()
# identifiers: dotted_as_name, dotted_as_name_, dottedasname, dottedasnamenode
target=''
value ->
* NameNode()
# identifiers: name, name_, namenode
value='plop'
* NameNode()
# identifiers: name, name_, namenode
value='dot'
Helpers¶
To reduce the complexity, 2 helpers method are provided:
In [302]: red = RedBaron("import foo.bar.baz as stuff, another_thing.plop")
In [303]: red[0].modules() # modules imported
Out[303]: ['foo.bar.baz', 'another_thing.plop']
In [304]: red[0].names() # names added to the context
Out[304]: ['stuff', 'another_thing.plop']
IntNode¶
A python integer.
In [305]: RedBaron("42")[0].help()
IntNode()
# identifiers: int, int_, intnode
value='42'
KwargsOnlyMarkerNode¶
New in 0.7.
A node representing the “*” in arguments declaration to force keywords only arguments after itself.
In [306]: RedBaron("def a(*): pass")[0].arguments[0].help(deep=True)
KwargsOnlyMarkerNode()
# identifiers: kwargs_only_marker, kwargs_only_marker_, kwargsonlymarker, kwargsonlymarkernode
LambdaNode¶
A node representing a lambda statement.
In [307]: RedBaron("lambda x: y")[0].help(deep=True)
LambdaNode()
# identifiers: lambda, lambda_, lambdanode
value ->
NameNode()
# identifiers: name, name_, namenode
value='y'
arguments ->
* DefArgumentNode()
# identifiers: def_argument, def_argument_, defargument, defargumentnode
target ->
NameNode()
# identifiers: name, name_, namenode
value='x'
annotation ->
None
value ->
None
SetAttr¶
Works as expected:
In [308]: red = RedBaron("lambda x: y")
In [309]: red
Out[309]: 0 lambda x: y
In [310]: red[0].arguments = "a, b=c, *d, **f"
In [311]: red
Out[311]: 0 lambda a, b=c, *d, **f: y
In [312]: red[0].value = "plop"
In [313]: red
Out[313]: 0 lambda a, b=c, *d, **f: plop
ListArgumentNode¶
A node representing a “star argument” in a function call or definition.
In [314]: RedBaron("def a(*b): pass")[0].arguments[0].help(deep=True)
ListArgumentNode()
# identifiers: list_argument, list_argument_, listargument, listargumentnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
annotation ->
None
SetAttr¶
Works as expected:
In [315]: red = RedBaron("def a(*b): pass")
In [316]: red
Out[316]:
0 def a(*b): pass
In [317]: red[0].arguments[0].value = "plop"
In [318]: red
Out[318]:
0 def a(*plop): pass
New in 0.9
Annotations:
In [319]: red = RedBaron("def a(*b): pass")
In [320]: red
Out[320]:
0 def a(*b): pass
In [321]: red[0].arguments[0].annotation = "Int"
In [322]: red
Out[322]:
0 def a(*b : Int): pass
In [323]: red[0].arguments[0].annotation
Out[323]: Int
In [324]: red
Out[324]:
0 def a(*b : Int): pass
ListComprehensionNode¶
A node representing a list comprehension node.
In [325]: RedBaron("[x for y in z]")[0].help(deep=True)
ListComprehensionNode()
# identifiers: list_comprehension, list_comprehension_, listcomprehension, listcomprehensionnode
result ->
NameNode()
# identifiers: name, name_, namenode
value='x'
generators ->
* ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='y'
target ->
NameNode()
# identifiers: name, name_, namenode
value='z'
ifs ->
SetAttr¶
In [326]: red = RedBaron("[x for y in z]")
In [327]: red
Out[327]: 0 [x for y in z]
In [328]: red[0].result = "pouet"
In [329]: red
Out[329]: 0 [pouet for y in z]
In [330]: red[0].generators = "for artichaut in courgette"
In [331]: red
Out[331]: 0 [pouet for artichaut in courgette]
ListNode¶
A node representing python sugar syntactic notation for list.
In [332]: RedBaron("[1, 2, 3]")[0].help(deep=True)
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'
NameAsNameNode¶
A node representing an argument to the from import statement.
In [333]: RedBaron("from x import a as d")[0].targets[0].help(deep=True)
NameAsNameNode()
# identifiers: name_as_name, name_as_name_, nameasname, nameasnamenode
value='a'
target='d'
SetAttr¶
In [334]: red = RedBaron("from x import a as d")
In [335]: red
Out[335]: 0 from x import a as d
In [336]: red[0].targets[0].value = "some_random_module"
In [337]: red
Out[337]: 0 from x import some_random_module as d
In [338]: red[0].targets[0].target = "stuff"
In [339]: red
Out[339]: 0 from x import some_random_module as stuff
NonlocalNode¶
New in 0.7.
A node representing a nonlocal statement.
In [340]: RedBaron("nonlocal a")[0].help(deep=True)
NonlocalNode()
# identifiers: nonlocal, nonlocal_, nonlocalnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='a'
SetAttr¶
In [341]: red = RedBaron("nonlocal a")
In [342]: red
Out[342]: 0 nonlocal a
In [343]: red[0].value = "stuff, plop"
In [344]: red
Out[344]: 0 nonlocal stuff, plop
PrintNode¶
A node representing a print statement.
In [345]: RedBaron("print(stuff)")[0].help(deep=True)
PrintNode()
# identifiers: print, print_, printnode
destination ->
None
value ->
* AssociativeParenthesisNode()
# identifiers: associative_parenthesis, associative_parenthesis_, associativeparenthesis, associativeparenthesisnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
SetAttr¶
In [346]: red = RedBaron("print(stuff)")
In [347]: red
Out[347]: 0 print(stuff)
In [348]: red[0].destination = "some_file"
In [349]: red
Out[349]: 0 print >>some_file, (stuff)
In [350]: red[0].value = "a, b, c"
In [351]: red
Out[351]: 0 print >>some_file, a, b, c
In [352]: red[0].destination = ""
In [353]: red
Out[353]: 0 print a, b, c
In [354]: red[0].value = ""
In [355]: red
Out[355]: 0 print
RaiseNode¶
A node representing a raise statement.
In [356]: RedBaron("raise Exception(), foo, bar")[0].help(deep=True)
RaiseNode()
# identifiers: raise, raise_, raisenode
comma_or_from=','
value ->
AtomtrailersNode()
# identifiers: atomtrailers, atomtrailers_, atomtrailersnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='Exception'
* CallNode()
# identifiers: call, call_, callnode
value ->
instance ->
NameNode()
# identifiers: name, name_, namenode
value='foo'
traceback ->
NameNode()
# identifiers: name, name_, namenode
value='bar'
SetAttr¶
In [357]: red = RedBaron("raise stuff")
In [358]: red
Out[358]: 0 raise stuff
In [359]: red[0].value = "foo"
In [360]: red
Out[360]: 0 raise foo
In [361]: red[0].instance = "bar"
In [362]: red
Out[362]: 0 raise foo, bar
In [363]: red[0].traceback = "baz"
In [364]: red
Out[364]: 0 raise foo, bar, baz
New in 0.9
How to deal with the “raise from” notation: (by default a comma is inserted to avoid breaking backward compatibility)
In [365]: red = RedBaron("raise stuff")
In [366]: red
Out[366]: 0 raise stuff
In [367]: red[0].instance = "foo"
In [368]: red
Out[368]: 0 raise stuff, foo
In [369]: red[0].comma_or_from = "from"
In [370]: red
Out[370]: 0 raise stuff from foo
In [371]: red[0].comma_or_from = ","
In [372]: red
Out[372]: 0 raise stuff, foo
In [373]: red[0].instance = ""
In [374]: red
Out[374]: 0 raise stuff
ReprNode¶
A node representing python sugar syntactic notation for repr.
In [375]: RedBaron("`pouet`")[0].help(deep=True)
ReprNode()
# identifiers: repr, repr_, reprnode
value ->
* NameNode()
# identifiers: name, name_, namenode
value='pouet'
ReturnNode¶
A node representing a return statement.
In [376]: RedBaron("return stuff")[0].help(deep=True)
ReturnNode()
# identifiers: return, return_, returnnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='stuff'
SetAttr¶
In [377]: red = RedBaron("return stuff")
In [378]: red
Out[378]: 0 return stuff
In [379]: red[0].value = "1 + 1"
In [380]: red
Out[380]: 0 return 1 + 1
In [381]: red[0].value = ""
In [382]: red
Out[382]: 0 return
SetNode¶
A node representing python sugar syntactic notation for set.
In [383]: RedBaron("{1, 2, 3}")[0].help(deep=True)
SetNode()
# identifiers: set, set_, setnode
value ->
* IntNode()
# identifiers: int, int_, intnode
value='1'
* IntNode()
# identifiers: int, int_, intnode
value='2'
* IntNode()
# identifiers: int, int_, intnode
value='3'
SetComprehensionNode¶
A node representing a set comprehension node.
In [384]: RedBaron("{x for y in z}")[0].help(deep=True)
SetComprehensionNode()
# identifiers: set_comprehension, set_comprehension_, setcomprehension, setcomprehensionnode
result ->
NameNode()
# identifiers: name, name_, namenode
value='x'
generators ->
* ComprehensionLoopNode()
# identifiers: comprehension_loop, comprehension_loop_, comprehensionloop, comprehensionloopnode
iterator ->
NameNode()
# identifiers: name, name_, namenode
value='y'
target ->
NameNode()
# identifiers: name, name_, namenode
value='z'
ifs ->
SetAttr¶
In [385]: red = RedBaron("{x for y in z}")
In [386]: red
Out[386]: 0 {x for y in z}
In [387]: red[0].result = "pouet"
In [388]: red
Out[388]: 0 {pouet for y in z}
In [389]: red[0].generators = "for artichaut in courgette"
In [390]: red
Out[390]: 0 {pouet for artichaut in courgette}
SliceNode¶
A node representing a slice, the “1:2:3” that can be found in a GetitemNode.
In [391]: RedBaron("a[1:-1:2]")[0].value[1].value.help(deep=True)
SliceNode()
# identifiers: slice, slice_, slicenode
has_two_colons=True
lower ->
IntNode()
# identifiers: int, int_, intnode
value='1'
upper ->
UnitaryOperatorNode()
# identifiers: unitary_operator, unitary_operator_, unitaryoperator, unitaryoperatornode
value='-'
target ->
IntNode()
# identifiers: int, int_, intnode
value='1'
step ->
IntNode()
# identifiers: int, int_, intnode
value='2'
SetAttr¶
In [392]: red = RedBaron("a[1:-1:2]")
In [393]: red
Out[393]: 0 a[1:-1:2]
In [394]: red[0].value[1].value.lower = "a"
In [395]: red
Out[395]: 0 a[a:-1:2]
In [396]: red[0].value[1].value.upper = "b"
In [397]: red
Out[397]: 0 a[a:b:2]
In [398]: red[0].value[1].value.step = "stuff"
In [399]: red
Out[399]: 0 a[a:b:stuff]
In [400]: red[0].value[1].value.step = ""
In [401]: red
Out[401]: 0 a[a:b]
SpaceNode¶
A formatting node representing a space. You’ll probably never have to deal with it except if you play with the way the file is rendered.
Those nodes will be hidden in formatting keys 99% of the time (the only exception is if it’s the last node of the file).
In [402]: RedBaron("1 + 1")[0].first_formatting[0].help()
SpaceNode()
# identifiers: space, space_, spacenode
value=' '
In [403]: RedBaron("1 + 1").help()
0 -----------------------------------------------------
BinaryOperatorNode()
# identifiers: binary_operator, binary_operator_, binaryoperator, binaryoperatornode
value='+'
first ->
IntNode()
# identifiers: int, int_, intnode
value='1'
second ->
IntNode()
# identifiers: int, int_, intnode
value='1'
StarExpressionNode¶
New in 0.9
A node representing the result of a deconstruction in an assignment.
In [404]: red = RedBaron("a, *b = c")
In [405]: red
Out[405]: 0 a, *b = c
In [406]: red[0].target[1].help()
StarExpressionNode()
# identifiers: star_expression, star_expression_, starexpression, starexpressionnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
StringChainNode¶
This is a special node that handle a particular way of writing a single string in python by putting several strings one after the other while only separated by spaces or endls.
In [407]: RedBaron("'a' r'b' b'c'")[0].help(deep=True)
StringChainNode()
# identifiers: string_chain, string_chain_, stringchain, stringchainnode
value ->
* StringNode()
# identifiers: string, string_, stringnode
value="'a'"
* RawStringNode()
# identifiers: raw_string, raw_string_, rawstring, rawstringnode
value="r'b'"
* BinaryStringNode()
# identifiers: binary_string, binary_string_, binarystring, binarystringnode
value="b'c'"
SetAttr¶
In [408]: red = RedBaron("'a' r'b' b'c'")
In [409]: red
Out[409]: 0 'a' r'b' b'c'
In [410]: red[0].value = "'plip' 'plop'"
In [411]: red
Out[411]: 0 'plip' 'plop'
TernaryOperatorNode¶
A node representing the ternary operator expression.
In [412]: RedBaron("a if b else c")[0].help(deep=True)
TernaryOperatorNode()
# identifiers: ternary_operator, ternary_operator_, ternaryoperator, ternaryoperatornode
first ->
NameNode()
# identifiers: name, name_, namenode
value='a'
value ->
NameNode()
# identifiers: name, name_, namenode
value='b'
second ->
NameNode()
# identifiers: name, name_, namenode
value='c'
SetAttr¶
In [413]: red = RedBaron("a if b else c")
In [414]: red
Out[414]: 0 a if b else c
In [415]: red[0].value = "some_test"
In [416]: red
Out[416]: 0 a if some_test else c
In [417]: red[0].first = "a_value"
In [418]: red
Out[418]: 0 a_value if some_test else c
In [419]: red[0].second = "another_value"
In [420]: red
Out[420]: 0 a_value if some_test else another_value
TryNode¶
A node representing a try statement. This node is responsible for holding the ExceptNode, FinallyNode and ElseNode.
In [421]: RedBaron("try: pass\nexcept FooBar: pass\nexcept Exception: pass\nelse: pass\nfinally: pass\n")[0].help(deep=True)
TryNode()
# identifiers: try, try_, trynode
else ->
ElseNode()
# identifiers: else, else_, elsenode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
finally ->
FinallyNode()
# identifiers: finally, finally_, finallynode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
value ->
* PassNode()
# identifiers: pass, pass_, passnode
excepts ->
* ExceptNode()
# identifiers: except, except_, exceptnode
delimiter=''
exception ->
NameNode()
# identifiers: name, name_, namenode
value='FooBar'
target ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
* ExceptNode()
# identifiers: except, except_, exceptnode
delimiter=''
exception ->
NameNode()
# identifiers: name, name_, namenode
value='Exception'
target ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
TryNode is a CodeBlockNode which means its value attribute accepts a wide range
of values, see CodeBlockNode for more information. For the else
and the finally
and the excepts
attributes, TryNode is very
flexible on the range of inputs it can get, like for a CodeBlockNode value’s
attribute.
Important: Since else
and finally
are reserved keywords in python, you
need to append a _
to those attributes name to access/modify them:
node.else_
and node.finally_
.
In [422]: red = RedBaron("try:\n pass\nexcept:\n pass\n")
In [423]: red
Out[423]:
0 try:
pass
except:
pass
In [424]: red[0].else_ = "do_stuff"
In [425]: red
Out[425]:
0 try:
pass
except:
pass
else:
do_stuff
In [426]: red[0].else_ = "else: foobar"
In [427]: red
Out[427]:
0 try:
pass
except:
pass
else:
foobar
In [428]: red[0].else_ = " else:\n badly_indented_and_trailing\n\n\n\n"
In [429]: red
Out[429]:
0 try:
pass
except:
pass
else:
badly_indented_and_trailing
# input management of finally_ works the same way than for else_
In [430]: red[0].finally_ = "close_some_stuff"
In [431]: red
Out[431]:
0 try:
pass
except:
pass
else:
badly_indented_and_trailing
finally:
close_some_stuff
In [432]: red[0].else_ = ""
In [433]: red
Out[433]:
0 try:
pass
except:
pass
finally:
close_some_stuff
In [434]: red[0].finally_ = ""
In [435]: red
Out[435]:
0 try:
pass
except:
pass
In [436]: red[0].excepts = "except A as b:\n pass"
In [437]: red
Out[437]:
0 try:
pass
except A as b:
pass
In [438]: red[0].excepts = "except X:\n pass\nexcept Y:\n pass"
In [439]: red
Out[439]:
0 try:
pass
except X:
pass
except Y:
pass
# You **CAN'T** do this red[0].excepts = "foobar"
TupleNode¶
A node representing python sugar syntactic notation for tuple.
In [440]: RedBaron("(1, 2, 3)")[0].help(deep=True)
TupleNode()
# identifiers: tuple, tuple_, tuplenode
with_parenthesis=True
value ->
* IntNode()
# identifiers: int, int_, intnode
value='1'
* IntNode()
# identifiers: int, int_, intnode
value='2'
* IntNode()
# identifiers: int, int_, intnode
value='3'
UnitaryOperatorNode¶
A node representing a number sign modification operator like -2
or +42
.
In [441]: RedBaron("-1")[0].help(deep=True)
UnitaryOperatorNode()
# identifiers: unitary_operator, unitary_operator_, unitaryoperator, unitaryoperatornode
value='-'
target ->
IntNode()
# identifiers: int, int_, intnode
value='1'
SetAttr¶
In [442]: red = RedBaron("-1")
In [443]: red
Out[443]: 0 -1
In [444]: red[0].target = "42"
In [445]: red
Out[445]: 0 -42
In [446]: red[0].value = "+"
In [447]: red
Out[447]: 0 +42
YieldNode¶
A node representing a yield statement.
In [448]: RedBaron("yield 42")[0].help(deep=True)
YieldNode()
# identifiers: yield, yield_, yieldnode
value ->
IntNode()
# identifiers: int, int_, intnode
value='42'
SetAttr¶
In [449]: red = RedBaron("yield 42")
In [450]: red
Out[450]: 0 yield 42
In [451]: red[0].value = "stuff"
In [452]: red
Out[452]: 0 yield stuff
In [453]: red[0].value = ""
In [454]: red
Out[454]: 0 yield
YieldAtomNode¶
A node representing a yield statement surrounded by parenthesis.
In [455]: RedBaron("(yield 42)")[0].help(deep=True)
YieldAtomNode()
# identifiers: yield_atom, yield_atom_, yieldatom, yieldatomnode
value ->
IntNode()
# identifiers: int, int_, intnode
value='42'
SetAttr¶
In [456]: red = RedBaron("(yield 42)")
In [457]: red
Out[457]: 0 (yield 42)
In [458]: red[0].value = "stuff"
In [459]: red
Out[459]: 0 (yield stuff)
In [460]: red[0].value = ""
In [461]: red
Out[461]: 0 (yield)
YieldFromNode¶
New in 0.7.
A node representing a “yield from” statement.
In [462]: RedBaron("yield from 42")[0].help(deep=True)
YieldFromNode()
# identifiers: yield_from, yield_from_, yieldfrom, yieldfromnode
value ->
IntNode()
# identifiers: int, int_, intnode
value='42'
SetAttr¶
In [463]: red = RedBaron("yield from 42")
In [464]: red
Out[464]: 0 yield from 42
In [465]: red[0].value = "stuff"
In [466]: red
Out[466]: 0 yield from stuff
WhileNode¶
A node representing a while loop.
In [467]: RedBaron("while condition:\n pass")[0].help(deep=True)
WhileNode()
# identifiers: while, while_, whilenode
test ->
NameNode()
# identifiers: name, name_, namenode
value='condition'
else ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
WhileNode is a CodeBlockNode which means its value attribute accepts a wide
range of values, see CodeBlockNode for more information. The else
attributes accept a great ranges of inputs, since else
is a reserved
python keyword, you need to access it using the else_
attribute. Other
attributes work as expected:
In [468]: red = RedBaron("while condition: pass")
In [469]: red
Out[469]:
0 while condition: pass
In [470]: red[0].test = "a is not None"
In [471]: red
Out[471]:
0 while a is not None: pass
In [472]: red[0].else_ = "do_stuff"
In [473]: red
Out[473]:
0 while a is not None: pass
else:
do_stuff
In [474]: red[0].else_ = "else: foobar"
In [475]: red
Out[475]:
0 while a is not None: pass
else:
foobar
In [476]: red[0].else_ = " else:\n badly_indented_and_trailing\n\n\n\n"
In [477]: red
Out[477]:
0 while a is not None: pass
else:
badly_indented_and_trailing
WithContext¶
A node representing a with statement.
In [478]: RedBaron("with a: pass")[0].help(deep=True)
WithNode()
# identifiers: with, with_, withnode
async=False
contexts ->
* WithContextItemNode()
# identifiers: with_context_item, with_context_item_, withcontextitem, withcontextitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='a'
as ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
WithContextItemNode¶
A node representing one of the context manager items in a with statement.
In [479]: RedBaron("with a as b: pass")[0].contexts[0].help(deep=True)
WithContextItemNode()
# identifiers: with_context_item, with_context_item_, withcontextitem, withcontextitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='a'
as ->
NameNode()
# identifiers: name, name_, namenode
value='b'
SetAttr¶
In [480]: red = RedBaron("with a: pass")
In [481]: red
Out[481]:
0 with a: pass
In [482]: red[0].contexts[0].value = "plop"
In [483]: red
Out[483]:
0 with plop: pass
In [484]: red[0].contexts[0].as_ = "stuff"
In [485]: red
Out[485]:
0 with plop as stuff: pass
In [486]: red[0].contexts[0].as_ = ""
In [487]: red
Out[487]:
0 with plop: pass
WithNode¶
A node representing a with statement.
In [488]: RedBaron("with a as b, c: pass")[0].help(deep=True)
WithNode()
# identifiers: with, with_, withnode
async=False
contexts ->
* WithContextItemNode()
# identifiers: with_context_item, with_context_item_, withcontextitem, withcontextitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='a'
as ->
NameNode()
# identifiers: name, name_, namenode
value='b'
* WithContextItemNode()
# identifiers: with_context_item, with_context_item_, withcontextitem, withcontextitemnode
value ->
NameNode()
# identifiers: name, name_, namenode
value='c'
as ->
None
value ->
* PassNode()
# identifiers: pass, pass_, passnode
SetAttr¶
WithNode is a CodeBlockNode which means its value attribute accepts a wide range of values, see CodeBlockNode for more information. Other attributes work as expected:
In [489]: red = RedBaron("with a: pass")
In [490]: red
Out[490]:
0 with a: pass
In [491]: red[0].contexts = "b as plop, stuff()"
In [492]: red
Out[492]:
0 with b as plop, stuff(): pass
New in 0.8.
Async is a boolean attribute that determine if a function is async:
In [493]: red = RedBaron("with a as b: pass")
In [494]: red[0].async_
Out[494]: False
In [495]: red[0].async_ = True
In [496]: red
Out[496]:
0 async with a as b: pass
In [497]: red[0].async_ = False
In [498]: red
Out[498]:
0 with a as b: pass
Warning
As of python 3.7 async and await are now reserved keywords so don’t uses red.async, it works as expected but won’t make your code forward compatible.