176 lines
6.4 KiB
Python
176 lines
6.4 KiB
Python
"""Create the PEP 376-compliant .dist-info directory."""
|
|
|
|
# Forked from the former install_egg_info command by Josip Djolonga
|
|
|
|
import csv
|
|
import os
|
|
import re
|
|
import hashlib
|
|
|
|
from packaging.command.cmd import Command
|
|
from packaging import logger
|
|
from shutil import rmtree
|
|
|
|
|
|
class install_distinfo(Command):
|
|
|
|
description = 'create a .dist-info directory for the distribution'
|
|
|
|
user_options = [
|
|
('distinfo-dir=', None,
|
|
"directory where the the .dist-info directory will be installed"),
|
|
('installer=', None,
|
|
"the name of the installer"),
|
|
('requested', None,
|
|
"generate a REQUESTED file"),
|
|
('no-requested', None,
|
|
"do not generate a REQUESTED file"),
|
|
('no-record', None,
|
|
"do not generate a RECORD file"),
|
|
('no-resources', None,
|
|
"do not generate a RESSOURCES list installed file")
|
|
]
|
|
|
|
boolean_options = ['requested', 'no-record', 'no-resources']
|
|
|
|
negative_opt = {'no-requested': 'requested'}
|
|
|
|
def initialize_options(self):
|
|
self.distinfo_dir = None
|
|
self.installer = None
|
|
self.requested = None
|
|
self.no_record = None
|
|
self.no_resources = None
|
|
|
|
def finalize_options(self):
|
|
self.set_undefined_options('install_dist',
|
|
'installer', 'requested', 'no_record')
|
|
|
|
self.set_undefined_options('install_lib',
|
|
('install_dir', 'distinfo_dir'))
|
|
|
|
if self.installer is None:
|
|
# FIXME distutils or packaging?
|
|
# + document default in the option help text above and in install
|
|
self.installer = 'distutils'
|
|
if self.requested is None:
|
|
self.requested = True
|
|
if self.no_record is None:
|
|
self.no_record = False
|
|
if self.no_resources is None:
|
|
self.no_resources = False
|
|
|
|
metadata = self.distribution.metadata
|
|
|
|
basename = "%s-%s.dist-info" % (
|
|
to_filename(safe_name(metadata['Name'])),
|
|
to_filename(safe_version(metadata['Version'])))
|
|
|
|
self.distinfo_dir = os.path.join(self.distinfo_dir, basename)
|
|
self.outputs = []
|
|
|
|
def run(self):
|
|
# FIXME dry-run should be used at a finer level, so that people get
|
|
# useful logging output and can have an idea of what the command would
|
|
# have done
|
|
if not self.dry_run:
|
|
target = self.distinfo_dir
|
|
|
|
if os.path.isdir(target) and not os.path.islink(target):
|
|
rmtree(target)
|
|
elif os.path.exists(target):
|
|
self.execute(os.unlink, (self.distinfo_dir,),
|
|
"removing " + target)
|
|
|
|
self.execute(os.makedirs, (target,), "creating " + target)
|
|
|
|
metadata_path = os.path.join(self.distinfo_dir, 'METADATA')
|
|
logger.info('creating %s', metadata_path)
|
|
self.distribution.metadata.write(metadata_path)
|
|
self.outputs.append(metadata_path)
|
|
|
|
installer_path = os.path.join(self.distinfo_dir, 'INSTALLER')
|
|
logger.info('creating %s', installer_path)
|
|
with open(installer_path, 'w') as f:
|
|
f.write(self.installer)
|
|
self.outputs.append(installer_path)
|
|
|
|
if self.requested:
|
|
requested_path = os.path.join(self.distinfo_dir, 'REQUESTED')
|
|
logger.info('creating %s', requested_path)
|
|
open(requested_path, 'wb').close()
|
|
self.outputs.append(requested_path)
|
|
|
|
|
|
if not self.no_resources:
|
|
install_data = self.get_finalized_command('install_data')
|
|
if install_data.get_resources_out() != []:
|
|
resources_path = os.path.join(self.distinfo_dir,
|
|
'RESOURCES')
|
|
logger.info('creating %s', resources_path)
|
|
with open(resources_path, 'wb') as f:
|
|
writer = csv.writer(f, delimiter=',',
|
|
lineterminator='\n',
|
|
quotechar='"')
|
|
for tuple in install_data.get_resources_out():
|
|
writer.writerow(tuple)
|
|
|
|
self.outputs.append(resources_path)
|
|
|
|
if not self.no_record:
|
|
record_path = os.path.join(self.distinfo_dir, 'RECORD')
|
|
logger.info('creating %s', record_path)
|
|
with open(record_path, 'w', encoding='utf-8') as f:
|
|
writer = csv.writer(f, delimiter=',',
|
|
lineterminator='\n',
|
|
quotechar='"')
|
|
|
|
install = self.get_finalized_command('install_dist')
|
|
|
|
for fpath in install.get_outputs():
|
|
if fpath.endswith('.pyc') or fpath.endswith('.pyo'):
|
|
# do not put size and md5 hash, as in PEP-376
|
|
writer.writerow((fpath, '', ''))
|
|
else:
|
|
size = os.path.getsize(fpath)
|
|
with open(fpath, 'rb') as fp:
|
|
hash = hashlib.md5()
|
|
hash.update(fp.read())
|
|
md5sum = hash.hexdigest()
|
|
writer.writerow((fpath, md5sum, size))
|
|
|
|
# add the RECORD file itself
|
|
writer.writerow((record_path, '', ''))
|
|
self.outputs.append(record_path)
|
|
|
|
def get_outputs(self):
|
|
return self.outputs
|
|
|
|
|
|
# The following functions are taken from setuptools' pkg_resources module.
|
|
|
|
def safe_name(name):
|
|
"""Convert an arbitrary string to a standard distribution name
|
|
|
|
Any runs of non-alphanumeric/. characters are replaced with a single '-'.
|
|
"""
|
|
return re.sub('[^A-Za-z0-9.]+', '-', name)
|
|
|
|
|
|
def safe_version(version):
|
|
"""Convert an arbitrary string to a standard version string
|
|
|
|
Spaces become dots, and all other non-alphanumeric characters become
|
|
dashes, with runs of multiple dashes condensed to a single dash.
|
|
"""
|
|
version = version.replace(' ', '.')
|
|
return re.sub('[^A-Za-z0-9.]+', '-', version)
|
|
|
|
|
|
def to_filename(name):
|
|
"""Convert a project or version name to its filename-escaped form
|
|
|
|
Any '-' characters are currently replaced with '_'.
|
|
"""
|
|
return name.replace('-', '_')
|