Spaces:
Build error
Build error
""" | |
"wheel" copyright (c) 2012-2017 Daniel Holth <[email protected]> and | |
contributors. | |
The MIT License | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included | |
in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
Create a Azure wheel (.whl) distribution (a wheel is a built archive format). | |
This file is a copy of the official bdist_wheel file from wheel 0.30.0a0, enhanced | |
of the bottom with some Microsoft extension for Azure SDK for Python | |
""" | |
import csv | |
import hashlib | |
import os | |
import subprocess | |
import warnings | |
import shutil | |
import json | |
import sys | |
try: | |
import sysconfig | |
except ImportError: # pragma nocover | |
# Python < 2.7 | |
import distutils.sysconfig as sysconfig | |
import pkg_resources | |
safe_name = pkg_resources.safe_name | |
safe_version = pkg_resources.safe_version | |
from shutil import rmtree | |
from email.generator import Generator | |
from distutils.core import Command | |
from distutils.sysconfig import get_python_version | |
from distutils import log as logger | |
from wheel.pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag, get_platform | |
from wheel.util import native, open_for_csv | |
from wheel.archive import archive_wheelfile | |
from wheel.pkginfo import read_pkg_info, write_pkg_info | |
from wheel.metadata import pkginfo_to_dict | |
from wheel import pep425tags, metadata | |
from wheel import __version__ as wheel_version | |
def safer_name(name): | |
return safe_name(name).replace("-", "_") | |
def safer_version(version): | |
return safe_version(version).replace("-", "_") | |
class bdist_wheel(Command): | |
description = "create a wheel distribution" | |
user_options = [ | |
("bdist-dir=", "b", "temporary directory for creating the distribution"), | |
( | |
"plat-name=", | |
"p", | |
"platform name to embed in generated filenames " | |
"(default: %s)" % get_platform(), | |
), | |
( | |
"keep-temp", | |
"k", | |
"keep the pseudo-installation tree around after " | |
+ "creating the distribution archive", | |
), | |
("dist-dir=", "d", "directory to put final built distributions in"), | |
("skip-build", None, "skip rebuilding everything (for testing/debugging)"), | |
("relative", None, "build the archive using relative paths" "(default: false)"), | |
( | |
"owner=", | |
"u", | |
"Owner name used when creating a tar file" " [default: current user]", | |
), | |
( | |
"group=", | |
"g", | |
"Group name used when creating a tar file" " [default: current group]", | |
), | |
("universal", None, "make a universal wheel" " (default: false)"), | |
( | |
"python-tag=", | |
None, | |
"Python implementation compatibility tag" | |
" (default: py%s)" % get_impl_ver()[0], | |
), | |
] | |
boolean_options = ["keep-temp", "skip-build", "relative", "universal"] | |
def initialize_options(self): | |
self.bdist_dir = None | |
self.data_dir = None | |
self.plat_name = None | |
self.plat_tag = None | |
self.format = "zip" | |
self.keep_temp = False | |
self.dist_dir = None | |
self.distinfo_dir = None | |
self.egginfo_dir = None | |
self.root_is_pure = None | |
self.skip_build = None | |
self.relative = False | |
self.owner = None | |
self.group = None | |
self.universal = False | |
self.python_tag = "py" + get_impl_ver()[0] | |
self.plat_name_supplied = False | |
def finalize_options(self): | |
if self.bdist_dir is None: | |
bdist_base = self.get_finalized_command("bdist").bdist_base | |
self.bdist_dir = os.path.join(bdist_base, "wheel") | |
self.data_dir = self.wheel_dist_name + ".data" | |
self.plat_name_supplied = self.plat_name is not None | |
need_options = ("dist_dir", "plat_name", "skip_build") | |
self.set_undefined_options("bdist", *zip(need_options, need_options)) | |
self.root_is_pure = not ( | |
self.distribution.has_ext_modules() or self.distribution.has_c_libraries() | |
) | |
# Support legacy [wheel] section for setting universal | |
wheel = self.distribution.get_option_dict("wheel") | |
if "universal" in wheel: | |
# please don't define this in your global configs | |
val = wheel["universal"][1].strip() | |
if val.lower() in ("1", "true", "yes"): | |
self.universal = True | |
def wheel_dist_name(self): | |
"""Return distribution full name with - replaced with _""" | |
return "-".join( | |
( | |
safer_name(self.distribution.get_name()), | |
safer_version(self.distribution.get_version()), | |
) | |
) | |
def get_tag(self): | |
# bdist sets self.plat_name if unset, we should only use it for purepy | |
# wheels if the user supplied it. | |
if self.plat_name_supplied: | |
plat_name = self.plat_name | |
elif self.root_is_pure: | |
plat_name = "any" | |
else: | |
plat_name = self.plat_name or get_platform() | |
if ( | |
plat_name in ("linux-x86_64", "linux_x86_64") | |
and sys.maxsize == 2147483647 | |
): | |
plat_name = "linux_i686" | |
plat_name = plat_name.replace("-", "_").replace(".", "_") | |
if self.root_is_pure: | |
if self.universal: | |
impl = "py2.py3" | |
else: | |
impl = self.python_tag | |
tag = (impl, "none", plat_name) | |
else: | |
impl_name = get_abbr_impl() | |
impl_ver = get_impl_ver() | |
# PEP 3149 | |
abi_tag = str(get_abi_tag()).lower() | |
tag = (impl_name + impl_ver, abi_tag, plat_name) | |
supported_tags = pep425tags.get_supported( | |
supplied_platform=plat_name if self.plat_name_supplied else None | |
) | |
# XXX switch to this alternate implementation for non-pure: | |
assert tag == supported_tags[0], "%s != %s" % (tag, supported_tags[0]) | |
return tag | |
def get_archive_basename(self): | |
"""Return archive name without extension""" | |
impl_tag, abi_tag, plat_tag = self.get_tag() | |
archive_basename = "%s-%s-%s-%s" % ( | |
self.wheel_dist_name, | |
impl_tag, | |
abi_tag, | |
plat_tag, | |
) | |
return archive_basename | |
def run(self): | |
build_scripts = self.reinitialize_command("build_scripts") | |
build_scripts.executable = "python" | |
if not self.skip_build: | |
self.run_command("build") | |
install = self.reinitialize_command("install", reinit_subcommands=True) | |
install.root = self.bdist_dir | |
install.compile = False | |
install.skip_build = self.skip_build | |
install.warn_dir = False | |
# A wheel without setuptools scripts is more cross-platform. | |
# Use the (undocumented) `no_ep` option to setuptools' | |
# install_scripts command to avoid creating entry point scripts. | |
install_scripts = self.reinitialize_command("install_scripts") | |
install_scripts.no_ep = True | |
# Use a custom scheme for the archive, because we have to decide | |
# at installation time which scheme to use. | |
for key in ("headers", "scripts", "data", "purelib", "platlib"): | |
setattr(install, "install_" + key, os.path.join(self.data_dir, key)) | |
basedir_observed = "" | |
if os.name == "nt": | |
# win32 barfs if any of these are ''; could be '.'? | |
# (distutils.command.install:change_roots bug) | |
basedir_observed = os.path.normpath(os.path.join(self.data_dir, "..")) | |
self.install_libbase = self.install_lib = basedir_observed | |
setattr( | |
install, | |
"install_purelib" if self.root_is_pure else "install_platlib", | |
basedir_observed, | |
) | |
logger.info("installing to %s", self.bdist_dir) | |
self.run_command("install") | |
archive_basename = self.get_archive_basename() | |
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) | |
if not self.relative: | |
archive_root = self.bdist_dir | |
else: | |
archive_root = os.path.join( | |
self.bdist_dir, self._ensure_relative(install.install_base) | |
) | |
self.set_undefined_options("install_egg_info", ("target", "egginfo_dir")) | |
self.distinfo_dir = os.path.join( | |
self.bdist_dir, "%s.dist-info" % self.wheel_dist_name | |
) | |
self.egg2dist(self.egginfo_dir, self.distinfo_dir) | |
self.write_wheelfile(self.distinfo_dir) | |
self.write_record(self.bdist_dir, self.distinfo_dir) | |
# Make the archive | |
if not os.path.exists(self.dist_dir): | |
os.makedirs(self.dist_dir) | |
wheel_name = archive_wheelfile(pseudoinstall_root, archive_root) | |
# Sign the archive | |
if "WHEEL_TOOL" in os.environ: | |
subprocess.call([os.environ["WHEEL_TOOL"], "sign", wheel_name]) | |
# Add to 'Distribution.dist_files' so that the "upload" command works | |
getattr(self.distribution, "dist_files", []).append( | |
("bdist_wheel", get_python_version(), wheel_name) | |
) | |
if not self.keep_temp: | |
if self.dry_run: | |
logger.info("removing %s", self.bdist_dir) | |
else: | |
rmtree(self.bdist_dir) | |
def write_wheelfile( | |
self, wheelfile_base, generator="bdist_wheel (" + wheel_version + ")" | |
): | |
from email.message import Message | |
msg = Message() | |
msg["Wheel-Version"] = "1.0" # of the spec | |
msg["Generator"] = generator | |
msg["Root-Is-Purelib"] = str(self.root_is_pure).lower() | |
# Doesn't work for bdist_wininst | |
impl_tag, abi_tag, plat_tag = self.get_tag() | |
for impl in impl_tag.split("."): | |
for abi in abi_tag.split("."): | |
for plat in plat_tag.split("."): | |
msg["Tag"] = "-".join((impl, abi, plat)) | |
wheelfile_path = os.path.join(wheelfile_base, "WHEEL") | |
logger.info("creating %s", wheelfile_path) | |
with open(wheelfile_path, "w") as f: | |
Generator(f, maxheaderlen=0).flatten(msg) | |
def _ensure_relative(self, path): | |
# copied from dir_util, deleted | |
drive, path = os.path.splitdrive(path) | |
if path[0:1] == os.sep: | |
path = drive + path[1:] | |
return path | |
def _pkginfo_to_metadata(self, egg_info_path, pkginfo_path): | |
return metadata.pkginfo_to_metadata(egg_info_path, pkginfo_path) | |
def license_file(self): | |
"""Return license filename from a license-file key in setup.cfg, or None.""" | |
metadata = self.distribution.get_option_dict("metadata") | |
if not "license_file" in metadata: | |
return None | |
return metadata["license_file"][1] | |
def setupcfg_requirements(self): | |
"""Generate requirements from setup.cfg as | |
('Requires-Dist', 'requirement; qualifier') tuples. From a metadata | |
section in setup.cfg: | |
[metadata] | |
provides-extra = extra1 | |
extra2 | |
requires-dist = requirement; qualifier | |
another; qualifier2 | |
unqualified | |
Yields | |
('Provides-Extra', 'extra1'), | |
('Provides-Extra', 'extra2'), | |
('Requires-Dist', 'requirement; qualifier'), | |
('Requires-Dist', 'another; qualifier2'), | |
('Requires-Dist', 'unqualified') | |
""" | |
metadata = self.distribution.get_option_dict("metadata") | |
# our .ini parser folds - to _ in key names: | |
for key, title in ( | |
("provides_extra", "Provides-Extra"), | |
("requires_dist", "Requires-Dist"), | |
): | |
if not key in metadata: | |
continue | |
field = metadata[key] | |
for line in field[1].splitlines(): | |
line = line.strip() | |
if not line: | |
continue | |
yield (title, line) | |
def add_requirements(self, metadata_path): | |
"""Add additional requirements from setup.cfg to file metadata_path""" | |
additional = list(self.setupcfg_requirements()) | |
if not additional: | |
return | |
pkg_info = read_pkg_info(metadata_path) | |
if "Provides-Extra" in pkg_info or "Requires-Dist" in pkg_info: | |
warnings.warn("setup.cfg requirements overwrite values from setup.py") | |
del pkg_info["Provides-Extra"] | |
del pkg_info["Requires-Dist"] | |
for k, v in additional: | |
pkg_info[k] = v | |
write_pkg_info(metadata_path, pkg_info) | |
def egg2dist(self, egginfo_path, distinfo_path): | |
"""Convert an .egg-info directory into a .dist-info directory""" | |
def adios(p): | |
"""Appropriately delete directory, file or link.""" | |
if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p): | |
shutil.rmtree(p) | |
elif os.path.exists(p): | |
os.unlink(p) | |
adios(distinfo_path) | |
if not os.path.exists(egginfo_path): | |
# There is no egg-info. This is probably because the egg-info | |
# file/directory is not named matching the distribution name used | |
# to name the archive file. Check for this case and report | |
# accordingly. | |
import glob | |
pat = os.path.join(os.path.dirname(egginfo_path), "*.egg-info") | |
possible = glob.glob(pat) | |
err = "Egg metadata expected at %s but not found" % (egginfo_path,) | |
if possible: | |
alt = os.path.basename(possible[0]) | |
err += " (%s found - possible misnamed archive file?)" % (alt,) | |
raise ValueError(err) | |
if os.path.isfile(egginfo_path): | |
# .egg-info is a single file | |
pkginfo_path = egginfo_path | |
pkg_info = self._pkginfo_to_metadata(egginfo_path, egginfo_path) | |
os.mkdir(distinfo_path) | |
else: | |
# .egg-info is a directory | |
pkginfo_path = os.path.join(egginfo_path, "PKG-INFO") | |
pkg_info = self._pkginfo_to_metadata(egginfo_path, pkginfo_path) | |
# ignore common egg metadata that is useless to wheel | |
shutil.copytree( | |
egginfo_path, | |
distinfo_path, | |
ignore=lambda x, y: set( | |
("PKG-INFO", "requires.txt", "SOURCES.txt", "not-zip-safe") | |
), | |
) | |
# delete dependency_links if it is only whitespace | |
dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt") | |
with open(dependency_links_path, "r") as dependency_links_file: | |
dependency_links = dependency_links_file.read().strip() | |
if not dependency_links: | |
adios(dependency_links_path) | |
write_pkg_info(os.path.join(distinfo_path, "METADATA"), pkg_info) | |
# XXX deprecated. Still useful for current distribute/setuptools. | |
metadata_path = os.path.join(distinfo_path, "METADATA") | |
self.add_requirements(metadata_path) | |
# XXX intentionally a different path than the PEP. | |
metadata_json_path = os.path.join(distinfo_path, "metadata.json") | |
pymeta = pkginfo_to_dict(metadata_path, distribution=self.distribution) | |
if "description" in pymeta: | |
description_filename = "DESCRIPTION.rst" | |
description_text = pymeta.pop("description") | |
description_path = os.path.join(distinfo_path, description_filename) | |
with open(description_path, "wb") as description_file: | |
description_file.write(description_text.encode("utf-8")) | |
pymeta["extensions"]["python.details"]["document_names"][ | |
"description" | |
] = description_filename | |
# XXX heuristically copy any LICENSE/LICENSE.txt? | |
license = self.license_file() | |
if license: | |
license_filename = "LICENSE.txt" | |
shutil.copy(license, os.path.join(self.distinfo_dir, license_filename)) | |
pymeta["extensions"]["python.details"]["document_names"][ | |
"license" | |
] = license_filename | |
with open(metadata_json_path, "w") as metadata_json: | |
json.dump(pymeta, metadata_json, sort_keys=True) | |
adios(egginfo_path) | |
def write_record(self, bdist_dir, distinfo_dir): | |
from wheel.util import urlsafe_b64encode | |
record_path = os.path.join(distinfo_dir, "RECORD") | |
record_relpath = os.path.relpath(record_path, bdist_dir) | |
def walk(): | |
for dir, dirs, files in os.walk(bdist_dir): | |
dirs.sort() | |
for f in sorted(files): | |
yield os.path.join(dir, f) | |
def skip(path): | |
"""Wheel hashes every possible file.""" | |
return path == record_relpath | |
with open_for_csv(record_path, "w+") as record_file: | |
writer = csv.writer(record_file) | |
for path in walk(): | |
relpath = os.path.relpath(path, bdist_dir) | |
if skip(relpath): | |
hash = "" | |
size = "" | |
else: | |
with open(path, "rb") as f: | |
data = f.read() | |
digest = hashlib.sha256(data).digest() | |
hash = "sha256=" + native(urlsafe_b64encode(digest)) | |
size = len(data) | |
record_path = os.path.relpath(path, bdist_dir).replace(os.path.sep, "/") | |
writer.writerow((record_path, hash, size)) | |
# ------------------------------------------------------------------------- | |
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. See License.txt in the project root for | |
# license information. | |
# -------------------------------------------------------------------------- | |
from distutils import log as logger | |
import os.path | |
# from wheel.bdist_wheel import bdist_wheel | |
class azure_bdist_wheel(bdist_wheel): | |
description = "Create an Azure wheel distribution" | |
user_options = bdist_wheel.user_options + [ | |
("azure-namespace-package=", None, "Name of the deepest nspkg used") | |
] | |
def initialize_options(self): | |
bdist_wheel.initialize_options(self) | |
self.azure_namespace_package = None | |
def finalize_options(self): | |
bdist_wheel.finalize_options(self) | |
if self.azure_namespace_package and not self.azure_namespace_package.endswith( | |
"-nspkg" | |
): | |
raise ValueError("azure_namespace_package must finish by -nspkg") | |
def run(self): | |
if not self.distribution.install_requires: | |
self.distribution.install_requires = [] | |
self.distribution.install_requires.append( | |
"{}>=2.0.0".format(self.azure_namespace_package) | |
) | |
bdist_wheel.run(self) | |
def write_record(self, bdist_dir, distinfo_dir): | |
if self.azure_namespace_package: | |
# Split and remove last part, assuming it's "nspkg" | |
subparts = self.azure_namespace_package.split("-")[0:-1] | |
folder_with_init = [ | |
os.path.join(*subparts[0 : i + 1]) for i in range(len(subparts)) | |
] | |
for azure_sub_package in folder_with_init: | |
init_file = os.path.join(bdist_dir, azure_sub_package, "__init__.py") | |
if os.path.isfile(init_file): | |
logger.info( | |
"manually remove {} while building the wheel".format(init_file) | |
) | |
os.remove(init_file) | |
else: | |
raise ValueError( | |
"Unable to find {}. Are you sure of your namespace package?".format( | |
init_file | |
) | |
) | |
bdist_wheel.write_record(self, bdist_dir, distinfo_dir) | |
cmdclass = {"bdist_wheel": azure_bdist_wheel} | |