mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-12 04:23:01 +00:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6f15113e2a | ||
![]() |
f3f08c9caa | ||
![]() |
c495c4731a | ||
![]() |
e08a50ef66 | ||
![]() |
fbcd792062 | ||
![]() |
bb81ce0160 | ||
![]() |
315087d67c | ||
![]() |
31e6a15a85 | ||
![]() |
aed99d8d19 | ||
![]() |
bedac5f148 | ||
![]() |
376aa13981 | ||
![]() |
4bc8b48763 | ||
![]() |
21496890f6 | ||
![]() |
70dcd50e44 | ||
![]() |
24094567e5 | ||
![]() |
6bd0febbe1 | ||
![]() |
57075aba52 | ||
![]() |
f0260aae52 | ||
![]() |
edd8e21f71 | ||
![]() |
681d3ce2d8 | ||
![]() |
97e792ccde | ||
![]() |
a5a0543b2a | ||
![]() |
5a810ccba3 | ||
![]() |
0a6b2cdadc | ||
![]() |
08903e7af8 | ||
![]() |
78439329c0 | ||
![]() |
4dfd6bc4b9 | ||
![]() |
574cc39b5f | ||
![]() |
6fb43a8241 | ||
![]() |
84c82fe382 | ||
![]() |
5e45e76f5b | ||
![]() |
92fd819cd6 | ||
![]() |
cb5ef0c302 | ||
![]() |
34fab033fe | ||
![]() |
37f4c4429e | ||
![]() |
293410ec94 | ||
![]() |
ed6ee27dcd | ||
![]() |
ca16ddb7ad |
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
Hi! Thanks for using JupyterHub.
|
||||
|
||||
If you are reporting an issue with JupyterHub, please use the [GitHub issue](https://github.com/jupyterhub/jupyterhub/issues) search feature to check if your issue has been asked already. If it has, please add your comments to the existing issue.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
- Running `jupyter troubleshoot` from the command line, if possible, and posting
|
||||
its output would also be helpful.
|
||||
- Running in `--debug` mode can also be helpful for troubleshooting.
|
7
.github/ISSUE_TEMPLATE/installation-and-configuration-issues.md
vendored
Normal file
7
.github/ISSUE_TEMPLATE/installation-and-configuration-issues.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Installation and configuration issues
|
||||
about: Installation and configuration assistance
|
||||
|
||||
---
|
||||
|
||||
If you are having issues with installation or configuration, you may ask for help on the JupyterHub gitter channel or file an issue here.
|
0
.github/PULL_REQUEST_TEMPLATE/.keep
vendored
Normal file
0
.github/PULL_REQUEST_TEMPLATE/.keep
vendored
Normal file
29
.github/issue_template.md
vendored
29
.github/issue_template.md
vendored
@@ -1,29 +0,0 @@
|
||||
Hi! Thanks for using JupyterHub.
|
||||
|
||||
If you are reporting an issue with JupyterHub:
|
||||
|
||||
- Please use the [GitHub issue](https://github.com/jupyterhub/jupyterhub/issues)
|
||||
search feature to check if your issue has been asked already. If it has,
|
||||
please add your comments to the existing issue.
|
||||
|
||||
- Where applicable, please fill out the details below to help us troubleshoot
|
||||
the issue that you are facing. Please be as thorough as you are able to
|
||||
provide details on the issue.
|
||||
|
||||
**How to reproduce the issue**
|
||||
|
||||
**What you expected to happen**
|
||||
|
||||
**What actually happens**
|
||||
|
||||
**Share what version of JupyterHub you are using**
|
||||
|
||||
Running `jupyter troubleshoot` from the command line, if possible, and posting
|
||||
its output would also be helpful.
|
||||
|
||||
```
|
||||
|
||||
Insert jupyter troubleshoot output here
|
||||
|
||||
|
||||
```
|
1
CODE_OF_CONDUCT.md
Normal file
1
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1 @@
|
||||
Please refer to [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md).
|
1
PULL_REQUEST_TEMPLATE.md
Normal file
1
PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1 @@
|
||||
|
@@ -81,7 +81,7 @@ for administration of the Hub and its users.
|
||||
To install JupyterHub along with its dependencies including nodejs/npm:
|
||||
|
||||
```bash
|
||||
conda install jupyterhub
|
||||
conda install -c conda-forge jupyterhub
|
||||
```
|
||||
|
||||
If you plan to run notebook servers locally, install the Jupyter notebook
|
||||
|
@@ -252,6 +252,17 @@ paths:
|
||||
$ref: '#/definitions/Token'
|
||||
post:
|
||||
summary: Create a new token for the user
|
||||
parameters:
|
||||
- name: expires_in
|
||||
type: number
|
||||
required: false
|
||||
in: body
|
||||
description: lifetime (in seconds) after which the requested token will expire.
|
||||
- name: note
|
||||
type: string
|
||||
required: false
|
||||
in: body
|
||||
description: A note attached to the token for future bookkeeping
|
||||
responses:
|
||||
'201':
|
||||
description: The newly created token
|
||||
@@ -716,6 +727,10 @@ definitions:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when this token was created
|
||||
expires_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when this token expires. Null if there is no expiry.
|
||||
last_activity:
|
||||
type: string
|
||||
format: date-time
|
||||
|
@@ -9,7 +9,7 @@ command line for details.
|
||||
|
||||
## 0.9
|
||||
|
||||
### 0.9.0
|
||||
### [0.9.0] 2018-06-15
|
||||
|
||||
JupyterHub 0.9 is a major upgrade of JupyterHub.
|
||||
There are several changes to the database schema,
|
||||
@@ -93,6 +93,12 @@ and tornado < 5.0.
|
||||
- Add session-id cookie, enabling immediate revocation of login tokens.
|
||||
- Authenticators may specify that users are admins by specifying the `admin` key when return the user model as a dict.
|
||||
- Added "Start All" button to admin page for launching all user servers at once.
|
||||
- Services have an `info` field which is a dictionary.
|
||||
This is accessible via the REST API.
|
||||
- `JupyterHub.extra_handlers` allows defining additonal tornado RequestHandlers attached to the Hub.
|
||||
- API tokens may now expire.
|
||||
Expiry is available in the REST model as `expires_at`,
|
||||
and settable when creating API tokens by specifying `expires_in`.
|
||||
|
||||
|
||||
#### Fixed
|
||||
@@ -113,6 +119,11 @@ and tornado < 5.0.
|
||||
- Various fixes in race conditions and performance improvements with the default proxy.
|
||||
- Fixes for CORS headers
|
||||
- Stop setting `.form-control` on spawner form inputs unconditionally.
|
||||
- Better recovery from database errors and database connection issues
|
||||
without having to restart the Hub.
|
||||
- Fix handling of `~` character in usernames.
|
||||
- Fix jupyterhub startup when `getpass.getuser()` would fail,
|
||||
e.g. due to missing entry in passwd file in containers.
|
||||
|
||||
|
||||
## 0.8
|
||||
@@ -368,7 +379,8 @@ Fix removal of `/login` page in 0.4.0, breaking some OAuth providers.
|
||||
First preview release
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/jupyterhub/jupyterhub/compare/0.8.1...HEAD
|
||||
[Unreleased]: https://github.com/jupyterhub/jupyterhub/compare/0.9.0...HEAD
|
||||
[0.9.0]: https://github.com/jupyterhub/jupyterhub/compare/0.8.1...0.9.0
|
||||
[0.8.1]: https://github.com/jupyterhub/jupyterhub/compare/0.8.0...0.8.1
|
||||
[0.8.0]: https://github.com/jupyterhub/jupyterhub/compare/0.7.2...0.8.0
|
||||
[0.7.2]: https://github.com/jupyterhub/jupyterhub/compare/0.7.1...0.7.2
|
||||
|
@@ -3,38 +3,65 @@
|
||||
Project Jupyter thanks the following people for their help and
|
||||
contribution on JupyterHub:
|
||||
|
||||
- adelcast
|
||||
- Analect
|
||||
- anderbubble
|
||||
- anikitml
|
||||
- ankitksharma
|
||||
- apetresc
|
||||
- athornton
|
||||
- barrachri
|
||||
- BerserkerTroll
|
||||
- betatim
|
||||
- Carreau
|
||||
- cfournie
|
||||
- charnpreetsingh
|
||||
- chicovenancio
|
||||
- cikao
|
||||
- ckald
|
||||
- cmoscardi
|
||||
- consideRatio
|
||||
- cqzlxl
|
||||
- CRegenschein
|
||||
- cwaldbieser
|
||||
- danielballen
|
||||
- danoventa
|
||||
- daradib
|
||||
- darky2004
|
||||
- datapolitan
|
||||
- dblockow-d2dcrc
|
||||
- DeepHorizons
|
||||
- DerekHeldtWerle
|
||||
- dhirschfeld
|
||||
- dietmarw
|
||||
- dingc3
|
||||
- dmartzol
|
||||
- DominicFollettSmith
|
||||
- dsblank
|
||||
- dtaniwaki
|
||||
- echarles
|
||||
- ellisonbg
|
||||
- emmanuel
|
||||
- evanlinde
|
||||
- Fokko
|
||||
- fperez
|
||||
- franga2000
|
||||
- GladysNalvarte
|
||||
- glenak1911
|
||||
- gweis
|
||||
- iamed18
|
||||
- jamescurtin
|
||||
- JamiesHQ
|
||||
- JasonJWilliamsNY
|
||||
- jbweston
|
||||
- jdavidheiser
|
||||
- jencabral
|
||||
- jhamrick
|
||||
- jkinkead
|
||||
- johnkpark
|
||||
- josephtate
|
||||
- jzf2101
|
||||
- karfai
|
||||
- kinuax
|
||||
- KrishnaPG
|
||||
- kroq-gar78
|
||||
@@ -44,27 +71,44 @@ contribution on JupyterHub:
|
||||
- minrk
|
||||
- mistercrunch
|
||||
- Mistobaan
|
||||
- mpacer
|
||||
- mwmarkland
|
||||
- ndly
|
||||
- nthiery
|
||||
- nxg
|
||||
- ObiWahn
|
||||
- ozancaglayan
|
||||
- paccorsi
|
||||
- parente
|
||||
- PeterDaveHello
|
||||
- peterruppel
|
||||
- phill84
|
||||
- pjamason
|
||||
- prasadkatti
|
||||
- rafael-ladislau
|
||||
- rcthomas
|
||||
- rgbkrk
|
||||
- rkdarst
|
||||
- robnagler
|
||||
- rschroll
|
||||
- ryanlovett
|
||||
- sangramga
|
||||
- Scrypy
|
||||
- schon
|
||||
- shreddd
|
||||
- Siecje
|
||||
- smiller5678
|
||||
- spoorthyv
|
||||
- ssanderson
|
||||
- summerswallow
|
||||
- syutbai
|
||||
- takluyver
|
||||
- temogen
|
||||
- ThomasMChen
|
||||
- Thoralf Gutierrez
|
||||
- timfreund
|
||||
- TimShawver
|
||||
- tklever
|
||||
- Todd-Z-Li
|
||||
- toobaz
|
||||
- tsaeger
|
||||
|
147
docs/source/reference/config-user-env.md
Normal file
147
docs/source/reference/config-user-env.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Configuring user environments
|
||||
|
||||
Deploying JupyterHub means you are providing Jupyter notebook environments for
|
||||
multiple users. Often, this includes a desire to configure the user
|
||||
environment in some way.
|
||||
|
||||
Since the `jupyterhub-singleuser` server extends the standard Jupyter notebook
|
||||
server, most configuration and documentation that applies to Jupyter Notebook
|
||||
applies to the single-user environments. Configuration of user environments
|
||||
typically does not occur through JupyterHub itself, but rather through system-
|
||||
wide configuration of Jupyter, which is inherited by `jupyterhub-singleuser`.
|
||||
|
||||
**Tip:** When searching for configuration tips for JupyterHub user
|
||||
environments, try removing JupyterHub from your search because there are a lot
|
||||
more people out there configuring Jupyter than JupyterHub and the
|
||||
configuration is the same.
|
||||
|
||||
This section will focus on user environments, including:
|
||||
|
||||
- Installing packages
|
||||
- Configuring Jupyter and IPython
|
||||
- Installing kernelspecs
|
||||
- Using containers vs. multi-user hosts
|
||||
|
||||
|
||||
## Installing packages
|
||||
|
||||
To make packages available to users, you generally will install packages
|
||||
system-wide or in a shared environment.
|
||||
|
||||
This installation location should always be in the same environment that
|
||||
`jupyterhub-singleuser` itself is installed in, and must be *readable and
|
||||
executable* by your users. If you want users to be able to install additional
|
||||
packages, it must also be *writable* by your users.
|
||||
|
||||
If you are using a standard system Python install, you would use:
|
||||
|
||||
|
||||
```bash
|
||||
sudo python3 -m pip install numpy
|
||||
```
|
||||
|
||||
to install the numpy package in the default system Python 3 environment
|
||||
(typically `/usr/local`).
|
||||
|
||||
You may also use conda to install packages. If you do, you should make sure
|
||||
that the conda environment has appropriate permissions for users to be able to
|
||||
run Python code in the env.
|
||||
|
||||
|
||||
## Configuring Jupyter and IPython
|
||||
|
||||
[Jupyter](https://jupyter-notebook.readthedocs.io/en/stable/config_overview.html)
|
||||
and [IPython](https://ipython.readthedocs.io/en/stable/development/config.html)
|
||||
have their own configuration systems.
|
||||
|
||||
As a JupyterHub administrator, you will typically want to install and configure
|
||||
environments for all JupyterHub users. For example, you wish for each student in
|
||||
a class to have the same user environment configuration.
|
||||
|
||||
Jupyter and IPython support **"system-wide"** locations for configuration, which
|
||||
is the logical place to put global configuration that you want to affect all
|
||||
users. It's generally more efficient to configure user environments "system-wide",
|
||||
and it's a good idea to avoid creating files in users' home directories.
|
||||
|
||||
The typical locations for these config files are:
|
||||
- **system-wide** in `/etc/{jupyter|ipython}`
|
||||
- **env-wide** (environment wide) in `{sys.prefix}/etc/{jupyter|ipython}`.
|
||||
|
||||
### Example: Enable an extension system-wide
|
||||
|
||||
For example, to enable the `cython` IPython extension for all of your users,
|
||||
create the file `/etc/ipython/ipython_config.py`:
|
||||
|
||||
```python
|
||||
c.InteractiveShellApp.extensions.append("cython")
|
||||
```
|
||||
|
||||
### Example: Enable a Jupyter notebook configuration setting for all users
|
||||
|
||||
To enable Jupyter notebook's internal idle-shutdown behavior (requires
|
||||
notebook ≥ 5.4), set the following in the `/etc/jupyter/jupyter_notebook_config.py`
|
||||
file:
|
||||
|
||||
```python
|
||||
# shutdown the server after no activity for an hour
|
||||
c.NotebookApp.shutdown_no_activity_timeout = 60 * 60
|
||||
# shutdown kernels after no activity for 20 minutes
|
||||
c.MappingKernelManager.cull_idle_timeout = 20 * 60
|
||||
# check for idle kernels every two minutes
|
||||
c.MappingKernelManager.cull_interval = 2 * 60
|
||||
```
|
||||
|
||||
|
||||
## Installing kernelspecs
|
||||
|
||||
You may have multiple Jupyter kernels installed and want to make sure that
|
||||
they are available to all of your users. This means installing kernelspecs
|
||||
either system-wide (e.g. in /usr/local/) or in the `sys.prefix` of JupyterHub
|
||||
itself.
|
||||
|
||||
Jupyter kernelspec installation is system wide by default, but some kernels
|
||||
may default to installing kernelspecs in your home directory. These will need
|
||||
to be moved system-wide to ensure that they are accessible.
|
||||
|
||||
You can see where your kernelspecs are with:
|
||||
|
||||
```bash
|
||||
jupyter kernelspec list
|
||||
```
|
||||
|
||||
### Example: Installing kernels system-wide
|
||||
|
||||
Assuming I have a Python 2 and Python 3 environment that I want to make
|
||||
sure are available, I can install their specs system-wide (in /usr/local) with:
|
||||
|
||||
```bash
|
||||
/path/to/python3 -m IPython kernel install --prefix=/usr/local
|
||||
/path/to/python2 -m IPython kernel install --prefix=/usr/local
|
||||
```
|
||||
|
||||
|
||||
## Multi-user hosts vs. Containers
|
||||
|
||||
There are two broad categories of user environments that depend on what
|
||||
Spawner you choose:
|
||||
|
||||
- Multi-user hosts (shared sytem)
|
||||
- Container-based
|
||||
|
||||
How you configure user environments for each category can differ a bit
|
||||
depending on what Spawner you are using.
|
||||
|
||||
The first category is a **shared system (multi-user host)** where
|
||||
each user has a JupyterHub account and a home directory as well as being
|
||||
a real system user. In this example, shared configuration and installation
|
||||
must be in a 'system-wide' location, such as `/etc/` or `/usr/local`
|
||||
or a custom prefix such as `/opt/conda`.
|
||||
|
||||
When JupyterHub uses **container-based** Spawners (e.g. KubeSpawner or
|
||||
DockerSpawner), the 'system-wide' environment is really the container image
|
||||
which you are using for users.
|
||||
|
||||
In both cases, you want to *avoid putting configuration in user home
|
||||
directories* because users can change those configuration settings. Also,
|
||||
home directories typically persist once they are created, so they are
|
||||
difficult for admins to update later.
|
@@ -14,6 +14,7 @@ Technical Reference
|
||||
database
|
||||
upgrading
|
||||
templates
|
||||
config-user-env
|
||||
config-examples
|
||||
config-ghoauth
|
||||
config-proxy
|
||||
|
@@ -9,6 +9,7 @@ problem and how to resolve it.
|
||||
- sudospawner fails to run
|
||||
- What is the default behavior when none of the lists (admin, whitelist,
|
||||
group whitelist) are set?
|
||||
- JupyterHub Docker container not accessible at localhost
|
||||
|
||||
[*Errors*](#errors)
|
||||
- 500 error after spawning my single-user server
|
||||
@@ -63,6 +64,17 @@ this to a particular set of users, and the admin_users lets you specify who
|
||||
among them may use the admin interface (not necessary, unless you need to do
|
||||
things like inspect other users' servers, or modify the userlist at runtime).
|
||||
|
||||
### JupyterHub Docker container not accessible at localhost
|
||||
|
||||
Even though the command to start your Docker container exposes port 8000
|
||||
(`docker run -p 8000:8000 -d --name jupyterhub jupyterhub/jupyterhub jupyterhub`),
|
||||
it is possible that the IP address itself is not accessible/visible. As a result
|
||||
when you try http://localhost:8000 in your browser, you are unable to connect
|
||||
even though the container is running properly. One workaround is to explicitly
|
||||
tell Jupyterhub to start at `0.0.0.0` which is visible to everyone. Try this
|
||||
command:
|
||||
`docker run -p 8000:8000 -d --name jupyterhub jupyterhub/jupyterhub jupyterhub --ip 0.0.0.0 --port 8000`
|
||||
|
||||
|
||||
## Errors
|
||||
|
||||
|
@@ -7,7 +7,7 @@ version_info = (
|
||||
0,
|
||||
9,
|
||||
0,
|
||||
"rc1", # release (b1, rc1)
|
||||
"", # release (b1, rc1)
|
||||
# "dev", # dev
|
||||
)
|
||||
|
||||
|
@@ -976,8 +976,6 @@ class JupyterHub(Application):
|
||||
self.handlers = self.add_url_prefix(self.hub_prefix, h)
|
||||
# some extra handlers, outside hub_prefix
|
||||
self.handlers.extend([
|
||||
# add trailing / to `/hub`
|
||||
(self.hub_prefix.rstrip('/'), handlers.AddSlashHandler),
|
||||
# add trailing / to ``/user|services/:name`
|
||||
(r"%s(user|services)/([^/]+)" % self.base_url, handlers.AddSlashHandler),
|
||||
(r"(?!%s).*" % self.hub_prefix, handlers.PrefixRedirectHandler),
|
||||
|
@@ -1119,6 +1119,7 @@ class AddSlashHandler(BaseHandler):
|
||||
self.redirect(urlunparse(dest))
|
||||
|
||||
default_handlers = [
|
||||
(r'', AddSlashHandler), # add trailing / to `/hub`
|
||||
(r'/user/([^/]+)(/.*)?', UserSpawnHandler),
|
||||
(r'/user-redirect/(.*)?', UserRedirectHandler),
|
||||
(r'/security/csp-report', CSPReportHandler),
|
||||
|
@@ -336,7 +336,7 @@ class ProxyErrorHandler(BaseHandler):
|
||||
|
||||
|
||||
default_handlers = [
|
||||
(r'/?', RootHandler),
|
||||
(r'/', RootHandler),
|
||||
(r'/home', HomeHandler),
|
||||
(r'/admin', AdminHandler),
|
||||
(r'/spawn', SpawnHandler),
|
||||
|
@@ -574,7 +574,7 @@ class ConfigurableHTTPProxy(Proxy):
|
||||
"""
|
||||
# chp stores routes in unescaped form.
|
||||
# restore escaped-form we created it with.
|
||||
routespec = quote(chp_path, safe='@/')
|
||||
routespec = quote(chp_path, safe='@/~')
|
||||
if self.host_routing:
|
||||
# host routes don't start with /
|
||||
routespec = routespec.lstrip('/')
|
||||
|
@@ -154,6 +154,8 @@ def test_external_proxy(request):
|
||||
'zoe',
|
||||
'50fia',
|
||||
'秀樹',
|
||||
'~TestJH',
|
||||
'has@',
|
||||
])
|
||||
def test_check_routes(app, username, disable_check_routes):
|
||||
proxy = app.proxy
|
||||
|
15
setup.py
15
setup.py
@@ -83,6 +83,10 @@ for d, _, _ in os.walk('jupyterhub'):
|
||||
if os.path.exists(pjoin(d, '__init__.py')):
|
||||
packages.append(d.replace(os.path.sep, '.'))
|
||||
|
||||
with open('README.md', encoding="utf8") as f:
|
||||
readme = f.read()
|
||||
|
||||
|
||||
setup_args = dict(
|
||||
name = 'jupyterhub',
|
||||
scripts = glob(pjoin('scripts', '*')),
|
||||
@@ -93,10 +97,11 @@ setup_args = dict(
|
||||
package_data = get_package_data(),
|
||||
version = ns['__version__'],
|
||||
description = "JupyterHub: A multi-user server for Jupyter notebooks",
|
||||
long_description = "See https://jupyterhub.readthedocs.io for more info.",
|
||||
long_description = readme,
|
||||
long_description_content_type = 'text/markdown',
|
||||
author = "Jupyter Development Team",
|
||||
author_email = "jupyter@googlegroups.com",
|
||||
url = "http://jupyter.org",
|
||||
url = "https://jupyter.org",
|
||||
license = "BSD",
|
||||
platforms = "Linux, Mac OS X",
|
||||
keywords = ['Interactive', 'Interpreter', 'Shell', 'Web'],
|
||||
@@ -109,6 +114,12 @@ setup_args = dict(
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
],
|
||||
project_urls = {
|
||||
'Documentation': 'https://jupyterhub.readthedocs.io',
|
||||
'Funding': 'https://jupyter.org/about',
|
||||
'Source': 'https://github.com/jupyterhub/jupyterhub/',
|
||||
'Tracker': 'https://github.com/jupyterhub/jupyterhub/issues',
|
||||
},
|
||||
)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user