Skip to content

Commit 8b30aed

Browse files
committed
perf: Only check defaults once
1 parent 57fe15c commit 8b30aed

1 file changed

Lines changed: 83 additions & 26 deletions

File tree

  • src/rust/engine/src/externs/target

src/rust/engine/src/externs/target/field.rs

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
// Licensed under the Apache License, Version 2.0 (see LICENSE).
33

44
use std::fmt::Write;
5+
use std::sync::OnceLock;
56

7+
use fnv::FnvHashMap;
8+
9+
use parking_lot::Mutex;
610
use pyo3::basic::CompareOp;
711
use pyo3::exceptions::PyValueError;
812
use pyo3::intern;
@@ -11,11 +15,50 @@ use pyo3::pybacked::PyBackedStr;
1115
use pyo3::pyclass_init::PyClassInitializer;
1216
use pyo3::types::PyType;
1317

18+
use crate::TypeId;
1419
use crate::externs::address::Address;
1520
use crate::python::PyComparedBool;
1621

1722
use super::util::{NoFieldValue, combine_hashes, raise_invalid_field_type, validate_choices};
1823

24+
static FIELD_TYPE_INFO_CACHE: OnceLock<Mutex<FnvHashMap<TypeId, FieldTypeInfo>>> = OnceLock::new();
25+
26+
struct FieldTypeInfo {
27+
none_is_valid_value: bool,
28+
deprecated: bool,
29+
required: bool,
30+
/// Populated lazily the first time the default is needed.
31+
default: Option<Py<PyAny>>,
32+
}
33+
34+
impl FieldTypeInfo {
35+
fn resolve(cls: &Bound<'_, PyType>, py: Python) -> PyResult<Self> {
36+
let removal_version: Option<PyBackedStr> =
37+
cls.getattr(intern!(py, "removal_version"))?.extract()?;
38+
Ok(Self {
39+
none_is_valid_value: cls.getattr(intern!(py, "none_is_valid_value"))?.extract()?,
40+
deprecated: removal_version.is_some(),
41+
required: cls.getattr(intern!(py, "required"))?.extract()?,
42+
default: None,
43+
})
44+
}
45+
}
46+
47+
fn with_field_type_info<R>(
48+
cls: &Bound<'_, PyType>,
49+
py: Python,
50+
f: impl FnOnce(&mut FieldTypeInfo) -> R,
51+
) -> PyResult<R> {
52+
let type_id = TypeId::new(cls);
53+
let cache = FIELD_TYPE_INFO_CACHE.get_or_init(|| Mutex::new(FnvHashMap::default()));
54+
let mut locked = cache.lock();
55+
let info = match locked.entry(type_id) {
56+
std::collections::hash_map::Entry::Occupied(e) => e.into_mut(),
57+
std::collections::hash_map::Entry::Vacant(e) => e.insert(FieldTypeInfo::resolve(cls, py)?),
58+
};
59+
Ok(f(info))
60+
}
61+
1962
#[pyclass(subclass, frozen, module = "pants.engine.internals.native_engine")]
2063
pub struct Field {
2164
pub(crate) value: Py<PyAny>,
@@ -37,13 +80,9 @@ impl Field {
3780
// to None below.
3881
Self::check_deprecated(cls, raw_value, &address, py)?;
3982

83+
let none_is_valid = with_field_type_info(cls, py, |info| info.none_is_valid_value)?;
4084
let raw_value = match raw_value {
41-
Some(value)
42-
if value.extract::<NoFieldValue>().is_ok()
43-
&& !Self::cls_none_is_valid_value(cls)? =>
44-
{
45-
None
46-
}
85+
Some(value) if value.extract::<NoFieldValue>().is_ok() && !none_is_valid => None,
4786
rv => rv,
4887
};
4988

@@ -97,28 +136,42 @@ impl Field {
97136
address: PyRef<Address>,
98137
py: Python<'py>,
99138
) -> PyResult<Bound<'py, PyAny>> {
100-
let default = || -> PyResult<Bound<'_, PyAny>> {
101-
if Self::cls_required(cls)? {
102-
// TODO: Should be `RequiredFieldMissingException`.
103-
Err(PyValueError::new_err(format!(
104-
"The `{}` field in target {} must be defined.",
105-
Self::cls_alias(cls)?,
106-
*address,
107-
)))
108-
} else {
109-
Self::cls_default(cls)
110-
}
111-
};
139+
enum Branch<'a, 'py> {
140+
Default,
141+
NoneValue,
142+
Value(&'a Bound<'py, PyAny>),
143+
}
112144

113-
let none_is_valid_value = Self::cls_none_is_valid_value(cls)?;
114-
match raw_value {
115-
Some(value) if none_is_valid_value && value.extract::<NoFieldValue>().is_ok() => {
116-
default()
145+
with_field_type_info(cls, py, |info| -> PyResult<Bound<'py, PyAny>> {
146+
let branch = match raw_value {
147+
Some(value)
148+
if info.none_is_valid_value && value.extract::<NoFieldValue>().is_ok() =>
149+
{
150+
Branch::Default
151+
}
152+
None if info.none_is_valid_value => Branch::NoneValue,
153+
None => Branch::Default,
154+
Some(value) => Branch::Value(value),
155+
};
156+
157+
match branch {
158+
Branch::NoneValue => Ok(py.None().into_bound(py)),
159+
Branch::Value(value) => Ok(value.clone()),
160+
Branch::Default => {
161+
if info.required {
162+
return Err(PyValueError::new_err(format!(
163+
"The `{}` field in target {} must be defined.",
164+
Self::cls_alias(cls)?,
165+
*address,
166+
)));
167+
}
168+
if info.default.is_none() {
169+
info.default = Some(cls.getattr(intern!(py, "default"))?.unbind());
170+
}
171+
Ok(info.default.as_ref().unwrap().bind(py).clone())
172+
}
117173
}
118-
None if none_is_valid_value => Ok(py.None().into_bound(py)),
119-
None => default(),
120-
Some(value) => Ok(value.clone()),
121-
}
174+
})?
122175
}
123176

124177
#[getter]
@@ -227,6 +280,10 @@ impl Field {
227280
address: &Bound<'_, Address>,
228281
py: Python,
229282
) -> PyResult<()> {
283+
let is_deprecated = with_field_type_info(cls, py, |info| info.deprecated)?;
284+
if !is_deprecated {
285+
return Ok(());
286+
}
230287
if address.borrow().is_generated_target() {
231288
return Ok(());
232289
}

0 commit comments

Comments
 (0)