use std::{collections::BTreeMap, fmt, sync::Arc};
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use compact_str::CompactString;
use indexmap::IndexMap;
use parking_lot::Mutex;
pub type SharedList = Arc<Mutex<Vec<Value>>>;
pub type SharedFields = Arc<Mutex<BTreeMap<String, Value>>>;
#[inline]
#[must_use]
pub fn shared_list(items: Vec<Value>) -> SharedList {
Arc::new(Mutex::new(items))
}
#[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)),
}
}
#[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))),
_ => None,
}
}
#[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_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())
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Value {
None,
NotImplemented,
Bool(bool),
Int(i64),
BigInt(Box<num_bigint::BigInt>),
Float(f64),
String(CompactString),
Bytes(Vec<u8>),
List(
#[serde(serialize_with = "serialize_shared_list")]
#[serde(deserialize_with = "deserialize_shared_list")]
SharedList,
),
Tuple(Vec<Self>),
Dict(
#[serde(serialize_with = "serialize_dict", deserialize_with = "deserialize_dict")]
IndexMap<ValueKey, Self>,
),
Set(Vec<Self>),
Function(Arc<FunctionDef>),
Lambda(Arc<LambdaDef>),
Range { start: i64, stop: i64, step: i64 },
Exception(ExceptionValue),
#[serde(skip)]
LazyProxy(crate::tools::lazy_proxy::LazyProxy),
Lazy { items: Vec<Self>, cursor_id: u64 },
Type(String),
Class(String),
Module(String),
Instance(InstanceValue),
Partial(Box<PartialData>),
LruCache(std::sync::Arc<LruCacheData>),
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>),
EnumMember { class_name: String, member_name: String, value: Box<Self>, kind: EnumKind },
ReMatch(Box<MatchValue>),
Super { defining_class: String, instance: Box<InstanceValue> },
Counter(
#[serde(serialize_with = "serialize_dict", deserialize_with = "deserialize_dict")]
IndexMap<ValueKey, Self>,
),
Decimal(Box<bigdecimal::BigDecimal>),
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)]
pub struct LruCacheData {
pub func: Value,
pub maxsize: Option<usize>,
pub cache: Mutex<IndexMap<Vec<ValueKey>, Value>>,
}
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()) })
}
}
#[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 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 annotations: Vec<String>,
#[serde(default)]
pub dataclass_fields: Option<Vec<DataclassField>>,
#[serde(default)]
pub frozen: bool,
#[serde(default)]
pub order: bool,
#[serde(default)]
pub slots: bool,
#[serde(default)]
pub slot_names: Vec<String>,
}
#[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,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EnumKind {
Plain,
Int,
Str,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PropertyDef {
pub getter: FunctionDef,
pub setter: Option<FunctionDef>,
pub deleter: Option<FunctionDef>,
}
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::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::Tuple(a), Self::Tuple(b)) | (Self::Set(a), Self::Set(b)) => a == b,
(Self::Dict(a), Self::Dict(b)) => a == b,
(
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,
Bool(bool),
Int(i64),
BigInt(num_bigint::BigInt),
Float(u64),
String(CompactString),
Tuple(Vec<Self>),
Instance {
hash: i64,
value: Box<Value>,
},
}
impl PartialEq for ValueKey {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::None, Self::None) => 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::String(a), Self::String(b)) => a == b,
(Self::Tuple(a), Self::Tuple(b)) => a == b,
(Self::Instance { value: a, .. }, Self::Instance { value: b, .. }) => {
crate::eval::operations::values_equal_pub(a, b)
}
_ => false,
}
}
}
impl Eq for ValueKey {}
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;
match self {
Self::None => NONE_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) => {
FLOAT_TAG.hash(state);
bits.hash(state);
}
Self::String(s) => {
STRING_TAG.hash(state);
s.hash(state);
}
Self::Tuple(items) => {
TUPLE_TAG.hash(state);
items.hash(state);
}
Self::Instance { hash, .. } => {
INSTANCE_TAG.hash(state);
hash.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>>,
}
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,
}
}
#[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(Value::Exception).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),
}
}
#[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: impl std::fmt::Display) -> Self {
Self::new("KeyError", format!("{key}"))
}
#[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,
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 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,
}
#[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,
}
#[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>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Param {
pub name: String,
}
impl Value {
#[inline]
#[must_use]
pub fn is_truthy(&self) -> bool {
match self {
Self::None | Self::NotImplemented => false,
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::List(l) => !l.lock().is_empty(),
Self::Tuple(t) => !t.is_empty(),
Self::Dict(d) => !d.is_empty(),
Self::Set(s) => !s.is_empty(),
Self::Function(_)
| Self::Lambda(_)
| Self::Exception(_)
| Self::LazyProxy(_)
| Self::Type(_)
| Self::Class(_)
| Self::Module(_)
| Self::Instance(_)
| Self::ModuleFunction { .. }
| Self::Date(_)
| Self::ReMatch(_)
| Self::Super { .. }
| Self::DateTime { .. }
| Self::Time(_)
| Self::TimeZone(_)
| Self::HashDigest { .. }
| Self::BoundMethod { .. }
| Self::BuiltinTypeMethod { .. }
| Self::BuiltinName(_)
| Self::ToolName(_)
| Self::ExceptionType(_)
| Self::UnboundClassMethod { .. }
| Self::Lazy { .. }
| Self::Partial { .. }
| Self::LruCache(_) => 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::EnumMember { value, .. } => value.is_truthy(),
Self::Decimal(d) => !d.is_zero(),
Self::Fraction(f) => !f.numer().is_zero(),
Self::Range { start, stop, step } => {
if *step > 0 {
start < stop
} else {
start > stop
}
}
}
}
#[must_use]
pub const fn type_name(&self) -> &'static str {
match self {
Self::None => "NoneType",
Self::NotImplemented => "NotImplementedType",
Self::Bool(_) => "bool",
Self::Int(_) | Self::BigInt(_) => "int",
Self::Float(_) => "float",
Self::String(_) => "str",
Self::Bytes(_) => "bytes",
Self::List(_) => "list",
Self::Tuple(_) => "tuple",
Self::Dict(_) => "dict",
Self::Set(_) => "set",
Self::Function(_) | Self::Lambda(_) => "function",
Self::Range { .. } => "range",
Self::Exception(_) => "Exception",
Self::LazyProxy(_) => "LazyProxy",
Self::Type(_) | Self::Class(_) | Self::ExceptionType(_) => "type",
Self::Module(_) => "module",
Self::Instance(_) => "object",
Self::ModuleFunction { .. }
| Self::BoundMethod { .. }
| Self::BuiltinTypeMethod { .. }
| Self::BuiltinName(_)
| Self::ToolName(_)
| Self::UnboundClassMethod { .. } => "builtin_function_or_method",
Self::Date(_) => "date",
Self::ReMatch(_) => "re.Match",
Self::Super { .. } => "super",
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::EnumMember { .. } => "enum",
Self::Decimal(_) => "Decimal",
Self::Fraction(_) => "Fraction",
Self::Lazy { .. } => "generator",
Self::Partial { .. } => "functools.partial",
Self::LruCache(_) => "functools._lru_cache_wrapper",
}
}
#[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 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}")
}
}
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::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::String(s) => write!(f, "{s}"),
Self::Bytes(b) => {
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)
}
Self::List(items) => {
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::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.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v.repr())?;
}
write!(f, "}}")
}
Self::Set(items) => {
write!(f, "{{")?;
for (i, item) in items.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::LazyProxy(p) => write!(f, "<LazyProxy tool={}>", p.tool_name),
Self::Type(n) | Self::Class(n) => write!(f, "<class '{n}'>"),
Self::Module(n) => write!(f, "<module '{n}'>"),
Self::Instance(inst) => write!(f, "<{} object>", inst.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::DateTime { dt, tz_offset_secs } => {
write!(f, "{}", dt.format("%Y-%m-%d %H:%M:%S"))?;
if let Some(secs) = tz_offset_secs {
write_tz_offset(f, *secs)?;
}
Ok(())
}
Self::Time(t) => write!(f, "{}", t.format("%H:%M:%S")),
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, .. } => {
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::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) => format_decimal_str(f, d),
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 { .. } => write!(f, "<generator object>"),
Self::Partial(data) => write!(f, "functools.partial({})", data.func),
Self::LruCache(_) => write!(f, "<functools._lru_cache_wrapper>"),
}
}
}
fn format_decimal_str(f: &mut fmt::Formatter<'_>, d: &bigdecimal::BigDecimal) -> fmt::Result {
write!(f, "{}", d.to_plain_string())
}
impl Value {
#[must_use]
pub fn repr(&self) -> String {
match self {
Self::String(s) => format!("'{s}'"),
Self::Date(d) => {
use chrono::Datelike;
format!("datetime.date({}, {}, {})", d.year(), d.month(), d.day())
}
Self::Exception(e) => format!("{}('{}')", e.type_name, e.message),
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()
}
_ => 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<'_, Vec<Self>>> {
match self {
Self::List(items) => Some(items.lock()),
_ => None,
}
}
#[must_use]
pub const fn as_dict(&self) -> Option<&IndexMap<ValueKey, Self>> {
match self {
Self::Dict(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(),
Err(shared) => shared.lock().clone(),
}),
other => Err(other),
}
}
pub fn try_into_dict(self) -> Result<IndexMap<ValueKey, Self>, Self> {
match self {
Self::Dict(map) => Ok(map),
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(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) => n
.as_i64()
.map(Self::Int)
.or_else(|| n.as_f64().map(Self::Float))
.unwrap_or(Self::None),
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(map)
}
}
}
#[must_use]
pub fn to_json(&self) -> serde_json::Value {
match self {
Self::Bool(b) => serde_json::Value::Bool(*b),
Self::Int(i) => serde_json::json!(*i),
Self::BigInt(i) => serde_json::Value::String(i.to_string()),
Self::Float(f) => serde_json::json!(*f),
Self::String(s) => serde_json::Value::String(s.to_string()),
Self::Bytes(b) => serde_json::json!(b),
Self::List(items) => {
let guard = items.lock();
serde_json::Value::Array(guard.iter().map(Self::to_json).collect())
}
Self::Tuple(items) | Self::Set(items) => {
serde_json::Value::Array(items.iter().map(Self::to_json).collect())
}
Self::Dict(map) => {
let mut obj = serde_json::Map::new();
for (k, v) in map {
let key = match k {
ValueKey::String(s) => s.to_string(),
other => format!("{other}"),
};
obj.insert(key, v.to_json());
}
serde_json::Value::Object(obj)
}
_ => serde_json::Value::Null,
}
}
}
impl ValueKey {
#[must_use]
pub fn to_value(&self) -> Value {
match self {
Self::None => Value::None,
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::String(s) => Value::String(s.clone()),
Self::Tuple(items) => Value::Tuple(items.iter().map(Self::to_value).collect()),
Self::Instance { value, .. } => (**value).clone(),
}
}
}
impl fmt::Display for ValueKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "None"),
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::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::Instance { value, .. } => write!(f, "{value}"),
}
}
}