Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: error when jupyter plugin is not installed #224

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,46 @@ Cast current deployed addresses to vyper contract

### Jupyter Integration

You can use Jupyter to execute titanoboa code in network mode from your browser using any wallet, using `boa.integrations.jupyter.BrowserSigner` as a drop-in replacement for `eth_account.Account`. For a full example, please see [this example Jupyter notebook](examples/jupyter_browser_signer.ipynb)
You can use Jupyter to execute titanoboa code in network mode from your browser using any wallet.
We provide a `BrowserSigner` as a drop-in replacement for `eth_account.Account`.
The `BrowserRPC` may be used to interact with the RPC server from the browser.

For a full example, please see [this example Jupyter notebook](examples/jupyter_browser_signer.ipynb)

#### JupyterLab

Before being able to use the plugin, you need to install it.
You can do this by running the following command in the terminal:

```bash
pip install titanoboa
jupyter lab extension enable boa
```
To activate our IPython extension, you need to run the following command in the notebook:
```jupyter
%load_ext boa.ipython
```

For ease of use, add the following to `ipython_config.py`:
```python
c.InteractiveShellApp.extensions = ["boa.ipython"]
c.InteractiveShellApp.exec_lines = ['import boa']
```

We provide a multi-user setup with JupyterLab in [try.vyperlang.org](https://try.vyperlang.org/), where the extension is installed and activated.
The source code for this website is available in the [GitHub repository](https://github.com/vyperlang/try.vyperlang.org).

#### Colab
It is also possible to run our plugin in [Google Colab](https://colab.research.google.com/).
To do this, you need to install the plugin by running the following commands:
```jupyter
!pip install titanoboa
%load_ext boa.ipython
```

#### IPython extensions

This activates the `%%vyper`, `%%contract` and `%%eval` magics.


### Basic tests
Expand Down
18 changes: 15 additions & 3 deletions boa/integrations/jupyter/jupyter.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,22 @@
/** Call the backend when the given function is called, handling errors */
const handleCallback = func => async (token, ...args) => {
if (!colab) {
// Check if the cell was already executed. In Colab, eval_js() doesn't replay.
// Check backend and whether cell was executed. In Colab, eval_js() doesn't replay.
const response = await fetch(`${base}/titanoboa_jupyterlab/callback/${token}`);
// !response.ok indicates the cell has already been executed
if (!response.ok) return;
if (response.status === 404 && response.headers.get('Content-Type') === 'application/json') {
return; // the cell has already been executed
}
if (!response.ok) {
const error = 'Could not connect to the titanoboa backend. Please make sure the Jupyter extension is installed by running the following command:';
const command = 'jupyter lab extension enable boa';
if (element) {
element.style.display = "block"; // show the output element in JupyterLab
element.innerHTML = `<h3 style="color: red">${error}</h3><pre>${command}</pre>`;
} else {
prompt(error, command);
}
return;
}
}

const body = stringify(await parsePromise(func(...args)));
Expand Down
3 changes: 2 additions & 1 deletion docs/source/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ ipython Vyper Cells

Titanoboa supports ipython Vyper cells. This means that you can write Vyper code in a ipython/Jupyter Notebook environment and execute it as if it was a Python cell (the contract will be compiled instead, and a ``ContractFactory`` will be returned).

To enable this feature, execute ``%load_ext boa.ipython`` in a cell.
You can use Jupyter to execute titanoboa code in network mode from your browser using any wallet, using your wallet to sign transactions and call the RPC.
For a full example, please see `this example Jupyter notebook <../../examples/jupyter_browser_signer.ipynb>`_.

.. code-block:: python

Expand Down
130 changes: 11 additions & 119 deletions examples/jupyter_browser_signer.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,130 +6,22 @@
"id": "94744db8",
"metadata": {},
"outputs": [],
"source": [
"import boa; from boa.network import NetworkEnv"
]
"source": "%load_ext boa.ipython"
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ff9dfb06",
"metadata": {},
"outputs": [],
"source": [
"%load_ext boa.ipython"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "9f241bf5",
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": [
"\n",
"require.config({\n",
" paths: {\n",
" //ethers: \"https://cdnjs.cloudflare.com/ajax/libs/ethers/5.7.2/ethers.umd.min\"\n",
" ethers: \"https://cdnjs.cloudflare.com/ajax/libs/ethers/6.4.2/ethers.umd.min\"\n",
" }\n",
"});\n",
"\n",
"require(['ethers'], function(ethers) {\n",
" // Initialize ethers\n",
" let provider = new ethers.BrowserProvider(window.ethereum);\n",
"\n",
" // check that we have a signer for this account\n",
" Jupyter.notebook.kernel.comm_manager.register_target('get_signer', function(c, msg) {\n",
" // console.log(\"get_signer created\", c)\n",
" c.on_msg(function(msg) {\n",
" // console.log(\"get_signer called\", c)\n",
" let account = msg.content.data.account\n",
" provider.getSigner(account).then(signer => {\n",
" // console.log(\"success\", signer)\n",
" c.send({\"success\": signer});\n",
" }).catch(function(error) {\n",
" console.error(\"got error, percolating up:\", error);\n",
" c.send({\"error\": error});\n",
" });\n",
" });\n",
" });\n",
"\n",
" Jupyter.notebook.kernel.comm_manager.register_target(\"send_transaction\", function(c, msg) {\n",
" c.on_msg(function(msg) {\n",
" let tx_data = msg.content.data.transaction_data;\n",
" let account = msg.content.data.account\n",
" provider.getSigner(account).then(signer => {\n",
" signer.sendTransaction(tx_data).then(response => {\n",
" console.log(response);\n",
" c.send({\"success\": response});\n",
" }).catch(function(error) {\n",
" console.error(\"got error, percolating up:\", error);\n",
" c.send({\"error\": error});\n",
" });\n",
" }).catch(function(error) {\n",
" console.error(\"got error, percolating up:\", error);\n",
" c.send({\"error\": error});\n",
" });\n",
" });\n",
" });\n",
"});\n",
"\n",
"Jupyter.notebook.kernel.comm_manager.register_target(\"test_comm\", function(comm, msg) {\n",
" console.log(\"ENTER\", comm);\n",
" /*comm.on_close(function(msg) {\n",
" console.log(\"CLOSING\", msg);\n",
" });\n",
" */\n",
"\n",
" comm.on_msg(function(msg) {\n",
" console.log(\"ENTER 2\", comm);\n",
" console.log(\"ENTER 3\", msg.content.data);\n",
" setTimeout(() => {\n",
" comm.send({\"success\": \"hello\", \"echo\": msg.content.data});\n",
" comm.close();\n",
" console.log(comm);\n",
" }, 350);\n",
" });\n",
"});\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from boa.integrations.jupyter import BrowserSigner"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "814ff4f3",
"metadata": {},
"outputs": [],
"source": [
"boa.set_env(NetworkEnv(\"<rpc server address, e.g. an alchemy endpoint>\"))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "a24872c9",
"metadata": {},
"outputs": [],
"execution_count": 2,
"source": [
"boa.env.add_account(BrowserSigner())"
]
"import boa\n",
"boa.set_browser_env() # this will use the browser signer and the browser RPC"
],
"id": "b724995f3df612f0"
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 3,
"id": "1e98969d",
"metadata": {},
"outputs": [
Expand All @@ -139,7 +31,7 @@
"<boa.vyper.contract.VyperDeployer at 0x7f5150614a90>"
]
},
"execution_count": 6,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
Expand All @@ -158,7 +50,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 4,
"id": "c5b60ed3",
"metadata": {},
"outputs": [
Expand All @@ -178,7 +70,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 5,
"id": "bdbfc09c",
"metadata": {},
"outputs": [
Expand All @@ -189,7 +81,7 @@
"<storage: totalSupply=1000, balances={'0x...<truncated>': 1000}>"
]
},
"execution_count": 8,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
Expand Down
Loading