1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
|
.. ipython:: python
:suppress:
import sys
sys.path.append("..")
import redbaron
redbaron.ipython_behavior = False
from redbaron import RedBaron
Proxy List
==========
Problem
-------
For a python developer, the list :file:`[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 :file:`"\\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. :file:`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:
.. ipython:: python
red = RedBaron("[1, 2, 3]")
red[0].value.append("42")
red
del red[0].value[2]
red
There are, for now, 4 kind of proxy lists:
* :file:`CommaProxyList` which handles comma separated lists
* :file:`DotProxyList` which handles :file:`atomtrailers` (those kind of constructions: :file:`a.b[plop].c()`)
* :file:`LineProxyList` which handles lines of code (like the body of a function or the
whole source code)
* :file:`DecoratorLineProxyList` which handles lists of decorators (they are nearly the
same as :file:`LineProxyList`)
**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 :file:`sort` and :file:`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:
.. ipython:: python
red = RedBaron("[1, 2, 3]")
Please refer to `python list documentation
<https://docs.python.org/2/tutorial/datastructures.html>`_ if you want to
know the exact behavior or those methods (or `send a patch
<https://github.com/PyCQA/redbaron>`_ to improve this documentation).
append
~~~~~~
.. ipython:: python
red
red[0].value.append("plop")
red
red[0].value
insert
~~~~~~
.. ipython:: python
red
red[0].value.insert(1, "42")
red
red[0].value
extend
~~~~~~
.. ipython:: python
red
red[0].value.extend(["pif", "paf", "pouf"])
red
red[0].value
pop
~~~
.. ipython:: python
red
red[0].value.pop()
red
red[0].value
red[0].value.pop(3)
red
red[0].value
__getitem__
~~~~~~~~~~~
.. ipython:: python
red
red[0].value
red[0].value[2]
__setitem__
~~~~~~~~~~~
.. ipython:: python
red
red[0].value[2] = "1 + 1"
red
red[0].value
remove
~~~~~~
.. ipython:: python
red
red[0].value.remove(red[0].value[2])
red
red[0].value
index
~~~~~
.. ipython:: python
red
red[0].value
red[0].value.index(red[0].value[2])
count
~~~~~
.. ipython:: python
red
red[0].value
red[0].value.count(red[0].value[2])
len
~~~
.. ipython:: python
red
red[0].value
len(red[0].value)
__delitem__
~~~~~~~~~~~
.. ipython:: python
red
del red[0].value[2]
red
red[0].value
in
~~
.. ipython:: python
red
red[0].value[2] in red[0].value
__iter__
~~~~~~~~
.. ipython:: python
red
for i in red[0].value:
print(i.dumps())
__getslice__
~~~~~~~~~~~~
.. ipython:: python
red
red[0].value
red[0].value[2:4]
__setslice__
~~~~~~~~~~~~
.. ipython:: python
red
red[0].value[2:4] = ["1 + 1", "a", "b", "c"]
red
red[0].value
__delslice__
~~~~~~~~~~~~
.. ipython:: python
red
red[0].value[2:5]
del red[0].value[2:5]
red
red[0].value
Access the unproxified node list
--------------------------------
The unproxified node list is stored under the attribute :file:`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.
.. ipython:: python
red = RedBaron("[1, 2, 3]")
red[0].value.node_list
red[0].value
Omitting ".value"
-----------------
For convenience, and because this is a super common typo error, if a node has a
proxy list on its :file:`.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:
.. ipython:: python
red[0]
red[0].value.append("plop")
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"
(:file:`(a, b=c, *d, **e)`) or a "getitem" (:file:`[foobar]`).
.. ipython:: python
red = RedBaron("a.b(c).d[e]")
red[0].value
red[0].extend(["[stuff]", "f", "(g, h)"])
red[0]
red[0].value
It is used everywhere where values are separated by ".".
You can see a complete example with a DotProxyList, like for the CommaProxyList,
here: :doc:`dotproxylist`.
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.
.. ipython:: python
red = RedBaron("while 42:\n stuff\n other_stuff\n\n there_is_an_empty_line_before_me")
red
red[0].value
red[0].append("plouf")
red
red[0].value
You can see a complete example with a LineProxyList, like for the CommaProxyList,
here: :doc:`lineproxylist`.
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:
.. ipython:: python
red = RedBaron("@plop\ndef stuff():\n pass\n")
red
red[0].decorators.append("@plouf")
red[0].decorators
red
Next
~~~~
To learn about various helpers and features in RedBaron, read :doc:`other`.
Be sure to check the :file:`.replace()` method on that page as it can be very useful.
|