---
title: "Using reticulate's Python Engine with knitr"
---

```{r setup, include=FALSE}
# load reticulate and set up engine
library(reticulate)
knitr::knit_engines$set(python = eng_python)

# use specific environment if available
# python <- "~/.virtualenvs/python-3.7.7-venv/bin/python"
# if (file.exists(python))
#   reticulate::use_python(python, required = TRUE)

# use rlang error handler
if (requireNamespace("rlang", quietly = TRUE))
  options(error = rlang::entrace)
```

```{r}
reticulate::py_config()
```


Python variables live across chunk boundaries.

```{python}
x = 1
y = 2
```

```{python}
print(x)
print(y)
```

Plots generated by `matplotlib` are properly displayed.

```{python, fig.width=4, fig.height=3, dev="svg"}
import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4])
plt.show()

plt.plot([1, 2, 3, 4])
plt.show()
```


```{python, fig.width=8, fig.height=3, dev="svg"}
import matplotlib.pyplot as plt

plt.plot(range(10))
```

```{python, fig.width=8, fig.height=3, dev="svg"}
import matplotlib.pyplot as plt

plt.hist(range(10))
```


Python can access objects available in the R environment.

```{r}
x <- 1:5
y <- 6:10
```

```{python}
print(r.x)
print(r['y'])

r.hello = "World"
r['answer'] = 42
```

```{r}
print(hello)
print(answer)
```

Arbitrary R code can be evaluated from Python.

```{python}
mpg = r["mtcars$mpg[1:5]"]
print(mpg)
```

- #126: Ensure single-line Python chunks that produce no output still have source code emitted.

```{python}
y = "a,b,c".split(",")
```

```{python}
print(y)
```

- #130: The `echo` chunk option is respected (for `TRUE` and `FALSE` values). Output, but not source, should show in the following output.

```{python echo=FALSE}
print("Chunk with echo = FALSE")
```

- #130: The `results` chunk option is respected for Python outputs. Source, but not output, should show in the following output.

```{python results='hide'}
print ("Chunk with results = 'hide'")
```

- #130: The `include` chunk option is respected for Python outputs. No chunk output should appear following this bullet.

```{python include=FALSE}
print ("Chunk with include = FALSE")
```

- #130: The `eval` chunk option is respected for Python outputs.

```{python eval=FALSE}
# We have set 'eval = FALSE' here
r["abc"] = 1
```

```{r}
exists("abc", envir = globalenv())
```

Respect the `error=TRUE` chunk option -- allow execution even after a Python
error occurs.

```{python, error=TRUE}
raise RuntimeError("oops!")
print("This line is still reached.")
```

Ensure that Exceptions are formatted w/ the type and notes.
```{python, error=TRUE}
class CustomException(Exception):
  pass

e = CustomException("oops!")

# BaseException.add_note() added in Python 3.11
import sys
if sys.version_info >= (3, 11):
  e.add_note("note 1: extra oopsy oops")
  e.add_note("note 2: in the future avoid oopsies")
else:
  print(sys.version_info)

raise e
print("This line is still reached.")
```

Ensure that lines with multiple statements are only run once.

```{python}
print("abc"); print("123")
```

Ensure that syntax errors do not block document generation when `eval = FALSE`.

```{python, eval=FALSE}
here be syntax errors
```

Output from bare statements should also be printed.

```{python}
"Hello, world!"
[x for x in range(10)]
```

Expressions that generate plots should be shown, if that is the final statement
within a chunk.

```{python}
import numpy as np, pandas as pd
a = np.random.normal(size=1000)
pd.Series(a).hist()
```

Ensure that outputs with `results = "hold"` are held to the end.

```{python, results="hold"}
print(1)
print(2)
print(3)
```

plotly plots should be auto-printed and displayed.

```{python}
import plotly.express as px
fig = px.scatter(x=[1, 2, 3], y=[5, 4, 6])
```

```{python}
fig
```

`_repr_html_()` methods are not called with unbound `__self__`
(https://github.com/rstudio/reticulate/issues/1249)

```{python}
import pandas as pd
pt = pd.DataFrame()
type(pt)
```

`_repr_markdown_()` methods are supported 
(https://github.com/quarto-dev/quarto-cli/issues/1501):

```{python}
from IPython.display import Markdown
from tabulate import tabulate
table = [["Sun",696000,1989100000],
         ["Earth",6371,5973.6],
         ["Moon",1737,73.5],
         ["Mars",3390,641.85]]
Markdown(tabulate(
  table, 
  headers=["Planet","R (km)", "mass (x 10^29 kg)"],
  tablefmt="pipe"
))
```


Setting option `error=TRUE` also allows parsing errors.

```{python, error=TRUE}
for i in range(3):
  print(i)
    print(i+10)
```

