From aa5920c18dbce403d7bc1e1e3a6de573532b2cec Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 22 Feb 2021 16:57:45 -0500 Subject: [PATCH] Add simple example Jupyter notebook (for Binder) (#127) * examples: Add Binder-compatible Jupyter notebook example * TO BE SQUASHED jupyterlab * try jupyterlab * give up on JupyterLab; leave as TODO * address review * address review * address review --- .binder/.dockerignore | 1 + .binder/Dockerfile | 65 ++++++++++++++ .binder/README.md | 33 +++++++ .dockerignore | 1 + README.rst | 14 +++ docs/examples/notebook/example.ipynb | 127 +++++++++++++++++++++++++++ setup.py | 16 ++-- 7 files changed, 251 insertions(+), 6 deletions(-) create mode 100644 .binder/.dockerignore create mode 100644 .binder/Dockerfile create mode 100644 .binder/README.md create mode 120000 .dockerignore create mode 100644 docs/examples/notebook/example.ipynb diff --git a/.binder/.dockerignore b/.binder/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/.binder/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/.binder/Dockerfile b/.binder/Dockerfile new file mode 100644 index 00000000..86c1c86b --- /dev/null +++ b/.binder/Dockerfile @@ -0,0 +1,65 @@ +# -*- mode: dockerfile -*- +# vi: set ft=dockerfile : + +# TODO(eric.cousineau): Figure out how to make JupyterLab work with this setup: +# https://github.com/binder-examples/jupyterlab + +# TODO(eric.cousineau): See if it's easier to use a conda-based workflow, or a +# simpler Docker base image, to use Eigen headers, rather than doing a custom +# Docker image: +# https://mybinder.readthedocs.io/en/latest/using/config_files.html + +FROM ubuntu:18.04 + +ARG NB_USER=jovyan +ARG NB_UID=1000 +ARG NB_GID=100 +EXPOSE 7000/tcp +EXPOSE 8888/tcp + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew \ + -o Dpkg::Use-Pty=0 \ + locales \ + python3-pip \ + python3-setuptools \ + && rm -rf /var/lib/apt/lists/* \ + && locale-gen en_US.UTF-8 + +# Install common C++ libraries for experimenting. These are not necessary to +# use pygccxml itself. +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew \ + -o Dpkg::Use-Pty=0 \ + libeigen3-dev \ + libstdc++-7-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -d "/home/$NB_USER" -G $NB_GID -mU -s /bin/bash "$NB_USER" +ENV HOME="/home/$NB_USER" \ + LANG=en_US.UTF-8 \ + LANGUAGE=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 \ + SHELL=/bin/bash \ + USER="$NB_USER" \ + PATH="/home/$NB_USER/.local/bin:/usr/local/bin:/usr/bin:/bin" + +# Upgrade pip to use newer indices for castxml. +# WARNING: Never upgrade a distribution `pip` on a host system using sudo! +# We are only doing this for a transient Docker image. For a host system, use a +# virtualenv to upgrade pip. +RUN pip3 --no-cache-dir install -U pip + +WORKDIR $HOME +RUN mkdir pygccxml +COPY ["/", "pygccxml/"] +RUN chown -R $NB_UID:$NB_GID \ + "$HOME/pygccxml" +USER "$NB_USER" +RUN pip3 --no-cache-dir install castxml +RUN pip3 --no-cache-dir install -e ./pygccxml[examples] +CMD ["jupyter", "notebook", "--ip", "0.0.0.0", "pygccxml/docs/examples/notebook/example.ipynb"] diff --git a/.binder/README.md b/.binder/README.md new file mode 100644 index 00000000..d9f2a820 --- /dev/null +++ b/.binder/README.md @@ -0,0 +1,33 @@ +# Docker Image for Binder + + + +*Note that due to Binder conventions, this directory MUST always be in the root +of the repository and named either `binder` or `.binder`. This image is NOT +intended for use by most developers or users.* + +These instructions are for running the image locally. For Binder itself, you +should only need to visit the link from the root-level README. + +To create a Docker image and run a Docker container similar to those used by +[Binder](https://mybinder.org) for local debugging purposes, execute the +following `build` and `run` commands from the top level of this Git repository: + +```bash +docker build -f .binder/Dockerfile -t binder . +docker run --rm -it --name mybinder -p 8888:8888 binder +``` + +For the URLs printed, only open the `127.0.0.1:8888` URL (including the login +token) in a web browser on your host system. + +To stop the running container, simply exit it from the terminal with Ctrl+C. + +*Note*: If you want to test the Docker image with the current source tree +(without copying, so you can modify source files), insert the arguments +`-v "${PWD}:/home/jovyan/pygccxml"` to `docker run`, before the image name +(`binder`), to mount it directly. This will *not* act on any changes to +`./setup.py`. diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 00000000..3e4e48b0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/README.rst b/README.rst index 167c3b4d..cf2dc59b 100644 --- a/README.rst +++ b/README.rst @@ -33,9 +33,23 @@ Documentation and examples -------------------------- The documentation can be found `here `_, examples can be found `here `_. +You can also run an example JupyterLab Notebook using Binder, or view it using +``nbviewer``: + +.. + Developers: See `.binder/README.md` for more information. + +.. image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/EricCousineau-TRI/pygccxml/feature-py-notebook-example?urlpath=tree/pygccxml/docs/examples/notebook/ + :alt: Binder +.. image:: https://img.shields.io/badge/view%20on-nbviewer-brightgreen.svg + :target: https://nbviewer.jupyter.org/github/EricCousineau-TRI/pygccxml/tree/feature-py-notebook-example/docs/examples/notebook/ + :alt: nbviewer If you want to know more about the API provided by pygccxml, read the `query interface `_ document or the `API documentation `_. + + A `FAQ `_ is also available and may answer some of your questions. License diff --git a/docs/examples/notebook/example.ipynb b/docs/examples/notebook/example.ipynb new file mode 100644 index 00000000..68c31f25 --- /dev/null +++ b/docs/examples/notebook/example.ipynb @@ -0,0 +1,127 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example pygccxml notebook on Binder\n", + "\n", + "Running this notebook on Binder allows you to execute the code online:", + "\n", + " \n", + "\n", + "\n", + "Please note that provisioning may take about 1-2 minutes.\n", + "\n", + "\n", + "The following example shows an example usage of `pygccxml` on Ubuntu\n", + "Bionic, from within a Docker container, for a simple toy C++ API that\n", + "uses both `std::vector` and `Eigen::Matrix<>`. The code is defined inline." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pygccxml import declarations\n", + "from pygccxml import utils\n", + "from pygccxml import parser" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find out the c++ parser. This should resolve to the castxml\n", + "# version installed in Docker.\n", + "generator_path, generator_name = utils.find_xml_generator()\n", + "\n", + "# Configure the xml generator\n", + "config = parser.xml_generator_configuration_t(\n", + " xml_generator_path=generator_path,\n", + " xml_generator=generator_name,\n", + " include_paths=[\"/usr/include/eigen3\"],\n", + " compiler_path=generator_path,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "code = r\"\"\"\n", + "#include \n", + "\n", + "#include \n", + "\n", + "namespace ns {\n", + "\n", + "template \n", + "class ExampleClass {\n", + "public:\n", + " std::vector make_std_vector() const;\n", + " Eigen::Matrix make_matrix3();\n", + "};\n", + "\n", + "// Analyze concrete instantiations of the given class.\n", + "extern template class ExampleClass;\n", + "extern template class ExampleClass;\n", + "\n", + "} // namespace ns\n", + "\"\"\"\n", + "\n", + "(global_ns,) = parser.parse_string(code, config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "ns = global_ns.namespace(\"ns\")\n", + "declarations.print_declarations([ns])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Retrieve an instantiation and show template parameters.\n", + "cls, = ns.classes('ExampleClass')\n", + "declarations.templates.split(cls.name)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/setup.py b/setup.py index a647e238..0501b1d9 100644 --- a/setup.py +++ b/setup.py @@ -9,15 +9,18 @@ version = utils.find_version("../pygccxml/__init__.py") -requirements_test = { +requirements_test = [ "coverage", "coveralls", "pycodestyle", -} -requirements_docs = { +] +requirements_docs = [ "sphinx", "sphinx_rtd_theme", -} +] +requirements_examples = [ + "notebook", +] setup(name="pygccxml", version=version, @@ -36,8 +39,9 @@ "pygccxml.parser", "pygccxml.utils"], extras_require={ - "test": list(requirements_test), - "docs": list(requirements_docs), + "test": requirements_test, + "docs": requirements_docs, + "examples": requirements_examples, }, classifiers=[ "Development Status :: 5 - Production/Stable",