1{
2 lib,
3 stdenv,
4 alembic,
5 async-generator,
6 beautifulsoup4,
7 buildPythonPackage,
8 certipy,
9 configurable-http-proxy,
10 cryptography,
11 entrypoints,
12 fetchPypi,
13 fetchzip,
14 importlib-metadata,
15 jinja2,
16 jsonschema,
17 jupyter-telemetry,
18 jupyterlab,
19 jupyter-core,
20 jupyter-server,
21 mock,
22 nbclassic,
23 nodePackages,
24 notebook,
25 oauthlib,
26 packaging,
27 pamela,
28 playwright,
29 prometheus-client,
30 pytest-asyncio,
31 pytestCheckHook,
32 python-dateutil,
33 pythonOlder,
34 requests,
35 requests-mock,
36 selenium,
37 sqlalchemy,
38 tornado,
39 traitlets,
40 virtualenv,
41}:
42
43let
44 # js/css assets that setup.py tries to fetch via `npm install` when building
45 # from source. https://github.com/jupyterhub/jupyterhub/blob/master/package.json
46 bootstrap = fetchzip {
47 url = "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz";
48 sha256 = "1ywmxqdccg0mgx0xknrn1hlrfnhcwphc12y9l91zizx26fqfmzgc";
49 };
50 font-awesome = fetchzip {
51 url = "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz";
52 sha256 = "1xnxbdlfdd60z5ix152m8r2kk9dkwlqwpypky1mm3dv64ajnzdbk";
53 };
54 jquery = fetchzip {
55 url = "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz";
56 sha256 = "0yi9ql493din1qa1s923nd5zvd0klk1sx00xj1wx2yambmq86vm9";
57 };
58 moment = fetchzip {
59 url = "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz";
60 sha256 = "0ifzzla4zffw23g3xvhwx3fj3jny6cjzxfzl1x0317q8wa0c7w5i";
61 };
62 requirejs = fetchzip {
63 url = "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz";
64 sha256 = "165hkli3qcd59cjqvli9r5f92i0h7czkmhcg1cgwamw2d0b7xibz";
65 };
66in
67
68buildPythonPackage rec {
69 pname = "jupyterhub";
70 version = "4.1.5";
71 format = "setuptools";
72
73 disabled = pythonOlder "3.7";
74
75 src = fetchPypi {
76 inherit pname version;
77 hash = "sha256-Y7ofxxhDbBUZRqWKO0A+xP6LP2JPsZW8HY5ww5sz4ZQ=";
78 };
79
80 # Most of this only applies when building from source (e.g. js/css assets are
81 # pre-built and bundled in the official release tarball on pypi).
82 #
83 # Stuff that's always needed:
84 # * At runtime, we need configurable-http-proxy, so we substitute the store
85 # path.
86 #
87 # Other stuff that's only needed when building from source:
88 # * js/css assets are fetched from npm.
89 # * substitute store path for `lessc` commmand.
90 # * set up NODE_PATH so `lessc` can find `less-plugin-clean-css`.
91 # * don't run `npm install`.
92 preBuild = ''
93 export NODE_PATH=${nodePackages.less-plugin-clean-css}/lib/node_modules
94
95 substituteInPlace jupyterhub/proxy.py --replace \
96 "'configurable-http-proxy'" \
97 "'${configurable-http-proxy}/bin/configurable-http-proxy'"
98
99 substituteInPlace jupyterhub/tests/test_proxy.py --replace \
100 "'configurable-http-proxy'" \
101 "'${configurable-http-proxy}/bin/configurable-http-proxy'"
102
103 substituteInPlace setup.py --replace \
104 "'npm'" "'true'"
105
106 declare -A deps
107 deps[bootstrap]=${bootstrap}
108 deps[font-awesome]=${font-awesome}
109 deps[jquery]=${jquery}
110 deps[moment]=${moment}
111 deps[requirejs]=${requirejs}
112
113 mkdir -p share/jupyter/hub/static/components
114 for dep in "''${!deps[@]}"; do
115 if [ ! -e share/jupyter/hub/static/components/$dep ]; then
116 cp -r ''${deps[$dep]} share/jupyter/hub/static/components/$dep
117 fi
118 done
119 '';
120
121 propagatedBuildInputs = [
122 alembic
123 async-generator
124 certipy
125 python-dateutil
126 entrypoints
127 jinja2
128 jupyter-telemetry
129 oauthlib
130 packaging
131 pamela
132 prometheus-client
133 requests
134 selenium
135 sqlalchemy
136 tornado
137 traitlets
138 jupyter-core
139 jupyter-server
140 ] ++ lib.optionals (pythonOlder "3.10") [ importlib-metadata ];
141
142 nativeCheckInputs = [
143 beautifulsoup4
144 cryptography
145 notebook
146 jsonschema
147 nbclassic
148 mock
149 jupyterlab
150 playwright
151 pytest-asyncio
152 pytestCheckHook
153 requests-mock
154 virtualenv
155 ];
156
157 preCheck = ''
158 substituteInPlace jupyterhub/tests/test_spawner.py --replace \
159 "'jupyterhub-singleuser'" "'$out/bin/jupyterhub-singleuser'"
160 export PATH="$PATH:$out/bin";
161 '';
162
163 disabledTests = [
164 # Tries to install older versions through pip
165 "test_upgrade"
166 # Testcase fails to find requests import
167 "test_external_service"
168 # Attempts to do TLS connection
169 "test_connection_notebook_wrong_certs"
170 # AttributeError: 'coroutine' object...
171 "test_valid_events"
172 "test_invalid_events"
173 "test_user_group_roles"
174 ];
175
176 disabledTestPaths = [
177 # Not testing with a running instance
178 # AttributeError: 'coroutine' object has no attribute 'db'
179 "docs/test_docs.py"
180 "jupyterhub/tests/browser/test_browser.py"
181 "jupyterhub/tests/test_api.py"
182 "jupyterhub/tests/test_auth_expiry.py"
183 "jupyterhub/tests/test_auth.py"
184 "jupyterhub/tests/test_metrics.py"
185 "jupyterhub/tests/test_named_servers.py"
186 "jupyterhub/tests/test_orm.py"
187 "jupyterhub/tests/test_pages.py"
188 "jupyterhub/tests/test_proxy.py"
189 "jupyterhub/tests/test_scopes.py"
190 "jupyterhub/tests/test_services_auth.py"
191 "jupyterhub/tests/test_singleuser.py"
192 "jupyterhub/tests/test_spawner.py"
193 "jupyterhub/tests/test_user.py"
194 ];
195
196 meta = with lib; {
197 description = "Serves multiple Jupyter notebook instances";
198 homepage = "https://jupyter.org/";
199 changelog = "https://github.com/jupyterhub/jupyterhub/blob/${version}/docs/source/reference/changelog.md";
200 license = licenses.bsd3;
201 maintainers = teams.jupyter.members;
202 # darwin: E OSError: dlopen(/nix/store/43zml0mlr17r5jsagxr00xxx91hz9lky-openpam-20170430/lib/libpam.so, 6): image not found
203 broken = stdenv.isDarwin;
204 };
205}