1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
import re
from .types import ensure_unicode
# below code mostly taken from pypi's mini_pkg_resources.py and webui.py
# on 13th Sep 2013 from http://bitbucket.org/pypa/pypi
legal_package_name = re.compile(r"^[a-z0-9\._-]+$", re.IGNORECASE)
safe_filenames = re.compile(r'.+?\.(exe|tar\.gz|bz2|rpm|deb|zip|tgz|egg|dmg|msi|whl)$', re.I)
safe_name_rex = re.compile('[^A-Za-z0-9]+')
def normalize_name(name):
"""Convert an arbitrary string to a standard distribution name
Any runs of non-alphanumeric/. characters are replaced with a single '-'.
"""
name = ensure_unicode(name)
return safe_name_rex.sub('-', name).lower()
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 safe_name_rex.sub('-', version)
def is_valid_archive_name(filename):
return safe_filenames.match(filename)
def validate_metadata(data):
# from https://bitbucket.org/pypa/pypi/src/1e31fd3cc7a72e4aa54a2bd79d50be5c8c0a3b1e/webui.py?at=default#cl-2124
''' Validate the contents of the metadata.
'''
if not data.get('name', ''):
raise ValueError('Missing required field "name"')
if not data.get('version', ''):
raise ValueError('Missing required field "version"')
if 'metadata_version' in data:
#metadata_version = data['metadata_version']
del data['metadata_version']
#else:
# metadata_version = '1.0' # default
# Ensure that package names follow a restricted set of characters.
# These characters are:
# * ASCII letters (``[a-zA-Z]``)
# * ASCII digits (``[0-9]``)
# * underscores (``_``)
# * hyphens (``-``)
# * periods (``.``)
# The reasoning for this restriction is codified in PEP426. For the
# time being this check is only validated against brand new packages
# and not pre-existing packages because of existing names that violate
# this policy.
if legal_package_name.search(data["name"]) is None:
raise ValueError("Invalid package name. Names must contain "
"only ASCII letters, digits, underscores, "
"hyphens, and periods")
if not data["name"][0].isalnum():
raise ValueError("Invalid package name. Names must start with "
"an ASCII letter or digit")
if not data["name"][-1].isalnum():
raise ValueError("Invalid package name. Names must end with "
"an ASCII letter or digit")
# Traditionally, package names are restricted only for
# technical reasons; / is not allowed because it may be
# possible to break path names for file and documentation
# uploads
if '/' in data['name']:
raise ValueError("Invalid package name")
# again, this is a restriction required by the implementation and not
# mentiond in documentation; ensure name and version are valid for URLs
if re.search('[<>%#"]', data['name'] + data['version']):
raise ValueError('Invalid package name or version (URL safety)')
# disabled some checks
# # check requires and obsoletes
# def validate_version_predicates(col, sequence):
# try:
# map(versionpredicate.VersionPredicate, sequence)
# except ValueError, message:
# raise ValueError, 'Bad "%s" syntax: %s'%(col, message)
# for col in ('requires', 'obsoletes'):
# if data.has_key(col) and data[col]:
# validate_version_predicates(col, data[col])
#
# # check provides
# if data.has_key('provides') and data['provides']:
# try:
# map(versionpredicate.check_provision, data['provides'])
# except ValueError, message:
# raise ValueError, 'Bad "provides" syntax: %s'%message
#
# # check PEP 345 fields
# if metadata_version == '1.2':
# self._validate_metadata_1_2(data)
#
# # check classifiers
# if data.has_key('classifiers'):
# d = {}
# for entry in self.store.get_classifiers():
# d[entry['classifier']] = 1
# for entry in data['classifiers']:
# if d.has_key(entry):
# continue
# raise ValueError, 'Invalid classifier "%s"'%entry
|