diff --git a/CONTRIBUTING b/CONTRIBUTING deleted file mode 100644 index 0e0061be..00000000 --- a/CONTRIBUTING +++ /dev/null @@ -1,66 +0,0 @@ -# User Guide - -Installation and usage instructions are available -[here](https://github.com/rstudio/rsconnect-jupyter/tree/master/docs). - -# Developing `rsconnect-jupyter` - -Need to run this after checkout and when modifying the docker images - - make images - -Launch jupyter in a python 2 environment - - make notebook2 - -Launch jupyter in a python 3 environment - - make notebook3 - -## Trying out notebooks - -> Note: notebooks in the `notebooks2` and `notebooks3` directories will be -> available in respective python environments. - -Sample notebooks can be obtained from: - -- https://github.com/ipython/ipython-in-depth -- https://github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks -- https://losc.ligo.org/tutorials/ -- http://nb.bianp.net/sort/views/ - -e.g. - -``` -cd notebooks3 -git clone https://github.com/ipython/ipython-in-depth -``` - -## Seeing code changes - -When modifying JavaScript files simply refresh the browser window to see -changes. - -When modifying Python files restart the jupyter process to see changes. - -# Packaging - -The following will create a universal [wheel](https://pythonwheels.com/) ready -to be installed in any python 2 or python 3 environment. - - make package - -## Updating rsconnect-jupyter on conda-forge - -rsconnect-jupyter exists on conda-forge as its own [feedstock](https://github.com/conda-forge/rsconnect-jupyter-feedstock) - -Updating the package requires a fork of the repository and a push request [example workflow](https://conda-forge.org/docs/maintainer/updating_pkgs.html#example-workflow-for-updating-a-package). - -- For new version/release, update the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file with the new version number, source url, and corresponding checksum. - -- For a rebuild of the same version, increase "number" under "build" by one in the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file. - - -### Adding yourself as a rsconnect-jupyter conda-forge maintainer - -Add your github username under recipe-maintainers in the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..694f5aec --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,99 @@ +# Contributing Guidelines + +This documentation covers common tasks related to development. + +For installation and usage instructions see [README.md](./README.md). + + +**Table of Contents** + +- [Contributing Guidelines](#contributing-guidelines) +- [Development](#development) + - [Trying out notebooks](#trying-out-notebooks) + - [Seeing code changes](#seeing-code-changes) +- [Packaging](#packaging) +- [Versioning and Releases](#versioning-and-releases) + - [Versioning](#versioning) + - [Releases](#releases) + - [Releasing on Conda Forge](#releasing-on-conda-forge) + - [Adding yourself as a rsconnect-jupyter conda-forge maintainer](#adding-yourself-as-a-rsconnect-jupyter-conda-forge-maintainer) + + + +# Development + +Need to run this after checkout and when modifying the docker images + + make images + +Launch jupyter in a python 3 environment + + make notebook3 + +## Trying out notebooks + +> Note: notebooks in the `notebooks3` directories will be available in respective python environments. + +Sample notebooks can be obtained from: + +- https://github.com/ipython/ipython-in-depth +- https://github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks +- https://losc.ligo.org/tutorials/ +- http://nb.bianp.net/sort/views/ + +e.g. + +``` +cd notebooks3 +git clone https://github.com/ipython/ipython-in-depth +``` + +## Seeing code changes + +When modifying JavaScript files simply refresh the browser window to see +changes. + +When modifying Python files restart the jupyter process to see changes. + +# Packaging + +The following will create a universal [wheel](https://pythonwheels.com/) ready +to be installed in any python 2 or python 3 environment. + + make package +# Versioning and Releases + +## Versioning + +Versioning is accomplished via Git tagging using [Semantic Versioning](https://semver.org/). Read more about tagging [here](https://git-scm.com/book/en/v2/Git-Basics-Tagging). +o view the most recent release tag, execute the following. + +```shell +git describe master --match "v*" --tags +``` + +## Releases + +Releases are accomplished through GitHub Actions. + +To initiate a release, create a manual tag use the following steps. The `..` values **MUST** follow semantic versioning. + + git tag v.. + git push origin v.. + +Once pushed, a GitHub Action will be trigged. This action with publish the release to [PyPi](https://pypi.org/project/rsconnect-jupyter/) using the specified version. + +### Releasing on Conda Forge + +`rsconnect-jupyter` exists on conda-forge as its own [feedstock](https://github.com/conda-forge/rsconnect-jupyter-feedstock) + +Updating the package requires a fork of the repository and a push request [example workflow](https://conda-forge.org/docs/maintainer/updating_pkgs.html#example-workflow-for-updating-a-package). + +- For new version/release, update the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file with the new version number, source url, and corresponding checksum. + +- For a rebuild of the same version, increase "number" under "build" by one in the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file. + +### Adding yourself as a rsconnect-jupyter conda-forge maintainer + +Add your GitHub username under recipe-maintainers in the [meta.yaml](https://github.com/conda-forge/rsconnect-jupyter-feedstock/blob/master/recipe/meta.yaml) file. + diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 74c4edd8..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,201 +0,0 @@ -#!groovy - -def gitClean() { - // inspired by: https://issues.jenkins-ci.org/browse/JENKINS-31924 - // https://issues.jenkins-ci.org/browse/JENKINS-32540 - // The sequence of reset --hard and clean -fdx first - // in the root and then using submodule foreach - // is based on how the Jenkins Git SCM clean before checkout - // feature works. - sh 'git reset --hard' - sh 'git clean -ffdx' -} - -// Build the name:tag for a docker image where the tag is the checksum -// computed from a specified file. -def imageName(name, filenames) { - // If this is extended to support multiple files, be wary of: - // https://issues.jenkins-ci.org/browse/JENKINS-26481 - // closures don't really work. - - // Suck in the contents of the file and then hash the result. - def contents = ""; - for (int i=0; i" - - // Looking up the author also demands being in a `node`. - gitAuthor = sh(returnStdout: true, script: 'git --no-pager show -s --format="%aN" HEAD').trim() - - stage('Docker build and test') { - parallel( - 'python2.7': { - buildAndTest("2.7") - }, - 'python3.5': { - img = buildAndTest("3.5") - }, - 'python3.6': { - img = buildAndTest("3.6") - - img.inside("-v ${env.WORKSPACE}:/rsconnect_jupyter") { - sh "pip install --user --upgrade twine setuptools wheel" - print "building python wheel package" - sh 'make dist' - archiveArtifacts artifacts: 'dist/*.whl,dist/*.tar.gz' - stash includes: 'dist/*.whl,dist/*.tar.gz', name: 'wheel' - } - }, - 'python3.7': { - img = buildAndTest("3.7") - }, - 'code-quality': { - def uid = sh (script: 'id -u jenkins', returnStdout: true).trim() - def gid = sh (script: 'id -g jenkins', returnStdout: true).trim() - img = pullBuildPush( - image_name: 'jenkins/rsconnect-jupyter-yarn', - image_tag: 'latest', - docker_context: './tools/yarn', - build_arg_nb_uid: 'JENKINS_UID', - build_arg_nb_gid: 'JENKINS_GID', - build_args: "--build-arg NB_UID=${uid} --build-arg NB_GID=${gid}", - push: !isUserBranch - ) - img.inside("-v ${env.WORKSPACE}:/rsconnect_jupyter") { - sh 'make yarn' - sh 'make lint' - } - } - ) - } - stage('Docs build') { - docs_image = pullBuildPush( - image_name: 'jenkins/rsconnect-jupyter', - image_tag: 'docs', - docker_context: './docs', - push: !isUserBranch - ) - docs_image.inside("-v ${env.WORKSPACE}:/rsconnect_jupyter") { - sh 'make docs-build' - archiveArtifacts artifacts: 'dist/*.pdf,dist/*.html' - stash includes: 'dist/*.pdf,dist/*.html', name: 'docs' - } - } - stage('S3 upload') { - unstash 'wheel' - unstash 'docs' - publishArtifacts() - } - } - } - - // Slack message includes username information. - message = "${messagePrefix} by ${gitAuthor} passed" - slackSend channel: slackChannelPass, color: 'good', message: message -} catch(err) { - // Slack message includes username information. When master/release fails, - // CC the whole connect team. - slackNameFail = "unknown" - if (!isUserBranch) { - slackNameFail = "${gitAuthor} (cc @rsconnect_jupyter)" - } - - message = "${messagePrefix} by ${slackNameFail} failed: ${err}" - slackSend channel: slackChannelFail, color: 'bad', message: message - throw err -} diff --git a/docs/docs/index.md b/docs/docs/index.md index d5ce61d8..8d34e190 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,143 +1,142 @@ # `rsconnect-jupyter` User Guide -`rsconnect-jupyter` is a plugin for Jupyter Notebooks that enables publishing notebooks to Posit Connect. +The `rsconnect-jupyter` package is a _Jupyter Notebook_ extension (i.e., `nbextension`) that provides publishing compatibility with [Posit Connect](https://docs.posit.co/connect). ## Requirements -- Python 3.5.0 and higher -- Jupyter Notebook 5.x -- [pip](https://pypi.org/project/pip/) -- [wheel](https://pypi.org/project/wheel/) -- [Posit Connect](https://www.posit.co/download/posit-connect/) v1.7.0 or higher, configured with Python support +- [Python >=3.7](https://www.python.org/downloads/) +- [Jupyter](https://pypi.org/project/jupyter) +- [Notebook >=5,7](https://pypi.org/project/notebook/) +- [Posit Connect](https://www.posit.co/download/posit-connect/) ([supported versions](https://posit.co/support/#supported-connect-versions)) -!!! note - If using `conda`, `pip` and `wheel` should already be installed. +!!! Warning + This extension is **NOT** compatible with _JupyterLab_. Only _Jupyter Notebooks_, and associated runtime environments, such as _JupyterHub_, are supported. -## Installation +!!! Warning + In order to publish to _Posit Connect_, a compatible Python environment must in exist on the _Posit Connect_ instance. See the _Posit Connect_ documentation on [Python integrations](https://docs.posit.co/connect/admin/python/) for additional information. -The installation method depends on the Python environment that you are installing the `rsconnect-jupyter` package into. +## Installation -!!! note - The `rsconnect-jupyter` package is developed for Jupyter Notebook, specifically. Therefore, the package does not work with the JupyterLab development environment. +--8<-- "snippets/install.md" -This documentation covers three methods: +## Environment Configuration -- [Installing Jupyter within a virtual environment](#installing-jupyter-within-a-virtual-environment) -- [Installing `rsconnect-jupyter` to Jupyter running on Posit Workbench](#installing-to-jupyter-running-on-posit-workbench) -- [Installation in JupyterHub](#installing-in-jupyterhub) +### Localhost (Your Computer) -Please navigate to the installation section below that is best for your environment. +For localhost installation, a Python virtual environment is recommended to isolate runtime dependencies. There are various Python virtual environments available. The following tutorial covers a few of them. -### Installing Jupyter within a virtual environment +#### Conda -To install and use Jupyter within a virtual environment using -`virtualenv`, follow the procedures shown below or learn more using the -[Virtualenv](https://virtualenv.pypa.io/en/latest/) documentation. +Install `rsconnect-jupyter` from [Conda Forge](https://conda-forge.org). -- These commands create and activate a `virtualenv` at `/my/path`: -
Terminal
- ```bash - $ pip install virtualenv - virtualenv /my/path - source /my/path/bin/activate - ``` +
Terminal
+```shell +conda create --name rsconnect-jupyter +conda activate rsconnect-jupyter +conda install -c conda-forge jupyter rsconnect-jupyter +``` -!!! tip - Running `source /my/path/bin/activate` activates the virtual environment. While the `virtualenv` is active, Python-related commands like `python`, `pip`, and `jupyter` will use to copies located inside the virtual environment. You can check which copy of `python` you're using by running `which python`. +Next, following the [installation guide](#installation). -- Install Jupyter inside the `virtualenv`: -
Terminal
- ```bash - $ pip install jupyter - ``` +!!! Tip + When creating a _Conda_ virtual environment, a specific Python version may be specified. Create your virtual environment with a Python environment that is available on your Posit Connect server. -- Install rsconnect-jupyter with your virtual environment active to install and activate the plugin for that copy of Jupyter: +
Terminal
+ ```shell + conda create --name rsconnect-jupyter python=3.8 + ``` - --8<-- "snippets/python_pkg.md" +!!! Note -!!! important - Be sure to run Jupyter from this virtual environment, not from - another installation, or the `rsconnect-jupyter` extension will - not be available. To do so, you will need to activate the virtual - environment in each new terminal session before you run `jupyter`. + If _Anaconda_ is used, then _Jupyter Notebook_ launches with kernel environments for each existing Conda environment. Follow the [installation guide](#installation) for each kernel/Conda-environment to enable `rsconnect-jupyter`. ---- +#### Python Virtual Environment ([venv](https://docs.python.org/3/library/venv.html)) -### Installing to Jupyter running on Posit Workbench +
Terminal
+```shell +python -m venv .venv +source .venv/bin/activate +``` -- If you are installing `rsconnect-jupyter` to Jupyter running on RStudio Server Pro, see -the [RStudio Server Pro documentation on Jupyter Notebooks](https://docs.rstudio.com/rsp/integration/jupyter-standalone/#4-install-jupyter-notebooks-jupyterlab-and-python-packages) -for instructions on installing the plugin to the right location. +Next, following the [installation guide](#installation). -- Once you complete the installation instructions, please return to this document for additional information such as [Upgrading](upgrading) or [Usage](usage) instructions. +!!! Tip + Running `source .venv/bin/activate` activates the virtual environment. While the virtual environment is active, the `.venv/bin` directory is prepended to the `$PATH` environment variable for the active shell. Therefore executables installed within the virtual environment, like `python`, `pip`, and `jupyter`, are utilized. To determine which version of `python`, `pip`, or `jupyter` is active, execute the `which` command (e.g, `which python`). ---- +### JupyterHub -### Installation in JupyterHub +Follow the [installation guide](#installation) to install and enable `rsconnect-jupyter` in _JupyterHub_. -In JupyterHub, follow these directions to install the -`rsconnect-jupyter` package into the Python environment where the Jupyter -notebook server and kernel are installed: +If you've configured separate kernel environments, repeat the installation guide for each kernel environment. ---8<-- "snippets/python_pkg.md" +!!! Note + The exact install location depends on your _JupyterHub_ configuration. -Typically those will be the same -environment. If you've configured separate kernel environments, install the -`rsconnect-jupyter` package in the notebook server environment as well as each -kernel environment. +#### Quick Start Example -The exact install location depends on your JupyterHub configuration. +The following example shows how to launch a Docker container running _JupyterHub_ with the `rsconnect-jupyter` extension installed. -#### JupyterHub Example Configuration +!!! Warning -This section presents a simple working example of a JupyterHub configuration -with `rsconnect-jupyter` installed. + This configuration is **NOT** intended for production usage. This is a minmal working configuration designed to highlight `rsconnect-jupyter` configuration. -??? example "Docker Example" - This example uses Docker, but you can install the `rsconnect-jupyter` package in - any Jupyterhub installation. Docker is not required. +!!! Example "Docker Example" - Example Dockerfile: + Create the following Dockerfile.

Dockerfile

```dockerfile - FROM jupyterhub/jupyterhub:0.9.4 - - # Install Jupyter notebook into the existing base conda environment - RUN conda install notebook - - # Download and install rsconnect-jupyter in the same environment - # Update this to specify the desired version of the rsconnect-jupyter package, - # or pass `--build-arg VERSION=...` to docker build. - ARG VERSION=RSCONNECT_VERSION - ARG REPOSITORY=https://s3.amazonaws.com/rstudio-rsconnect-jupyter - - RUN wget ${REPOSITORY}/rsconnect_jupyter-${VERSION}-py2.py3-none-any.whl - RUN pip install rsconnect_jupyter-${VERSION}-py2.py3-none-any.whl && \ - jupyter-nbextension install --sys-prefix --py rsconnect_jupyter && \ - jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ - jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter - - # create test users - RUN useradd -m -s /bin/bash user1 && \ - useradd -m -s /bin/bash user2 && \ - useradd -m -s /bin/bash user3 && \ - bash -c 'echo -en "password\npassword" | passwd user1' && \ - bash -c 'echo -en "password\npassword" | passwd user2' && \ - bash -c 'echo -en "password\npassword" | passwd user3' - - CMD ["jupyterhub"] + FROM jupyterhub/jupyterhub:3 + + # Install Jupyter and the rsconnect-jupyter extension + RUN python3 -m pip install jupyter rsconnect-jupyter + + # Enables the rsconnect-jupyter extension + RUN python3 -m jupyter nbextension install --sys-prefix --py rsconnect_jupyter + RUN python3 -m jupyter nbextension enable --sys-prefix --py rsconnect_jupyter + RUN python3 -m jupyter serverextension enable --sys-prefix --py rsconnect_jupyter + + # Create a new user called "username" with the password "password" + # + # Use these credentials when logging into JupyterHub. + # + # Example: + # username: "username" + # password: "password" + RUN useradd -m -p $(openssl passwd -1 password) -s /bin/sh username + + ENTRYPOINT ["jupyterhub"] ``` - Run these commands to build and start the container: + Next, build the `Dockerfile` to create a new Docker image named jupyterhub:rsconnect-jupyter.

Terminal

- ```bash + ```shell docker build -t jupyterhub:rsconnect-jupyter . + ``` + + Finally, launch the Docker image. + +

Terminal

+ ```shell docker run --rm -p 8000:8000 --name jupyterhub jupyterhub:rsconnect-jupyter ``` - Connect to Jupyterhub on http://localhost:8000 and log in as one of the test - users. From there, you can create a notebook and publish it to Posit Connect. - Note that the current Jupyterhub docker image uses Python 3.6.5, so you will - need a compatible Python version installed on your Posit Connect server. + Once executed, a series of startup logs will be shown. Wait for the log message: `JupyterHub is now running at http://:8000`. + + Once shown, the _JupyterHub_ server is running on your local machine. To access _JupyterHub_ procceed with the following steps: + + 1. Open [http://localhost:8000](http://localhost:8000) in your browser. + 1. Login to using the credentials "username" and "password". These credentials match the credentials set in the Dockerfile and may be changed. + 1. Select the "New" dropdown menu and select "Python 3 (pykernal)". + 1. Next, follow the [usage guide](./usage). + + !!! Warning + + At the time of writing, the `jupyterhub/jupyterhub:3` Docker image is built using Python version 3.10.6. Therefore, in order to publish to Posit Connect, a compatible Python version 3.10 environment must exist in Posit Connect. + +### Posit Workbench + +[Posit Workbench](https://docs.posit.co/ide/server-pro/) supports _Jupyter Notebook_ sessions. See the [Jupyter Configuration](https://docs.posit.co/ide/server-pro/jupyter-sessions/configuration.html) guide to configure _Jupyter Notebook_ sessions. + +Once enabled, follow the [installation guide](#installation) to install and enable the `rsconnect-jupyter` plugin for each Jupyter kernel. diff --git a/docs/docs/snippets/install.md b/docs/docs/snippets/install.md new file mode 100644 index 00000000..8903bfec --- /dev/null +++ b/docs/docs/snippets/install.md @@ -0,0 +1,28 @@ +1. Install `jupyter` and `rsconnect-jupyter`. + +
Terminal
+ ``` shell + python -m pip install jupyter rsconnect-jupyter + ``` + +1. Install the `rsconnect-jupyter` _Jupyter Notebook_ extension. + +
Terminal
+ ```shell + python -m jupyter nbextension install --sys-prefix --py rsconnect_jupyter + ``` + +1. Enable the `rsconnect-jupyter` _Jupyter Notebook_ extension + +
Terminal
+ ```shell + python -m jupyter nbextension enable --sys-prefix --py rsconnect_jupyter + ``` + +1. Enable the `rsconnect-jupyter` _Jupyter Server_ extension. + +
Terminal
+ ```shell + python -m jupyter serverextension enable --sys-prefix --py rsconnect_jupyter + ``` + diff --git a/docs/docs/snippets/python_pkg.md b/docs/docs/snippets/python_pkg.md deleted file mode 100644 index 38046119..00000000 --- a/docs/docs/snippets/python_pkg.md +++ /dev/null @@ -1,25 +0,0 @@ -- The following commands should be run after activating the Python environment where you plan to use `jupyter`. - - - Install the `rsconnect-jupyter` package with the following command: -
Terminal
- ```bash - $ pip install rsconnect_jupyter - ``` - - - Enable the `rsconnect-jupyter` extension with the following commands: -
Terminal
- ```bash - # Install `rsconnect-jupyter` as a jupyter extension - jupyter-nbextension install --sys-prefix --py rsconnect_jupyter - - # Enable JavaScript extension - jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter - - # Enable Python extension - jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter - ``` - - !!! note - - The above commands only need to be run once when installing `rsconnect_jupyter`. - - To deploy content, you will need at least the [rsconnect-python](https://github.com/rstudio/rsconnect-python) package in every kernel you plan to deploy from. - - If you run into an issue during installation, please let us know by filing a bug [here](https://github.com/rstudio/rsconnect-jupyter/issues). \ No newline at end of file diff --git a/docs/docs/upgrading.md b/docs/docs/upgrading.md index 2b8e464a..5b2e467e 100644 --- a/docs/docs/upgrading.md +++ b/docs/docs/upgrading.md @@ -6,4 +6,4 @@ Follow the procedures below to upgrade `rsconnect-jupyter`. --8<-- "snippets/uninstall.md" - Then, reinstall it. - --8<-- "snippets/python_pkg.md" + --8<-- "snippets/install.md" diff --git a/examples/jupyterhub/Dockerfile b/examples/jupyterhub/Dockerfile new file mode 100644 index 00000000..282aa0a1 --- /dev/null +++ b/examples/jupyterhub/Dockerfile @@ -0,0 +1,20 @@ +FROM jupyterhub/jupyterhub:3 + +# Install Jupyter and the rsconnect-jupyter extension +RUN python3 -m pip install jupyter rsconnect-jupyter + +# Enables the rsconnect-jupyter extension +RUN python3 -m jupyter nbextension install --sys-prefix --py rsconnect_jupyter +RUN python3 -m jupyter nbextension enable --sys-prefix --py rsconnect_jupyter +RUN python3 -m jupyter serverextension enable --sys-prefix --py rsconnect_jupyter + +# Create a new user called "username" with the password "password" +# +# Use these credentials when logging into JupyterHub. +# +# Example: +# username: "username" +# password: "password" +RUN useradd -m -p $(openssl passwd -1 password) -s /bin/sh username + +ENTRYPOINT ["jupyterhub"] diff --git a/examples/jupyterhub/justfile b/examples/jupyterhub/justfile new file mode 100644 index 00000000..c0e2e04e --- /dev/null +++ b/examples/jupyterhub/justfile @@ -0,0 +1,7 @@ +PORT := "8000" + +build: + docker build -t jupyterhub:rsconnect-jupyter . + +start: + docker run --rm -p {{PORT}}:8000 --name jupyterhub jupyterhub:rsconnect-jupyter \ No newline at end of file diff --git a/notebooks2/.keep b/notebooks2/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/setup.cfg b/setup.cfg index 64257545..d3781c2a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,6 +25,6 @@ install_requires = setup_requires = setuptools packages = rsconnect_jupyter -python_requires = >=3.5 +python_requires = >=3.7 include_package_data = true zip_safe = false