1+ import dataclasses
12import logging
23import typing
34from email .message import EmailMessage , Message
2223 Metadata = Message
2324
2425
26+ @dataclasses .dataclass (frozen = True , order = True , slots = True , repr = False , kw_only = True )
2527class Candidate :
26- def __init__ (
27- self ,
28- name : str ,
29- version : Version ,
30- url : str ,
31- extras : typing .Iterable [str ] | None = None ,
32- is_sdist : bool | None = None ,
33- build_tag : BuildTag = (),
34- metadata_url : str | None = None ,
35- ):
36- self .name = canonicalize_name (name )
37- self .version = version
38- self .url = url
39- self .extras = extras
40- self .is_sdist = is_sdist
41- self .build_tag = build_tag
42- self .metadata_url = metadata_url
43-
44- self ._metadata : Metadata | None = None
45- self ._dependencies : list [Requirement ] | None = None
28+ name : str
29+ version : Version
30+ url : str
31+ is_sdist : bool | None = dataclasses .field (default = None )
32+ extras : tuple [str , ...] = dataclasses .field (default = (), compare = False )
33+ build_tag : BuildTag = dataclasses .field (default = (), compare = False )
34+ has_metadata : bool = dataclasses .field (default = False , compare = False )
35+
36+ _metadata : Metadata | None = dataclasses .field (
37+ default = None , init = False , compare = False
38+ )
39+ _dependencies : list [Requirement ] | None = dataclasses .field (
40+ default = None , init = False , compare = False
41+ )
42+
43+ def __post_init__ (self ):
44+ # force normalized name
45+ object .__setattr__ (self , "name" , canonicalize_name (self .name ))
4646
4747 def __repr__ (self ) -> str :
4848 if not self .extras :
4949 return f"<{ self .name } =={ self .version } >"
5050 return f"<{ self .name } [{ ',' .join (self .extras )} ]=={ self .version } >"
5151
52+ @property
53+ def metadata_url (self ) -> str | None :
54+ """PEP 658: metadata is available at {url}.metadata"""
55+ if self .has_metadata :
56+ return self .url + ".metadata"
57+ return None
58+
5259 @property
5360 def metadata (self ) -> Metadata :
5461 if self ._metadata is None :
55- self ._metadata = get_metadata_for_wheel (self .url , self .metadata_url )
62+ if not self .has_metadata :
63+ raise ValueError (f"{ self .url } does not have metadata" )
64+ metadata = get_metadata_for_wheel (self .url , self .metadata_url )
65+ object .__setattr__ (self , "_metadata" , metadata )
66+ assert self ._metadata
5667 return self ._metadata
5768
5869 def _get_dependencies (self ) -> typing .Iterable [Requirement ]:
@@ -71,7 +82,9 @@ def _get_dependencies(self) -> typing.Iterable[Requirement]:
7182 @property
7283 def dependencies (self ) -> list [Requirement ]:
7384 if self ._dependencies is None :
74- self ._dependencies = list (self ._get_dependencies ())
85+ dependencies = list (self ._get_dependencies ())
86+ object .__setattr__ (self , "_dependencies" , dependencies )
87+ assert self ._dependencies
7588 return self ._dependencies
7689
7790 @property
0 commit comments