Skip to content

Commit 0baa85d

Browse files
authored
Initial refactoring to better handle graphics devices in notebooks. (#1144)
Refactoring to better handle default options for graphics devices and conversion rules for the R magics.
1 parent 055ffb6 commit 0baa85d

File tree

7 files changed

+877
-342
lines changed

7 files changed

+877
-342
lines changed

.github/workflows/pythonpackage.yml

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525
uses: r-lib/actions/setup-r@v2
2626
with:
2727
r-version: release
28-
2928
- name: Check type hints rinterface
3029
run: |
3130
cd rpy2-rinterface

NEWS

+26-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ New features
99
encoding is now the system's encoding (rather than assume utf-8).
1010
This may provide a fix to issue #1018.
1111

12+
- Noteboook "R magic" features for ipython/jupyter support more
13+
graphical devices to render R graphics in notebooks. Registered
14+
devices are in :obj:`rpy2.ipython.rmags.graphics_devices`. This
15+
allows the specification of fallbacks when the preferred device is
16+
not available because of a missing R package. For SVG graphics,
17+
it will first try to use the R package `svglite` to produce
18+
lighter-weight SVG representations that are better when notebooks
19+
may include many figures and figure with large number of visual
20+
elements.
21+
22+
- Experimental minimalist set of optional conversion rules to
23+
convert Python :class:`list` objects to R vectors when all elements
24+
are of atomic type: :obj:`rpy2.robjects.flatlist_converter`. Those
25+
rules have been in used for the ipython/jupyter default converter.
26+
1227
Changes
1328
-------
1429

@@ -40,7 +55,14 @@ Changes
4055

4156
- The wrapper for dplyr is now targeting the R package `dplyr`
4257
version 1.1.
43-
58+
- The default converter for the ipython/jupyter magics depended on
59+
what is the default converter at the time of module import. To
60+
simplify this, the default converter is now fixed to
61+
`robjects.default_converter` + `robjects.flatlist_converter` +
62+
`robjects.numpy2ri.converter` + `robjects.pandas2ri.converter`.
63+
If `pandas` cannot be imported
64+
it falls back to `robjects.default_converter` + `robjects.numpy2ri.converter`.
65+
If `numpy` cannot be imported it falls back to `robjects.default_converter`.
4466
- The definition of `Rcomplex` from R's C-API was updated to match
4567
the definition in recent R release (>= 4.3) when rpy2 is in "API mode".
4668
This does not work with the "ABI mode" though, and the legacy
@@ -57,6 +79,9 @@ Bugs fixed
5779
play well with conversion rules in :mod:`rpy2.robjects.pandas2ri`.
5880
The default conversion rules are now forced (issue #1116).
5981

82+
- The documentation for the notebook "magic" `Rdevice` was not
83+
displayed in the module-level docstring for :mod:`rpy2.ipython.rmagic`.
84+
6085

6186
Release 3.5.17
6287
==============

doc/notebooks/jupyter.md

+105-1
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,13 @@ p = (gp.ggplot(dataf) +
156156
image_png(p)
157157
```
158158

159+
# R "magics"
160+
159161
So far we have shown that using `ggplot2` can be done from Python as if it
160162
was just an other Python library for visualization, but R can also be used
161163
in cells.
162164

163-
First the so-called "R magic" extension should be loaded.
165+
First the so-called "R magic" extensions should be loaded.
164166

165167
```python
166168
%load_ext rpy2.ipython
@@ -205,3 +207,105 @@ p <- ggplot(dataf) +
205207
theme_light(base_size=16)
206208
print(p)
207209
```
210+
211+
## Options.
212+
213+
The R "magics" use a number of configurable options that control
214+
aspects of the interaction between Python and R, or between ipython / jupyter
215+
and R. Some of the options can specified through arguments to "magics",
216+
but it is also possible to configure the default value for some of them
217+
when an argument to a "magic" does not specify otherwise. The easiest
218+
way to list these default-configurable options can be achieved
219+
with a "magic":
220+
221+
```python
222+
%Roptions show
223+
```
224+
225+
Changing these defaults requires to modify components of the attribute
226+
:attr:`rpy2.ipython.rmagic.RMagic.options`. Here is an example showing
227+
how to changing the default of R figures rendered in the notebook to "svg".
228+
229+
```python
230+
import IPython
231+
ipy = IPython.get_ipython()
232+
ipy.magics_manager.registry['RMagics'].options.graphics_device_name = 'svg'
233+
```
234+
235+
Another example is how to change conversion rules, that can be
236+
an expensive operation depending object sizes and rules, to a no-op:
237+
no convertion rules to keep all rpy2 objects as proxy wrappers
238+
to R objects for example, no conversion of Python objects to R:
239+
240+
```python
241+
import rpy2.robjects
242+
import IPython
243+
ipy = IPython.get_ipython()
244+
245+
noop_conv = rpy2.robjects.conversion.Converter('No-Op')
246+
noop_conv.rpy2py.register(rpy2.robjects.RObject,
247+
rpy2.robjects._rpy2py_robject)
248+
249+
ipy.magics_manager.registry['RMagics'].options.converter = noop_conv
250+
```
251+
252+
Modifying this come with risks though, or afterthoughts about changes made.
253+
While there are some checks in place, the new default options set this way
254+
may break the R "magics".
255+
To address this, options can be restore to
256+
"factory settings". Here again, there is no guarantee that this can work no
257+
matter what. A driven
258+
(or unlucky) user may break this mechanism if modifying it in the module.
259+
Restarting kernel will be needed in that case.
260+
261+
```python
262+
%Roptions refresh
263+
```
264+
265+
## Graphics devices
266+
267+
Graphics devices can be specified precisely as a function in an R namespace.
268+
For example, "grDevices:png". It is also possible to specify a generic graphical
269+
device name that is a sequence of graphics devices to try. The first one in that
270+
sequence that appears to be functioning is then selected.
271+
272+
For example, this is the case when
273+
the graphics device option is set to "svg". The R package `svglite` is
274+
the first to try in the sequence. If R fails to load that package there
275+
are other SVG devices to try try as alternatives.
276+
277+
To demonstrate this, let's set the graphics devices to "svg":
278+
279+
```python
280+
import IPython
281+
ipy = IPython.get_ipython()
282+
ipy.magics_manager.registry['RMagics'].options.graphics_device_name = 'svg'
283+
```
284+
285+
The output of the following magic depends on the R
286+
packages installed on your system. If the R package `svglite` is installed
287+
its device will be used. Otherwise alternatives will be tried.
288+
289+
```python
290+
%Roptions show
291+
```
292+
293+
### Options for graphics device
294+
295+
Graphics devices, selected from a generic names when the case (see above),
296+
may havee options. These options can be specific to each graphics device.
297+
298+
**note: this is prefixed with an underscore to indicate that the API is
299+
subject to changes.**
300+
301+
Options to display graphics are under `_options_display`. For example,
302+
to change the default width:
303+
304+
```python
305+
(
306+
ipy.magics_manager.registry['RMagics'].graphics_device
307+
._options_display
308+
.width
309+
) = 10
310+
```
311+

rpy2-rinterface/src/rpy2/rinterface_lib/callbacks.py

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import logging
99
import sys
1010
import typing
11+
import warnings
1112
import os
1213
from rpy2.rinterface_lib import openrlib
1314
from rpy2.rinterface_lib import ffi_proxy
@@ -21,6 +22,20 @@
2122
# TODO: rename to "replace_in_module"
2223
@contextmanager
2324
def obj_in_module(module, name: str, obj: typing.Any):
25+
warnings.warn(
26+
'"obj_in_module" was renamed to "replace_in_module".',
27+
DeprecationWarning
28+
)
29+
obj_orig = getattr(module, name)
30+
setattr(module, name, obj)
31+
try:
32+
yield
33+
finally:
34+
setattr(module, name, obj_orig)
35+
36+
37+
@contextmanager
38+
def replace_in_module(module, name: str, obj: typing.Any):
2439
obj_orig = getattr(module, name)
2540
setattr(module, name, obj)
2641
try:

0 commit comments

Comments
 (0)