Commit 1231a4e0 authored by Tarek Ziade's avatar Tarek Ziade

initial import of the packaging package in the standard library

parent 566f8a64
"""Support for packaging, distribution and installation of Python projects.
Third-party tools can use parts of packaging as building blocks
without causing the other modules to be imported:
import packaging.version
import packaging.metadata
import packaging.pypi.simple
import packaging.tests.pypi_server
"""
from logging import getLogger
__all__ = ['__version__', 'logger']
__version__ = "1.0a3"
logger = getLogger('packaging')
This diff is collapsed.
"""Subpackage containing all standard commands."""
from packaging.errors import PackagingModuleError
from packaging.util import resolve_name
__all__ = ['get_command_names', 'set_command', 'get_command_class',
'STANDARD_COMMANDS']
_COMMANDS = {
'check': 'packaging.command.check.check',
'test': 'packaging.command.test.test',
'build': 'packaging.command.build.build',
'build_py': 'packaging.command.build_py.build_py',
'build_ext': 'packaging.command.build_ext.build_ext',
'build_clib': 'packaging.command.build_clib.build_clib',
'build_scripts': 'packaging.command.build_scripts.build_scripts',
'clean': 'packaging.command.clean.clean',
'install_dist': 'packaging.command.install_dist.install_dist',
'install_lib': 'packaging.command.install_lib.install_lib',
'install_headers': 'packaging.command.install_headers.install_headers',
'install_scripts': 'packaging.command.install_scripts.install_scripts',
'install_data': 'packaging.command.install_data.install_data',
'install_distinfo':
'packaging.command.install_distinfo.install_distinfo',
'sdist': 'packaging.command.sdist.sdist',
'bdist': 'packaging.command.bdist.bdist',
'bdist_dumb': 'packaging.command.bdist_dumb.bdist_dumb',
'bdist_wininst': 'packaging.command.bdist_wininst.bdist_wininst',
'register': 'packaging.command.register.register',
'upload': 'packaging.command.upload.upload',
'upload_docs': 'packaging.command.upload_docs.upload_docs'}
STANDARD_COMMANDS = set(_COMMANDS)
def get_command_names():
"""Return registered commands"""
return sorted(_COMMANDS)
def set_command(location):
cls = resolve_name(location)
# XXX we want to do the duck-type checking here
_COMMANDS[cls.get_command_name()] = cls
def get_command_class(name):
"""Return the registered command"""
try:
cls = _COMMANDS[name]
if isinstance(cls, str):
cls = resolve_name(cls)
_COMMANDS[name] = cls
return cls
except KeyError:
raise PackagingModuleError("Invalid command %s" % name)
"""Create a built (binary) distribution.
If a --formats option was given on the command line, this command will
call the corresponding bdist_* commands; if the option was absent, a
bdist_* command depending on the current platform will be called.
"""
import os
from packaging import util
from packaging.command.cmd import Command
from packaging.errors import PackagingPlatformError, PackagingOptionError
def show_formats():
"""Print list of available formats (arguments to "--format" option).
"""
from packaging.fancy_getopt import FancyGetopt
formats = []
for format in bdist.format_commands:
formats.append(("formats=" + format, None,
bdist.format_command[format][1]))
pretty_printer = FancyGetopt(formats)
pretty_printer.print_help("List of available distribution formats:")
class bdist(Command):
description = "create a built (binary) distribution"
user_options = [('bdist-base=', 'b',
"temporary directory for creating built distributions"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % util.get_platform()),
('formats=', None,
"formats for distribution (comma-separated list)"),
('dist-dir=', 'd',
"directory to put final built distributions in "
"[default: dist]"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('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]"),
]
boolean_options = ['skip-build']
help_options = [
('help-formats', None,
"lists available distribution formats", show_formats),
]
# This is of course very simplistic. The various UNIX family operating
# systems have their specific formats, but they are out of scope for us;
# bdist_dumb is, well, dumb; it's more a building block for other
# packaging tools than a real end-user binary format.
default_format = {'posix': 'gztar',
'nt': 'zip',
'os2': 'zip'}
# Establish the preferred order (for the --help-formats option).
format_commands = ['gztar', 'bztar', 'ztar', 'tar',
'wininst', 'zip', 'msi']
# And the real information.
format_command = {'gztar': ('bdist_dumb', "gzip'ed tar file"),
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
'ztar': ('bdist_dumb', "compressed tar file"),
'tar': ('bdist_dumb', "tar file"),
'wininst': ('bdist_wininst',
"Windows executable installer"),
'zip': ('bdist_dumb', "ZIP file"),
'msi': ('bdist_msi', "Microsoft Installer")
}
def initialize_options(self):
self.bdist_base = None
self.plat_name = None
self.formats = None
self.dist_dir = None
self.skip_build = False
self.group = None
self.owner = None
def finalize_options(self):
# have to finalize 'plat_name' before 'bdist_base'
if self.plat_name is None:
if self.skip_build:
self.plat_name = util.get_platform()
else:
self.plat_name = self.get_finalized_command('build').plat_name
# 'bdist_base' -- parent of per-built-distribution-format
# temporary directories (eg. we'll probably have
# "build/bdist.<plat>/dumb", etc.)
if self.bdist_base is None:
build_base = self.get_finalized_command('build').build_base
self.bdist_base = os.path.join(build_base,
'bdist.' + self.plat_name)
self.ensure_string_list('formats')
if self.formats is None:
try:
self.formats = [self.default_format[os.name]]
except KeyError:
raise PackagingPlatformError("don't know how to create built distributions " + \
"on platform %s" % os.name)
if self.dist_dir is None:
self.dist_dir = "dist"
def run(self):
# Figure out which sub-commands we need to run.
commands = []
for format in self.formats:
try:
commands.append(self.format_command[format][0])
except KeyError:
raise PackagingOptionError("invalid format '%s'" % format)
# Reinitialize and run each command.
for i in range(len(self.formats)):
cmd_name = commands[i]
sub_cmd = self.get_reinitialized_command(cmd_name)
# passing the owner and group names for tar archiving
if cmd_name == 'bdist_dumb':
sub_cmd.owner = self.owner
sub_cmd.group = self.group
# If we're going to need to run this command again, tell it to
# keep its temporary files around so subsequent runs go faster.
if cmd_name in commands[i+1:]:
sub_cmd.keep_temp = True
self.run_command(cmd_name)
"""Create a "dumb" built distribution.
A dumb distribution is just an archive meant to be unpacked under
sys.prefix or sys.exec_prefix.
"""
import os
from shutil import rmtree
from sysconfig import get_python_version
from packaging.util import get_platform
from packaging.command.cmd import Command
from packaging.errors import PackagingPlatformError
from packaging import logger
class bdist_dumb(Command):
description = 'create a "dumb" built distribution'
user_options = [('bdist-dir=', 'd',
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('format=', 'f',
"archive format to create (tar, ztar, gztar, zip)"),
('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]"),
]
boolean_options = ['keep-temp', 'skip-build', 'relative']
default_format = { 'posix': 'gztar',
'nt': 'zip',
'os2': 'zip' }
def initialize_options(self):
self.bdist_dir = None
self.plat_name = None
self.format = None
self.keep_temp = False
self.dist_dir = None
self.skip_build = False
self.relative = False
self.owner = None
self.group = None
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, 'dumb')
if self.format is None:
try:
self.format = self.default_format[os.name]
except KeyError:
raise PackagingPlatformError(("don't know how to create dumb built distributions " +
"on platform %s") % os.name)
self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
def run(self):
if not self.skip_build:
self.run_command('build')
install = self.get_reinitialized_command('install_dist',
reinit_subcommands=True)
install.root = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = False
logger.info("installing to %s", self.bdist_dir)
self.run_command('install_dist')
# And make an archive relative to the root of the
# pseudo-installation tree.
archive_basename = "%s.%s" % (self.distribution.get_fullname(),
self.plat_name)
# OS/2 objects to any ":" characters in a filename (such as when
# a timestamp is used in a version) so change them to hyphens.
if os.name == "os2":
archive_basename = archive_basename.replace(":", "-")
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
if not self.relative:
archive_root = self.bdist_dir
else:
if (self.distribution.has_ext_modules() and
(install.install_base != install.install_platbase)):
raise PackagingPlatformError(
"can't make a dumb built distribution where base and "
"platbase are different (%r, %r)" %
(install.install_base, install.install_platbase))
else:
archive_root = os.path.join(
self.bdist_dir,
self._ensure_relative(install.install_base))
# Make the archive
filename = self.make_archive(pseudoinstall_root,
self.format, root_dir=archive_root,
owner=self.owner, group=self.group)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
pyversion = 'any'
self.distribution.dist_files.append(('bdist_dumb', pyversion,
filename))
if not self.keep_temp:
if self.dry_run:
logger.info('removing %s', self.bdist_dir)
else:
rmtree(self.bdist_dir)
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
This diff is collapsed.
This diff is collapsed.
"""Main build command, which calls the other build_* commands."""
import sys
import os
from packaging.util import get_platform
from packaging.command.cmd import Command
from packaging.errors import PackagingOptionError
from packaging.compiler import show_compilers
class build(Command):
description = "build everything needed to install"
user_options = [
('build-base=', 'b',
"base directory for build library"),
('build-purelib=', None,
"build directory for platform-neutral distributions"),
('build-platlib=', None,
"build directory for platform-specific distributions"),
('build-lib=', None,
"build directory for all distribution (defaults to either " +
"build-purelib or build-platlib"),
('build-scripts=', None,
"build directory for scripts"),
('build-temp=', 't',
"temporary build directory"),
('plat-name=', 'p',
"platform name to build for, if supported "
"(default: %s)" % get_platform()),
('compiler=', 'c',
"specify the compiler type"),
('debug', 'g',
"compile extensions and libraries with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('executable=', 'e',
"specify final destination interpreter path (build.py)"),
('use-2to3', None,
"use 2to3 to make source python 3.x compatible"),
('convert-2to3-doctests', None,
"use 2to3 to convert doctests in seperate text files"),
('use-2to3-fixers', None,
"list additional fixers opted for during 2to3 conversion"),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
def initialize_options(self):
self.build_base = 'build'
# these are decided only after 'build_base' has its final value
# (unless overridden by the user or client)
self.build_purelib = None
self.build_platlib = None
self.build_lib = None
self.build_temp = None
self.build_scripts = None
self.compiler = None
self.plat_name = None
self.debug = None
self.force = False
self.executable = None
self.use_2to3 = False
self.convert_2to3_doctests = None
self.use_2to3_fixers = None
def finalize_options(self):
if self.plat_name is None:
self.plat_name = get_platform()
else:
# plat-name only supported for windows (other platforms are
# supported via ./configure flags, if at all). Avoid misleading
# other platforms.
if os.name != 'nt':
raise PackagingOptionError(
"--plat-name only supported on Windows (try "
"using './configure --help' on your platform)")
plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])
# Make it so Python 2.x and Python 2.x with --with-pydebug don't
# share the same build directories. Doing so confuses the build
# process for C modules
if hasattr(sys, 'gettotalrefcount'):
plat_specifier += '-pydebug'
# 'build_purelib' and 'build_platlib' just default to 'lib' and
# 'lib.<plat>' under the base build directory. We only use one of
# them for a given distribution, though --
if self.build_purelib is None:
self.build_purelib = os.path.join(self.build_base, 'lib')
if self.build_platlib is None:
self.build_platlib = os.path.join(self.build_base,
'lib' + plat_specifier)
# 'build_lib' is the actual directory that we will use for this
# particular module distribution -- if user didn't supply it, pick
# one of 'build_purelib' or 'build_platlib'.
if self.build_lib is None:
if self.distribution.ext_modules:
self.build_lib = self.build_platlib
else:
self.build_lib = self.build_purelib
# 'build_temp' -- temporary directory for compiler turds,
# "build/temp.<plat>"
if self.build_temp is None:
self.build_temp = os.path.join(self.build_base,
'temp' + plat_specifier)
if self.build_scripts is None:
self.build_scripts = os.path.join(self.build_base,
'scripts-' + sys.version[0:3])
if self.executable is None:
self.executable = os.path.normpath(sys.executable)
def run(self):
# Run all relevant sub-commands. This will be some subset of:
# - build_py - pure Python modules
# - build_clib - standalone C libraries
# - build_ext - Python extension modules
# - build_scripts - Python scripts
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
# -- Predicates for the sub-command list ---------------------------
def has_pure_modules(self):
return self.distribution.has_pure_modules()
def has_c_libraries(self):
return self.distribution.has_c_libraries()
def has_ext_modules(self):
return self.distribution.has_ext_modules()
def has_scripts(self):
return self.distribution.has_scripts()
sub_commands = [('build_py', has_pure_modules),
('build_clib', has_c_libraries),
('build_ext', has_ext_modules),
('build_scripts', has_scripts),
]
"""Build C/C++ libraries.
This command is useful to build libraries that are included in the
distribution and needed by extension modules.
"""
# XXX this module has *lots* of code ripped-off quite transparently from
# build_ext.py -- not surprisingly really, as the work required to build
# a static library from a collection of C source files is not really all
# that different from what's required to build a shared object file from
# a collection of C source files. Nevertheless, I haven't done the
# necessary refactoring to account for the overlap in code between the
# two modules, mainly because a number of subtle details changed in the
# cut 'n paste. Sigh.
import os
from packaging.command.cmd import Command
from packaging.errors import PackagingSetupError
from packaging.compiler import customize_compiler
from packaging import logger
def show_compilers():
from packaging.compiler import show_compilers
show_compilers()
class build_clib(Command):
description = "build C/C++ libraries used by extension modules"
user_options = [
('build-clib=', 'b',
"directory to build C/C++ libraries to"),
('build-temp=', 't',
"directory to put temporary build by-products"),
('debug', 'g',
"compile with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('compiler=', 'c',
"specify the compiler type"),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
def initialize_options(self):
self.build_clib = None
self.build_temp = None
# List of libraries to build
self.libraries = None
# Compilation options for all libraries
self.include_dirs = None
self.define = None
self.undef = None
self.debug = None
self.force = False
self.compiler = None
def finalize_options(self):
# This might be confusing: both build-clib and build-temp default
# to build-temp as defined by the "build" command. This is because
# I think that C libraries are really just temporary build
# by-products, at least from the point of view of building Python
# extensions -- but I want to keep my options open.
self.set_undefined_options('build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
'compiler', 'debug', 'force')
self.libraries = self.distribution.libraries
if self.libraries:
self.check_library_list(self.libraries)
if self.include_dirs is None:
self.include_dirs = self.distribution.include_dirs or []
if isinstance(self.include_dirs, str):
self.include_dirs = self.include_dirs.split(os.pathsep)
# XXX same as for build_ext -- what about 'self.define' and
# 'self.undef' ?
def run(self):
if not self.libraries:
return
# Yech -- this is cut 'n pasted from build_ext.py!
from packaging.compiler import new_compiler
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run,
force=self.force)
customize_compiler(self.compiler)
if self.include_dirs is not None:
self.compiler.set_include_dirs(self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for name, value in self.define:
self.compiler.define_macro(name, value)
if self.undef is not None:
for macro in self.undef:
self.compiler.undefine_macro(macro)
self.build_libraries(self.libraries)
def check_library_list(self, libraries):
"""Ensure that the list of libraries is valid.
`library` is presumably provided as a command option 'libraries'.
This method checks that it is a list of 2-tuples, where the tuples
are (library_name, build_info_dict).
Raise PackagingSetupError if the structure is invalid anywhere;
just returns otherwise.
"""
if not isinstance(libraries, list):
raise PackagingSetupError("'libraries' option must be a list of tuples")
for lib in libraries:
if not isinstance(lib, tuple) and len(lib) != 2:
raise PackagingSetupError("each element of 'libraries' must a 2-tuple")
name, build_info = lib
if not isinstance(name, str):
raise PackagingSetupError("first element of each tuple in 'libraries' " + \
"must be a string (the library name)")
if '/' in name or (os.sep != '/' and os.sep in name):
raise PackagingSetupError(("bad library name '%s': " +
"may not contain directory separators") % \
lib[0])
if not isinstance(build_info, dict):
raise PackagingSetupError("second element of each tuple in 'libraries' " + \
"must be a dictionary (build info)")
def get_library_names(self):
# Assume the library list is valid -- 'check_library_list()' is
# called from 'finalize_options()', so it should be!
if not self.libraries:
return None
lib_names = []
for lib_name, build_info in self.libraries:
lib_names.append(lib_name)
return lib_names
def get_source_files(self):
self.check_library_list(self.libraries)
filenames = []
for lib_name, build_info in self.libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise PackagingSetupError(("in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames") % lib_name)
filenames.extend(sources)
return filenames
def build_libraries(self, libraries):
for lib_name, build_info in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise PackagingSetupError(("in 'libraries' option (library '%s'), " +
"'sources' must be present and must be " +
"a list of source filenames") % lib_name)
sources = list(sources)
logger.info("building '%s' library", lib_name)
# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(objects, lib_name,
output_dir=self.build_clib,
debug=self.debug)
This diff is collapsed.
This diff is collapsed.
"""Build scripts (copy to build dir and fix up shebang line)."""
import os
import re
import sysconfig
from packaging.command.cmd import Command
from packaging.util import convert_path, newer
from packaging import logger
from packaging.compat import Mixin2to3
# check if Python is called on the first line with this expression
first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$')
class build_scripts(Command, Mixin2to3):
description = "build scripts (copy and fix up shebang line)"
user_options = [
('build-dir=', 'd', "directory to build (copy) to"),
('force', 'f', "forcibly build everything (ignore file timestamps"),
('executable=', 'e', "specify final destination interpreter path"),
]
boolean_options = ['force']
def initialize_options(self):
self.build_dir = None
self.scripts = None
self.force = None
self.executable = None
self.outfiles = None
self.use_2to3 = False
self.convert_2to3_doctests = None
self.use_2to3_fixers = None
def finalize_options(self):
self.set_undefined_options('build',
('build_scripts', 'build_dir'),
'use_2to3', 'use_2to3_fixers',
'convert_2to3_doctests', 'force',
'executable')
self.scripts = self.distribution.scripts
def get_source_files(self):
return self.scripts
def run(self):
if not self.scripts:
return
copied_files = self.copy_scripts()
if self.use_2to3 and copied_files:
self._run_2to3(copied_files, fixers=self.use_2to3_fixers)
def copy_scripts(self):
"""Copy each script listed in 'self.scripts'; if it's marked as a
Python script in the Unix way (first line matches 'first_line_re',
ie. starts with "\#!" and contains "python"), then adjust the first
line to refer to the current Python interpreter as we copy.
"""
self.mkpath(self.build_dir)
outfiles = []
for script in self.scripts:
adjust = False
script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script))
outfiles.append(outfile)
if not self.force and not newer(script, outfile):
logger.debug("not copying %s (up-to-date)", script)
continue
# Always open the file, but ignore failures in dry-run mode --
# that way, we'll get accurate feedback if we can read the
# script.
try:
f = open(script, "r")
except IOError:
if not self.dry_run:
raise
f = None
else:
first_line = f.readline()
if not first_line:
logger.warning('%s: %s is an empty file (skipping)',
self.get_command_name(), script)
continue
match = first_line_re.match(first_line)
if match:
adjust = True
post_interp = match.group(1) or ''
if adjust:
logger.info("copying and adjusting %s -> %s", script,
self.build_dir)
if not self.dry_run:
outf = open(outfile, "w")
if not sysconfig.is_python_build():
outf.write("#!%s%s\n" %
(self.executable,
post_interp))
else:
outf.write("#!%s%s\n" %
(os.path.join(
sysconfig.get_config_var("BINDIR"),
"python%s%s" % (sysconfig.get_config_var("VERSION"),
sysconfig.get_config_var("EXE"))),
post_interp))
outf.writelines(f.readlines())
outf.close()
if f:
f.close()
else:
if f:
f.close()
self.copy_file(script, outfile)
if os.name == 'posix':
for file in outfiles:
if self.dry_run:
logger.info("changing mode of %s", file)
else:
oldmode = os.stat(file).st_mode & 0o7777
newmode = (oldmode | 0o555) & 0o7777
if newmode != oldmode:
logger.info("changing mode of %s from %o to %o",
file, oldmode, newmode)
os.chmod(file, newmode)
return outfiles
"""Check PEP compliance of metadata."""
from packaging import logger
from packaging.command.cmd import Command
from packaging.errors import PackagingSetupError
from packaging.util import resolve_name
class check(Command):
description = "check PEP compliance of metadata"
user_options = [('metadata', 'm', 'Verify metadata'),
('all', 'a',
('runs extended set of checks')),
('strict', 's',
'Will exit with an error if a check fails')]
boolean_options = ['metadata', 'all', 'strict']
def initialize_options(self):
"""Sets default values for options."""
self.all = False
self.metadata = True
self.strict = False
self._warnings = []
def finalize_options(self):
pass
def warn(self, msg, *args):
"""Wrapper around logging that also remembers messages."""
# XXX we could use a special handler for this, but would need to test
# if it works even if the logger has a too high level
self._warnings.append((msg, args))
return logger.warning(self.get_command_name() + msg, *args)
def run(self):
"""Runs the command."""
# perform the various tests
if self.metadata:
self.check_metadata()
if self.all:
self.check_restructuredtext()
self.check_hooks_resolvable()
# let's raise an error in strict mode, if we have at least
# one warning
if self.strict and len(self._warnings) > 0:
msg = '\n'.join(msg % args for msg, args in self._warnings)
raise PackagingSetupError(msg)
def check_metadata(self):
"""Ensures that all required elements of metadata are supplied.
name, version, URL, author
Warns if any are missing.
"""
missing, warnings = self.distribution.metadata.check(strict=True)
if missing != []:
self.warn('missing required metadata: %s', ', '.join(missing))
for warning in warnings:
self.warn(warning)
def check_restructuredtext(self):
"""Checks if the long string fields are reST-compliant."""
missing, warnings = self.distribution.metadata.check(restructuredtext=True)
if self.distribution.metadata.docutils_support:
for warning in warnings:
line = warning[-1].get('line')
if line is None:
warning = warning[1]
else:
warning = '%s (line %s)' % (warning[1], line)
self.warn(warning)
elif self.strict:
raise PackagingSetupError('The docutils package is needed.')
def check_hooks_resolvable(self):
for options in self.distribution.command_options.values():
for hook_kind in ("pre_hook", "post_hook"):
if hook_kind not in options:
break
for hook_name in options[hook_kind][1].values():
try:
resolve_name(hook_name)
except ImportError:
self.warn('name %r cannot be resolved', hook_name)
"""Clean up temporary files created by the build command."""
# Contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>
import os
from shutil import rmtree
from packaging.command.cmd import Command
from packaging import logger
class clean(Command):
description = "clean up temporary files from 'build' command"
user_options = [
('build-base=', 'b',
"base build directory (default: 'build.build-base')"),
('build-lib=', None,
"build directory for all modules (default: 'build.build-lib')"),
('build-temp=', 't',
"temporary build directory (default: 'build.build-temp')"),
('build-scripts=', None,
"build directory for scripts (default: 'build.build-scripts')"),
('bdist-base=', None,
"temporary directory for built distributions"),
('all', 'a',
"remove all build output, not just temporary by-products")
]
boolean_options = ['all']
def initialize_options(self):
self.build_base = None
self.build_lib = None
self.build_temp = None
self.build_scripts = None
self.bdist_base = None
self.all = None
def finalize_options(self):
self.set_undefined_options('build', 'build_base', 'build_lib',
'build_scripts', 'build_temp')
self.set_undefined_options('bdist', 'bdist_base')
def run(self):
# remove the build/temp.<plat> directory (unless it's already
# gone)
if os.path.exists(self.build_temp):
if self.dry_run:
logger.info('removing %s', self.build_temp)
else:
rmtree(self.build_temp)
else:
logger.debug("'%s' does not exist -- can't clean it",
self.build_temp)
if self.all:
# remove build directories
for directory in (self.build_lib,
self.bdist_base,
self.build_scripts):
if os.path.exists(directory):
if self.dry_run:
logger.info('removing %s', directory)
else:
rmtree(directory)
else:
logger.warning("'%s' does not exist -- can't clean it",
directory)
# just for the heck of it, try to remove the base build directory:
# we might have emptied it right now, but if not we don't care
if not self.dry_run:
try:
os.rmdir(self.build_base)
logger.info("removing '%s'", self.build_base)
except OSError:
pass
This diff is collapsed.
"""Do X and Y."""
from packaging import logger
from packaging.command.cmd import Command
class x(Command):
# Brief (40-50 characters) description of the command
description = ""
# List of option tuples: long name, short name (None if no short
# name), and help string.
user_options = [
('', '', # long option, short option (one letter) or None
""), # help text
]
def initialize_options(self):
self. = None
self. = None
self. = None
def finalize_options(self):
if self.x is None:
self.x = ...
def run(self):
...
logger.info(...)
if not self.dry_run:
...
self.execute(..., dry_run=self.dry_run)
This diff is collapsed.
"""Install platform-independent data files."""
# Contributed by Bastian Kleineidam
import os
from shutil import Error
from sysconfig import get_paths, format_value
from packaging import logger
from packaging.util import convert_path
from packaging.command.cmd import Command
class install_data(Command):
description = "install platform-independent data files"
user_options = [
('install-dir=', 'd',
"base directory for installing data files "
"(default: installation base dir)"),
('root=', None,
"install everything relative to this alternate root directory"),
('force', 'f', "force installation (overwrite existing files)"),
]
boolean_options = ['force']
def initialize_options(self):
self.install_dir = None
self.outfiles = []
self.data_files_out = []
self.root = None
self.force = False
self.data_files = self.distribution.data_files
self.warn_dir = True
def finalize_options(self):
self.set_undefined_options('install_dist',
('install_data', 'install_dir'),
'root', 'force')
def run(self):
self.mkpath(self.install_dir)
for _file in self.data_files.items():
destination = convert_path(self.expand_categories(_file[1]))
dir_dest = os.path.abspath(os.path.dirname(destination))
self.mkpath(dir_dest)
try:
out = self.copy_file(_file[0], dir_dest)[0]
except Error as e:
logger.warning('%s: %s', self.get_command_name(), e)
out = destination
self.outfiles.append(out)
self.data_files_out.append((_file[0], destination))
def expand_categories(self, path_with_categories):
local_vars = get_paths()
local_vars['distribution.name'] = self.distribution.metadata['Name']
expanded_path = format_value(path_with_categories, local_vars)
expanded_path = format_value(expanded_path, local_vars)
if '{' in expanded_path and '}' in expanded_path:
logger.warning(
'%s: unable to expand %s, some categories may be missing',
self.get_command_name(), path_with_categories)
return expanded_path
def get_source_files(self):
return list(self.data_files)
def get_inputs(self):
return list(self.data_files)
def get_outputs(self):
return self.outfiles
def get_resources_out(self):
return self.data_files_out
This diff is collapsed.
"""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, 'w').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=os.linesep,
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=os.linesep,
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, 'r') as fp:
hash = hashlib.md5()
hash.update(fp.read().encode())
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('-', '_')
"""Install C/C++ header files to the Python include directory."""
from packaging.command.cmd import Command
# XXX force is never used
class install_headers(Command):
description = "install C/C++ header files"
user_options = [('install-dir=', 'd',
"directory to install header files to"),
('force', 'f',
"force installation (overwrite existing files)"),
]
boolean_options = ['force']
def initialize_options(self):
self.install_dir = None
self.force = False
self.outfiles = []
def finalize_options(self):
self.set_undefined_options('install_dist',
('install_headers', 'install_dir'),
'force')
def run(self):
headers = self.distribution.headers
if not headers:
return
self.mkpath(self.install_dir)
for header in headers:
out = self.copy_file(header, self.install_dir)[0]
self.outfiles.append(out)
def get_inputs(self):
return self.distribution.headers or []
def get_outputs(self):
return self.outfiles
This diff is collapsed.
"""Install scripts."""
# Contributed by Bastian Kleineidam
import os
from packaging.command.cmd import Command
from packaging import logger
class install_scripts(Command):
description = "install scripts (Python or otherwise)"
user_options = [
('install-dir=', 'd', "directory to install scripts to"),
('build-dir=','b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('skip-build', None, "skip the build steps"),
]
boolean_options = ['force', 'skip-build']
def initialize_options(self):
self.install_dir = None
self.force = False
self.build_dir = None
self.skip_build = None
def finalize_options(self):
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
self.set_undefined_options('install_dist',
('install_scripts', 'install_dir'),
'force', 'skip_build')
def run(self):
if not self.skip_build:
self.run_command('build_scripts')
if not os.path.exists(self.build_dir):
self.outfiles = []
return
self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
if os.name == 'posix':
# Set the executable bits (owner, group, and world) on
# all the scripts we just installed.
for file in self.get_outputs():
if self.dry_run:
logger.info("changing mode of %s", file)
else:
mode = (os.stat(file).st_mode | 0o555) & 0o7777
logger.info("changing mode of %s to %o", file, mode)
os.chmod(file, mode)
def get_inputs(self):
return self.distribution.scripts or []
def get_outputs(self):
return self.outfiles or []
This diff is collapsed.
This diff is collapsed.
"""Run the project's test suite."""
import os
import sys
import logging
import unittest
from packaging import logger
from packaging.command.cmd import Command
from packaging.database import get_distribution
from packaging.errors import PackagingOptionError
from packaging.util import resolve_name
class test(Command):
description = "run the project's test suite"
user_options = [
('suite=', 's',
"test suite to run (for example: 'some_module.test_suite')"),
('runner=', None,
"test runner to be called."),
('tests-require=', None,
"list of distributions required to run the test suite."),
]
def initialize_options(self):
self.suite = None
self.runner = None
self.tests_require = []
def finalize_options(self):
self.build_lib = self.get_finalized_command("build").build_lib
for requirement in self.tests_require:
if get_distribution(requirement) is None:
logger.warning("test dependency %s is not installed, "
"tests may fail", requirement)
if (not self.suite and not self.runner and
self.get_ut_with_discovery() is None):
raise PackagingOptionError(
"no test discovery available, please give a 'suite' or "
"'runner' option or install unittest2")
def get_ut_with_discovery(self):
if hasattr(unittest.TestLoader, "discover"):
return unittest
else:
try:
import unittest2
return unittest2
except ImportError:
return None
def run(self):
prev_syspath = sys.path[:]
try:
# build release
build = self.get_reinitialized_command('build')
self.run_command('build')
sys.path.insert(0, build.build_lib)
# Temporary kludge until we remove the verbose arguments and use
# logging everywhere
logger = logging.getLogger('packaging')
verbose = logger.getEffectiveLevel() >= logging.DEBUG
verbosity = verbose + 1
# run the tests
if self.runner:
resolve_name(self.runner)()
elif self.suite:
runner = unittest.TextTestRunner(verbosity=verbosity)
runner.run(resolve_name(self.suite)())
elif self.get_ut_with_discovery():
ut = self.get_ut_with_discovery()
test_suite = ut.TestLoader().discover(os.curdir)
runner = ut.TextTestRunner(verbosity=verbosity)
runner.run(test_suite)
finally:
sys.path[:] = prev_syspath
This diff is collapsed.
This diff is collapsed.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
"""Low-level and high-level APIs to interact with project indexes."""
__all__ = ['simple',
'xmlrpc',
'dist',
'errors',
'mirrors']
from packaging.pypi.dist import ReleaseInfo, ReleasesList, DistInfo
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Metadata-version: 1.2
Name: babar
Version: 0.1
Author: FELD Boris
\ No newline at end of file
babar.png,babar.png
babar.cfg,babar.cfg
\ No newline at end of file
Config
\ No newline at end of file
This diff was suppressed by a .gitattributes entry.
This diff is collapsed.
# -*- Entry points: -*-
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment