Repository: jupyter/jupyter-packaging

Issue:
Add support for data_files in develop mode
`data_files` do not get moved into the appropriate place during an editable install, and that is a [won't fix](https://github.com/pypa/pip/issues/6592).  We can add support for that here.

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 6d00f02..9245fd2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: python
 python:
-    - 2.7
     - 3.5
+    - 3.8
 sudo: false
 env:
 install:
diff --git a/jupyter_packaging/setupbase.py b/jupyter_packaging/setupbase.py
index 8338106..04edfe0 100644
--- a/jupyter_packaging/setupbase.py
+++ b/jupyter_packaging/setupbase.py
@@ -161,8 +161,10 @@ def create_cmdclass(prerelease_cmd=None, package_data_spec=None,
     wrapped = [prerelease_cmd] if prerelease_cmd else []
     if package_data_spec or data_files_spec:
         wrapped.append('handle_files')
+
     wrapper = functools.partial(_wrap_command, wrapped)
     handle_files = _get_file_handler(package_data_spec, data_files_spec)
+    develop_handler = _get_develop_handler()
 
     if 'bdist_egg' in sys.argv:
         egg = wrapper(bdist_egg, strict=True)
@@ -181,7 +183,7 @@ def create_cmdclass(prerelease_cmd=None, package_data_spec=None,
     if bdist_wheel:
         cmdclass['bdist_wheel'] = wrapper(bdist_wheel, strict=True)
 
-    cmdclass['develop'] = wrapper(develop, strict=True)
+    cmdclass['develop'] = wrapper(develop_handler, strict=True)
     return cmdclass
 
 
@@ -496,6 +498,22 @@ def _get_file_handler(package_data_spec, data_files_spec):
     return FileHandler
 
 
+def _get_develop_handler():
+    """Get a handler for the develop command"""
+    class _develop(develop):
+
+        def install_for_development(self):
+            super(_develop, self).install_for_development()
+            self.run_command('handle_files')
+            for _, filenames in self.distribution.data_files:
+                for filename in filenames:
+                    target = os.path.join(sys.prefix, filename)
+                    self.mkpath(os.path.dirname(target))
+                    outf, copied = self.copy_file(filename, target)
+
+    return _develop
+
+
 def _glob_pjoin(*parts):
     """Join paths for glob processing"""
     if parts[0] in ('.', ''):
@@ -529,9 +547,11 @@ def _get_data_files(data_specs, existing, top=None):
     for (path, dname, pattern) in data_specs or []:
         if os.path.isabs(dname):
             dname = os.path.relpath(dname, top)
+
         dname = dname.replace(os.sep, '/')
         offset = 0 if dname in ('.', '') else len(dname) + 1
         files = _get_files(_glob_pjoin(dname, pattern), top=top)
+
         for fname in files:
             # Normalize the path.
             root = os.path.dirname(fname)
diff --git a/setup.py b/setup.py
index 12b7a09..5687ae6 100644
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,6 @@ setup_args = dict(
         'Intended Audience :: Science/Research',
         'License :: OSI Approved :: BSD License',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 7c5d332..4c6549f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -32,3 +32,54 @@ def destination_dir(tmpdir):
     for p in destination.visit():
         p.setmtime(20000)
     return destination
+
+
+@fixture
+def package_dir(tmpdir):
+    pkg = tmpdir.mkdir('package')
+    share = pkg.mkdir('share')
+    jupyter = share.mkdir('jupyter')
+    foo = jupyter.mkdir('jupyter_packaging_test_foo')
+    foo.join('test.txt').write('hello, world!')
+    pkg.join('jupyter_packaging_test_foo.py').write('print("hello, world!")')
+    pkg.join('MANIFEST.in').write('recursive-include share *.*')
+    pkg.join('setup.py').write("""
+from jupyter_packaging import create_cmdclass
+import setuptools
+import os
+
+
+name = "jupyter_packaging_test_foo"
+HERE = os.path.abspath(os.path.dirname(__file__))
+share_path = os.path.join(HERE, "share", "jupyter", name)
+
+data_files_spec = [
+    ("share/jupyter/%s" % name, share_path,  "*.txt"),
+]
+
+
+cmdclass = create_cmdclass( 
+    data_files_spec=data_files_spec
+)
+    
+setup_args = dict(
+    name=name,
+    version="foo version",
+    url="foo url",
+    author="foo author",
+    description="foo package",
+    long_description="long_description",
+    long_description_content_type="text/markdown",
+    cmdclass= cmdclass,
+    packages=setuptools.find_packages(),
+    zip_safe=False,
+    py_modules=["jupyter_packaging_test_foo"],
+    include_package_data=True
+)
+
+
+if __name__ == "__main__":
+    setuptools.setup(**setup_args)
+""")
+    return pkg
+
diff --git a/tests/test_develop.py b/tests/test_develop.py
new file mode 100644
index 0000000..779ac41
--- /dev/null
+++ b/tests/test_develop.py
@@ -0,0 +1,19 @@
+
+import importlib
+import os
+import os.path as osp
+import subprocess
+import shutil
+import sys
+
+
+def test_develop(package_dir):
+    name = 'jupyter_packaging_test_foo'
+    share = osp.join(sys.prefix, 'share', 'jupyter', name)
+    if osp.exists(share):
+        shutil.rmtree(share)
+    subprocess.check_output([shutil.which('pip'), 'install', '-e', '.'], cwd=str(package_dir))
+    assert osp.exists(osp.join(share, 'test.txt'))
+    subprocess.check_output([shutil.which('pip'), 'uninstall', '-y', name], cwd=str(package_dir))
+    assert osp.exists(osp.join(share, 'test.txt'))
+    shutil.rmtree(share)