73
73
except ImportError :
74
74
pass
75
75
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
+
76
84
77
85
_state_vars = {}
78
86
@@ -1143,13 +1151,14 @@ def safe_name(name):
1143
1151
1144
1152
1145
1153
def 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.
1150
1154
"""
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 )
1153
1162
1154
1163
1155
1164
def safe_extra (extra ):
@@ -2067,7 +2076,7 @@ def yield_lines(strs):
2067
2076
# Distribution or extra
2068
2077
DISTRO = re .compile (r"\s*((\w|[-.])+)" ).match
2069
2078
# ver. info
2070
- VERSION = re .compile (r"\s*(<=?|>=?|==|!=)\s*((\w|[-.])+)" ).match
2079
+ VERSION = re .compile (r"\s*(<=?|>=?|===? |!=|~= )\s*((\w|[-.*_!+ ])+)" ).match
2071
2080
# comma between items
2072
2081
COMMA = re .compile (r"\s*," ).match
2073
2082
OBRACKET = re .compile (r"\s*\[" ).match
@@ -2079,67 +2088,6 @@ def yield_lines(strs):
2079
2088
re .VERBOSE | re .IGNORECASE
2080
2089
).match
2081
2090
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
-
2143
2091
2144
2092
class EntryPoint (object ):
2145
2093
"""Object representing an advertised importable object"""
@@ -2292,7 +2240,7 @@ def from_location(cls, location, basename, metadata=None,**kw):
2292
2240
@property
2293
2241
def hashcmp (self ):
2294
2242
return (
2295
- getattr ( self , ' parsed_version' , ()) ,
2243
+ self . parsed_version ,
2296
2244
self .precedence ,
2297
2245
self .key ,
2298
2246
_remove_md5_fragment (self .location ),
@@ -2338,11 +2286,10 @@ def key(self):
2338
2286
2339
2287
@property
2340
2288
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
2346
2293
2347
2294
@property
2348
2295
def version (self ):
@@ -2447,7 +2394,12 @@ def from_filename(cls, filename, metadata=None, **kw):
2447
2394
2448
2395
def as_requirement (self ):
2449
2396
"""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 )
2451
2403
2452
2404
def load_entry_point (self , group , name ):
2453
2405
"""Return the `name` entry point of `group` or raise ImportError"""
@@ -2699,7 +2651,7 @@ def scan_list(ITEM, TERMINATOR, line, p, groups, item_name):
2699
2651
2700
2652
line , p , specs = scan_list (VERSION , LINE_END , line , p , (1 , 2 ),
2701
2653
"version spec" )
2702
- specs = [(op , safe_version ( val ) ) for op , val in specs ]
2654
+ specs = [(op , val ) for op , val in specs ]
2703
2655
yield Requirement (project_name , specs , extras )
2704
2656
2705
2657
@@ -2708,26 +2660,23 @@ def __init__(self, project_name, specs, extras):
2708
2660
"""DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
2709
2661
self .unsafe_name , project_name = project_name , safe_name (project_name )
2710
2662
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 ))
2718
2668
self .hashCmp = (
2719
2669
self .key ,
2720
- tuple (( op , parsed ) for parsed , trans , op , ver in index ) ,
2670
+ self . specifier ,
2721
2671
frozenset (self .extras ),
2722
2672
)
2723
2673
self .__hash = hash (self .hashCmp )
2724
2674
2725
2675
def __str__ (self ):
2726
- specs = ',' .join (['' .join (s ) for s in self .specs ])
2727
2676
extras = ',' .join (self .extras )
2728
2677
if extras :
2729
2678
extras = '[%s]' % extras
2730
- return '%s%s%s' % (self .project_name , extras , specs )
2679
+ return '%s%s%s' % (self .project_name , extras , self . specifier )
2731
2680
2732
2681
def __eq__ (self , other ):
2733
2682
return (
@@ -2739,29 +2688,13 @@ def __contains__(self, item):
2739
2688
if isinstance (item , Distribution ):
2740
2689
if item .key != self .key :
2741
2690
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 )
2765
2698
2766
2699
def __hash__ (self ):
2767
2700
return self .__hash
@@ -2777,16 +2710,6 @@ def parse(s):
2777
2710
raise ValueError ("Expected only one requirement" , s )
2778
2711
raise ValueError ("No requirements found" , s )
2779
2712
2780
- state_machine = {
2781
- # =><
2782
- '<' : '--T' ,
2783
- '<=' : 'T-T' ,
2784
- '>' : 'F+F' ,
2785
- '>=' : 'T+F' ,
2786
- '==' : 'T..' ,
2787
- '!=' : 'F++' ,
2788
- }
2789
-
2790
2713
2791
2714
def _get_mro (cls ):
2792
2715
"""Get an mro for a type or classic class"""
0 commit comments