Skip to content

Commit 6472428

Browse files
committed
Migrate straightforward remaining ProjectMetadata properties
1 parent 904b191 commit 6472428

File tree

1 file changed

+41
-50
lines changed
  • backend/src/hatchling/metadata

1 file changed

+41
-50
lines changed

backend/src/hatchling/metadata/core.py

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ def __init__(
4848
self.plugin_manager = plugin_manager
4949
self._config = config
5050

51-
self._core_raw_metadata: dict[str, Any] | None = None
52-
self._dynamic: list[str] | None = None
53-
self._name: str | None = None
5451
self._version: str | None = None
5552
self._project_file: str | None = None
5653

@@ -70,68 +67,62 @@ def context(self) -> Context:
7067

7168
return Context(self.root)
7269

73-
@property
70+
@cached_property
7471
def core_raw_metadata(self) -> dict[str, Any]:
75-
if self._core_raw_metadata is None:
76-
if 'project' not in self.config:
77-
message = 'Missing `project` metadata table in configuration'
78-
raise ValueError(message)
79-
80-
core_raw_metadata = self.config['project']
81-
if not isinstance(core_raw_metadata, dict):
82-
message = 'The `project` configuration must be a table'
83-
raise TypeError(message)
72+
if 'project' not in self.config:
73+
message = 'Missing `project` metadata table in configuration'
74+
raise ValueError(message)
8475

85-
core_raw_metadata = deepcopy(core_raw_metadata)
86-
pkg_info = os.path.join(self.root, 'PKG-INFO')
87-
if os.path.isfile(pkg_info):
88-
from hatchling.metadata.spec import PROJECT_CORE_METADATA_FIELDS, project_metadata_from_core_metadata
76+
core_raw_metadata = self.config['project']
77+
if not isinstance(core_raw_metadata, dict):
78+
message = 'The `project` configuration must be a table'
79+
raise TypeError(message)
8980

90-
with open(pkg_info, encoding='utf-8') as f:
91-
pkg_info_contents = f.read()
81+
core_raw_metadata = deepcopy(core_raw_metadata)
82+
pkg_info = os.path.join(self.root, 'PKG-INFO')
83+
if os.path.isfile(pkg_info):
84+
from hatchling.metadata.spec import PROJECT_CORE_METADATA_FIELDS, project_metadata_from_core_metadata
9285

93-
base_metadata = project_metadata_from_core_metadata(pkg_info_contents)
94-
defined_dynamic = core_raw_metadata.get('dynamic', [])
95-
for field in list(defined_dynamic):
96-
if field in PROJECT_CORE_METADATA_FIELDS and field in base_metadata:
97-
core_raw_metadata[field] = base_metadata[field]
98-
defined_dynamic.remove(field)
86+
with open(pkg_info, encoding='utf-8') as f:
87+
pkg_info_contents = f.read()
9988

100-
self._core_raw_metadata = core_raw_metadata
89+
base_metadata = project_metadata_from_core_metadata(pkg_info_contents)
90+
defined_dynamic = core_raw_metadata.get('dynamic', [])
91+
for field in list(defined_dynamic):
92+
if field in PROJECT_CORE_METADATA_FIELDS and field in base_metadata:
93+
core_raw_metadata[field] = base_metadata[field]
94+
defined_dynamic.remove(field)
10195

102-
return self._core_raw_metadata
96+
return core_raw_metadata
10397

104-
@property
98+
@cached_property
10599
def dynamic(self) -> list[str]:
106-
# Keep track of the original dynamic fields before depopulation
107-
if self._dynamic is None:
108-
dynamic = self.core_raw_metadata.get('dynamic', [])
109-
if not isinstance(dynamic, list):
110-
message = 'Field `project.dynamic` must be an array'
111-
raise TypeError(message)
112-
113-
for i, field in enumerate(dynamic, 1):
114-
if not isinstance(field, str):
115-
message = f'Field #{i} of field `project.dynamic` must be a string'
116-
raise TypeError(message)
100+
# Here we maintain a copy of the dynamic fields from `self.core raw metadata`.
101+
# This property should never be mutated. In contrast, the fields in
102+
# `self.core.dynamic` are depopulated on the first evaulation of `self.core`
103+
# or `self.version` as the actual values are computed.
104+
dynamic = self.core_raw_metadata.get('dynamic', [])
105+
if not isinstance(dynamic, list):
106+
message = 'Field `project.dynamic` must be an array'
107+
raise TypeError(message)
117108

118-
self._dynamic = list(dynamic)
109+
for i, field in enumerate(dynamic, 1):
110+
if not isinstance(field, str):
111+
message = f'Field #{i} of field `project.dynamic` must be a string'
112+
raise TypeError(message)
119113

120-
return self._dynamic
114+
return list(dynamic)
121115

122-
@property
116+
@cached_property
123117
def name(self) -> str:
124118
# Duplicate the name parsing here for situations where it's
125119
# needed but metadata plugins might not be available
126-
if self._name is None:
127-
name = self.core_raw_metadata.get('name', '')
128-
if not name:
129-
message = 'Missing required field `project.name`'
130-
raise ValueError(message)
131-
132-
self._name = normalize_project_name(name)
120+
name = self.core_raw_metadata.get('name', '')
121+
if not name:
122+
message = 'Missing required field `project.name`'
123+
raise ValueError(message)
133124

134-
return self._name
125+
return normalize_project_name(name)
135126

136127
@property
137128
def version(self) -> str:

0 commit comments

Comments
 (0)