Skip to content

Commit a61cd9e

Browse files
committed
perf: Only check defaults once
1 parent 0587253 commit a61cd9e

1 file changed

Lines changed: 75 additions & 27 deletions

File tree

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

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

Lines changed: 75 additions & 27 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,54 @@ 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+
/// None means required (no default). Some holds the default value.
30+
default: Option<Py<PyAny>>,
31+
}
32+
33+
impl FieldTypeInfo {
34+
fn resolve(cls: &Bound<'_, PyType>, py: Python) -> PyResult<Self> {
35+
let removal_version: Option<PyBackedStr> =
36+
cls.getattr(intern!(py, "removal_version"))?.extract()?;
37+
let required: bool = cls.getattr(intern!(py, "required"))?.extract()?;
38+
let default = if required {
39+
None
40+
} else {
41+
Some(cls.getattr(intern!(py, "default"))?.unbind())
42+
};
43+
Ok(Self {
44+
none_is_valid_value: cls.getattr(intern!(py, "none_is_valid_value"))?.extract()?,
45+
deprecated: removal_version.is_some(),
46+
default,
47+
})
48+
}
49+
}
50+
51+
fn with_field_type_info<R>(
52+
cls: &Bound<'_, PyType>,
53+
py: Python,
54+
f: impl FnOnce(&FieldTypeInfo) -> R,
55+
) -> PyResult<R> {
56+
let type_id = TypeId::new(cls);
57+
let cache = FIELD_TYPE_INFO_CACHE.get_or_init(|| Mutex::new(FnvHashMap::default()));
58+
let mut locked = cache.lock();
59+
let info = match locked.entry(type_id) {
60+
std::collections::hash_map::Entry::Occupied(e) => e.into_mut(),
61+
std::collections::hash_map::Entry::Vacant(e) => e.insert(FieldTypeInfo::resolve(cls, py)?),
62+
};
63+
Ok(f(info))
64+
}
65+
1966
#[pyclass(subclass, frozen, module = "pants.engine.internals.native_engine")]
2067
pub struct Field {
2168
pub(crate) value: Py<PyAny>,
@@ -37,13 +84,9 @@ impl Field {
3784
// to None below.
3885
Self::check_deprecated(cls, raw_value, &address, py)?;
3986

87+
let none_is_valid = with_field_type_info(cls, py, |info| info.none_is_valid_value)?;
4088
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-
}
89+
Some(value) if value.extract::<NoFieldValue>().is_ok() && !none_is_valid => None,
4790
rv => rv,
4891
};
4992

@@ -97,28 +140,29 @@ impl Field {
97140
address: PyRef<Address>,
98141
py: Python<'py>,
99142
) -> 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)
143+
with_field_type_info(cls, py, |info| {
144+
let default = || -> PyResult<Bound<'_, PyAny>> {
145+
match &info.default {
146+
Some(d) => Ok(d.bind(py).clone()),
147+
None => Err(PyValueError::new_err(format!(
148+
"The `{}` field in target {} must be defined.",
149+
Self::cls_alias(cls)?,
150+
*address,
151+
))),
152+
}
153+
};
154+
155+
match raw_value {
156+
Some(value)
157+
if info.none_is_valid_value && value.extract::<NoFieldValue>().is_ok() =>
158+
{
159+
default()
160+
}
161+
None if info.none_is_valid_value => Ok(py.None().into_bound(py)),
162+
None => default(),
163+
Some(value) => Ok(value.clone()),
110164
}
111-
};
112-
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()
117-
}
118-
None if none_is_valid_value => Ok(py.None().into_bound(py)),
119-
None => default(),
120-
Some(value) => Ok(value.clone()),
121-
}
165+
})?
122166
}
123167

124168
#[getter]
@@ -227,6 +271,10 @@ impl Field {
227271
address: &Bound<'_, Address>,
228272
py: Python,
229273
) -> PyResult<()> {
274+
let is_deprecated = with_field_type_info(cls, py, |info| info.deprecated)?;
275+
if !is_deprecated {
276+
return Ok(());
277+
}
230278
if address.borrow().is_generated_target() {
231279
return Ok(());
232280
}

0 commit comments

Comments
 (0)