Kaydet (Commit) 95fc53f2 authored tarafından Éric Araujo's avatar Éric Araujo

Clean up packaging.util: add __all__, remove some unused functions.

This huge module is the heir of six distutils modules, and contains
a number of miscellaneous functions.  I have attempted to help readers
of the source code with an annoted __all__.  Removed or deprecated
functions have been removed from the documentation; I’m working on
another patch to document the remaining public functions.

For the curious:

The unzip_file and untar_file were used by (or intended to be used by)
“pysetup install path/to/archive.tar.gz”, but the code presently used
shutil.unpack_archive and an helper function, so I just deleted them.
They’re still in the repository if we need them in the future.

The find_packages function is not used anymore but I want to discuss
module and package auto-discovery (in “pysetup create”) again before
removing it.

subst_vars now lives in sysconfig; rfc822_escape is inlined in
packaging.metadata.  Other functions are for internal use only, or
deprecated; I have left them out of __all__ and sprinkled TODO notes
for future cleanups.
üst ab3bea68
...@@ -90,34 +90,6 @@ This module contains various helpers for the other modules. ...@@ -90,34 +90,6 @@ This module contains various helpers for the other modules.
Search the path for a given executable name. Search the path for a given executable name.
.. function:: subst_vars(s, local_vars)
Perform shell/Perl-style variable substitution on *s*. Every occurrence of
``$`` followed by a name is considered a variable, and variable is
substituted by the value found in the *local_vars* dictionary, or in
``os.environ`` if it's not in *local_vars*. *os.environ* is first
checked/augmented to guarantee that it contains certain values: see
:func:`check_environ`. Raise :exc:`ValueError` for any variables not found
in either *local_vars* or ``os.environ``.
Note that this is not a fully-fledged string interpolation function. A valid
``$variable`` can consist only of upper and lower case letters, numbers and
an underscore. No { } or ( ) style quoting is available.
.. function:: split_quoted(s)
Split a string up according to Unix shell-like rules for quotes and
backslashes. In short: words are delimited by spaces, as long as those spaces
are not escaped by a backslash, or inside a quoted string. Single and double
quotes are equivalent, and the quote characters can be backslash-escaped.
The backslash is stripped from any two-character escape sequence, leaving
only the escaped character. The quote characters are stripped from any
quoted string. Returns a list of words.
.. TODO Should probably be moved into the standard library.
.. function:: execute(func, args[, msg=None, verbose=0, dry_run=0]) .. function:: execute(func, args[, msg=None, verbose=0, dry_run=0])
Perform some action that affects the outside world (for instance, writing to Perform some action that affects the outside world (for instance, writing to
...@@ -175,12 +147,3 @@ This module contains various helpers for the other modules. ...@@ -175,12 +147,3 @@ This module contains various helpers for the other modules.
figure out to use direct compilation or not (see the source for details). figure out to use direct compilation or not (see the source for details).
The *direct* flag is used by the script generated in indirect mode; unless The *direct* flag is used by the script generated in indirect mode; unless
you know what you're doing, leave it set to ``None``. you know what you're doing, leave it set to ``None``.
.. function:: rfc822_escape(header)
Return a version of *header* escaped for inclusion in an :rfc:`822` header, by
ensuring there are 8 spaces space after each newline. Note that it does no
other modification of the string.
.. TODO this _can_ be replaced
...@@ -393,7 +393,7 @@ class build_py(Command, Mixin2to3): ...@@ -393,7 +393,7 @@ class build_py(Command, Mixin2to3):
self.get_command_name()) self.get_command_name())
return return
from packaging.util import byte_compile from packaging.util import byte_compile # FIXME use compileall
prefix = self.build_lib prefix = self.build_lib
if prefix[-1] != os.sep: if prefix[-1] != os.sep:
prefix = prefix + os.sep prefix = prefix + os.sep
......
...@@ -122,7 +122,7 @@ class install_lib(Command): ...@@ -122,7 +122,7 @@ class install_lib(Command):
self.get_command_name()) self.get_command_name())
return return
from packaging.util import byte_compile from packaging.util import byte_compile # FIXME use compileall
# Get the "--root" directory supplied to the "install_dist" command, # Get the "--root" directory supplied to the "install_dist" command,
# and use it as a prefix to strip off the purported filename # and use it as a prefix to strip off the purported filename
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# Contributed by Richard Jones # Contributed by Richard Jones
import io
import getpass import getpass
import urllib.error import urllib.error
import urllib.parse import urllib.parse
......
...@@ -5,7 +5,6 @@ import socket ...@@ -5,7 +5,6 @@ import socket
import logging import logging
import platform import platform
import urllib.parse import urllib.parse
from io import BytesIO
from base64 import standard_b64encode from base64 import standard_b64encode
from hashlib import md5 from hashlib import md5
from urllib.error import HTTPError from urllib.error import HTTPError
......
...@@ -216,7 +216,7 @@ class Config: ...@@ -216,7 +216,7 @@ class Config:
for data in files.get('package_data', []): for data in files.get('package_data', []):
data = data.split('=') data = data.split('=')
if len(data) != 2: if len(data) != 2:
continue # XXX error should never pass silently continue # FIXME errors should never pass silently
key, value = data key, value = data
self.dist.package_data[key.strip()] = value.strip() self.dist.package_data[key.strip()] = value.strip()
......
...@@ -36,7 +36,7 @@ from packaging._trove import all_classifiers as _CLASSIFIERS_LIST ...@@ -36,7 +36,7 @@ from packaging._trove import all_classifiers as _CLASSIFIERS_LIST
from packaging.version import is_valid_version from packaging.version import is_valid_version
_FILENAME = 'setup.cfg' _FILENAME = 'setup.cfg'
_DEFAULT_CFG = '.pypkgcreate' _DEFAULT_CFG = '.pypkgcreate' # FIXME use a section in user .pydistutils.cfg
_helptext = { _helptext = {
'name': ''' 'name': '''
...@@ -127,6 +127,10 @@ def ask_yn(question, default=None, helptext=None): ...@@ -127,6 +127,10 @@ def ask_yn(question, default=None, helptext=None):
print('\nERROR: You must select "Y" or "N".\n') print('\nERROR: You must select "Y" or "N".\n')
# XXX use util.ask
# FIXME: if prompt ends with '?', don't add ':'
def ask(question, default=None, helptext=None, required=True, def ask(question, default=None, helptext=None, required=True,
lengthy=False, multiline=False): lengthy=False, multiline=False):
prompt = '%s: ' % (question,) prompt = '%s: ' % (question,)
......
...@@ -15,7 +15,7 @@ from packaging.errors import ( ...@@ -15,7 +15,7 @@ from packaging.errors import (
from packaging import util from packaging import util
from packaging.dist import Distribution from packaging.dist import Distribution
from packaging.util import ( from packaging.util import (
convert_path, change_root, split_quoted, strtobool, rfc822_escape, convert_path, change_root, split_quoted, strtobool,
get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages,
spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob, spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob,
RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging, RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging,
...@@ -255,13 +255,6 @@ class UtilTestCase(support.EnvironRestorer, ...@@ -255,13 +255,6 @@ class UtilTestCase(support.EnvironRestorer,
for n in no: for n in no:
self.assertFalse(strtobool(n)) self.assertFalse(strtobool(n))
def test_rfc822_escape(self):
header = 'I am a\npoor\nlonesome\nheader\n'
res = rfc822_escape(header)
wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s'
'header%(8s)s') % {'8s': '\n' + 8 * ' '}
self.assertEqual(res, wanted)
def test_find_exe_version(self): def test_find_exe_version(self):
# the ld version scheme under MAC OS is: # the ld version scheme under MAC OS is:
# ^@(#)PROGRAM:ld PROJECT:ld64-VERSION # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION
......
...@@ -8,8 +8,6 @@ import errno ...@@ -8,8 +8,6 @@ import errno
import shutil import shutil
import string import string
import hashlib import hashlib
import tarfile
import zipfile
import posixpath import posixpath
import subprocess import subprocess
import sysconfig import sysconfig
...@@ -23,6 +21,30 @@ from packaging.errors import (PackagingPlatformError, PackagingFileError, ...@@ -23,6 +21,30 @@ from packaging.errors import (PackagingPlatformError, PackagingFileError,
PackagingByteCompileError, PackagingExecError, PackagingByteCompileError, PackagingExecError,
InstallationException, PackagingInternalError) InstallationException, PackagingInternalError)
__all__ = [
# file dependencies
'newer', 'newer_group',
# helpers for commands (dry-run system)
'execute', 'write_file',
# spawning programs
'find_executable', 'spawn',
# path manipulation
'convert_path', 'change_root',
# 2to3 conversion
'Mixin2to3', 'run_2to3',
# packaging compatibility helpers
'cfg_to_args', 'generate_setup_py',
'egginfo_to_distinfo',
'get_install_method',
# misc
'ask', 'check_environ', 'encode_multipart', 'resolve_name',
# querying for information TODO move to sysconfig
'get_compiler_versions', 'get_platform', 'set_platform',
# configuration TODO move to packaging.config
'get_pypirc_path', 'read_pypirc', 'generate_pypirc',
'strtobool', 'split_multiline',
]
_PLATFORM = None _PLATFORM = None
_DEFAULT_INSTALLER = 'packaging' _DEFAULT_INSTALLER = 'packaging'
...@@ -152,31 +174,6 @@ def check_environ(): ...@@ -152,31 +174,6 @@ def check_environ():
_environ_checked = True _environ_checked = True
def subst_vars(s, local_vars):
"""Perform shell/Perl-style variable substitution on 'string'.
Every occurrence of '$' followed by a name is considered a variable, and
variable is substituted by the value found in the 'local_vars'
dictionary, or in 'os.environ' if it's not in 'local_vars'.
'os.environ' is first checked/augmented to guarantee that it contains
certain values: see 'check_environ()'. Raise ValueError for any
variables not found in either 'local_vars' or 'os.environ'.
"""
check_environ()
def _subst(match, local_vars=local_vars):
var_name = match.group(1)
if var_name in local_vars:
return str(local_vars[var_name])
else:
return os.environ[var_name]
try:
return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
except KeyError as var:
raise ValueError("invalid variable '$%s'" % var)
# Needed by 'split_quoted()' # Needed by 'split_quoted()'
_wordchars_re = _squote_re = _dquote_re = None _wordchars_re = _squote_re = _dquote_re = None
...@@ -188,6 +185,8 @@ def _init_regex(): ...@@ -188,6 +185,8 @@ def _init_regex():
_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
# TODO replace with shlex.split after testing
def split_quoted(s): def split_quoted(s):
"""Split a string up according to Unix shell-like rules for quotes and """Split a string up according to Unix shell-like rules for quotes and
backslashes. backslashes.
...@@ -435,15 +434,6 @@ byte_compile(files, optimize=%r, force=%r, ...@@ -435,15 +434,6 @@ byte_compile(files, optimize=%r, force=%r,
file, cfile_base) file, cfile_base)
def rfc822_escape(header):
"""Return a form of *header* suitable for inclusion in an RFC 822-header.
This function ensures there are 8 spaces after each newline.
"""
lines = header.split('\n')
sep = '\n' + 8 * ' '
return sep.join(lines)
_RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') _RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld ' _MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld '
'PROJECT:ld64-((\d+)(\.\d+)*)') 'PROJECT:ld64-((\d+)(\.\d+)*)')
...@@ -543,6 +533,10 @@ def write_file(filename, contents): ...@@ -543,6 +533,10 @@ def write_file(filename, contents):
"""Create *filename* and write *contents* to it. """Create *filename* and write *contents* to it.
*contents* is a sequence of strings without line terminators. *contents* is a sequence of strings without line terminators.
This functions is not intended to replace the usual with open + write
idiom in all cases, only with Command.execute, which runs depending on
the dry_run argument and also logs its arguments).
""" """
with open(filename, "w") as f: with open(filename, "w") as f:
for line in contents: for line in contents:
...@@ -562,6 +556,7 @@ def _is_archive_file(name): ...@@ -562,6 +556,7 @@ def _is_archive_file(name):
def _under(path, root): def _under(path, root):
# XXX use os.path
path = path.split(os.sep) path = path.split(os.sep)
root = root.split(os.sep) root = root.split(os.sep)
if len(root) > len(path): if len(root) > len(path):
...@@ -664,103 +659,11 @@ def splitext(path): ...@@ -664,103 +659,11 @@ def splitext(path):
return base, ext return base, ext
def unzip_file(filename, location, flatten=True):
"""Unzip the file *filename* into the *location* directory."""
if not os.path.exists(location):
os.makedirs(location)
with open(filename, 'rb') as zipfp:
zip = zipfile.ZipFile(zipfp)
leading = has_leading_dir(zip.namelist()) and flatten
for name in zip.namelist():
data = zip.read(name)
fn = name
if leading:
fn = split_leading_dir(name)[1]
fn = os.path.join(location, fn)
dir = os.path.dirname(fn)
if not os.path.exists(dir):
os.makedirs(dir)
if fn.endswith('/') or fn.endswith('\\'):
# A directory
if not os.path.exists(fn):
os.makedirs(fn)
else:
with open(fn, 'wb') as fp:
fp.write(data)
def untar_file(filename, location):
"""Untar the file *filename* into the *location* directory."""
if not os.path.exists(location):
os.makedirs(location)
if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'):
mode = 'r:gz'
elif (filename.lower().endswith('.bz2')
or filename.lower().endswith('.tbz')):
mode = 'r:bz2'
elif filename.lower().endswith('.tar'):
mode = 'r'
else:
mode = 'r:*'
with tarfile.open(filename, mode) as tar:
leading = has_leading_dir(member.name for member in tar.getmembers())
for member in tar.getmembers():
fn = member.name
if leading:
fn = split_leading_dir(fn)[1]
path = os.path.join(location, fn)
if member.isdir():
if not os.path.exists(path):
os.makedirs(path)
else:
try:
fp = tar.extractfile(member)
except (KeyError, AttributeError):
# Some corrupt tar files seem to produce this
# (specifically bad symlinks)
continue
try:
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
with open(path, 'wb') as destfp:
shutil.copyfileobj(fp, destfp)
finally:
fp.close()
def has_leading_dir(paths):
"""Return true if all the paths have the same leading path name.
In other words, check that everything is in one subdirectory in an
archive.
"""
common_prefix = None
for path in paths:
prefix, rest = split_leading_dir(path)
if not prefix:
return False
elif common_prefix is None:
common_prefix = prefix
elif prefix != common_prefix:
return False
return True
def split_leading_dir(path):
path = str(path)
path = path.lstrip('/').lstrip('\\')
if '/' in path and (('\\' in path and path.find('/') < path.find('\\'))
or '\\' not in path):
return path.split('/', 1)
elif '\\' in path:
return path.split('\\', 1)
else:
return path, ''
if sys.platform == 'darwin': if sys.platform == 'darwin':
_cfg_target = None _cfg_target = None
_cfg_target_split = None _cfg_target_split = None
def spawn(cmd, search_path=True, verbose=0, dry_run=False, env=None): def spawn(cmd, search_path=True, verbose=0, dry_run=False, env=None):
"""Run another program specified as a command list 'cmd' in a new process. """Run another program specified as a command list 'cmd' in a new process.
...@@ -1510,7 +1413,7 @@ def encode_multipart(fields, files, boundary=None): ...@@ -1510,7 +1413,7 @@ def encode_multipart(fields, files, boundary=None):
for key, values in fields: for key, values in fields:
# handle multiple entries for the same name # handle multiple entries for the same name
if not isinstance(values, (tuple, list)): if not isinstance(values, (tuple, list)):
values=[values] values = [values]
for value in values: for value in values:
l.extend(( l.extend((
......
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