7373except ImportError :
7474 pass
7575
76+ # Import packaging.version.parse as parse_version for a compat shim with the
77+ # old parse_version that used to be defined in this file.
78+ from setuptools ._vendor .packaging .version import parse as parse_version
79+
80+ from setuptools ._vendor .packaging .version import (
81+ Version , InvalidVersion , Specifier ,
82+ )
83+
7684
7785_state_vars = {}
7886
@@ -1143,13 +1151,14 @@ def safe_name(name):
11431151
11441152
11451153def safe_version (version ):
1146- """Convert an arbitrary string to a standard version string
1147-
1148- Spaces become dots, and all other non-alphanumeric characters become
1149- dashes, with runs of multiple dashes condensed to a single dash.
11501154 """
1151- version = version .replace (' ' ,'.' )
1152- return re .sub ('[^A-Za-z0-9.]+' , '-' , version )
1155+ Convert an arbitrary string to a standard version string
1156+ """
1157+ try :
1158+ return str (Version (version )) # this will normalize the version
1159+ except InvalidVersion :
1160+ version = version .replace (' ' ,'.' )
1161+ return re .sub ('[^A-Za-z0-9.]+' , '-' , version )
11531162
11541163
11551164def safe_extra (extra ):
@@ -2067,7 +2076,7 @@ def yield_lines(strs):
20672076# Distribution or extra
20682077DISTRO = re .compile (r"\s*((\w|[-.])+)" ).match
20692078# ver. info
2070- VERSION = re .compile (r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)" ).match
2079+ VERSION = re .compile (r"\s*(<=?|>=?|===? |!=|~= )\s*((\w|[-.*_!+ ])+)" ).match
20712080# comma between items
20722081COMMA = re .compile (r"\s*," ).match
20732082OBRACKET = re .compile (r"\s*\[" ).match
@@ -2079,67 +2088,6 @@ def yield_lines(strs):
20792088 re .VERBOSE | re .IGNORECASE
20802089).match
20812090
2082- component_re = re .compile (r'(\d+ | [a-z]+ | \.| -)' , re .VERBOSE )
2083- replace = {'pre' :'c' , 'preview' :'c' ,'-' :'final-' ,'rc' :'c' ,'dev' :'@' }.get
2084-
2085- def _parse_version_parts (s ):
2086- for part in component_re .split (s ):
2087- part = replace (part , part )
2088- if not part or part == '.' :
2089- continue
2090- if part [:1 ] in '0123456789' :
2091- # pad for numeric comparison
2092- yield part .zfill (8 )
2093- else :
2094- yield '*' + part
2095-
2096- # ensure that alpha/beta/candidate are before final
2097- yield '*final'
2098-
2099- def parse_version (s ):
2100- """Convert a version string to a chronologically-sortable key
2101-
2102- This is a rough cross between distutils' StrictVersion and LooseVersion;
2103- if you give it versions that would work with StrictVersion, then it behaves
2104- the same; otherwise it acts like a slightly-smarter LooseVersion. It is
2105- *possible* to create pathological version coding schemes that will fool
2106- this parser, but they should be very rare in practice.
2107-
2108- The returned value will be a tuple of strings. Numeric portions of the
2109- version are padded to 8 digits so they will compare numerically, but
2110- without relying on how numbers compare relative to strings. Dots are
2111- dropped, but dashes are retained. Trailing zeros between alpha segments
2112- or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
2113- "2.4". Alphanumeric parts are lower-cased.
2114-
2115- The algorithm assumes that strings like "-" and any alpha string that
2116- alphabetically follows "final" represents a "patch level". So, "2.4-1"
2117- is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
2118- considered newer than "2.4-1", which in turn is newer than "2.4".
2119-
2120- Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
2121- come before "final" alphabetically) are assumed to be pre-release versions,
2122- so that the version "2.4" is considered newer than "2.4a1".
2123-
2124- Finally, to handle miscellaneous cases, the strings "pre", "preview", and
2125- "rc" are treated as if they were "c", i.e. as though they were release
2126- candidates, and therefore are not as new as a version string that does not
2127- contain them, and "dev" is replaced with an '@' so that it sorts lower than
2128- than any other pre-release tag.
2129- """
2130- parts = []
2131- for part in _parse_version_parts (s .lower ()):
2132- if part .startswith ('*' ):
2133- # remove '-' before a prerelease tag
2134- if part < '*final' :
2135- while parts and parts [- 1 ] == '*final-' :
2136- parts .pop ()
2137- # remove trailing zeros from each series of numeric parts
2138- while parts and parts [- 1 ]== '00000000' :
2139- parts .pop ()
2140- parts .append (part )
2141- return tuple (parts )
2142-
21432091
21442092class EntryPoint (object ):
21452093 """Object representing an advertised importable object"""
@@ -2292,7 +2240,7 @@ def from_location(cls, location, basename, metadata=None,**kw):
22922240 @property
22932241 def hashcmp (self ):
22942242 return (
2295- getattr ( self , ' parsed_version' , ()) ,
2243+ self . parsed_version ,
22962244 self .precedence ,
22972245 self .key ,
22982246 _remove_md5_fragment (self .location ),
@@ -2338,11 +2286,10 @@ def key(self):
23382286
23392287 @property
23402288 def parsed_version (self ):
2341- try :
2342- return self ._parsed_version
2343- except AttributeError :
2344- self ._parsed_version = pv = parse_version (self .version )
2345- return pv
2289+ if not hasattr (self , "_parsed_version" ):
2290+ self ._parsed_version = parse_version (self .version )
2291+
2292+ return self ._parsed_version
23462293
23472294 @property
23482295 def version (self ):
@@ -2447,7 +2394,12 @@ def from_filename(cls, filename, metadata=None, **kw):
24472394
24482395 def as_requirement (self ):
24492396 """Return a ``Requirement`` that matches this distribution exactly"""
2450- return Requirement .parse ('%s==%s' % (self .project_name , self .version ))
2397+ if isinstance (self .parsed_version , Version ):
2398+ spec = "%s==%s" % (self .project_name , self .parsed_version )
2399+ else :
2400+ spec = "%s===%s" % (self .project_name , self .parsed_version )
2401+
2402+ return Requirement .parse (spec )
24512403
24522404 def load_entry_point (self , group , name ):
24532405 """Return the `name` entry point of `group` or raise ImportError"""
@@ -2699,7 +2651,7 @@ def scan_list(ITEM, TERMINATOR, line, p, groups, item_name):
26992651
27002652 line , p , specs = scan_list (VERSION , LINE_END , line , p , (1 , 2 ),
27012653 "version spec" )
2702- specs = [(op , safe_version ( val ) ) for op , val in specs ]
2654+ specs = [(op , val ) for op , val in specs ]
27032655 yield Requirement (project_name , specs , extras )
27042656
27052657
@@ -2708,26 +2660,23 @@ def __init__(self, project_name, specs, extras):
27082660 """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
27092661 self .unsafe_name , project_name = project_name , safe_name (project_name )
27102662 self .project_name , self .key = project_name , project_name .lower ()
2711- index = [
2712- (parse_version (v ), state_machine [op ], op , v )
2713- for op , v in specs
2714- ]
2715- index .sort ()
2716- self .specs = [(op , ver ) for parsed , trans , op , ver in index ]
2717- self .index , self .extras = index , tuple (map (safe_extra , extras ))
2663+ self .specifier = Specifier (
2664+ "," .join (["" .join ([x , y ]) for x , y in specs ])
2665+ )
2666+ self .specs = specs
2667+ self .extras = tuple (map (safe_extra , extras ))
27182668 self .hashCmp = (
27192669 self .key ,
2720- tuple (( op , parsed ) for parsed , trans , op , ver in index ) ,
2670+ self . specifier ,
27212671 frozenset (self .extras ),
27222672 )
27232673 self .__hash = hash (self .hashCmp )
27242674
27252675 def __str__ (self ):
2726- specs = ',' .join (['' .join (s ) for s in self .specs ])
27272676 extras = ',' .join (self .extras )
27282677 if extras :
27292678 extras = '[%s]' % extras
2730- return '%s%s%s' % (self .project_name , extras , specs )
2679+ return '%s%s%s' % (self .project_name , extras , self . specifier )
27312680
27322681 def __eq__ (self , other ):
27332682 return (
@@ -2739,29 +2688,13 @@ def __contains__(self, item):
27392688 if isinstance (item , Distribution ):
27402689 if item .key != self .key :
27412690 return False
2742- # only get if we need it
2743- if self .index :
2744- item = item .parsed_version
2745- elif isinstance (item , string_types ):
2746- item = parse_version (item )
2747- last = None
2748- # -1, 0, 1
2749- compare = lambda a , b : (a > b ) - (a < b )
2750- for parsed , trans , op , ver in self .index :
2751- # Indexing: 0, 1, -1
2752- action = trans [compare (item , parsed )]
2753- if action == 'F' :
2754- return False
2755- elif action == 'T' :
2756- return True
2757- elif action == '+' :
2758- last = True
2759- elif action == '-' or last is None :
2760- last = False
2761- # no rules encountered
2762- if last is None :
2763- last = True
2764- return last
2691+
2692+ item = item .version
2693+
2694+ # Allow prereleases always in order to match the previous behavior of
2695+ # this method. In the future this should be smarter and follow PEP 440
2696+ # more accurately.
2697+ return self .specifier .contains (item , prereleases = True )
27652698
27662699 def __hash__ (self ):
27672700 return self .__hash
@@ -2777,16 +2710,6 @@ def parse(s):
27772710 raise ValueError ("Expected only one requirement" , s )
27782711 raise ValueError ("No requirements found" , s )
27792712
2780- state_machine = {
2781- # =><
2782- '<' : '--T' ,
2783- '<=' : 'T-T' ,
2784- '>' : 'F+F' ,
2785- '>=' : 'T+F' ,
2786- '==' : 'T..' ,
2787- '!=' : 'F++' ,
2788- }
2789-
27902713
27912714def _get_mro (cls ):
27922715 """Get an mro for a type or classic class"""
0 commit comments