forked from pypa/packaging
/
metadata.py
170 lines (153 loc) · 5.53 KB
/
metadata.py
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
from __future__ import annotations
import enum
from collections.abc import Iterable
from typing import Optional, Tuple
from . import ( # Alt name avoids shadowing.
requirements,
specifiers,
utils,
version as packaging_version,
)
# Type aliases.
_NameAndEmail = Tuple[Optional[str], str]
_LabelAndURL = Tuple[str, str]
@enum.unique
class DynamicField(enum.Enum):
"""
Field names for the `dynamic` field.
All values are lower-cased for easy comparison.
"""
# `Name`, `Version`, and `Metadata-Version` are invalid in `Dynamic`.
# 1.0
PLATFORM = "platform"
SUMMARY = "summary"
DESCRIPTION = "description"
KEYWORDS = "keywords"
HOME_PAGE = "home-page"
AUTHOR = "author"
AUTHOR_EMAIL = "author-email"
LICENSE = "license"
# 1.1
SUPPORTED_PLATFORM = "supported-platform"
DOWNLOAD_URL = "download-url"
CLASSIFIER = "classifier"
# 1.2
MAINTAINER = "maintainer"
MAINTAINER_EMAIL = "maintainer-email"
REQUIRES_DIST = "requires-dist"
REQUIRES_PYTHON = "requires-python"
REQUIRES_EXTERNAL = "requires-external"
PROJECT_URL = "project-url"
PROVIDES_DIST = "provides-dist"
OBSOLETES_DIST = "obsoletes-dist"
# 2.1
DESCRIPTION_CONTENT_TYPE = "description-content-type"
PROVIDES_EXTRA = "provides-extra"
class Metadata:
"""
A representation of core metadata.
"""
# A property named `display_name` exposes the value.
_display_name: str
# A property named `canonical_name` exposes the value.
_canonical_name: utils.NormalizedName
version: packaging_version.Version
platforms: list[str]
summary: str
description: str
keywords: list[str]
home_page: str
author: str
author_emails: list[_NameAndEmail]
license: str
supported_platforms: list[str]
download_url: str
classifiers: list[str]
maintainer: str
maintainer_emails: list[_NameAndEmail]
requires_dists: list[requirements.Requirement]
requires_python: specifiers.SpecifierSet
requires_externals: list[str]
project_urls: list[_LabelAndURL]
provides_dists: list[str]
obsoletes_dists: list[str]
description_content_type: str
provides_extras: list[utils.NormalizedName]
dynamic_fields: list[DynamicField]
def __init__(
self,
name: str,
version: packaging_version.Version,
*,
# 1.0
platforms: Iterable[str] | None = None,
summary: str | None = None,
description: str | None = None,
keywords: Iterable[str] | None = None,
home_page: str | None = None,
author: str | None = None,
author_emails: Iterable[_NameAndEmail] | None = None,
license: str | None = None,
# 1.1
supported_platforms: Iterable[str] | None = None,
download_url: str | None = None,
classifiers: Iterable[str] | None = None,
# 1.2
maintainer: str | None = None,
maintainer_emails: Iterable[_NameAndEmail] | None = None,
requires_dists: Iterable[requirements.Requirement] | None = None,
requires_python: specifiers.SpecifierSet | None = None,
requires_externals: Iterable[str] | None = None,
project_urls: Iterable[_LabelAndURL] | None = None,
provides_dists: Iterable[str] | None = None,
obsoletes_dists: Iterable[str] | None = None,
# 2.1
description_content_type: str | None = None,
provides_extras: Iterable[utils.NormalizedName] | None = None,
# 2.2
dynamic_fields: Iterable[DynamicField] | None = None,
) -> None:
"""
Set all attributes on the instance.
An argument of `None` will be converted to an appropriate, false-y value
(e.g. the empty string).
"""
self.display_name = name
self.version = version
self.platforms = list(platforms or [])
self.summary = summary or ""
self.description = description or ""
self.keywords = list(keywords or [])
self.home_page = home_page or ""
self.author = author or ""
self.author_emails = list(author_emails or [])
self.license = license or ""
self.supported_platforms = list(supported_platforms or [])
self.download_url = download_url or ""
self.classifiers = list(classifiers or [])
self.maintainer = maintainer or ""
self.maintainer_emails = list(maintainer_emails or [])
self.requires_dists = list(requires_dists or [])
self.requires_python = requires_python or specifiers.SpecifierSet()
self.requires_externals = list(requires_externals or [])
self.project_urls = list(project_urls or [])
self.provides_dists = list(provides_dists or [])
self.obsoletes_dists = list(obsoletes_dists or [])
self.description_content_type = description_content_type or ""
self.provides_extras = list(provides_extras or [])
self.dynamic_fields = list(dynamic_fields or [])
@property
def display_name(self) -> str:
return self._display_name
@display_name.setter
def display_name(self, value: str) -> None:
"""
Set the value for self.display_name and self.canonical_name.
"""
self._display_name = value
self._canonical_name = utils.canonicalize_name(value)
# Use functools.cached_property once Python 3.7 support is dropped.
# Value is set by self.display_name.setter to keep in sync with self.display_name.
@property
def canonical_name(self) -> utils.NormalizedName:
return self._canonical_name