We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
When to drop Python 3.3 support
Initially released in 2012, Python 3.3 reaches EOL on 09-29-2017.

[python-dev mailing list reference](https://mail.python.org/pipermail/python-dev/2017-July/148584.html)

I would recommend officially dropping Python 3.3 support for installation in JupyterHub 0.8.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `setup.py`
Content:
```
1 #!/usr/bin/env python3
2 # coding: utf-8
3 
4 # Copyright (c) Juptyer Development Team.
5 # Distributed under the terms of the Modified BSD License.
6 
7 #-----------------------------------------------------------------------------
8 # Minimal Python version sanity check (from IPython)
9 #-----------------------------------------------------------------------------
10 
11 from __future__ import print_function
12 
13 import os
14 import shutil
15 import sys
16 
17 v = sys.version_info
18 if v[:2] < (3,3):
19     error = "ERROR: JupyterHub requires Python version 3.3 or above."
20     print(error, file=sys.stderr)
21     sys.exit(1)
22 
23 shell = False
24 if os.name in ('nt', 'dos'):
25     shell = True
26     warning = "WARNING: Windows is not officially supported"
27     print(warning, file=sys.stderr)
28 
29 # At least we're on the python version we need, move on.
30 
31 import os
32 from glob import glob
33 from subprocess import check_call
34 
35 from setuptools import setup
36 from setuptools.command.bdist_egg import bdist_egg
37 
38 pjoin = os.path.join
39 
40 here = os.path.abspath(os.path.dirname(__file__))
41 share_jupyter = pjoin(here, 'share', 'jupyter', 'hub')
42 static = pjoin(share_jupyter, 'static')
43 
44 is_repo = os.path.exists(pjoin(here, '.git'))
45 
46 #---------------------------------------------------------------------------
47 # Build basic package data, etc.
48 #---------------------------------------------------------------------------
49 
50 def get_data_files():
51     """Get data files in share/jupyter"""
52     
53     data_files = []
54     ntrim = len(here + os.path.sep)
55     
56     for (d, dirs, filenames) in os.walk(share_jupyter):
57         data_files.append((
58             d[ntrim:],
59             [ pjoin(d, f) for f in filenames ]
60         ))
61     return data_files
62 
63 def get_package_data():
64     """Get package data
65 
66     (mostly alembic config)
67     """
68     package_data = {}
69     package_data['jupyterhub'] = [
70         'alembic.ini',
71         'alembic/*',
72         'alembic/versions/*',
73     ]
74     return package_data
75 
76 ns = {}
77 with open(pjoin(here, 'jupyterhub', '_version.py')) as f:
78     exec(f.read(), {}, ns)
79 
80 
81 packages = []
82 for d, _, _ in os.walk('jupyterhub'):
83     if os.path.exists(pjoin(d, '__init__.py')):
84         packages.append(d.replace(os.path.sep, '.'))
85 
86 setup_args = dict(
87     name                = 'jupyterhub',
88     scripts             = glob(pjoin('scripts', '*')),
89     packages            = packages,
90                         # dummy, so that install_data doesn't get skipped
91                         # this will be overridden when bower is run anyway
92     data_files          = get_data_files() or ['dummy'],
93     package_data        = get_package_data(),
94     version             = ns['__version__'],
95     description         = "JupyterHub: A multi-user server for Jupyter notebooks",
96     long_description    = "See https://jupyterhub.readthedocs.io for more info.",
97     author              = "Jupyter Development Team",
98     author_email        = "jupyter@googlegroups.com",
99     url                 = "http://jupyter.org",
100     license             = "BSD",
101     platforms           = "Linux, Mac OS X",
102     keywords            = ['Interactive', 'Interpreter', 'Shell', 'Web'],
103     classifiers         = [
104         'Intended Audience :: Developers',
105         'Intended Audience :: System Administrators',
106         'Intended Audience :: Science/Research',
107         'License :: OSI Approved :: BSD License',
108         'Programming Language :: Python',
109         'Programming Language :: Python :: 3',
110     ],
111 )
112 
113 #---------------------------------------------------------------------------
114 # custom distutils commands
115 #---------------------------------------------------------------------------
116 
117 # imports here, so they are after setuptools import if there was one
118 from distutils.cmd import Command
119 from distutils.command.build_py import build_py
120 from distutils.command.sdist import sdist
121 
122 
123 npm_path = os.pathsep.join([
124     pjoin(here, 'node_modules', '.bin'),
125     os.environ.get("PATH", os.defpath),
126 ])
127 
128 
129 def mtime(path):
130     """shorthand for mtime"""
131     return os.stat(path).st_mtime
132 
133 
134 class BaseCommand(Command):
135     """Dumb empty command because Command needs subclasses to override too much"""
136     user_options = []
137     
138     def initialize_options(self):
139         pass
140     
141     def finalize_options(self):
142         pass
143     
144     def get_inputs(self):
145         return []
146     
147     def get_outputs(self):
148         return []
149 
150 
151 class Bower(BaseCommand):
152     description = "fetch static client-side components with bower"
153     
154     user_options = []
155     bower_dir = pjoin(static, 'components')
156     node_modules = pjoin(here, 'node_modules')
157     
158     def should_run(self):
159         if not os.path.exists(self.bower_dir):
160             return True
161         return mtime(self.bower_dir) < mtime(pjoin(here, 'bower.json'))
162 
163     def should_run_npm(self):
164         if not shutil.which('npm'):
165             print("npm unavailable", file=sys.stderr)
166             return False
167         if not os.path.exists(self.node_modules):
168             return True
169         return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))
170     
171     def run(self):
172         if not self.should_run():
173             print("bower dependencies up to date")
174             return
175         
176         if self.should_run_npm():
177             print("installing build dependencies with npm")
178             check_call(['npm', 'install', '--progress=false'], cwd=here, shell=shell)
179             os.utime(self.node_modules)
180         
181         env = os.environ.copy()
182         env['PATH'] = npm_path
183         args = ['bower', 'install', '--allow-root', '--config.interactive=false']
184         try:
185             check_call(args, cwd=here, env=env, shell=shell)
186         except OSError as e:
187             print("Failed to run bower: %s" % e, file=sys.stderr)
188             print("You can install js dependencies with `npm install`", file=sys.stderr)
189             raise
190         os.utime(self.bower_dir)
191         # update data-files in case this created new files
192         self.distribution.data_files = get_data_files()
193 
194 
195 class CSS(BaseCommand):
196     description = "compile CSS from LESS"
197     
198     def should_run(self):
199         """Does less need to run?"""
200         # from IPython.html.tasks.py
201         
202         css_targets = [pjoin(static, 'css', 'style.min.css')]
203         css_maps = [t + '.map' for t in css_targets]
204         targets = css_targets + css_maps
205         if not all(os.path.exists(t) for t in targets):
206             # some generated files don't exist
207             return True
208         earliest_target = sorted(mtime(t) for t in targets)[0]
209     
210         # check if any .less files are newer than the generated targets
211         for (dirpath, dirnames, filenames) in os.walk(static):
212             for f in filenames:
213                 if f.endswith('.less'):
214                     path = pjoin(static, dirpath, f)
215                     timestamp = mtime(path)
216                     if timestamp > earliest_target:
217                         return True
218     
219         return False
220     
221     def run(self):
222         if not self.should_run():
223             print("CSS up-to-date")
224             return
225         
226         self.run_command('js')
227         
228         style_less = pjoin(static, 'less', 'style.less')
229         style_css = pjoin(static, 'css', 'style.min.css')
230         sourcemap = style_css + '.map'
231         
232         env = os.environ.copy()
233         env['PATH'] = npm_path
234         args = [
235             'lessc', '--clean-css',
236             '--source-map-basepath={}'.format(static),
237             '--source-map={}'.format(sourcemap),
238             '--source-map-rootpath=../',
239             style_less, style_css,
240         ]
241         try:
242             check_call(args, cwd=here, env=env, shell=shell)
243         except OSError as e:
244             print("Failed to run lessc: %s" % e, file=sys.stderr)
245             print("You can install js dependencies with `npm install`", file=sys.stderr)
246             raise
247         # update data-files in case this created new files
248         self.distribution.data_files = get_data_files()
249 
250 
251 def js_css_first(cls, strict=True):
252     class Command(cls):
253         def run(self):
254             try:
255                 self.run_command('js')
256                 self.run_command('css')
257             except Exception:
258                 if strict:
259                     raise
260                 else:
261                     pass
262             return super().run()
263     return Command
264 
265 
266 class bdist_egg_disabled(bdist_egg):
267     """Disabled version of bdist_egg
268 
269     Prevents setup.py install from performing setuptools' default easy_install,
270     which it should never ever do.
271     """
272     def run(self):
273         sys.exit("Aborting implicit building of eggs. Use `pip install .` to install from source.")
274 
275 
276 setup_args['cmdclass'] = {
277     'js': Bower,
278     'css': CSS,
279     'build_py': js_css_first(build_py, strict=is_repo),
280     'sdist': js_css_first(sdist, strict=True),
281     'bdist_egg': bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled,
282 }
283 
284 
285 # setuptools requirements
286 
287 setup_args['zip_safe'] = False
288 from setuptools.command.develop import develop
289 class develop_js_css(develop):
290     def run(self):
291         if not self.uninstall:
292             self.distribution.run_command('js')
293             self.distribution.run_command('css')
294         develop.run(self)
295 setup_args['cmdclass']['develop'] = develop_js_css
296 setup_args['install_requires'] = install_requires = []
297 
298 with open('requirements.txt') as f:
299     for line in f.readlines():
300         req = line.strip()
301         if not req or req.startswith('#') or '://' in req:
302             continue
303         install_requires.append(req)
304 
305 #---------------------------------------------------------------------------
306 # setup
307 #---------------------------------------------------------------------------
308 
309 def main():
310     setup(**setup_args)
311 
312 if __name__ == '__main__':
313     main()
314
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -15,8 +15,8 @@
 import sys
 
 v = sys.version_info
-if v[:2] < (3,3):
-    error = "ERROR: JupyterHub requires Python version 3.3 or above."
+if v[:2] < (3,4):
+    error = "ERROR: JupyterHub requires Python version 3.4 or above."
     print(error, file=sys.stderr)
     sys.exit(1)
 
@@ -49,10 +49,10 @@
 
 def get_data_files():
     """Get data files in share/jupyter"""
-    
+
     data_files = []
     ntrim = len(here + os.path.sep)
-    
+
     for (d, dirs, filenames) in os.walk(share_jupyter):
         data_files.append((
             d[ntrim:],
@@ -134,27 +134,27 @@
 class BaseCommand(Command):
     """Dumb empty command because Command needs subclasses to override too much"""
     user_options = []
-    
+
     def initialize_options(self):
         pass
-    
+
     def finalize_options(self):
         pass
-    
+
     def get_inputs(self):
         return []
-    
+
     def get_outputs(self):
         return []
 
 
 class Bower(BaseCommand):
     description = "fetch static client-side components with bower"
-    
+
     user_options = []
     bower_dir = pjoin(static, 'components')
     node_modules = pjoin(here, 'node_modules')
-    
+
     def should_run(self):
         if not os.path.exists(self.bower_dir):
             return True
@@ -167,17 +167,17 @@
         if not os.path.exists(self.node_modules):
             return True
         return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))
-    
+
     def run(self):
         if not self.should_run():
             print("bower dependencies up to date")
             return
-        
+
         if self.should_run_npm():
             print("installing build dependencies with npm")
             check_call(['npm', 'install', '--progress=false'], cwd=here, shell=shell)
             os.utime(self.node_modules)
-        
+
         env = os.environ.copy()
         env['PATH'] = npm_path
         args = ['bower', 'install', '--allow-root', '--config.interactive=false']
@@ -194,11 +194,11 @@
 
 class CSS(BaseCommand):
     description = "compile CSS from LESS"
-    
+
     def should_run(self):
         """Does less need to run?"""
         # from IPython.html.tasks.py
-        
+
         css_targets = [pjoin(static, 'css', 'style.min.css')]
         css_maps = [t + '.map' for t in css_targets]
         targets = css_targets + css_maps
@@ -206,7 +206,7 @@
             # some generated files don't exist
             return True
         earliest_target = sorted(mtime(t) for t in targets)[0]
-    
+
         # check if any .less files are newer than the generated targets
         for (dirpath, dirnames, filenames) in os.walk(static):
             for f in filenames:
@@ -215,20 +215,20 @@
                     timestamp = mtime(path)
                     if timestamp > earliest_target:
                         return True
-    
+
         return False
-    
+
     def run(self):
         if not self.should_run():
             print("CSS up-to-date")
             return
-        
+
         self.run_command('js')
-        
+
         style_less = pjoin(static, 'less', 'style.less')
         style_css = pjoin(static, 'css', 'style.min.css')
         sourcemap = style_css + '.map'
-        
+
         env = os.environ.copy()
         env['PATH'] = npm_path
         args = [

{"golden_diff": "diff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -15,8 +15,8 @@\n import sys\n \n v = sys.version_info\n-if v[:2] < (3,3):\n-    error = \"ERROR: JupyterHub requires Python version 3.3 or above.\"\n+if v[:2] < (3,4):\n+    error = \"ERROR: JupyterHub requires Python version 3.4 or above.\"\n     print(error, file=sys.stderr)\n     sys.exit(1)\n \n@@ -49,10 +49,10 @@\n \n def get_data_files():\n     \"\"\"Get data files in share/jupyter\"\"\"\n-    \n+\n     data_files = []\n     ntrim = len(here + os.path.sep)\n-    \n+\n     for (d, dirs, filenames) in os.walk(share_jupyter):\n         data_files.append((\n             d[ntrim:],\n@@ -134,27 +134,27 @@\n class BaseCommand(Command):\n     \"\"\"Dumb empty command because Command needs subclasses to override too much\"\"\"\n     user_options = []\n-    \n+\n     def initialize_options(self):\n         pass\n-    \n+\n     def finalize_options(self):\n         pass\n-    \n+\n     def get_inputs(self):\n         return []\n-    \n+\n     def get_outputs(self):\n         return []\n \n \n class Bower(BaseCommand):\n     description = \"fetch static client-side components with bower\"\n-    \n+\n     user_options = []\n     bower_dir = pjoin(static, 'components')\n     node_modules = pjoin(here, 'node_modules')\n-    \n+\n     def should_run(self):\n         if not os.path.exists(self.bower_dir):\n             return True\n@@ -167,17 +167,17 @@\n         if not os.path.exists(self.node_modules):\n             return True\n         return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))\n-    \n+\n     def run(self):\n         if not self.should_run():\n             print(\"bower dependencies up to date\")\n             return\n-        \n+\n         if self.should_run_npm():\n             print(\"installing build dependencies with npm\")\n             check_call(['npm', 'install', '--progress=false'], cwd=here, shell=shell)\n             os.utime(self.node_modules)\n-        \n+\n         env = os.environ.copy()\n         env['PATH'] = npm_path\n         args = ['bower', 'install', '--allow-root', '--config.interactive=false']\n@@ -194,11 +194,11 @@\n \n class CSS(BaseCommand):\n     description = \"compile CSS from LESS\"\n-    \n+\n     def should_run(self):\n         \"\"\"Does less need to run?\"\"\"\n         # from IPython.html.tasks.py\n-        \n+\n         css_targets = [pjoin(static, 'css', 'style.min.css')]\n         css_maps = [t + '.map' for t in css_targets]\n         targets = css_targets + css_maps\n@@ -206,7 +206,7 @@\n             # some generated files don't exist\n             return True\n         earliest_target = sorted(mtime(t) for t in targets)[0]\n-    \n+\n         # check if any .less files are newer than the generated targets\n         for (dirpath, dirnames, filenames) in os.walk(static):\n             for f in filenames:\n@@ -215,20 +215,20 @@\n                     timestamp = mtime(path)\n                     if timestamp > earliest_target:\n                         return True\n-    \n+\n         return False\n-    \n+\n     def run(self):\n         if not self.should_run():\n             print(\"CSS up-to-date\")\n             return\n-        \n+\n         self.run_command('js')\n-        \n+\n         style_less = pjoin(static, 'less', 'style.less')\n         style_css = pjoin(static, 'css', 'style.min.css')\n         sourcemap = style_css + '.map'\n-        \n+\n         env = os.environ.copy()\n         env['PATH'] = npm_path\n         args = [\n", "issue": "When to drop Python 3.3 support\nInitially released in 2012, Python 3.3 reaches EOL on 09-29-2017.\r\n\r\n[python-dev mailing list reference](https://mail.python.org/pipermail/python-dev/2017-July/148584.html)\r\n\r\nI would recommend officially dropping Python 3.3 support for installation in JupyterHub 0.8.\n", "before_files": [{"content": "#!/usr/bin/env python3\n# coding: utf-8\n\n# Copyright (c) Juptyer Development Team.\n# Distributed under the terms of the Modified BSD License.\n\n#-----------------------------------------------------------------------------\n# Minimal Python version sanity check (from IPython)\n#-----------------------------------------------------------------------------\n\nfrom __future__ import print_function\n\nimport os\nimport shutil\nimport sys\n\nv = sys.version_info\nif v[:2] < (3,3):\n    error = \"ERROR: JupyterHub requires Python version 3.3 or above.\"\n    print(error, file=sys.stderr)\n    sys.exit(1)\n\nshell = False\nif os.name in ('nt', 'dos'):\n    shell = True\n    warning = \"WARNING: Windows is not officially supported\"\n    print(warning, file=sys.stderr)\n\n# At least we're on the python version we need, move on.\n\nimport os\nfrom glob import glob\nfrom subprocess import check_call\n\nfrom setuptools import setup\nfrom setuptools.command.bdist_egg import bdist_egg\n\npjoin = os.path.join\n\nhere = os.path.abspath(os.path.dirname(__file__))\nshare_jupyter = pjoin(here, 'share', 'jupyter', 'hub')\nstatic = pjoin(share_jupyter, 'static')\n\nis_repo = os.path.exists(pjoin(here, '.git'))\n\n#---------------------------------------------------------------------------\n# Build basic package data, etc.\n#---------------------------------------------------------------------------\n\ndef get_data_files():\n    \"\"\"Get data files in share/jupyter\"\"\"\n    \n    data_files = []\n    ntrim = len(here + os.path.sep)\n    \n    for (d, dirs, filenames) in os.walk(share_jupyter):\n        data_files.append((\n            d[ntrim:],\n            [ pjoin(d, f) for f in filenames ]\n        ))\n    return data_files\n\ndef get_package_data():\n    \"\"\"Get package data\n\n    (mostly alembic config)\n    \"\"\"\n    package_data = {}\n    package_data['jupyterhub'] = [\n        'alembic.ini',\n        'alembic/*',\n        'alembic/versions/*',\n    ]\n    return package_data\n\nns = {}\nwith open(pjoin(here, 'jupyterhub', '_version.py')) as f:\n    exec(f.read(), {}, ns)\n\n\npackages = []\nfor d, _, _ in os.walk('jupyterhub'):\n    if os.path.exists(pjoin(d, '__init__.py')):\n        packages.append(d.replace(os.path.sep, '.'))\n\nsetup_args = dict(\n    name                = 'jupyterhub',\n    scripts             = glob(pjoin('scripts', '*')),\n    packages            = packages,\n                        # dummy, so that install_data doesn't get skipped\n                        # this will be overridden when bower is run anyway\n    data_files          = get_data_files() or ['dummy'],\n    package_data        = get_package_data(),\n    version             = ns['__version__'],\n    description         = \"JupyterHub: A multi-user server for Jupyter notebooks\",\n    long_description    = \"See https://jupyterhub.readthedocs.io for more info.\",\n    author              = \"Jupyter Development Team\",\n    author_email        = \"jupyter@googlegroups.com\",\n    url                 = \"http://jupyter.org\",\n    license             = \"BSD\",\n    platforms           = \"Linux, Mac OS X\",\n    keywords            = ['Interactive', 'Interpreter', 'Shell', 'Web'],\n    classifiers         = [\n        'Intended Audience :: Developers',\n        'Intended Audience :: System Administrators',\n        'Intended Audience :: Science/Research',\n        'License :: OSI Approved :: BSD License',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 3',\n    ],\n)\n\n#---------------------------------------------------------------------------\n# custom distutils commands\n#---------------------------------------------------------------------------\n\n# imports here, so they are after setuptools import if there was one\nfrom distutils.cmd import Command\nfrom distutils.command.build_py import build_py\nfrom distutils.command.sdist import sdist\n\n\nnpm_path = os.pathsep.join([\n    pjoin(here, 'node_modules', '.bin'),\n    os.environ.get(\"PATH\", os.defpath),\n])\n\n\ndef mtime(path):\n    \"\"\"shorthand for mtime\"\"\"\n    return os.stat(path).st_mtime\n\n\nclass BaseCommand(Command):\n    \"\"\"Dumb empty command because Command needs subclasses to override too much\"\"\"\n    user_options = []\n    \n    def initialize_options(self):\n        pass\n    \n    def finalize_options(self):\n        pass\n    \n    def get_inputs(self):\n        return []\n    \n    def get_outputs(self):\n        return []\n\n\nclass Bower(BaseCommand):\n    description = \"fetch static client-side components with bower\"\n    \n    user_options = []\n    bower_dir = pjoin(static, 'components')\n    node_modules = pjoin(here, 'node_modules')\n    \n    def should_run(self):\n        if not os.path.exists(self.bower_dir):\n            return True\n        return mtime(self.bower_dir) < mtime(pjoin(here, 'bower.json'))\n\n    def should_run_npm(self):\n        if not shutil.which('npm'):\n            print(\"npm unavailable\", file=sys.stderr)\n            return False\n        if not os.path.exists(self.node_modules):\n            return True\n        return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))\n    \n    def run(self):\n        if not self.should_run():\n            print(\"bower dependencies up to date\")\n            return\n        \n        if self.should_run_npm():\n            print(\"installing build dependencies with npm\")\n            check_call(['npm', 'install', '--progress=false'], cwd=here, shell=shell)\n            os.utime(self.node_modules)\n        \n        env = os.environ.copy()\n        env['PATH'] = npm_path\n        args = ['bower', 'install', '--allow-root', '--config.interactive=false']\n        try:\n            check_call(args, cwd=here, env=env, shell=shell)\n        except OSError as e:\n            print(\"Failed to run bower: %s\" % e, file=sys.stderr)\n            print(\"You can install js dependencies with `npm install`\", file=sys.stderr)\n            raise\n        os.utime(self.bower_dir)\n        # update data-files in case this created new files\n        self.distribution.data_files = get_data_files()\n\n\nclass CSS(BaseCommand):\n    description = \"compile CSS from LESS\"\n    \n    def should_run(self):\n        \"\"\"Does less need to run?\"\"\"\n        # from IPython.html.tasks.py\n        \n        css_targets = [pjoin(static, 'css', 'style.min.css')]\n        css_maps = [t + '.map' for t in css_targets]\n        targets = css_targets + css_maps\n        if not all(os.path.exists(t) for t in targets):\n            # some generated files don't exist\n            return True\n        earliest_target = sorted(mtime(t) for t in targets)[0]\n    \n        # check if any .less files are newer than the generated targets\n        for (dirpath, dirnames, filenames) in os.walk(static):\n            for f in filenames:\n                if f.endswith('.less'):\n                    path = pjoin(static, dirpath, f)\n                    timestamp = mtime(path)\n                    if timestamp > earliest_target:\n                        return True\n    \n        return False\n    \n    def run(self):\n        if not self.should_run():\n            print(\"CSS up-to-date\")\n            return\n        \n        self.run_command('js')\n        \n        style_less = pjoin(static, 'less', 'style.less')\n        style_css = pjoin(static, 'css', 'style.min.css')\n        sourcemap = style_css + '.map'\n        \n        env = os.environ.copy()\n        env['PATH'] = npm_path\n        args = [\n            'lessc', '--clean-css',\n            '--source-map-basepath={}'.format(static),\n            '--source-map={}'.format(sourcemap),\n            '--source-map-rootpath=../',\n            style_less, style_css,\n        ]\n        try:\n            check_call(args, cwd=here, env=env, shell=shell)\n        except OSError as e:\n            print(\"Failed to run lessc: %s\" % e, file=sys.stderr)\n            print(\"You can install js dependencies with `npm install`\", file=sys.stderr)\n            raise\n        # update data-files in case this created new files\n        self.distribution.data_files = get_data_files()\n\n\ndef js_css_first(cls, strict=True):\n    class Command(cls):\n        def run(self):\n            try:\n                self.run_command('js')\n                self.run_command('css')\n            except Exception:\n                if strict:\n                    raise\n                else:\n                    pass\n            return super().run()\n    return Command\n\n\nclass bdist_egg_disabled(bdist_egg):\n    \"\"\"Disabled version of bdist_egg\n\n    Prevents setup.py install from performing setuptools' default easy_install,\n    which it should never ever do.\n    \"\"\"\n    def run(self):\n        sys.exit(\"Aborting implicit building of eggs. Use `pip install .` to install from source.\")\n\n\nsetup_args['cmdclass'] = {\n    'js': Bower,\n    'css': CSS,\n    'build_py': js_css_first(build_py, strict=is_repo),\n    'sdist': js_css_first(sdist, strict=True),\n    'bdist_egg': bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled,\n}\n\n\n# setuptools requirements\n\nsetup_args['zip_safe'] = False\nfrom setuptools.command.develop import develop\nclass develop_js_css(develop):\n    def run(self):\n        if not self.uninstall:\n            self.distribution.run_command('js')\n            self.distribution.run_command('css')\n        develop.run(self)\nsetup_args['cmdclass']['develop'] = develop_js_css\nsetup_args['install_requires'] = install_requires = []\n\nwith open('requirements.txt') as f:\n    for line in f.readlines():\n        req = line.strip()\n        if not req or req.startswith('#') or '://' in req:\n            continue\n        install_requires.append(req)\n\n#---------------------------------------------------------------------------\n# setup\n#---------------------------------------------------------------------------\n\ndef main():\n    setup(**setup_args)\n\nif __name__ == '__main__':\n    main()\n", "path": "setup.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n# coding: utf-8\n\n# Copyright (c) Juptyer Development Team.\n# Distributed under the terms of the Modified BSD License.\n\n#-----------------------------------------------------------------------------\n# Minimal Python version sanity check (from IPython)\n#-----------------------------------------------------------------------------\n\nfrom __future__ import print_function\n\nimport os\nimport shutil\nimport sys\n\nv = sys.version_info\nif v[:2] < (3,4):\n    error = \"ERROR: JupyterHub requires Python version 3.4 or above.\"\n    print(error, file=sys.stderr)\n    sys.exit(1)\n\nshell = False\nif os.name in ('nt', 'dos'):\n    shell = True\n    warning = \"WARNING: Windows is not officially supported\"\n    print(warning, file=sys.stderr)\n\n# At least we're on the python version we need, move on.\n\nimport os\nfrom glob import glob\nfrom subprocess import check_call\n\nfrom setuptools import setup\nfrom setuptools.command.bdist_egg import bdist_egg\n\npjoin = os.path.join\n\nhere = os.path.abspath(os.path.dirname(__file__))\nshare_jupyter = pjoin(here, 'share', 'jupyter', 'hub')\nstatic = pjoin(share_jupyter, 'static')\n\nis_repo = os.path.exists(pjoin(here, '.git'))\n\n#---------------------------------------------------------------------------\n# Build basic package data, etc.\n#---------------------------------------------------------------------------\n\ndef get_data_files():\n    \"\"\"Get data files in share/jupyter\"\"\"\n\n    data_files = []\n    ntrim = len(here + os.path.sep)\n\n    for (d, dirs, filenames) in os.walk(share_jupyter):\n        data_files.append((\n            d[ntrim:],\n            [ pjoin(d, f) for f in filenames ]\n        ))\n    return data_files\n\ndef get_package_data():\n    \"\"\"Get package data\n\n    (mostly alembic config)\n    \"\"\"\n    package_data = {}\n    package_data['jupyterhub'] = [\n        'alembic.ini',\n        'alembic/*',\n        'alembic/versions/*',\n    ]\n    return package_data\n\nns = {}\nwith open(pjoin(here, 'jupyterhub', '_version.py')) as f:\n    exec(f.read(), {}, ns)\n\n\npackages = []\nfor d, _, _ in os.walk('jupyterhub'):\n    if os.path.exists(pjoin(d, '__init__.py')):\n        packages.append(d.replace(os.path.sep, '.'))\n\nsetup_args = dict(\n    name                = 'jupyterhub',\n    scripts             = glob(pjoin('scripts', '*')),\n    packages            = packages,\n                        # dummy, so that install_data doesn't get skipped\n                        # this will be overridden when bower is run anyway\n    data_files          = get_data_files() or ['dummy'],\n    package_data        = get_package_data(),\n    version             = ns['__version__'],\n    description         = \"JupyterHub: A multi-user server for Jupyter notebooks\",\n    long_description    = \"See https://jupyterhub.readthedocs.io for more info.\",\n    author              = \"Jupyter Development Team\",\n    author_email        = \"jupyter@googlegroups.com\",\n    url                 = \"http://jupyter.org\",\n    license             = \"BSD\",\n    platforms           = \"Linux, Mac OS X\",\n    keywords            = ['Interactive', 'Interpreter', 'Shell', 'Web'],\n    classifiers         = [\n        'Intended Audience :: Developers',\n        'Intended Audience :: System Administrators',\n        'Intended Audience :: Science/Research',\n        'License :: OSI Approved :: BSD License',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 3',\n    ],\n)\n\n#---------------------------------------------------------------------------\n# custom distutils commands\n#---------------------------------------------------------------------------\n\n# imports here, so they are after setuptools import if there was one\nfrom distutils.cmd import Command\nfrom distutils.command.build_py import build_py\nfrom distutils.command.sdist import sdist\n\n\nnpm_path = os.pathsep.join([\n    pjoin(here, 'node_modules', '.bin'),\n    os.environ.get(\"PATH\", os.defpath),\n])\n\n\ndef mtime(path):\n    \"\"\"shorthand for mtime\"\"\"\n    return os.stat(path).st_mtime\n\n\nclass BaseCommand(Command):\n    \"\"\"Dumb empty command because Command needs subclasses to override too much\"\"\"\n    user_options = []\n\n    def initialize_options(self):\n        pass\n\n    def finalize_options(self):\n        pass\n\n    def get_inputs(self):\n        return []\n\n    def get_outputs(self):\n        return []\n\n\nclass Bower(BaseCommand):\n    description = \"fetch static client-side components with bower\"\n\n    user_options = []\n    bower_dir = pjoin(static, 'components')\n    node_modules = pjoin(here, 'node_modules')\n\n    def should_run(self):\n        if not os.path.exists(self.bower_dir):\n            return True\n        return mtime(self.bower_dir) < mtime(pjoin(here, 'bower.json'))\n\n    def should_run_npm(self):\n        if not shutil.which('npm'):\n            print(\"npm unavailable\", file=sys.stderr)\n            return False\n        if not os.path.exists(self.node_modules):\n            return True\n        return mtime(self.node_modules) < mtime(pjoin(here, 'package.json'))\n\n    def run(self):\n        if not self.should_run():\n            print(\"bower dependencies up to date\")\n            return\n\n        if self.should_run_npm():\n            print(\"installing build dependencies with npm\")\n            check_call(['npm', 'install', '--progress=false'], cwd=here, shell=shell)\n            os.utime(self.node_modules)\n\n        env = os.environ.copy()\n        env['PATH'] = npm_path\n        args = ['bower', 'install', '--allow-root', '--config.interactive=false']\n        try:\n            check_call(args, cwd=here, env=env, shell=shell)\n        except OSError as e:\n            print(\"Failed to run bower: %s\" % e, file=sys.stderr)\n            print(\"You can install js dependencies with `npm install`\", file=sys.stderr)\n            raise\n        os.utime(self.bower_dir)\n        # update data-files in case this created new files\n        self.distribution.data_files = get_data_files()\n\n\nclass CSS(BaseCommand):\n    description = \"compile CSS from LESS\"\n\n    def should_run(self):\n        \"\"\"Does less need to run?\"\"\"\n        # from IPython.html.tasks.py\n\n        css_targets = [pjoin(static, 'css', 'style.min.css')]\n        css_maps = [t + '.map' for t in css_targets]\n        targets = css_targets + css_maps\n        if not all(os.path.exists(t) for t in targets):\n            # some generated files don't exist\n            return True\n        earliest_target = sorted(mtime(t) for t in targets)[0]\n\n        # check if any .less files are newer than the generated targets\n        for (dirpath, dirnames, filenames) in os.walk(static):\n            for f in filenames:\n                if f.endswith('.less'):\n                    path = pjoin(static, dirpath, f)\n                    timestamp = mtime(path)\n                    if timestamp > earliest_target:\n                        return True\n\n        return False\n\n    def run(self):\n        if not self.should_run():\n            print(\"CSS up-to-date\")\n            return\n\n        self.run_command('js')\n\n        style_less = pjoin(static, 'less', 'style.less')\n        style_css = pjoin(static, 'css', 'style.min.css')\n        sourcemap = style_css + '.map'\n\n        env = os.environ.copy()\n        env['PATH'] = npm_path\n        args = [\n            'lessc', '--clean-css',\n            '--source-map-basepath={}'.format(static),\n            '--source-map={}'.format(sourcemap),\n            '--source-map-rootpath=../',\n            style_less, style_css,\n        ]\n        try:\n            check_call(args, cwd=here, env=env, shell=shell)\n        except OSError as e:\n            print(\"Failed to run lessc: %s\" % e, file=sys.stderr)\n            print(\"You can install js dependencies with `npm install`\", file=sys.stderr)\n            raise\n        # update data-files in case this created new files\n        self.distribution.data_files = get_data_files()\n\n\ndef js_css_first(cls, strict=True):\n    class Command(cls):\n        def run(self):\n            try:\n                self.run_command('js')\n                self.run_command('css')\n            except Exception:\n                if strict:\n                    raise\n                else:\n                    pass\n            return super().run()\n    return Command\n\n\nclass bdist_egg_disabled(bdist_egg):\n    \"\"\"Disabled version of bdist_egg\n\n    Prevents setup.py install from performing setuptools' default easy_install,\n    which it should never ever do.\n    \"\"\"\n    def run(self):\n        sys.exit(\"Aborting implicit building of eggs. Use `pip install .` to install from source.\")\n\n\nsetup_args['cmdclass'] = {\n    'js': Bower,\n    'css': CSS,\n    'build_py': js_css_first(build_py, strict=is_repo),\n    'sdist': js_css_first(sdist, strict=True),\n    'bdist_egg': bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled,\n}\n\n\n# setuptools requirements\n\nsetup_args['zip_safe'] = False\nfrom setuptools.command.develop import develop\nclass develop_js_css(develop):\n    def run(self):\n        if not self.uninstall:\n            self.distribution.run_command('js')\n            self.distribution.run_command('css')\n        develop.run(self)\nsetup_args['cmdclass']['develop'] = develop_js_css\nsetup_args['install_requires'] = install_requires = []\n\nwith open('requirements.txt') as f:\n    for line in f.readlines():\n        req = line.strip()\n        if not req or req.startswith('#') or '://' in req:\n            continue\n        install_requires.append(req)\n\n#---------------------------------------------------------------------------\n# setup\n#---------------------------------------------------------------------------\n\ndef main():\n    setup(**setup_args)\n\nif __name__ == '__main__':\n    main()\n", "path": "setup.py"}]}