{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Jupyter and IPython Configuration Tips" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Enabling the Host's Certificates\n", "\n", "When you want to access REST API endpoints secured by TLS then you need to have the related certificates visible to the `requests` library, which is normally used inder the hood to estalish the TLS connections.\n", "\n", "Call these commands in your user account to set relevant environment variables:\n", "\n", " ipython profile create\n", " cat >~/.ipython/profile_default/startup/00-ca-certificates.py <<'EOF'\n", " import os as _os\n", " _os.environ.setdefault('REQUESTS_CA_BUNDLE', '/etc/ssl/certs/ca-certificates.crt')\n", " del _os\n", " EOF\n", "\n", "⚠️ If you have notebooks running, restart their kernel via the web UI to load those changes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How to Provide Your Own Personal CSS\n", "\n", "You can provide a `custom.css` file as part of the personal configuration in `~/.ipython`:\n", "\n", " mkdir -p ~/.ipython/profile_default/static/custom\n", " test -f $_/custom.css || echo >$_ \"div.input_area { background: #ddf; }\"\n", "\n", "The above is a complete example for input cells with a faint blu-ish background.\n", "\n", "⚠️ Restart your kernel after applying changes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Embedded Dependency Installation\n", "\n", "### Overview\n", "To install the requirements of a notebook from within a code cell, you can use the `%pip` magic available with *IPython* `0.7.3` and up (in earlier versions, use `!pip` instead). This also makes sure that installing uncommon packages used in your notebook is documented, improving reproducability.\n", "\n", "You need permission to write into pip's install prefix though, which depending on the exact runtime environment you won't have – especially with JupyterHub installations. Passing the `--user` option will install to a writable path, but then you might want to have your notebook packages separate from ones you use in the shell.\n", "\n", "Therefore, installed packages go into `~/.ipython`, but not directly into that directory, which would become a mess otherwise. Instead, we install into the usual `lib/pythonX.X/site-packages` hierarchy, and then make that visible to Python. An additional important advantage is that kernels using different Python versions co-exist peacefully.\n", "\n", "`%env` magic can be used to point `pip` at a local repository server like *devpi* or *Artifactory*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Install & Import Mechanics\n", "The following code is pure boilerplate to provide the `require` function. In production environments, you probably want to have this in your underlying configuration, or as a custom `%require` magic." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "env: PIP_INDEX_URL=https://pypi.org/pypi\n", "env: PIP_PREFIX=~/.ipython\n" ] } ], "source": [ "%env PIP_INDEX_URL=https://pypi.org/pypi\n", "%env PIP_PREFIX=~/.ipython\n", "\n", "def require(spec, alias=None, pkgname=None):\n", " \"\"\" Helper for importing custom packages.\n", " \n", " Use ``alias`` to change the ‘imported-as’ name,\n", " and ``pkgname`` in cases where the package's name\n", " is different from the project name on PyPI.\n", " Both default to the name part from ``spec``.\n", " \"\"\"\n", " import importlib, os, re, sys\n", "\n", " site_packages = os.path.expanduser(\n", " '~/.ipython/lib/python{v.major}.{v.minor}/site-packages'\n", " .format(v=sys.version_info))\n", " if site_packages not in sys.path:\n", " sys.path.insert(0, site_packages)\n", "\n", " name = re.split('[ ;,<>=]', spec)[0].replace('-', '_')\n", " pkgname = pkgname or name\n", " try:\n", " module = importlib.import_module(pkgname)\n", " print(\"⚠ Using already installed '{}' package.\"\n", " .format(pkgname))\n", " except ImportError:\n", " pip_opts = ' '.join([\n", " '-q',\n", " '--disable-pip-version-check',\n", " '--no-warn-script-location',\n", " ])\n", " %pip install {pip_opts} \"{spec}\"\n", " module = importlib.import_module(pkgname)\n", "\n", " globals()[alias or name] = module\n", " return module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This code shows the extended Python search path with the user-specific `site-packages` directory upfront." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "⚠ Using already installed 'sys' package.\n" ] }, { "data": { "text/plain": [ "['~/.ipython/lib/python3.6/site-packages',\n", " '/opt/venvs/jupyterhub/lib/python36.zip',\n", " '/opt/venvs/jupyterhub/lib/python3.6',\n", " '/opt/venvs/jupyterhub/lib/python3.6/lib-dynload',\n", " '/usr/lib/python3.6',\n", " '',\n", " '/opt/venvs/jupyterhub/lib/python3.6/site-packages',\n", " '/opt/venvs/jupyterhub/lib/python3.6/site-packages/IPython/extensions',\n", " '~/.ipython']" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def prettify(path):\n", " import os\n", " return path.replace(os.path.expanduser('~/'), '~/')\n", "\n", "require('sys')\n", "[prettify(x) for x in sys.path]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Defining & Using Requirements\n", "Given the above code is executed once in a kernel, you can now replace a normal import with a `require` call, and the specified package is installed if not available yet." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note: you may need to restart the kernel to use updated packages.\n" ] }, { "data": { "text/plain": [ "\"\"" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "require('distro>=1.4', 'do', pkgname='distro')\n", "prettify(repr(do))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, just use the package as usual." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'id': 'ubuntu',\n", " 'version': '18.04',\n", " 'version_parts': {'major': '18', 'minor': '04', 'build_number': ''},\n", " 'like': 'debian',\n", " 'codename': 'bionic'}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "do.info()" ] } ], "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.7" } }, "nbformat": 4, "nbformat_minor": 2 }