OSDN Git Service

環境情報を削除
[stux/ultron.git] / venv / Lib / site-packages / pip / _vendor / distlib / metadata.py
diff --git a/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py b/venv/Lib/site-packages/pip/_vendor/distlib/metadata.py
deleted file mode 100644 (file)
index 75bfd68..0000000
+++ /dev/null
@@ -1,1068 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2012 The Python Software Foundation.
-# See LICENSE.txt and CONTRIBUTORS.txt.
-#
-"""Implementation of the Metadata for Python packages PEPs.
-
-Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental).
-"""
-from __future__ import unicode_literals
-
-import codecs
-from email import message_from_file
-import json
-import logging
-import re
-
-
-from . import DistlibException, __version__
-from .compat import StringIO, string_types, text_type
-from .markers import interpret
-from .util import extract_by_key, get_extras
-from .version import get_scheme, PEP440_VERSION_RE
-
-logger = logging.getLogger(__name__)
-
-
-class MetadataMissingError(DistlibException):
-    """A required metadata is missing"""
-
-
-class MetadataConflictError(DistlibException):
-    """Attempt to read or write metadata fields that are conflictual."""
-
-
-class MetadataUnrecognizedVersionError(DistlibException):
-    """Unknown metadata version number."""
-
-
-class MetadataInvalidError(DistlibException):
-    """A metadata value is invalid"""
-
-# public API of this module
-__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION']
-
-# Encoding used for the PKG-INFO files
-PKG_INFO_ENCODING = 'utf-8'
-
-# preferred version. Hopefully will be changed
-# to 1.2 once PEP 345 is supported everywhere
-PKG_INFO_PREFERRED_VERSION = '1.1'
-
-_LINE_PREFIX_1_2 = re.compile('\n       \|')
-_LINE_PREFIX_PRE_1_2 = re.compile('\n        ')
-_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
-               'Summary', 'Description',
-               'Keywords', 'Home-page', 'Author', 'Author-email',
-               'License')
-
-_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
-               'Supported-Platform', 'Summary', 'Description',
-               'Keywords', 'Home-page', 'Author', 'Author-email',
-               'License', 'Classifier', 'Download-URL', 'Obsoletes',
-               'Provides', 'Requires')
-
-_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier',
-                'Download-URL')
-
-_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
-               'Supported-Platform', 'Summary', 'Description',
-               'Keywords', 'Home-page', 'Author', 'Author-email',
-               'Maintainer', 'Maintainer-email', 'License',
-               'Classifier', 'Download-URL', 'Obsoletes-Dist',
-               'Project-URL', 'Provides-Dist', 'Requires-Dist',
-               'Requires-Python', 'Requires-External')
-
-_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python',
-                'Obsoletes-Dist', 'Requires-External', 'Maintainer',
-                'Maintainer-email', 'Project-URL')
-
-_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
-               'Supported-Platform', 'Summary', 'Description',
-               'Keywords', 'Home-page', 'Author', 'Author-email',
-               'Maintainer', 'Maintainer-email', 'License',
-               'Classifier', 'Download-URL', 'Obsoletes-Dist',
-               'Project-URL', 'Provides-Dist', 'Requires-Dist',
-               'Requires-Python', 'Requires-External', 'Private-Version',
-               'Obsoleted-By', 'Setup-Requires-Dist', 'Extension',
-               'Provides-Extra')
-
-_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By',
-                'Setup-Requires-Dist', 'Extension')
-
-_ALL_FIELDS = set()
-_ALL_FIELDS.update(_241_FIELDS)
-_ALL_FIELDS.update(_314_FIELDS)
-_ALL_FIELDS.update(_345_FIELDS)
-_ALL_FIELDS.update(_426_FIELDS)
-
-EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''')
-
-
-def _version2fieldlist(version):
-    if version == '1.0':
-        return _241_FIELDS
-    elif version == '1.1':
-        return _314_FIELDS
-    elif version == '1.2':
-        return _345_FIELDS
-    elif version == '2.0':
-        return _426_FIELDS
-    raise MetadataUnrecognizedVersionError(version)
-
-
-def _best_version(fields):
-    """Detect the best version depending on the fields used."""
-    def _has_marker(keys, markers):
-        for marker in markers:
-            if marker in keys:
-                return True
-        return False
-
-    keys = []
-    for key, value in fields.items():
-        if value in ([], 'UNKNOWN', None):
-            continue
-        keys.append(key)
-
-    possible_versions = ['1.0', '1.1', '1.2', '2.0']
-
-    # first let's try to see if a field is not part of one of the version
-    for key in keys:
-        if key not in _241_FIELDS and '1.0' in possible_versions:
-            possible_versions.remove('1.0')
-        if key not in _314_FIELDS and '1.1' in possible_versions:
-            possible_versions.remove('1.1')
-        if key not in _345_FIELDS and '1.2' in possible_versions:
-            possible_versions.remove('1.2')
-        if key not in _426_FIELDS and '2.0' in possible_versions:
-            possible_versions.remove('2.0')
-
-    # possible_version contains qualified versions
-    if len(possible_versions) == 1:
-        return possible_versions[0]   # found !
-    elif len(possible_versions) == 0:
-        raise MetadataConflictError('Unknown metadata set')
-
-    # let's see if one unique marker is found
-    is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS)
-    is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS)
-    is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS)
-    if int(is_1_1) + int(is_1_2) + int(is_2_0) > 1:
-        raise MetadataConflictError('You used incompatible 1.1/1.2/2.0 fields')
-
-    # we have the choice, 1.0, or 1.2, or 2.0
-    #   - 1.0 has a broken Summary field but works with all tools
-    #   - 1.1 is to avoid
-    #   - 1.2 fixes Summary but has little adoption
-    #   - 2.0 adds more features and is very new
-    if not is_1_1 and not is_1_2 and not is_2_0:
-        # we couldn't find any specific marker
-        if PKG_INFO_PREFERRED_VERSION in possible_versions:
-            return PKG_INFO_PREFERRED_VERSION
-    if is_1_1:
-        return '1.1'
-    if is_1_2:
-        return '1.2'
-
-    return '2.0'
-
-_ATTR2FIELD = {
-    'metadata_version': 'Metadata-Version',
-    'name': 'Name',
-    'version': 'Version',
-    'platform': 'Platform',
-    'supported_platform': 'Supported-Platform',
-    'summary': 'Summary',
-    'description': 'Description',
-    'keywords': 'Keywords',
-    'home_page': 'Home-page',
-    'author': 'Author',
-    'author_email': 'Author-email',
-    'maintainer': 'Maintainer',
-    'maintainer_email': 'Maintainer-email',
-    'license': 'License',
-    'classifier': 'Classifier',
-    'download_url': 'Download-URL',
-    'obsoletes_dist': 'Obsoletes-Dist',
-    'provides_dist': 'Provides-Dist',
-    'requires_dist': 'Requires-Dist',
-    'setup_requires_dist': 'Setup-Requires-Dist',
-    'requires_python': 'Requires-Python',
-    'requires_external': 'Requires-External',
-    'requires': 'Requires',
-    'provides': 'Provides',
-    'obsoletes': 'Obsoletes',
-    'project_url': 'Project-URL',
-    'private_version': 'Private-Version',
-    'obsoleted_by': 'Obsoleted-By',
-    'extension': 'Extension',
-    'provides_extra': 'Provides-Extra',
-}
-
-_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist')
-_VERSIONS_FIELDS = ('Requires-Python',)
-_VERSION_FIELDS = ('Version',)
-_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes',
-               'Requires', 'Provides', 'Obsoletes-Dist',
-               'Provides-Dist', 'Requires-Dist', 'Requires-External',
-               'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist',
-               'Provides-Extra', 'Extension')
-_LISTTUPLEFIELDS = ('Project-URL',)
-
-_ELEMENTSFIELD = ('Keywords',)
-
-_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description')
-
-_MISSING = object()
-
-_FILESAFE = re.compile('[^A-Za-z0-9.]+')
-
-
-def _get_name_and_version(name, version, for_filename=False):
-    """Return the distribution name with version.
-
-    If for_filename is true, return a filename-escaped form."""
-    if for_filename:
-        # For both name and version any runs of non-alphanumeric or '.'
-        # characters are replaced with a single '-'.  Additionally any
-        # spaces in the version string become '.'
-        name = _FILESAFE.sub('-', name)
-        version = _FILESAFE.sub('-', version.replace(' ', '.'))
-    return '%s-%s' % (name, version)
-
-
-class LegacyMetadata(object):
-    """The legacy metadata of a release.
-
-    Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can
-    instantiate the class with one of these arguments (or none):
-    - *path*, the path to a metadata file
-    - *fileobj* give a file-like object with metadata as content
-    - *mapping* is a dict-like object
-    - *scheme* is a version scheme name
-    """
-    # TODO document the mapping API and UNKNOWN default key
-
-    def __init__(self, path=None, fileobj=None, mapping=None,
-                 scheme='default'):
-        if [path, fileobj, mapping].count(None) < 2:
-            raise TypeError('path, fileobj and mapping are exclusive')
-        self._fields = {}
-        self.requires_files = []
-        self._dependencies = None
-        self.scheme = scheme
-        if path is not None:
-            self.read(path)
-        elif fileobj is not None:
-            self.read_file(fileobj)
-        elif mapping is not None:
-            self.update(mapping)
-            self.set_metadata_version()
-
-    def set_metadata_version(self):
-        self._fields['Metadata-Version'] = _best_version(self._fields)
-
-    def _write_field(self, fileobj, name, value):
-        fileobj.write('%s: %s\n' % (name, value))
-
-    def __getitem__(self, name):
-        return self.get(name)
-
-    def __setitem__(self, name, value):
-        return self.set(name, value)
-
-    def __delitem__(self, name):
-        field_name = self._convert_name(name)
-        try:
-            del self._fields[field_name]
-        except KeyError:
-            raise KeyError(name)
-
-    def __contains__(self, name):
-        return (name in self._fields or
-                self._convert_name(name) in self._fields)
-
-    def _convert_name(self, name):
-        if name in _ALL_FIELDS:
-            return name
-        name = name.replace('-', '_').lower()
-        return _ATTR2FIELD.get(name, name)
-
-    def _default_value(self, name):
-        if name in _LISTFIELDS or name in _ELEMENTSFIELD:
-            return []
-        return 'UNKNOWN'
-
-    def _remove_line_prefix(self, value):
-        if self.metadata_version in ('1.0', '1.1'):
-            return _LINE_PREFIX_PRE_1_2.sub('\n', value)
-        else:
-            return _LINE_PREFIX_1_2.sub('\n', value)
-
-    def __getattr__(self, name):
-        if name in _ATTR2FIELD:
-            return self[name]
-        raise AttributeError(name)
-
-    #
-    # Public API
-    #
-
-#    dependencies = property(_get_dependencies, _set_dependencies)
-
-    def get_fullname(self, filesafe=False):
-        """Return the distribution name with version.
-
-        If filesafe is true, return a filename-escaped form."""
-        return _get_name_and_version(self['Name'], self['Version'], filesafe)
-
-    def is_field(self, name):
-        """return True if name is a valid metadata key"""
-        name = self._convert_name(name)
-        return name in _ALL_FIELDS
-
-    def is_multi_field(self, name):
-        name = self._convert_name(name)
-        return name in _LISTFIELDS
-
-    def read(self, filepath):
-        """Read the metadata values from a file path."""
-        fp = codecs.open(filepath, 'r', encoding='utf-8')
-        try:
-            self.read_file(fp)
-        finally:
-            fp.close()
-
-    def read_file(self, fileob):
-        """Read the metadata values from a file object."""
-        msg = message_from_file(fileob)
-        self._fields['Metadata-Version'] = msg['metadata-version']
-
-        # When reading, get all the fields we can
-        for field in _ALL_FIELDS:
-            if field not in msg:
-                continue
-            if field in _LISTFIELDS:
-                # we can have multiple lines
-                values = msg.get_all(field)
-                if field in _LISTTUPLEFIELDS and values is not None:
-                    values = [tuple(value.split(',')) for value in values]
-                self.set(field, values)
-            else:
-                # single line
-                value = msg[field]
-                if value is not None and value != 'UNKNOWN':
-                    self.set(field, value)
-        self.set_metadata_version()
-
-    def write(self, filepath, skip_unknown=False):
-        """Write the metadata fields to filepath."""
-        fp = codecs.open(filepath, 'w', encoding='utf-8')
-        try:
-            self.write_file(fp, skip_unknown)
-        finally:
-            fp.close()
-
-    def write_file(self, fileobject, skip_unknown=False):
-        """Write the PKG-INFO format data to a file object."""
-        self.set_metadata_version()
-
-        for field in _version2fieldlist(self['Metadata-Version']):
-            values = self.get(field)
-            if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']):
-                continue
-            if field in _ELEMENTSFIELD:
-                self._write_field(fileobject, field, ','.join(values))
-                continue
-            if field not in _LISTFIELDS:
-                if field == 'Description':
-                    if self.metadata_version in ('1.0', '1.1'):
-                        values = values.replace('\n', '\n        ')
-                    else:
-                        values = values.replace('\n', '\n       |')
-                values = [values]
-
-            if field in _LISTTUPLEFIELDS:
-                values = [','.join(value) for value in values]
-
-            for value in values:
-                self._write_field(fileobject, field, value)
-
-    def update(self, other=None, **kwargs):
-        """Set metadata values from the given iterable `other` and kwargs.
-
-        Behavior is like `dict.update`: If `other` has a ``keys`` method,
-        they are looped over and ``self[key]`` is assigned ``other[key]``.
-        Else, ``other`` is an iterable of ``(key, value)`` iterables.
-
-        Keys that don't match a metadata field or that have an empty value are
-        dropped.
-        """
-        def _set(key, value):
-            if key in _ATTR2FIELD and value:
-                self.set(self._convert_name(key), value)
-
-        if not other:
-            # other is None or empty container
-            pass
-        elif hasattr(other, 'keys'):
-            for k in other.keys():
-                _set(k, other[k])
-        else:
-            for k, v in other:
-                _set(k, v)
-
-        if kwargs:
-            for k, v in kwargs.items():
-                _set(k, v)
-
-    def set(self, name, value):
-        """Control then set a metadata field."""
-        name = self._convert_name(name)
-
-        if ((name in _ELEMENTSFIELD or name == 'Platform') and
-            not isinstance(value, (list, tuple))):
-            if isinstance(value, string_types):
-                value = [v.strip() for v in value.split(',')]
-            else:
-                value = []
-        elif (name in _LISTFIELDS and
-              not isinstance(value, (list, tuple))):
-            if isinstance(value, string_types):
-                value = [value]
-            else:
-                value = []
-
-        if logger.isEnabledFor(logging.WARNING):
-            project_name = self['Name']
-
-            scheme = get_scheme(self.scheme)
-            if name in _PREDICATE_FIELDS and value is not None:
-                for v in value:
-                    # check that the values are valid
-                    if not scheme.is_valid_matcher(v.split(';')[0]):
-                        logger.warning(
-                            "'%s': '%s' is not valid (field '%s')",
-                            project_name, v, name)
-            # FIXME this rejects UNKNOWN, is that right?
-            elif name in _VERSIONS_FIELDS and value is not None:
-                if not scheme.is_valid_constraint_list(value):
-                    logger.warning("'%s': '%s' is not a valid version (field '%s')",
-                                   project_name, value, name)
-            elif name in _VERSION_FIELDS and value is not None:
-                if not scheme.is_valid_version(value):
-                    logger.warning("'%s': '%s' is not a valid version (field '%s')",
-                                   project_name, value, name)
-
-        if name in _UNICODEFIELDS:
-            if name == 'Description':
-                value = self._remove_line_prefix(value)
-
-        self._fields[name] = value
-
-    def get(self, name, default=_MISSING):
-        """Get a metadata field."""
-        name = self._convert_name(name)
-        if name not in self._fields:
-            if default is _MISSING:
-                default = self._default_value(name)
-            return default
-        if name in _UNICODEFIELDS:
-            value = self._fields[name]
-            return value
-        elif name in _LISTFIELDS:
-            value = self._fields[name]
-            if value is None:
-                return []
-            res = []
-            for val in value:
-                if name not in _LISTTUPLEFIELDS:
-                    res.append(val)
-                else:
-                    # That's for Project-URL
-                    res.append((val[0], val[1]))
-            return res
-
-        elif name in _ELEMENTSFIELD:
-            value = self._fields[name]
-            if isinstance(value, string_types):
-                return value.split(',')
-        return self._fields[name]
-
-    def check(self, strict=False):
-        """Check if the metadata is compliant. If strict is True then raise if
-        no Name or Version are provided"""
-        self.set_metadata_version()
-
-        # XXX should check the versions (if the file was loaded)
-        missing, warnings = [], []
-
-        for attr in ('Name', 'Version'):  # required by PEP 345
-            if attr not in self:
-                missing.append(attr)
-
-        if strict and missing != []:
-            msg = 'missing required metadata: %s' % ', '.join(missing)
-            raise MetadataMissingError(msg)
-
-        for attr in ('Home-page', 'Author'):
-            if attr not in self:
-                missing.append(attr)
-
-        # checking metadata 1.2 (XXX needs to check 1.1, 1.0)
-        if self['Metadata-Version'] != '1.2':
-            return missing, warnings
-
-        scheme = get_scheme(self.scheme)
-
-        def are_valid_constraints(value):
-            for v in value:
-                if not scheme.is_valid_matcher(v.split(';')[0]):
-                    return False
-            return True
-
-        for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints),
-                                   (_VERSIONS_FIELDS,
-                                    scheme.is_valid_constraint_list),
-                                   (_VERSION_FIELDS,
-                                    scheme.is_valid_version)):
-            for field in fields:
-                value = self.get(field, None)
-                if value is not None and not controller(value):
-                    warnings.append("Wrong value for '%s': %s" % (field, value))
-
-        return missing, warnings
-
-    def todict(self, skip_missing=False):
-        """Return fields as a dict.
-
-        Field names will be converted to use the underscore-lowercase style
-        instead of hyphen-mixed case (i.e. home_page instead of Home-page).
-        """
-        self.set_metadata_version()
-
-        mapping_1_0 = (
-            ('metadata_version', 'Metadata-Version'),
-            ('name', 'Name'),
-            ('version', 'Version'),
-            ('summary', 'Summary'),
-            ('home_page', 'Home-page'),
-            ('author', 'Author'),
-            ('author_email', 'Author-email'),
-            ('license', 'License'),
-            ('description', 'Description'),
-            ('keywords', 'Keywords'),
-            ('platform', 'Platform'),
-            ('classifiers', 'Classifier'),
-            ('download_url', 'Download-URL'),
-        )
-
-        data = {}
-        for key, field_name in mapping_1_0:
-            if not skip_missing or field_name in self._fields:
-                data[key] = self[field_name]
-
-        if self['Metadata-Version'] == '1.2':
-            mapping_1_2 = (
-                ('requires_dist', 'Requires-Dist'),
-                ('requires_python', 'Requires-Python'),
-                ('requires_external', 'Requires-External'),
-                ('provides_dist', 'Provides-Dist'),
-                ('obsoletes_dist', 'Obsoletes-Dist'),
-                ('project_url', 'Project-URL'),
-                ('maintainer', 'Maintainer'),
-                ('maintainer_email', 'Maintainer-email'),
-            )
-            for key, field_name in mapping_1_2:
-                if not skip_missing or field_name in self._fields:
-                    if key != 'project_url':
-                        data[key] = self[field_name]
-                    else:
-                        data[key] = [','.join(u) for u in self[field_name]]
-
-        elif self['Metadata-Version'] == '1.1':
-            mapping_1_1 = (
-                ('provides', 'Provides'),
-                ('requires', 'Requires'),
-                ('obsoletes', 'Obsoletes'),
-            )
-            for key, field_name in mapping_1_1:
-                if not skip_missing or field_name in self._fields:
-                    data[key] = self[field_name]
-
-        return data
-
-    def add_requirements(self, requirements):
-        if self['Metadata-Version'] == '1.1':
-            # we can't have 1.1 metadata *and* Setuptools requires
-            for field in ('Obsoletes', 'Requires', 'Provides'):
-                if field in self:
-                    del self[field]
-        self['Requires-Dist'] += requirements
-
-    # Mapping API
-    # TODO could add iter* variants
-
-    def keys(self):
-        return list(_version2fieldlist(self['Metadata-Version']))
-
-    def __iter__(self):
-        for key in self.keys():
-            yield key
-
-    def values(self):
-        return [self[key] for key in self.keys()]
-
-    def items(self):
-        return [(key, self[key]) for key in self.keys()]
-
-    def __repr__(self):
-        return '<%s %s %s>' % (self.__class__.__name__, self.name,
-                               self.version)
-
-
-METADATA_FILENAME = 'pydist.json'
-WHEEL_METADATA_FILENAME = 'metadata.json'
-
-
-class Metadata(object):
-    """
-    The metadata of a release. This implementation uses 2.0 (JSON)
-    metadata where possible. If not possible, it wraps a LegacyMetadata
-    instance which handles the key-value metadata format.
-    """
-
-    METADATA_VERSION_MATCHER = re.compile('^\d+(\.\d+)*$')
-
-    NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I)
-
-    VERSION_MATCHER = PEP440_VERSION_RE
-
-    SUMMARY_MATCHER = re.compile('.{1,2047}')
-
-    METADATA_VERSION = '2.0'
-
-    GENERATOR = 'distlib (%s)' % __version__
-
-    MANDATORY_KEYS = {
-        'name': (),
-        'version': (),
-        'summary': ('legacy',),
-    }
-
-    INDEX_KEYS = ('name version license summary description author '
-                  'author_email keywords platform home_page classifiers '
-                  'download_url')
-
-    DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires '
-                       'dev_requires provides meta_requires obsoleted_by '
-                       'supports_environments')
-
-    SYNTAX_VALIDATORS = {
-        'metadata_version': (METADATA_VERSION_MATCHER, ()),
-        'name': (NAME_MATCHER, ('legacy',)),
-        'version': (VERSION_MATCHER, ('legacy',)),
-        'summary': (SUMMARY_MATCHER, ('legacy',)),
-    }
-
-    __slots__ = ('_legacy', '_data', 'scheme')
-
-    def __init__(self, path=None, fileobj=None, mapping=None,
-                 scheme='default'):
-        if [path, fileobj, mapping].count(None) < 2:
-            raise TypeError('path, fileobj and mapping are exclusive')
-        self._legacy = None
-        self._data = None
-        self.scheme = scheme
-        #import pdb; pdb.set_trace()
-        if mapping is not None:
-            try:
-                self._validate_mapping(mapping, scheme)
-                self._data = mapping
-            except MetadataUnrecognizedVersionError:
-                self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme)
-                self.validate()
-        else:
-            data = None
-            if path:
-                with open(path, 'rb') as f:
-                    data = f.read()
-            elif fileobj:
-                data = fileobj.read()
-            if data is None:
-                # Initialised with no args - to be added
-                self._data = {
-                    'metadata_version': self.METADATA_VERSION,
-                    'generator': self.GENERATOR,
-                }
-            else:
-                if not isinstance(data, text_type):
-                    data = data.decode('utf-8')
-                try:
-                    self._data = json.loads(data)
-                    self._validate_mapping(self._data, scheme)
-                except ValueError:
-                    # Note: MetadataUnrecognizedVersionError does not
-                    # inherit from ValueError (it's a DistlibException,
-                    # which should not inherit from ValueError).
-                    # The ValueError comes from the json.load - if that
-                    # succeeds and we get a validation error, we want
-                    # that to propagate
-                    self._legacy = LegacyMetadata(fileobj=StringIO(data),
-                                                  scheme=scheme)
-                    self.validate()
-
-    common_keys = set(('name', 'version', 'license', 'keywords', 'summary'))
-
-    none_list = (None, list)
-    none_dict = (None, dict)
-
-    mapped_keys = {
-        'run_requires': ('Requires-Dist', list),
-        'build_requires': ('Setup-Requires-Dist', list),
-        'dev_requires': none_list,
-        'test_requires': none_list,
-        'meta_requires': none_list,
-        'extras': ('Provides-Extra', list),
-        'modules': none_list,
-        'namespaces': none_list,
-        'exports': none_dict,
-        'commands': none_dict,
-        'classifiers': ('Classifier', list),
-        'source_url': ('Download-URL', None),
-        'metadata_version': ('Metadata-Version', None),
-    }
-
-    del none_list, none_dict
-
-    def __getattribute__(self, key):
-        common = object.__getattribute__(self, 'common_keys')
-        mapped = object.__getattribute__(self, 'mapped_keys')
-        if key in mapped:
-            lk, maker = mapped[key]
-            if self._legacy:
-                if lk is None:
-                    result = None if maker is None else maker()
-                else:
-                    result = self._legacy.get(lk)
-            else:
-                value = None if maker is None else maker()
-                if key not in ('commands', 'exports', 'modules', 'namespaces',
-                               'classifiers'):
-                    result = self._data.get(key, value)
-                else:
-                    # special cases for PEP 459
-                    sentinel = object()
-                    result = sentinel
-                    d = self._data.get('extensions')
-                    if d:
-                        if key == 'commands':
-                            result = d.get('python.commands', value)
-                        elif key == 'classifiers':
-                            d = d.get('python.details')
-                            if d:
-                                result = d.get(key, value)
-                        else:
-                            d = d.get('python.exports')
-                            if not d:
-                                d = self._data.get('python.exports')
-                            if d:
-                                result = d.get(key, value)
-                    if result is sentinel:
-                        result = value
-        elif key not in common:
-            result = object.__getattribute__(self, key)
-        elif self._legacy:
-            result = self._legacy.get(key)
-        else:
-            result = self._data.get(key)
-        return result
-
-    def _validate_value(self, key, value, scheme=None):
-        if key in self.SYNTAX_VALIDATORS:
-            pattern, exclusions = self.SYNTAX_VALIDATORS[key]
-            if (scheme or self.scheme) not in exclusions:
-                m = pattern.match(value)
-                if not m:
-                    raise MetadataInvalidError("'%s' is an invalid value for "
-                                               "the '%s' property" % (value,
-                                                                    key))
-
-    def __setattr__(self, key, value):
-        self._validate_value(key, value)
-        common = object.__getattribute__(self, 'common_keys')
-        mapped = object.__getattribute__(self, 'mapped_keys')
-        if key in mapped:
-            lk, _ = mapped[key]
-            if self._legacy:
-                if lk is None:
-                    raise NotImplementedError
-                self._legacy[lk] = value
-            elif key not in ('commands', 'exports', 'modules', 'namespaces',
-                             'classifiers'):
-                self._data[key] = value
-            else:
-                # special cases for PEP 459
-                d = self._data.setdefault('extensions', {})
-                if key == 'commands':
-                    d['python.commands'] = value
-                elif key == 'classifiers':
-                    d = d.setdefault('python.details', {})
-                    d[key] = value
-                else:
-                    d = d.setdefault('python.exports', {})
-                    d[key] = value
-        elif key not in common:
-            object.__setattr__(self, key, value)
-        else:
-            if key == 'keywords':
-                if isinstance(value, string_types):
-                    value = value.strip()
-                    if value:
-                        value = value.split()
-                    else:
-                        value = []
-            if self._legacy:
-                self._legacy[key] = value
-            else:
-                self._data[key] = value
-
-    @property
-    def name_and_version(self):
-        return _get_name_and_version(self.name, self.version, True)
-
-    @property
-    def provides(self):
-        if self._legacy:
-            result = self._legacy['Provides-Dist']
-        else:
-            result = self._data.setdefault('provides', [])
-        s = '%s (%s)' % (self.name, self.version)
-        if s not in result:
-            result.append(s)
-        return result
-
-    @provides.setter
-    def provides(self, value):
-        if self._legacy:
-            self._legacy['Provides-Dist'] = value
-        else:
-            self._data['provides'] = value
-
-    def get_requirements(self, reqts, extras=None, env=None):
-        """
-        Base method to get dependencies, given a set of extras
-        to satisfy and an optional environment context.
-        :param reqts: A list of sometimes-wanted dependencies,
-                      perhaps dependent on extras and environment.
-        :param extras: A list of optional components being requested.
-        :param env: An optional environment for marker evaluation.
-        """
-        if self._legacy:
-            result = reqts
-        else:
-            result = []
-            extras = get_extras(extras or [], self.extras)
-            for d in reqts:
-                if 'extra' not in d and 'environment' not in d:
-                    # unconditional
-                    include = True
-                else:
-                    if 'extra' not in d:
-                        # Not extra-dependent - only environment-dependent
-                        include = True
-                    else:
-                        include = d.get('extra') in extras
-                    if include:
-                        # Not excluded because of extras, check environment
-                        marker = d.get('environment')
-                        if marker:
-                            include = interpret(marker, env)
-                if include:
-                    result.extend(d['requires'])
-            for key in ('build', 'dev', 'test'):
-                e = ':%s:' % key
-                if e in extras:
-                    extras.remove(e)
-                    # A recursive call, but it should terminate since 'test'
-                    # has been removed from the extras
-                    reqts = self._data.get('%s_requires' % key, [])
-                    result.extend(self.get_requirements(reqts, extras=extras,
-                                                        env=env))
-        return result
-
-    @property
-    def dictionary(self):
-        if self._legacy:
-            return self._from_legacy()
-        return self._data
-
-    @property
-    def dependencies(self):
-        if self._legacy:
-            raise NotImplementedError
-        else:
-            return extract_by_key(self._data, self.DEPENDENCY_KEYS)
-
-    @dependencies.setter
-    def dependencies(self, value):
-        if self._legacy:
-            raise NotImplementedError
-        else:
-            self._data.update(value)
-
-    def _validate_mapping(self, mapping, scheme):
-        if mapping.get('metadata_version') != self.METADATA_VERSION:
-            raise MetadataUnrecognizedVersionError()
-        missing = []
-        for key, exclusions in self.MANDATORY_KEYS.items():
-            if key not in mapping:
-                if scheme not in exclusions:
-                    missing.append(key)
-        if missing:
-            msg = 'Missing metadata items: %s' % ', '.join(missing)
-            raise MetadataMissingError(msg)
-        for k, v in mapping.items():
-            self._validate_value(k, v, scheme)
-
-    def validate(self):
-        if self._legacy:
-            missing, warnings = self._legacy.check(True)
-            if missing or warnings:
-                logger.warning('Metadata: missing: %s, warnings: %s',
-                               missing, warnings)
-        else:
-            self._validate_mapping(self._data, self.scheme)
-
-    def todict(self):
-        if self._legacy:
-            return self._legacy.todict(True)
-        else:
-            result = extract_by_key(self._data, self.INDEX_KEYS)
-            return result
-
-    def _from_legacy(self):
-        assert self._legacy and not self._data
-        result = {
-            'metadata_version': self.METADATA_VERSION,
-            'generator': self.GENERATOR,
-        }
-        lmd = self._legacy.todict(True)     # skip missing ones
-        for k in ('name', 'version', 'license', 'summary', 'description',
-                  'classifier'):
-            if k in lmd:
-                if k == 'classifier':
-                    nk = 'classifiers'
-                else:
-                    nk = k
-                result[nk] = lmd[k]
-        kw = lmd.get('Keywords', [])
-        if kw == ['']:
-            kw = []
-        result['keywords'] = kw
-        keys = (('requires_dist', 'run_requires'),
-                ('setup_requires_dist', 'build_requires'))
-        for ok, nk in keys:
-            if ok in lmd and lmd[ok]:
-                result[nk] = [{'requires': lmd[ok]}]
-        result['provides'] = self.provides
-        author = {}
-        maintainer = {}
-        return result
-
-    LEGACY_MAPPING = {
-        'name': 'Name',
-        'version': 'Version',
-        'license': 'License',
-        'summary': 'Summary',
-        'description': 'Description',
-        'classifiers': 'Classifier',
-    }
-
-    def _to_legacy(self):
-        def process_entries(entries):
-            reqts = set()
-            for e in entries:
-                extra = e.get('extra')
-                env = e.get('environment')
-                rlist = e['requires']
-                for r in rlist:
-                    if not env and not extra:
-                        reqts.add(r)
-                    else:
-                        marker = ''
-                        if extra:
-                            marker = 'extra == "%s"' % extra
-                        if env:
-                            if marker:
-                                marker = '(%s) and %s' % (env, marker)
-                            else:
-                                marker = env
-                        reqts.add(';'.join((r, marker)))
-            return reqts
-
-        assert self._data and not self._legacy
-        result = LegacyMetadata()
-        nmd = self._data
-        for nk, ok in self.LEGACY_MAPPING.items():
-            if nk in nmd:
-                result[ok] = nmd[nk]
-        r1 = process_entries(self.run_requires + self.meta_requires)
-        r2 = process_entries(self.build_requires + self.dev_requires)
-        if self.extras:
-            result['Provides-Extra'] = sorted(self.extras)
-        result['Requires-Dist'] = sorted(r1)
-        result['Setup-Requires-Dist'] = sorted(r2)
-        # TODO: other fields such as contacts
-        return result
-
-    def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True):
-        if [path, fileobj].count(None) != 1:
-            raise ValueError('Exactly one of path and fileobj is needed')
-        self.validate()
-        if legacy:
-            if self._legacy:
-                legacy_md = self._legacy
-            else:
-                legacy_md = self._to_legacy()
-            if path:
-                legacy_md.write(path, skip_unknown=skip_unknown)
-            else:
-                legacy_md.write_file(fileobj, skip_unknown=skip_unknown)
-        else:
-            if self._legacy:
-                d = self._from_legacy()
-            else:
-                d = self._data
-            if fileobj:
-                json.dump(d, fileobj, ensure_ascii=True, indent=2,
-                          sort_keys=True)
-            else:
-                with codecs.open(path, 'w', 'utf-8') as f:
-                    json.dump(d, f, ensure_ascii=True, indent=2,
-                              sort_keys=True)
-
-    def add_requirements(self, requirements):
-        if self._legacy:
-            self._legacy.add_requirements(requirements)
-        else:
-            run_requires = self._data.setdefault('run_requires', [])
-            always = None
-            for entry in run_requires:
-                if 'environment' not in entry and 'extra' not in entry:
-                    always = entry
-                    break
-            if always is None:
-                always = { 'requires': requirements }
-                run_requires.insert(0, always)
-            else:
-                rset = set(always['requires']) | set(requirements)
-                always['requires'] = sorted(rset)
-
-    def __repr__(self):
-        name = self.name or '(no name)'
-        version = self.version or 'no version'
-        return '<%s %s %s (%s)>' % (self.__class__.__name__,
-                                    self.metadata_version, name, version)