summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
authorAnderson Bravalheri <andersonbravalheri+github@gmail.com>2022-04-11 23:41:02 +0100
committerAnderson Bravalheri <andersonbravalheri@gmail.com>2022-04-22 16:39:50 +0100
commit2bddfdffbcb2fc76092d2d6029669f1bb96a742a (patch)
tree254a7b3cb0b3b6089c589723010f5dd14058e297 /setuptools
parentddb8844eac49e0bf3b4f20067b425ffeac1531a2 (diff)
parentbe3778e796424146e53b93a032a0a6a39979d9ff (diff)
downloadpython-setuptools-git-2bddfdffbcb2fc76092d2d6029669f1bb96a742a.tar.gz
include pep660 proof of concept (#3082)
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/build_meta.py21
-rw-r--r--setuptools/command/editable_wheel.py157
2 files changed, 178 insertions, 0 deletions
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index 5dc65e2d..8b592fad 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -46,6 +46,8 @@ __all__ = ['get_requires_for_build_sdist',
'prepare_metadata_for_build_wheel',
'build_wheel',
'build_sdist',
+ 'get_requires_for_build_editable',
+ 'build_editable',
'__legacy__',
'SetupRequirementsError']
@@ -250,6 +252,23 @@ class _BuildMetaBackend:
config_settings)
+ # PEP660 hooks:
+ # build_editable
+ # get_requires_for_build_editable
+ # prepare_metadata_for_build_editable
+ def build_editable(
+ self, wheel_directory, scheme=None, config_settings=None
+ ):
+ # XXX can or should we hide our editable_wheel command normally?
+ return self._build_with_temp_dir(
+ ["editable_wheel"], ".whl", wheel_directory, config_settings
+ )
+
+
+ def get_requires_for_build_editable(self, config_settings=None):
+ return ['editables', 'wheel']
+
+
class _BuildMetaLegacyBackend(_BuildMetaBackend):
"""Compatibility backend for setuptools
@@ -295,9 +314,11 @@ _BACKEND = _BuildMetaBackend()
get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel
get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
+get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
build_wheel = _BACKEND.build_wheel
build_sdist = _BACKEND.build_sdist
+build_editable = _BACKEND.build_editable
# The legacy backend
diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py
new file mode 100644
index 00000000..c827efa3
--- /dev/null
+++ b/setuptools/command/editable_wheel.py
@@ -0,0 +1,157 @@
+"""
+Create a wheel that, when installed, will make the source package 'editable'
+(add it to the interpreter's path, including metadata) per PEP 660. Replaces
+'setup.py develop'. Based on the setuptools develop command.
+"""
+
+# TODO doesn't behave when called outside the hook
+
+import os
+import time
+from pathlib import Path
+
+from distutils.core import Command
+from distutils.errors import DistutilsError
+
+import pkg_resources
+
+SOURCE_EPOCH_ZIP = 499162860
+
+
+class editable_wheel(Command):
+ """Build 'editable' wheel for development"""
+
+ description = "create a PEP 660 'editable' wheel"
+
+ user_options = [
+ ("dist-dir=", "d", "directory to put final built distributions in"),
+ ]
+
+ boolean_options = []
+
+ def run(self):
+ self.build_editable_wheel()
+
+ def initialize_options(self):
+ self.dist_dir = None
+
+ def finalize_options(self):
+ # is this part of the 'develop' command needed?
+ ei = self.get_finalized_command("egg_info")
+ if ei.broken_egg_info:
+ template = "Please rename %r to %r before using 'develop'"
+ args = ei.egg_info, ei.broken_egg_info
+ raise DistutilsError(template % args)
+ self.args = [ei.egg_name]
+
+ # the .pth file should point to target
+ self.egg_base = ei.egg_base
+ self.target = pkg_resources.normalize_path(self.egg_base)
+ self.dist_info_dir = Path(
+ (ei.egg_info[: -len(".egg-info")] + ".dist-info").rpartition("/")[-1]
+ )
+
+ def build_editable_wheel(self):
+ if getattr(self.distribution, "use_2to3", False):
+ raise NotImplementedError("2to3 not supported")
+
+ di = self.get_finalized_command("dist_info")
+ di.egg_base = self.dist_dir
+ di.finalize_options()
+ self.run_command("dist_info")
+
+ # Build extensions in-place
+ self.reinitialize_command("build_ext", inplace=1)
+ self.run_command("build_ext")
+
+ # now build the wheel
+ # with the dist-info directory and .pth from 'editables' library
+ # ...
+
+ import zipfile
+ import editables # could we use 'develop' command's .pth file
+
+ project = editables.EditableProject(
+ self.distribution.metadata.name, self.target
+ )
+ project.add_to_path(self.target)
+
+ dist_dir = Path(self.dist_dir)
+ dist_info_dir = self.dist_info_dir
+ fullname = self.distribution.metadata.get_fullname()
+ # superfluous 'ed' tag is only a hint to the user,
+ # and guarantees we can't overwrite the normal wheel
+ wheel_name = f"{fullname}-ed.py3-none-any.whl"
+ wheel_path = dist_dir / wheel_name
+
+ wheelmeta_builder(dist_dir / dist_info_dir / "WHEEL")
+
+ if wheel_path.exists():
+ wheel_path.unlink()
+
+ with zipfile.ZipFile(
+ wheel_path, "a", compression=zipfile.ZIP_DEFLATED
+ ) as archive:
+
+ # copy .pth file
+ for f, data in project.files():
+ archive.writestr(
+ zipfile.ZipInfo(f, time.gmtime(SOURCE_EPOCH_ZIP)[:6]), data
+ )
+
+ # copy .dist-info directory
+ for f in sorted(os.listdir(dist_dir / dist_info_dir)):
+ with (dist_dir / dist_info_dir / f).open() as metadata:
+ archive.writestr(
+ zipfile.ZipInfo(
+ str(dist_info_dir / f), time.gmtime(SOURCE_EPOCH_ZIP)[:6]
+ ),
+ metadata.read(),
+ )
+
+ add_manifest(archive, dist_info_dir)
+
+
+import base64
+
+
+def urlsafe_b64encode(data):
+ """urlsafe_b64encode without padding"""
+ return base64.urlsafe_b64encode(data).rstrip(b"=")
+
+
+# standalone wheel helpers based on enscons
+def add_manifest(archive, dist_info_dir):
+ """
+ Add the wheel manifest.
+ """
+ import hashlib
+ import zipfile
+
+ lines = []
+ for f in archive.namelist():
+ data = archive.read(f)
+ size = len(data)
+ digest = hashlib.sha256(data).digest()
+ digest = "sha256=" + (urlsafe_b64encode(digest).decode("ascii"))
+ lines.append("%s,%s,%s" % (f.replace(",", ",,"), digest, size))
+
+ record_path = dist_info_dir / "RECORD"
+ lines.append(str(record_path) + ",,")
+ RECORD = "\n".join(lines)
+ archive.writestr(
+ zipfile.ZipInfo(str(record_path), time.gmtime(SOURCE_EPOCH_ZIP)[:6]), RECORD
+ )
+ archive.close()
+
+
+def wheelmeta_builder(target):
+ with open(target, "w+") as f:
+ f.write(
+ """Wheel-Version: 1.0
+Generator: setuptools_pep660 (0.1)
+Root-Is-Purelib: false
+Tag: py3-none-any
+Tag: ed-none-any
+"""
+ )