use std::{
collections::BTreeMap,
fmt,
ops::{Deref, DerefMut},
sync::Arc,
};
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use compact_str::CompactString;
use indexmap::IndexMap;
use parking_lot::Mutex;
pub type SharedList = Arc<Mutex<ListBody>>;
const DROP_STACK_RED_ZONE: usize = 1024 * 1024;
const DROP_STACK_GROW: usize = 32 * 1024 * 1024;
#[derive(Debug, Default)]
pub struct ListBody {
items: Vec<Value>,
size_cache: Option<usize>,
}
impl ListBody {
#[inline]
#[must_use]
pub fn new(items: Vec<Value>) -> Self {
Self { items, size_cache: None }
}
#[inline]
pub fn set_items(&mut self, items: Vec<Value>) {
self.items = items;
self.size_cache = None;
}
#[inline]
#[must_use]
pub fn into_items(mut self) -> Vec<Value> {
std::mem::take(&mut self.items)
}
#[inline]
pub fn cached_size(&mut self, compute: impl FnOnce(&[Value]) -> usize) -> usize {
match self.size_cache {
Some(size) => size,
None => {
let size = compute(&self.items);
self.size_cache = Some(size);
size
}
}
}
}
impl Deref for ListBody {
type Target = Vec<Value>;
#[inline]
fn deref(&self) -> &Vec<Value> {
&self.items
}
}
impl DerefMut for ListBody {
#[inline]
fn deref_mut(&mut self) -> &mut Vec<Value> {
self.size_cache = None;
&mut self.items
}
}
impl Drop for ListBody {
fn drop(&mut self) {
if self.items.is_empty() {
return;
}
let items = std::mem::take(&mut self.items);
stacker::maybe_grow(DROP_STACK_RED_ZONE, DROP_STACK_GROW, move || drop(items));
}
}
pub type SharedFields = Arc<Mutex<BTreeMap<String, Value>>>;
#[inline]
#[must_use]
pub fn shared_list(items: Vec<Value>) -> SharedList {
Arc::new(Mutex::new(ListBody::new(items)))
}
pub type SharedSet = Arc<Mutex<crate::pyset::SetBody>>;
pub type SharedFrozenset = Arc<crate::pyset::SetBody>;
#[inline]
#[must_use]
pub fn shared_set(body: crate::pyset::SetBody) -> SharedSet {
Arc::new(Mutex::new(body))
}
pub type SharedDict = Arc<Mutex<DictBody>>;
#[derive(Debug, Default)]
pub struct DictBody {
map: IndexMap<ValueKey, Value>,
size_cache: Option<usize>,
}
impl DictBody {
#[inline]
#[must_use]
pub fn new(map: IndexMap<ValueKey, Value>) -> Self {
Self { map, size_cache: None }
}
#[inline]
pub fn set_map(&mut self, map: IndexMap<ValueKey, Value>) {
self.map = map;
self.size_cache = None;
}
#[inline]
pub fn cached_size(
&mut self,
compute: impl FnOnce(&IndexMap<ValueKey, Value>) -> usize,
) -> usize {
match self.size_cache {
Some(size) => size,
None => {
let size = compute(&self.map);
self.size_cache = Some(size);
size
}
}
}
}
impl Deref for DictBody {
type Target = IndexMap<ValueKey, Value>;
#[inline]
fn deref(&self) -> &IndexMap<ValueKey, Value> {
&self.map
}
}
impl DerefMut for DictBody {
#[inline]
fn deref_mut(&mut self) -> &mut IndexMap<ValueKey, Value> {
self.size_cache = None;
&mut self.map
}
}
impl PartialEq for DictBody {
fn eq(&self, other: &Self) -> bool {
self.map == other.map
}
}
impl Drop for DictBody {
fn drop(&mut self) {
if self.map.is_empty() {
return;
}
let map = std::mem::take(&mut self.map);
stacker::maybe_grow(DROP_STACK_RED_ZONE, DROP_STACK_GROW, move || drop(map));
}
}
#[inline]
#[must_use]
pub fn shared_dict(map: IndexMap<ValueKey, Value>) -> SharedDict {
Arc::new(Mutex::new(DictBody::new(map)))
}
pub type SharedByteArray = Arc<Mutex<Vec<u8>>>;
#[inline]
#[must_use]
pub fn shared_bytes(bytes: Vec<u8>) -> SharedByteArray {
Arc::new(Mutex::new(bytes))
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StringIoData {
pub buf: String,
pub pos: usize,
}
pub type SharedStringIo = Arc<Mutex<StringIoData>>;
#[must_use]
pub fn shared_stringio(initial: String) -> SharedStringIo {
Arc::new(Mutex::new(StringIoData { buf: initial, pos: 0 }))
}
#[inline]
#[must_use]
pub fn shared_fields(fields: BTreeMap<String, Value>) -> SharedFields {
Arc::new(Mutex::new(fields))
}
#[inline]
#[must_use]
pub fn int_from_bigint(n: num_bigint::BigInt) -> Value {
match i64::try_from(&n) {
Ok(v) => Value::Int(v),
Err(_) => Value::BigInt(Box::new(n)),
}
}
pub(crate) fn int_from_bigint_limited(
n: num_bigint::BigInt,
max_bits: u64,
) -> Result<Value, crate::error::EvalError> {
use crate::error::EvalError;
use crate::value::ExceptionValue;
if n.bits() > max_bits {
return Err(EvalError::Exception(ExceptionValue::new(
"OverflowError",
format!("int exceeds max_int_bits limit ({max_bits} bits)"),
)));
}
Ok(int_from_bigint(n))
}
#[must_use]
pub fn value_as_bigint(v: &Value) -> Option<num_bigint::BigInt> {
match v {
Value::Int(i) => Some(num_bigint::BigInt::from(*i)),
Value::BigInt(b) => Some((**b).clone()),
Value::Bool(b) => Some(num_bigint::BigInt::from(i64::from(*b))),
Value::EnumMember { value, kind: EnumKind::Int | EnumKind::IntFlag, .. } => {
value_as_bigint(value)
}
_ => None,
}
}
#[must_use]
pub fn is_builtin_type_name(name: &str) -> bool {
matches!(
name,
"int"
| "float"
| "complex"
| "bool"
| "str"
| "bytes"
| "bytearray"
| "list"
| "tuple"
| "dict"
| "set"
| "frozenset"
| "range"
| "type"
| "object"
| "slice"
| "memoryview"
| "enumerate"
| "zip"
| "map"
| "filter"
| "reversed"
| "property"
| "super"
| "staticmethod"
| "classmethod"
)
}
#[must_use]
pub fn value_as_i64(v: &Value) -> Option<i64> {
match v {
Value::Int(i) => Some(*i),
Value::Bool(b) => Some(i64::from(*b)),
Value::BigInt(b) => i64::try_from(b.as_ref()).ok(),
_ => None,
}
}
fn serialize_shared_list<S: serde::Serializer>(
list: &SharedList,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq;
let snapshot = list.lock().clone();
let mut seq = serializer.serialize_seq(Some(snapshot.len()))?;
for v in &snapshot {
seq.serialize_element(v)?;
}
seq.end()
}
fn deserialize_shared_list<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedList, D::Error> {
let items: Vec<Value> = Deserialize::deserialize(deserializer)?;
Ok(shared_list(items))
}
fn serialize_shared_bytes<S: serde::Serializer>(
bytes: &SharedByteArray,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&bytes.lock())
}
fn deserialize_shared_bytes<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedByteArray, D::Error> {
let bytes: Vec<u8> = Deserialize::deserialize(deserializer)?;
Ok(shared_bytes(bytes))
}
fn serialize_shared_stringio<S: serde::Serializer>(
io: &SharedStringIo,
serializer: S,
) -> Result<S::Ok, S::Error> {
io.lock().clone().serialize(serializer)
}
fn deserialize_shared_stringio<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedStringIo, D::Error> {
let data: StringIoData = Deserialize::deserialize(deserializer)?;
Ok(Arc::new(Mutex::new(data)))
}
fn serialize_shared_fields<S: serde::Serializer>(
fields: &SharedFields,
serializer: S,
) -> Result<S::Ok, S::Error> {
let snapshot = fields.lock().clone();
snapshot.serialize(serializer)
}
fn deserialize_shared_fields<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedFields, D::Error> {
let map: BTreeMap<String, Value> = Deserialize::deserialize(deserializer)?;
Ok(shared_fields(map))
}
use num_traits::Zero as _;
use serde::{Deserialize, Serialize};
fn serialize_dict<S: serde::Serializer>(
map: &IndexMap<ValueKey, Value>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(map.len()))?;
for (k, v) in map {
seq.serialize_element(&(k, v))?;
}
seq.end()
}
fn deserialize_dict<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<IndexMap<ValueKey, Value>, D::Error> {
let pairs: Vec<(ValueKey, Value)> = Deserialize::deserialize(deserializer)?;
Ok(pairs.into_iter().collect())
}
fn serialize_shared_dict<S: serde::Serializer>(
map: &SharedDict,
serializer: S,
) -> Result<S::Ok, S::Error> {
serialize_dict(&map.lock(), serializer)
}
fn deserialize_shared_dict<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedDict, D::Error> {
Ok(shared_dict(deserialize_dict(deserializer)?))
}
fn serialize_shared_set<S: serde::Serializer>(
body: &SharedSet,
serializer: S,
) -> Result<S::Ok, S::Error> {
serde::Serialize::serialize(&body.lock().iter_ordered(), serializer)
}
fn deserialize_shared_set<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedSet, D::Error> {
let items = <Vec<Value> as serde::Deserialize>::deserialize(deserializer)?;
Ok(shared_set(crate::pyset::SetBody::from_items(items)))
}
fn serialize_shared_frozenset<S: serde::Serializer>(
body: &SharedFrozenset,
serializer: S,
) -> Result<S::Ok, S::Error> {
serde::Serialize::serialize(&body.iter_ordered(), serializer)
}
fn deserialize_shared_frozenset<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<SharedFrozenset, D::Error> {
let items = <Vec<Value> as serde::Deserialize>::deserialize(deserializer)?;
Ok(Arc::new(crate::pyset::SetBody::from_items(items)))
}
fn set_bodies_equal(a: &crate::pyset::SetBody, b: &crate::pyset::SetBody) -> bool {
if a.len() != b.len() {
return false;
}
let (ai, bi) = (a.iter_ordered(), b.iter_ordered());
ai.iter().all(|x| bi.iter().any(|y| x == y))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BuiltinIterName {
Count,
Cycle,
Repeat,
ListIterator,
BytearrayIterator,
}
impl BuiltinIterName {
#[must_use]
pub const fn type_name(self) -> &'static str {
match self {
Self::Count => "count",
Self::Cycle => "cycle",
Self::Repeat => "repeat",
Self::ListIterator => "list_iterator",
Self::BytearrayIterator => "bytearray_iterator",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum DecimalKind {
#[default]
Normal,
NegZero,
PosInf,
NegInf,
Nan,
}
impl DecimalKind {
#[must_use]
pub const fn is_nan(self) -> bool {
matches!(self, Self::Nan)
}
#[must_use]
pub const fn is_infinite(self) -> bool {
matches!(self, Self::PosInf | Self::NegInf)
}
#[must_use]
pub const fn is_special(self) -> bool {
matches!(self, Self::PosInf | Self::NegInf | Self::Nan)
}
#[must_use]
pub const fn special_str(self) -> Option<&'static str> {
match self {
Self::PosInf => Some("Infinity"),
Self::NegInf => Some("-Infinity"),
Self::Nan => Some("NaN"),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum LazyKind {
#[default]
Generator,
Map,
Filter,
Zip,
Enumerate,
Chain,
Islice,
Groupby,
Accumulate,
Combinations,
CombinationsWithReplacement,
Permutations,
Product,
Starmap,
Takewhile,
Dropwhile,
Filterfalse,
Compress,
Pairwise,
Tee,
ZipLongest,
Batched,
TupleIterator,
StrAsciiIterator,
StrIterator,
SetIterator,
DictKeyIterator,
DictValueIterator,
DictItemIterator,
RangeIterator,
BytesIterator,
ListReverseIterator,
DictReverseKeyIterator,
DictReverseValueIterator,
DictReverseItemIterator,
Reversed,
}
impl LazyKind {
#[must_use]
pub const fn type_name(self) -> &'static str {
match self {
Self::Generator => "generator",
Self::Map => "map",
Self::Filter => "filter",
Self::Zip => "zip",
Self::Enumerate => "enumerate",
Self::Chain => "chain",
Self::Islice => "islice",
Self::Groupby => "groupby",
Self::Accumulate => "accumulate",
Self::Combinations => "combinations",
Self::CombinationsWithReplacement => "combinations_with_replacement",
Self::Permutations => "permutations",
Self::Product => "product",
Self::Starmap => "starmap",
Self::Takewhile => "takewhile",
Self::Dropwhile => "dropwhile",
Self::Filterfalse => "filterfalse",
Self::Compress => "compress",
Self::Pairwise => "pairwise",
Self::Tee => "_tee",
Self::ZipLongest => "zip_longest",
Self::Batched => "batched",
Self::TupleIterator => "tuple_iterator",
Self::StrAsciiIterator => "str_ascii_iterator",
Self::StrIterator => "str_iterator",
Self::SetIterator => "set_iterator",
Self::DictKeyIterator => "dict_keyiterator",
Self::DictValueIterator => "dict_valueiterator",
Self::DictItemIterator => "dict_itemiterator",
Self::RangeIterator => "range_iterator",
Self::BytesIterator => "bytes_iterator",
Self::ListReverseIterator => "list_reverseiterator",
Self::DictReverseKeyIterator => "dict_reversekeyiterator",
Self::DictReverseValueIterator => "dict_reversevalueiterator",
Self::DictReverseItemIterator => "dict_reverseitemiterator",
Self::Reversed => "reversed",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DictViewKind {
Keys,
Values,
Items,
}
impl DictViewKind {
#[must_use]
pub const fn type_name(self) -> &'static str {
match self {
Self::Keys => "dict_keys",
Self::Values => "dict_values",
Self::Items => "dict_items",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Value {
None,
NotImplemented,
Ellipsis,
Bool(bool),
Int(i64),
BigInt(Box<num_bigint::BigInt>),
Float(f64),
Complex(Box<num_complex::Complex64>),
String(CompactString),
Bytes(Vec<u8>),
ByteArray(
#[serde(serialize_with = "serialize_shared_bytes")]
#[serde(deserialize_with = "deserialize_shared_bytes")]
SharedByteArray,
),
MemoryView(Box<Value>),
StringIO(
#[serde(serialize_with = "serialize_shared_stringio")]
#[serde(deserialize_with = "deserialize_shared_stringio")]
SharedStringIo,
),
List(
#[serde(serialize_with = "serialize_shared_list")]
#[serde(deserialize_with = "deserialize_shared_list")]
SharedList,
),
Array {
typecode: char,
#[serde(serialize_with = "serialize_shared_list")]
#[serde(deserialize_with = "deserialize_shared_list")]
items: SharedList,
},
Tuple(Vec<Self>),
Dict(
#[serde(
serialize_with = "serialize_shared_dict",
deserialize_with = "deserialize_shared_dict"
)]
SharedDict,
),
OrderedDict(
#[serde(
serialize_with = "serialize_shared_dict",
deserialize_with = "deserialize_shared_dict"
)]
SharedDict,
),
DictView {
#[serde(
serialize_with = "serialize_shared_dict",
deserialize_with = "deserialize_shared_dict"
)]
dict: SharedDict,
kind: DictViewKind,
},
#[serde(serialize_with = "serialize_shared_set", deserialize_with = "deserialize_shared_set")]
Set(SharedSet),
#[serde(
serialize_with = "serialize_shared_frozenset",
deserialize_with = "deserialize_shared_frozenset"
)]
Frozenset(SharedFrozenset),
Function(Arc<FunctionDef>),
Coroutine(Box<CoroutineValue>),
Lambda(Arc<LambdaDef>),
Range { start: i64, stop: i64, step: i64 },
Slice(Box<SliceValue>),
Exception(Box<ExceptionValue>),
ExceptionMethod { method: String, exception: Box<ExceptionValue> },
#[serde(skip)]
LazyProxy(crate::tools::lazy_proxy::LazyProxy),
Lazy { items: Vec<Self>, cursor_id: u64, kind: LazyKind },
Generator { id: u64 },
BuiltinIter { id: u64, kind: BuiltinIterName },
Type(String),
Class(String),
Module(String),
Instance(InstanceValue),
Partial(Box<PartialData>),
OperatorGetter(Box<OperatorGetter>),
LruCache(std::sync::Arc<LruCacheData>),
SingleDispatch(std::sync::Arc<SingleDispatchData>),
ModuleFunction { module: String, name: String },
Date(NaiveDate),
DateTime { dt: NaiveDateTime, tz_offset_secs: Option<i32> },
Time(NaiveTime),
TimeDelta(i64),
TimeZone(i32),
HashDigest { algo: String, bytes: Vec<u8> },
Deque { items: std::collections::VecDeque<Self>, maxlen: Option<usize> },
DefaultDict(Box<DefaultDictData>),
ChainMap(Vec<Self>),
Template(CompactString),
EnumMember { class_name: String, member_name: String, value: Box<Self>, kind: EnumKind },
ReMatch(Box<MatchValue>),
RePattern(Box<String>),
Super { defining_class: String, instance: Box<InstanceValue> },
SuperClass { defining_class: String, class_name: String },
Property { class_name: String, name: String },
Counter(
#[serde(serialize_with = "serialize_dict", deserialize_with = "deserialize_dict")]
IndexMap<ValueKey, Self>,
),
Decimal(Box<bigdecimal::BigDecimal>, DecimalKind),
Fraction(Box<num_rational::BigRational>),
BoundMethod { receiver: BoundMethodReceiver, method: String },
BuiltinTypeMethod { type_name: String, method: String },
BuiltinName(String),
ToolName(String),
ExceptionType(String),
UnboundClassMethod { class: String, method: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BoundMethodReceiver {
Snapshot(Box<Value>),
Place {
root: String,
steps: Vec<BoundMethodStep>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BoundMethodStep {
Index(Value),
Attr(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefaultDictData {
#[serde(serialize_with = "serialize_dict", deserialize_with = "deserialize_dict")]
pub items: IndexMap<ValueKey, Value>,
pub factory: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartialData {
pub func: Value,
pub args: Vec<Value>,
pub keywords: indexmap::IndexMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OperatorGetter {
ItemGetter(Vec<Value>),
AttrGetter(Vec<Vec<String>>),
MethodCaller { name: String, args: Vec<Value>, kwargs: indexmap::IndexMap<String, Value> },
}
#[derive(Debug)]
pub struct LruCacheData {
pub func: Value,
pub maxsize: Option<usize>,
pub cache: Mutex<IndexMap<Vec<ValueKey>, Value>>,
pub hits: std::sync::atomic::AtomicU64,
pub misses: std::sync::atomic::AtomicU64,
}
impl Serialize for LruCacheData {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut st = serializer.serialize_struct("LruCacheData", 2)?;
st.serialize_field("func", &self.func)?;
st.serialize_field("maxsize", &self.maxsize)?;
st.end()
}
}
impl<'de> Deserialize<'de> for LruCacheData {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Wire {
func: Value,
maxsize: Option<usize>,
}
let w = Wire::deserialize(deserializer)?;
Ok(Self {
func: w.func,
maxsize: w.maxsize,
cache: Mutex::new(IndexMap::new()),
hits: std::sync::atomic::AtomicU64::new(0),
misses: std::sync::atomic::AtomicU64::new(0),
})
}
}
#[derive(Debug)]
pub struct SingleDispatchData {
pub name: String,
pub default: Value,
pub registry: Mutex<IndexMap<String, Value>>,
}
impl Serialize for SingleDispatchData {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut st = serializer.serialize_struct("SingleDispatchData", 3)?;
st.serialize_field("name", &self.name)?;
st.serialize_field("default", &self.default)?;
st.serialize_field("registry", &*self.registry.lock())?;
st.end()
}
}
impl<'de> Deserialize<'de> for SingleDispatchData {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Wire {
name: String,
default: Value,
registry: IndexMap<String, Value>,
}
let w = Wire::deserialize(deserializer)?;
Ok(Self { name: w.name, default: w.default, registry: Mutex::new(w.registry) })
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchValue {
pub groups: Vec<Option<MatchGroup>>,
pub named: indexmap::IndexMap<String, usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchGroup {
pub text: String,
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SliceValue {
pub start: Value,
pub stop: Value,
pub step: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceValue {
pub class_name: String,
#[serde(serialize_with = "serialize_shared_fields")]
#[serde(deserialize_with = "deserialize_shared_fields")]
pub fields: SharedFields,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClassValue {
pub name: String,
pub methods: BTreeMap<String, FunctionDef>,
pub class_attrs: BTreeMap<String, Value>,
pub bases: Vec<String>,
pub mro: Vec<String>,
pub properties: BTreeMap<String, PropertyDef>,
pub static_methods: BTreeMap<String, FunctionDef>,
pub class_methods: BTreeMap<String, FunctionDef>,
#[serde(default)]
pub enum_kind: Option<EnumKind>,
#[serde(default)]
pub enum_members: Vec<String>,
#[serde(default)]
pub annotations: Vec<String>,
#[serde(default)]
pub dataclass_fields: Option<Vec<DataclassField>>,
#[serde(default)]
pub frozen: bool,
#[serde(default)]
pub order: bool,
#[serde(default)]
pub total_ordering: bool,
#[serde(default)]
pub slots: bool,
#[serde(default)]
pub slot_names: Vec<String>,
#[serde(default)]
pub abstract_methods: Vec<String>,
#[serde(default)]
pub metaclass: Option<String>,
#[serde(default)]
pub qualname: String,
#[serde(default)]
pub is_generic: bool,
#[serde(default)]
pub initvar_fields: Vec<String>,
}
impl ClassValue {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
Self {
name: name.clone(),
methods: BTreeMap::new(),
class_attrs: BTreeMap::new(),
bases: Vec::new(),
mro: vec![name.clone()],
properties: BTreeMap::new(),
static_methods: BTreeMap::new(),
class_methods: BTreeMap::new(),
enum_kind: None,
enum_members: Vec::new(),
annotations: Vec::new(),
dataclass_fields: None,
frozen: false,
order: false,
total_ordering: false,
slots: false,
slot_names: Vec::new(),
abstract_methods: Vec::new(),
metaclass: None,
qualname: name,
is_generic: false,
initvar_fields: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataclassField {
pub name: String,
pub default: Option<Value>,
pub default_factory: Option<Value>,
pub init: bool,
pub repr: bool,
pub compare: bool,
#[serde(default)]
pub init_only: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EnumKind {
Plain,
Int,
Str,
Flag,
IntFlag,
}
impl EnumKind {
#[must_use]
pub const fn is_flag(self) -> bool {
matches!(self, Self::Flag | Self::IntFlag)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PropertyDef {
pub getter: FunctionDef,
pub setter: Option<FunctionDef>,
pub deleter: Option<FunctionDef>,
#[serde(default)]
pub cached: bool,
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::None, Self::None) => true,
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Int(a), Self::Int(b)) => a == b,
(Self::BigInt(a), Self::BigInt(b)) => a == b,
(Self::Int(a), Self::BigInt(b)) | (Self::BigInt(b), Self::Int(a)) => {
b.as_ref() == &num_bigint::BigInt::from(*a)
}
(Self::Bool(b), Self::BigInt(i)) | (Self::BigInt(i), Self::Bool(b)) => {
i.as_ref() == &num_bigint::BigInt::from(i64::from(*b))
}
(Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
(Self::String(a), Self::String(b)) => a == b,
(Self::Type(a), Self::Type(b))
| (Self::Class(a), Self::Class(b))
| (Self::Module(a), Self::Module(b)) => a == b,
(Self::Bytes(a), Self::Bytes(b)) => a == b,
(Self::ByteArray(a), Self::ByteArray(b)) => Arc::ptr_eq(a, b) || *a.lock() == *b.lock(),
(Self::ByteArray(a), Self::Bytes(b)) | (Self::Bytes(b), Self::ByteArray(a)) => {
*a.lock() == *b
}
(Self::List(a), Self::List(b)) => {
if Arc::ptr_eq(a, b) {
return true;
}
let a_guard = a.lock();
let b_guard = b.lock();
a_guard.len() == b_guard.len()
&& a_guard.iter().zip(b_guard.iter()).all(|(x, y)| x == y)
}
(Self::Array { items: a, .. }, Self::Array { items: b, .. }) => {
if Arc::ptr_eq(a, b) {
return true;
}
let a_guard = a.lock();
let b_guard = b.lock();
a_guard.len() == b_guard.len()
&& a_guard.iter().zip(b_guard.iter()).all(|(x, y)| x == y)
}
(Self::Tuple(a), Self::Tuple(b)) => a == b,
(Self::Set(a), Self::Set(b)) => {
Arc::ptr_eq(a, b) || set_bodies_equal(&a.lock(), &b.lock())
}
(Self::Frozenset(a), Self::Frozenset(b)) => set_bodies_equal(a, b),
(Self::Frozenset(a), Self::Set(b)) => set_bodies_equal(a, &b.lock()),
(Self::Set(a), Self::Frozenset(b)) => set_bodies_equal(&a.lock(), b),
(Self::Dict(a), Self::Dict(b)) => Arc::ptr_eq(a, b) || *a.lock() == *b.lock(),
(
Self::Range { start: s1, stop: e1, step: st1 },
Self::Range { start: s2, stop: e2, step: st2 },
) => s1 == s2 && e1 == e2 && st1 == st2,
(Self::Exception(a), Self::Exception(b)) => {
a.type_name == b.type_name && a.message == b.message
}
(Self::Date(a), Self::Date(b)) => a == b,
(
Self::ModuleFunction { module: m1, name: n1 },
Self::ModuleFunction { module: m2, name: n2 },
) => m1 == m2 && n1 == n2,
(
Self::EnumMember { class_name: c1, member_name: m1, .. },
Self::EnumMember { class_name: c2, member_name: m2, .. },
) => c1 == c2 && m1 == m2,
(Self::EnumMember { value, kind: EnumKind::Int | EnumKind::Str, .. }, other) => {
value.as_ref() == other
}
(other, Self::EnumMember { value, kind: EnumKind::Int | EnumKind::Str, .. }) => {
other == value.as_ref()
}
(Self::Decimal(a, _), Self::Decimal(b, _)) => a == b,
(Self::Fraction(a), Self::Fraction(b)) => a == b,
(Self::Decimal(d, _), Self::Int(i)) | (Self::Int(i), Self::Decimal(d, _)) => {
d.as_ref() == &bigdecimal::BigDecimal::from(*i)
}
(Self::Decimal(d, _), Self::BigInt(i)) | (Self::BigInt(i), Self::Decimal(d, _)) => {
d.as_ref() == &bigdecimal::BigDecimal::from(i.as_ref().clone())
}
(Self::Fraction(f), Self::Int(i)) | (Self::Int(i), Self::Fraction(f)) => {
f.as_ref() == &num_rational::BigRational::from_integer(num_bigint::BigInt::from(*i))
}
(Self::Fraction(f), Self::BigInt(i)) | (Self::BigInt(i), Self::Fraction(f)) => {
f.as_ref() == &num_rational::BigRational::from_integer(i.as_ref().clone())
}
_ => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ValueKey {
None,
Ellipsis,
Bool(bool),
Int(i64),
BigInt(num_bigint::BigInt),
Float(u64),
Complex(u64, u64),
String(CompactString),
Tuple(Vec<Self>),
Frozenset(Vec<Self>),
Instance {
hash: i64,
value: Box<Value>,
},
Date(NaiveDate),
Time(NaiveTime),
DateTime {
dt: NaiveDateTime,
tz_offset_secs: Option<i32>,
},
TimeDelta(i64),
Decimal(Box<bigdecimal::BigDecimal>),
Fraction(Box<num_rational::BigRational>),
}
impl PartialEq for ValueKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::None, Self::None) => true,
(Self::Ellipsis, Self::Ellipsis) => true,
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Bool(b), Self::Int(i)) | (Self::Int(i), Self::Bool(b)) => *i == i64::from(*b),
(Self::Int(a), Self::Int(b)) => a == b,
(Self::BigInt(a), Self::BigInt(b)) => a == b,
(Self::Int(a), Self::BigInt(b)) | (Self::BigInt(b), Self::Int(a)) => {
b == &num_bigint::BigInt::from(*a)
}
(Self::Bool(b), Self::BigInt(i)) | (Self::BigInt(i), Self::Bool(b)) => {
i == &num_bigint::BigInt::from(i64::from(*b))
}
(Self::Float(a), Self::Float(b)) => a == b,
(Self::Float(bits), Self::Int(i)) | (Self::Int(i), Self::Float(bits)) => {
float_key_i64(*bits) == Some(*i)
}
(Self::Float(bits), Self::Bool(b)) | (Self::Bool(b), Self::Float(bits)) => {
float_key_i64(*bits) == Some(i64::from(*b))
}
(Self::Float(bits), Self::BigInt(n)) | (Self::BigInt(n), Self::Float(bits)) => {
float_key_i64(*bits).is_some_and(|i| n == &num_bigint::BigInt::from(i))
}
(Self::Complex(ar, ai), Self::Complex(br, bi)) => ar == br && ai == bi,
(Self::String(a), Self::String(b)) => a == b,
(Self::Tuple(a), Self::Tuple(b)) => a == b,
(Self::Frozenset(a), Self::Frozenset(b)) => {
a.len() == b.len() && a.iter().all(|x| b.contains(x))
}
(Self::Instance { value: a, .. }, Self::Instance { value: b, .. }) => {
match (a.as_ref(), b.as_ref()) {
(Value::Instance(ia), Value::Instance(ib)) => {
std::sync::Arc::ptr_eq(&ia.fields, &ib.fields)
}
(Value::Function(fa), Value::Function(fb)) => std::sync::Arc::ptr_eq(fa, fb),
(Value::Lambda(la), Value::Lambda(lb)) => std::sync::Arc::ptr_eq(la, lb),
_ => crate::eval::operations::values_equal_pub(a, b),
}
}
(Self::Date(a), Self::Date(b)) => a == b,
(Self::Time(a), Self::Time(b)) => a == b,
(Self::TimeDelta(a), Self::TimeDelta(b)) => a == b,
(Self::Decimal(a), Self::Decimal(b)) => a == b,
(Self::Fraction(a), Self::Fraction(b)) => a == b,
(
Self::DateTime { dt: a, tz_offset_secs: ta },
Self::DateTime { dt: b, tz_offset_secs: tb },
) => match (ta, tb) {
(None, None) => a == b,
(Some(oa), Some(ob)) => {
(*a - chrono::Duration::seconds(i64::from(*oa)))
== (*b - chrono::Duration::seconds(i64::from(*ob)))
}
_ => false,
},
_ => false,
}
}
}
impl Eq for ValueKey {}
#[expect(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::float_cmp,
reason = "exact round-trip guard: `i` is returned only when `i as f64 == f`, \
so the truncating cast is exact and the equality check is the point"
)]
fn float_key_i64(bits: u64) -> Option<i64> {
let f = f64::from_bits(bits);
if f.is_finite() && f.fract() == 0.0 {
let i = f as i64;
if i as f64 == f {
return Some(i);
}
}
None
}
impl core::hash::Hash for ValueKey {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
const NONE_TAG: u8 = 0;
const NUMERIC_TAG: u8 = 1;
const FLOAT_TAG: u8 = 2;
const STRING_TAG: u8 = 3;
const TUPLE_TAG: u8 = 4;
const INSTANCE_TAG: u8 = 5;
const COMPLEX_TAG: u8 = 6;
const ELLIPSIS_TAG: u8 = 7;
const FROZENSET_TAG: u8 = 8;
const DATE_TAG: u8 = 9;
const TIME_TAG: u8 = 10;
const DATETIME_TAG: u8 = 11;
const TIMEDELTA_TAG: u8 = 12;
const DECIMAL_TAG: u8 = 13;
const FRACTION_TAG: u8 = 14;
match self {
Self::None => NONE_TAG.hash(state),
Self::Ellipsis => ELLIPSIS_TAG.hash(state),
Self::Bool(b) => {
NUMERIC_TAG.hash(state);
i64::from(*b).hash(state);
}
Self::Int(i) => {
NUMERIC_TAG.hash(state);
i.hash(state);
}
Self::BigInt(i) => {
NUMERIC_TAG.hash(state);
if let Ok(n) = i64::try_from(i) {
n.hash(state);
} else {
i.hash(state);
}
}
Self::Float(bits) => {
if let Some(i) = float_key_i64(*bits) {
NUMERIC_TAG.hash(state);
i.hash(state);
} else {
FLOAT_TAG.hash(state);
bits.hash(state);
}
}
Self::Complex(re, im) => {
COMPLEX_TAG.hash(state);
re.hash(state);
im.hash(state);
}
Self::String(s) => {
STRING_TAG.hash(state);
s.hash(state);
}
Self::Tuple(items) => {
TUPLE_TAG.hash(state);
items.hash(state);
}
Self::Frozenset(items) => {
FROZENSET_TAG.hash(state);
let mut acc: u64 = 0;
for item in items {
let mut h = std::collections::hash_map::DefaultHasher::new();
item.hash(&mut h);
acc ^= core::hash::Hasher::finish(&h);
}
acc.hash(state);
}
Self::Instance { hash, .. } => {
INSTANCE_TAG.hash(state);
hash.hash(state);
}
Self::Date(d) => {
DATE_TAG.hash(state);
d.hash(state);
}
Self::Time(t) => {
TIME_TAG.hash(state);
t.hash(state);
}
Self::TimeDelta(m) => {
TIMEDELTA_TAG.hash(state);
m.hash(state);
}
Self::Decimal(d) => {
DECIMAL_TAG.hash(state);
let (mantissa, scale) = d.normalized().as_bigint_and_exponent();
mantissa.hash(state);
scale.hash(state);
}
Self::Fraction(fr) => {
FRACTION_TAG.hash(state);
fr.numer().hash(state);
fr.denom().hash(state);
}
Self::DateTime { dt, tz_offset_secs } => {
DATETIME_TAG.hash(state);
match tz_offset_secs {
None => {
0u8.hash(state);
dt.hash(state);
}
Some(o) => {
1u8.hash(state);
(*dt - chrono::Duration::seconds(i64::from(*o))).hash(state);
}
}
}
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ExceptionValue {
pub type_name: String,
pub message: String,
pub cause: Option<Box<Self>>,
#[serde(default)]
pub args: Vec<Value>,
#[serde(default)]
pub stamped_line: Option<u32>,
#[serde(default)]
pub exceptions: Option<Vec<Self>>,
#[serde(default)]
pub fields: BTreeMap<String, Value>,
}
impl ExceptionValue {
#[must_use]
pub fn new(type_name: impl Into<String>, message: impl Into<String>) -> Self {
let message = message.into();
let args = if message.is_empty() {
Vec::new()
} else {
vec![Value::String(message.clone().into())]
};
Self {
type_name: type_name.into(),
message,
cause: None,
args,
stamped_line: None,
exceptions: None,
fields: BTreeMap::new(),
}
}
#[must_use]
pub fn group(
type_name: impl Into<String>,
message: impl Into<String>,
exceptions: Vec<Self>,
) -> Self {
let message = message.into();
let nested: Vec<Value> =
exceptions.iter().cloned().map(|exc| Value::Exception(Box::new(exc))).collect();
Self {
type_name: type_name.into(),
message: message.clone(),
cause: None,
args: vec![Value::String(message.into()), Value::List(shared_list(nested))],
stamped_line: None,
exceptions: Some(exceptions),
fields: BTreeMap::new(),
}
}
#[must_use]
pub fn with_cause(mut self, cause: Self) -> Self {
self.cause = Some(Box::new(cause));
self
}
#[must_use]
pub fn with_args(mut self, args: Vec<Value>) -> Self {
self.args = args;
self
}
#[must_use]
pub fn key_error(key: &ValueKey) -> Self {
Self::new("KeyError", format!("{key}")).with_args(vec![key.to_value()])
}
#[must_use]
pub fn index_error(kind: &str) -> Self {
Self::new("IndexError", format!("{kind} index out of range"))
}
#[must_use]
pub fn zero_division_error(message: impl Into<String>) -> Self {
Self::new("ZeroDivisionError", message)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDef {
pub name: String,
#[serde(skip)]
pub body_key: String,
pub params: FunctionParams,
pub closure: BTreeMap<String, Value>,
pub source: String,
#[serde(default)]
pub nonlocal_names: Vec<String>,
#[serde(default)]
pub is_generator: bool,
#[serde(default)]
pub wraps_name: Option<String>,
#[serde(default)]
pub nonlocal_cell_id: Option<u64>,
#[serde(default)]
pub assigned_names: Vec<String>,
#[serde(default)]
pub global_names: Vec<String>,
#[serde(default)]
pub is_module_level: bool,
#[serde(default)]
pub docstring: Option<String>,
#[serde(default)]
pub cell_refreshes: Vec<(String, u64)>,
#[serde(default)]
pub qualname: String,
#[serde(default)]
pub annotations: Vec<(String, Value)>,
#[serde(default)]
pub is_async: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoroutineValue {
pub func: Arc<FunctionDef>,
pub args: Vec<Value>,
pub kwargs: indexmap::IndexMap<String, Value>,
pub awaited: bool,
}
impl FunctionDef {
#[must_use]
pub fn display_qualname(&self) -> &str {
if self.qualname.is_empty() { &self.name } else { &self.qualname }
}
}
impl FunctionDef {
#[must_use]
pub fn body_cache_key(&self) -> &str {
if self.body_key.is_empty() { &self.name } else { &self.body_key }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LambdaDef {
pub params: FunctionParams,
pub lambda_id: String,
pub source: String,
#[serde(default)]
pub closure: BTreeMap<String, Value>,
#[serde(default)]
pub assigned_names: Vec<String>,
#[serde(default)]
pub is_module_level: bool,
#[serde(default)]
pub cell_refreshes: Vec<(String, u64)>,
#[serde(default)]
pub qualname: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionParams {
pub args: Vec<Param>,
pub defaults: Vec<String>,
#[serde(default)]
pub default_values: Vec<Value>,
pub vararg: Option<String>,
pub kwonlyargs: Vec<Param>,
pub kw_defaults: Vec<Option<String>>,
#[serde(default)]
pub kw_default_values: Vec<Option<Value>>,
pub kwarg: Option<String>,
#[serde(default)]
pub posonly_count: usize,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Param {
pub name: String,
#[serde(default)]
pub annotation: Option<String>,
}
impl Value {
#[must_use]
pub fn set_items(&self) -> Option<Vec<Value>> {
match self {
Value::Set(b) => Some(b.lock().iter_ordered()),
Value::Frozenset(b) => Some(b.iter_ordered()),
_ => None,
}
}
#[must_use]
pub(crate) fn set_len(&self) -> Option<usize> {
match self {
Value::Set(b) => Some(b.lock().len()),
Value::Frozenset(b) => Some(b.len()),
_ => None,
}
}
#[must_use]
pub fn new_set(items: Vec<Value>) -> Value {
Value::Set(shared_set(crate::pyset::SetBody::from_items(items)))
}
#[must_use]
pub fn new_frozenset(items: Vec<Value>) -> Value {
Value::Frozenset(Arc::new(crate::pyset::SetBody::from_items(items)))
}
#[inline]
#[must_use]
pub fn is_truthy(&self) -> bool {
match self {
Self::None | Self::NotImplemented => false,
Self::Ellipsis => true,
Self::Bool(b) => *b,
Self::Int(i) => *i != 0,
Self::BigInt(i) => {
use num_traits::Zero as _;
!i.is_zero()
}
Self::Float(f) => *f != 0.0,
Self::String(s) => !s.is_empty(),
Self::Bytes(b) => !b.is_empty(),
Self::ByteArray(b) => !b.lock().is_empty(),
Self::MemoryView(inner) => inner.is_truthy(),
Self::List(l) => !l.lock().is_empty(),
Self::Array { items, .. } => !items.lock().is_empty(),
Self::Tuple(t) => !t.is_empty(),
Self::Dict(d) | Self::OrderedDict(d) => !d.lock().is_empty(),
Self::Set(s) => !s.lock().is_empty(),
Self::Frozenset(s) => !s.is_empty(),
Self::StringIO(_)
| Self::Function(_)
| Self::Lambda(_)
| Self::Exception(_)
| Self::ExceptionMethod { .. }
| Self::LazyProxy(_)
| Self::Type(_)
| Self::Class(_)
| Self::Module(_)
| Self::Instance(_)
| Self::ModuleFunction { .. }
| Self::Date(_)
| Self::ReMatch(_)
| Self::RePattern(_)
| Self::Slice(_)
| Self::Super { .. }
| Self::SuperClass { .. }
| Self::Property { .. }
| Self::DateTime { .. }
| Self::Time(_)
| Self::TimeZone(_)
| Self::HashDigest { .. }
| Self::BoundMethod { .. }
| Self::BuiltinTypeMethod { .. }
| Self::BuiltinName(_)
| Self::ToolName(_)
| Self::ExceptionType(_)
| Self::UnboundClassMethod { .. }
| Self::Lazy { .. }
| Self::Generator { .. }
| Self::BuiltinIter { .. }
| Self::Partial { .. }
| Self::OperatorGetter(_)
| Self::LruCache(_)
| Self::SingleDispatch(_) => true,
Self::Counter(c) => !c.is_empty(),
Self::TimeDelta(micros) => *micros != 0,
Self::Deque { items, .. } => !items.is_empty(),
Self::DefaultDict(data) => !data.items.is_empty(),
Self::ChainMap(maps) => maps.iter().any(Self::is_truthy),
Self::DictView { dict, .. } => !dict.lock().is_empty(),
Self::Template(_) => true,
Self::EnumMember { value, .. } => value.is_truthy(),
Self::Decimal(d, kind) => kind.is_special() || !d.is_zero(),
Self::Fraction(f) => !f.numer().is_zero(),
Self::Complex(c) => c.re != 0.0 || c.im != 0.0,
Self::Range { start, stop, step } => {
if *step > 0 {
start < stop
} else {
start > stop
}
}
Self::Coroutine(_) => true,
}
}
#[must_use]
pub fn type_name(&self) -> &'static str {
match self {
Self::OperatorGetter(g) => match **g {
OperatorGetter::ItemGetter(_) => "itemgetter",
OperatorGetter::AttrGetter(_) => "attrgetter",
OperatorGetter::MethodCaller { .. } => "methodcaller",
},
Self::None => "NoneType",
Self::NotImplemented => "NotImplementedType",
Self::Ellipsis => "ellipsis",
Self::Bool(_) => "bool",
Self::Int(_) | Self::BigInt(_) => "int",
Self::Float(_) => "float",
Self::Complex(_) => "complex",
Self::String(_) => "str",
Self::Bytes(_) => "bytes",
Self::ByteArray(_) => "bytearray",
Self::MemoryView(_) => "memoryview",
Self::Array { .. } => "array",
Self::List(_) => "list",
Self::Tuple(_) => "tuple",
Self::Dict(_) => "dict",
Self::OrderedDict(_) => "OrderedDict",
Self::Set(_) => "set",
Self::Frozenset(_) => "frozenset",
Self::StringIO(_) => "StringIO",
Self::Function(_) | Self::Lambda(_) => "function",
Self::Coroutine(_) => "coroutine",
Self::Range { .. } => "range",
Self::Exception(_) => "Exception",
Self::ExceptionMethod { .. } => "method",
Self::LazyProxy(_) => "LazyProxy",
Self::Type(_) | Self::Class(_) | Self::ExceptionType(_) => "type",
Self::Module(_) => "module",
Self::Instance(_) => "object",
Self::BuiltinTypeMethod { method, .. } => {
if matches!(method.as_str(), "fromkeys" | "fromhex" | "from_bytes" | "maketrans") {
"builtin_function_or_method"
} else {
"method_descriptor"
}
}
Self::ModuleFunction { .. }
| Self::BoundMethod { .. }
| Self::BuiltinName(_)
| Self::ToolName(_)
| Self::UnboundClassMethod { .. } => "builtin_function_or_method",
Self::Date(_) => "date",
Self::ReMatch(_) => "re.Match",
Self::RePattern(_) => "re.Pattern",
Self::Slice(_) => "slice",
Self::Super { .. } | Self::SuperClass { .. } => "super",
Self::Property { .. } => "property",
Self::Counter(_) => "Counter",
Self::DateTime { .. } => "datetime",
Self::Time(_) => "time",
Self::TimeDelta(_) => "timedelta",
Self::TimeZone(_) => "timezone",
Self::HashDigest { .. } => "_hashlib.HASH",
Self::Deque { .. } => "deque",
Self::DefaultDict { .. } => "defaultdict",
Self::ChainMap(_) => "ChainMap",
Self::Template(_) => "Template",
Self::DictView { kind, .. } => kind.type_name(),
Self::EnumMember { .. } => "enum",
Self::Decimal(..) => "Decimal",
Self::Fraction(_) => "Fraction",
Self::Lazy { kind, .. } => kind.type_name(),
Self::Generator { .. } => "generator",
Self::BuiltinIter { kind, .. } => kind.type_name(),
Self::Partial { .. } => "functools.partial",
Self::LruCache(_) => "functools._lru_cache_wrapper",
Self::SingleDispatch(_) => "function",
}
}
#[must_use]
pub fn python_type_name(&self) -> String {
match self {
Self::Instance(inst) => inst.class_name.clone(),
Self::Type(n) | Self::Class(n) | Self::Module(n) => n.clone(),
other => other.type_name().to_string(),
}
}
}
fn write_tz_offset(f: &mut fmt::Formatter<'_>, secs: i32) -> fmt::Result {
let sign = if secs < 0 { '-' } else { '+' };
let abs = secs.unsigned_abs();
let hours = abs / 3600;
let minutes = (abs % 3600) / 60;
write!(f, "{sign}{hours:02}:{minutes:02}")
}
fn write_timedelta(f: &mut fmt::Formatter<'_>, micros: i64) -> fmt::Result {
let secs_total = micros.div_euclid(1_000_000);
let us = micros.rem_euclid(1_000_000);
let days = secs_total.div_euclid(86_400);
let day_remainder = secs_total.rem_euclid(86_400);
let hours = day_remainder / 3600;
let minutes = (day_remainder % 3600) / 60;
let seconds = day_remainder % 60;
if days != 0 {
let suffix = if days == 1 || days == -1 { "" } else { "s" };
write!(f, "{days} day{suffix}, ")?;
}
write!(f, "{hours}:{minutes:02}:{seconds:02}")?;
if us != 0 {
write!(f, ".{us:06}")?;
}
Ok(())
}
fn timedelta_repr(micros: i64) -> String {
let secs_total = micros.div_euclid(1_000_000);
let us = micros.rem_euclid(1_000_000);
let days = secs_total.div_euclid(86_400);
let seconds = secs_total.rem_euclid(86_400);
let mut parts = Vec::new();
if days != 0 {
parts.push(format!("days={days}"));
}
if seconds != 0 {
parts.push(format!("seconds={seconds}"));
}
if us != 0 {
parts.push(format!("microseconds={us}"));
}
let inner = if parts.is_empty() { "0".to_string() } else { parts.join(", ") };
format!("datetime.timedelta({inner})")
}
fn timezone_repr(offset_secs: i32) -> String {
if offset_secs == 0 {
"datetime.timezone.utc".to_string()
} else {
format!("datetime.timezone({})", timedelta_repr(i64::from(offset_secs) * 1_000_000))
}
}
fn time_repr(t: &chrono::NaiveTime) -> String {
use chrono::Timelike as _;
let (h, m, s, us) = (t.hour(), t.minute(), t.second(), t.nanosecond() / 1000);
let mut out = format!("datetime.time({h}, {m}");
if s != 0 || us != 0 {
out.push_str(&format!(", {s}"));
if us != 0 {
out.push_str(&format!(", {us}"));
}
}
out.push(')');
out
}
fn datetime_repr(dt: &chrono::NaiveDateTime, tz_offset_secs: Option<i32>) -> String {
use chrono::{Datelike as _, Timelike as _};
let mut out = format!(
"datetime.datetime({}, {}, {}, {}, {}",
dt.year(),
dt.month(),
dt.day(),
dt.hour(),
dt.minute()
);
let (s, us) = (dt.second(), dt.nanosecond() / 1000);
if s != 0 || us != 0 {
out.push_str(&format!(", {s}"));
if us != 0 {
out.push_str(&format!(", {us}"));
}
}
if let Some(secs) = tz_offset_secs {
out.push_str(&format!(", tzinfo={}", timezone_repr(secs)));
}
out.push(')');
out
}
fn counter_value_as_i64(value: &Value) -> i64 {
match value {
Value::Int(n) => *n,
Value::Bool(b) => i64::from(*b),
_ => 0,
}
}
fn write_python_float(f: &mut fmt::Formatter<'_>, v: f64) -> fmt::Result {
if v.is_nan() {
return write!(f, "nan");
}
if v.is_infinite() {
return write!(f, "{}", if v > 0.0 { "inf" } else { "-inf" });
}
if v == 0.0 {
return write!(f, "{}", if v.is_sign_negative() { "-0.0" } else { "0.0" });
}
let scientific = format!("{v:e}");
let exponent: i32 = scientific.split_once('e').and_then(|(_, e)| e.parse().ok()).unwrap_or(0);
if !(-4..16).contains(&exponent) {
match scientific.split_once('e') {
Some((mantissa, _)) => write!(f, "{mantissa}e{exponent:+03}"),
None => write!(f, "{v}"),
}
} else if v.fract() == 0.0 {
write!(f, "{v:.1}")
} else {
write!(f, "{v}")
}
}
fn format_complex_component(v: f64) -> String {
if v.is_nan() {
return "nan".to_string();
}
if v.is_infinite() {
return if v > 0.0 { "inf".to_string() } else { "-inf".to_string() };
}
if v == 0.0 {
return if v.is_sign_negative() { "-0".to_string() } else { "0".to_string() };
}
let scientific = format!("{v:e}");
let exponent: i32 = scientific.split_once('e').and_then(|(_, e)| e.parse().ok()).unwrap_or(0);
if !(-4..16).contains(&exponent) {
match scientific.split_once('e') {
Some((mantissa, _)) => format!("{mantissa}e{exponent:+03}"),
None => format!("{v}"),
}
} else {
format!("{v}")
}
}
fn format_complex(c: &num_complex::Complex64) -> String {
if c.re == 0.0 && !c.re.is_sign_negative() {
return format!("{}j", format_complex_component(c.im));
}
let re = format_complex_component(c.re);
let neg = c.im.is_sign_negative() && !c.im.is_nan();
let sign = if neg { "-" } else { "+" };
let im = format_complex_component(c.im.abs());
format!("({re}{sign}{im}j)")
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::NotImplemented => write!(f, "NotImplemented"),
Self::Ellipsis => write!(f, "Ellipsis"),
Self::Bool(true) => write!(f, "True"),
Self::Bool(false) => write!(f, "False"),
Self::Int(i) => write!(f, "{i}"),
Self::BigInt(i) => write!(f, "{i}"),
Self::Float(v) => write_python_float(f, *v),
Self::Complex(c) => write!(f, "{}", format_complex(c)),
Self::String(s) => write!(f, "{s}"),
Self::Bytes(b) => write_bytes_literal(f, b),
Self::ByteArray(b) => {
write!(f, "bytearray(")?;
write_bytes_literal(f, &b.lock())?;
write!(f, ")")
}
Self::List(items) => {
let Some(_cycle) = crate::cycle::repr_enter(Arc::as_ptr(items) as usize) else {
return write!(f, "[...]");
};
let snapshot = items.lock().clone();
write!(f, "[")?;
for (i, item) in snapshot.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
write!(f, "]")
}
Self::Array { typecode, items } => {
let snapshot = items.lock().clone();
if snapshot.is_empty() {
return write!(f, "array('{typecode}')");
}
write!(f, "array('{typecode}', [")?;
for (i, item) in snapshot.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
write!(f, "])")
}
Self::Tuple(items) => {
write!(f, "(")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
if items.len() == 1 {
write!(f, ",")?;
}
write!(f, ")")
}
Self::Dict(map) => {
write!(f, "{{")?;
for (i, (k, v)) in map.lock().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v.repr())?;
}
write!(f, "}}")
}
Self::OrderedDict(map) => {
let guard = map.lock();
if guard.is_empty() {
return write!(f, "OrderedDict()");
}
write!(f, "OrderedDict({{")?;
for (i, (k, v)) in guard.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v.repr())?;
}
write!(f, "}})")
}
Self::Set(body) => {
let seq = body.lock().iter_ordered();
write!(f, "{{")?;
for (i, item) in seq.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
write!(f, "}}")
}
Self::Frozenset(body) => {
if body.is_empty() {
return write!(f, "frozenset()");
}
let seq = body.iter_ordered();
write!(f, "frozenset({{")?;
for (i, item) in seq.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
write!(f, "}})")
}
Self::Function(fd) => write!(f, "<function {}>", fd.name),
Self::Lambda(_) => write!(f, "<function <lambda>>"),
Self::Range { start, stop, step } => {
if *step == 1 {
write!(f, "range({start}, {stop})")
} else {
write!(f, "range({start}, {stop}, {step})")
}
}
Self::Exception(e) => write!(f, "{}", e.message),
Self::ExceptionMethod { method, exception } => {
write!(f, "<bound method {method} of {}>", exception.type_name)
}
Self::LazyProxy(p) => write!(f, "<LazyProxy tool={}>", p.tool_name),
Self::Type(n) if n.starts_with("typing.") || n.contains('[') || n.starts_with('~') => {
write!(f, "{n}")
}
Self::Type(n) => write!(f, "<class '{n}'>"),
Self::Class(n) => write!(f, "<class '__main__.{n}'>"),
Self::Module(n) => write!(f, "<module '{n}'>"),
Self::Instance(inst) => write!(f, "<{} object>", inst.class_name),
Self::BuiltinName(name) if is_builtin_type_name(name) => {
write!(f, "<class '{name}'>")
}
Self::ModuleFunction { name, .. } | Self::BuiltinName(name) => {
write!(f, "<built-in function {name}>")
}
Self::Date(d) => write!(f, "{d}"),
Self::ReMatch(m) => match m.groups.first().and_then(Option::as_ref) {
Some(whole) => write!(
f,
"<re.Match object; span=({}, {}), match='{}'>",
whole.start, whole.end, whole.text
),
None => write!(f, "<re.Match object>"),
},
Self::Super { defining_class, instance } => {
write!(f, "<super: <class '{defining_class}'>, <{} object>>", instance.class_name)
}
Self::SuperClass { defining_class, class_name } => {
write!(f, "<super: <class '{defining_class}'>, <{class_name} object>>")
}
Self::DateTime { dt, tz_offset_secs } => {
use chrono::Timelike as _;
write!(f, "{}", dt.format("%Y-%m-%d %H:%M:%S"))?;
let micros = dt.nanosecond() / 1_000;
if micros != 0 {
write!(f, ".{micros:06}")?;
}
if let Some(secs) = tz_offset_secs {
write_tz_offset(f, *secs)?;
}
Ok(())
}
Self::Time(t) => {
use chrono::Timelike as _;
write!(f, "{}", t.format("%H:%M:%S"))?;
let micros = t.nanosecond() / 1_000;
if micros != 0 {
write!(f, ".{micros:06}")?;
}
Ok(())
}
Self::TimeDelta(micros) => write_timedelta(f, *micros),
Self::TimeZone(secs) => {
if *secs == 0 {
write!(f, "UTC")
} else {
write!(f, "UTC")?;
write_tz_offset(f, *secs)
}
}
Self::HashDigest { algo, bytes } => {
write!(f, "<{algo} HASH object, len={}>", bytes.len())
}
Self::Deque { items, maxlen } => {
write!(f, "deque([")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item.repr())?;
}
write!(f, "]")?;
if let Some(n) = maxlen {
write!(f, ", maxlen={n}")?;
}
write!(f, ")")
}
Self::EnumMember { class_name, member_name, value, kind } => match kind {
EnumKind::Int | EnumKind::Str | EnumKind::IntFlag => write!(f, "{value}"),
EnumKind::Plain | EnumKind::Flag => write!(f, "{class_name}.{member_name}"),
},
Self::DefaultDict(data) => {
write!(f, "defaultdict({}, {{", data.factory)?;
for (i, (k, v)) in data.items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v.repr())?;
}
write!(f, "}})")
}
Self::ChainMap(maps) => {
write!(f, "ChainMap(")?;
for (i, m) in maps.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{m}")?;
}
write!(f, ")")
}
Self::Template(_) => write!(f, "<string.Template object>"),
Self::DictView { dict, kind } => {
write!(f, "{}([", kind.type_name())?;
let guard = dict.lock();
for (i, (k, v)) in guard.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match kind {
DictViewKind::Keys => write!(f, "{}", k.to_value().repr())?,
DictViewKind::Values => write!(f, "{}", v.repr())?,
DictViewKind::Items => {
write!(f, "({}, {})", k.to_value().repr(), v.repr())?
}
}
}
write!(f, "])")
}
Self::Counter(map) => {
if map.is_empty() {
return write!(f, "Counter()");
}
let mut entries: Vec<(&ValueKey, &Self)> = map.iter().collect();
entries.sort_by(|a, b| {
let av = counter_value_as_i64(a.1);
let bv = counter_value_as_i64(b.1);
bv.cmp(&av)
});
write!(f, "Counter({{")?;
for (i, (k, v)) in entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v.repr())?;
}
write!(f, "}})")
}
Self::Decimal(d, kind) => format_decimal_str(f, d, *kind),
Self::Fraction(f_val) => write!(f, "{f_val}"),
Self::BoundMethod { receiver, method } => match receiver {
BoundMethodReceiver::Snapshot(value) => {
write!(f, "<built-in method {method} of {} object>", value.python_type_name())
}
BoundMethodReceiver::Place { root, .. } => {
write!(f, "<built-in method {method} of {root}>")
}
},
Self::BuiltinTypeMethod { type_name, method } => {
write!(f, "<method '{method}' of '{type_name}' objects>")
}
Self::ToolName(name) => write!(f, "<tool {name}>"),
Self::ExceptionType(name) => write!(f, "<class '{name}'>"),
Self::UnboundClassMethod { class, method } => {
write!(f, "<bound method {class}.{method}>")
}
Self::Lazy { kind, .. } => write!(f, "<{} object>", kind.type_name()),
Self::Generator { .. } => write!(f, "<generator object>"),
Self::BuiltinIter { kind, .. } => write!(f, "<{} object>", kind.type_name()),
Self::Partial(data) => write!(f, "functools.partial({})", data.func),
Self::OperatorGetter(g) => match &**g {
OperatorGetter::ItemGetter(items) => {
let rendered: Vec<String> = items.iter().map(|v| v.repr()).collect();
write!(f, "operator.itemgetter({})", rendered.join(", "))
}
OperatorGetter::AttrGetter(attrs) => {
let rendered: Vec<String> =
attrs.iter().map(|parts| format!("'{}'", parts.join("."))).collect();
write!(f, "operator.attrgetter({})", rendered.join(", "))
}
OperatorGetter::MethodCaller { name, .. } => {
write!(f, "operator.methodcaller('{name}')")
}
},
Self::LruCache(_) => write!(f, "<functools._lru_cache_wrapper>"),
Self::SingleDispatch(d) => write!(f, "<function {}>", d.name),
Self::RePattern(p) => write!(f, "re.compile({})", python_str_repr(p)),
Self::Slice(s) => write!(f, "slice({}, {}, {})", s.start, s.stop, s.step),
Self::MemoryView(_) => write!(f, "<memory>"),
Self::StringIO(_) => write!(f, "<_io.StringIO object>"),
Self::Property { .. } => write!(f, "<property object>"),
Self::Coroutine(c) => write!(f, "<coroutine object {}>", c.func.name),
}
}
}
fn format_decimal_str(
f: &mut fmt::Formatter<'_>,
d: &bigdecimal::BigDecimal,
kind: DecimalKind,
) -> fmt::Result {
use num_traits::Signed as _;
if let Some(special) = kind.special_str() {
return write!(f, "{special}");
}
let (mantissa, scale) = d.as_bigint_and_exponent();
let negative = mantissa.is_negative() || kind == DecimalKind::NegZero;
let digits = mantissa.abs().to_string();
let exp: i64 = -scale;
let sign = if negative { "-" } else { "" };
let n = digits.len() as i64;
let leftdigits = exp + n;
if exp <= 0 && leftdigits > -6 {
if exp == 0 {
write!(f, "{sign}{digits}")
} else if leftdigits > 0 {
let (int_part, frac_part) = digits.split_at(leftdigits as usize);
write!(f, "{sign}{int_part}.{frac_part}")
} else {
write!(f, "{sign}0.{}{digits}", "0".repeat(usize::try_from(-leftdigits).unwrap_or(0)))
}
} else {
let adjusted = leftdigits - 1;
if n == 1 {
write!(f, "{sign}{digits}E{adjusted:+}")
} else {
let (first, rest) = digits.split_at(1);
write!(f, "{sign}{first}.{rest}E{adjusted:+}")
}
}
}
fn write_bytes_literal(f: &mut fmt::Formatter<'_>, b: &[u8]) -> fmt::Result {
let has_single = b.contains(&b'\'');
let has_double = b.contains(&b'"');
let quote = if has_single && !has_double { b'"' } else { b'\'' };
write!(f, "b{}", quote as char)?;
for &byte in b {
match byte {
b'\\' => write!(f, "\\\\")?,
b'\n' => write!(f, "\\n")?,
b'\r' => write!(f, "\\r")?,
b'\t' => write!(f, "\\t")?,
b if b == quote => write!(f, "\\{}", b as char)?,
0x20..=0x7E => write!(f, "{}", byte as char)?,
_ => write!(f, "\\x{byte:02x}")?,
}
}
write!(f, "{}", quote as char)
}
#[must_use]
pub fn python_str_repr(s: &str) -> String {
use std::fmt::Write as _;
let quote = if s.contains('\'') && !s.contains('"') { '"' } else { '\'' };
let mut out = String::with_capacity(s.len() + 2);
out.push(quote);
for ch in s.chars() {
match ch {
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if c == quote => {
out.push('\\');
out.push(c);
}
c if char_is_printable(c) => out.push(c),
c => {
let u = c as u32;
if u <= 0xff {
let _ = write!(out, "\\x{u:02x}");
} else if u <= 0xffff {
let _ = write!(out, "\\u{u:04x}");
} else {
let _ = write!(out, "\\U{u:08x}");
}
}
}
}
out.push(quote);
out
}
#[must_use]
pub fn char_is_printable(c: char) -> bool {
use unicode_general_category::{GeneralCategory as G, get_general_category};
if c == ' ' {
return true;
}
!matches!(
get_general_category(c),
G::Control
| G::Format
| G::Surrogate
| G::PrivateUse
| G::Unassigned
| G::LineSeparator
| G::ParagraphSeparator
| G::SpaceSeparator
)
}
impl Value {
#[must_use]
pub fn short_type_name(name: &str) -> &str {
name.rsplit('.').next().unwrap_or(name)
}
#[must_use]
pub fn repr(&self) -> String {
match self {
Self::String(s) => python_str_repr(s),
Self::Date(d) => {
use chrono::Datelike;
format!("datetime.date({}, {}, {})", d.year(), d.month(), d.day())
}
Self::Exception(e) => {
let name = Self::short_type_name(&e.type_name);
let inner = e.args.iter().map(Self::repr).collect::<Vec<_>>().join(", ");
format!("{name}({inner})")
}
Self::EnumMember { class_name, member_name, value, .. } => {
format!("<{class_name}.{member_name}: {}>", value.repr())
}
Self::Decimal(..) => format!("Decimal('{self}')"),
Self::Fraction(fr) => format!("Fraction({}, {})", fr.numer(), fr.denom()),
Self::Time(t) => time_repr(t),
Self::DateTime { dt, tz_offset_secs } => datetime_repr(dt, *tz_offset_secs),
Self::TimeDelta(micros) => timedelta_repr(*micros),
Self::TimeZone(secs) => timezone_repr(*secs),
other => format!("{other}"),
}
}
}
impl Value {
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s.as_str()),
_ => None,
}
}
#[must_use]
pub fn as_int(&self) -> Option<i64> {
match self {
Self::Int(i) => Some(*i),
Self::BigInt(b) => i64::try_from(b.as_ref()).ok(),
_ => None,
}
}
#[must_use]
pub fn as_float(&self) -> Option<f64> {
match self {
Self::Float(f) => Some(*f),
#[expect(
clippy::cast_precision_loss,
reason = "matches Python's `float(int)` semantic: the standard \
library is lossy for ints beyond 2^53 and we faithfully \
reproduce that"
)]
Self::Int(i) => Some(*i as f64),
Self::BigInt(b) => {
use num_traits::ToPrimitive as _;
b.to_f64()
}
Self::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
Self::Decimal(d, _) => {
use num_traits::ToPrimitive as _;
d.to_f64()
}
Self::Fraction(fr) => {
use num_traits::ToPrimitive as _;
fr.to_f64()
}
Self::EnumMember { value, kind: EnumKind::Int | EnumKind::IntFlag, .. } => {
value.as_float()
}
_ => None,
}
}
#[must_use]
pub const fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(b) => Some(*b),
_ => None,
}
}
#[must_use]
pub fn as_list(&self) -> Option<parking_lot::MutexGuard<'_, ListBody>> {
match self {
Self::List(items) => Some(items.lock()),
_ => None,
}
}
#[must_use]
pub fn dict_view_elements(&self) -> Option<Vec<Self>> {
let Self::DictView { dict, kind } = self else { return None };
let guard = dict.lock();
Some(match kind {
DictViewKind::Keys => guard.keys().map(ValueKey::to_value).collect(),
DictViewKind::Values => guard.values().cloned().collect(),
DictViewKind::Items => {
guard.iter().map(|(k, v)| Self::Tuple(vec![k.to_value(), v.clone()])).collect()
}
})
}
#[must_use]
pub const fn as_dict(&self) -> Option<&SharedDict> {
match self {
Self::Dict(map) | Self::OrderedDict(map) => Some(map),
_ => None,
}
}
pub fn try_into_string(self) -> Result<String, Self> {
match self {
Self::String(s) => Ok(s.to_string()),
other => Err(other),
}
}
pub fn try_into_list(self) -> Result<Vec<Self>, Self> {
match self {
Self::List(items) => Ok(match Arc::try_unwrap(items) {
Ok(mutex) => mutex.into_inner().into_items(),
Err(shared) => shared.lock().clone(),
}),
other => Err(other),
}
}
pub fn try_into_dict(self) -> Result<IndexMap<ValueKey, Self>, Self> {
match self {
Self::Dict(map) | Self::OrderedDict(map) => Ok(map.lock().clone()),
other => Err(other),
}
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Self::Bool(v)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Self::Int(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Self::Int(i64::from(v))
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Self::Float(v)
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Self::String(v.into())
}
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Self::String(v.into())
}
}
impl From<Vec<Self>> for Value {
fn from(v: Vec<Self>) -> Self {
Self::List(shared_list(v))
}
}
impl From<IndexMap<ValueKey, Self>> for Value {
fn from(v: IndexMap<ValueKey, Self>) -> Self {
Self::Dict(shared_dict(v))
}
}
impl<T: Into<Self>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
v.map_or(Self::None, Into::into)
}
}
impl Value {
pub fn from_json(json: serde_json::Value) -> Self {
match json {
serde_json::Value::Null => Self::None,
serde_json::Value::Bool(b) => Self::Bool(b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Self::Int(i)
} else {
let raw = n.to_string();
if raw.contains(['.', 'e', 'E']) {
n.as_f64().map(Self::Float).unwrap_or(Self::None)
} else {
raw.parse::<num_bigint::BigInt>().map_or_else(
|_| n.as_f64().map(Self::Float).unwrap_or(Self::None),
int_from_bigint,
)
}
}
}
serde_json::Value::String(s) => Self::String(s.into()),
serde_json::Value::Array(arr) => {
Self::List(shared_list(arr.into_iter().map(Self::from_json).collect()))
}
serde_json::Value::Object(obj) => {
let mut map = IndexMap::new();
for (k, v) in obj {
map.insert(ValueKey::String(k.into()), Self::from_json(v));
}
Self::Dict(shared_dict(map))
}
}
}
pub fn to_json(&self) -> Result<serde_json::Value, crate::error::InterpreterError> {
use serde_json::Value as J;
let array = |items: &[Self]| -> Result<J, crate::error::InterpreterError> {
Ok(J::Array(items.iter().map(Self::to_json).collect::<Result<_, _>>()?))
};
Ok(match self {
Self::None => J::Null,
Self::Bool(b) => J::Bool(*b),
Self::Int(i) => serde_json::json!(*i),
Self::BigInt(i) => J::String(i.to_string()),
Self::Float(f) => serde_json::json!(*f),
Self::String(s) => J::String(s.to_string()),
Self::Bytes(b) => serde_json::json!(b),
Self::List(items) => {
let Some(_cycle) = crate::cycle::json_enter(Arc::as_ptr(items) as usize) else {
return Err(crate::error::InterpreterError::ValueError(
"Circular reference detected".into(),
));
};
array(&items.lock())?
}
Self::Tuple(items) => array(items)?,
Self::Set(body) => array(&body.lock().iter_ordered())?,
Self::Frozenset(body) => array(&body.iter_ordered())?,
Self::Deque { items, .. } => {
J::Array(items.iter().map(Self::to_json).collect::<Result<_, _>>()?)
}
Self::Dict(map) => {
let Some(_cycle) = crate::cycle::json_enter(Arc::as_ptr(map) as usize) else {
return Err(crate::error::InterpreterError::ValueError(
"Circular reference detected".into(),
));
};
json_object(map.lock().iter())?
}
Self::OrderedDict(map) => {
let Some(_cycle) = crate::cycle::json_enter(Arc::as_ptr(map) as usize) else {
return Err(crate::error::InterpreterError::ValueError(
"Circular reference detected".into(),
));
};
json_object(map.lock().iter())?
}
Self::Counter(map) => json_object(map.iter())?,
Self::DefaultDict(data) => json_object(data.items.iter())?,
Self::Decimal(d, _) => {
serde_json::from_str(&d.to_string()).unwrap_or_else(|_| J::String(d.to_string()))
}
Self::Fraction(fr) => {
use num_traits::ToPrimitive as _;
serde_json::json!(fr.to_f64().unwrap_or(f64::NAN))
}
Self::EnumMember { value, .. } => value.to_json()?,
Self::Date(d) => J::String(d.format("%Y-%m-%d").to_string()),
Self::DateTime { dt, .. } => J::String(dt.format("%Y-%m-%dT%H:%M:%S").to_string()),
Self::Time(t) => J::String(t.format("%H:%M:%S").to_string()),
#[expect(clippy::cast_precision_loss, reason = "seconds as f64 is the host JSON form")]
Self::TimeDelta(us) => serde_json::json!(*us as f64 / 1_000_000.0),
other => {
return Err(crate::error::InterpreterError::TypeError(format!(
"Object of type {} is not JSON serializable",
other.type_name()
)));
}
})
}
}
fn json_object<'a, I>(entries: I) -> Result<serde_json::Value, crate::error::InterpreterError>
where
I: Iterator<Item = (&'a ValueKey, &'a Value)>,
{
let mut obj = serde_json::Map::new();
for (k, v) in entries {
let key = match k {
ValueKey::String(s) => s.to_string(),
other => format!("{other}"),
};
obj.insert(key, v.to_json()?);
}
Ok(serde_json::Value::Object(obj))
}
impl Value {
pub fn to_key(&self) -> Result<ValueKey, crate::error::InterpreterError> {
match crate::eval::literals::value_to_key(self) {
Ok(key) => Ok(key),
Err(crate::error::EvalError::Interpreter(e)) => Err(e),
Err(_) => Err(crate::error::InterpreterError::TypeError(format!(
"unhashable type: '{}'",
self.type_name()
))),
}
}
}
impl ValueKey {
#[must_use]
pub fn to_value(&self) -> Value {
match self {
Self::None => Value::None,
Self::Ellipsis => Value::Ellipsis,
Self::Bool(b) => Value::Bool(*b),
Self::Int(i) => Value::Int(*i),
Self::BigInt(i) => crate::value::int_from_bigint(i.clone()),
Self::Float(bits) => Value::Float(f64::from_bits(*bits)),
Self::Complex(re, im) => Value::Complex(Box::new(num_complex::Complex64::new(
f64::from_bits(*re),
f64::from_bits(*im),
))),
Self::String(s) => Value::String(s.clone()),
Self::Tuple(items) => Value::Tuple(items.iter().map(Self::to_value).collect()),
Self::Frozenset(items) => Value::Frozenset(std::sync::Arc::new(
crate::pyset::SetBody::from_items(items.iter().map(Self::to_value).collect()),
)),
Self::Instance { value, .. } => (**value).clone(),
Self::Date(d) => Value::Date(*d),
Self::Time(t) => Value::Time(*t),
Self::TimeDelta(m) => Value::TimeDelta(*m),
Self::DateTime { dt, tz_offset_secs } => {
Value::DateTime { dt: *dt, tz_offset_secs: *tz_offset_secs }
}
Self::Decimal(d) => Value::Decimal(d.clone(), DecimalKind::Normal),
Self::Fraction(fr) => Value::Fraction(fr.clone()),
}
}
}
impl fmt::Display for ValueKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "None"),
Self::Ellipsis => write!(f, "Ellipsis"),
Self::Bool(true) => write!(f, "True"),
Self::Bool(false) => write!(f, "False"),
Self::Int(i) => write!(f, "{i}"),
Self::BigInt(i) => write!(f, "{i}"),
Self::Float(bits) => write_python_float(f, f64::from_bits(*bits)),
Self::Complex(re, im) => write!(
f,
"{}",
format_complex(&num_complex::Complex64::new(
f64::from_bits(*re),
f64::from_bits(*im)
))
),
Self::String(s) => write!(f, "'{s}'"),
Self::Tuple(items) => {
write!(f, "(")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{item}")?;
}
if items.len() == 1 {
write!(f, ",")?;
}
write!(f, ")")
}
Self::Frozenset(items) => {
if items.is_empty() {
return write!(f, "frozenset()");
}
let values: Vec<Value> = items.iter().map(ValueKey::to_value).collect();
let order = crate::pyhash::cpython_set_order_indices(&values)
.unwrap_or_else(|| (0..items.len()).collect());
write!(f, "frozenset({{")?;
for (i, &idx) in order.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", items[idx])?;
}
write!(f, "}})")
}
Self::Instance { value, .. } => write!(f, "{value}"),
Self::Date(_)
| Self::Time(_)
| Self::TimeDelta(_)
| Self::DateTime { .. }
| Self::Decimal(_)
| Self::Fraction(_) => {
write!(f, "{}", self.to_value())
}
}
}
}