6from collections
import deque
9from glob
import iglob
as std_iglob
30 import dummy_threading
as threading
33from .
import DistlibException
34from .compat
import (string_types, text_type, shutil, raw_input, StringIO,
35 cache_from_source, urlopen, urljoin, httplib, xmlrpclib,
36 splittype, HTTPHandler, BaseConfigurator, valid_ident,
37 Container, configparser, URLError, ZipFile, fsdecode,
49MARKER_OP =
re.compile(
r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*')
53STRING_CHUNK =
re.compile(
r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)')
56def parse_marker(marker_string):
58 Parse a marker string and return a dictionary containing a marker expression.
60 The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in
61 the expression grammar, or strings. A string contained in quotes is to be
62 interpreted as a literal string, and a string not contained in quotes is a
63 variable (such as os_name).
70 remaining = remaining[
m.end():]
76 raise SyntaxError(
'invalid expression: %s' % remaining)
77 oq =
'\'"'.replace(q,
'')
78 remaining = remaining[1:]
84 elif remaining[0] == oq:
86 remaining = remaining[1:]
90 raise SyntaxError(
'error in string literal: %s' % remaining)
92 remaining = remaining[
m.end():]
97 result =
''.join(parts)
98 remaining = remaining[1:].
lstrip()
99 return result, remaining
102 if remaining
and remaining[0] ==
'(':
103 result, remaining = marker(remaining[1:].
lstrip())
104 if remaining[0] !=
')':
105 raise SyntaxError(
'unterminated parenthesis: %s' % remaining)
106 remaining = remaining[1:].
lstrip()
114 remaining = remaining[
m.end():]
116 lhs = {
'op': op,
'lhs': lhs,
'rhs': rhs}
118 return result, remaining
126 remaining = remaining[
m.end():]
128 lhs = {
'op':
'and',
'lhs': lhs,
'rhs': rhs}
129 return lhs, remaining
131 def marker(remaining):
137 remaining = remaining[
m.end():]
139 lhs = {
'op':
'or',
'lhs': lhs,
'rhs': rhs}
140 return lhs, remaining
142 return marker(marker_string)
145def parse_requirement(req):
147 Parse a requirement passed in as a string. Return a Container
148 whose attributes contain the various parts of the requirement.
157 remaining = remaining[
m.end():]
158 extras = mark_expr = versions = uri =
None
159 if remaining
and remaining[0] ==
'[':
162 raise SyntaxError(
'unterminated extra: %s' % remaining)
164 remaining = remaining[i + 1:].
lstrip()
175 raise SyntaxError(
'comma expected in extras: %s' % s)
180 if remaining[0] ==
'@':
182 remaining = remaining[1:].
lstrip()
199 Return a list of operator, version tuples if any are
200 specified, else None.
208 ver_remaining = ver_remaining[
m.end():]
211 raise SyntaxError(
'invalid version: %s' % ver_remaining)
214 ver_remaining = ver_remaining[
m.end():]
215 if not ver_remaining
or ver_remaining[0] !=
',':
217 ver_remaining = ver_remaining[1:].
lstrip()
220 if not ver_remaining:
224 raise SyntaxError(
'invalid constraint: %s' % ver_remaining)
227 return versions, ver_remaining
229 if remaining[0] !=
'(':
234 raise SyntaxError(
'unterminated parenthesis: %s' % remaining)
236 remaining = remaining[i + 1:].
lstrip()
250 versions = [(
'~=', v)]
253 if remaining[0] !=
';':
254 raise SyntaxError(
'invalid requirement: %s' % remaining)
255 remaining = remaining[1:].
lstrip()
257 mark_expr, remaining = parse_marker(remaining)
259 if remaining
and remaining[0] !=
'#':
260 raise SyntaxError(
'unexpected trailing data: %s' % remaining)
265 rs =
'%s %s' % (distname,
', '.join([
'%s %s' % con
for con
in versions]))
266 return Container(name=distname, extras=extras, constraints=versions,
267 marker=mark_expr, url=uri, requirement=rs)
271 """Find destinations for resources files"""
281 for base, suffix, dest
in rules:
283 for abs_base
in iglob(prefix):
285 for abs_path
in iglob(abs_glob):
292 destinations[resource_file] = rel_dest +
'/' + rel_path
297 if hasattr(sys,
'real_prefix'):
320 result = fsdecode(result)
324def proceed(prompt, allowed_chars, error_prompt=None, default=None):
329 if not s
and default:
333 if c
in allowed_chars:
336 p =
'%c: %s\n%s' % (c, error_prompt, prompt)
340def extract_by_key(d, keys):
349def read_exports(stream):
355 stream = StringIO(data)
358 result = jdata[
'extensions'][
'python.exports'][
'exports']
361 s =
'%s = %s' % (k, v)
362 entry = get_export_entry(s)
363 assert entry
is not None
381 stream = StringIO(data)
386 result[key] = entries = {}
388 s =
'%s = %s' % (name, value)
389 entry = get_export_entry(s)
390 assert entry
is not None
392 entries[name] = entry
396def write_exports(exports, stream):
415@contextlib.contextmanager
423@contextlib.contextmanager
433@contextlib.contextmanager
452 value = self.
func(obj)
457def convert_path(pathname):
458 """Return 'pathname' as a name that will work on the native filesystem.
460 The path is split on '/' and put back together again using the current
461 directory separator. Needed because filenames in the setup script are
462 always supplied in Unix style, and have to be converted to the local
463 convention before we can actually use them in the filesystem. Raises
464 ValueError on non-Unix-ish systems if 'pathname' either starts or
471 if pathname[0] ==
'/':
472 raise ValueError(
"path '%s' cannot be absolute" % pathname)
473 if pathname[-1] ==
'/':
474 raise ValueError(
"path '%s' cannot end with '/'" % pathname)
500 """Tell if the target is newer than the source.
502 Returns true if 'source' exists and is more recently modified than
503 'target', or if 'source' exists and 'target' doesn't.
505 Returns false if both exist and 'target' is the same age or younger
506 than 'source'. Raise PackagingFileError if 'source' does not exist.
508 Note that this test is not very accurate: files created in the same
509 second will have the same "age".
512 raise DistlibException(
"file '%r' does not exist" %
520 """Copy a file respecting dry-run and force flags.
528 msg =
'%s is a symlink' % outfile
530 msg =
'%s is a non-regular file' % outfile
532 raise ValueError(msg +
' which would be overwritten')
539 logger.info(
'Copying stream %s to %s', instream, outfile)
542 outstream = open(outfile,
'wb')
544 outstream =
codecs.open(outfile,
'w', encoding=encoding)
556 with open(path,
'wb')
as f:
571 mode = (
os.stat(f).st_mode | bits) & mask
575 set_executable_mode =
lambda s, f:
s.set_mode(0o555, 0o7777, f)
577 def ensure_dir(self, path):
589 def byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False):
590 dpath = cache_from_source(path,
not optimize)
591 logger.info(
'Byte-compiling %s to %s', path, dpath)
593 if force
or self.
newer(path, dpath):
598 diagpath = path[
len(prefix):]
600 if hashed_invalidation
and hasattr(py_compile,
'PycInvalidationMode'):
641 Commit recorded changes, turn off recording, return
661 assert flist == [
'__pycache__']
672 if dotted_path
is None:
694 return '<ExportEntry %s = %s:%s %s>' % (self.
name, self.
prefix,
711 \s*=\s*(?P<callable>(\w+)([:\.]\w+)*)
712 \s*(\[\s*(?P<flags>[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])?
715def get_export_entry(specification):
719 if '[' in specification
or ']' in specification:
720 raise DistlibException(
"Invalid specification "
721 "'%s'" % specification)
728 prefix, suffix = path,
None
731 raise DistlibException(
"Invalid specification "
732 "'%s'" % specification)
736 if '[' in specification
or ']' in specification:
737 raise DistlibException(
"Invalid specification "
738 "'%s'" % specification)
746def get_cache_base(suffix=None):
748 Return the default base location for distlib caches. If the directory does
749 not exist, it is created. Use the suffix provided for the base directory,
750 and default to '.distlib' if it isn't provided.
752 On Windows, if LOCALAPPDATA is defined in the environment, then it is
753 assumed to be a directory, and will be the parent directory of the result.
754 On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home
755 directory - using os.expanduser('~') - will be the parent directory of
758 The result is just the directory '.distlib' in the parent directory as
759 determined above, or with the name specified with ``suffix``.
773 logger.warning(
'Directory exists but is not writable: %s', result)
789 Convert an absolute path to a directory name for use in a cache.
791 The algorithm used is:
793 #. On Windows, any ``':'`` in the drive is replaced with ``'---'``.
794 #. Any occurrence of ``os.sep`` is replaced with ``'--'``.
795 #. ``'.cache'`` is appended.
801 return d + p +
'.cache'
811 username = password =
None
814 if ':' not in prefix:
819 username = unquote(username)
821 password = unquote(password)
822 return username, password, netloc
840PROJECT_NAME_AND_VERSION =
re.compile(
'([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-'
841 '([a-z0-9_.+-]+)',
re.I)
845def split_filename(filename, project_name=None):
847 Extract name, version, python version from a filename (no extension)
849 Return name, version, pyver or None
853 filename = unquote(filename).replace(
' ',
'-')
857 filename = filename[:
m.start()]
858 if project_name
and len(filename) >
len(project_name) + 1:
862 result = filename[:n], filename[n + 1:], pyver
871 r'\(\s*(?P<ver>[^\s)]+)\)$')
873def parse_name_and_version(p):
875 A utility method used to get name and version from a string.
877 From e.g. a Provides-Dist value.
879 :param p: A value in a form 'foo (1.0)'
880 :return: The name and version as a tuple.
884 raise DistlibException(
'Ill-formed name/version string: \'%s\'' % p)
886 return d[
'name'].strip().
lower(), d[
'ver']
888def get_extras(requested, available):
890 requested = set(requested
or [])
891 available = set(available
or [])
900 if unwanted
not in available:
902 if unwanted
in result:
905 if r
not in available:
923 logger.debug(
'Unexpected response for JSON request: %s', ct)
929 except Exception
as e:
933_external_data_base_url =
'https://www.red-dove.com/pypi/projects/'
935def get_project_data(name):
936 url =
'%s/%s/project.json' % (name[0].
upper(), name)
937 url = urljoin(_external_data_base_url, url)
942 url =
'%s/%s/package-%s.json' % (name[0].
upper(), name, version)
943 url = urljoin(_external_data_base_url, url)
949 A class implementing a cache for resources that need to live in the file system
950 e.g. shared libraries. This class was moved from resources to here because it
951 could be used by other modules, e.g. the wheel module.
956 Initialise an instance.
958 :param base: The base directory where the cache should be located.
964 if (
os.stat(base).st_mode & 0o77) != 0:
970 Converts a resource prefix to a directory name in the cache.
993 A very simple publish/subscribe system.
998 def add(self, event, subscriber, append=True):
1000 Add a subscriber for an event.
1002 :param event: The name of an event.
1003 :param subscriber: The subscriber to be added (and called when the
1004 event is published).
1005 :param append: Whether to append or prepend the subscriber to an
1006 existing subscriber list for the event.
1009 if event
not in subs:
1010 subs[event] = deque([subscriber])
1020 Remove a subscriber for an event.
1022 :param event: The name of an event.
1023 :param subscriber: The subscriber to be removed.
1026 if event
not in subs:
1027 raise ValueError(
'No subscribers: %r' % event)
1028 subs[event].
remove(subscriber)
1032 Return an iterator for the subscribers for an event.
1033 :param event: The event to return subscribers for.
1039 Publish a event and return a list of values returned by its
1042 :param event: The event to publish.
1043 :param args: The positional arguments to pass to the event's
1045 :param kwargs: The keyword arguments to pass to the event's
1056 logger.debug(
'publish %s: args = %s, kwargs = %s, result = %s',
1057 event, args, kwargs, result)
1076 for p
in set(self.
_preds.get(node, ())):
1078 for s
in set(self.
_succs.get(node, ())):
1081 for k, v
in list(self.
_preds.items()):
1084 for k, v
in list(self.
_succs.items()):
1090 self.
_preds.setdefault(succ, set()).
add(pred)
1091 self.
_succs.setdefault(pred, set()).
add(succ)
1096 preds = self.
_preds[succ]
1097 succs = self.
_succs[pred]
1099 raise ValueError(
'%r not a successor of anything' % succ)
1104 raise ValueError(
'%r not a successor of %r' % (succ, pred))
1112 raise ValueError(
'Unknown: %r' % final)
1131 preds = self.
_preds.get(step, ())
1148 index[node] = index_counter[0]
1149 lowlinks[node] = index_counter[0]
1150 index_counter[0] += 1
1155 successors = graph[node]
1158 for successor
in successors:
1159 if successor
not in lowlinks:
1162 lowlinks[node] = min(lowlinks[node],lowlinks[successor])
1163 elif successor
in stack:
1166 lowlinks[node] = min(lowlinks[node],index[successor])
1169 if lowlinks[node] == index[node]:
1170 connected_component = []
1175 if successor == node:
break
1176 component = tuple(connected_component)
1181 if node
not in lowlinks:
1188 result = [
'digraph G {']
1190 preds = self.
_preds[succ]
1196 return '\n'.join(result)
1202ARCHIVE_EXTENSIONS = (
'.tar.gz',
'.tar.bz2',
'.tar',
'.zip',
1203 '.tgz',
'.tbz',
'.whl')
1205def unarchive(archive_filename, dest_dir, format=None, check=True):
1212 raise ValueError(
'path outside destination: %r' % p)
1215 plen =
len(dest_dir)
1230 raise ValueError(
'Unknown format for %r' % archive_filename)
1233 archive =
ZipFile(archive_filename,
'r')
1259def zip_dir(directory):
1260 """zip a directory tree into a BytesIO object"""
1262 dlen =
len(directory)
1263 with ZipFile(result,
"w")
as zf:
1264 for root, dirs, files
in os.walk(directory):
1276UNITS = (
'',
'K',
'M',
'G',
'T',
'P')
1283 assert maxval
is None or maxval >= minval
1291 assert self.
min <= curval
1292 assert self.
max is None or curval <= self.
max
1309 if self.
max is not None:
1321 elif self.
max is None:
1324 v = 100.0 * (self.
cur - self.
min) / (self.
max - self.
min)
1325 result =
'%3d %%' % v
1329 if (duration <= 0)
and self.
max is None or self.
cur == self.
min:
1345 if self.
max is None:
1351 t = float(self.
max - self.
min)
1366 return '%d %sB/s' % (result, unit)
1377def iglob(path_glob):
1378 """Extended globbing function that supports ** and {opt1,opt2,opt3}."""
1380 msg =
"""invalid glob %r: recursive glob "**" must be used alone"""
1381 raise ValueError(msg % path_glob)
1383 msg =
"""invalid glob %r: mismatching set marker '{' or '}'"""
1384 raise ValueError(msg % path_glob)
1390 if len(rich_path_glob) > 1:
1391 assert len(rich_path_glob) == 3, rich_path_glob
1392 prefix, set, suffix = rich_path_glob
1394 for path
in _iglob(
''.join((prefix, item, suffix))):
1397 if '**' not in path_glob:
1410 for path, dir, files
in os.walk(prefix):
1416 from .compat
import (HTTPSHandler
as BaseHTTPSHandler, match_hostname,
1431 if getattr(self,
'_tunnel_host',
False):
1436 if hasattr(ssl,
'OP_NO_SSLv2'):
1444 if getattr(ssl,
'HAS_SNI',
False):
1445 kwargs[
'server_hostname'] = self.
host
1452 except CertificateError:
1465 This is called to create a connection instance. Normally you'd
1466 pass a connection class to do_open, but it doesn't actually check for
1467 a class, and just expects a callable. As long as we behave just as a
1468 constructor would have, we should be OK. If it ever changes so that
1469 we *must* pass a class, we'll create an UnsafeHTTPSConnection class
1470 which just sets check_domain to False in the class definition, and
1471 choose which one to pass to do_open.
1482 except URLError
as e:
1483 if 'certificate verify failed' in str(
e.reason):
1500 raise URLError(
'Unexpected HTTP request on what should be a secure '
1501 'connection: %s' % req)
1512 h, eh, x509 = self.get_host_info(host)
1525 h, eh, kwargs = self.get_host_info(host)
1528 kwargs[
'timeout'] = self.
timeout
1541 if timeout
is not None:
1543 scheme = urlparse(uri)[0]
1545 if scheme ==
'https':
1546 tcls = SafeTransport
1549 kwargs[
'transport'] = t =
tcls(timeout, use_datetime=use_datetime)
1562 kwargs[
'newline'] =
''
1565 kwargs[
'encoding'] =
'utf-8'
1566 return open(fn, mode, **kwargs)
1571 'delimiter': str(
','),
1572 'quotechar': str(
'"'),
1573 'lineterminator': str(
'\n')
1576 def __enter__(self):
1579 def __exit__(self, *exc_info):
1585 if 'stream' in kwargs:
1586 stream = kwargs[
'stream']
1630 value_converters[
'inc'] =
'inc_convert'
1639 result = type(o)([
convert(i)
for i
in o])
1658 args = tuple([
convert(o)
for o
in args])
1659 items = [(k,
convert(config[k]))
for k
in config
if valid_ident(k)]
1660 kwargs = dict(items)
1661 result = c(*args, **kwargs)
1668 result = self.
config[key]
1669 if isinstance(result, dict)
and '()' in result:
1674 """Default converter for the inc:// protocol."""
1677 with codecs.open(value,
'r', encoding=
'utf-8')
as f:
1684 Mixin for running subprocesses and capturing their output
1692 Read lines from a subprocess' output stream and either pass to a progress
1693 callable (if specified) or write progress information to sys.stderr.
1701 if progress
is not None:
1728def normalize_name(name):
1729 """Normalize a python package name a la PEP 503"""
1745 DEFAULT_REPOSITORY =
'https://upload.pypi.org/legacy/'
1746 DEFAULT_REALM =
'pypi'
1763 if 'distutils' in sections:
1765 index_servers =
config.get(
'distutils',
'index-servers')
1771 if 'pypi' in sections:
1774 for server
in _servers:
1775 result = {
'server': server}
1776 result[
'username'] =
config.get(server,
'username')
1781 (
'password',
None)):
1785 result[key] = default
1790 if (server ==
'pypi' and
1793 elif (result[
'server'] != repository
and
1794 result[
'repository'] != repository):
1796 elif 'server-login' in sections:
1798 server =
'server-login'
1800 repository =
config.get(server,
'repository')
1806 'repository': repository,
1821 with open(fn,
'w')
as f:
1826 Read the PyPI access configuration as supported by distutils.
1839 """Return a string that identifies the current platform. This is used mainly to
1840 distinguish platform-specific build directories and platform-specific built
1841 distributions. Typically includes the OS name and version and the
1842 architecture (as supplied by 'os.uname()'), although the exact information
1843 included depends on the OS; eg. on Linux, the kernel version isn't
1844 particularly important.
1846 Examples of returned values:
1851 Windows will return one of:
1852 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
1853 win32 (all others - specifically, sys.platform is returned)
1855 For other non-POSIX platforms, currently just returns 'sys.platform'.
1878 (osname, host, release, version, machine) =
os.uname()
1885 if osname[:5] ==
'linux':
1889 return "%s-%s" % (osname, machine)
1891 elif osname[:5] ==
'sunos':
1892 if release[0] >=
'5':
1894 release =
'%d.%s' % (int(release[0]) - 3, release[2:])
1898 bitness = {2147483647:
'32bit', 9223372036854775807:
'64bit'}
1901 elif osname[:3] ==
'aix':
1902 from _aix_support
import aix_platform
1904 elif osname[:6] ==
'cygwin':
1910 elif osname[:6] ==
'darwin':
1914 osname, release, machine)
1916 return '%s-%s-%s' % (osname, release, machine)
1921 'x64' :
'win-amd64',
1922 'arm' :
'win-arm32',
1930 if cross_compilation_target
not in _TARGET_TO_PLAT:
1932 return _TARGET_TO_PLAT[cross_compilation_target]
configure_custom(self, config)
__init__(self, fn, **kwargs)
prefix_to_dir(self, prefix)
configure_custom(self, config)
__init__(self, config, base=None)
publish(self, event, *args, **kwargs)
remove(self, event, subscriber)
add(self, event, subscriber, append=True)
get_subscribers(self, event)
__init__(self, name, prefix, suffix, flags)
ensure_removed(self, path)
set_mode(self, bits, mask, files)
copy_stream(self, instream, outfile, encoding=None)
write_binary_file(self, path, data)
__init__(self, dry_run=False)
newer(self, source, target)
write_text_file(self, path, data, encoding)
byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False)
record_as_written(self, path)
copy_file(self, infile, outfile, check=True)
_conn_maker(self, *args, **kwargs)
__init__(self, ca_certs, check_domain=True)
__init__(self, minval=0, maxval=100)
format_duration(self, duration)
__init__(self, fn=None, url=None)
update(self, username, password)
__init__(self, timeout, use_datetime=0)
make_connection(self, host)
remove_node(self, node, edges=False)
__init__(self, uri, **kwargs)
run_command(self, cmd, **kwargs)
__init__(self, verbose=False, progress=None)
reader(self, stream, context)
__init__(self, timeout, use_datetime=0)
make_connection(self, host)
__get__(self, obj, cls=None)
get_resources_dests(resources_root, rules)
socket_timeout(seconds=15)
parse_credentials(netloc)
proceed(prompt, allowed_chars, error_prompt=None, default=None)
get_package_data(name, version)
_csv_open(fn, mode, **kwargs)
resolve(module_name, dotted_path)
unarchive(archive_filename, dest_dir, format=None, check=True)