From 90f7537f51fb4ba97644a61eef291639e3a807ce Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Tue, 30 Apr 2024 15:22:10 +0200 Subject: [PATCH 01/57] Add experimental typeshare support for Python Thanks to the prior work of @adriangb: https://github.com/1Password/typeshare/pull/25 --- Cargo.lock | 23 ++ cli/src/config.rs | 7 + cli/src/main.rs | 10 +- core/Cargo.toml | 3 + core/src/language/mod.rs | 6 +- core/src/language/python.rs | 776 ++++++++++++++++++++++++++++++++++++ 6 files changed, 821 insertions(+), 4 deletions(-) create mode 100644 core/src/language/python.rs diff --git a/Cargo.lock b/Cargo.lock index 58cddc9f..5d4c84f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -581,6 +590,12 @@ dependencies = [ "serde", ] +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + [[package]] name = "typeshare" version = "1.0.2" @@ -618,6 +633,7 @@ name = "typeshare-core" version = "1.9.2" dependencies = [ "anyhow", + "convert_case", "expect-test", "itertools", "joinery", @@ -627,6 +643,7 @@ dependencies = [ "quote", "syn", "thiserror", + "topological-sort", ] [[package]] @@ -635,6 +652,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/cli/src/config.rs b/cli/src/config.rs index 9305a9b5..2cb64e03 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -9,6 +9,12 @@ use std::{ const DEFAULT_CONFIG_FILE_NAME: &str = "typeshare.toml"; +#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(default)] +pub struct PythonParams { + pub type_mappings: HashMap, +} + #[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(default)] pub struct KotlinParams { @@ -59,6 +65,7 @@ pub(crate) struct Config { pub typescript: TypeScriptParams, pub kotlin: KotlinParams, pub scala: ScalaParams, + pub python: PythonParams, #[cfg(feature = "go")] pub go: GoParams, } diff --git a/cli/src/main.rs b/cli/src/main.rs index 066b0c1d..de32d153 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -8,9 +8,9 @@ use ignore::types::TypesBuilder; use ignore::WalkBuilder; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::{fs, path::Path}; -use typeshare_core::language::GenericConstraints; #[cfg(feature = "go")] use typeshare_core::language::Go; +use typeshare_core::language::{GenericConstraints, Python}; use typeshare_core::{ language::{Kotlin, Language, Scala, SupportedLanguage, Swift, TypeScript}, parser::ParsedData, @@ -35,10 +35,10 @@ const ARG_OUTPUT_FILE: &str = "output-file"; const ARG_FOLLOW_LINKS: &str = "follow-links"; #[cfg(feature = "go")] -const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "go"]; +const AVAILABLE_LANGUAGES: [&str; 6] = ["kotlin", "scala", "swift", "typescript", "go", "python"]; #[cfg(not(feature = "go"))] -const AVAILABLE_LANGUAGES: [&str; 4] = ["kotlin", "scala", "swift", "typescript"]; +const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "python"]; fn build_command() -> Command<'static> { command!("typeshare") @@ -225,6 +225,10 @@ fn main() { type_mappings: config.typescript.type_mappings, ..Default::default() }), + Some(SupportedLanguage::Python) => Box::new(Python { + type_mappings: config.python.type_mappings, + ..Default::default() + }), #[cfg(feature = "go")] Some(SupportedLanguage::Go) => Box::new(Go { package: config.go.package, diff --git a/core/Cargo.toml b/core/Cargo.toml index 6dcddf02..892c5ffd 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,6 +15,9 @@ thiserror = "1.0.40" itertools = "0.10" lazy_format = "1.8" joinery = "2" +topological-sort = { version = "0.2.2"} +once_cell = { version = "1"} +convert_case = { version = "0.6.0"} [dev-dependencies] anyhow = "1" diff --git a/core/src/language/mod.rs b/core/src/language/mod.rs index c96ba5ac..37b523f3 100644 --- a/core/src/language/mod.rs +++ b/core/src/language/mod.rs @@ -9,6 +9,7 @@ use std::{collections::HashMap, fmt::Debug, io::Write, str::FromStr}; mod go; mod kotlin; +mod python; mod scala; mod swift; mod typescript; @@ -16,6 +17,7 @@ mod typescript; use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; pub use go::Go; pub use kotlin::Kotlin; +pub use python::Python; pub use scala::Scala; pub use swift::GenericConstraints; pub use swift::Swift; @@ -30,13 +32,14 @@ pub enum SupportedLanguage { Scala, Swift, TypeScript, + Python, } impl SupportedLanguage { /// Returns an iterator over all supported language variants. pub fn all_languages() -> impl Iterator { use SupportedLanguage::*; - [Go, Kotlin, Scala, Swift, TypeScript].into_iter() + [Go, Kotlin, Scala, Swift, TypeScript, Python].into_iter() } } @@ -50,6 +53,7 @@ impl FromStr for SupportedLanguage { "scala" => Ok(Self::Scala), "swift" => Ok(Self::Swift), "typescript" => Ok(Self::TypeScript), + "python" => Ok(Self::Python), _ => Err(ParseError::UnsupportedLanguage(s.into())), } } diff --git a/core/src/language/python.rs b/core/src/language/python.rs new file mode 100644 index 00000000..6554754b --- /dev/null +++ b/core/src/language/python.rs @@ -0,0 +1,776 @@ +use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; +use crate::{ + language::Language, + rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, +}; +use once_cell::sync::Lazy; +use std::cell::RefCell; +use std::collections::hash_map::Entry; +use std::collections::HashSet; +use std::hash::Hash; +use std::{collections::HashMap, io::Write}; + +use convert_case::{Case, Casing}; +use topological_sort::TopologicalSort; + +#[derive(Debug, Default)] +pub struct Module { + // HashMap + imports: HashMap>, + // HashMap> + // Used to lay out runtime references in the module + // such that it can be read top to bottom + globals: HashMap>, + type_variables: HashSet, +} + +#[derive(Debug)] +struct GenerationError(String); + +impl Module { + // Idempotently insert an import + fn add_import(&mut self, module: String, identifier: String) { + self.imports.entry(module).or_default().insert(identifier); + } + fn add_global(&mut self, identifier: String, deps: Vec) { + match self.globals.entry(identifier) { + Entry::Occupied(mut e) => e.get_mut().extend_from_slice(&deps), + Entry::Vacant(e) => { + e.insert(deps); + } + } + } + fn add_type_var(&mut self, name: String) { + self.add_import("typing".to_string(), "TypeVar".to_string()); + self.type_variables.insert(name); + } + fn get_type_vars(&mut self, n: usize) -> Vec { + let vars: Vec = (0..n) + .into_iter() + .map(|i| { + if i == 0 { + "T".to_string() + } else { + format!("T{}", i) + } + }) + .collect(); + vars.iter().for_each(|tv| self.add_type_var(tv.clone())); + vars + } + // Rust lets you declare type aliases before the struct they point to. + // But in Python we need the struct to come first. + // So we need to topologically sort the globals so that type aliases + // always come _after_ the struct/enum they point to. + fn topologically_sorted_globals(&self) -> Result, GenerationError> { + let mut ts: TopologicalSort = TopologicalSort::new(); + for (identifier, dependencies) in &self.globals { + for dependency in dependencies { + ts.add_dependency(dependency.clone(), identifier.clone()) + } + } + let mut res: Vec = Vec::new(); + loop { + let mut level = ts.pop_all(); + level.sort(); + res.extend_from_slice(&level); + if level.is_empty() { + if !ts.is_empty() { + return Err(GenerationError("Cyclical runtime dependency".to_string())); + } + break; + } + } + let existing: HashSet<&String> = HashSet::from_iter(res.iter()); + let mut missing: Vec = self + .globals + .iter() + .map(|(k, _)| k.clone()) + .filter(|k| !existing.contains(k)) + .collect(); + missing.sort(); + res.extend(missing); + Ok(res) + } +} + +#[derive(Debug, Clone)] +enum ParsedRusthThing<'a> { + Struct(&'a RustStruct), + Enum(&'a RustEnum), + TypeAlias(&'a RustTypeAlias), +} + +// Collect unique type vars from an enum field +// Since we explode enums into unions of types, we need to extract all of the generics +// used by each individual field +// We do this by exploring each field's type and comparing against the generics used by the enum +// itself +fn collect_generics_for_variant(variant_type: &RustType, generics: &[String]) -> Vec { + let mut all = vec![]; + match variant_type { + RustType::Generic { id, parameters } => { + if generics.contains(id) { + all.push(id.clone()) + } + // Recurse into the params for the case of `Foo(HashMap)` + for param in parameters { + all.extend(collect_generics_for_variant(param, generics)) + } + } + RustType::Simple { id } => { + if generics.contains(id) { + all.push(id.clone()) + } + } + RustType::Special(special) => match &special { + SpecialRustType::HashMap(key_type, value_type) => { + all.extend(collect_generics_for_variant(key_type, generics)); + all.extend(collect_generics_for_variant(value_type, generics)); + } + SpecialRustType::Option(some_type) => { + all.extend(collect_generics_for_variant(some_type, generics)); + } + SpecialRustType::Vec(value_type) => { + all.extend(collect_generics_for_variant(value_type, generics)); + } + _ => {} + }, + } + // Remove any duplicates + // E.g. Foo(HashMap) should only produce a single type var + dedup(&mut all); + all +} + +fn dedup(v: &mut Vec) { + // note the Copy constraint + let mut uniques = HashSet::new(); + v.retain(|e| uniques.insert(e.clone())); +} + +/// All information needed to generate Python type-code +#[derive(Default)] +pub struct Python { + /// Mappings from Rust type names to Python type names + pub type_mappings: HashMap, + pub module: RefCell, +} + +impl Language for Python { + fn type_map(&mut self) -> &HashMap { + &self.type_mappings + } + fn generate_types( + &mut self, + w: &mut dyn Write, + data: &crate::parser::ParsedData, + ) -> std::io::Result<()> { + let mut globals: Vec; + { + let mut module = self.module.borrow_mut(); + for alias in &data.aliases { + let thing = ParsedRusthThing::TypeAlias(alias); + let identifier = self.get_identifier(thing); + match &alias.r#type { + RustType::Generic { id, parameters: _ } => { + module.add_global(identifier, vec![id.clone()]) + } + RustType::Simple { id } => module.add_global(identifier, vec![id.clone()]), + RustType::Special(_) => {} + } + } + for strct in &data.structs { + let thing = ParsedRusthThing::Struct(strct); + let identifier = self.get_identifier(thing); + module.add_global(identifier, vec![]); + } + for enm in &data.enums { + let thing = ParsedRusthThing::Enum(enm); + let identifier = self.get_identifier(thing); + module.add_global(identifier, vec![]); + } + globals = data + .aliases + .iter() + .map(ParsedRusthThing::TypeAlias) + .chain(data.structs.iter().map(ParsedRusthThing::Struct)) + .chain(data.enums.iter().map(ParsedRusthThing::Enum)) + .collect(); + let sorted_identifiers = module.topologically_sorted_globals().unwrap(); + globals.sort_by(|a, b| { + let identifier_a = self.get_identifier(a.clone()); + let identifier_b = self.get_identifier(b.clone()); + let pos_a = sorted_identifiers + .iter() + .position(|o| o.eq(&identifier_a)) + .unwrap_or(0); + let pos_b = sorted_identifiers + .iter() + .position(|o| o.eq(&identifier_b)) + .unwrap_or(0); + pos_a.cmp(&pos_b) + }); + } + let mut body: Vec = Vec::new(); + for thing in globals { + match thing { + ParsedRusthThing::Enum(e) => self.write_enum(&mut body, e)?, + ParsedRusthThing::Struct(rs) => self.write_struct(&mut body, rs)?, + ParsedRusthThing::TypeAlias(t) => self.write_type_alias(&mut body, t)?, + }; + } + self.begin_file(w)?; + let _ = w.write(&body)?; + Ok(()) + } + + fn format_generic_type( + &mut self, + base: &String, + parameters: &[RustType], + generic_types: &[String], + ) -> Result { + if let Some(mapped) = self.type_map().get(base) { + Ok(mapped.into()) + } else { + let parameters: Result, RustTypeFormatError> = parameters + .iter() + .map(|p| self.format_type(p, generic_types)) + .collect(); + let parameters = parameters?; + Ok(format!( + "{}{}", + self.format_simple_type(base, generic_types)?, + (!parameters.is_empty()) + .then(|| format!("[{}]", parameters.join(", "))) + .unwrap_or_default() + )) + } + } + + fn format_simple_type( + &mut self, + base: &String, + _generic_types: &[String], + ) -> Result { + self.add_imports(base); + Ok(if let Some(mapped) = self.type_map().get(base) { + mapped.into() + } else { + base.into() + }) + } + + fn format_special_type( + &mut self, + special_ty: &SpecialRustType, + generic_types: &[String], + ) -> Result { + match special_ty { + SpecialRustType::Vec(rtype) => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "List".to_string()); + Ok(format!("List[{}]", self.format_type(rtype, generic_types)?)) + } + // We add optionality above the type formatting level + SpecialRustType::Option(rtype) => self.format_type(rtype, generic_types), + SpecialRustType::HashMap(rtype1, rtype2) => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "Dict".to_string()); + Ok(format!( + "Dict[{}, {}]", + match rtype1.as_ref() { + RustType::Simple { id } if generic_types.contains(id) => { + return Err(RustTypeFormatError::GenericKeyForbiddenInTS(id.clone())); + } + _ => self.format_type(rtype1, generic_types)?, + }, + self.format_type(rtype2, generic_types)? + )) + } + SpecialRustType::Unit => Ok("None".into()), + SpecialRustType::String => Ok("str".into()), + SpecialRustType::I8 + | SpecialRustType::U8 + | SpecialRustType::I16 + | SpecialRustType::U16 + | SpecialRustType::I32 + | SpecialRustType::U32 + | SpecialRustType::I54 + | SpecialRustType::U53 => Ok("int".into()), + SpecialRustType::F32 | SpecialRustType::F64 => Ok("float".into()), + SpecialRustType::Bool => Ok("bool".into()), + SpecialRustType::U64 + | SpecialRustType::I64 + | SpecialRustType::ISize + | SpecialRustType::USize => { + panic!("64 bit types not allowed in Typeshare") + } + SpecialRustType::Array(_, _) => todo!(), + SpecialRustType::Slice(_) => todo!(), + SpecialRustType::Char => todo!(), + } + } + + fn begin_file(&mut self, w: &mut dyn Write) -> std::io::Result<()> { + let module = self.module.borrow(); + let mut type_var_names: Vec = module.type_variables.iter().cloned().collect(); + type_var_names.sort(); + let type_vars: Vec = type_var_names + .iter() + .map(|name| format!("{} = TypeVar(\"{}\")", name, name)) + .collect(); + let mut imports = vec![]; + for (import_module, identifiers) in &module.imports { + let mut identifier_vec = identifiers.iter().cloned().collect::>(); + identifier_vec.sort(); + imports.push(format!( + "from {} import {}", + import_module, + identifier_vec.join(", ") + )) + } + imports.sort(); + writeln!(w, "\"\"\"")?; + writeln!(w, " Generated by typeshare {}", env!("CARGO_PKG_VERSION"))?; + writeln!(w, "\"\"\"")?; + writeln!(w, "from __future__ import annotations\n").unwrap(); + writeln!(w, "{}\n", imports.join("\n"))?; + match type_vars.is_empty() { + true => writeln!(w).unwrap(), + false => writeln!(w, "{}\n\n", type_vars.join("\n")).unwrap(), + }; + Ok(()) + } + + fn write_type_alias(&mut self, w: &mut dyn Write, ty: &RustTypeAlias) -> std::io::Result<()> { + let r#type = self + .format_type(&ty.r#type, ty.generic_types.as_slice()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + writeln!( + w, + "{}{} = {}\n\n", + ty.id.renamed, + (!ty.generic_types.is_empty()) + .then(|| format!("[{}]", ty.generic_types.join(", "))) + .unwrap_or_default(), + r#type, + )?; + + self.write_comments(w, true, &ty.comments, 1)?; + + Ok(()) + } + + fn write_struct(&mut self, w: &mut dyn Write, rs: &RustStruct) -> std::io::Result<()> { + { + let mut module = self.module.borrow_mut(); + rs.generic_types + .iter() + .cloned() + .for_each(|v| module.add_type_var(v)) + } + let bases = match rs.generic_types.is_empty() { + true => "BaseModel".to_string(), + false => { + self.module + .borrow_mut() + .add_import("pydantic.generics".to_string(), "GenericModel".to_string()); + self.module + .borrow_mut() + .add_import("typing".to_string(), "Generic".to_string()); + format!("GenericModel, Generic[{}]", rs.generic_types.join(", ")) + } + }; + writeln!(w, "class {}({}):", rs.id.renamed, bases,)?; + + self.write_comments(w, true, &rs.comments, 1)?; + + rs.fields + .iter() + .try_for_each(|f| self.write_field(w, f, rs.generic_types.as_slice()))?; + + if rs.fields.is_empty() { + write!(w, " pass")? + } + write!(w, "\n\n")?; + self.module + .borrow_mut() + .add_import("pydantic".to_string(), "BaseModel".to_string()); + Ok(()) + } + + fn write_enum(&mut self, w: &mut dyn Write, e: &RustEnum) -> std::io::Result<()> { + // Make a suitable name for an anonymous struct enum variant + let make_anonymous_struct_name = + |variant_name: &str| format!("{}{}Inner", &e.shared().id.original, variant_name); + + // Generate named types for any anonymous struct variants of this enum + self.write_types_for_anonymous_structs(w, e, &make_anonymous_struct_name)?; + + match e { + // Write all the unit variants out (there can only be unit variants in + // this case) + RustEnum::Unit(shared) => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "Literal".to_string()); + write!( + w, + "{} = Literal[{}]", + shared.id.renamed, + shared + .variants + .iter() + .map(|v| format!( + "\"{}\"", + match v { + RustEnumVariant::Unit(v) => { + v.id.renamed.clone() + } + _ => panic!(), + } + )) + .collect::>() + .join(", ") + )?; + write!(w, "\n\n").unwrap(); + } + // Write all the algebraic variants out (all three variant types are possible + // here) + RustEnum::Algebraic { + tag_key, + content_key, + shared, + .. + } => { + { + let mut module = self.module.borrow_mut(); + shared + .generic_types + .iter() + .cloned() + .for_each(|v| module.add_type_var(v)) + } + let mut variants: Vec<(String, Vec)> = Vec::new(); + shared.variants.iter().for_each(|variant| { + match variant { + RustEnumVariant::Unit(unit_variant) => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "Literal".to_string()); + let variant_name = + format!("{}{}", shared.id.original, unit_variant.id.original); + variants.push((variant_name.clone(), vec![])); + writeln!(w, "class {}:", variant_name).unwrap(); + writeln!( + w, + " {}: Literal[\"{}\"]", + tag_key, unit_variant.id.renamed + ) + .unwrap(); + } + RustEnumVariant::Tuple { + ty, + shared: variant_shared, + } => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "Literal".to_string()); + let variant_name = + format!("{}{}", shared.id.original, variant_shared.id.original); + match ty { + RustType::Generic { id: _, parameters } => { + // This variant has generics, include them in the class def + let mut generic_parameters: Vec = parameters + .iter() + .flat_map(|p| { + collect_generics_for_variant(p, &shared.generic_types) + }) + .collect(); + dedup(&mut generic_parameters); + let type_vars = self + .module + .borrow_mut() + .get_type_vars(generic_parameters.len()); + variants.push((variant_name.clone(), type_vars)); + { + let mut module = self.module.borrow_mut(); + if generic_parameters.is_empty() { + module.add_import( + "pydantic".to_string(), + "BaseModel".to_string(), + ); + writeln!(w, "class {}(BaseModel):", variant_name) + .unwrap(); + } else { + module.add_import( + "typing".to_string(), + "Generic".to_string(), + ); + module.add_import( + "pydantic.generics".to_string(), + "GenericModel".to_string(), + ); + writeln!( + w, + "class {}(GenericModel, Generic[{}]):", + // note: generics is always unique (a single item) + variant_name, + generic_parameters.join(", ") + ) + .unwrap(); + } + } + } + other => { + let mut generics = vec![]; + if let RustType::Simple { id } = other { + // This could be a bare generic + if shared.generic_types.contains(id) { + generics = vec![id.clone()]; + } + } + variants.push((variant_name.clone(), generics.clone())); + { + let mut module = self.module.borrow_mut(); + if generics.is_empty() { + module.add_import( + "pydantic".to_string(), + "BaseModel".to_string(), + ); + writeln!(w, "class {}(BaseModel):", variant_name) + .unwrap(); + } else { + module.add_import( + "typing".to_string(), + "Generic".to_string(), + ); + module.add_import( + "pydantic.generics".to_string(), + "GenericModel".to_string(), + ); + writeln!( + w, + "class {}(GenericModel, Generic[{}]):", + // note: generics is always unique (a single item) + variant_name, + generics.join(", ") + ) + .unwrap(); + } + } + } + }; + writeln!( + w, + " {}: Literal[\"{}\"]", + tag_key, variant_shared.id.renamed + ) + .unwrap(); + writeln!( + w, + " {}: {}", + content_key, + match ty { + RustType::Simple { id } => id.to_owned(), + RustType::Special(special_ty) => self + .format_special_type(special_ty, &shared.generic_types) + .unwrap(), + RustType::Generic { id, parameters } => { + self.format_generic_type(id, parameters, &[]).unwrap() + } + } + ) + .unwrap(); + write!(w, "\n\n").unwrap(); + } + RustEnumVariant::AnonymousStruct { + shared: variant_shared, + fields, + } => { + let num_generic_parameters = fields + .iter() + .flat_map(|f| { + collect_generics_for_variant(&f.ty, &shared.generic_types) + }) + .count(); + let type_vars = self + .module + .borrow_mut() + .get_type_vars(num_generic_parameters); + let name = make_anonymous_struct_name(&variant_shared.id.original); + variants.push((name, type_vars)); + } + }; + }); + writeln!( + w, + "{} = {}", + shared.id.original, + variants + .iter() + .map(|(name, parameters)| match parameters.is_empty() { + true => name.clone(), + false => format!("{}[{}]", name, parameters.join(", ")), + }) + .collect::>() + .join(" | ") + ) + .unwrap(); + self.write_comments(w, true, &e.shared().comments, 0)?; + writeln!(w).unwrap(); + } + }; + Ok(()) + } +} + +impl Python { + pub fn new(type_mappings: HashMap) -> Self { + let mut mappings = type_mappings; + mappings.insert("DateTime".to_string(), "datetime".to_string()); + mappings.insert("Url".to_string(), "AnyUrl".to_string()); + Python { + type_mappings: mappings, + module: RefCell::new(Module::default()), + } + } + fn add_imports(&self, tp: &str) { + match tp { + "Url" => { + self.module + .borrow_mut() + .add_import("pydantic.networks".to_string(), "AnyUrl".to_string()); + } + "DateTime" => { + self.module + .borrow_mut() + .add_import("datetime".to_string(), "datetime".to_string()); + } + _ => {} + } + } + + fn get_identifier(&self, thing: ParsedRusthThing) -> String { + match thing { + ParsedRusthThing::TypeAlias(alias) => alias.id.original.clone(), + ParsedRusthThing::Struct(strct) => strct.id.original.clone(), + ParsedRusthThing::Enum(enm) => match enm { + RustEnum::Unit(u) => u.id.original.clone(), + RustEnum::Algebraic { + tag_key: _, + content_key: _, + shared, + } => shared.id.original.clone(), + }, + } + } + + fn write_field( + &mut self, + w: &mut dyn Write, + field: &RustField, + generic_types: &[String], + ) -> std::io::Result<()> { + let mut python_type = self + .format_type(&field.ty, generic_types) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + if field.ty.is_optional() || field.has_default { + python_type = format!("Optional[{}]", python_type); + self.module + .borrow_mut() + .add_import("typing".to_string(), "Optional".to_string()); + } + let mut default = None; + if field.has_default { + default = Some("None".to_string()) + } + let python_field_name = python_property_aware_rename(&field.id.original); + python_type = match python_field_name == field.id.renamed { + true => python_type, + false => { + self.module + .borrow_mut() + .add_import("typing".to_string(), "Annotated".to_string()); + self.module + .borrow_mut() + .add_import("pydantic".to_string(), "Field".to_string()); + format!( + "Annotated[{}, Field(alias=\"{}\")]", + python_type, field.id.renamed + ) + } + }; + match default { + Some(default) => writeln!( + w, + " {}: {} = {}", + python_field_name, python_type, default, + )?, + None => writeln!(w, " {}: {}", python_field_name, python_type)?, + } + + self.write_comments(w, true, &field.comments, 1)?; + Ok(()) + } + + fn write_comments( + &self, + w: &mut dyn Write, + is_docstring: bool, + comments: &[String], + indent_level: usize, + ) -> std::io::Result<()> { + // Only attempt to write a comment if there are some, otherwise we're Ok() + let indent = " ".repeat(indent_level); + if !comments.is_empty() { + let comment: String = { + if is_docstring { + format!( + "{indent}\"\"\"\n{indented_comments}\n{indent}\"\"\"", + indent = indent, + indented_comments = comments + .iter() + .map(|v| format!("{}{}", indent, v)) + .collect::>() + .join("\n"), + ) + } else { + comments + .iter() + .map(|v| format!("{}# {}", indent, v)) + .collect::>() + .join("\n") + } + }; + writeln!(w, "{}", comment)?; + } + Ok(()) + } +} + +static PYTHON_KEYWORDS: Lazy> = Lazy::new(|| { + HashSet::from_iter( + vec![ + "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", + "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", + "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", + "return", "try", "while", "with", "yield", + ] + .iter() + .map(|v| v.to_string()), + ) +}); + +fn python_property_aware_rename(name: &str) -> String { + let snake_name = name.to_case(Case::Snake); + match PYTHON_KEYWORDS.contains(&snake_name) { + true => format!("{}_", name), + false => snake_name, + } +} From ee5673c1b1f57cf869ef2cd910f8a232fa542517 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Tue, 28 May 2024 16:45:57 +0200 Subject: [PATCH 02/57] Resolve clippy errors --- core/src/language/python.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 6554754b..b00f83be 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -25,7 +25,7 @@ pub struct Module { } #[derive(Debug)] -struct GenerationError(String); +struct GenerationError; impl Module { // Idempotently insert an import @@ -46,7 +46,6 @@ impl Module { } fn get_type_vars(&mut self, n: usize) -> Vec { let vars: Vec = (0..n) - .into_iter() .map(|i| { if i == 0 { "T".to_string() @@ -76,7 +75,7 @@ impl Module { res.extend_from_slice(&level); if level.is_empty() { if !ts.is_empty() { - return Err(GenerationError("Cyclical runtime dependency".to_string())); + return Err(GenerationError); } break; } @@ -84,8 +83,8 @@ impl Module { let existing: HashSet<&String> = HashSet::from_iter(res.iter()); let mut missing: Vec = self .globals - .iter() - .map(|(k, _)| k.clone()) + .keys() + .cloned() .filter(|k| !existing.contains(k)) .collect(); missing.sort(); @@ -154,6 +153,7 @@ fn dedup(v: &mut Vec) { pub struct Python { /// Mappings from Rust type names to Python type names pub type_mappings: HashMap, + /// The Python module for the generated code. pub module: RefCell, } @@ -631,15 +631,6 @@ impl Language for Python { } impl Python { - pub fn new(type_mappings: HashMap) -> Self { - let mut mappings = type_mappings; - mappings.insert("DateTime".to_string(), "datetime".to_string()); - mappings.insert("Url".to_string(), "AnyUrl".to_string()); - Python { - type_mappings: mappings, - module: RefCell::new(Module::default()), - } - } fn add_imports(&self, tp: &str) { match tp { "Url" => { From dfd6522e0a953de9cf4b76fc9b28e1cb2cfa6ae5 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Tue, 28 May 2024 16:59:50 +0200 Subject: [PATCH 03/57] Resolve merge conflicts with latest main --- cli/src/main.rs | 5 +---- core/src/language/python.rs | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 29060c78..2927bf5c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -16,10 +16,7 @@ use std::collections::HashMap; use typeshare_core::language::Go; use typeshare_core::language::{GenericConstraints, Python}; use typeshare_core::{ - language::{ - CrateName, Kotlin, Language, Scala, SupportedLanguage, Swift, - TypeScript, - }, + language::{CrateName, Kotlin, Language, Scala, SupportedLanguage, Swift, TypeScript}, parser::ParsedData, }; use writer::write_generated; diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 9a2c2c8a..1c2ea0d5 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -1,3 +1,4 @@ +use crate::parser::ParsedData; use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; use crate::{ language::Language, @@ -8,12 +9,10 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::collections::HashSet; use std::hash::Hash; -use crate::parser::ParsedData; use std::{collections::HashMap, io::Write}; use super::CrateTypes; - use convert_case::{Case, Casing}; use topological_sort::TopologicalSort; @@ -88,7 +87,8 @@ impl Module { let mut missing: Vec = self .globals .keys() - .filter(|&k| !existing.contains(k)).cloned() + .filter(|&k| !existing.contains(k)) + .cloned() .collect(); missing.sort(); res.extend(missing); @@ -632,7 +632,7 @@ impl Language for Python { }; Ok(()) } - + fn write_imports( &mut self, _writer: &mut dyn Write, From 0c149147258d5bb49168f697d8205c6dff35aedd Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Tue, 28 May 2024 17:07:11 +0200 Subject: [PATCH 04/57] Add python as an option for the --lang flag --- cli/src/args.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/args.rs b/cli/src/args.rs index 8bf11d01..61f363e8 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -19,10 +19,10 @@ pub const ARG_OUTPUT_FOLDER: &str = "output-folder"; pub const ARG_FOLLOW_LINKS: &str = "follow-links"; #[cfg(feature = "go")] -const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "go"]; +const AVAILABLE_LANGUAGES: [&str; 6] = ["kotlin", "scala", "swift", "typescript", "go", "python"]; #[cfg(not(feature = "go"))] -const AVAILABLE_LANGUAGES: [&str; 4] = ["kotlin", "scala", "swift", "typescript"]; +const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "python"]; /// Parse command line arguments. pub(crate) fn build_command() -> Command<'static> { From 439783b4e5ad4439c80839fa72275edb87ad9de5 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Mon, 10 Jun 2024 14:58:10 +0200 Subject: [PATCH 05/57] Add support for todo and priorly unsupported types --- core/src/language/python.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 1c2ea0d5..13ae99c6 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -272,7 +272,9 @@ impl Language for Python { generic_types: &[String], ) -> Result { match special_ty { - SpecialRustType::Vec(rtype) => { + SpecialRustType::Vec(rtype) + | SpecialRustType::Array(rtype, _) + | SpecialRustType::Slice(rtype) => { self.module .borrow_mut() .add_import("typing".to_string(), "List".to_string()); @@ -296,7 +298,7 @@ impl Language for Python { )) } SpecialRustType::Unit => Ok("None".into()), - SpecialRustType::String => Ok("str".into()), + SpecialRustType::String | SpecialRustType::Char => Ok("str".into()), SpecialRustType::I8 | SpecialRustType::U8 | SpecialRustType::I16 @@ -304,18 +306,13 @@ impl Language for Python { | SpecialRustType::I32 | SpecialRustType::U32 | SpecialRustType::I54 - | SpecialRustType::U53 => Ok("int".into()), - SpecialRustType::F32 | SpecialRustType::F64 => Ok("float".into()), - SpecialRustType::Bool => Ok("bool".into()), - SpecialRustType::U64 + | SpecialRustType::U53 + | SpecialRustType::U64 | SpecialRustType::I64 | SpecialRustType::ISize - | SpecialRustType::USize => { - panic!("64 bit types not allowed in Typeshare") - } - SpecialRustType::Array(_, _) => todo!(), - SpecialRustType::Slice(_) => todo!(), - SpecialRustType::Char => todo!(), + | SpecialRustType::USize => Ok("int".into()), + SpecialRustType::F32 | SpecialRustType::F64 => Ok("float".into()), + SpecialRustType::Bool => Ok("bool".into()), } } From d7f12d0f494dffca7992ca74241bbf8dc7097b81 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Mon, 10 Jun 2024 15:07:59 +0200 Subject: [PATCH 06/57] Leverage mutable self to remove RefCell around Module --- core/src/language/python.rs | 65 ++++++++++++------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 13ae99c6..be43a160 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -5,7 +5,6 @@ use crate::{ rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; use once_cell::sync::Lazy; -use std::cell::RefCell; use std::collections::hash_map::Entry; use std::collections::HashSet; use std::hash::Hash; @@ -157,7 +156,7 @@ pub struct Python { /// Mappings from Rust type names to Python type names pub type_mappings: HashMap, /// The Python module for the generated code. - pub module: RefCell, + pub module: Module, } impl Language for Python { @@ -172,27 +171,26 @@ impl Language for Python { ) -> std::io::Result<()> { let mut globals: Vec; { - let mut module = self.module.borrow_mut(); for alias in &data.aliases { let thing = ParsedRusthThing::TypeAlias(alias); let identifier = self.get_identifier(thing); match &alias.r#type { RustType::Generic { id, parameters: _ } => { - module.add_global(identifier, vec![id.clone()]) + self.module.add_global(identifier, vec![id.clone()]) } - RustType::Simple { id } => module.add_global(identifier, vec![id.clone()]), + RustType::Simple { id } => self.module.add_global(identifier, vec![id.clone()]), RustType::Special(_) => {} } } for strct in &data.structs { let thing = ParsedRusthThing::Struct(strct); let identifier = self.get_identifier(thing); - module.add_global(identifier, vec![]); + self.module.add_global(identifier, vec![]); } for enm in &data.enums { let thing = ParsedRusthThing::Enum(enm); let identifier = self.get_identifier(thing); - module.add_global(identifier, vec![]); + self.module.add_global(identifier, vec![]); } globals = data .aliases @@ -201,7 +199,7 @@ impl Language for Python { .chain(data.structs.iter().map(ParsedRusthThing::Struct)) .chain(data.enums.iter().map(ParsedRusthThing::Enum)) .collect(); - let sorted_identifiers = module.topologically_sorted_globals().unwrap(); + let sorted_identifiers = self.module.topologically_sorted_globals().unwrap(); globals.sort_by(|a, b| { let identifier_a = self.get_identifier(a.clone()); let identifier_b = self.get_identifier(b.clone()); @@ -276,7 +274,6 @@ impl Language for Python { | SpecialRustType::Array(rtype, _) | SpecialRustType::Slice(rtype) => { self.module - .borrow_mut() .add_import("typing".to_string(), "List".to_string()); Ok(format!("List[{}]", self.format_type(rtype, generic_types)?)) } @@ -284,7 +281,6 @@ impl Language for Python { SpecialRustType::Option(rtype) => self.format_type(rtype, generic_types), SpecialRustType::HashMap(rtype1, rtype2) => { self.module - .borrow_mut() .add_import("typing".to_string(), "Dict".to_string()); Ok(format!( "Dict[{}, {}]", @@ -317,15 +313,14 @@ impl Language for Python { } fn begin_file(&mut self, w: &mut dyn Write, _parsed_data: &ParsedData) -> std::io::Result<()> { - let module = self.module.borrow(); - let mut type_var_names: Vec = module.type_variables.iter().cloned().collect(); + let mut type_var_names: Vec = self.module.type_variables.iter().cloned().collect(); type_var_names.sort(); let type_vars: Vec = type_var_names .iter() .map(|name| format!("{} = TypeVar(\"{}\")", name, name)) .collect(); let mut imports = vec![]; - for (import_module, identifiers) in &module.imports { + for (import_module, identifiers) in &self.module.imports { let mut identifier_vec = identifiers.iter().cloned().collect::>(); identifier_vec.sort(); imports.push(format!( @@ -369,20 +364,17 @@ impl Language for Python { fn write_struct(&mut self, w: &mut dyn Write, rs: &RustStruct) -> std::io::Result<()> { { - let mut module = self.module.borrow_mut(); rs.generic_types .iter() .cloned() - .for_each(|v| module.add_type_var(v)) + .for_each(|v| self.module.add_type_var(v)) } let bases = match rs.generic_types.is_empty() { true => "BaseModel".to_string(), false => { self.module - .borrow_mut() .add_import("pydantic.generics".to_string(), "GenericModel".to_string()); self.module - .borrow_mut() .add_import("typing".to_string(), "Generic".to_string()); format!("GenericModel, Generic[{}]", rs.generic_types.join(", ")) } @@ -400,7 +392,6 @@ impl Language for Python { } write!(w, "\n\n")?; self.module - .borrow_mut() .add_import("pydantic".to_string(), "BaseModel".to_string()); Ok(()) } @@ -418,7 +409,6 @@ impl Language for Python { // this case) RustEnum::Unit(shared) => { self.module - .borrow_mut() .add_import("typing".to_string(), "Literal".to_string()); write!( w, @@ -450,19 +440,17 @@ impl Language for Python { .. } => { { - let mut module = self.module.borrow_mut(); shared .generic_types .iter() .cloned() - .for_each(|v| module.add_type_var(v)) + .for_each(|v| self.module.add_type_var(v)) } let mut variants: Vec<(String, Vec)> = Vec::new(); shared.variants.iter().for_each(|variant| { match variant { RustEnumVariant::Unit(unit_variant) => { self.module - .borrow_mut() .add_import("typing".to_string(), "Literal".to_string()); let variant_name = format!("{}{}", shared.id.original, unit_variant.id.original); @@ -480,7 +468,6 @@ impl Language for Python { shared: variant_shared, } => { self.module - .borrow_mut() .add_import("typing".to_string(), "Literal".to_string()); let variant_name = format!("{}{}", shared.id.original, variant_shared.id.original); @@ -494,26 +481,23 @@ impl Language for Python { }) .collect(); dedup(&mut generic_parameters); - let type_vars = self - .module - .borrow_mut() - .get_type_vars(generic_parameters.len()); + let type_vars = + self.module.get_type_vars(generic_parameters.len()); variants.push((variant_name.clone(), type_vars)); { - let mut module = self.module.borrow_mut(); if generic_parameters.is_empty() { - module.add_import( + self.module.add_import( "pydantic".to_string(), "BaseModel".to_string(), ); writeln!(w, "class {}(BaseModel):", variant_name) .unwrap(); } else { - module.add_import( + self.module.add_import( "typing".to_string(), "Generic".to_string(), ); - module.add_import( + self.module.add_import( "pydantic.generics".to_string(), "GenericModel".to_string(), ); @@ -538,20 +522,19 @@ impl Language for Python { } variants.push((variant_name.clone(), generics.clone())); { - let mut module = self.module.borrow_mut(); if generics.is_empty() { - module.add_import( + self.module.add_import( "pydantic".to_string(), "BaseModel".to_string(), ); writeln!(w, "class {}(BaseModel):", variant_name) .unwrap(); } else { - module.add_import( + self.module.add_import( "typing".to_string(), "Generic".to_string(), ); - module.add_import( + self.module.add_import( "pydantic.generics".to_string(), "GenericModel".to_string(), ); @@ -600,10 +583,7 @@ impl Language for Python { collect_generics_for_variant(&f.ty, &shared.generic_types) }) .count(); - let type_vars = self - .module - .borrow_mut() - .get_type_vars(num_generic_parameters); + let type_vars = self.module.get_type_vars(num_generic_parameters); let name = make_anonymous_struct_name(&variant_shared.id.original); variants.push((name, type_vars)); } @@ -640,16 +620,14 @@ impl Language for Python { } impl Python { - fn add_imports(&self, tp: &str) { + fn add_imports(&mut self, tp: &str) { match tp { "Url" => { self.module - .borrow_mut() .add_import("pydantic.networks".to_string(), "AnyUrl".to_string()); } "DateTime" => { self.module - .borrow_mut() .add_import("datetime".to_string(), "datetime".to_string()); } _ => {} @@ -683,7 +661,6 @@ impl Python { if field.ty.is_optional() || field.has_default { python_type = format!("Optional[{}]", python_type); self.module - .borrow_mut() .add_import("typing".to_string(), "Optional".to_string()); } let mut default = None; @@ -695,10 +672,8 @@ impl Python { true => python_type, false => { self.module - .borrow_mut() .add_import("typing".to_string(), "Annotated".to_string()); self.module - .borrow_mut() .add_import("pydantic".to_string(), "Field".to_string()); format!( "Annotated[{}, Field(alias=\"{}\")]", From 203981804ad24aa8205edaa14daa50d0e7936e7c Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Mon, 10 Jun 2024 15:32:55 +0200 Subject: [PATCH 07/57] Fix ParsedRusthThing typo --- core/src/language/python.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index be43a160..48234910 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -96,7 +96,7 @@ impl Module { } #[derive(Debug, Clone)] -enum ParsedRusthThing<'a> { +enum ParsedRustThing<'a> { Struct(&'a RustStruct), Enum(&'a RustEnum), TypeAlias(&'a RustTypeAlias), @@ -169,10 +169,10 @@ impl Language for Python { _imports: &CrateTypes, data: ParsedData, ) -> std::io::Result<()> { - let mut globals: Vec; + let mut globals: Vec; { for alias in &data.aliases { - let thing = ParsedRusthThing::TypeAlias(alias); + let thing = ParsedRustThing::TypeAlias(alias); let identifier = self.get_identifier(thing); match &alias.r#type { RustType::Generic { id, parameters: _ } => { @@ -183,21 +183,21 @@ impl Language for Python { } } for strct in &data.structs { - let thing = ParsedRusthThing::Struct(strct); + let thing = ParsedRustThing::Struct(strct); let identifier = self.get_identifier(thing); self.module.add_global(identifier, vec![]); } for enm in &data.enums { - let thing = ParsedRusthThing::Enum(enm); + let thing = ParsedRustThing::Enum(enm); let identifier = self.get_identifier(thing); self.module.add_global(identifier, vec![]); } globals = data .aliases .iter() - .map(ParsedRusthThing::TypeAlias) - .chain(data.structs.iter().map(ParsedRusthThing::Struct)) - .chain(data.enums.iter().map(ParsedRusthThing::Enum)) + .map(ParsedRustThing::TypeAlias) + .chain(data.structs.iter().map(ParsedRustThing::Struct)) + .chain(data.enums.iter().map(ParsedRustThing::Enum)) .collect(); let sorted_identifiers = self.module.topologically_sorted_globals().unwrap(); globals.sort_by(|a, b| { @@ -217,9 +217,9 @@ impl Language for Python { let mut body: Vec = Vec::new(); for thing in globals { match thing { - ParsedRusthThing::Enum(e) => self.write_enum(&mut body, e)?, - ParsedRusthThing::Struct(rs) => self.write_struct(&mut body, rs)?, - ParsedRusthThing::TypeAlias(t) => self.write_type_alias(&mut body, t)?, + ParsedRustThing::Enum(e) => self.write_enum(&mut body, e)?, + ParsedRustThing::Struct(rs) => self.write_struct(&mut body, rs)?, + ParsedRustThing::TypeAlias(t) => self.write_type_alias(&mut body, t)?, }; } self.begin_file(w, &data)?; @@ -634,11 +634,11 @@ impl Python { } } - fn get_identifier(&self, thing: ParsedRusthThing) -> String { + fn get_identifier(&self, thing: ParsedRustThing) -> String { match thing { - ParsedRusthThing::TypeAlias(alias) => alias.id.original.clone(), - ParsedRusthThing::Struct(strct) => strct.id.original.clone(), - ParsedRusthThing::Enum(enm) => match enm { + ParsedRustThing::TypeAlias(alias) => alias.id.original.clone(), + ParsedRustThing::Struct(strct) => strct.id.original.clone(), + ParsedRustThing::Enum(enm) => match enm { RustEnum::Unit(u) => u.id.original.clone(), RustEnum::Algebraic { tag_key: _, From c9f4be42b0415c1844db7a95d4d2604072e7fb0c Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Mon, 5 Aug 2024 11:32:14 +0200 Subject: [PATCH 08/57] Use serialization alias in renaming fields in Python --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 48234910..2c10a85f 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -676,7 +676,7 @@ impl Python { self.module .add_import("pydantic".to_string(), "Field".to_string()); format!( - "Annotated[{}, Field(alias=\"{}\")]", + "Annotated[{}, Field(serialization_alias=\"{}\")]", python_type, field.id.renamed ) } From 459f596d304ca6f3d75a74f8c7af35306e3fab80 Mon Sep 17 00:00:00 2001 From: AndyTitu Date: Tue, 20 Aug 2024 13:25:02 +0300 Subject: [PATCH 09/57] Fix python serializing and deserialzing using pydantic Field aliasing and ConfigDict --- core/src/language/python.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 2c10a85f..9c562e54 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -383,6 +383,8 @@ impl Language for Python { self.write_comments(w, true, &rs.comments, 1)?; + handle_model_config(w, &mut self.module, rs); + rs.fields .iter() .try_for_each(|f| self.write_field(w, f, rs.generic_types.as_slice()))?; @@ -676,7 +678,7 @@ impl Python { self.module .add_import("pydantic".to_string(), "Field".to_string()); format!( - "Annotated[{}, Field(serialization_alias=\"{}\")]", + "Annotated[{}, Field(alias=\"{}\")]", python_type, field.id.renamed ) } @@ -749,3 +751,15 @@ fn python_property_aware_rename(name: &str) -> String { false => snake_name, } } + +// If at least one field from within a class is changed when the serde rename is used (a.k.a the field has 2 words) then we must use aliasing and we must also use a config dict at the top level of the class. +fn handle_model_config(w: &mut dyn Write, python_module: &mut Module, rs: &RustStruct) { + let visibly_renamed_field = rs.fields.iter().find(|f| { + let python_field_name = python_property_aware_rename(&f.id.original); + python_field_name != f.id.renamed + }); + if visibly_renamed_field.is_some() { + python_module.add_import("pydantic".to_string(), "ConfigDict".to_string()); + let _ = writeln!(w, "model_config = ConfigDict(populate_by_name=True)"); + }; +} From fde8907a20ad9774d865eb647dd43bbafe9f9a35 Mon Sep 17 00:00:00 2001 From: AndyTitu Date: Tue, 20 Aug 2024 14:28:45 +0300 Subject: [PATCH 10/57] Fix indentation of writing config dict --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 9c562e54..bf982783 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -760,6 +760,6 @@ fn handle_model_config(w: &mut dyn Write, python_module: &mut Module, rs: &RustS }); if visibly_renamed_field.is_some() { python_module.add_import("pydantic".to_string(), "ConfigDict".to_string()); - let _ = writeln!(w, "model_config = ConfigDict(populate_by_name=True)"); + let _ = writeln!(w, " model_config = ConfigDict(populate_by_name=True)\n"); }; } From ead7d3af505c024f16242382418b330b400e1db8 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Tue, 20 Aug 2024 16:55:49 -0400 Subject: [PATCH 11/57] added None default to Optional python type --- core/src/language/python.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index bf982783..9f8f6a1c 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,7 +661,7 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; if field.ty.is_optional() || field.has_default { - python_type = format!("Optional[{}]", python_type); + python_type = format!("Optional[{}] = None", python_type); self.module .add_import("typing".to_string(), "Optional".to_string()); } @@ -678,7 +678,7 @@ impl Python { self.module .add_import("pydantic".to_string(), "Field".to_string()); format!( - "Annotated[{}, Field(alias=\"{}\")]", + "Annotated[{}, Field(alias=\"{}\")] = None", python_type, field.id.renamed ) } From a74e4cea8d48806a33ea9224ee959ffa2f7c2470 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 15:54:59 -0400 Subject: [PATCH 12/57] Updated None default condition --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 9f8f6a1c..a5db9601 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -670,7 +670,7 @@ impl Python { default = Some("None".to_string()) } let python_field_name = python_property_aware_rename(&field.id.original); - python_type = match python_field_name == field.id.renamed { + python_type = match python_field_name == field.id.renamed && field.ty.is_optional() || field.has_default{ true => python_type, false => { self.module From 9b5c4be4ddfb4cec9ac03903822f7863721add39 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 16:38:58 -0400 Subject: [PATCH 13/57] fixing optional field set to default --- core/src/language/python.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index a5db9601..a7ab35dc 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -660,17 +660,8 @@ impl Python { let mut python_type = self .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - if field.ty.is_optional() || field.has_default { - python_type = format!("Optional[{}] = None", python_type); - self.module - .add_import("typing".to_string(), "Optional".to_string()); - } - let mut default = None; - if field.has_default { - default = Some("None".to_string()) - } let python_field_name = python_property_aware_rename(&field.id.original); - python_type = match python_field_name == field.id.renamed && field.ty.is_optional() || field.has_default{ + python_type = match python_field_name == field.id.renamed || field.ty.is_optional() || field.has_default{ true => python_type, false => { self.module @@ -683,6 +674,15 @@ impl Python { ) } }; + if field.ty.is_optional() || field.has_default && python_field_name == field.id.renamed { + python_type = format!("Optional[{}] = None", python_type); + self.module + .add_import("typing".to_string(), "Optional".to_string()); + } + let mut default = None; + if field.has_default { + default = Some("None".to_string()) + } match default { Some(default) => writeln!( w, From 628608d8bdf4c90ed8f03ec2c2353b365a24ca66 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 16:58:32 -0400 Subject: [PATCH 14/57] add fix for missing fields --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index a7ab35dc..eb42feb7 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,7 +661,7 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - python_type = match python_field_name == field.id.renamed || field.ty.is_optional() || field.has_default{ + python_type = match python_field_name == field.id.renamed{ true => python_type, false => { self.module From 8519dea6d3ca49f46af0bd55960a604d0413e472 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 17:02:50 -0400 Subject: [PATCH 15/57] added fix --- core/src/language/python.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index eb42feb7..216c17ec 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,6 +661,15 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); + if field.ty.is_optional() || field.has_default && python_field_name == field.id.renamed { + python_type = format!("Optional[{}] = None", python_type); + self.module + .add_import("typing".to_string(), "Optional".to_string()); + } + let mut default = None; + if field.has_default { + default = Some("None".to_string()) + } python_type = match python_field_name == field.id.renamed{ true => python_type, false => { @@ -674,15 +683,6 @@ impl Python { ) } }; - if field.ty.is_optional() || field.has_default && python_field_name == field.id.renamed { - python_type = format!("Optional[{}] = None", python_type); - self.module - .add_import("typing".to_string(), "Optional".to_string()); - } - let mut default = None; - if field.has_default { - default = Some("None".to_string()) - } match default { Some(default) => writeln!( w, From 6786aad827aab8ecb9b835d67cf3d958c2547ce4 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 17:05:34 -0400 Subject: [PATCH 16/57] changed fix --- core/src/language/python.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 216c17ec..fbe154e8 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,10 +661,12 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - if field.ty.is_optional() || field.has_default && python_field_name == field.id.renamed { - python_type = format!("Optional[{}] = None", python_type); - self.module - .add_import("typing".to_string(), "Optional".to_string()); + if field.ty.is_optional() || field.has_default { + if python_field_name == field.id.renamed{ + python_type = format!("Optional[{}] = None", python_type); + self.module + .add_import("typing".to_string(), "Optional".to_string()); + } } let mut default = None; if field.has_default { From 2d0e62747d118c64452e34f3591aadbd7a77bd3c Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 17:10:53 -0400 Subject: [PATCH 17/57] testing new approach --- core/src/language/python.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index fbe154e8..4983e81e 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -662,11 +662,13 @@ impl Python { .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); if field.ty.is_optional() || field.has_default { + let mut add_none_str: &str = ""; if python_field_name == field.id.renamed{ - python_type = format!("Optional[{}] = None", python_type); - self.module - .add_import("typing".to_string(), "Optional".to_string()); - } + add_none_str = " = None"; + } + python_type = format!("Optional[{}]{}", python_type, add_none_str); + self.module + .add_import("typing".to_string(), "Optional".to_string()); } let mut default = None; if field.has_default { From e6bea87fc4f5d72077ee02e2c92a499c2ae1f3fd Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Thu, 22 Aug 2024 17:34:00 -0400 Subject: [PATCH 18/57] added clarifying comment for None default --- core/src/language/python.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 4983e81e..c3c4c3cf 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -662,6 +662,7 @@ impl Python { .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); if field.ty.is_optional() || field.has_default { + // add_none_str sets Optional fields to None only when it is not wrapped by an Annotated field let mut add_none_str: &str = ""; if python_field_name == field.id.renamed{ add_none_str = " = None"; From d51d9034ee01f7e9740a349688f0ed0e83caba56 Mon Sep 17 00:00:00 2001 From: Amanda Yu Date: Mon, 26 Aug 2024 22:10:06 -0400 Subject: [PATCH 19/57] set optional field to None by default --- core/src/language/python.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index c3c4c3cf..9164a215 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,13 +661,10 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); + let mut add_none_default: bool = false; if field.ty.is_optional() || field.has_default { - // add_none_str sets Optional fields to None only when it is not wrapped by an Annotated field - let mut add_none_str: &str = ""; - if python_field_name == field.id.renamed{ - add_none_str = " = None"; - } - python_type = format!("Optional[{}]{}", python_type, add_none_str); + add_none_default = true; + python_type = format!("Optional[{}]", python_type); self.module .add_import("typing".to_string(), "Optional".to_string()); } @@ -683,11 +680,14 @@ impl Python { self.module .add_import("pydantic".to_string(), "Field".to_string()); format!( - "Annotated[{}, Field(alias=\"{}\")] = None", + "Annotated[{}, Field(alias=\"{}\")]", python_type, field.id.renamed ) } }; + if add_none_default { + python_type = format!("{python_type} = None"); + } match default { Some(default) => writeln!( w, From eeaa0d1d45499746554c5ac6e971d8095a8cb854 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Tue, 1 Oct 2024 10:39:48 +0200 Subject: [PATCH 20/57] Run cargo fmt --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 9164a215..24da0266 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -672,7 +672,7 @@ impl Python { if field.has_default { default = Some("None".to_string()) } - python_type = match python_field_name == field.id.renamed{ + python_type = match python_field_name == field.id.renamed { true => python_type, false => { self.module From e79b7b6e1cc6429a196344bc62d75ea860f82dcc Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 1 Oct 2024 17:21:20 -0400 Subject: [PATCH 21/57] fix duplicated " = None = None" --- core/src/language/python.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 24da0266..49038ac0 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,17 +661,12 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - let mut add_none_default: bool = false; - if field.ty.is_optional() || field.has_default { - add_none_default = true; + let add_none_default: bool = field.ty.is_optional() || field.has_default; + if add_none_default { python_type = format!("Optional[{}]", python_type); self.module .add_import("typing".to_string(), "Optional".to_string()); } - let mut default = None; - if field.has_default { - default = Some("None".to_string()) - } python_type = match python_field_name == field.id.renamed { true => python_type, false => { @@ -688,13 +683,9 @@ impl Python { if add_none_default { python_type = format!("{python_type} = None"); } - match default { - Some(default) => writeln!( - w, - " {}: {} = {}", - python_field_name, python_type, default, - )?, - None => writeln!(w, " {}: {}", python_field_name, python_type)?, + match field.has_default { + true => writeln!(w, " {}: {} = None", python_field_name, python_type)?, + false => writeln!(w, " {}: {}", python_field_name, python_type)?, } self.write_comments(w, true, &field.comments, 1)?; From d98a3e216450b10289d359496e69d97836d581e3 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 1 Oct 2024 17:29:48 -0400 Subject: [PATCH 22/57] fix duplicate --- core/src/language/python.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 49038ac0..1f5ed7a5 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -680,9 +680,6 @@ impl Python { ) } }; - if add_none_default { - python_type = format!("{python_type} = None"); - } match field.has_default { true => writeln!(w, " {}: {} = None", python_field_name, python_type)?, false => writeln!(w, " {}: {}", python_field_name, python_type)?, From 677cf1398cf922dabfe3fdbce62adc49a92d7005 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 1 Oct 2024 18:07:38 -0400 Subject: [PATCH 23/57] add tests --- core/src/language/python.rs | 104 ++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 1f5ed7a5..fb7812d1 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -661,8 +661,7 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - let add_none_default: bool = field.ty.is_optional() || field.has_default; - if add_none_default { + if field.ty.is_optional() { python_type = format!("Optional[{}]", python_type); self.module .add_import("typing".to_string(), "Optional".to_string()); @@ -680,7 +679,8 @@ impl Python { ) } }; - match field.has_default { + // TODO: Add support for default values other than None + match field.has_default && field.ty.is_optional() { true => writeln!(w, " {}: {} = None", python_field_name, python_type)?, false => writeln!(w, " {}: {}", python_field_name, python_type)?, } @@ -756,3 +756,101 @@ fn handle_model_config(w: &mut dyn Write, python_module: &mut Module, rs: &RustS let _ = writeln!(w, " model_config = ConfigDict(populate_by_name=True)\n"); }; } + +#[cfg(test)] +mod test { + use crate::rust_types::Id; + + use super::*; + #[test] + fn test_python_property_aware_rename() { + assert_eq!(python_property_aware_rename("class"), "class_"); + assert_eq!(python_property_aware_rename("snake_case"), "snake_case"); + } + + #[test] + fn test_optional_value_with_serde_default() { + let mut python = Python::default(); + let mock_writer = &mut Vec::new(); + let rust_field = RustField { + id: Id { + original: "field".to_string(), + renamed: "field".to_string(), + }, + ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { + id: "str".to_string(), + }))), + has_default: true, + comments: Default::default(), + decorators: Default::default(), + }; + python.write_field(mock_writer, &rust_field, &[]).unwrap(); + assert_eq!( + String::from_utf8_lossy(mock_writer), + " field: Optional[str] = None\n" + ); + } + + #[test] + fn test_optional_value_no_serde_default() { + let mut python = Python::default(); + let mock_writer = &mut Vec::new(); + let rust_field = RustField { + id: Id { + original: "field".to_string(), + renamed: "field".to_string(), + }, + ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { + id: "str".to_string(), + }))), + has_default: false, + comments: Default::default(), + decorators: Default::default(), + }; + python.write_field(mock_writer, &rust_field, &[]).unwrap(); + assert_eq!( + String::from_utf8_lossy(mock_writer), + " field: Optional[str]\n" + ); + } + + #[test] + fn test_non_optional_value_with_serde_default() { + let mut python = Python::default(); + let mock_writer = &mut Vec::new(); + let rust_field = RustField { + id: Id { + original: "field".to_string(), + renamed: "field".to_string(), + }, + ty: RustType::Simple { + id: "str".to_string(), + }, + has_default: true, + comments: Default::default(), + decorators: Default::default(), + }; + python.write_field(mock_writer, &rust_field, &[]).unwrap(); + assert_eq!(String::from_utf8_lossy(mock_writer), " field: str\n"); + } + + #[test] + fn test_non_optional_value_with_no_serde_default() { + let mut python = Python::default(); + let mock_writer = &mut Vec::new(); + let rust_field = RustField { + id: Id { + original: "field".to_string(), + renamed: "field".to_string(), + }, + ty: RustType::Simple { + id: "str".to_string(), + }, + has_default: false, + comments: Default::default(), + decorators: Default::default(), + }; + python.write_field(mock_writer, &rust_field, &[]).unwrap(); + assert_eq!(String::from_utf8_lossy(mock_writer), " field: str\n"); + } +} From df4a611207c9dbba4a609e8d3e1c38b895b9c426 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 1 Oct 2024 18:09:30 -0400 Subject: [PATCH 24/57] add comments to test --- core/src/language/python.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index fb7812d1..f85397c6 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -816,6 +816,8 @@ mod test { #[test] fn test_non_optional_value_with_serde_default() { + // technically an invalid case at the moment, as we don't support serde default values other than None + // TODO: change this test if we do let mut python = Python::default(); let mock_writer = &mut Vec::new(); let rust_field = RustField { From 77b7239a96d7e2bd8e5dfd4e97326d1c512e0913 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 1 Oct 2024 18:12:10 -0400 Subject: [PATCH 25/57] minor formatting --- core/src/language/python.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index f85397c6..c169ee82 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -681,8 +681,16 @@ impl Python { }; // TODO: Add support for default values other than None match field.has_default && field.ty.is_optional() { - true => writeln!(w, " {}: {} = None", python_field_name, python_type)?, - false => writeln!(w, " {}: {}", python_field_name, python_type)?, + true => { + // in the future we will want to get the default value properly, something like: + // let default_value = get_default_value(...) + let default_value = "None"; + writeln!( + w, + " {python_field_name}: {python_type} = {default_value}" + )? + } + false => writeln!(w, " {python_field_name}: {python_type}")?, } self.write_comments(w, true, &field.comments, 1)?; From b3536c8542e96ee86791db8e1a83fbe2125d61b5 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Wed, 2 Oct 2024 15:43:51 -0400 Subject: [PATCH 26/57] Remove level of indentation of type alias on comments --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index c169ee82..041a2bbe 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -357,7 +357,7 @@ impl Language for Python { r#type, )?; - self.write_comments(w, true, &ty.comments, 1)?; + self.write_comments(w, true, &ty.comments, 0)?; Ok(()) } From 45e9e5f523248e0b0edadcc674c0177fffa307e7 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Thu, 3 Oct 2024 12:04:22 -0400 Subject: [PATCH 27/57] Refactor Python Codegen to correctly top sort file and use typeshare types --- core/src/language/python.rs | 341 ++++++++++++------------------------ 1 file changed, 116 insertions(+), 225 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 041a2bbe..cf8be267 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -1,11 +1,11 @@ use crate::parser::ParsedData; -use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; +use crate::rust_types::{RustItem, RustType, RustTypeFormatError, SpecialRustType}; +use crate::topsort::topsort; use crate::{ language::Language, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; use once_cell::sync::Lazy; -use std::collections::hash_map::Entry; use std::collections::HashSet; use std::hash::Hash; use std::{collections::HashMap, io::Write}; @@ -13,95 +13,6 @@ use std::{collections::HashMap, io::Write}; use super::CrateTypes; use convert_case::{Case, Casing}; -use topological_sort::TopologicalSort; - -#[derive(Debug, Default)] -pub struct Module { - // HashMap - imports: HashMap>, - // HashMap> - // Used to lay out runtime references in the module - // such that it can be read top to bottom - globals: HashMap>, - type_variables: HashSet, -} - -#[derive(Debug)] -struct GenerationError; - -impl Module { - // Idempotently insert an import - fn add_import(&mut self, module: String, identifier: String) { - self.imports.entry(module).or_default().insert(identifier); - } - fn add_global(&mut self, identifier: String, deps: Vec) { - match self.globals.entry(identifier) { - Entry::Occupied(mut e) => e.get_mut().extend_from_slice(&deps), - Entry::Vacant(e) => { - e.insert(deps); - } - } - } - fn add_type_var(&mut self, name: String) { - self.add_import("typing".to_string(), "TypeVar".to_string()); - self.type_variables.insert(name); - } - fn get_type_vars(&mut self, n: usize) -> Vec { - let vars: Vec = (0..n) - .map(|i| { - if i == 0 { - "T".to_string() - } else { - format!("T{}", i) - } - }) - .collect(); - vars.iter().for_each(|tv| self.add_type_var(tv.clone())); - vars - } - // Rust lets you declare type aliases before the struct they point to. - // But in Python we need the struct to come first. - // So we need to topologically sort the globals so that type aliases - // always come _after_ the struct/enum they point to. - fn topologically_sorted_globals(&self) -> Result, GenerationError> { - let mut ts: TopologicalSort = TopologicalSort::new(); - for (identifier, dependencies) in &self.globals { - for dependency in dependencies { - ts.add_dependency(dependency.clone(), identifier.clone()) - } - } - let mut res: Vec = Vec::new(); - loop { - let mut level = ts.pop_all(); - level.sort(); - res.extend_from_slice(&level); - if level.is_empty() { - if !ts.is_empty() { - return Err(GenerationError); - } - break; - } - } - let existing: HashSet<&String> = HashSet::from_iter(res.iter()); - let mut missing: Vec = self - .globals - .keys() - .filter(|&k| !existing.contains(k)) - .cloned() - .collect(); - missing.sort(); - res.extend(missing); - Ok(res) - } -} - -#[derive(Debug, Clone)] -enum ParsedRustThing<'a> { - Struct(&'a RustStruct), - Enum(&'a RustEnum), - TypeAlias(&'a RustTypeAlias), -} - // Collect unique type vars from an enum field // Since we explode enums into unions of types, we need to extract all of the generics // used by each individual field @@ -155,8 +66,13 @@ fn dedup(v: &mut Vec) { pub struct Python { /// Mappings from Rust type names to Python type names pub type_mappings: HashMap, - /// The Python module for the generated code. - pub module: Module, + // HashMap + pub imports: HashMap>, + // HashMap> + // Used to lay out runtime references in the module + // such that it can be read top to bottom + // globals: HashMap>, + pub type_variables: HashSet, } impl Language for Python { @@ -169,61 +85,36 @@ impl Language for Python { _imports: &CrateTypes, data: ParsedData, ) -> std::io::Result<()> { - let mut globals: Vec; - { - for alias in &data.aliases { - let thing = ParsedRustThing::TypeAlias(alias); - let identifier = self.get_identifier(thing); - match &alias.r#type { - RustType::Generic { id, parameters: _ } => { - self.module.add_global(identifier, vec![id.clone()]) - } - RustType::Simple { id } => self.module.add_global(identifier, vec![id.clone()]), - RustType::Special(_) => {} - } - } - for strct in &data.structs { - let thing = ParsedRustThing::Struct(strct); - let identifier = self.get_identifier(thing); - self.module.add_global(identifier, vec![]); - } - for enm in &data.enums { - let thing = ParsedRustThing::Enum(enm); - let identifier = self.get_identifier(thing); - self.module.add_global(identifier, vec![]); - } - globals = data - .aliases - .iter() - .map(ParsedRustThing::TypeAlias) - .chain(data.structs.iter().map(ParsedRustThing::Struct)) - .chain(data.enums.iter().map(ParsedRustThing::Enum)) - .collect(); - let sorted_identifiers = self.module.topologically_sorted_globals().unwrap(); - globals.sort_by(|a, b| { - let identifier_a = self.get_identifier(a.clone()); - let identifier_b = self.get_identifier(b.clone()); - let pos_a = sorted_identifiers - .iter() - .position(|o| o.eq(&identifier_a)) - .unwrap_or(0); - let pos_b = sorted_identifiers - .iter() - .position(|o| o.eq(&identifier_b)) - .unwrap_or(0); - pos_a.cmp(&pos_b) - }); - } + self.begin_file(w, &data)?; + + let ParsedData { + structs, + enums, + aliases, + .. + } = data; + + let mut items = aliases + .into_iter() + .map(RustItem::Alias) + .chain(structs.into_iter().map(RustItem::Struct)) + .chain(enums.into_iter().map(RustItem::Enum)) + .collect::>(); + + topsort(&mut items); + let mut body: Vec = Vec::new(); - for thing in globals { + for thing in items { match thing { - ParsedRustThing::Enum(e) => self.write_enum(&mut body, e)?, - ParsedRustThing::Struct(rs) => self.write_struct(&mut body, rs)?, - ParsedRustThing::TypeAlias(t) => self.write_type_alias(&mut body, t)?, + RustItem::Enum(e) => self.write_enum(&mut body, &e)?, + RustItem::Struct(rs) => self.write_struct(&mut body, &rs)?, + RustItem::Alias(t) => self.write_type_alias(&mut body, &t)?, }; } - self.begin_file(w, &data)?; - let _ = w.write(&body)?; + + self.write_all_imports(w)?; + + w.write_all(&body)?; Ok(()) } @@ -273,15 +164,13 @@ impl Language for Python { SpecialRustType::Vec(rtype) | SpecialRustType::Array(rtype, _) | SpecialRustType::Slice(rtype) => { - self.module - .add_import("typing".to_string(), "List".to_string()); + self.add_import("typing".to_string(), "List".to_string()); Ok(format!("List[{}]", self.format_type(rtype, generic_types)?)) } // We add optionality above the type formatting level SpecialRustType::Option(rtype) => self.format_type(rtype, generic_types), SpecialRustType::HashMap(rtype1, rtype2) => { - self.module - .add_import("typing".to_string(), "Dict".to_string()); + self.add_import("typing".to_string(), "Dict".to_string()); Ok(format!( "Dict[{}, {}]", match rtype1.as_ref() { @@ -313,32 +202,9 @@ impl Language for Python { } fn begin_file(&mut self, w: &mut dyn Write, _parsed_data: &ParsedData) -> std::io::Result<()> { - let mut type_var_names: Vec = self.module.type_variables.iter().cloned().collect(); - type_var_names.sort(); - let type_vars: Vec = type_var_names - .iter() - .map(|name| format!("{} = TypeVar(\"{}\")", name, name)) - .collect(); - let mut imports = vec![]; - for (import_module, identifiers) in &self.module.imports { - let mut identifier_vec = identifiers.iter().cloned().collect::>(); - identifier_vec.sort(); - imports.push(format!( - "from {} import {}", - import_module, - identifier_vec.join(", ") - )) - } - imports.sort(); writeln!(w, "\"\"\"")?; writeln!(w, " Generated by typeshare {}", env!("CARGO_PKG_VERSION"))?; writeln!(w, "\"\"\"")?; - writeln!(w, "from __future__ import annotations\n").unwrap(); - writeln!(w, "{}\n", imports.join("\n"))?; - match type_vars.is_empty() { - true => writeln!(w).unwrap(), - false => writeln!(w, "{}\n\n", type_vars.join("\n")).unwrap(), - }; Ok(()) } @@ -367,15 +233,13 @@ impl Language for Python { rs.generic_types .iter() .cloned() - .for_each(|v| self.module.add_type_var(v)) + .for_each(|v| self.add_type_var(v)) } let bases = match rs.generic_types.is_empty() { true => "BaseModel".to_string(), false => { - self.module - .add_import("pydantic.generics".to_string(), "GenericModel".to_string()); - self.module - .add_import("typing".to_string(), "Generic".to_string()); + self.add_import("pydantic.generics".to_string(), "GenericModel".to_string()); + self.add_import("typing".to_string(), "Generic".to_string()); format!("GenericModel, Generic[{}]", rs.generic_types.join(", ")) } }; @@ -383,7 +247,7 @@ impl Language for Python { self.write_comments(w, true, &rs.comments, 1)?; - handle_model_config(w, &mut self.module, rs); + handle_model_config(w, self, rs); rs.fields .iter() @@ -393,8 +257,7 @@ impl Language for Python { write!(w, " pass")? } write!(w, "\n\n")?; - self.module - .add_import("pydantic".to_string(), "BaseModel".to_string()); + self.add_import("pydantic".to_string(), "BaseModel".to_string()); Ok(()) } @@ -410,8 +273,7 @@ impl Language for Python { // Write all the unit variants out (there can only be unit variants in // this case) RustEnum::Unit(shared) => { - self.module - .add_import("typing".to_string(), "Literal".to_string()); + self.add_import("typing".to_string(), "Literal".to_string()); write!( w, "{} = Literal[{}]", @@ -431,7 +293,7 @@ impl Language for Python { .collect::>() .join(", ") )?; - write!(w, "\n\n").unwrap(); + write!(w, "\n\n")?; } // Write all the algebraic variants out (all three variant types are possible // here) @@ -446,14 +308,13 @@ impl Language for Python { .generic_types .iter() .cloned() - .for_each(|v| self.module.add_type_var(v)) + .for_each(|v| self.add_type_var(v)) } let mut variants: Vec<(String, Vec)> = Vec::new(); shared.variants.iter().for_each(|variant| { match variant { RustEnumVariant::Unit(unit_variant) => { - self.module - .add_import("typing".to_string(), "Literal".to_string()); + self.add_import("typing".to_string(), "Literal".to_string()); let variant_name = format!("{}{}", shared.id.original, unit_variant.id.original); variants.push((variant_name.clone(), vec![])); @@ -469,8 +330,7 @@ impl Language for Python { ty, shared: variant_shared, } => { - self.module - .add_import("typing".to_string(), "Literal".to_string()); + self.add_import("typing".to_string(), "Literal".to_string()); let variant_name = format!("{}{}", shared.id.original, variant_shared.id.original); match ty { @@ -483,23 +343,22 @@ impl Language for Python { }) .collect(); dedup(&mut generic_parameters); - let type_vars = - self.module.get_type_vars(generic_parameters.len()); + let type_vars = self.get_type_vars(generic_parameters.len()); variants.push((variant_name.clone(), type_vars)); { if generic_parameters.is_empty() { - self.module.add_import( + self.add_import( "pydantic".to_string(), "BaseModel".to_string(), ); writeln!(w, "class {}(BaseModel):", variant_name) .unwrap(); } else { - self.module.add_import( + self.add_import( "typing".to_string(), "Generic".to_string(), ); - self.module.add_import( + self.add_import( "pydantic.generics".to_string(), "GenericModel".to_string(), ); @@ -525,18 +384,18 @@ impl Language for Python { variants.push((variant_name.clone(), generics.clone())); { if generics.is_empty() { - self.module.add_import( + self.add_import( "pydantic".to_string(), "BaseModel".to_string(), ); writeln!(w, "class {}(BaseModel):", variant_name) .unwrap(); } else { - self.module.add_import( + self.add_import( "typing".to_string(), "Generic".to_string(), ); - self.module.add_import( + self.add_import( "pydantic.generics".to_string(), "GenericModel".to_string(), ); @@ -585,7 +444,7 @@ impl Language for Python { collect_generics_for_variant(&f.ty, &shared.generic_types) }) .count(); - let type_vars = self.module.get_type_vars(num_generic_parameters); + let type_vars = self.get_type_vars(num_generic_parameters); let name = make_anonymous_struct_name(&variant_shared.id.original); variants.push((name, type_vars)); } @@ -603,10 +462,9 @@ impl Language for Python { }) .collect::>() .join(" | ") - ) - .unwrap(); + )?; self.write_comments(w, true, &e.shared().comments, 0)?; - writeln!(w).unwrap(); + writeln!(w)?; } }; Ok(()) @@ -625,32 +483,15 @@ impl Python { fn add_imports(&mut self, tp: &str) { match tp { "Url" => { - self.module - .add_import("pydantic.networks".to_string(), "AnyUrl".to_string()); + self.add_import("pydantic.networks".to_string(), "AnyUrl".to_string()); } "DateTime" => { - self.module - .add_import("datetime".to_string(), "datetime".to_string()); + self.add_import("datetime".to_string(), "datetime".to_string()); } _ => {} } } - fn get_identifier(&self, thing: ParsedRustThing) -> String { - match thing { - ParsedRustThing::TypeAlias(alias) => alias.id.original.clone(), - ParsedRustThing::Struct(strct) => strct.id.original.clone(), - ParsedRustThing::Enum(enm) => match enm { - RustEnum::Unit(u) => u.id.original.clone(), - RustEnum::Algebraic { - tag_key: _, - content_key: _, - shared, - } => shared.id.original.clone(), - }, - } - } - fn write_field( &mut self, w: &mut dyn Write, @@ -663,16 +504,13 @@ impl Python { let python_field_name = python_property_aware_rename(&field.id.original); if field.ty.is_optional() { python_type = format!("Optional[{}]", python_type); - self.module - .add_import("typing".to_string(), "Optional".to_string()); + self.add_import("typing".to_string(), "Optional".to_string()); } python_type = match python_field_name == field.id.renamed { true => python_type, false => { - self.module - .add_import("typing".to_string(), "Annotated".to_string()); - self.module - .add_import("pydantic".to_string(), "Field".to_string()); + self.add_import("typing".to_string(), "Annotated".to_string()); + self.add_import("pydantic".to_string(), "Field".to_string()); format!( "Annotated[{}, Field(alias=\"{}\")]", python_type, field.id.renamed @@ -730,6 +568,59 @@ impl Python { } Ok(()) } + + // Idempotently insert an import + fn add_import(&mut self, module: String, identifier: String) { + self.imports.entry(module).or_default().insert(identifier); + } + + fn add_type_var(&mut self, name: String) { + self.add_import("typing".to_string(), "TypeVar".to_string()); + self.type_variables.insert(name); + } + + fn get_type_vars(&mut self, n: usize) -> Vec { + let vars: Vec = (0..n) + .map(|i| { + if i == 0 { + "T".to_string() + } else { + format!("T{}", i) + } + }) + .collect(); + vars.iter().for_each(|tv| self.add_type_var(tv.clone())); + vars + } + + fn write_all_imports(&self, w: &mut dyn Write) -> std::io::Result<()> { + let mut type_var_names: Vec = self.type_variables.iter().cloned().collect(); + type_var_names.sort(); + let type_vars: Vec = type_var_names + .iter() + .map(|name| format!("{} = TypeVar(\"{}\")", name, name)) + .collect(); + let mut imports = vec![]; + for (import_module, identifiers) in &self.imports { + let mut identifier_vec = identifiers.iter().cloned().collect::>(); + identifier_vec.sort(); + imports.push(format!( + "from {} import {}", + import_module, + identifier_vec.join(", ") + )) + } + imports.sort(); + + writeln!(w, "from __future__ import annotations\n")?; + writeln!(w, "{}\n", imports.join("\n"))?; + + match type_vars.is_empty() { + true => writeln!(w)?, + false => writeln!(w, "{}\n\n", type_vars.join("\n"))?, + }; + Ok(()) + } } static PYTHON_KEYWORDS: Lazy> = Lazy::new(|| { @@ -754,7 +645,7 @@ fn python_property_aware_rename(name: &str) -> String { } // If at least one field from within a class is changed when the serde rename is used (a.k.a the field has 2 words) then we must use aliasing and we must also use a config dict at the top level of the class. -fn handle_model_config(w: &mut dyn Write, python_module: &mut Module, rs: &RustStruct) { +fn handle_model_config(w: &mut dyn Write, python_module: &mut Python, rs: &RustStruct) { let visibly_renamed_field = rs.fields.iter().find(|f| { let python_field_name = python_property_aware_rename(&f.id.original); python_field_name != f.id.renamed From c8026b0764c3a03168846485b8e25e4f69e92a4a Mon Sep 17 00:00:00 2001 From: Mohammed Omar Miraj <103403052+MOmarMiraj@users.noreply.github.com> Date: Mon, 28 Oct 2024 09:06:45 -0400 Subject: [PATCH 28/57] Add Feature Flag for Python TS (#3) * Add feature flag for Python TS * Fix length of arrays * Remove Available Languages and make it more scaleable * make languages mutable --- cli/Cargo.toml | 1 + cli/src/args.rs | 15 ++++++++------- cli/src/config.rs | 2 ++ cli/src/main.rs | 9 ++++++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 44f95a0d..fb3fab5b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -13,6 +13,7 @@ path = "src/main.rs" [features] go = [] +python = [] [dependencies] clap = { version = "3", features = ["cargo"] } diff --git a/cli/src/args.rs b/cli/src/args.rs index 6f964e7f..a71bef8d 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -19,14 +19,15 @@ pub const ARG_OUTPUT_FOLDER: &str = "output-folder"; pub const ARG_FOLLOW_LINKS: &str = "follow-links"; pub const ARG_TARGET_OS: &str = "target_os"; -#[cfg(feature = "go")] -const AVAILABLE_LANGUAGES: [&str; 6] = ["kotlin", "scala", "swift", "typescript", "go", "python"]; - -#[cfg(not(feature = "go"))] -const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "python"]; - /// Parse command line arguments. pub(crate) fn build_command() -> Command<'static> { + #[allow(unused_mut)] // As its thrown behind feature flag, the compiler thinks we are not mutating the vector + let mut languages: Vec<&str> = vec!["kotlin", "scala", "swift", "typescript"]; + #[cfg(feature = "go")] + languages.push("go"); + #[cfg(feature = "python")] + languages.push("python"); + command!("typeshare") .version(VERSION) .args_conflicts_with_subcommands(true) @@ -48,7 +49,7 @@ pub(crate) fn build_command() -> Command<'static> { .long("lang") .help("Language of generated types") .takes_value(true) - .possible_values(AVAILABLE_LANGUAGES) + .possible_values(languages) .required_unless(ARG_GENERATE_CONFIG), ) .arg( diff --git a/cli/src/config.rs b/cli/src/config.rs index d2b38b1e..9cd7304c 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -12,6 +12,7 @@ const DEFAULT_CONFIG_FILE_NAME: &str = "typeshare.toml"; #[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(default)] +#[cfg(feature = "python")] pub struct PythonParams { pub type_mappings: HashMap, } @@ -69,6 +70,7 @@ pub(crate) struct Config { pub typescript: TypeScriptParams, pub kotlin: KotlinParams, pub scala: ScalaParams, + #[cfg(feature = "python")] pub python: PythonParams, #[cfg(feature = "go")] pub go: GoParams, diff --git a/cli/src/main.rs b/cli/src/main.rs index 89653cdb..b082d676 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -16,7 +16,9 @@ use rayon::iter::ParallelBridge; use std::collections::{BTreeMap, HashMap}; #[cfg(feature = "go")] use typeshare_core::language::Go; -use typeshare_core::language::{GenericConstraints, Python}; +#[cfg(feature = "python")] +use typeshare_core::language::Python; +use typeshare_core::language::GenericConstraints; use typeshare_core::{ language::{CrateName, Kotlin, Language, Scala, SupportedLanguage, Swift, TypeScript}, parser::ParsedData, @@ -191,10 +193,15 @@ fn language( SupportedLanguage::Go => { panic!("go support is currently experimental and must be enabled as a feature flag for typeshare-cli") } + #[cfg(feature = "python")] SupportedLanguage::Python => Box::new(Python { type_mappings: config.python.type_mappings, ..Default::default() }), + #[cfg(not(feature = "python"))] + SupportedLanguage::Python => { + panic!("python support is currently experimental and must be enabled as a feature flag for typeshare-cli") + } } } From e41924616328992a6b8cf19c3194a14504a05221 Mon Sep 17 00:00:00 2001 From: Peter Jiang <47200772+CheatCod@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:08:44 -0400 Subject: [PATCH 29/57] Draft: Change tagged enum generation in python (#2) * init * refactor functionality to its own method * remove serde * test signing * YOUR_COMMIT_MESSAGE * test signing * YOUR_COMMIT_MESSAGE * test signing * test signing * YOUR_COMMIT_MESSAGE * YOUR_COMMIT_MESSAGE * YOUR_COMMIT_MESSAGE * YOUR_COMMIT_MESSAGE * rewrite tests * fix generation * remove comma in types * remove inner * wip test * setup test and fix tests * fix * enable and fix more tests * add ctor for each variant class * extract to method * add support for ctor method for unit and tuple variants * fix formatting * apply omar's fixes * Add more snapshot tests and remove unused function * Add expected outputs for snapshot tests * Change unit enums to pass as content and fix some unneeded imports * fix config test * Add newline for EOF * Add snapshot test for serde default * make code more readable * fix single union, and rework to new pydantic standard * fix unit tests * remove useless tests * remove empty unit class generation, remove unit variant from union * update test --------- Co-authored-by: Omar Miraj --- cli/data/tests/mappings_config.toml | 6 +- cli/src/config.rs | 5 + .../anonymous_struct_with_rename/output.py | 72 +++ .../can_apply_prefix_correctly/output.py | 92 +++ .../can_generate_algebraic_enum/output.py | 136 +++++ .../output.py | 38 ++ .../output.py | 54 ++ .../can_generate_bare_string_enum/output.py | 13 + .../output.py | 41 ++ .../tests/can_generate_simple_enum/output.py | 13 + .../output.py | 26 + .../can_generate_slice_of_user_type/output.py | 13 + .../output.py | 13 + .../tests/can_generate_unit_structs/output.py | 11 + .../can_handle_anonymous_struct/output.py | 141 +++++ .../output.py | 11 + .../tests/can_handle_serde_rename/output.py | 26 + .../can_handle_serde_rename_all/output.py | 33 + .../output.py | 26 + .../data/tests/can_handle_unit_type/output.py | 36 ++ .../output.py | 24 + .../output.py | 13 + .../tests/excluded_by_target_os/output.py | 106 ++++ core/data/tests/generate_types/output.py | 27 + .../output.py | 11 + core/data/tests/kebab_case_rename/output.py | 20 + core/data/tests/orders_types/output.py | 38 ++ .../tests/recursive_enum_decorator/output.py | 102 ++++ .../tests/resolves_qualified_type/output.py | 18 + .../serialize_anonymous_field_as/output.py | 40 ++ core/data/tests/serialize_field_as/output.py | 15 + .../data/tests/serialize_type_alias/output.py | 23 + core/data/tests/smart_pointers/output.py | 96 +++ .../output.py | 67 ++ core/data/tests/test_generate_char/output.py | 12 + core/data/tests/test_i54_u53_type/output.py | 13 + .../tests/test_optional_type_alias/output.py | 19 + .../tests/test_serde_default_struct/output.py | 13 + core/data/tests/test_serde_url/output.py | 13 + core/data/tests/test_serialized_as/output.py | 17 + .../tests/test_serialized_as_tuple/output.py | 11 + .../output.py | 13 + core/data/tests/test_type_alias/output.py | 15 + .../output.py | 11 + .../tests/use_correct_integer_types/output.py | 20 + core/src/language/python.rs | 572 +++++++++++------- core/tests/snapshot_tests.rs | 118 ++-- 47 files changed, 1989 insertions(+), 264 deletions(-) create mode 100644 core/data/tests/anonymous_struct_with_rename/output.py create mode 100644 core/data/tests/can_apply_prefix_correctly/output.py create mode 100644 core/data/tests/can_generate_algebraic_enum/output.py create mode 100644 core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py create mode 100644 core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py create mode 100644 core/data/tests/can_generate_bare_string_enum/output.py create mode 100644 core/data/tests/can_generate_empty_algebraic_enum/output.py create mode 100644 core/data/tests/can_generate_simple_enum/output.py create mode 100644 core/data/tests/can_generate_simple_struct_with_a_comment/output.py create mode 100644 core/data/tests/can_generate_slice_of_user_type/output.py create mode 100644 core/data/tests/can_generate_struct_with_skipped_fields/output.py create mode 100644 core/data/tests/can_generate_unit_structs/output.py create mode 100644 core/data/tests/can_handle_anonymous_struct/output.py create mode 100644 core/data/tests/can_handle_quote_in_serde_rename/output.py create mode 100644 core/data/tests/can_handle_serde_rename/output.py create mode 100644 core/data/tests/can_handle_serde_rename_all/output.py create mode 100644 core/data/tests/can_handle_serde_rename_on_top_level/output.py create mode 100644 core/data/tests/can_handle_unit_type/output.py create mode 100644 core/data/tests/can_recognize_types_inside_modules/output.py create mode 100644 core/data/tests/enum_is_properly_named_with_serde_overrides/output.py create mode 100644 core/data/tests/excluded_by_target_os/output.py create mode 100644 core/data/tests/generate_types/output.py create mode 100644 core/data/tests/generates_empty_structs_and_initializers/output.py create mode 100644 core/data/tests/kebab_case_rename/output.py create mode 100644 core/data/tests/orders_types/output.py create mode 100644 core/data/tests/recursive_enum_decorator/output.py create mode 100644 core/data/tests/resolves_qualified_type/output.py create mode 100644 core/data/tests/serialize_anonymous_field_as/output.py create mode 100644 core/data/tests/serialize_field_as/output.py create mode 100644 core/data/tests/serialize_type_alias/output.py create mode 100644 core/data/tests/smart_pointers/output.py create mode 100644 core/data/tests/test_algebraic_enum_case_name_support/output.py create mode 100644 core/data/tests/test_generate_char/output.py create mode 100644 core/data/tests/test_i54_u53_type/output.py create mode 100644 core/data/tests/test_optional_type_alias/output.py create mode 100644 core/data/tests/test_serde_default_struct/output.py create mode 100644 core/data/tests/test_serde_url/output.py create mode 100644 core/data/tests/test_serialized_as/output.py create mode 100644 core/data/tests/test_serialized_as_tuple/output.py create mode 100644 core/data/tests/test_simple_enum_case_name_support/output.py create mode 100644 core/data/tests/test_type_alias/output.py create mode 100644 core/data/tests/use_correct_decoded_variable_name/output.py create mode 100644 core/data/tests/use_correct_integer_types/output.py diff --git a/cli/data/tests/mappings_config.toml b/cli/data/tests/mappings_config.toml index 1ffbedcc..2bbcb330 100644 --- a/cli/data/tests/mappings_config.toml +++ b/cli/data/tests/mappings_config.toml @@ -11,4 +11,8 @@ "DateTime" = "String" [go.type_mappings] -"DateTime" = "string" \ No newline at end of file +"DateTime" = "string" + +[python.type_mappings] +"DateTime" = "datetime" +"Url" = "AnyUrl" diff --git a/cli/src/config.rs b/cli/src/config.rs index 9cd7304c..8fb25776 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -169,6 +169,11 @@ mod test { assert_eq!(config.kotlin.type_mappings["DateTime"], "String"); assert_eq!(config.scala.type_mappings["DateTime"], "String"); assert_eq!(config.typescript.type_mappings["DateTime"], "string"); + #[cfg(feature = "python")] + { + assert_eq!(config.python.type_mappings["Url"], "AnyUrl"); + assert_eq!(config.python.type_mappings["DateTime"], "datetime"); + } #[cfg(feature = "go")] assert_eq!(config.go.type_mappings["DateTime"], "string"); } diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py new file mode 100644 index 00000000..7b17cab3 --- /dev/null +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -0,0 +1,72 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict, Field +from typing import List, Union + + +class AnonymousStructWithRenameList(BaseModel): + """ + Generated type representing the anonymous struct variant `List` of the `AnonymousStructWithRename` Rust enum + """ + list: List[str] + + +class AnonymousStructWithRenameLongFieldNames(BaseModel): + """ + Generated type representing the anonymous struct variant `LongFieldNames` of the `AnonymousStructWithRename` Rust enum + """ + model_config = ConfigDict(populate_by_name=True) + + some_long_field_name: str + and_: bool + but_one_more: List[str] + + +class AnonymousStructWithRenameKebabCase(BaseModel): + """ + Generated type representing the anonymous struct variant `KebabCase` of the `AnonymousStructWithRename` Rust enum + """ + model_config = ConfigDict(populate_by_name=True) + + another_list: List[str] = Field(alias="another-list") + camel_case_string_field: str + something_else: bool = Field(alias="something-else") + + +class AnonymousStructWithRenameTypes(str, Enum): + LIST = "list" + LONG_FIELD_NAMES = "longFieldNames" + KEBAB_CASE = "kebabCase" + +class AnonymousStructWithRename(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AnonymousStructWithRenameTypes + content: Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] + + + @classmethod + def new_anonymous_struct_with_rename_list(cls, list: List[str]): + return cls( + type=AnonymousStructWithRenameTypes.LIST, + content=AnonymousStructWithRenameList(list = list) + ) + + + @classmethod + def new_anonymous_struct_with_rename_long_field_names(cls, some_long_field_name: str, and_: bool, but_one_more: List[str]): + return cls( + type=AnonymousStructWithRenameTypes.LONG_FIELD_NAMES, + content=AnonymousStructWithRenameLongFieldNames(some_long_field_name = some_long_field_name, and_ = and_, but_one_more = but_one_more) + ) + + + @classmethod + def new_anonymous_struct_with_rename_kebab_case(cls, another_list: List[str], camel_case_string_field: str, something_else: bool): + return cls( + type=AnonymousStructWithRenameTypes.KEBAB_CASE, + content=AnonymousStructWithRenameKebabCase(another_list = another_list, camel_case_string_field = camel_case_string_field, something_else = something_else) + ) diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py new file mode 100644 index 00000000..629aeb46 --- /dev/null +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -0,0 +1,92 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Dict, List, Union + + +class ItemDetailsFieldValue(BaseModel): + hello: str + + +class AdvancedColorsTypes(str, Enum): + STRING = "String" + NUMBER = "Number" + NUMBER_ARRAY = "NumberArray" + REALLY_COOL_TYPE = "ReallyCoolType" + ARRAY_REALLY_COOL_TYPE = "ArrayReallyCoolType" + DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" + +class AdvancedColorsString(BaseModel): + c: str + +class AdvancedColorsNumber(BaseModel): + c: int + +class AdvancedColorsNumberArray(BaseModel): + c: List[int] + +class AdvancedColorsReallyCoolType(BaseModel): + c: ItemDetailsFieldValue + +class AdvancedColorsArrayReallyCoolType(BaseModel): + c: List[ItemDetailsFieldValue] + +class AdvancedColorsDictionaryReallyCoolType(BaseModel): + c: Dict[str, ItemDetailsFieldValue] + +class AdvancedColors(BaseModel): + model_config = ConfigDict(use_enum_values=True) + t: AdvancedColorsTypes + c: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType, AdvancedColorsArrayReallyCoolType, AdvancedColorsDictionaryReallyCoolType] + + + @classmethod + def new_advanced_colors_string(cls, c : AdvancedColorsString): + return cls( + t=AdvancedColorsTypes.STRING, + c=c + ) + + + @classmethod + def new_advanced_colors_number(cls, c : AdvancedColorsNumber): + return cls( + t=AdvancedColorsTypes.NUMBER, + c=c + ) + + + @classmethod + def new_advanced_colors_number_array(cls, c : AdvancedColorsNumberArray): + return cls( + t=AdvancedColorsTypes.NUMBER_ARRAY, + c=c + ) + + + @classmethod + def new_advanced_colors_really_cool_type(cls, c : AdvancedColorsReallyCoolType): + return cls( + t=AdvancedColorsTypes.REALLY_COOL_TYPE, + c=c + ) + + + @classmethod + def new_advanced_colors_array_really_cool_type(cls, c : AdvancedColorsArrayReallyCoolType): + return cls( + t=AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE, + c=c + ) + + + @classmethod + def new_advanced_colors_dictionary_really_cool_type(cls, c : AdvancedColorsDictionaryReallyCoolType): + return cls( + t=AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE, + c=c + ) diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py new file mode 100644 index 00000000..c306bb36 --- /dev/null +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -0,0 +1,136 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import List, Union + + +class ItemDetailsFieldValue(BaseModel): + """ + Struct comment + """ + pass + +class AdvancedColorsTypes(str, Enum): + STRING = "String" + NUMBER = "Number" + UNSIGNED_NUMBER = "UnsignedNumber" + NUMBER_ARRAY = "NumberArray" + REALLY_COOL_TYPE = "ReallyCoolType" + +class AdvancedColorsString(BaseModel): + content: str + +class AdvancedColorsNumber(BaseModel): + content: int + +class AdvancedColorsUnsignedNumber(BaseModel): + content: int + +class AdvancedColorsNumberArray(BaseModel): + content: List[int] + +class AdvancedColorsReallyCoolType(BaseModel): + content: ItemDetailsFieldValue + +class AdvancedColors(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AdvancedColorsTypes + content: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] + + + @classmethod + def new_advanced_colors_string(cls, content : AdvancedColorsString): + return cls( + type=AdvancedColorsTypes.STRING, + content=content + ) + + + @classmethod + def new_advanced_colors_number(cls, content : AdvancedColorsNumber): + return cls( + type=AdvancedColorsTypes.NUMBER, + content=content + ) + + + @classmethod + def new_advanced_colors_unsigned_number(cls, content : AdvancedColorsUnsignedNumber): + return cls( + type=AdvancedColorsTypes.UNSIGNED_NUMBER, + content=content + ) + + + @classmethod + def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): + return cls( + type=AdvancedColorsTypes.NUMBER_ARRAY, + content=content + ) + + + @classmethod + def new_advanced_colors_really_cool_type(cls, content : AdvancedColorsReallyCoolType): + return cls( + type=AdvancedColorsTypes.REALLY_COOL_TYPE, + content=content + ) +class AdvancedColors2Types(str, Enum): + STRING = "string" + NUMBER = "number" + NUMBER_ARRAY = "number-array" + REALLY_COOL_TYPE = "really-cool-type" + +class AdvancedColors2String(BaseModel): + content: str + +class AdvancedColors2Number(BaseModel): + content: int + +class AdvancedColors2NumberArray(BaseModel): + content: List[int] + +class AdvancedColors2ReallyCoolType(BaseModel): + content: ItemDetailsFieldValue + +class AdvancedColors2(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AdvancedColors2Types + content: Union[AdvancedColors2String, AdvancedColors2Number, AdvancedColors2NumberArray, AdvancedColors2ReallyCoolType] + + + @classmethod + def new_advanced_colors_2_string(cls, content : AdvancedColors2String): + return cls( + type=AdvancedColors2Types.STRING, + content=content + ) + + + @classmethod + def new_advanced_colors_2_number(cls, content : AdvancedColors2Number): + return cls( + type=AdvancedColors2Types.NUMBER, + content=content + ) + + + @classmethod + def new_advanced_colors_2_number_array(cls, content : AdvancedColors2NumberArray): + return cls( + type=AdvancedColors2Types.NUMBER_ARRAY, + content=content + ) + + + @classmethod + def new_advanced_colors_2_really_cool_type(cls, content : AdvancedColors2ReallyCoolType): + return cls( + type=AdvancedColors2Types.REALLY_COOL_TYPE, + content=content + ) diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py new file mode 100644 index 00000000..774735be --- /dev/null +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -0,0 +1,38 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class SomeEnumTypes(str, Enum): + A = "A" + C = "C" + + +class SomeEnumC(BaseModel): + content: int + +class SomeEnum(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: SomeEnumTypes + content: Union[SomeEnumC, None] + + + @classmethod + def new_some_enum_a(cls) -> SomeEnum: + return cls( + type=SomeEnumTypes.A, + content=None + ) + + + @classmethod + def new_some_enum_c(cls, content : SomeEnumC): + return cls( + type=SomeEnumTypes.C, + content=content + ) diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py new file mode 100644 index 00000000..81b122c9 --- /dev/null +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -0,0 +1,54 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class AutofilledByUs(BaseModel): + """ + Generated type representing the anonymous struct variant `Us` of the `AutofilledBy` Rust enum + """ + uuid: str + """ + The UUID for the fill + """ + + +class AutofilledBySomethingElse(BaseModel): + """ + Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum + """ + uuid: str + """ + The UUID for the fill + """ + + +class AutofilledByTypes(str, Enum): + US = "Us" + SOMETHING_ELSE = "SomethingElse" + +class AutofilledBy(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AutofilledByTypes + content: Union[AutofilledByUs, AutofilledBySomethingElse] + + + @classmethod + def new_autofilled_by_us(cls, uuid: str): + return cls( + type=AutofilledByTypes.US, + content=AutofilledByUs(uuid = uuid) + ) + + + @classmethod + def new_autofilled_by_something_else(cls, uuid: str): + return cls( + type=AutofilledByTypes.SOMETHING_ELSE, + content=AutofilledBySomethingElse(uuid = uuid) + ) diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py new file mode 100644 index 00000000..e260548e --- /dev/null +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum + + +class Colors(Enum): + RED = "Red", + BLUE = "Blue", + GREEN = "Green" + diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py new file mode 100644 index 00000000..a3623202 --- /dev/null +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -0,0 +1,41 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class AddressDetails(BaseModel): + pass + +class AddressTypes(str, Enum): + FIXED_ADDRESS = "FixedAddress" + NO_FIXED_ADDRESS = "NoFixedAddress" + +class AddressFixedAddress(BaseModel): + content: AddressDetails + + +class Address(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AddressTypes + content: Union[AddressFixedAddress, None] + + + @classmethod + def new_address_fixed_address(cls, content : AddressFixedAddress): + return cls( + type=AddressTypes.FIXED_ADDRESS, + content=content + ) + + + @classmethod + def new_address_no_fixed_address(cls) -> Address: + return cls( + type=AddressTypes.NO_FIXED_ADDRESS, + content=None + ) diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py new file mode 100644 index 00000000..e260548e --- /dev/null +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum + + +class Colors(Enum): + RED = "Red", + BLUE = "Blue", + GREEN = "Green" + diff --git a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py new file mode 100644 index 00000000..9f42dd58 --- /dev/null +++ b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py @@ -0,0 +1,26 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, Field +from typing import List, Optional + + +class Location(BaseModel): + pass + +class Person(BaseModel): + """ + This is a comment. + """ + name: str + """ + This is another comment + """ + age: int + info: Optional[str] = Field(default=None) + emails: List[str] + location: Location + + diff --git a/core/data/tests/can_generate_slice_of_user_type/output.py b/core/data/tests/can_generate_slice_of_user_type/output.py new file mode 100644 index 00000000..f975fd4d --- /dev/null +++ b/core/data/tests/can_generate_slice_of_user_type/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel +from typing import List + + +class Video(BaseModel): + tags: List[Tag] + + diff --git a/core/data/tests/can_generate_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_struct_with_skipped_fields/output.py new file mode 100644 index 00000000..82f30bfd --- /dev/null +++ b/core/data/tests/can_generate_struct_with_skipped_fields/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class MyStruct(BaseModel): + a: int + c: int + + diff --git a/core/data/tests/can_generate_unit_structs/output.py b/core/data/tests/can_generate_unit_structs/output.py new file mode 100644 index 00000000..4f8afe8e --- /dev/null +++ b/core/data/tests/can_generate_unit_structs/output.py @@ -0,0 +1,11 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class UnitStruct(BaseModel): + pass + diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py new file mode 100644 index 00000000..5061d84b --- /dev/null +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -0,0 +1,141 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class AutofilledByUs(BaseModel): + """ + Generated type representing the anonymous struct variant `Us` of the `AutofilledBy` Rust enum + """ + uuid: str + """ + The UUID for the fill + """ + + +class AutofilledBySomethingElse(BaseModel): + """ + Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum + """ + uuid: str + """ + The UUID for the fill + """ + thing: int + """ + Some other thing + """ + + +class AutofilledByTypes(str, Enum): + US = "Us" + SOMETHING_ELSE = "SomethingElse" + +class AutofilledBy(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AutofilledByTypes + content: Union[AutofilledByUs, AutofilledBySomethingElse] + + + @classmethod + def new_autofilled_by_us(cls, uuid: str): + return cls( + type=AutofilledByTypes.US, + content=AutofilledByUs(uuid = uuid) + ) + + + @classmethod + def new_autofilled_by_something_else(cls, uuid: str, thing: int): + return cls( + type=AutofilledByTypes.SOMETHING_ELSE, + content=AutofilledBySomethingElse(uuid = uuid, thing = thing) + ) +class EnumWithManyVariantsAnonVariant(BaseModel): + """ + Generated type representing the anonymous struct variant `AnonVariant` of the `EnumWithManyVariants` Rust enum + """ + uuid: str + + +class EnumWithManyVariantsAnotherAnonVariant(BaseModel): + """ + Generated type representing the anonymous struct variant `AnotherAnonVariant` of the `EnumWithManyVariants` Rust enum + """ + uuid: str + thing: int + + +class EnumWithManyVariantsTypes(str, Enum): + UNIT_VARIANT = "UnitVariant" + TUPLE_VARIANT_STRING = "TupleVariantString" + ANON_VARIANT = "AnonVariant" + TUPLE_VARIANT_INT = "TupleVariantInt" + ANOTHER_UNIT_VARIANT = "AnotherUnitVariant" + ANOTHER_ANON_VARIANT = "AnotherAnonVariant" + + +class EnumWithManyVariantsTupleVariantString(BaseModel): + content: str + +class EnumWithManyVariantsTupleVariantInt(BaseModel): + content: int + + +class EnumWithManyVariants(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: EnumWithManyVariantsTypes + content: Union[EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherAnonVariant, None] + + + @classmethod + def new_enum_with_many_variants_unit_variant(cls) -> EnumWithManyVariants: + return cls( + type=EnumWithManyVariantsTypes.UNIT_VARIANT, + content=None + ) + + + @classmethod + def new_enum_with_many_variants_tuple_variant_string(cls, content : EnumWithManyVariantsTupleVariantString): + return cls( + type=EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING, + content=content + ) + + + @classmethod + def new_enum_with_many_variants_anon_variant(cls, uuid: str): + return cls( + type=EnumWithManyVariantsTypes.ANON_VARIANT, + content=EnumWithManyVariantsAnonVariant(uuid = uuid) + ) + + + @classmethod + def new_enum_with_many_variants_tuple_variant_int(cls, content : EnumWithManyVariantsTupleVariantInt): + return cls( + type=EnumWithManyVariantsTypes.TUPLE_VARIANT_INT, + content=content + ) + + + @classmethod + def new_enum_with_many_variants_another_unit_variant(cls) -> EnumWithManyVariants: + return cls( + type=EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT, + content=None + ) + + + @classmethod + def new_enum_with_many_variants_another_anon_variant(cls, uuid: str, thing: int): + return cls( + type=EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT, + content=EnumWithManyVariantsAnotherAnonVariant(uuid = uuid, thing = thing) + ) diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py new file mode 100644 index 00000000..19626de0 --- /dev/null +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -0,0 +1,11 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum + + +class Colors(Enum): + GREEN = "Green\"" + diff --git a/core/data/tests/can_handle_serde_rename/output.py b/core/data/tests/can_handle_serde_rename/output.py new file mode 100644 index 00000000..0e7d580a --- /dev/null +++ b/core/data/tests/can_handle_serde_rename/output.py @@ -0,0 +1,26 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import List, Optional + + +class OtherType(BaseModel): + pass + +class Person(BaseModel): + """ + This is a comment. + """ + model_config = ConfigDict(populate_by_name=True) + + name: str + age: int + extra_special_field_1: int = Field(alias="extraSpecialFieldOne") + extra_special_field_2: Optional[List[str]] = Field(alias="extraSpecialFieldTwo", default=None) + non_standard_data_type: OtherType = Field(alias="nonStandardDataType") + non_standard_data_type_in_array: Optional[List[OtherType]] = Field(alias="nonStandardDataTypeInArray", default=None) + + diff --git a/core/data/tests/can_handle_serde_rename_all/output.py b/core/data/tests/can_handle_serde_rename_all/output.py new file mode 100644 index 00000000..db994ea8 --- /dev/null +++ b/core/data/tests/can_handle_serde_rename_all/output.py @@ -0,0 +1,33 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import List, Optional + + +class Person(BaseModel): + """ + This is a Person struct with camelCase rename + """ + model_config = ConfigDict(populate_by_name=True) + + first_name: str = Field(alias="firstName") + last_name: str = Field(alias="lastName") + age: int + extra_special_field_1: int = Field(alias="extraSpecialField1") + extra_special_field_2: Optional[List[str]] = Field(alias="extraSpecialField2", default=None) + + +class Person2(BaseModel): + """ + This is a Person2 struct with UPPERCASE rename + """ + model_config = ConfigDict(populate_by_name=True) + + first_name: str = Field(alias="FIRST_NAME") + last_name: str = Field(alias="LAST_NAME") + age: int = Field(alias="AGE") + + diff --git a/core/data/tests/can_handle_serde_rename_on_top_level/output.py b/core/data/tests/can_handle_serde_rename_on_top_level/output.py new file mode 100644 index 00000000..b27859e6 --- /dev/null +++ b/core/data/tests/can_handle_serde_rename_on_top_level/output.py @@ -0,0 +1,26 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import List, Optional + + +class OtherType(BaseModel): + pass + +class PersonTwo(BaseModel): + """ + This is a comment. + """ + model_config = ConfigDict(populate_by_name=True) + + name: str + age: int + extra_special_field_1: int = Field(alias="extraSpecialFieldOne") + extra_special_field_2: Optional[List[str]] = Field(alias="extraSpecialFieldTwo", default=None) + non_standard_data_type: OtherType = Field(alias="nonStandardDataType") + non_standard_data_type_in_array: Optional[List[OtherType]] = Field(alias="nonStandardDataTypeInArray", default=None) + + diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py new file mode 100644 index 00000000..bbc4231e --- /dev/null +++ b/core/data/tests/can_handle_unit_type/output.py @@ -0,0 +1,36 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict, Field + + +class StructHasVoidType(BaseModel): + """ + This struct has a unit field + """ + model_config = ConfigDict(populate_by_name=True) + + this_is_a_unit: None = Field(alias="thisIsAUnit") + + +class EnumHasVoidTypeTypes(str, Enum): + HAS_A_UNIT = "hasAUnit" + +class EnumHasVoidTypeHasAUnit(BaseModel): + content: None + +class EnumHasVoidType(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: EnumHasVoidTypeTypes + content: EnumHasVoidTypeHasAUnit + + + @classmethod + def new_enum_has_void_type_has_a_unit(cls, content : EnumHasVoidTypeHasAUnit): + return cls( + type=EnumHasVoidTypeTypes.HAS_A_UNIT, + content=content + ) diff --git a/core/data/tests/can_recognize_types_inside_modules/output.py b/core/data/tests/can_recognize_types_inside_modules/output.py new file mode 100644 index 00000000..d01df05f --- /dev/null +++ b/core/data/tests/can_recognize_types_inside_modules/output.py @@ -0,0 +1,24 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class A(BaseModel): + field: int + + +class AB(BaseModel): + field: int + + +class ABC(BaseModel): + field: int + + +class OutsideOfModules(BaseModel): + field: int + + diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py new file mode 100644 index 00000000..152af0a3 --- /dev/null +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum + + +class Colors(Enum): + RED = "red", + BLUE = "blue", + GREEN = "green-like" + diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py new file mode 100644 index 00000000..fd3106da --- /dev/null +++ b/core/data/tests/excluded_by_target_os/output.py @@ -0,0 +1,106 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class DefinedTwice(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + field_1: str + + +class Excluded(BaseModel): + pass + +class ManyStruct(BaseModel): + pass + +class MultipleTargets(BaseModel): + pass + +class NestedNotTarget1(BaseModel): + pass + +class OtherExcluded(BaseModel): + pass + +class SomeEnum(Enum): + pass + +class TestEnumVariant7(BaseModel): + """ + Generated type representing the anonymous struct variant `Variant7` of the `TestEnum` Rust enum + """ + model_config = ConfigDict(populate_by_name=True) + + field_1: str + + +class TestEnumVariant9(BaseModel): + """ + Generated type representing the anonymous struct variant `Variant9` of the `TestEnum` Rust enum + """ + model_config = ConfigDict(populate_by_name=True) + + field_2: str + + +class TestEnumTypes(str, Enum): + VARIANT_1 = "Variant1" + VARIANT_5 = "Variant5" + VARIANT_7 = "Variant7" + VARIANT_8 = "Variant8" + VARIANT_9 = "Variant9" + + + + +class TestEnum(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: TestEnumTypes + content: Union[TestEnumVariant7, TestEnumVariant9, None] + + + @classmethod + def new_test_enum_variant_1(cls) -> TestEnum: + return cls( + type=TestEnumTypes.VARIANT_1, + content=None + ) + + + @classmethod + def new_test_enum_variant_5(cls) -> TestEnum: + return cls( + type=TestEnumTypes.VARIANT_5, + content=None + ) + + + @classmethod + def new_test_enum_variant_7(cls, field_1: str): + return cls( + type=TestEnumTypes.VARIANT_7, + content=TestEnumVariant7(field_1 = field_1) + ) + + + @classmethod + def new_test_enum_variant_8(cls) -> TestEnum: + return cls( + type=TestEnumTypes.VARIANT_8, + content=None + ) + + + @classmethod + def new_test_enum_variant_9(cls, field_2: str): + return cls( + type=TestEnumTypes.VARIANT_9, + content=TestEnumVariant9(field_2 = field_2) + ) diff --git a/core/data/tests/generate_types/output.py b/core/data/tests/generate_types/output.py new file mode 100644 index 00000000..3ea4c904 --- /dev/null +++ b/core/data/tests/generate_types/output.py @@ -0,0 +1,27 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import Dict, List, Optional + + +class CustomType(BaseModel): + pass + +class Types(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + s: str + static_s: str + int_8: int + float: float + double: float + array: List[str] + fixed_length_array: List[str] + dictionary: Dict[str, int] + optional_dictionary: Optional[Dict[str, int]] = Field(default=None) + custom_type: CustomType + + diff --git a/core/data/tests/generates_empty_structs_and_initializers/output.py b/core/data/tests/generates_empty_structs_and_initializers/output.py new file mode 100644 index 00000000..97b6ecee --- /dev/null +++ b/core/data/tests/generates_empty_structs_and_initializers/output.py @@ -0,0 +1,11 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class MyEmptyStruct(BaseModel): + pass + diff --git a/core/data/tests/kebab_case_rename/output.py b/core/data/tests/kebab_case_rename/output.py new file mode 100644 index 00000000..a63235b6 --- /dev/null +++ b/core/data/tests/kebab_case_rename/output.py @@ -0,0 +1,20 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import Optional + + +class Things(BaseModel): + """ + This is a comment. + """ + model_config = ConfigDict(populate_by_name=True) + + bla: str + some_label: Optional[str] = Field(alias="label", default=None) + label_left: Optional[str] = Field(alias="label-left", default=None) + + diff --git a/core/data/tests/orders_types/output.py b/core/data/tests/orders_types/output.py new file mode 100644 index 00000000..5bfa3548 --- /dev/null +++ b/core/data/tests/orders_types/output.py @@ -0,0 +1,38 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field +from typing import Optional + + +class A(BaseModel): + field: int + + +class B(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + depends_on: A = Field(alias="dependsOn") + + +class C(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + depends_on: B = Field(alias="dependsOn") + + +class E(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + depends_on: D = Field(alias="dependsOn") + + +class D(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + depends_on: C = Field(alias="dependsOn") + also_depends_on: Optional[E] = Field(alias="alsoDependsOn", default=None) + + diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py new file mode 100644 index 00000000..9c23d68a --- /dev/null +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -0,0 +1,102 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class MoreOptionsExactly(BaseModel): + """ + Generated type representing the anonymous struct variant `Exactly` of the `MoreOptions` Rust enum + """ + config: str + + +class MoreOptionsBuilt(BaseModel): + """ + Generated type representing the anonymous struct variant `Built` of the `MoreOptions` Rust enum + """ + top: MoreOptions + + +class MoreOptionsTypes(str, Enum): + NEWS = "news" + EXACTLY = "exactly" + BUILT = "built" + +class MoreOptionsNews(BaseModel): + content: bool + +class MoreOptions(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: MoreOptionsTypes + content: Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] + + + @classmethod + def new_more_options_news(cls, content : MoreOptionsNews): + return cls( + type=MoreOptionsTypes.NEWS, + content=content + ) + + + @classmethod + def new_more_options_exactly(cls, config: str): + return cls( + type=MoreOptionsTypes.EXACTLY, + content=MoreOptionsExactly(config = config) + ) + + + @classmethod + def new_more_options_built(cls, top: MoreOptions): + return cls( + type=MoreOptionsTypes.BUILT, + content=MoreOptionsBuilt(top = top) + ) +class OptionsTypes(str, Enum): + RED = "red" + BANANA = "banana" + VERMONT = "vermont" + +class OptionsRed(BaseModel): + content: bool + +class OptionsBanana(BaseModel): + content: str + +class OptionsVermont(BaseModel): + content: Options + +class Options(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: OptionsTypes + content: Union[OptionsRed, OptionsBanana, OptionsVermont] + + + @classmethod + def new_options_red(cls, content : OptionsRed): + return cls( + type=OptionsTypes.RED, + content=content + ) + + + @classmethod + def new_options_banana(cls, content : OptionsBanana): + return cls( + type=OptionsTypes.BANANA, + content=content + ) + + + @classmethod + def new_options_vermont(cls, content : OptionsVermont): + return cls( + type=OptionsTypes.VERMONT, + content=content + ) diff --git a/core/data/tests/resolves_qualified_type/output.py b/core/data/tests/resolves_qualified_type/output.py new file mode 100644 index 00000000..1dfb932b --- /dev/null +++ b/core/data/tests/resolves_qualified_type/output.py @@ -0,0 +1,18 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, Field +from typing import Dict, List, Optional + + +class QualifiedTypes(BaseModel): + unqualified: str + qualified: str + qualified_vec: List[str] + qualified_hashmap: Dict[str, str] + qualified_optional: Optional[str] = Field(default=None) + qualfied_optional_hashmap_vec: Optional[Dict[str, List[str]]] = Field(default=None) + + diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py new file mode 100644 index 00000000..f77fcf55 --- /dev/null +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -0,0 +1,40 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import Union + + +class SomeEnumTypes(str, Enum): + CONTEXT = "Context" + OTHER = "Other" + +class SomeEnumContext(BaseModel): + content: str + +class SomeEnumOther(BaseModel): + content: int + +class SomeEnum(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: SomeEnumTypes + content: Union[SomeEnumContext, SomeEnumOther] + + + @classmethod + def new_some_enum_context(cls, content : SomeEnumContext): + return cls( + type=SomeEnumTypes.CONTEXT, + content=content + ) + + + @classmethod + def new_some_enum_other(cls, content : SomeEnumOther): + return cls( + type=SomeEnumTypes.OTHER, + content=content + ) diff --git a/core/data/tests/serialize_field_as/output.py b/core/data/tests/serialize_field_as/output.py new file mode 100644 index 00000000..b926fbfb --- /dev/null +++ b/core/data/tests/serialize_field_as/output.py @@ -0,0 +1,15 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, Field +from typing import List, Optional + + +class EditItemViewModelSaveRequest(BaseModel): + context: str + values: List[EditItemSaveValue] + fill_action: Optional[AutoFillItemActionRequest] = Field(default=None) + + diff --git a/core/data/tests/serialize_type_alias/output.py b/core/data/tests/serialize_type_alias/output.py new file mode 100644 index 00000000..2fe312b6 --- /dev/null +++ b/core/data/tests/serialize_type_alias/output.py @@ -0,0 +1,23 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + + + + +Uuid = str + + +AccountUuid = Uuid + + +""" +Unique identifier for an Account +""" +AlsoString = str + + +ItemUuid = str + + diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py new file mode 100644 index 00000000..4cc548d1 --- /dev/null +++ b/core/data/tests/smart_pointers/output.py @@ -0,0 +1,96 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import List, Union + + +class ArcyColors(BaseModel): + """ + This is a comment. + """ + red: int + blue: str + green: List[str] + + +class CellyColors(BaseModel): + """ + This is a comment. + """ + red: str + blue: List[str] + + +class CowyColors(BaseModel): + """ + This is a comment. + """ + lifetime: str + + +class LockyColors(BaseModel): + """ + This is a comment. + """ + red: str + + +class MutexyColors(BaseModel): + """ + This is a comment. + """ + blue: List[str] + green: str + + +class RcyColors(BaseModel): + """ + This is a comment. + """ + red: str + blue: List[str] + green: str + + +class BoxyColorsTypes(str, Enum): + RED = "Red" + BLUE = "Blue" + GREEN = "Green" + + + +class BoxyColorsGreen(BaseModel): + content: str + +class BoxyColors(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: BoxyColorsTypes + content: Union[BoxyColorsGreen, None] + + + @classmethod + def new_boxy_colors_red(cls) -> BoxyColors: + return cls( + type=BoxyColorsTypes.RED, + content=None + ) + + + @classmethod + def new_boxy_colors_blue(cls) -> BoxyColors: + return cls( + type=BoxyColorsTypes.BLUE, + content=None + ) + + + @classmethod + def new_boxy_colors_green(cls, content : BoxyColorsGreen): + return cls( + type=BoxyColorsTypes.GREEN, + content=content + ) diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py new file mode 100644 index 00000000..9e25fbcc --- /dev/null +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -0,0 +1,67 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum +from pydantic import BaseModel, ConfigDict +from typing import List, Union + + +class ItemDetailsFieldValue(BaseModel): + pass + +class AdvancedColorsTypes(str, Enum): + STRING = "string" + NUMBER = "number" + NUMBER_ARRAY = "number-array" + REALLY_COOL_TYPE = "reallyCoolType" + +class AdvancedColorsString(BaseModel): + content: str + +class AdvancedColorsNumber(BaseModel): + content: int + +class AdvancedColorsNumberArray(BaseModel): + content: List[int] + +class AdvancedColorsReallyCoolType(BaseModel): + content: ItemDetailsFieldValue + +class AdvancedColors(BaseModel): + model_config = ConfigDict(use_enum_values=True) + type: AdvancedColorsTypes + content: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] + + + @classmethod + def new_advanced_colors_string(cls, content : AdvancedColorsString): + return cls( + type=AdvancedColorsTypes.STRING, + content=content + ) + + + @classmethod + def new_advanced_colors_number(cls, content : AdvancedColorsNumber): + return cls( + type=AdvancedColorsTypes.NUMBER, + content=content + ) + + + @classmethod + def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): + return cls( + type=AdvancedColorsTypes.NUMBER_ARRAY, + content=content + ) + + + @classmethod + def new_advanced_colors_really_cool_type(cls, content : AdvancedColorsReallyCoolType): + return cls( + type=AdvancedColorsTypes.REALLY_COOL_TYPE, + content=content + ) diff --git a/core/data/tests/test_generate_char/output.py b/core/data/tests/test_generate_char/output.py new file mode 100644 index 00000000..1bb1fb06 --- /dev/null +++ b/core/data/tests/test_generate_char/output.py @@ -0,0 +1,12 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class MyType(BaseModel): + field: str + + diff --git a/core/data/tests/test_i54_u53_type/output.py b/core/data/tests/test_i54_u53_type/output.py new file mode 100644 index 00000000..3a748bd3 --- /dev/null +++ b/core/data/tests/test_i54_u53_type/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class Foo(BaseModel): + a: int + b: int + + diff --git a/core/data/tests/test_optional_type_alias/output.py b/core/data/tests/test_optional_type_alias/output.py new file mode 100644 index 00000000..e599c2a4 --- /dev/null +++ b/core/data/tests/test_optional_type_alias/output.py @@ -0,0 +1,19 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +OptionalU16 = int + + +OptionalU32 = int + + +class FooBar(BaseModel): + foo: OptionalU32 + bar: OptionalU16 + + diff --git a/core/data/tests/test_serde_default_struct/output.py b/core/data/tests/test_serde_default_struct/output.py new file mode 100644 index 00000000..af347221 --- /dev/null +++ b/core/data/tests/test_serde_default_struct/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel, Field +from typing import Optional + + +class Foo(BaseModel): + bar: Optional[bool] = Field(default=None) + + diff --git a/core/data/tests/test_serde_url/output.py b/core/data/tests/test_serde_url/output.py new file mode 100644 index 00000000..8461fb12 --- /dev/null +++ b/core/data/tests/test_serde_url/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel +from pydantic.networks import AnyUrl + + +class Foo(BaseModel): + url: AnyUrl + + diff --git a/core/data/tests/test_serialized_as/output.py b/core/data/tests/test_serialized_as/output.py new file mode 100644 index 00000000..c018d741 --- /dev/null +++ b/core/data/tests/test_serialized_as/output.py @@ -0,0 +1,17 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + + + + +ItemId = str + + +Options = str + + +""" +Options that you could pick +""" diff --git a/core/data/tests/test_serialized_as_tuple/output.py b/core/data/tests/test_serialized_as_tuple/output.py new file mode 100644 index 00000000..215c119a --- /dev/null +++ b/core/data/tests/test_serialized_as_tuple/output.py @@ -0,0 +1,11 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + + + + +ItemId = str + + diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py new file mode 100644 index 00000000..b395ae77 --- /dev/null +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -0,0 +1,13 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from enum import Enum + + +class Colors(Enum): + RED = "red", + BLUE = "blue-ish", + GREEN = "Green" + diff --git a/core/data/tests/test_type_alias/output.py b/core/data/tests/test_type_alias/output.py new file mode 100644 index 00000000..51c0f4b2 --- /dev/null +++ b/core/data/tests/test_type_alias/output.py @@ -0,0 +1,15 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +Bar = str + + +class Foo(BaseModel): + bar: Bar + + diff --git a/core/data/tests/use_correct_decoded_variable_name/output.py b/core/data/tests/use_correct_decoded_variable_name/output.py new file mode 100644 index 00000000..97b6ecee --- /dev/null +++ b/core/data/tests/use_correct_decoded_variable_name/output.py @@ -0,0 +1,11 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class MyEmptyStruct(BaseModel): + pass + diff --git a/core/data/tests/use_correct_integer_types/output.py b/core/data/tests/use_correct_integer_types/output.py new file mode 100644 index 00000000..dbd78b9c --- /dev/null +++ b/core/data/tests/use_correct_integer_types/output.py @@ -0,0 +1,20 @@ +""" + Generated by typeshare 1.11.0 +""" +from __future__ import annotations + +from pydantic import BaseModel + + +class Foo(BaseModel): + """ + This is a comment. + """ + a: int + b: int + c: int + e: int + f: int + g: int + + diff --git a/core/src/language/python.rs b/core/src/language/python.rs index cf8be267..7e931293 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -1,5 +1,7 @@ use crate::parser::ParsedData; -use crate::rust_types::{RustItem, RustType, RustTypeFormatError, SpecialRustType}; +use crate::rust_types::{ + RustEnumShared, RustEnumVariantShared, RustItem, RustType, RustTypeFormatError, SpecialRustType, +}; use crate::topsort::topsort; use crate::{ language::Language, @@ -238,9 +240,8 @@ impl Language for Python { let bases = match rs.generic_types.is_empty() { true => "BaseModel".to_string(), false => { - self.add_import("pydantic.generics".to_string(), "GenericModel".to_string()); self.add_import("typing".to_string(), "Generic".to_string()); - format!("GenericModel, Generic[{}]", rs.generic_types.join(", ")) + format!("BaseModel, Generic[{}]", rs.generic_types.join(", ")) } }; writeln!(w, "class {}({}):", rs.id.renamed, bases,)?; @@ -264,36 +265,39 @@ impl Language for Python { fn write_enum(&mut self, w: &mut dyn Write, e: &RustEnum) -> std::io::Result<()> { // Make a suitable name for an anonymous struct enum variant let make_anonymous_struct_name = - |variant_name: &str| format!("{}{}Inner", &e.shared().id.original, variant_name); + |variant_name: &str| format!("{}{}", &e.shared().id.original, variant_name); // Generate named types for any anonymous struct variants of this enum self.write_types_for_anonymous_structs(w, e, &make_anonymous_struct_name)?; - + self.add_import("enum".to_string(), "Enum".to_string()); match e { // Write all the unit variants out (there can only be unit variants in // this case) RustEnum::Unit(shared) => { - self.add_import("typing".to_string(), "Literal".to_string()); - write!( - w, - "{} = Literal[{}]", - shared.id.renamed, + writeln!(w, "class {}(Enum):", shared.id.renamed)?; + + let fields = if shared.variants.is_empty() { + " pass".to_string() + } else { shared .variants .iter() - .map(|v| format!( - "\"{}\"", - match v { - RustEnumVariant::Unit(v) => { - v.id.renamed.clone() + .map(|v| { + format!( + " {} = \"{}\"", + v.shared().id.original.to_uppercase(), + match v { + RustEnumVariant::Unit(v) => { + v.id.renamed.replace("\"", "\\\"") + } + _ => panic!(), } - _ => panic!(), - } - )) + ) + }) .collect::>() - .join(", ") - )?; - write!(w, "\n\n")?; + .join(",\n") + }; + writeln!(w, "{fields}\n")?; } // Write all the algebraic variants out (all three variant types are possible // here) @@ -303,168 +307,13 @@ impl Language for Python { shared, .. } => { - { - shared - .generic_types - .iter() - .cloned() - .for_each(|v| self.add_type_var(v)) - } - let mut variants: Vec<(String, Vec)> = Vec::new(); - shared.variants.iter().for_each(|variant| { - match variant { - RustEnumVariant::Unit(unit_variant) => { - self.add_import("typing".to_string(), "Literal".to_string()); - let variant_name = - format!("{}{}", shared.id.original, unit_variant.id.original); - variants.push((variant_name.clone(), vec![])); - writeln!(w, "class {}:", variant_name).unwrap(); - writeln!( - w, - " {}: Literal[\"{}\"]", - tag_key, unit_variant.id.renamed - ) - .unwrap(); - } - RustEnumVariant::Tuple { - ty, - shared: variant_shared, - } => { - self.add_import("typing".to_string(), "Literal".to_string()); - let variant_name = - format!("{}{}", shared.id.original, variant_shared.id.original); - match ty { - RustType::Generic { id: _, parameters } => { - // This variant has generics, include them in the class def - let mut generic_parameters: Vec = parameters - .iter() - .flat_map(|p| { - collect_generics_for_variant(p, &shared.generic_types) - }) - .collect(); - dedup(&mut generic_parameters); - let type_vars = self.get_type_vars(generic_parameters.len()); - variants.push((variant_name.clone(), type_vars)); - { - if generic_parameters.is_empty() { - self.add_import( - "pydantic".to_string(), - "BaseModel".to_string(), - ); - writeln!(w, "class {}(BaseModel):", variant_name) - .unwrap(); - } else { - self.add_import( - "typing".to_string(), - "Generic".to_string(), - ); - self.add_import( - "pydantic.generics".to_string(), - "GenericModel".to_string(), - ); - writeln!( - w, - "class {}(GenericModel, Generic[{}]):", - // note: generics is always unique (a single item) - variant_name, - generic_parameters.join(", ") - ) - .unwrap(); - } - } - } - other => { - let mut generics = vec![]; - if let RustType::Simple { id } = other { - // This could be a bare generic - if shared.generic_types.contains(id) { - generics = vec![id.clone()]; - } - } - variants.push((variant_name.clone(), generics.clone())); - { - if generics.is_empty() { - self.add_import( - "pydantic".to_string(), - "BaseModel".to_string(), - ); - writeln!(w, "class {}(BaseModel):", variant_name) - .unwrap(); - } else { - self.add_import( - "typing".to_string(), - "Generic".to_string(), - ); - self.add_import( - "pydantic.generics".to_string(), - "GenericModel".to_string(), - ); - writeln!( - w, - "class {}(GenericModel, Generic[{}]):", - // note: generics is always unique (a single item) - variant_name, - generics.join(", ") - ) - .unwrap(); - } - } - } - }; - writeln!( - w, - " {}: Literal[\"{}\"]", - tag_key, variant_shared.id.renamed - ) - .unwrap(); - writeln!( - w, - " {}: {}", - content_key, - match ty { - RustType::Simple { id } => id.to_owned(), - RustType::Special(special_ty) => self - .format_special_type(special_ty, &shared.generic_types) - .unwrap(), - RustType::Generic { id, parameters } => { - self.format_generic_type(id, parameters, &[]).unwrap() - } - } - ) - .unwrap(); - write!(w, "\n\n").unwrap(); - } - RustEnumVariant::AnonymousStruct { - shared: variant_shared, - fields, - } => { - let num_generic_parameters = fields - .iter() - .flat_map(|f| { - collect_generics_for_variant(&f.ty, &shared.generic_types) - }) - .count(); - let type_vars = self.get_type_vars(num_generic_parameters); - let name = make_anonymous_struct_name(&variant_shared.id.original); - variants.push((name, type_vars)); - } - }; - }); - writeln!( + self.write_algebraic_enum( + tag_key, + content_key, + shared, w, - "{} = {}", - shared.id.original, - variants - .iter() - .map(|(name, parameters)| match parameters.is_empty() { - true => name.clone(), - false => format!("{}[{}]", name, parameters.join(", ")), - }) - .collect::>() - .join(" | ") + &make_anonymous_struct_name, )?; - self.write_comments(w, true, &e.shared().comments, 0)?; - writeln!(w)?; } }; Ok(()) @@ -498,37 +347,40 @@ impl Python { field: &RustField, generic_types: &[String], ) -> std::io::Result<()> { - let mut python_type = self + let is_optional = field.ty.is_optional() || field.has_default; + let python_type = self .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - if field.ty.is_optional() { - python_type = format!("Optional[{}]", python_type); - self.add_import("typing".to_string(), "Optional".to_string()); - } - python_type = match python_field_name == field.id.renamed { - true => python_type, - false => { - self.add_import("typing".to_string(), "Annotated".to_string()); + let is_aliased = field.id.original != field.id.renamed; + match (is_optional, is_aliased) { + (true, true) => { + self.add_import("typing".to_string(), "Optional".to_string()); self.add_import("pydantic".to_string(), "Field".to_string()); - format!( - "Annotated[{}, Field(alias=\"{}\")]", - python_type, field.id.renamed - ) + writeln!(w, " {python_field_name}: Optional[{python_type}] = Field(alias=\"{renamed}\", default=None)", renamed=field.id.renamed)?; } - }; - // TODO: Add support for default values other than None - match field.has_default && field.ty.is_optional() { - true => { - // in the future we will want to get the default value properly, something like: - // let default_value = get_default_value(...) - let default_value = "None"; + (true, false) => { + self.add_import("typing".to_string(), "Optional".to_string()); + self.add_import("pydantic".to_string(), "Field".to_string()); writeln!( w, - " {python_field_name}: {python_type} = {default_value}" + " {python_field_name}: Optional[{python_type}] = Field(default=None)" )? } - false => writeln!(w, " {python_field_name}: {python_type}")?, + (false, true) => { + self.add_import("pydantic".to_string(), "Field".to_string()); + writeln!( + w, + " {python_field_name}: {python_type} = Field(alias=\"{renamed}\")", + renamed = field.id.renamed + )? + } + (false, false) => writeln!( + w, + " {python_field_name}: {python_type}", + python_field_name = python_field_name, + python_type = python_type + )?, } self.write_comments(w, true, &field.comments, 1)?; @@ -579,20 +431,6 @@ impl Python { self.type_variables.insert(name); } - fn get_type_vars(&mut self, n: usize) -> Vec { - let vars: Vec = (0..n) - .map(|i| { - if i == 0 { - "T".to_string() - } else { - format!("T{}", i) - } - }) - .collect(); - vars.iter().for_each(|tv| self.add_type_var(tv.clone())); - vars - } - fn write_all_imports(&self, w: &mut dyn Write) -> std::io::Result<()> { let mut type_var_names: Vec = self.type_variables.iter().cloned().collect(); type_var_names.sort(); @@ -621,6 +459,293 @@ impl Python { }; Ok(()) } + + fn get_constructor_method_name(enum_name: &str, variant_name: &str) -> String { + format!("new_{}_{}", enum_name, variant_name).to_case(Case::Snake) + } + + fn gen_unit_variant_constructor( + variant_constructors: &mut Vec, + variant_shared: &RustEnumVariantShared, + enum_shared: &RustEnumShared, + tag_key: &str, + content_key: &str, + ) { + let method_name = + Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); + + variant_constructors.push(format!( + r#" + @classmethod + def {method_name}(cls) -> {class_name}: + return cls( + {tag_key}={enum_name}Types.{variant_tag}, + {content_key}=None + )"#, + tag_key = tag_key, + content_key = content_key, + enum_name = enum_shared.id.renamed, + variant_tag = variant_shared + .id + .renamed + .to_case(Case::Snake) + .to_uppercase(), + class_name = enum_shared.id.renamed, + )); + } + + fn gen_tuple_variant_constructor( + variant_constructors: &mut Vec, + variant_shared: &RustEnumVariantShared, + enum_shared: &RustEnumShared, + param_type: String, + tag_key: &str, + content_key: &str, + ) { + let method_name = + Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); + + variant_constructors.push(format!( + r#" + @classmethod + def {method_name}(cls, {content_key} : {param_type}): + return cls( + {tag_key}={enum_name}Types.{variant_tag}, + {content_key}={content_key} + )"#, + enum_name = enum_shared.id.renamed, + variant_tag = variant_shared + .id + .renamed + .to_case(Case::Snake) + .to_uppercase(), + )); + } + + #[allow(clippy::too_many_arguments)] + fn gen_anon_struct_variant_constructor( + &mut self, + variant_constructors: &mut Vec, + variant_fields: &[RustField], + variant_shared: &RustEnumVariantShared, + enum_shared: &RustEnumShared, + class_name: &str, + tag_key: &str, + content_key: &str, + ) { + let method_name = + Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); + + let ctor_param = variant_fields + .iter() + .map(|f| { + let python_field_name = python_property_aware_rename(&f.id.original); + let python_type = self + .format_type(&f.ty, enum_shared.generic_types.as_slice()) + .unwrap(); + (python_field_name, python_type) + }) + .collect::>(); + variant_constructors.push(format!( + r#" + @classmethod + def {method_name}(cls, {ctor_params}): + return cls( + {tag_key}={enum_name}Types.{variant_tag}, + {content_key}={class_name}({ctor_params_names}) + )"#, + ctor_params = ctor_param + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty)) + .collect::>() + .join(", "), + content_key = content_key, + class_name = class_name, + ctor_params_names = ctor_param + .iter() + .map(|(name, _)| format!("{name} = {name}")) + .collect::>() + .join(", "), + tag_key = tag_key, + enum_name = enum_shared.id.renamed, + variant_tag = variant_shared + .id + .renamed + .to_case(Case::Snake) + .to_uppercase(), + )); + } + + fn write_algebraic_enum( + &mut self, + tag_key: &str, + content_key: &str, + shared: &RustEnumShared, + w: &mut dyn Write, + make_struct_name: &dyn Fn(&str) -> String, + ) -> std::io::Result<()> { + shared + .generic_types + .iter() + .cloned() + .for_each(|v| self.add_type_var(v)); + let mut variants: Vec<(String, Vec)> = Vec::new(); + self.add_import("pydantic".to_string(), "ConfigDict".to_string()); + self.add_import("pydantic".to_string(), "BaseModel".to_string()); + // write "types" class: a union of all the enum variants + writeln!(w, "class {}Types(str, Enum):", shared.id.renamed)?; + let all_enum_variants_name = shared + .variants + .iter() + .map(|v| match v { + RustEnumVariant::Unit(v) => v.id.renamed.clone(), + RustEnumVariant::Tuple { shared, .. } => shared.id.renamed.clone(), + RustEnumVariant::AnonymousStruct { shared, .. } => shared.id.renamed.clone(), + }) + .collect::>(); + writeln!( + w, + "{}", + all_enum_variants_name + .iter() + .map(|name| format!( + " {} = \"{}\"", + name.to_case(Case::Snake).to_uppercase(), + name + )) + .collect::>() + .join("\n") + )?; + writeln!(w)?; + + let mut variant_class_names = vec![]; + let mut variant_constructors = vec![]; + let mut contains_unit_variant = false; + // write each of the enum variant as a class: + for variant in &shared.variants { + let variant_class_name = make_struct_name(&variant.shared().id.original); + match variant { + RustEnumVariant::Unit(unit_variant) => { + contains_unit_variant = true; + let variant_name = format!("{}{}", shared.id.renamed, unit_variant.id.renamed); + variants.push((variant_name.clone(), vec![])); + Self::gen_unit_variant_constructor( + &mut variant_constructors, + unit_variant, + shared, + tag_key, + content_key, + ); + writeln!(w)?; + } + RustEnumVariant::Tuple { + ty, + shared: variant_shared, + } => { + variant_class_names.push(variant_class_name.clone()); + Self::gen_tuple_variant_constructor( + &mut variant_constructors, + variant_shared, + shared, + variant_class_name.clone(), + tag_key, + content_key, + ); + match ty { + RustType::Generic { parameters, .. } => { + let mut generic_parameters: Vec = parameters + .iter() + .flat_map(|p| { + collect_generics_for_variant(p, &shared.generic_types) + }) + .collect(); + dedup(&mut generic_parameters); + if generic_parameters.is_empty() { + writeln!(w, "class {variant_class_name}(BaseModel):",).unwrap(); + } else { + self.add_import("typing".to_string(), "Generic".to_string()); + writeln!( + w, + "class {variant_class_name}(BaseModel, Generic[{}]):", + // note: generics is always unique (a single item) + generic_parameters.join(", ") + ) + .unwrap(); + } + } + other => { + let mut generics = vec![]; + if let RustType::Simple { id } = other { + // This could be a bare generic + if shared.generic_types.contains(id) { + generics = vec![id.clone()]; + } + } + if generics.is_empty() { + writeln!(w, "class {variant_class_name}(BaseModel):",).unwrap(); + } else { + self.add_import("typing".to_string(), "Generic".to_string()); + writeln!( + w, + "class {variant_class_name}(BaseModel, Generic[{}]):", + generics.join(", ") + ) + .unwrap(); + } + } + } + let python_type = self + .format_type(ty, shared.generic_types.as_slice()) + .unwrap(); + writeln!(w, " {content_key}: {python_type}")?; + writeln!(w)?; + } + RustEnumVariant::AnonymousStruct { + fields, + shared: variant_shared, + } => { + variant_class_names.push(variant_class_name.clone()); + // writing is taken care of by write_types_for_anonymous_structs in write_enum + // we just need to push to the variant_constructors + self.gen_anon_struct_variant_constructor( + &mut variant_constructors, + fields, + variant_shared, + shared, + &variant_class_name, + tag_key, + content_key, + ); + } + } + } + if contains_unit_variant { + variant_class_names.push("None".to_string()); + } + + // finally, write the enum class itself consists of a type and a union of all the enum variants + + writeln!(w, "class {}(BaseModel):", shared.id.renamed)?; + writeln!(w, " model_config = ConfigDict(use_enum_values=True)")?; + writeln!(w, " {tag_key}: {}Types", shared.id.renamed)?; + // if there is only 1 variant, we can use that directly, no need for Union + let union_type = if variant_class_names.len() == 1 { + variant_class_names[0].clone() + } else { + self.add_import("typing".to_string(), "Union".to_string()); + format!("Union[{}]", variant_class_names.join(", ")) + }; + writeln!(w, " {content_key}: {union_type}",)?; + writeln!(w)?; + if !variant_constructors.is_empty() { + writeln!( + w, + "{variant_constructors}", + variant_constructors = variant_constructors.join("\n\n") + )?; + } + Ok(()) + } } static PYTHON_KEYWORDS: Lazy> = Lazy::new(|| { @@ -658,7 +783,9 @@ fn handle_model_config(w: &mut dyn Write, python_module: &mut Python, rs: &RustS #[cfg(test)] mod test { - use crate::rust_types::Id; + use syn::{parse_str, ItemEnum}; + + use crate::{parser::parse_enum, rust_types::Id}; use super::*; #[test] @@ -686,7 +813,7 @@ mod test { python.write_field(mock_writer, &rust_field, &[]).unwrap(); assert_eq!( String::from_utf8_lossy(mock_writer), - " field: Optional[str] = None\n" + " field: Optional[str] = Field(default=None)\n" ); } @@ -709,7 +836,7 @@ mod test { python.write_field(mock_writer, &rust_field, &[]).unwrap(); assert_eq!( String::from_utf8_lossy(mock_writer), - " field: Optional[str]\n" + " field: Optional[str] = Field(default=None)\n" ); } @@ -732,7 +859,10 @@ mod test { decorators: Default::default(), }; python.write_field(mock_writer, &rust_field, &[]).unwrap(); - assert_eq!(String::from_utf8_lossy(mock_writer), " field: str\n"); + assert_eq!( + String::from_utf8_lossy(mock_writer), + " field: Optional[str] = Field(default=None)\n" + ); } #[test] diff --git a/core/tests/snapshot_tests.rs b/core/tests/snapshot_tests.rs index 6d9b887f..4bf277ca 100644 --- a/core/tests/snapshot_tests.rs +++ b/core/tests/snapshot_tests.rs @@ -138,6 +138,9 @@ macro_rules! output_file_for_ident { (go) => { "output.go" }; + (python) => { + "output.py" + }; } /// Simplifies the construction of `Language` instances for each language. @@ -191,6 +194,20 @@ macro_rules! language_instance { }) }; + // Default Python + (python) => { + language_instance!(python { }) + }; + + // python with configuration fields forwarded + (python {$($field:ident: $val:expr),* $(,)?}) => { + #[allow(clippy::needless_update)] + Box::new(typeshare_core::language::Python { + $($field: $val,)* + ..Default::default() + }) + }; + // Default scala (scala) => { language_instance!(scala { @@ -406,6 +423,13 @@ static GO_MAPPINGS: Lazy> = Lazy::new(|| { .collect() }); +static PYTHON_MAPPINGS: Lazy> = Lazy::new(|| { + [("Url", "AnyUrl"), ("DateTime", "datetime")] + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect() +}); + tests! { /// Enums can_generate_algebraic_enum: [ @@ -421,7 +445,8 @@ tests! { module_name: "colorsModule".to_string(), }, typescript, - go + go, + python ]; can_generate_generic_enum: [ swift { @@ -438,7 +463,7 @@ tests! { }, kotlin, scala, - typescript + typescript, ]; can_generate_generic_type_alias: [ swift { @@ -448,7 +473,7 @@ tests! { scala, typescript ]; - can_generate_slice_of_user_type: [swift, kotlin, scala, typescript, go]; + can_generate_slice_of_user_type: [swift, kotlin, scala, typescript, go, python]; can_generate_readonly_fields: [ typescript ]; @@ -459,16 +484,17 @@ tests! { kotlin, scala, typescript, - go + go, + python ]; - can_generate_bare_string_enum: [swift, kotlin, scala, typescript, go ]; + can_generate_bare_string_enum: [swift, kotlin, scala, typescript, go, python ]; can_generate_double_option_pattern: [ typescript ]; can_recognize_types_inside_modules: [ - swift, kotlin, scala, typescript, go + swift, kotlin, scala, typescript, go, python ]; - test_simple_enum_case_name_support: [swift, kotlin, scala, typescript, go ]; + test_simple_enum_case_name_support: [swift, kotlin, scala, typescript, go, python ]; test_algebraic_enum_case_name_support: [ swift { prefix: "OP".to_string(), @@ -482,16 +508,17 @@ tests! { module_name: "colorModule".to_string(), }, typescript, - go + go, + python ]; - can_apply_prefix_correctly: [ swift { prefix: "OP".to_string(), }, kotlin { prefix: "OP".to_string(), }, scala, typescript, go ]; - can_generate_empty_algebraic_enum: [ swift { prefix: "OP".to_string(), }, kotlin { prefix: "OP".to_string(), }, scala, typescript, go ]; - can_generate_algebraic_enum_with_skipped_variants: [swift, kotlin, scala, typescript, go]; - can_generate_struct_with_skipped_fields: [swift, kotlin, scala, typescript, go]; - enum_is_properly_named_with_serde_overrides: [swift, kotlin, scala, typescript, go]; - can_handle_quote_in_serde_rename: [swift, kotlin, scala, typescript, go]; - can_handle_anonymous_struct: [swift, kotlin, scala, typescript, go]; - test_generate_char: [swift, kotlin, scala, typescript, go]; + can_apply_prefix_correctly: [ swift { prefix: "OP".to_string(), }, kotlin { prefix: "OP".to_string(), }, scala, typescript, go, python ]; + can_generate_empty_algebraic_enum: [ swift { prefix: "OP".to_string(), }, kotlin { prefix: "OP".to_string(), }, scala, typescript, go, python ]; + can_generate_algebraic_enum_with_skipped_variants: [swift, kotlin, scala, typescript, go, python]; + can_generate_struct_with_skipped_fields: [swift, kotlin, scala, typescript, go, python]; + enum_is_properly_named_with_serde_overrides: [swift, kotlin, scala, typescript, go, python]; + can_handle_quote_in_serde_rename: [swift, kotlin, scala, typescript, go, python]; + can_handle_anonymous_struct: [swift, kotlin, scala, typescript, go, python]; + test_generate_char: [swift, kotlin, scala, typescript, go, python]; anonymous_struct_with_rename: [ swift { prefix: "Core".to_string(), @@ -499,13 +526,14 @@ tests! { kotlin, scala, typescript, - go + go, + python ]; can_override_types: [swift, kotlin, scala, typescript, go]; /// Structs - can_generate_simple_struct_with_a_comment: [kotlin, swift, typescript, scala, go]; - generate_types: [kotlin, swift, typescript, scala, go]; + can_generate_simple_struct_with_a_comment: [kotlin, swift, typescript, scala, go, python]; + generate_types: [kotlin, swift, typescript, scala, go, python]; can_handle_serde_rename: [ swift { prefix: "TypeShareX_".to_string(), @@ -513,14 +541,15 @@ tests! { kotlin, scala, typescript, - go + go, + python ]; // TODO: kotlin and typescript don't appear to support this yet - generates_empty_structs_and_initializers: [swift, kotlin, scala, typescript, go]; + generates_empty_structs_and_initializers: [swift, kotlin, scala, typescript, go,python]; test_default_decorators: [swift { default_decorators: vec!["Sendable".into(), "Identifiable".into()]}]; test_default_generic_constraints: [swift { default_generic_constraints: typeshare_core::language::GenericConstraints::from_config(vec!["Sendable".into(), "Identifiable".into()]) }]; - test_i54_u53_type: [swift, kotlin, scala, typescript, go]; - test_serde_default_struct: [swift, kotlin, scala, typescript, go]; + test_i54_u53_type: [swift, kotlin, scala, typescript, go, python]; + test_serde_default_struct: [swift, kotlin, scala, typescript, go, python]; test_serde_iso8601: [ swift { prefix: String::new(), @@ -565,10 +594,13 @@ tests! { type_mappings: super::GO_MAPPINGS.clone(), uppercase_acronyms: vec!["URL".to_string()], }, + python{ + type_mappings: super::PYTHON_MAPPINGS.clone() + } ]; - test_type_alias: [ swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go ]; - test_optional_type_alias: [swift, kotlin, scala, typescript, go]; - test_serialized_as: [ swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go ]; + test_type_alias: [ swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go, python ]; + test_optional_type_alias: [swift, kotlin, scala, typescript, go, python]; + test_serialized_as: [ swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go, python ]; test_serialized_as_tuple: [ swift { prefix: "OP".to_string(), @@ -579,32 +611,33 @@ tests! { go { uppercase_acronyms: vec!["ID".to_string()], }, + python ]; - can_handle_serde_rename_all: [swift, kotlin, scala, typescript, go]; - can_handle_serde_rename_on_top_level: [swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go]; - can_generate_unit_structs: [swift, kotlin, scala, typescript, go]; - kebab_case_rename: [swift, kotlin, scala, typescript, go]; + can_handle_serde_rename_all: [swift, kotlin, scala, typescript, go,python]; + can_handle_serde_rename_on_top_level: [swift { prefix: "OP".to_string(), }, kotlin, scala, typescript, go, python]; + can_generate_unit_structs: [swift, kotlin, scala, typescript, go, python]; + kebab_case_rename: [swift, kotlin, scala, typescript, go, python]; /// Globals get topologically sorted - orders_types: [swift, kotlin, go]; + orders_types: [swift, kotlin, go, python]; /// Other - use_correct_integer_types: [swift, kotlin, scala, typescript, go]; + use_correct_integer_types: [swift, kotlin, scala, typescript, go, python]; // Only swift supports generating types with keywords generate_types_with_keywords: [swift]; // TODO: how is this different from generates_empty_structs_and_initializers? - use_correct_decoded_variable_name: [swift, kotlin, scala, typescript, go]; - can_handle_unit_type: [swift { codablevoid_constraints: vec!["Equatable".into()]} , kotlin, scala, typescript, go]; + use_correct_decoded_variable_name: [swift, kotlin, scala, typescript, go, python]; + can_handle_unit_type: [swift { codablevoid_constraints: vec!["Equatable".into()]} , kotlin, scala, typescript, go, python]; //3 tests for adding decorators to enums and structs const_enum_decorator: [ swift{ prefix: "OP".to_string(), } ]; algebraic_enum_decorator: [ swift{ prefix: "OP".to_string(), } ]; struct_decorator: [ kotlin, swift{ prefix: "OP".to_string(), } ]; - serialize_field_as: [kotlin, swift, typescript, scala, go]; - serialize_type_alias: [kotlin, swift, typescript, scala, go]; - serialize_anonymous_field_as: [kotlin, swift, typescript, scala, go]; - smart_pointers: [kotlin, swift, typescript, scala, go]; - recursive_enum_decorator: [kotlin, swift, typescript, scala, go]; + serialize_field_as: [kotlin, swift, typescript, scala, go, python]; + serialize_type_alias: [kotlin, swift, typescript, scala, go, python]; + serialize_anonymous_field_as: [kotlin, swift, typescript, scala, go, python]; + smart_pointers: [kotlin, swift, typescript, scala, go, python]; + recursive_enum_decorator: [kotlin, swift, typescript, scala, go, python]; uppercase_go_acronyms: [ go { @@ -618,10 +651,11 @@ tests! { typescript, kotlin, scala, - go + go, + python ]; - can_generate_anonymous_struct_with_skipped_fields: [swift, kotlin, scala, typescript, go]; + can_generate_anonymous_struct_with_skipped_fields: [swift, kotlin, scala, typescript, go, python]; generic_struct_with_constraints_and_decorators: [swift { codablevoid_constraints: vec!["Equatable".into()] }]; - excluded_by_target_os: [ swift, kotlin, scala, typescript, go ] target_os: ["android", "macos"]; + excluded_by_target_os: [ swift, kotlin, scala, typescript, go,python ] target_os: ["android", "macos"]; // excluded_by_target_os_full_module: [swift] target_os: "ios"; } From fb666ca1a7f76b319d1e0f2470893b06f94165d9 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Thu, 31 Oct 2024 16:18:44 -0400 Subject: [PATCH 30/57] Add python support and update cargo lock --- Cargo.lock | 401 ++++++++++++++++++++---------------------------- cli/src/args.rs | 2 + 2 files changed, 169 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ba2e3cd..ba2c911f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -43,49 +43,49 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" @@ -95,9 +95,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "serde", @@ -111,9 +111,12 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -132,14 +135,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] name = "clap" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -147,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -162,9 +165,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.32" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a01f4f9ee6c066d42a1c8dedf0dcddad16c72a8981a309d6398de3a75b0c39" +checksum = "86bc73de94bc81e52f3bebec71bc4463e9748f7a59166663e32044669577b0e2" dependencies = [ "clap", ] @@ -189,9 +192,18 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "cool_asserts" @@ -204,9 +216,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crossbeam-deque" @@ -241,9 +253,9 @@ checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -293,9 +305,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", @@ -306,15 +318,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.3" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -324,9 +330,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -347,9 +353,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", @@ -369,9 +375,9 @@ checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -406,9 +412,9 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -421,9 +427,9 @@ checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "linux-raw-sys" @@ -433,15 +439,15 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "nu-ansi-term" @@ -463,24 +469,24 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -507,9 +513,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -519,9 +525,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -530,15 +536,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags", "errno", @@ -564,18 +570,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -584,24 +590,31 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strsim" version = "0.11.1" @@ -610,9 +623,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -621,28 +634,28 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", @@ -651,9 +664,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -663,18 +676,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -742,28 +755,32 @@ dependencies = [ "quote", "syn", "thiserror", + "topological-sort", ] [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "utf8parse" @@ -771,12 +788,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "walkdir" version = "2.5.0" @@ -789,19 +800,20 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -814,9 +826,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -824,9 +836,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -837,57 +849,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -896,135 +877,87 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/cli/src/args.rs b/cli/src/args.rs index 31270f61..ad416327 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -10,6 +10,8 @@ pub enum AvailableLanguage { Typescript, #[cfg(feature = "go")] Go, + #[cfg(feature = "python")] + Python } #[derive(clap::Parser)] From 40206a7a6be9caa058701faab1ce8187b73b1c08 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Thu, 31 Oct 2024 16:20:31 -0400 Subject: [PATCH 31/57] fmt fix and fix rust doc --- cli/src/args.rs | 2 +- cli/src/config.rs | 4 ++-- cli/src/main.rs | 2 +- core/src/language/python.rs | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/src/args.rs b/cli/src/args.rs index ad416327..38ca460c 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -11,7 +11,7 @@ pub enum AvailableLanguage { #[cfg(feature = "go")] Go, #[cfg(feature = "python")] - Python + Python, } #[derive(clap::Parser)] diff --git a/cli/src/config.rs b/cli/src/config.rs index fdebafdc..0475e3bc 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -171,8 +171,8 @@ mod test { assert_eq!(config.typescript.type_mappings["DateTime"], "string"); #[cfg(feature = "python")] { - assert_eq!(config.python.type_mappings["Url"], "AnyUrl"); - assert_eq!(config.python.type_mappings["DateTime"], "datetime"); + assert_eq!(config.python.type_mappings["Url"], "AnyUrl"); + assert_eq!(config.python.type_mappings["DateTime"], "datetime"); } #[cfg(feature = "go")] assert_eq!(config.go.type_mappings["DateTime"], "string"); diff --git a/cli/src/main.rs b/cli/src/main.rs index 2b1c05a8..17cb933a 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -19,11 +19,11 @@ use clap_complete::Generator; use ignore::{overrides::OverrideBuilder, types::TypesBuilder, WalkBuilder}; use log::error; use rayon::iter::ParallelBridge; +use typeshare_core::language::GenericConstraints; #[cfg(feature = "go")] use typeshare_core::language::Go; #[cfg(feature = "python")] use typeshare_core::language::Python; -use typeshare_core::language::GenericConstraints; use typeshare_core::{ language::{CrateName, Kotlin, Language, Scala, SupportedLanguage, Swift, TypeScript}, parser::ParsedData, diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 7e931293..a91e24c3 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -68,12 +68,12 @@ fn dedup(v: &mut Vec) { pub struct Python { /// Mappings from Rust type names to Python type names pub type_mappings: HashMap, - // HashMap + /// HashMap pub imports: HashMap>, - // HashMap> - // Used to lay out runtime references in the module - // such that it can be read top to bottom - // globals: HashMap>, + /// HashMap> + /// Used to lay out runtime references in the module + /// such that it can be read top to bottom + /// globals: HashMap>, pub type_variables: HashSet, } From 7184b52c33931f7f4648738e6f60d62961b4f0fa Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Thu, 31 Oct 2024 16:23:59 -0400 Subject: [PATCH 32/57] update test cases --- cli/src/main.rs | 2 ++ core/data/tests/anonymous_struct_with_rename/output.py | 2 +- core/data/tests/can_apply_prefix_correctly/output.py | 2 +- core/data/tests/can_generate_algebraic_enum/output.py | 2 +- .../output.py | 2 +- .../output.py | 2 +- core/data/tests/can_generate_bare_string_enum/output.py | 2 +- core/data/tests/can_generate_empty_algebraic_enum/output.py | 2 +- core/data/tests/can_generate_simple_enum/output.py | 2 +- .../tests/can_generate_simple_struct_with_a_comment/output.py | 2 +- core/data/tests/can_generate_slice_of_user_type/output.py | 2 +- .../tests/can_generate_struct_with_skipped_fields/output.py | 2 +- core/data/tests/can_generate_unit_structs/output.py | 2 +- core/data/tests/can_handle_anonymous_struct/output.py | 2 +- core/data/tests/can_handle_quote_in_serde_rename/output.py | 2 +- core/data/tests/can_handle_serde_rename/output.py | 2 +- core/data/tests/can_handle_serde_rename_all/output.py | 2 +- .../data/tests/can_handle_serde_rename_on_top_level/output.py | 2 +- core/data/tests/can_handle_unit_type/output.py | 2 +- core/data/tests/can_recognize_types_inside_modules/output.py | 2 +- .../enum_is_properly_named_with_serde_overrides/output.py | 2 +- core/data/tests/excluded_by_target_os/output.py | 2 +- core/data/tests/generate_types/output.py | 2 +- .../tests/generates_empty_structs_and_initializers/output.py | 2 +- core/data/tests/kebab_case_rename/output.py | 2 +- core/data/tests/orders_types/output.py | 2 +- core/data/tests/recursive_enum_decorator/output.py | 2 +- core/data/tests/resolves_qualified_type/output.py | 2 +- core/data/tests/serialize_anonymous_field_as/output.py | 2 +- core/data/tests/serialize_field_as/output.py | 2 +- core/data/tests/serialize_type_alias/output.py | 2 +- core/data/tests/smart_pointers/output.py | 2 +- .../tests/test_algebraic_enum_case_name_support/output.py | 2 +- core/data/tests/test_generate_char/output.py | 2 +- core/data/tests/test_i54_u53_type/output.py | 2 +- core/data/tests/test_optional_type_alias/output.py | 2 +- core/data/tests/test_serde_default_struct/output.py | 2 +- core/data/tests/test_serde_url/output.py | 2 +- core/data/tests/test_serialized_as/output.py | 2 +- core/data/tests/test_serialized_as_tuple/output.py | 2 +- core/data/tests/test_simple_enum_case_name_support/output.py | 2 +- core/data/tests/test_type_alias/output.py | 2 +- core/data/tests/use_correct_decoded_variable_name/output.py | 2 +- core/data/tests/use_correct_integer_types/output.py | 2 +- core/src/language/python.rs | 4 +--- 45 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 17cb933a..76e522ee 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -78,6 +78,8 @@ fn main() -> anyhow::Result<()> { args::AvailableLanguage::Typescript => SupportedLanguage::TypeScript, #[cfg(feature = "go")] args::AvailableLanguage::Go => SupportedLanguage::Go, + #[cfg(feature = "python")] + args::AvailableLanguage::Python => SupportedLanguage::Python, }, }; diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index 7b17cab3..b041257f 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 629aeb46..8c412be6 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index c306bb36..27e3351c 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 774735be..3381a1db 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 81b122c9..24872bf7 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index e260548e..02b06d64 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index a3623202..590c4e0a 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index e260548e..02b06d64 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py index 9f42dd58..e179dc69 100644 --- a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py +++ b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_slice_of_user_type/output.py b/core/data/tests/can_generate_slice_of_user_type/output.py index f975fd4d..620a6bac 100644 --- a/core/data/tests/can_generate_slice_of_user_type/output.py +++ b/core/data/tests/can_generate_slice_of_user_type/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_struct_with_skipped_fields/output.py index 82f30bfd..ddfbf28f 100644 --- a/core/data/tests/can_generate_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_struct_with_skipped_fields/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_generate_unit_structs/output.py b/core/data/tests/can_generate_unit_structs/output.py index 4f8afe8e..a77d85ff 100644 --- a/core/data/tests/can_generate_unit_structs/output.py +++ b/core/data/tests/can_generate_unit_structs/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 5061d84b..1b4f9bf5 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index 19626de0..afbf5ac2 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_serde_rename/output.py b/core/data/tests/can_handle_serde_rename/output.py index 0e7d580a..da438dc7 100644 --- a/core/data/tests/can_handle_serde_rename/output.py +++ b/core/data/tests/can_handle_serde_rename/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_serde_rename_all/output.py b/core/data/tests/can_handle_serde_rename_all/output.py index db994ea8..fc8f4cec 100644 --- a/core/data/tests/can_handle_serde_rename_all/output.py +++ b/core/data/tests/can_handle_serde_rename_all/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_serde_rename_on_top_level/output.py b/core/data/tests/can_handle_serde_rename_on_top_level/output.py index b27859e6..47ae8158 100644 --- a/core/data/tests/can_handle_serde_rename_on_top_level/output.py +++ b/core/data/tests/can_handle_serde_rename_on_top_level/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index bbc4231e..58af3995 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/can_recognize_types_inside_modules/output.py b/core/data/tests/can_recognize_types_inside_modules/output.py index d01df05f..60f869e8 100644 --- a/core/data/tests/can_recognize_types_inside_modules/output.py +++ b/core/data/tests/can_recognize_types_inside_modules/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index 152af0a3..564201d8 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index fd3106da..21e0e93b 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/generate_types/output.py b/core/data/tests/generate_types/output.py index 3ea4c904..693e6a86 100644 --- a/core/data/tests/generate_types/output.py +++ b/core/data/tests/generate_types/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/generates_empty_structs_and_initializers/output.py b/core/data/tests/generates_empty_structs_and_initializers/output.py index 97b6ecee..8e9c1c6d 100644 --- a/core/data/tests/generates_empty_structs_and_initializers/output.py +++ b/core/data/tests/generates_empty_structs_and_initializers/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/kebab_case_rename/output.py b/core/data/tests/kebab_case_rename/output.py index a63235b6..3195e7ee 100644 --- a/core/data/tests/kebab_case_rename/output.py +++ b/core/data/tests/kebab_case_rename/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/orders_types/output.py b/core/data/tests/orders_types/output.py index 5bfa3548..ec85655d 100644 --- a/core/data/tests/orders_types/output.py +++ b/core/data/tests/orders_types/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 9c23d68a..08b5ae83 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/resolves_qualified_type/output.py b/core/data/tests/resolves_qualified_type/output.py index 1dfb932b..026765ac 100644 --- a/core/data/tests/resolves_qualified_type/output.py +++ b/core/data/tests/resolves_qualified_type/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index f77fcf55..b8cb7b71 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/serialize_field_as/output.py b/core/data/tests/serialize_field_as/output.py index b926fbfb..effbe952 100644 --- a/core/data/tests/serialize_field_as/output.py +++ b/core/data/tests/serialize_field_as/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/serialize_type_alias/output.py b/core/data/tests/serialize_type_alias/output.py index 2fe312b6..63258c3c 100644 --- a/core/data/tests/serialize_type_alias/output.py +++ b/core/data/tests/serialize_type_alias/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 4cc548d1..e436054e 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index 9e25fbcc..3123b721 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_generate_char/output.py b/core/data/tests/test_generate_char/output.py index 1bb1fb06..8c6e84c0 100644 --- a/core/data/tests/test_generate_char/output.py +++ b/core/data/tests/test_generate_char/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_i54_u53_type/output.py b/core/data/tests/test_i54_u53_type/output.py index 3a748bd3..af49359c 100644 --- a/core/data/tests/test_i54_u53_type/output.py +++ b/core/data/tests/test_i54_u53_type/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_optional_type_alias/output.py b/core/data/tests/test_optional_type_alias/output.py index e599c2a4..52c765ba 100644 --- a/core/data/tests/test_optional_type_alias/output.py +++ b/core/data/tests/test_optional_type_alias/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_serde_default_struct/output.py b/core/data/tests/test_serde_default_struct/output.py index af347221..35438bac 100644 --- a/core/data/tests/test_serde_default_struct/output.py +++ b/core/data/tests/test_serde_default_struct/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_serde_url/output.py b/core/data/tests/test_serde_url/output.py index 8461fb12..602e52cd 100644 --- a/core/data/tests/test_serde_url/output.py +++ b/core/data/tests/test_serde_url/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_serialized_as/output.py b/core/data/tests/test_serialized_as/output.py index c018d741..28a0cc6b 100644 --- a/core/data/tests/test_serialized_as/output.py +++ b/core/data/tests/test_serialized_as/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_serialized_as_tuple/output.py b/core/data/tests/test_serialized_as_tuple/output.py index 215c119a..a6208fe4 100644 --- a/core/data/tests/test_serialized_as_tuple/output.py +++ b/core/data/tests/test_serialized_as_tuple/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index b395ae77..1f88591f 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/test_type_alias/output.py b/core/data/tests/test_type_alias/output.py index 51c0f4b2..fce5ad2b 100644 --- a/core/data/tests/test_type_alias/output.py +++ b/core/data/tests/test_type_alias/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/use_correct_decoded_variable_name/output.py b/core/data/tests/use_correct_decoded_variable_name/output.py index 97b6ecee..8e9c1c6d 100644 --- a/core/data/tests/use_correct_decoded_variable_name/output.py +++ b/core/data/tests/use_correct_decoded_variable_name/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/data/tests/use_correct_integer_types/output.py b/core/data/tests/use_correct_integer_types/output.py index dbd78b9c..821fc38c 100644 --- a/core/data/tests/use_correct_integer_types/output.py +++ b/core/data/tests/use_correct_integer_types/output.py @@ -1,5 +1,5 @@ """ - Generated by typeshare 1.11.0 + Generated by typeshare 1.12.0 """ from __future__ import annotations diff --git a/core/src/language/python.rs b/core/src/language/python.rs index a91e24c3..0ee9c54d 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -783,9 +783,7 @@ fn handle_model_config(w: &mut dyn Write, python_module: &mut Python, rs: &RustS #[cfg(test)] mod test { - use syn::{parse_str, ItemEnum}; - - use crate::{parser::parse_enum, rust_types::Id}; + use crate::rust_types::Id; use super::*; #[test] From 3d447df1f1164e8778082e1d6f31f0fa8a738c1e Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Mon, 4 Nov 2024 11:28:36 +0100 Subject: [PATCH 33/57] Add mention of Python support in README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e2e41f3..203b0865 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ _And in the darkness, compile them_ 💍 Do you like manually managing types that need to be passed through an FFI layer, so that your code doesn't archaically break at runtime? Be honest, nobody does. Typeshare is here to take that burden away from you! Leveraging the power of the `serde` library, Typeshare is a tool that converts your -Rust types into their equivalent forms in Swift, Go**, Kotlin, Scala and Typescript, keeping +Rust types into their equivalent forms in Swift, Go**, Python**, Kotlin, Scala and Typescript, keeping your cross-language codebase in sync. With automatic implementation for serialization and deserialization on both sides of the FFI, Typeshare does all the heavy lifting for you. It can even handle generics and convert effortlessly between standard libraries in different languages! **A few caveats. See [here](#a-quick-refresher-on-supported-languages) for more details. @@ -98,12 +98,13 @@ Are you getting weird deserialization issues? Did our procedural macro throw a c - Swift - Typescript - Go** +- Python** If there is a language that you want Typeshare to generate definitions for, you can either: 1. Open an issue in this repository requesting your language of choice. 2. Implement support for that language and open a PR with your implementation. We would be eternally grateful! 🙏 -** Right now, Go support is experimental. Enable the `go` feature when installing typeshare-cli if you want to use it. +** Right now, Go and Python support is experimental. Enable the `go` or `python` features, respectively, when installing typeshare-cli if you want to use these. ## Credits From a251db139099f67bbb7d72b600ffb2088fb6a246 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Wed, 6 Nov 2024 21:26:42 -0500 Subject: [PATCH 34/57] remove trailing comma --- core/data/tests/can_generate_bare_string_enum/output.py | 4 ++-- core/data/tests/can_generate_simple_enum/output.py | 4 ++-- .../enum_is_properly_named_with_serde_overrides/output.py | 4 ++-- core/data/tests/test_simple_enum_case_name_support/output.py | 4 ++-- core/src/language/python.rs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index 02b06d64..eb19b3a0 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -7,7 +7,7 @@ class Colors(Enum): - RED = "Red", - BLUE = "Blue", + RED = "Red" + BLUE = "Blue" GREEN = "Green" diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index 02b06d64..eb19b3a0 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -7,7 +7,7 @@ class Colors(Enum): - RED = "Red", - BLUE = "Blue", + RED = "Red" + BLUE = "Blue" GREEN = "Green" diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index 564201d8..77c49bdc 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -7,7 +7,7 @@ class Colors(Enum): - RED = "red", - BLUE = "blue", + RED = "red" + BLUE = "blue" GREEN = "green-like" diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index 1f88591f..d31cfe33 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -7,7 +7,7 @@ class Colors(Enum): - RED = "red", - BLUE = "blue-ish", + RED = "red" + BLUE = "blue-ish" GREEN = "Green" diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 0ee9c54d..d77bb5d9 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -295,7 +295,7 @@ impl Language for Python { ) }) .collect::>() - .join(",\n") + .join("\n") }; writeln!(w, "{fields}\n")?; } From 61b6afd7cbb748bcf2ef5b434f2404d14e941a7f Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Wed, 6 Nov 2024 21:47:29 -0500 Subject: [PATCH 35/57] Update unit enum to use enum values --- .../can_generate_bare_string_enum/output.py | 3 ++- .../tests/can_generate_simple_enum/output.py | 3 ++- .../can_handle_quote_in_serde_rename/output.py | 3 ++- .../output.py | 3 ++- core/data/tests/excluded_by_target_os/output.py | 1 - .../output.py | 3 ++- core/src/language/python.rs | 17 +++++++---------- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index eb19b3a0..61dd6727 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -4,10 +4,11 @@ from __future__ import annotations from enum import Enum +from pydantic import ConfigDict class Colors(Enum): + model_config = ConfigDict(use_enum_values=True) RED = "Red" BLUE = "Blue" GREEN = "Green" - diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index eb19b3a0..61dd6727 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -4,10 +4,11 @@ from __future__ import annotations from enum import Enum +from pydantic import ConfigDict class Colors(Enum): + model_config = ConfigDict(use_enum_values=True) RED = "Red" BLUE = "Blue" GREEN = "Green" - diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index afbf5ac2..1163497d 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -4,8 +4,9 @@ from __future__ import annotations from enum import Enum +from pydantic import ConfigDict class Colors(Enum): + model_config = ConfigDict(use_enum_values=True) GREEN = "Green\"" - diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index 77c49bdc..ff5ecfc1 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -4,10 +4,11 @@ from __future__ import annotations from enum import Enum +from pydantic import ConfigDict class Colors(Enum): + model_config = ConfigDict(use_enum_values=True) RED = "red" BLUE = "blue" GREEN = "green-like" - diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 21e0e93b..38be6ed1 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -31,7 +31,6 @@ class OtherExcluded(BaseModel): class SomeEnum(Enum): pass - class TestEnumVariant7(BaseModel): """ Generated type representing the anonymous struct variant `Variant7` of the `TestEnum` Rust enum diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index d31cfe33..ca0545a2 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -4,10 +4,11 @@ from __future__ import annotations from enum import Enum +from pydantic import ConfigDict class Colors(Enum): + model_config = ConfigDict(use_enum_values=True) RED = "red" BLUE = "blue-ish" GREEN = "Green" - diff --git a/core/src/language/python.rs b/core/src/language/python.rs index d77bb5d9..a05d7e86 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -270,20 +270,21 @@ impl Language for Python { // Generate named types for any anonymous struct variants of this enum self.write_types_for_anonymous_structs(w, e, &make_anonymous_struct_name)?; self.add_import("enum".to_string(), "Enum".to_string()); + self.add_import("pydantic".to_string(), "ConfigDict".to_string()); match e { // Write all the unit variants out (there can only be unit variants in // this case) RustEnum::Unit(shared) => { writeln!(w, "class {}(Enum):", shared.id.renamed)?; - - let fields = if shared.variants.is_empty() { - " pass".to_string() + if shared.variants.is_empty(){ + writeln!(w, " pass")?; } else { + writeln!(w, " model_config = ConfigDict(use_enum_values=True)")?; shared .variants .iter() - .map(|v| { - format!( + .try_for_each(|v| { + writeln!(w, " {} = \"{}\"", v.shared().id.original.to_uppercase(), match v { @@ -293,11 +294,8 @@ impl Language for Python { _ => panic!(), } ) - }) - .collect::>() - .join("\n") + })? }; - writeln!(w, "{fields}\n")?; } // Write all the algebraic variants out (all three variant types are possible // here) @@ -590,7 +588,6 @@ impl Python { .cloned() .for_each(|v| self.add_type_var(v)); let mut variants: Vec<(String, Vec)> = Vec::new(); - self.add_import("pydantic".to_string(), "ConfigDict".to_string()); self.add_import("pydantic".to_string(), "BaseModel".to_string()); // write "types" class: a union of all the enum variants writeln!(w, "class {}Types(str, Enum):", shared.id.renamed)?; From 9810e7b4dfe830c8cc10bc0019b7d06f87c63262 Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Wed, 6 Nov 2024 21:48:33 -0500 Subject: [PATCH 36/57] fmt fix --- core/src/language/python.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index a05d7e86..85793b6c 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -276,25 +276,23 @@ impl Language for Python { // this case) RustEnum::Unit(shared) => { writeln!(w, "class {}(Enum):", shared.id.renamed)?; - if shared.variants.is_empty(){ + if shared.variants.is_empty() { writeln!(w, " pass")?; } else { writeln!(w, " model_config = ConfigDict(use_enum_values=True)")?; - shared - .variants - .iter() - .try_for_each(|v| { - writeln!(w, - " {} = \"{}\"", - v.shared().id.original.to_uppercase(), - match v { - RustEnumVariant::Unit(v) => { - v.id.renamed.replace("\"", "\\\"") - } - _ => panic!(), + shared.variants.iter().try_for_each(|v| { + writeln!( + w, + " {} = \"{}\"", + v.shared().id.original.to_uppercase(), + match v { + RustEnumVariant::Unit(v) => { + v.id.renamed.replace("\"", "\\\"") } - ) - })? + _ => panic!(), + } + ) + })? }; } // Write all the algebraic variants out (all three variant types are possible From 92f6922efa293fc0bd0c85cac733fd3939ee085d Mon Sep 17 00:00:00 2001 From: Omar Miraj Date: Wed, 6 Nov 2024 22:48:31 -0500 Subject: [PATCH 37/57] Remove config dict and allow unit enums to be serializble --- core/data/tests/can_generate_bare_string_enum/output.py | 3 +-- core/data/tests/can_generate_simple_enum/output.py | 3 +-- .../tests/can_handle_quote_in_serde_rename/output.py | 3 +-- .../output.py | 3 +-- core/data/tests/excluded_by_target_os/output.py | 2 +- .../tests/test_simple_enum_case_name_support/output.py | 3 +-- core/src/language/python.rs | 9 ++++----- 7 files changed, 10 insertions(+), 16 deletions(-) diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index 61dd6727..c14c5705 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -7,8 +7,7 @@ from pydantic import ConfigDict -class Colors(Enum): - model_config = ConfigDict(use_enum_values=True) +class Colors(str, Enum): RED = "Red" BLUE = "Blue" GREEN = "Green" diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index 61dd6727..c14c5705 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -7,8 +7,7 @@ from pydantic import ConfigDict -class Colors(Enum): - model_config = ConfigDict(use_enum_values=True) +class Colors(str, Enum): RED = "Red" BLUE = "Blue" GREEN = "Green" diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index 1163497d..563d2caa 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -7,6 +7,5 @@ from pydantic import ConfigDict -class Colors(Enum): - model_config = ConfigDict(use_enum_values=True) +class Colors(str, Enum): GREEN = "Green\"" diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index ff5ecfc1..cb503037 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -7,8 +7,7 @@ from pydantic import ConfigDict -class Colors(Enum): - model_config = ConfigDict(use_enum_values=True) +class Colors(str, Enum): RED = "red" BLUE = "blue" GREEN = "green-like" diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 38be6ed1..aa48ceb1 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -29,7 +29,7 @@ class NestedNotTarget1(BaseModel): class OtherExcluded(BaseModel): pass -class SomeEnum(Enum): +class SomeEnum(str, Enum): pass class TestEnumVariant7(BaseModel): """ diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index ca0545a2..b77f1d11 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -7,8 +7,7 @@ from pydantic import ConfigDict -class Colors(Enum): - model_config = ConfigDict(use_enum_values=True) +class Colors(str, Enum): RED = "red" BLUE = "blue-ish" GREEN = "Green" diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 85793b6c..2bb4d151 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -248,7 +248,7 @@ impl Language for Python { self.write_comments(w, true, &rs.comments, 1)?; - handle_model_config(w, self, rs); + handle_model_config(w, self, &rs.fields); rs.fields .iter() @@ -275,11 +275,10 @@ impl Language for Python { // Write all the unit variants out (there can only be unit variants in // this case) RustEnum::Unit(shared) => { - writeln!(w, "class {}(Enum):", shared.id.renamed)?; + writeln!(w, "class {}(str, Enum):", shared.id.renamed)?; if shared.variants.is_empty() { writeln!(w, " pass")?; } else { - writeln!(w, " model_config = ConfigDict(use_enum_values=True)")?; shared.variants.iter().try_for_each(|v| { writeln!( w, @@ -765,8 +764,8 @@ fn python_property_aware_rename(name: &str) -> String { } // If at least one field from within a class is changed when the serde rename is used (a.k.a the field has 2 words) then we must use aliasing and we must also use a config dict at the top level of the class. -fn handle_model_config(w: &mut dyn Write, python_module: &mut Python, rs: &RustStruct) { - let visibly_renamed_field = rs.fields.iter().find(|f| { +fn handle_model_config(w: &mut dyn Write, python_module: &mut Python, fields: &[RustField]) { + let visibly_renamed_field = fields.iter().find(|f| { let python_field_name = python_property_aware_rename(&f.id.original); python_field_name != f.id.renamed }); From 0f897839986c7dae6062163ecfa4853e95f3dde7 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Thu, 7 Nov 2024 23:43:54 -0500 Subject: [PATCH 38/57] fix tuple variant generation --- .../can_apply_prefix_correctly/output.py | 32 +++-------- .../can_generate_algebraic_enum/output.py | 49 ++++------------- .../output.py | 7 +-- .../output.py | 7 +-- .../can_handle_anonymous_struct/output.py | 12 +--- .../data/tests/can_handle_unit_type/output.py | 7 +-- .../tests/recursive_enum_decorator/output.py | 24 ++------ .../serialize_anonymous_field_as/output.py | 12 +--- core/data/tests/smart_pointers/output.py | 7 +-- .../output.py | 22 ++------ core/src/language/python.rs | 55 ++----------------- 11 files changed, 48 insertions(+), 186 deletions(-) diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 8c412be6..9c97a30b 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -20,32 +20,14 @@ class AdvancedColorsTypes(str, Enum): ARRAY_REALLY_COOL_TYPE = "ArrayReallyCoolType" DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" -class AdvancedColorsString(BaseModel): - c: str - -class AdvancedColorsNumber(BaseModel): - c: int - -class AdvancedColorsNumberArray(BaseModel): - c: List[int] - -class AdvancedColorsReallyCoolType(BaseModel): - c: ItemDetailsFieldValue - -class AdvancedColorsArrayReallyCoolType(BaseModel): - c: List[ItemDetailsFieldValue] - -class AdvancedColorsDictionaryReallyCoolType(BaseModel): - c: Dict[str, ItemDetailsFieldValue] - class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) t: AdvancedColorsTypes - c: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType, AdvancedColorsArrayReallyCoolType, AdvancedColorsDictionaryReallyCoolType] + c: Union[str, int, List[int], ItemDetailsFieldValue, List[ItemDetailsFieldValue], Dict[str, ItemDetailsFieldValue]] @classmethod - def new_advanced_colors_string(cls, c : AdvancedColorsString): + def new_advanced_colors_string(cls, c : str): return cls( t=AdvancedColorsTypes.STRING, c=c @@ -53,7 +35,7 @@ def new_advanced_colors_string(cls, c : AdvancedColorsString): @classmethod - def new_advanced_colors_number(cls, c : AdvancedColorsNumber): + def new_advanced_colors_number(cls, c : int): return cls( t=AdvancedColorsTypes.NUMBER, c=c @@ -61,7 +43,7 @@ def new_advanced_colors_number(cls, c : AdvancedColorsNumber): @classmethod - def new_advanced_colors_number_array(cls, c : AdvancedColorsNumberArray): + def new_advanced_colors_number_array(cls, c : List[int]): return cls( t=AdvancedColorsTypes.NUMBER_ARRAY, c=c @@ -69,7 +51,7 @@ def new_advanced_colors_number_array(cls, c : AdvancedColorsNumberArray): @classmethod - def new_advanced_colors_really_cool_type(cls, c : AdvancedColorsReallyCoolType): + def new_advanced_colors_really_cool_type(cls, c : ItemDetailsFieldValue): return cls( t=AdvancedColorsTypes.REALLY_COOL_TYPE, c=c @@ -77,7 +59,7 @@ def new_advanced_colors_really_cool_type(cls, c : AdvancedColorsReallyCoolType): @classmethod - def new_advanced_colors_array_really_cool_type(cls, c : AdvancedColorsArrayReallyCoolType): + def new_advanced_colors_array_really_cool_type(cls, c : List[ItemDetailsFieldValue]): return cls( t=AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE, c=c @@ -85,7 +67,7 @@ def new_advanced_colors_array_really_cool_type(cls, c : AdvancedColorsArrayReall @classmethod - def new_advanced_colors_dictionary_really_cool_type(cls, c : AdvancedColorsDictionaryReallyCoolType): + def new_advanced_colors_dictionary_really_cool_type(cls, c : Dict[str, ItemDetailsFieldValue]): return cls( t=AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE, c=c diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 27e3351c..d2961729 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -21,29 +21,14 @@ class AdvancedColorsTypes(str, Enum): NUMBER_ARRAY = "NumberArray" REALLY_COOL_TYPE = "ReallyCoolType" -class AdvancedColorsString(BaseModel): - content: str - -class AdvancedColorsNumber(BaseModel): - content: int - -class AdvancedColorsUnsignedNumber(BaseModel): - content: int - -class AdvancedColorsNumberArray(BaseModel): - content: List[int] - -class AdvancedColorsReallyCoolType(BaseModel): - content: ItemDetailsFieldValue - class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] + content: Union[str, int, int, List[int], ItemDetailsFieldValue] @classmethod - def new_advanced_colors_string(cls, content : AdvancedColorsString): + def new_advanced_colors_string(cls, content : str): return cls( type=AdvancedColorsTypes.STRING, content=content @@ -51,7 +36,7 @@ def new_advanced_colors_string(cls, content : AdvancedColorsString): @classmethod - def new_advanced_colors_number(cls, content : AdvancedColorsNumber): + def new_advanced_colors_number(cls, content : int): return cls( type=AdvancedColorsTypes.NUMBER, content=content @@ -59,7 +44,7 @@ def new_advanced_colors_number(cls, content : AdvancedColorsNumber): @classmethod - def new_advanced_colors_unsigned_number(cls, content : AdvancedColorsUnsignedNumber): + def new_advanced_colors_unsigned_number(cls, content : int): return cls( type=AdvancedColorsTypes.UNSIGNED_NUMBER, content=content @@ -67,7 +52,7 @@ def new_advanced_colors_unsigned_number(cls, content : AdvancedColorsUnsignedNum @classmethod - def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): + def new_advanced_colors_number_array(cls, content : List[int]): return cls( type=AdvancedColorsTypes.NUMBER_ARRAY, content=content @@ -75,7 +60,7 @@ def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): @classmethod - def new_advanced_colors_really_cool_type(cls, content : AdvancedColorsReallyCoolType): + def new_advanced_colors_really_cool_type(cls, content : ItemDetailsFieldValue): return cls( type=AdvancedColorsTypes.REALLY_COOL_TYPE, content=content @@ -86,26 +71,14 @@ class AdvancedColors2Types(str, Enum): NUMBER_ARRAY = "number-array" REALLY_COOL_TYPE = "really-cool-type" -class AdvancedColors2String(BaseModel): - content: str - -class AdvancedColors2Number(BaseModel): - content: int - -class AdvancedColors2NumberArray(BaseModel): - content: List[int] - -class AdvancedColors2ReallyCoolType(BaseModel): - content: ItemDetailsFieldValue - class AdvancedColors2(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColors2Types - content: Union[AdvancedColors2String, AdvancedColors2Number, AdvancedColors2NumberArray, AdvancedColors2ReallyCoolType] + content: Union[str, int, List[int], ItemDetailsFieldValue] @classmethod - def new_advanced_colors_2_string(cls, content : AdvancedColors2String): + def new_advanced_colors_2_string(cls, content : str): return cls( type=AdvancedColors2Types.STRING, content=content @@ -113,7 +86,7 @@ def new_advanced_colors_2_string(cls, content : AdvancedColors2String): @classmethod - def new_advanced_colors_2_number(cls, content : AdvancedColors2Number): + def new_advanced_colors_2_number(cls, content : int): return cls( type=AdvancedColors2Types.NUMBER, content=content @@ -121,7 +94,7 @@ def new_advanced_colors_2_number(cls, content : AdvancedColors2Number): @classmethod - def new_advanced_colors_2_number_array(cls, content : AdvancedColors2NumberArray): + def new_advanced_colors_2_number_array(cls, content : List[int]): return cls( type=AdvancedColors2Types.NUMBER_ARRAY, content=content @@ -129,7 +102,7 @@ def new_advanced_colors_2_number_array(cls, content : AdvancedColors2NumberArray @classmethod - def new_advanced_colors_2_really_cool_type(cls, content : AdvancedColors2ReallyCoolType): + def new_advanced_colors_2_really_cool_type(cls, content : ItemDetailsFieldValue): return cls( type=AdvancedColors2Types.REALLY_COOL_TYPE, content=content diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 3381a1db..203e59aa 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -13,13 +13,10 @@ class SomeEnumTypes(str, Enum): C = "C" -class SomeEnumC(BaseModel): - content: int - class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[SomeEnumC, None] + content: Union[int, None] @classmethod @@ -31,7 +28,7 @@ def new_some_enum_a(cls) -> SomeEnum: @classmethod - def new_some_enum_c(cls, content : SomeEnumC): + def new_some_enum_c(cls, content : int): return cls( type=SomeEnumTypes.C, content=content diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index 590c4e0a..9e9c32eb 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -15,18 +15,15 @@ class AddressTypes(str, Enum): FIXED_ADDRESS = "FixedAddress" NO_FIXED_ADDRESS = "NoFixedAddress" -class AddressFixedAddress(BaseModel): - content: AddressDetails - class Address(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AddressTypes - content: Union[AddressFixedAddress, None] + content: Union[AddressDetails, None] @classmethod - def new_address_fixed_address(cls, content : AddressFixedAddress): + def new_address_fixed_address(cls, content : AddressDetails): return cls( type=AddressTypes.FIXED_ADDRESS, content=content diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 1b4f9bf5..caad2e88 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -80,17 +80,11 @@ class EnumWithManyVariantsTypes(str, Enum): ANOTHER_ANON_VARIANT = "AnotherAnonVariant" -class EnumWithManyVariantsTupleVariantString(BaseModel): - content: str - -class EnumWithManyVariantsTupleVariantInt(BaseModel): - content: int - class EnumWithManyVariants(BaseModel): model_config = ConfigDict(use_enum_values=True) type: EnumWithManyVariantsTypes - content: Union[EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherAnonVariant, None] + content: Union[str, EnumWithManyVariantsAnonVariant, int, EnumWithManyVariantsAnotherAnonVariant, None] @classmethod @@ -102,7 +96,7 @@ def new_enum_with_many_variants_unit_variant(cls) -> EnumWithManyVariants: @classmethod - def new_enum_with_many_variants_tuple_variant_string(cls, content : EnumWithManyVariantsTupleVariantString): + def new_enum_with_many_variants_tuple_variant_string(cls, content : str): return cls( type=EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING, content=content @@ -118,7 +112,7 @@ def new_enum_with_many_variants_anon_variant(cls, uuid: str): @classmethod - def new_enum_with_many_variants_tuple_variant_int(cls, content : EnumWithManyVariantsTupleVariantInt): + def new_enum_with_many_variants_tuple_variant_int(cls, content : int): return cls( type=EnumWithManyVariantsTypes.TUPLE_VARIANT_INT, content=content diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index 58af3995..b10f2048 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -19,17 +19,14 @@ class StructHasVoidType(BaseModel): class EnumHasVoidTypeTypes(str, Enum): HAS_A_UNIT = "hasAUnit" -class EnumHasVoidTypeHasAUnit(BaseModel): - content: None - class EnumHasVoidType(BaseModel): model_config = ConfigDict(use_enum_values=True) type: EnumHasVoidTypeTypes - content: EnumHasVoidTypeHasAUnit + content: None @classmethod - def new_enum_has_void_type_has_a_unit(cls, content : EnumHasVoidTypeHasAUnit): + def new_enum_has_void_type_has_a_unit(cls, content : None): return cls( type=EnumHasVoidTypeTypes.HAS_A_UNIT, content=content diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 08b5ae83..400ded8e 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -27,17 +27,14 @@ class MoreOptionsTypes(str, Enum): EXACTLY = "exactly" BUILT = "built" -class MoreOptionsNews(BaseModel): - content: bool - class MoreOptions(BaseModel): model_config = ConfigDict(use_enum_values=True) type: MoreOptionsTypes - content: Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] + content: Union[bool, MoreOptionsExactly, MoreOptionsBuilt] @classmethod - def new_more_options_news(cls, content : MoreOptionsNews): + def new_more_options_news(cls, content : bool): return cls( type=MoreOptionsTypes.NEWS, content=content @@ -63,23 +60,14 @@ class OptionsTypes(str, Enum): BANANA = "banana" VERMONT = "vermont" -class OptionsRed(BaseModel): - content: bool - -class OptionsBanana(BaseModel): - content: str - -class OptionsVermont(BaseModel): - content: Options - class Options(BaseModel): model_config = ConfigDict(use_enum_values=True) type: OptionsTypes - content: Union[OptionsRed, OptionsBanana, OptionsVermont] + content: Union[bool, str, Options] @classmethod - def new_options_red(cls, content : OptionsRed): + def new_options_red(cls, content : bool): return cls( type=OptionsTypes.RED, content=content @@ -87,7 +75,7 @@ def new_options_red(cls, content : OptionsRed): @classmethod - def new_options_banana(cls, content : OptionsBanana): + def new_options_banana(cls, content : str): return cls( type=OptionsTypes.BANANA, content=content @@ -95,7 +83,7 @@ def new_options_banana(cls, content : OptionsBanana): @classmethod - def new_options_vermont(cls, content : OptionsVermont): + def new_options_vermont(cls, content : Options): return cls( type=OptionsTypes.VERMONT, content=content diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index b8cb7b71..4105003c 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -12,20 +12,14 @@ class SomeEnumTypes(str, Enum): CONTEXT = "Context" OTHER = "Other" -class SomeEnumContext(BaseModel): - content: str - -class SomeEnumOther(BaseModel): - content: int - class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[SomeEnumContext, SomeEnumOther] + content: Union[str, int] @classmethod - def new_some_enum_context(cls, content : SomeEnumContext): + def new_some_enum_context(cls, content : str): return cls( type=SomeEnumTypes.CONTEXT, content=content @@ -33,7 +27,7 @@ def new_some_enum_context(cls, content : SomeEnumContext): @classmethod - def new_some_enum_other(cls, content : SomeEnumOther): + def new_some_enum_other(cls, content : int): return cls( type=SomeEnumTypes.OTHER, content=content diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index e436054e..d59ae02d 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -63,13 +63,10 @@ class BoxyColorsTypes(str, Enum): -class BoxyColorsGreen(BaseModel): - content: str - class BoxyColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: BoxyColorsTypes - content: Union[BoxyColorsGreen, None] + content: Union[str, None] @classmethod @@ -89,7 +86,7 @@ def new_boxy_colors_blue(cls) -> BoxyColors: @classmethod - def new_boxy_colors_green(cls, content : BoxyColorsGreen): + def new_boxy_colors_green(cls, content : str): return cls( type=BoxyColorsTypes.GREEN, content=content diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index 3123b721..d08b4e75 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -17,26 +17,14 @@ class AdvancedColorsTypes(str, Enum): NUMBER_ARRAY = "number-array" REALLY_COOL_TYPE = "reallyCoolType" -class AdvancedColorsString(BaseModel): - content: str - -class AdvancedColorsNumber(BaseModel): - content: int - -class AdvancedColorsNumberArray(BaseModel): - content: List[int] - -class AdvancedColorsReallyCoolType(BaseModel): - content: ItemDetailsFieldValue - class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] + content: Union[str, int, List[int], ItemDetailsFieldValue] @classmethod - def new_advanced_colors_string(cls, content : AdvancedColorsString): + def new_advanced_colors_string(cls, content : str): return cls( type=AdvancedColorsTypes.STRING, content=content @@ -44,7 +32,7 @@ def new_advanced_colors_string(cls, content : AdvancedColorsString): @classmethod - def new_advanced_colors_number(cls, content : AdvancedColorsNumber): + def new_advanced_colors_number(cls, content : int): return cls( type=AdvancedColorsTypes.NUMBER, content=content @@ -52,7 +40,7 @@ def new_advanced_colors_number(cls, content : AdvancedColorsNumber): @classmethod - def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): + def new_advanced_colors_number_array(cls, content : List[int]): return cls( type=AdvancedColorsTypes.NUMBER_ARRAY, content=content @@ -60,7 +48,7 @@ def new_advanced_colors_number_array(cls, content : AdvancedColorsNumberArray): @classmethod - def new_advanced_colors_really_cool_type(cls, content : AdvancedColorsReallyCoolType): + def new_advanced_colors_really_cool_type(cls, content : ItemDetailsFieldValue): return cls( type=AdvancedColorsTypes.REALLY_COOL_TYPE, content=content diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 2bb4d151..d4b5fb00 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -636,63 +636,18 @@ impl Python { ty, shared: variant_shared, } => { - variant_class_names.push(variant_class_name.clone()); + let tuple_name = self + .format_type(ty, shared.generic_types.as_slice()) + .unwrap(); + variant_class_names.push(tuple_name.clone()); Self::gen_tuple_variant_constructor( &mut variant_constructors, variant_shared, shared, - variant_class_name.clone(), + tuple_name, tag_key, content_key, ); - match ty { - RustType::Generic { parameters, .. } => { - let mut generic_parameters: Vec = parameters - .iter() - .flat_map(|p| { - collect_generics_for_variant(p, &shared.generic_types) - }) - .collect(); - dedup(&mut generic_parameters); - if generic_parameters.is_empty() { - writeln!(w, "class {variant_class_name}(BaseModel):",).unwrap(); - } else { - self.add_import("typing".to_string(), "Generic".to_string()); - writeln!( - w, - "class {variant_class_name}(BaseModel, Generic[{}]):", - // note: generics is always unique (a single item) - generic_parameters.join(", ") - ) - .unwrap(); - } - } - other => { - let mut generics = vec![]; - if let RustType::Simple { id } = other { - // This could be a bare generic - if shared.generic_types.contains(id) { - generics = vec![id.clone()]; - } - } - if generics.is_empty() { - writeln!(w, "class {variant_class_name}(BaseModel):",).unwrap(); - } else { - self.add_import("typing".to_string(), "Generic".to_string()); - writeln!( - w, - "class {variant_class_name}(BaseModel, Generic[{}]):", - generics.join(", ") - ) - .unwrap(); - } - } - } - let python_type = self - .format_type(ty, shared.generic_types.as_slice()) - .unwrap(); - writeln!(w, " {content_key}: {python_type}")?; - writeln!(w)?; } RustEnumVariant::AnonymousStruct { fields, From 90793c7d26d54445cc95ef416ee123fab117664d Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 8 Nov 2024 11:14:48 -0500 Subject: [PATCH 39/57] dedup union --- .../tests/anonymous_struct_with_rename/output.py | 2 +- core/data/tests/can_apply_prefix_correctly/output.py | 2 +- .../data/tests/can_generate_algebraic_enum/output.py | 4 ++-- .../output.py | 2 +- .../output.py | 2 +- .../data/tests/can_handle_anonymous_struct/output.py | 4 ++-- core/data/tests/excluded_by_target_os/output.py | 2 +- core/data/tests/recursive_enum_decorator/output.py | 4 ++-- .../tests/serialize_anonymous_field_as/output.py | 2 +- core/data/tests/smart_pointers/output.py | 2 +- .../test_algebraic_enum_case_name_support/output.py | 2 +- core/src/language/python.rs | 12 +++++++----- 12 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index b041257f..f3d528fb 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -45,7 +45,7 @@ class AnonymousStructWithRenameTypes(str, Enum): class AnonymousStructWithRename(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AnonymousStructWithRenameTypes - content: Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] + content: Union[AnonymousStructWithRenameKebabCase, AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames] @classmethod diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 9c97a30b..a1b278f1 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -23,7 +23,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) t: AdvancedColorsTypes - c: Union[str, int, List[int], ItemDetailsFieldValue, List[ItemDetailsFieldValue], Dict[str, ItemDetailsFieldValue]] + c: Union[Dict[str, ItemDetailsFieldValue], ItemDetailsFieldValue, List[ItemDetailsFieldValue], List[int], int, str] @classmethod diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index d2961729..c09faa43 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -24,7 +24,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[str, int, int, List[int], ItemDetailsFieldValue] + content: Union[ItemDetailsFieldValue, List[int], int, str] @classmethod @@ -74,7 +74,7 @@ class AdvancedColors2Types(str, Enum): class AdvancedColors2(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColors2Types - content: Union[str, int, List[int], ItemDetailsFieldValue] + content: Union[ItemDetailsFieldValue, List[int], int, str] @classmethod diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 203e59aa..6ba7d80e 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -16,7 +16,7 @@ class SomeEnumTypes(str, Enum): class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[int, None] + content: Union[None, int] @classmethod diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 24872bf7..2015e544 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -35,7 +35,7 @@ class AutofilledByTypes(str, Enum): class AutofilledBy(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AutofilledByTypes - content: Union[AutofilledByUs, AutofilledBySomethingElse] + content: Union[AutofilledBySomethingElse, AutofilledByUs] @classmethod diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index caad2e88..43c8a1b3 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -39,7 +39,7 @@ class AutofilledByTypes(str, Enum): class AutofilledBy(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AutofilledByTypes - content: Union[AutofilledByUs, AutofilledBySomethingElse] + content: Union[AutofilledBySomethingElse, AutofilledByUs] @classmethod @@ -84,7 +84,7 @@ class EnumWithManyVariantsTypes(str, Enum): class EnumWithManyVariants(BaseModel): model_config = ConfigDict(use_enum_values=True) type: EnumWithManyVariantsTypes - content: Union[str, EnumWithManyVariantsAnonVariant, int, EnumWithManyVariantsAnotherAnonVariant, None] + content: Union[EnumWithManyVariantsAnonVariant, EnumWithManyVariantsAnotherAnonVariant, None, int, str] @classmethod diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index aa48ceb1..f4f74150 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -62,7 +62,7 @@ class TestEnumTypes(str, Enum): class TestEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: TestEnumTypes - content: Union[TestEnumVariant7, TestEnumVariant9, None] + content: Union[None, TestEnumVariant7, TestEnumVariant9] @classmethod diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 400ded8e..19dddbb4 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -30,7 +30,7 @@ class MoreOptionsTypes(str, Enum): class MoreOptions(BaseModel): model_config = ConfigDict(use_enum_values=True) type: MoreOptionsTypes - content: Union[bool, MoreOptionsExactly, MoreOptionsBuilt] + content: Union[MoreOptionsBuilt, MoreOptionsExactly, bool] @classmethod @@ -63,7 +63,7 @@ class OptionsTypes(str, Enum): class Options(BaseModel): model_config = ConfigDict(use_enum_values=True) type: OptionsTypes - content: Union[bool, str, Options] + content: Union[Options, bool, str] @classmethod diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 4105003c..041a2f4a 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -15,7 +15,7 @@ class SomeEnumTypes(str, Enum): class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[str, int] + content: Union[int, str] @classmethod diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index d59ae02d..9e1922f4 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -66,7 +66,7 @@ class BoxyColorsTypes(str, Enum): class BoxyColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: BoxyColorsTypes - content: Union[str, None] + content: Union[None, str] @classmethod diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index d08b4e75..47d6a0b3 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -20,7 +20,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[str, int, List[int], ItemDetailsFieldValue] + content: Union[ItemDetailsFieldValue, List[int], int, str] @classmethod diff --git a/core/src/language/python.rs b/core/src/language/python.rs index d4b5fb00..56db6d83 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -8,7 +8,7 @@ use crate::{ rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; use once_cell::sync::Lazy; -use std::collections::HashSet; +use std::collections::{BTreeSet, HashSet}; use std::hash::Hash; use std::{collections::HashMap, io::Write}; @@ -612,7 +612,7 @@ impl Python { )?; writeln!(w)?; - let mut variant_class_names = vec![]; + let mut variant_class_names = BTreeSet::new(); let mut variant_constructors = vec![]; let mut contains_unit_variant = false; // write each of the enum variant as a class: @@ -639,7 +639,7 @@ impl Python { let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) .unwrap(); - variant_class_names.push(tuple_name.clone()); + variant_class_names.insert(tuple_name.clone()); Self::gen_tuple_variant_constructor( &mut variant_constructors, variant_shared, @@ -653,7 +653,7 @@ impl Python { fields, shared: variant_shared, } => { - variant_class_names.push(variant_class_name.clone()); + variant_class_names.insert(variant_class_name.clone()); // writing is taken care of by write_types_for_anonymous_structs in write_enum // we just need to push to the variant_constructors self.gen_anon_struct_variant_constructor( @@ -669,9 +669,11 @@ impl Python { } } if contains_unit_variant { - variant_class_names.push("None".to_string()); + variant_class_names.insert("None".to_string()); } + let variant_class_names = variant_class_names.into_iter().collect::>(); + // finally, write the enum class itself consists of a type and a union of all the enum variants writeln!(w, "class {}(BaseModel):", shared.id.renamed)?; From 40803c4e749aae107244361fc53a31be98184dc2 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 8 Nov 2024 12:20:18 -0500 Subject: [PATCH 40/57] better error handling --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 56db6d83..3069cfdb 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -638,7 +638,7 @@ impl Python { } => { let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) - .unwrap(); + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; variant_class_names.insert(tuple_name.clone()); Self::gen_tuple_variant_constructor( &mut variant_constructors, From ce97007cddd6c2c0cd62528fadfc546fceb7a3b9 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 12 Nov 2024 10:13:13 -0500 Subject: [PATCH 41/57] remove once-cell; use index-set --- Cargo.lock | 1 + core/Cargo.toml | 2 +- core/src/language/python.rs | 37 +++++++++++++++++++++---------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba2c911f..15127744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,6 +746,7 @@ dependencies = [ "cool_asserts", "expect-test", "flexi_logger", + "indexmap", "itertools", "joinery", "lazy_format", diff --git a/core/Cargo.toml b/core/Cargo.toml index c6962340..f36d831c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,10 +15,10 @@ itertools = "0.12" lazy_format = "2" joinery = "2" topological-sort = { version = "0.2.2"} -once_cell = { version = "1"} convert_case = { version = "0.6.0"} log.workspace = true flexi_logger.workspace = true +indexmap = "2.6.0" [dev-dependencies] expect-test = "1.5" diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 3069cfdb..963d3caf 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -7,14 +7,15 @@ use crate::{ language::Language, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; -use once_cell::sync::Lazy; -use std::collections::{BTreeSet, HashSet}; +use std::collections::HashSet; use std::hash::Hash; +use std::sync::OnceLock; use std::{collections::HashMap, io::Write}; use super::CrateTypes; use convert_case::{Case, Casing}; +use indexmap::IndexSet; // Collect unique type vars from an enum field // Since we explode enums into unions of types, we need to extract all of the generics // used by each individual field @@ -612,7 +613,7 @@ impl Python { )?; writeln!(w)?; - let mut variant_class_names = BTreeSet::new(); + let mut variant_class_names = IndexSet::new(); let mut variant_constructors = vec![]; let mut contains_unit_variant = false; // write each of the enum variant as a class: @@ -699,22 +700,26 @@ impl Python { } } -static PYTHON_KEYWORDS: Lazy> = Lazy::new(|| { - HashSet::from_iter( - vec![ - "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", - "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", - "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", - "return", "try", "while", "with", "yield", - ] - .iter() - .map(|v| v.to_string()), - ) -}); +static PYTHON_KEYWORDS: OnceLock> = OnceLock::new(); + +fn get_python_keywords() -> &'static HashSet { + PYTHON_KEYWORDS.get_or_init(|| { + HashSet::from_iter( + vec![ + "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", + "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", + "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", + "raise", "return", "try", "while", "with", "yield", + ] + .iter() + .map(|v| v.to_string()), + ) + }) +} fn python_property_aware_rename(name: &str) -> String { let snake_name = name.to_case(Case::Snake); - match PYTHON_KEYWORDS.contains(&snake_name) { + match get_python_keywords().contains(&snake_name) { true => format!("{}_", name), false => snake_name, } From 260fa756846e332a2291a4797d78218b96738087 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 12 Nov 2024 10:18:45 -0500 Subject: [PATCH 42/57] update tests --- core/data/tests/anonymous_struct_with_rename/output.py | 2 +- core/data/tests/can_apply_prefix_correctly/output.py | 2 +- core/data/tests/can_generate_algebraic_enum/output.py | 4 ++-- .../output.py | 2 +- .../output.py | 2 +- core/data/tests/can_handle_anonymous_struct/output.py | 4 ++-- core/data/tests/excluded_by_target_os/output.py | 2 +- core/data/tests/recursive_enum_decorator/output.py | 4 ++-- core/data/tests/serialize_anonymous_field_as/output.py | 2 +- core/data/tests/smart_pointers/output.py | 2 +- .../tests/test_algebraic_enum_case_name_support/output.py | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index f3d528fb..b041257f 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -45,7 +45,7 @@ class AnonymousStructWithRenameTypes(str, Enum): class AnonymousStructWithRename(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AnonymousStructWithRenameTypes - content: Union[AnonymousStructWithRenameKebabCase, AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames] + content: Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] @classmethod diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index a1b278f1..9c97a30b 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -23,7 +23,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) t: AdvancedColorsTypes - c: Union[Dict[str, ItemDetailsFieldValue], ItemDetailsFieldValue, List[ItemDetailsFieldValue], List[int], int, str] + c: Union[str, int, List[int], ItemDetailsFieldValue, List[ItemDetailsFieldValue], Dict[str, ItemDetailsFieldValue]] @classmethod diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index c09faa43..8be6f721 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -24,7 +24,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[ItemDetailsFieldValue, List[int], int, str] + content: Union[str, int, List[int], ItemDetailsFieldValue] @classmethod @@ -74,7 +74,7 @@ class AdvancedColors2Types(str, Enum): class AdvancedColors2(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColors2Types - content: Union[ItemDetailsFieldValue, List[int], int, str] + content: Union[str, int, List[int], ItemDetailsFieldValue] @classmethod diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 6ba7d80e..203e59aa 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -16,7 +16,7 @@ class SomeEnumTypes(str, Enum): class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[None, int] + content: Union[int, None] @classmethod diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 2015e544..24872bf7 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -35,7 +35,7 @@ class AutofilledByTypes(str, Enum): class AutofilledBy(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AutofilledByTypes - content: Union[AutofilledBySomethingElse, AutofilledByUs] + content: Union[AutofilledByUs, AutofilledBySomethingElse] @classmethod diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 43c8a1b3..caad2e88 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -39,7 +39,7 @@ class AutofilledByTypes(str, Enum): class AutofilledBy(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AutofilledByTypes - content: Union[AutofilledBySomethingElse, AutofilledByUs] + content: Union[AutofilledByUs, AutofilledBySomethingElse] @classmethod @@ -84,7 +84,7 @@ class EnumWithManyVariantsTypes(str, Enum): class EnumWithManyVariants(BaseModel): model_config = ConfigDict(use_enum_values=True) type: EnumWithManyVariantsTypes - content: Union[EnumWithManyVariantsAnonVariant, EnumWithManyVariantsAnotherAnonVariant, None, int, str] + content: Union[str, EnumWithManyVariantsAnonVariant, int, EnumWithManyVariantsAnotherAnonVariant, None] @classmethod diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index f4f74150..aa48ceb1 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -62,7 +62,7 @@ class TestEnumTypes(str, Enum): class TestEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: TestEnumTypes - content: Union[None, TestEnumVariant7, TestEnumVariant9] + content: Union[TestEnumVariant7, TestEnumVariant9, None] @classmethod diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 19dddbb4..400ded8e 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -30,7 +30,7 @@ class MoreOptionsTypes(str, Enum): class MoreOptions(BaseModel): model_config = ConfigDict(use_enum_values=True) type: MoreOptionsTypes - content: Union[MoreOptionsBuilt, MoreOptionsExactly, bool] + content: Union[bool, MoreOptionsExactly, MoreOptionsBuilt] @classmethod @@ -63,7 +63,7 @@ class OptionsTypes(str, Enum): class Options(BaseModel): model_config = ConfigDict(use_enum_values=True) type: OptionsTypes - content: Union[Options, bool, str] + content: Union[bool, str, Options] @classmethod diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 041a2f4a..4105003c 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -15,7 +15,7 @@ class SomeEnumTypes(str, Enum): class SomeEnum(BaseModel): model_config = ConfigDict(use_enum_values=True) type: SomeEnumTypes - content: Union[int, str] + content: Union[str, int] @classmethod diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 9e1922f4..d59ae02d 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -66,7 +66,7 @@ class BoxyColorsTypes(str, Enum): class BoxyColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: BoxyColorsTypes - content: Union[None, str] + content: Union[str, None] @classmethod diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index 47d6a0b3..d08b4e75 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -20,7 +20,7 @@ class AdvancedColorsTypes(str, Enum): class AdvancedColors(BaseModel): model_config = ConfigDict(use_enum_values=True) type: AdvancedColorsTypes - content: Union[ItemDetailsFieldValue, List[int], int, str] + content: Union[str, int, List[int], ItemDetailsFieldValue] @classmethod From d4fabb632dd803d47042e96277eafa3ae3d2fe75 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Tue, 12 Nov 2024 10:39:36 -0500 Subject: [PATCH 43/57] add comments on unused function for generics --- core/src/language/python.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 963d3caf..3fcc7a0e 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -16,11 +16,15 @@ use super::CrateTypes; use convert_case::{Case, Casing}; use indexmap::IndexSet; + +// Utility function from the original author of supporting Python +// Since we won't be supporting generics right now, this function is unused and is left here for future reference // Collect unique type vars from an enum field // Since we explode enums into unions of types, we need to extract all of the generics // used by each individual field // We do this by exploring each field's type and comparing against the generics used by the enum // itself +#[allow(dead_code)] fn collect_generics_for_variant(variant_type: &RustType, generics: &[String]) -> Vec { let mut all = vec![]; match variant_type { From 83001f7a71b8d3060351e41b92f8d241b8a49891 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Wed, 13 Nov 2024 20:43:20 -0500 Subject: [PATCH 44/57] refactor how union works. Note this requires the struct to be externally tagged --- Cargo.lock | 1 - core/Cargo.toml | 1 - .../anonymous_struct_with_rename/output.py | 41 +-- .../can_apply_prefix_correctly/output.py | 67 ++--- .../can_generate_algebraic_enum/output.py | 102 ++----- .../output.py | 27 +- .../output.py | 31 +-- .../can_generate_bare_string_enum/output.py | 1 - .../output.py | 27 +- .../tests/can_generate_simple_enum/output.py | 1 - .../can_handle_anonymous_struct/output.py | 100 ++----- .../output.py | 1 - .../data/tests/can_handle_unit_type/output.py | 13 +- .../output.py | 1 - .../tests/excluded_by_target_os/output.py | 59 +--- .../tests/recursive_enum_decorator/output.py | 76 ++--- .../serialize_anonymous_field_as/output.py | 27 +- core/data/tests/smart_pointers/output.py | 37 +-- .../output.py | 47 +--- .../output.py | 1 - core/src/language/python.rs | 262 ++++++------------ 21 files changed, 267 insertions(+), 656 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15127744..ba2c911f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,7 +746,6 @@ dependencies = [ "cool_asserts", "expect-test", "flexi_logger", - "indexmap", "itertools", "joinery", "lazy_format", diff --git a/core/Cargo.toml b/core/Cargo.toml index f36d831c..72881e77 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -18,7 +18,6 @@ topological-sort = { version = "0.2.2"} convert_case = { version = "0.6.0"} log.workspace = true flexi_logger.workspace = true -indexmap = "2.6.0" [dev-dependencies] expect-test = "1.5" diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index b041257f..2ef51de6 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -8,14 +8,14 @@ from typing import List, Union -class AnonymousStructWithRenameList(BaseModel): +class AnonymousStructWithRenameListInner(BaseModel): """ Generated type representing the anonymous struct variant `List` of the `AnonymousStructWithRename` Rust enum """ list: List[str] -class AnonymousStructWithRenameLongFieldNames(BaseModel): +class AnonymousStructWithRenameLongFieldNamesInner(BaseModel): """ Generated type representing the anonymous struct variant `LongFieldNames` of the `AnonymousStructWithRename` Rust enum """ @@ -26,7 +26,7 @@ class AnonymousStructWithRenameLongFieldNames(BaseModel): but_one_more: List[str] -class AnonymousStructWithRenameKebabCase(BaseModel): +class AnonymousStructWithRenameKebabCaseInner(BaseModel): """ Generated type representing the anonymous struct variant `KebabCase` of the `AnonymousStructWithRename` Rust enum """ @@ -42,31 +42,16 @@ class AnonymousStructWithRenameTypes(str, Enum): LONG_FIELD_NAMES = "longFieldNames" KEBAB_CASE = "kebabCase" -class AnonymousStructWithRename(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AnonymousStructWithRenameTypes - content: Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] - - - @classmethod - def new_anonymous_struct_with_rename_list(cls, list: List[str]): - return cls( - type=AnonymousStructWithRenameTypes.LIST, - content=AnonymousStructWithRenameList(list = list) - ) - +class AnonymousStructWithRenameList(BaseModel): + type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.LIST + content: AnonymousStructWithRenameListInner - @classmethod - def new_anonymous_struct_with_rename_long_field_names(cls, some_long_field_name: str, and_: bool, but_one_more: List[str]): - return cls( - type=AnonymousStructWithRenameTypes.LONG_FIELD_NAMES, - content=AnonymousStructWithRenameLongFieldNames(some_long_field_name = some_long_field_name, and_ = and_, but_one_more = but_one_more) - ) +class AnonymousStructWithRenameLongFieldNames(BaseModel): + type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.LONG_FIELD_NAMES + content: AnonymousStructWithRenameLongFieldNamesInner +class AnonymousStructWithRenameKebabCase(BaseModel): + type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.KEBAB_CASE + content: AnonymousStructWithRenameKebabCaseInner - @classmethod - def new_anonymous_struct_with_rename_kebab_case(cls, another_list: List[str], camel_case_string_field: str, something_else: bool): - return cls( - type=AnonymousStructWithRenameTypes.KEBAB_CASE, - content=AnonymousStructWithRenameKebabCase(another_list = another_list, camel_case_string_field = camel_case_string_field, something_else = something_else) - ) +AnonymousStructWithRename = Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 9c97a30b..52216645 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Dict, List, Union @@ -20,55 +20,28 @@ class AdvancedColorsTypes(str, Enum): ARRAY_REALLY_COOL_TYPE = "ArrayReallyCoolType" DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" -class AdvancedColors(BaseModel): - model_config = ConfigDict(use_enum_values=True) - t: AdvancedColorsTypes - c: Union[str, int, List[int], ItemDetailsFieldValue, List[ItemDetailsFieldValue], Dict[str, ItemDetailsFieldValue]] +class AdvancedColorsString(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.STRING + c: str +class AdvancedColorsNumber(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + c: int - @classmethod - def new_advanced_colors_string(cls, c : str): - return cls( - t=AdvancedColorsTypes.STRING, - c=c - ) +class AdvancedColorsNumberArray(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + c: List[int] +class AdvancedColorsReallyCoolType(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + c: ItemDetailsFieldValue - @classmethod - def new_advanced_colors_number(cls, c : int): - return cls( - t=AdvancedColorsTypes.NUMBER, - c=c - ) +class AdvancedColorsArrayReallyCoolType(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE + c: List[ItemDetailsFieldValue] +class AdvancedColorsDictionaryReallyCoolType(BaseModel): + t: AdvancedColorsTypes = AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE + c: Dict[str, ItemDetailsFieldValue] - @classmethod - def new_advanced_colors_number_array(cls, c : List[int]): - return cls( - t=AdvancedColorsTypes.NUMBER_ARRAY, - c=c - ) - - - @classmethod - def new_advanced_colors_really_cool_type(cls, c : ItemDetailsFieldValue): - return cls( - t=AdvancedColorsTypes.REALLY_COOL_TYPE, - c=c - ) - - - @classmethod - def new_advanced_colors_array_really_cool_type(cls, c : List[ItemDetailsFieldValue]): - return cls( - t=AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE, - c=c - ) - - - @classmethod - def new_advanced_colors_dictionary_really_cool_type(cls, c : Dict[str, ItemDetailsFieldValue]): - return cls( - t=AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE, - c=c - ) +AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType, AdvancedColorsArrayReallyCoolType, AdvancedColorsDictionaryReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 8be6f721..facb158d 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import List, Union @@ -21,89 +21,47 @@ class AdvancedColorsTypes(str, Enum): NUMBER_ARRAY = "NumberArray" REALLY_COOL_TYPE = "ReallyCoolType" -class AdvancedColors(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AdvancedColorsTypes - content: Union[str, int, List[int], ItemDetailsFieldValue] +class AdvancedColorsString(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.STRING + content: str +class AdvancedColorsNumber(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + content: int - @classmethod - def new_advanced_colors_string(cls, content : str): - return cls( - type=AdvancedColorsTypes.STRING, - content=content - ) +class AdvancedColorsUnsignedNumber(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.UNSIGNED_NUMBER + content: int +class AdvancedColorsNumberArray(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + content: List[int] - @classmethod - def new_advanced_colors_number(cls, content : int): - return cls( - type=AdvancedColorsTypes.NUMBER, - content=content - ) +class AdvancedColorsReallyCoolType(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + content: ItemDetailsFieldValue - - @classmethod - def new_advanced_colors_unsigned_number(cls, content : int): - return cls( - type=AdvancedColorsTypes.UNSIGNED_NUMBER, - content=content - ) - - - @classmethod - def new_advanced_colors_number_array(cls, content : List[int]): - return cls( - type=AdvancedColorsTypes.NUMBER_ARRAY, - content=content - ) - - - @classmethod - def new_advanced_colors_really_cool_type(cls, content : ItemDetailsFieldValue): - return cls( - type=AdvancedColorsTypes.REALLY_COOL_TYPE, - content=content - ) +AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] class AdvancedColors2Types(str, Enum): STRING = "string" NUMBER = "number" NUMBER_ARRAY = "number-array" REALLY_COOL_TYPE = "really-cool-type" -class AdvancedColors2(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AdvancedColors2Types - content: Union[str, int, List[int], ItemDetailsFieldValue] - - - @classmethod - def new_advanced_colors_2_string(cls, content : str): - return cls( - type=AdvancedColors2Types.STRING, - content=content - ) - - - @classmethod - def new_advanced_colors_2_number(cls, content : int): - return cls( - type=AdvancedColors2Types.NUMBER, - content=content - ) +class AdvancedColors2String(BaseModel): + type: AdvancedColors2Types = AdvancedColors2Types.STRING + content: str +class AdvancedColors2Number(BaseModel): + type: AdvancedColors2Types = AdvancedColors2Types.NUMBER + content: int - @classmethod - def new_advanced_colors_2_number_array(cls, content : List[int]): - return cls( - type=AdvancedColors2Types.NUMBER_ARRAY, - content=content - ) +class AdvancedColors2NumberArray(BaseModel): + type: AdvancedColors2Types = AdvancedColors2Types.NUMBER_ARRAY + content: List[int] +class AdvancedColors2ReallyCoolType(BaseModel): + type: AdvancedColors2Types = AdvancedColors2Types.REALLY_COOL_TYPE + content: ItemDetailsFieldValue - @classmethod - def new_advanced_colors_2_really_cool_type(cls, content : ItemDetailsFieldValue): - return cls( - type=AdvancedColors2Types.REALLY_COOL_TYPE, - content=content - ) +AdvancedColors2 = Union[AdvancedColors2String, AdvancedColors2Number, AdvancedColors2NumberArray, AdvancedColors2ReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 203e59aa..59b84d19 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union @@ -12,24 +12,11 @@ class SomeEnumTypes(str, Enum): A = "A" C = "C" +class SomeEnumA(BaseModel): + type: SomeEnumTypes = SomeEnumTypes.A -class SomeEnum(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: SomeEnumTypes - content: Union[int, None] +class SomeEnumC(BaseModel): + type: SomeEnumTypes = SomeEnumTypes.C + content: int - - @classmethod - def new_some_enum_a(cls) -> SomeEnum: - return cls( - type=SomeEnumTypes.A, - content=None - ) - - - @classmethod - def new_some_enum_c(cls, content : int): - return cls( - type=SomeEnumTypes.C, - content=content - ) +SomeEnum = Union[SomeEnumA, SomeEnumC] diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 24872bf7..4a77517a 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -4,11 +4,11 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union -class AutofilledByUs(BaseModel): +class AutofilledByUsInner(BaseModel): """ Generated type representing the anonymous struct variant `Us` of the `AutofilledBy` Rust enum """ @@ -18,7 +18,7 @@ class AutofilledByUs(BaseModel): """ -class AutofilledBySomethingElse(BaseModel): +class AutofilledBySomethingElseInner(BaseModel): """ Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum """ @@ -32,23 +32,12 @@ class AutofilledByTypes(str, Enum): US = "Us" SOMETHING_ELSE = "SomethingElse" -class AutofilledBy(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AutofilledByTypes - content: Union[AutofilledByUs, AutofilledBySomethingElse] - - - @classmethod - def new_autofilled_by_us(cls, uuid: str): - return cls( - type=AutofilledByTypes.US, - content=AutofilledByUs(uuid = uuid) - ) +class AutofilledByUs(BaseModel): + type: AutofilledByTypes = AutofilledByTypes.US + content: AutofilledByUsInner +class AutofilledBySomethingElse(BaseModel): + type: AutofilledByTypes = AutofilledByTypes.SOMETHING_ELSE + content: AutofilledBySomethingElseInner - @classmethod - def new_autofilled_by_something_else(cls, uuid: str): - return cls( - type=AutofilledByTypes.SOMETHING_ELSE, - content=AutofilledBySomethingElse(uuid = uuid) - ) +AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index c14c5705..3fcf3636 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -4,7 +4,6 @@ from __future__ import annotations from enum import Enum -from pydantic import ConfigDict class Colors(str, Enum): diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index 9e9c32eb..c0c85c5d 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union @@ -15,24 +15,11 @@ class AddressTypes(str, Enum): FIXED_ADDRESS = "FixedAddress" NO_FIXED_ADDRESS = "NoFixedAddress" +class AddressFixedAddress(BaseModel): + type: AddressTypes = AddressTypes.FIXED_ADDRESS + content: AddressDetails -class Address(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AddressTypes - content: Union[AddressDetails, None] +class AddressNoFixedAddress(BaseModel): + type: AddressTypes = AddressTypes.NO_FIXED_ADDRESS - - @classmethod - def new_address_fixed_address(cls, content : AddressDetails): - return cls( - type=AddressTypes.FIXED_ADDRESS, - content=content - ) - - - @classmethod - def new_address_no_fixed_address(cls) -> Address: - return cls( - type=AddressTypes.NO_FIXED_ADDRESS, - content=None - ) +Address = Union[AddressFixedAddress, AddressNoFixedAddress] diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index c14c5705..3fcf3636 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -4,7 +4,6 @@ from __future__ import annotations from enum import Enum -from pydantic import ConfigDict class Colors(str, Enum): diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index caad2e88..0b61b4df 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -4,11 +4,11 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union -class AutofilledByUs(BaseModel): +class AutofilledByUsInner(BaseModel): """ Generated type representing the anonymous struct variant `Us` of the `AutofilledBy` Rust enum """ @@ -18,7 +18,7 @@ class AutofilledByUs(BaseModel): """ -class AutofilledBySomethingElse(BaseModel): +class AutofilledBySomethingElseInner(BaseModel): """ Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum """ @@ -36,34 +36,23 @@ class AutofilledByTypes(str, Enum): US = "Us" SOMETHING_ELSE = "SomethingElse" -class AutofilledBy(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AutofilledByTypes - content: Union[AutofilledByUs, AutofilledBySomethingElse] - - - @classmethod - def new_autofilled_by_us(cls, uuid: str): - return cls( - type=AutofilledByTypes.US, - content=AutofilledByUs(uuid = uuid) - ) +class AutofilledByUs(BaseModel): + type: AutofilledByTypes = AutofilledByTypes.US + content: AutofilledByUsInner +class AutofilledBySomethingElse(BaseModel): + type: AutofilledByTypes = AutofilledByTypes.SOMETHING_ELSE + content: AutofilledBySomethingElseInner - @classmethod - def new_autofilled_by_something_else(cls, uuid: str, thing: int): - return cls( - type=AutofilledByTypes.SOMETHING_ELSE, - content=AutofilledBySomethingElse(uuid = uuid, thing = thing) - ) -class EnumWithManyVariantsAnonVariant(BaseModel): +AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] +class EnumWithManyVariantsAnonVariantInner(BaseModel): """ Generated type representing the anonymous struct variant `AnonVariant` of the `EnumWithManyVariants` Rust enum """ uuid: str -class EnumWithManyVariantsAnotherAnonVariant(BaseModel): +class EnumWithManyVariantsAnotherAnonVariantInner(BaseModel): """ Generated type representing the anonymous struct variant `AnotherAnonVariant` of the `EnumWithManyVariants` Rust enum """ @@ -79,57 +68,26 @@ class EnumWithManyVariantsTypes(str, Enum): ANOTHER_UNIT_VARIANT = "AnotherUnitVariant" ANOTHER_ANON_VARIANT = "AnotherAnonVariant" +class EnumWithManyVariantsUnitVariant(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.UNIT_VARIANT +class EnumWithManyVariantsTupleVariantString(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING + content: str -class EnumWithManyVariants(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: EnumWithManyVariantsTypes - content: Union[str, EnumWithManyVariantsAnonVariant, int, EnumWithManyVariantsAnotherAnonVariant, None] - - - @classmethod - def new_enum_with_many_variants_unit_variant(cls) -> EnumWithManyVariants: - return cls( - type=EnumWithManyVariantsTypes.UNIT_VARIANT, - content=None - ) - - - @classmethod - def new_enum_with_many_variants_tuple_variant_string(cls, content : str): - return cls( - type=EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING, - content=content - ) - - - @classmethod - def new_enum_with_many_variants_anon_variant(cls, uuid: str): - return cls( - type=EnumWithManyVariantsTypes.ANON_VARIANT, - content=EnumWithManyVariantsAnonVariant(uuid = uuid) - ) - - - @classmethod - def new_enum_with_many_variants_tuple_variant_int(cls, content : int): - return cls( - type=EnumWithManyVariantsTypes.TUPLE_VARIANT_INT, - content=content - ) +class EnumWithManyVariantsAnonVariant(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANON_VARIANT + content: EnumWithManyVariantsAnonVariantInner +class EnumWithManyVariantsTupleVariantInt(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.TUPLE_VARIANT_INT + content: int - @classmethod - def new_enum_with_many_variants_another_unit_variant(cls) -> EnumWithManyVariants: - return cls( - type=EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT, - content=None - ) +class EnumWithManyVariantsAnotherUnitVariant(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT +class EnumWithManyVariantsAnotherAnonVariant(BaseModel): + type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT + content: EnumWithManyVariantsAnotherAnonVariantInner - @classmethod - def new_enum_with_many_variants_another_anon_variant(cls, uuid: str, thing: int): - return cls( - type=EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT, - content=EnumWithManyVariantsAnotherAnonVariant(uuid = uuid, thing = thing) - ) +EnumWithManyVariants = Union[EnumWithManyVariantsUnitVariant, EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherUnitVariant, EnumWithManyVariantsAnotherAnonVariant] diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index 563d2caa..3b96d390 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -4,7 +4,6 @@ from __future__ import annotations from enum import Enum -from pydantic import ConfigDict class Colors(str, Enum): diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index b10f2048..aa906caa 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -19,15 +19,8 @@ class StructHasVoidType(BaseModel): class EnumHasVoidTypeTypes(str, Enum): HAS_A_UNIT = "hasAUnit" -class EnumHasVoidType(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: EnumHasVoidTypeTypes +class EnumHasVoidTypeHasAUnit(BaseModel): + type: EnumHasVoidTypeTypes = EnumHasVoidTypeTypes.HAS_A_UNIT content: None - - @classmethod - def new_enum_has_void_type_has_a_unit(cls, content : None): - return cls( - type=EnumHasVoidTypeTypes.HAS_A_UNIT, - content=content - ) +EnumHasVoidType = EnumHasVoidTypeHasAUnit diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index cb503037..c25f2e3a 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -4,7 +4,6 @@ from __future__ import annotations from enum import Enum -from pydantic import ConfigDict class Colors(str, Enum): diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index aa48ceb1..1daa2aee 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -31,7 +31,7 @@ class OtherExcluded(BaseModel): class SomeEnum(str, Enum): pass -class TestEnumVariant7(BaseModel): +class TestEnumVariant7Inner(BaseModel): """ Generated type representing the anonymous struct variant `Variant7` of the `TestEnum` Rust enum """ @@ -40,7 +40,7 @@ class TestEnumVariant7(BaseModel): field_1: str -class TestEnumVariant9(BaseModel): +class TestEnumVariant9Inner(BaseModel): """ Generated type representing the anonymous struct variant `Variant9` of the `TestEnum` Rust enum """ @@ -56,50 +56,21 @@ class TestEnumTypes(str, Enum): VARIANT_8 = "Variant8" VARIANT_9 = "Variant9" +class TestEnumVariant1(BaseModel): + type: TestEnumTypes = TestEnumTypes.VARIANT_1 +class TestEnumVariant5(BaseModel): + type: TestEnumTypes = TestEnumTypes.VARIANT_5 +class TestEnumVariant7(BaseModel): + type: TestEnumTypes = TestEnumTypes.VARIANT_7 + content: TestEnumVariant7Inner -class TestEnum(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: TestEnumTypes - content: Union[TestEnumVariant7, TestEnumVariant9, None] - - - @classmethod - def new_test_enum_variant_1(cls) -> TestEnum: - return cls( - type=TestEnumTypes.VARIANT_1, - content=None - ) - - - @classmethod - def new_test_enum_variant_5(cls) -> TestEnum: - return cls( - type=TestEnumTypes.VARIANT_5, - content=None - ) - - - @classmethod - def new_test_enum_variant_7(cls, field_1: str): - return cls( - type=TestEnumTypes.VARIANT_7, - content=TestEnumVariant7(field_1 = field_1) - ) - - - @classmethod - def new_test_enum_variant_8(cls) -> TestEnum: - return cls( - type=TestEnumTypes.VARIANT_8, - content=None - ) +class TestEnumVariant8(BaseModel): + type: TestEnumTypes = TestEnumTypes.VARIANT_8 +class TestEnumVariant9(BaseModel): + type: TestEnumTypes = TestEnumTypes.VARIANT_9 + content: TestEnumVariant9Inner - @classmethod - def new_test_enum_variant_9(cls, field_2: str): - return cls( - type=TestEnumTypes.VARIANT_9, - content=TestEnumVariant9(field_2 = field_2) - ) +TestEnum = Union[TestEnumVariant1, TestEnumVariant5, TestEnumVariant7, TestEnumVariant8, TestEnumVariant9] diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 400ded8e..ef2964ed 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -4,18 +4,18 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union -class MoreOptionsExactly(BaseModel): +class MoreOptionsExactlyInner(BaseModel): """ Generated type representing the anonymous struct variant `Exactly` of the `MoreOptions` Rust enum """ config: str -class MoreOptionsBuilt(BaseModel): +class MoreOptionsBuiltInner(BaseModel): """ Generated type representing the anonymous struct variant `Built` of the `MoreOptions` Rust enum """ @@ -27,64 +27,34 @@ class MoreOptionsTypes(str, Enum): EXACTLY = "exactly" BUILT = "built" -class MoreOptions(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: MoreOptionsTypes - content: Union[bool, MoreOptionsExactly, MoreOptionsBuilt] - - - @classmethod - def new_more_options_news(cls, content : bool): - return cls( - type=MoreOptionsTypes.NEWS, - content=content - ) - +class MoreOptionsNews(BaseModel): + type: MoreOptionsTypes = MoreOptionsTypes.NEWS + content: bool - @classmethod - def new_more_options_exactly(cls, config: str): - return cls( - type=MoreOptionsTypes.EXACTLY, - content=MoreOptionsExactly(config = config) - ) +class MoreOptionsExactly(BaseModel): + type: MoreOptionsTypes = MoreOptionsTypes.EXACTLY + content: MoreOptionsExactlyInner +class MoreOptionsBuilt(BaseModel): + type: MoreOptionsTypes = MoreOptionsTypes.BUILT + content: MoreOptionsBuiltInner - @classmethod - def new_more_options_built(cls, top: MoreOptions): - return cls( - type=MoreOptionsTypes.BUILT, - content=MoreOptionsBuilt(top = top) - ) +MoreOptions = Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] class OptionsTypes(str, Enum): RED = "red" BANANA = "banana" VERMONT = "vermont" -class Options(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: OptionsTypes - content: Union[bool, str, Options] - - - @classmethod - def new_options_red(cls, content : bool): - return cls( - type=OptionsTypes.RED, - content=content - ) - +class OptionsRed(BaseModel): + type: OptionsTypes = OptionsTypes.RED + content: bool - @classmethod - def new_options_banana(cls, content : str): - return cls( - type=OptionsTypes.BANANA, - content=content - ) +class OptionsBanana(BaseModel): + type: OptionsTypes = OptionsTypes.BANANA + content: str +class OptionsVermont(BaseModel): + type: OptionsTypes = OptionsTypes.VERMONT + content: Options - @classmethod - def new_options_vermont(cls, content : Options): - return cls( - type=OptionsTypes.VERMONT, - content=content - ) +Options = Union[OptionsRed, OptionsBanana, OptionsVermont] diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 4105003c..2f18e390 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import Union @@ -12,23 +12,12 @@ class SomeEnumTypes(str, Enum): CONTEXT = "Context" OTHER = "Other" -class SomeEnum(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: SomeEnumTypes - content: Union[str, int] +class SomeEnumContext(BaseModel): + type: SomeEnumTypes = SomeEnumTypes.CONTEXT + content: str +class SomeEnumOther(BaseModel): + type: SomeEnumTypes = SomeEnumTypes.OTHER + content: int - @classmethod - def new_some_enum_context(cls, content : str): - return cls( - type=SomeEnumTypes.CONTEXT, - content=content - ) - - - @classmethod - def new_some_enum_other(cls, content : int): - return cls( - type=SomeEnumTypes.OTHER, - content=content - ) +SomeEnum = Union[SomeEnumContext, SomeEnumOther] diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index d59ae02d..03e21ec7 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import List, Union @@ -61,33 +61,14 @@ class BoxyColorsTypes(str, Enum): BLUE = "Blue" GREEN = "Green" +class BoxyColorsRed(BaseModel): + type: BoxyColorsTypes = BoxyColorsTypes.RED +class BoxyColorsBlue(BaseModel): + type: BoxyColorsTypes = BoxyColorsTypes.BLUE -class BoxyColors(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: BoxyColorsTypes - content: Union[str, None] +class BoxyColorsGreen(BaseModel): + type: BoxyColorsTypes = BoxyColorsTypes.GREEN + content: str - - @classmethod - def new_boxy_colors_red(cls) -> BoxyColors: - return cls( - type=BoxyColorsTypes.RED, - content=None - ) - - - @classmethod - def new_boxy_colors_blue(cls) -> BoxyColors: - return cls( - type=BoxyColorsTypes.BLUE, - content=None - ) - - - @classmethod - def new_boxy_colors_green(cls, content : str): - return cls( - type=BoxyColorsTypes.GREEN, - content=content - ) +BoxyColors = Union[BoxyColorsRed, BoxyColorsBlue, BoxyColorsGreen] diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index d08b4e75..6ddcedb4 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -4,7 +4,7 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel from typing import List, Union @@ -17,39 +17,20 @@ class AdvancedColorsTypes(str, Enum): NUMBER_ARRAY = "number-array" REALLY_COOL_TYPE = "reallyCoolType" -class AdvancedColors(BaseModel): - model_config = ConfigDict(use_enum_values=True) - type: AdvancedColorsTypes - content: Union[str, int, List[int], ItemDetailsFieldValue] +class AdvancedColorsString(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.STRING + content: str +class AdvancedColorsNumber(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + content: int - @classmethod - def new_advanced_colors_string(cls, content : str): - return cls( - type=AdvancedColorsTypes.STRING, - content=content - ) +class AdvancedColorsNumberArray(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + content: List[int] +class AdvancedColorsReallyCoolType(BaseModel): + type: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + content: ItemDetailsFieldValue - @classmethod - def new_advanced_colors_number(cls, content : int): - return cls( - type=AdvancedColorsTypes.NUMBER, - content=content - ) - - - @classmethod - def new_advanced_colors_number_array(cls, content : List[int]): - return cls( - type=AdvancedColorsTypes.NUMBER_ARRAY, - content=content - ) - - - @classmethod - def new_advanced_colors_really_cool_type(cls, content : ItemDetailsFieldValue): - return cls( - type=AdvancedColorsTypes.REALLY_COOL_TYPE, - content=content - ) +AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index b77f1d11..ae314bd0 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -4,7 +4,6 @@ from __future__ import annotations from enum import Enum -from pydantic import ConfigDict class Colors(str, Enum): diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 3fcc7a0e..4280b68c 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -1,7 +1,5 @@ use crate::parser::ParsedData; -use crate::rust_types::{ - RustEnumShared, RustEnumVariantShared, RustItem, RustType, RustTypeFormatError, SpecialRustType, -}; +use crate::rust_types::{RustEnumShared, RustItem, RustType, RustTypeFormatError, SpecialRustType}; use crate::topsort::topsort; use crate::{ language::Language, @@ -15,7 +13,6 @@ use std::{collections::HashMap, io::Write}; use super::CrateTypes; use convert_case::{Case, Casing}; -use indexmap::IndexSet; // Utility function from the original author of supporting Python // Since we won't be supporting generics right now, this function is unused and is left here for future reference @@ -270,12 +267,11 @@ impl Language for Python { fn write_enum(&mut self, w: &mut dyn Write, e: &RustEnum) -> std::io::Result<()> { // Make a suitable name for an anonymous struct enum variant let make_anonymous_struct_name = - |variant_name: &str| format!("{}{}", &e.shared().id.original, variant_name); + |variant_name: &str| format!("{}{}Inner", &e.shared().id.renamed, variant_name); // Generate named types for any anonymous struct variants of this enum self.write_types_for_anonymous_structs(w, e, &make_anonymous_struct_name)?; self.add_import("enum".to_string(), "Enum".to_string()); - self.add_import("pydantic".to_string(), "ConfigDict".to_string()); match e { // Write all the unit variants out (there can only be unit variants in // this case) @@ -310,6 +306,7 @@ impl Language for Python { self.write_algebraic_enum( tag_key, content_key, + &e.shared().id.renamed, shared, w, &make_anonymous_struct_name, @@ -460,126 +457,43 @@ impl Python { Ok(()) } - fn get_constructor_method_name(enum_name: &str, variant_name: &str) -> String { - format!("new_{}_{}", enum_name, variant_name).to_case(Case::Snake) - } - - fn gen_unit_variant_constructor( - variant_constructors: &mut Vec, - variant_shared: &RustEnumVariantShared, - enum_shared: &RustEnumShared, - tag_key: &str, - content_key: &str, - ) { - let method_name = - Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); - - variant_constructors.push(format!( - r#" - @classmethod - def {method_name}(cls) -> {class_name}: - return cls( - {tag_key}={enum_name}Types.{variant_tag}, - {content_key}=None - )"#, - tag_key = tag_key, - content_key = content_key, - enum_name = enum_shared.id.renamed, - variant_tag = variant_shared - .id - .renamed - .to_case(Case::Snake) - .to_uppercase(), - class_name = enum_shared.id.renamed, - )); - } - - fn gen_tuple_variant_constructor( - variant_constructors: &mut Vec, - variant_shared: &RustEnumVariantShared, - enum_shared: &RustEnumShared, - param_type: String, - tag_key: &str, - content_key: &str, - ) { - let method_name = - Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); - - variant_constructors.push(format!( - r#" - @classmethod - def {method_name}(cls, {content_key} : {param_type}): - return cls( - {tag_key}={enum_name}Types.{variant_tag}, - {content_key}={content_key} - )"#, - enum_name = enum_shared.id.renamed, - variant_tag = variant_shared - .id - .renamed - .to_case(Case::Snake) - .to_uppercase(), - )); - } - #[allow(clippy::too_many_arguments)] - fn gen_anon_struct_variant_constructor( - &mut self, - variant_constructors: &mut Vec, - variant_fields: &[RustField], - variant_shared: &RustEnumVariantShared, - enum_shared: &RustEnumShared, + fn write_variant_class( class_name: &str, tag_key: &str, + tag_type: &str, + tag_value: &str, content_key: &str, - ) { - let method_name = - Self::get_constructor_method_name(&enum_shared.id.renamed, &variant_shared.id.renamed); - - let ctor_param = variant_fields - .iter() - .map(|f| { - let python_field_name = python_property_aware_rename(&f.id.original); - let python_type = self - .format_type(&f.ty, enum_shared.generic_types.as_slice()) - .unwrap(); - (python_field_name, python_type) - }) - .collect::>(); - variant_constructors.push(format!( - r#" - @classmethod - def {method_name}(cls, {ctor_params}): - return cls( - {tag_key}={enum_name}Types.{variant_tag}, - {content_key}={class_name}({ctor_params_names}) - )"#, - ctor_params = ctor_param - .iter() - .map(|(name, ty)| format!("{}: {}", name, ty)) - .collect::>() - .join(", "), - content_key = content_key, - class_name = class_name, - ctor_params_names = ctor_param - .iter() - .map(|(name, _)| format!("{name} = {name}")) - .collect::>() - .join(", "), - tag_key = tag_key, - enum_name = enum_shared.id.renamed, - variant_tag = variant_shared - .id - .renamed - .to_case(Case::Snake) - .to_uppercase(), - )); + content_type: Option<&str>, + content_value: Option<&str>, + w: &mut dyn Write, + ) -> std::io::Result<()> { + writeln!(w, "class {class_name}(BaseModel):")?; + writeln!(w, " {tag_key}: {tag_type} = {tag_value}",)?; + if content_type.is_none() && content_value.is_none() { + return Ok(()); + } + writeln!( + w, + " {content_key}{}{}", + if let Some(content_type) = content_type { + format!(": {}", content_type) + } else { + "".to_string() + }, + if let Some(content_value) = content_value { + format!(" = {}", content_value) + } else { + "".to_string() + } + )?; + Ok(()) } - fn write_algebraic_enum( &mut self, tag_key: &str, content_key: &str, + enum_name: &str, shared: &RustEnumShared, w: &mut dyn Write, make_struct_name: &dyn Fn(&str) -> String, @@ -589,10 +503,10 @@ impl Python { .iter() .cloned() .for_each(|v| self.add_type_var(v)); - let mut variants: Vec<(String, Vec)> = Vec::new(); self.add_import("pydantic".to_string(), "BaseModel".to_string()); - // write "types" class: a union of all the enum variants - writeln!(w, "class {}Types(str, Enum):", shared.id.renamed)?; + // write "types" class: a union of all the enum variants4 + let enum_type_class_name = format!("{}Types", shared.id.renamed); + writeln!(w, "class {enum_type_class_name}(str, Enum):")?; let all_enum_variants_name = shared .variants .iter() @@ -601,104 +515,86 @@ impl Python { RustEnumVariant::Tuple { shared, .. } => shared.id.renamed.clone(), RustEnumVariant::AnonymousStruct { shared, .. } => shared.id.renamed.clone(), }) - .collect::>(); + .map(|name| (name.to_case(Case::Snake).to_uppercase(), name)) + .collect::>(); writeln!( w, "{}", all_enum_variants_name .iter() - .map(|name| format!( - " {} = \"{}\"", - name.to_case(Case::Snake).to_uppercase(), - name + .map(|(type_key_name, type_string)| format!( + " {type_key_name} = \"{type_string}\"" )) .collect::>() .join("\n") )?; writeln!(w)?; - let mut variant_class_names = IndexSet::new(); - let mut variant_constructors = vec![]; - let mut contains_unit_variant = false; + // all the types and class names for the enum variants in tuple + // (type_name, class_name) + let mut union_members = Vec::new(); // write each of the enum variant as a class: - for variant in &shared.variants { - let variant_class_name = make_struct_name(&variant.shared().id.original); + for (variant, (type_key_name, _)) in + shared.variants.iter().zip(all_enum_variants_name.iter()) + { + let variant_class_name = format!("{enum_name}{}", &variant.shared().id.original); + union_members.push(variant_class_name.clone()); match variant { - RustEnumVariant::Unit(unit_variant) => { - contains_unit_variant = true; - let variant_name = format!("{}{}", shared.id.renamed, unit_variant.id.renamed); - variants.push((variant_name.clone(), vec![])); - Self::gen_unit_variant_constructor( - &mut variant_constructors, - unit_variant, - shared, + RustEnumVariant::Unit(..) => { + Self::write_variant_class( + &variant_class_name, tag_key, + &enum_type_class_name, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, - ); + None, + None, + w, + )?; writeln!(w)?; } - RustEnumVariant::Tuple { - ty, - shared: variant_shared, - } => { + RustEnumVariant::Tuple { ty, .. } => { let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - variant_class_names.insert(tuple_name.clone()); - Self::gen_tuple_variant_constructor( - &mut variant_constructors, - variant_shared, - shared, - tuple_name, + Self::write_variant_class( + &variant_class_name, tag_key, + &enum_type_class_name, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, - ); + Some(&tuple_name), + None, + w, + )?; + writeln!(w)?; } RustEnumVariant::AnonymousStruct { - fields, shared: variant_shared, + .. } => { - variant_class_names.insert(variant_class_name.clone()); // writing is taken care of by write_types_for_anonymous_structs in write_enum - // we just need to push to the variant_constructors - self.gen_anon_struct_variant_constructor( - &mut variant_constructors, - fields, - variant_shared, - shared, + let variant_class_inner_name = make_struct_name(&variant_shared.id.original); + + Self::write_variant_class( &variant_class_name, tag_key, + &enum_type_class_name, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, - ); + Some(&variant_class_inner_name), + None, + w, + )?; + writeln!(w)?; } } } - if contains_unit_variant { - variant_class_names.insert("None".to_string()); - } - - let variant_class_names = variant_class_names.into_iter().collect::>(); - - // finally, write the enum class itself consists of a type and a union of all the enum variants - - writeln!(w, "class {}(BaseModel):", shared.id.renamed)?; - writeln!(w, " model_config = ConfigDict(use_enum_values=True)")?; - writeln!(w, " {tag_key}: {}Types", shared.id.renamed)?; - // if there is only 1 variant, we can use that directly, no need for Union - let union_type = if variant_class_names.len() == 1 { - variant_class_names[0].clone() + if union_members.len() == 1 { + writeln!(w, "{enum_name} = {}", union_members[0])?; } else { self.add_import("typing".to_string(), "Union".to_string()); - format!("Union[{}]", variant_class_names.join(", ")) - }; - writeln!(w, " {content_key}: {union_type}",)?; - writeln!(w)?; - if !variant_constructors.is_empty() { - writeln!( - w, - "{variant_constructors}", - variant_constructors = variant_constructors.join("\n\n") - )?; + writeln!(w, "{enum_name} = Union[{}]", union_members.join(", "))?; } Ok(()) } From 6afd25af5427f427e0ef2759dd4fcea5def69267 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Wed, 13 Nov 2024 23:53:44 -0500 Subject: [PATCH 45/57] proper rename --- core/data/tests/anonymous_struct_with_rename/output.py | 4 ++-- core/data/tests/excluded_by_target_os/output.py | 8 ++++---- core/data/tests/generate_types/output.py | 2 +- core/src/language/python.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index 2ef51de6..114bb13b 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -22,7 +22,7 @@ class AnonymousStructWithRenameLongFieldNamesInner(BaseModel): model_config = ConfigDict(populate_by_name=True) some_long_field_name: str - and_: bool + and_: bool = Field(alias="and") but_one_more: List[str] @@ -33,7 +33,7 @@ class AnonymousStructWithRenameKebabCaseInner(BaseModel): model_config = ConfigDict(populate_by_name=True) another_list: List[str] = Field(alias="another-list") - camel_case_string_field: str + camel_case_string_field: str = Field(alias="camelCaseStringField") something_else: bool = Field(alias="something-else") diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 1daa2aee..1238c9f6 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -4,14 +4,14 @@ from __future__ import annotations from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, Field from typing import Union class DefinedTwice(BaseModel): model_config = ConfigDict(populate_by_name=True) - field_1: str + field_1: str = Field(alias="field1") class Excluded(BaseModel): @@ -37,7 +37,7 @@ class TestEnumVariant7Inner(BaseModel): """ model_config = ConfigDict(populate_by_name=True) - field_1: str + field_1: str = Field(alias="field1") class TestEnumVariant9Inner(BaseModel): @@ -46,7 +46,7 @@ class TestEnumVariant9Inner(BaseModel): """ model_config = ConfigDict(populate_by_name=True) - field_2: str + field_2: str = Field(alias="field2") class TestEnumTypes(str, Enum): diff --git a/core/data/tests/generate_types/output.py b/core/data/tests/generate_types/output.py index 693e6a86..2f12a676 100644 --- a/core/data/tests/generate_types/output.py +++ b/core/data/tests/generate_types/output.py @@ -15,7 +15,7 @@ class Types(BaseModel): s: str static_s: str - int_8: int + int_8: int = Field(alias="int8") float: float double: float array: List[str] diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 4280b68c..40b3bcee 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -349,7 +349,7 @@ impl Python { .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); - let is_aliased = field.id.original != field.id.renamed; + let is_aliased = python_field_name != field.id.renamed; match (is_optional, is_aliased) { (true, true) => { self.add_import("typing".to_string(), "Optional".to_string()); From 0c9b8ac69638fe5aaa6e82d3d0af0359bdb141e5 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 11:45:25 -0500 Subject: [PATCH 46/57] use literal for tag keys --- .../anonymous_struct_with_rename/output.py | 8 +++---- .../can_apply_prefix_correctly/output.py | 14 +++++------ .../can_generate_algebraic_enum/output.py | 20 ++++++++-------- .../output.py | 6 ++--- .../output.py | 6 ++--- .../output.py | 6 ++--- .../can_handle_anonymous_struct/output.py | 18 +++++++-------- .../data/tests/can_handle_unit_type/output.py | 3 ++- .../tests/excluded_by_target_os/output.py | 12 +++++----- .../tests/recursive_enum_decorator/output.py | 14 +++++------ .../serialize_anonymous_field_as/output.py | 6 ++--- core/data/tests/smart_pointers/output.py | 8 +++---- .../output.py | 10 ++++---- core/src/language/python.rs | 23 ++++++++----------- 14 files changed, 76 insertions(+), 78 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index 114bb13b..51d381f3 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel, ConfigDict, Field -from typing import List, Union +from typing import List, Literal, Union class AnonymousStructWithRenameListInner(BaseModel): @@ -43,15 +43,15 @@ class AnonymousStructWithRenameTypes(str, Enum): KEBAB_CASE = "kebabCase" class AnonymousStructWithRenameList(BaseModel): - type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.LIST + AnonymousStructWithRenameTypes: Literal["list"] = "list" content: AnonymousStructWithRenameListInner class AnonymousStructWithRenameLongFieldNames(BaseModel): - type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.LONG_FIELD_NAMES + AnonymousStructWithRenameTypes: Literal["longFieldNames"] = "longFieldNames" content: AnonymousStructWithRenameLongFieldNamesInner class AnonymousStructWithRenameKebabCase(BaseModel): - type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.KEBAB_CASE + AnonymousStructWithRenameTypes: Literal["kebabCase"] = "kebabCase" content: AnonymousStructWithRenameKebabCaseInner AnonymousStructWithRename = Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 52216645..d850291f 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Dict, List, Union +from typing import Dict, List, Literal, Union class ItemDetailsFieldValue(BaseModel): @@ -21,27 +21,27 @@ class AdvancedColorsTypes(str, Enum): DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" class AdvancedColorsString(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.STRING + AdvancedColorsTypes: Literal["String"] = "String" c: str class AdvancedColorsNumber(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + AdvancedColorsTypes: Literal["Number"] = "Number" c: int class AdvancedColorsNumberArray(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" c: List[int] class AdvancedColorsReallyCoolType(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" c: ItemDetailsFieldValue class AdvancedColorsArrayReallyCoolType(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE + AdvancedColorsTypes: Literal["ArrayReallyCoolType"] = "ArrayReallyCoolType" c: List[ItemDetailsFieldValue] class AdvancedColorsDictionaryReallyCoolType(BaseModel): - t: AdvancedColorsTypes = AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE + AdvancedColorsTypes: Literal["DictionaryReallyCoolType"] = "DictionaryReallyCoolType" c: Dict[str, ItemDetailsFieldValue] AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType, AdvancedColorsArrayReallyCoolType, AdvancedColorsDictionaryReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index facb158d..8b128828 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import List, Union +from typing import List, Literal, Union class ItemDetailsFieldValue(BaseModel): @@ -22,23 +22,23 @@ class AdvancedColorsTypes(str, Enum): REALLY_COOL_TYPE = "ReallyCoolType" class AdvancedColorsString(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.STRING + AdvancedColorsTypes: Literal["String"] = "String" content: str class AdvancedColorsNumber(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + AdvancedColorsTypes: Literal["Number"] = "Number" content: int class AdvancedColorsUnsignedNumber(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.UNSIGNED_NUMBER + AdvancedColorsTypes: Literal["UnsignedNumber"] = "UnsignedNumber" content: int class AdvancedColorsNumberArray(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" content: List[int] class AdvancedColorsReallyCoolType(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" content: ItemDetailsFieldValue AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] @@ -49,19 +49,19 @@ class AdvancedColors2Types(str, Enum): REALLY_COOL_TYPE = "really-cool-type" class AdvancedColors2String(BaseModel): - type: AdvancedColors2Types = AdvancedColors2Types.STRING + AdvancedColors2Types: Literal["string"] = "string" content: str class AdvancedColors2Number(BaseModel): - type: AdvancedColors2Types = AdvancedColors2Types.NUMBER + AdvancedColors2Types: Literal["number"] = "number" content: int class AdvancedColors2NumberArray(BaseModel): - type: AdvancedColors2Types = AdvancedColors2Types.NUMBER_ARRAY + AdvancedColors2Types: Literal["number-array"] = "number-array" content: List[int] class AdvancedColors2ReallyCoolType(BaseModel): - type: AdvancedColors2Types = AdvancedColors2Types.REALLY_COOL_TYPE + AdvancedColors2Types: Literal["really-cool-type"] = "really-cool-type" content: ItemDetailsFieldValue AdvancedColors2 = Union[AdvancedColors2String, AdvancedColors2Number, AdvancedColors2NumberArray, AdvancedColors2ReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 59b84d19..82757ee0 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class SomeEnumTypes(str, Enum): @@ -13,10 +13,10 @@ class SomeEnumTypes(str, Enum): C = "C" class SomeEnumA(BaseModel): - type: SomeEnumTypes = SomeEnumTypes.A + SomeEnumTypes: Literal["A"] = "A" class SomeEnumC(BaseModel): - type: SomeEnumTypes = SomeEnumTypes.C + SomeEnumTypes: Literal["C"] = "C" content: int SomeEnum = Union[SomeEnumA, SomeEnumC] diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 4a77517a..02d86787 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class AutofilledByUsInner(BaseModel): @@ -33,11 +33,11 @@ class AutofilledByTypes(str, Enum): SOMETHING_ELSE = "SomethingElse" class AutofilledByUs(BaseModel): - type: AutofilledByTypes = AutofilledByTypes.US + AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner class AutofilledBySomethingElse(BaseModel): - type: AutofilledByTypes = AutofilledByTypes.SOMETHING_ELSE + AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index c0c85c5d..6eb48283 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class AddressDetails(BaseModel): @@ -16,10 +16,10 @@ class AddressTypes(str, Enum): NO_FIXED_ADDRESS = "NoFixedAddress" class AddressFixedAddress(BaseModel): - type: AddressTypes = AddressTypes.FIXED_ADDRESS + AddressTypes: Literal["FixedAddress"] = "FixedAddress" content: AddressDetails class AddressNoFixedAddress(BaseModel): - type: AddressTypes = AddressTypes.NO_FIXED_ADDRESS + AddressTypes: Literal["NoFixedAddress"] = "NoFixedAddress" Address = Union[AddressFixedAddress, AddressNoFixedAddress] diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 0b61b4df..a8c358fe 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class AutofilledByUsInner(BaseModel): @@ -37,11 +37,11 @@ class AutofilledByTypes(str, Enum): SOMETHING_ELSE = "SomethingElse" class AutofilledByUs(BaseModel): - type: AutofilledByTypes = AutofilledByTypes.US + AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner class AutofilledBySomethingElse(BaseModel): - type: AutofilledByTypes = AutofilledByTypes.SOMETHING_ELSE + AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] @@ -69,25 +69,25 @@ class EnumWithManyVariantsTypes(str, Enum): ANOTHER_ANON_VARIANT = "AnotherAnonVariant" class EnumWithManyVariantsUnitVariant(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.UNIT_VARIANT + EnumWithManyVariantsTypes: Literal["UnitVariant"] = "UnitVariant" class EnumWithManyVariantsTupleVariantString(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING + EnumWithManyVariantsTypes: Literal["TupleVariantString"] = "TupleVariantString" content: str class EnumWithManyVariantsAnonVariant(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANON_VARIANT + EnumWithManyVariantsTypes: Literal["AnonVariant"] = "AnonVariant" content: EnumWithManyVariantsAnonVariantInner class EnumWithManyVariantsTupleVariantInt(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.TUPLE_VARIANT_INT + EnumWithManyVariantsTypes: Literal["TupleVariantInt"] = "TupleVariantInt" content: int class EnumWithManyVariantsAnotherUnitVariant(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT + EnumWithManyVariantsTypes: Literal["AnotherUnitVariant"] = "AnotherUnitVariant" class EnumWithManyVariantsAnotherAnonVariant(BaseModel): - type: EnumWithManyVariantsTypes = EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT + EnumWithManyVariantsTypes: Literal["AnotherAnonVariant"] = "AnotherAnonVariant" content: EnumWithManyVariantsAnotherAnonVariantInner EnumWithManyVariants = Union[EnumWithManyVariantsUnitVariant, EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherUnitVariant, EnumWithManyVariantsAnotherAnonVariant] diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index aa906caa..0dd13314 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -5,6 +5,7 @@ from enum import Enum from pydantic import BaseModel, ConfigDict, Field +from typing import Literal class StructHasVoidType(BaseModel): @@ -20,7 +21,7 @@ class EnumHasVoidTypeTypes(str, Enum): HAS_A_UNIT = "hasAUnit" class EnumHasVoidTypeHasAUnit(BaseModel): - type: EnumHasVoidTypeTypes = EnumHasVoidTypeTypes.HAS_A_UNIT + EnumHasVoidTypeTypes: Literal["hasAUnit"] = "hasAUnit" content: None EnumHasVoidType = EnumHasVoidTypeHasAUnit diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 1238c9f6..f9ad75a8 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel, ConfigDict, Field -from typing import Union +from typing import Literal, Union class DefinedTwice(BaseModel): @@ -57,20 +57,20 @@ class TestEnumTypes(str, Enum): VARIANT_9 = "Variant9" class TestEnumVariant1(BaseModel): - type: TestEnumTypes = TestEnumTypes.VARIANT_1 + TestEnumTypes: Literal["Variant1"] = "Variant1" class TestEnumVariant5(BaseModel): - type: TestEnumTypes = TestEnumTypes.VARIANT_5 + TestEnumTypes: Literal["Variant5"] = "Variant5" class TestEnumVariant7(BaseModel): - type: TestEnumTypes = TestEnumTypes.VARIANT_7 + TestEnumTypes: Literal["Variant7"] = "Variant7" content: TestEnumVariant7Inner class TestEnumVariant8(BaseModel): - type: TestEnumTypes = TestEnumTypes.VARIANT_8 + TestEnumTypes: Literal["Variant8"] = "Variant8" class TestEnumVariant9(BaseModel): - type: TestEnumTypes = TestEnumTypes.VARIANT_9 + TestEnumTypes: Literal["Variant9"] = "Variant9" content: TestEnumVariant9Inner TestEnum = Union[TestEnumVariant1, TestEnumVariant5, TestEnumVariant7, TestEnumVariant8, TestEnumVariant9] diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index ef2964ed..191f78c9 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class MoreOptionsExactlyInner(BaseModel): @@ -28,15 +28,15 @@ class MoreOptionsTypes(str, Enum): BUILT = "built" class MoreOptionsNews(BaseModel): - type: MoreOptionsTypes = MoreOptionsTypes.NEWS + MoreOptionsTypes: Literal["news"] = "news" content: bool class MoreOptionsExactly(BaseModel): - type: MoreOptionsTypes = MoreOptionsTypes.EXACTLY + MoreOptionsTypes: Literal["exactly"] = "exactly" content: MoreOptionsExactlyInner class MoreOptionsBuilt(BaseModel): - type: MoreOptionsTypes = MoreOptionsTypes.BUILT + MoreOptionsTypes: Literal["built"] = "built" content: MoreOptionsBuiltInner MoreOptions = Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] @@ -46,15 +46,15 @@ class OptionsTypes(str, Enum): VERMONT = "vermont" class OptionsRed(BaseModel): - type: OptionsTypes = OptionsTypes.RED + OptionsTypes: Literal["red"] = "red" content: bool class OptionsBanana(BaseModel): - type: OptionsTypes = OptionsTypes.BANANA + OptionsTypes: Literal["banana"] = "banana" content: str class OptionsVermont(BaseModel): - type: OptionsTypes = OptionsTypes.VERMONT + OptionsTypes: Literal["vermont"] = "vermont" content: Options Options = Union[OptionsRed, OptionsBanana, OptionsVermont] diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 2f18e390..5aebe3e0 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import Union +from typing import Literal, Union class SomeEnumTypes(str, Enum): @@ -13,11 +13,11 @@ class SomeEnumTypes(str, Enum): OTHER = "Other" class SomeEnumContext(BaseModel): - type: SomeEnumTypes = SomeEnumTypes.CONTEXT + SomeEnumTypes: Literal["Context"] = "Context" content: str class SomeEnumOther(BaseModel): - type: SomeEnumTypes = SomeEnumTypes.OTHER + SomeEnumTypes: Literal["Other"] = "Other" content: int SomeEnum = Union[SomeEnumContext, SomeEnumOther] diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 03e21ec7..1cb4ad06 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import List, Union +from typing import List, Literal, Union class ArcyColors(BaseModel): @@ -62,13 +62,13 @@ class BoxyColorsTypes(str, Enum): GREEN = "Green" class BoxyColorsRed(BaseModel): - type: BoxyColorsTypes = BoxyColorsTypes.RED + BoxyColorsTypes: Literal["Red"] = "Red" class BoxyColorsBlue(BaseModel): - type: BoxyColorsTypes = BoxyColorsTypes.BLUE + BoxyColorsTypes: Literal["Blue"] = "Blue" class BoxyColorsGreen(BaseModel): - type: BoxyColorsTypes = BoxyColorsTypes.GREEN + BoxyColorsTypes: Literal["Green"] = "Green" content: str BoxyColors = Union[BoxyColorsRed, BoxyColorsBlue, BoxyColorsGreen] diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index 6ddcedb4..a6a14b97 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -5,7 +5,7 @@ from enum import Enum from pydantic import BaseModel -from typing import List, Union +from typing import List, Literal, Union class ItemDetailsFieldValue(BaseModel): @@ -18,19 +18,19 @@ class AdvancedColorsTypes(str, Enum): REALLY_COOL_TYPE = "reallyCoolType" class AdvancedColorsString(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.STRING + AdvancedColorsTypes: Literal["string"] = "string" content: str class AdvancedColorsNumber(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER + AdvancedColorsTypes: Literal["number"] = "number" content: int class AdvancedColorsNumberArray(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.NUMBER_ARRAY + AdvancedColorsTypes: Literal["number-array"] = "number-array" content: List[int] class AdvancedColorsReallyCoolType(BaseModel): - type: AdvancedColorsTypes = AdvancedColorsTypes.REALLY_COOL_TYPE + AdvancedColorsTypes: Literal["reallyCoolType"] = "reallyCoolType" content: ItemDetailsFieldValue AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 40b3bcee..b451b44f 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -459,17 +459,18 @@ impl Python { #[allow(clippy::too_many_arguments)] fn write_variant_class( + &mut self, class_name: &str, tag_key: &str, - tag_type: &str, tag_value: &str, content_key: &str, content_type: Option<&str>, content_value: Option<&str>, w: &mut dyn Write, ) -> std::io::Result<()> { + self.add_import("typing".to_string(), "Literal".to_string()); writeln!(w, "class {class_name}(BaseModel):")?; - writeln!(w, " {tag_key}: {tag_type} = {tag_value}",)?; + writeln!(w, " {tag_key}: Literal[{tag_value}] = {tag_value}",)?; if content_type.is_none() && content_value.is_none() { return Ok(()); } @@ -534,18 +535,16 @@ impl Python { // (type_name, class_name) let mut union_members = Vec::new(); // write each of the enum variant as a class: - for (variant, (type_key_name, _)) in - shared.variants.iter().zip(all_enum_variants_name.iter()) + for (variant, (.., type_value)) in shared.variants.iter().zip(all_enum_variants_name.iter()) { let variant_class_name = format!("{enum_name}{}", &variant.shared().id.original); union_members.push(variant_class_name.clone()); match variant { RustEnumVariant::Unit(..) => { - Self::write_variant_class( + self.write_variant_class( &variant_class_name, - tag_key, &enum_type_class_name, - format!("{enum_type_class_name}.{type_key_name}",).as_str(), + format!("\"{type_value}\"",).as_str(), content_key, None, None, @@ -557,11 +556,10 @@ impl Python { let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - Self::write_variant_class( + self.write_variant_class( &variant_class_name, - tag_key, &enum_type_class_name, - format!("{enum_type_class_name}.{type_key_name}",).as_str(), + format!("\"{type_value}\"",).as_str(), content_key, Some(&tuple_name), None, @@ -576,11 +574,10 @@ impl Python { // writing is taken care of by write_types_for_anonymous_structs in write_enum let variant_class_inner_name = make_struct_name(&variant_shared.id.original); - Self::write_variant_class( + self.write_variant_class( &variant_class_name, - tag_key, &enum_type_class_name, - format!("{enum_type_class_name}.{type_key_name}",).as_str(), + format!("\"{type_value}\"",).as_str(), content_key, Some(&variant_class_inner_name), None, From a10d3e79723d5e715f903df5a6a240b45ec41760 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 12:01:21 -0500 Subject: [PATCH 47/57] generate comments properly --- .../anonymous_struct_with_rename/output.py | 6 ---- .../can_apply_prefix_correctly/output.py | 9 ------ .../can_generate_algebraic_enum/output.py | 29 +++++++++--------- .../output.py | 5 ---- .../output.py | 14 +++++---- .../output.py | 5 ---- .../can_handle_anonymous_struct/output.py | 25 ++++++++-------- .../data/tests/can_handle_unit_type/output.py | 7 ++--- .../tests/excluded_by_target_os/output.py | 7 ----- .../tests/recursive_enum_decorator/output.py | 11 ------- .../serialize_anonymous_field_as/output.py | 8 ++--- core/data/tests/smart_pointers/output.py | 9 ++---- .../output.py | 7 ----- core/src/language/python.rs | 30 ++++++++----------- 14 files changed, 57 insertions(+), 115 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index 51d381f3..b701b65a 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel, ConfigDict, Field from typing import List, Literal, Union @@ -37,11 +36,6 @@ class AnonymousStructWithRenameKebabCaseInner(BaseModel): something_else: bool = Field(alias="something-else") -class AnonymousStructWithRenameTypes(str, Enum): - LIST = "list" - LONG_FIELD_NAMES = "longFieldNames" - KEBAB_CASE = "kebabCase" - class AnonymousStructWithRenameList(BaseModel): AnonymousStructWithRenameTypes: Literal["list"] = "list" content: AnonymousStructWithRenameListInner diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index d850291f..1bf22790 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Dict, List, Literal, Union @@ -12,14 +11,6 @@ class ItemDetailsFieldValue(BaseModel): hello: str -class AdvancedColorsTypes(str, Enum): - STRING = "String" - NUMBER = "Number" - NUMBER_ARRAY = "NumberArray" - REALLY_COOL_TYPE = "ReallyCoolType" - ARRAY_REALLY_COOL_TYPE = "ArrayReallyCoolType" - DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" - class AdvancedColorsString(BaseModel): AdvancedColorsTypes: Literal["String"] = "String" c: str diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 8b128828..37f4db6d 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union @@ -14,13 +13,9 @@ class ItemDetailsFieldValue(BaseModel): """ pass -class AdvancedColorsTypes(str, Enum): - STRING = "String" - NUMBER = "Number" - UNSIGNED_NUMBER = "UnsignedNumber" - NUMBER_ARRAY = "NumberArray" - REALLY_COOL_TYPE = "ReallyCoolType" - +""" +This is a case comment +""" class AdvancedColorsString(BaseModel): AdvancedColorsTypes: Literal["String"] = "String" content: str @@ -37,17 +32,20 @@ class AdvancedColorsNumberArray(BaseModel): AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" content: List[int] +""" +Comment on the last element +""" class AdvancedColorsReallyCoolType(BaseModel): AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" content: ItemDetailsFieldValue +""" +Enum comment +""" AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] -class AdvancedColors2Types(str, Enum): - STRING = "string" - NUMBER = "number" - NUMBER_ARRAY = "number-array" - REALLY_COOL_TYPE = "really-cool-type" - +""" +This is a case comment +""" class AdvancedColors2String(BaseModel): AdvancedColors2Types: Literal["string"] = "string" content: str @@ -60,6 +58,9 @@ class AdvancedColors2NumberArray(BaseModel): AdvancedColors2Types: Literal["number-array"] = "number-array" content: List[int] +""" +Comment on the last element +""" class AdvancedColors2ReallyCoolType(BaseModel): AdvancedColors2Types: Literal["really-cool-type"] = "really-cool-type" content: ItemDetailsFieldValue diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index 82757ee0..ba4c3cc7 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -3,15 +3,10 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union -class SomeEnumTypes(str, Enum): - A = "A" - C = "C" - class SomeEnumA(BaseModel): SomeEnumTypes: Literal["A"] = "A" diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 02d86787..0ea777c9 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -28,16 +27,21 @@ class AutofilledBySomethingElseInner(BaseModel): """ -class AutofilledByTypes(str, Enum): - US = "Us" - SOMETHING_ELSE = "SomethingElse" - +""" +This field was autofilled by us +""" class AutofilledByUs(BaseModel): AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner +""" +Something else autofilled this field +""" class AutofilledBySomethingElse(BaseModel): AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner +""" +Enum keeping track of who autofilled a field +""" AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index 6eb48283..ae8a2c45 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -11,10 +10,6 @@ class AddressDetails(BaseModel): pass -class AddressTypes(str, Enum): - FIXED_ADDRESS = "FixedAddress" - NO_FIXED_ADDRESS = "NoFixedAddress" - class AddressFixedAddress(BaseModel): AddressTypes: Literal["FixedAddress"] = "FixedAddress" content: AddressDetails diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index a8c358fe..069a7e34 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -32,18 +31,23 @@ class AutofilledBySomethingElseInner(BaseModel): """ -class AutofilledByTypes(str, Enum): - US = "Us" - SOMETHING_ELSE = "SomethingElse" - +""" +This field was autofilled by us +""" class AutofilledByUs(BaseModel): AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner +""" +Something else autofilled this field +""" class AutofilledBySomethingElse(BaseModel): AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner +""" +Enum keeping track of who autofilled a field +""" AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] class EnumWithManyVariantsAnonVariantInner(BaseModel): """ @@ -60,14 +64,6 @@ class EnumWithManyVariantsAnotherAnonVariantInner(BaseModel): thing: int -class EnumWithManyVariantsTypes(str, Enum): - UNIT_VARIANT = "UnitVariant" - TUPLE_VARIANT_STRING = "TupleVariantString" - ANON_VARIANT = "AnonVariant" - TUPLE_VARIANT_INT = "TupleVariantInt" - ANOTHER_UNIT_VARIANT = "AnotherUnitVariant" - ANOTHER_ANON_VARIANT = "AnotherAnonVariant" - class EnumWithManyVariantsUnitVariant(BaseModel): EnumWithManyVariantsTypes: Literal["UnitVariant"] = "UnitVariant" @@ -90,4 +86,7 @@ class EnumWithManyVariantsAnotherAnonVariant(BaseModel): EnumWithManyVariantsTypes: Literal["AnotherAnonVariant"] = "AnotherAnonVariant" content: EnumWithManyVariantsAnotherAnonVariantInner +""" +This is a comment (yareek sameek wuz here) +""" EnumWithManyVariants = Union[EnumWithManyVariantsUnitVariant, EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherUnitVariant, EnumWithManyVariantsAnotherAnonVariant] diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index 0dd13314..a6ffd367 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel, ConfigDict, Field from typing import Literal @@ -17,11 +16,11 @@ class StructHasVoidType(BaseModel): this_is_a_unit: None = Field(alias="thisIsAUnit") -class EnumHasVoidTypeTypes(str, Enum): - HAS_A_UNIT = "hasAUnit" - class EnumHasVoidTypeHasAUnit(BaseModel): EnumHasVoidTypeTypes: Literal["hasAUnit"] = "hasAUnit" content: None +""" +This enum has a variant associated with unit data +""" EnumHasVoidType = EnumHasVoidTypeHasAUnit diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index f9ad75a8..37cc7e57 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -49,13 +49,6 @@ class TestEnumVariant9Inner(BaseModel): field_2: str = Field(alias="field2") -class TestEnumTypes(str, Enum): - VARIANT_1 = "Variant1" - VARIANT_5 = "Variant5" - VARIANT_7 = "Variant7" - VARIANT_8 = "Variant8" - VARIANT_9 = "Variant9" - class TestEnumVariant1(BaseModel): TestEnumTypes: Literal["Variant1"] = "Variant1" diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index 191f78c9..b04799f9 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -22,11 +21,6 @@ class MoreOptionsBuiltInner(BaseModel): top: MoreOptions -class MoreOptionsTypes(str, Enum): - NEWS = "news" - EXACTLY = "exactly" - BUILT = "built" - class MoreOptionsNews(BaseModel): MoreOptionsTypes: Literal["news"] = "news" content: bool @@ -40,11 +34,6 @@ class MoreOptionsBuilt(BaseModel): content: MoreOptionsBuiltInner MoreOptions = Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] -class OptionsTypes(str, Enum): - RED = "red" - BANANA = "banana" - VERMONT = "vermont" - class OptionsRed(BaseModel): OptionsTypes: Literal["red"] = "red" content: bool diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 5aebe3e0..87c06a9d 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -3,15 +3,13 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import Literal, Union -class SomeEnumTypes(str, Enum): - CONTEXT = "Context" - OTHER = "Other" - +""" +The associated String contains some opaque context +""" class SomeEnumContext(BaseModel): SomeEnumTypes: Literal["Context"] = "Context" content: str diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 1cb4ad06..6b290653 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union @@ -56,11 +55,6 @@ class RcyColors(BaseModel): green: str -class BoxyColorsTypes(str, Enum): - RED = "Red" - BLUE = "Blue" - GREEN = "Green" - class BoxyColorsRed(BaseModel): BoxyColorsTypes: Literal["Red"] = "Red" @@ -71,4 +65,7 @@ class BoxyColorsGreen(BaseModel): BoxyColorsTypes: Literal["Green"] = "Green" content: str +""" +This is a comment. +""" BoxyColors = Union[BoxyColorsRed, BoxyColorsBlue, BoxyColorsGreen] diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index a6a14b97..f9362543 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union @@ -11,12 +10,6 @@ class ItemDetailsFieldValue(BaseModel): pass -class AdvancedColorsTypes(str, Enum): - STRING = "string" - NUMBER = "number" - NUMBER_ARRAY = "number-array" - REALLY_COOL_TYPE = "reallyCoolType" - class AdvancedColorsString(BaseModel): AdvancedColorsTypes: Literal["string"] = "string" content: str diff --git a/core/src/language/python.rs b/core/src/language/python.rs index b451b44f..f0f52ecc 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -271,11 +271,11 @@ impl Language for Python { // Generate named types for any anonymous struct variants of this enum self.write_types_for_anonymous_structs(w, e, &make_anonymous_struct_name)?; - self.add_import("enum".to_string(), "Enum".to_string()); match e { // Write all the unit variants out (there can only be unit variants in // this case) RustEnum::Unit(shared) => { + self.add_import("enum".to_string(), "Enum".to_string()); writeln!(w, "class {}(str, Enum):", shared.id.renamed)?; if shared.variants.is_empty() { writeln!(w, " pass")?; @@ -505,9 +505,7 @@ impl Python { .cloned() .for_each(|v| self.add_type_var(v)); self.add_import("pydantic".to_string(), "BaseModel".to_string()); - // write "types" class: a union of all the enum variants4 let enum_type_class_name = format!("{}Types", shared.id.renamed); - writeln!(w, "class {enum_type_class_name}(str, Enum):")?; let all_enum_variants_name = shared .variants .iter() @@ -518,19 +516,6 @@ impl Python { }) .map(|name| (name.to_case(Case::Snake).to_uppercase(), name)) .collect::>(); - writeln!( - w, - "{}", - all_enum_variants_name - .iter() - .map(|(type_key_name, type_string)| format!( - " {type_key_name} = \"{type_string}\"" - )) - .collect::>() - .join("\n") - )?; - writeln!(w)?; - // all the types and class names for the enum variants in tuple // (type_name, class_name) let mut union_members = Vec::new(); @@ -540,7 +525,8 @@ impl Python { let variant_class_name = format!("{enum_name}{}", &variant.shared().id.original); union_members.push(variant_class_name.clone()); match variant { - RustEnumVariant::Unit(..) => { + RustEnumVariant::Unit(variant_shared) => { + self.write_comments(w, true, &variant_shared.comments, 0)?; self.write_variant_class( &variant_class_name, &enum_type_class_name, @@ -552,7 +538,12 @@ impl Python { )?; writeln!(w)?; } - RustEnumVariant::Tuple { ty, .. } => { + RustEnumVariant::Tuple { + ty, + shared: variant_shared, + } => { + self.write_comments(w, true, &variant_shared.comments, 0)?; + let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; @@ -571,6 +562,8 @@ impl Python { shared: variant_shared, .. } => { + self.write_comments(w, true, &variant_shared.comments, 0)?; + // writing is taken care of by write_types_for_anonymous_structs in write_enum let variant_class_inner_name = make_struct_name(&variant_shared.id.original); @@ -587,6 +580,7 @@ impl Python { } } } + self.write_comments(w, true, &shared.comments, 0)?; if union_members.len() == 1 { writeln!(w, "{enum_name} = {}", union_members[0])?; } else { From 3df3ece81dab3aa684fa249db20f749698a3d088 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 12:24:11 -0500 Subject: [PATCH 48/57] fix type aliasing issue --- .../tests/test_optional_type_alias/output.py | 5 ++- core/src/language/python.rs | 45 ++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/core/data/tests/test_optional_type_alias/output.py b/core/data/tests/test_optional_type_alias/output.py index 52c765ba..a3169eac 100644 --- a/core/data/tests/test_optional_type_alias/output.py +++ b/core/data/tests/test_optional_type_alias/output.py @@ -4,12 +4,13 @@ from __future__ import annotations from pydantic import BaseModel +from typing import Optional -OptionalU16 = int +OptionalU16 = Optional[int] -OptionalU32 = int +OptionalU32 = Optional[int] class FooBar(BaseModel): diff --git a/core/src/language/python.rs b/core/src/language/python.rs index f0f52ecc..d00ca7c2 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -172,7 +172,13 @@ impl Language for Python { Ok(format!("List[{}]", self.format_type(rtype, generic_types)?)) } // We add optionality above the type formatting level - SpecialRustType::Option(rtype) => self.format_type(rtype, generic_types), + SpecialRustType::Option(rtype) => { + self.add_import("typing".to_string(), "Optional".to_string()); + Ok(format!( + "Optional[{}]", + self.format_type(rtype, generic_types)? + )) + } SpecialRustType::HashMap(rtype1, rtype2) => { self.add_import("typing".to_string(), "Dict".to_string()); Ok(format!( @@ -345,16 +351,18 @@ impl Python { generic_types: &[String], ) -> std::io::Result<()> { let is_optional = field.ty.is_optional() || field.has_default; + // currently, if a field has a serde default value, it must be an Option + let not_optional_but_default = !field.ty.is_optional() && field.has_default; let python_type = self .format_type(&field.ty, generic_types) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; let python_field_name = python_property_aware_rename(&field.id.original); let is_aliased = python_field_name != field.id.renamed; - match (is_optional, is_aliased) { + match (not_optional_but_default, is_aliased) { (true, true) => { self.add_import("typing".to_string(), "Optional".to_string()); self.add_import("pydantic".to_string(), "Field".to_string()); - writeln!(w, " {python_field_name}: Optional[{python_type}] = Field(alias=\"{renamed}\", default=None)", renamed=field.id.renamed)?; + write!(w, " {python_field_name}: Optional[{python_type}] = Field(alias=\"{renamed}\", default=None)", renamed=field.id.renamed)?; } (true, false) => { self.add_import("typing".to_string(), "Optional".to_string()); @@ -366,18 +374,31 @@ impl Python { } (false, true) => { self.add_import("pydantic".to_string(), "Field".to_string()); - writeln!( + write!( w, - " {python_field_name}: {python_type} = Field(alias=\"{renamed}\")", + " {python_field_name}: {python_type} = Field(alias=\"{renamed}\"", renamed = field.id.renamed - )? + )?; + if is_optional { + writeln!(w, ", default=None)")?; + } else { + writeln!(w, ")")?; + } + } + (false, false) => { + write!( + w, + " {python_field_name}: {python_type}", + python_field_name = python_field_name, + python_type = python_type + )?; + if is_optional { + self.add_import("pydantic".to_string(), "Field".to_string()); + writeln!(w, " = Field(default=None)")?; + } else { + writeln!(w)?; + } } - (false, false) => writeln!( - w, - " {python_field_name}: {python_type}", - python_field_name = python_field_name, - python_type = python_type - )?, } self.write_comments(w, true, &field.comments, 1)?; From 533c75f4fbd9c198d90a7c048a555016443dfc20 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 12:28:06 -0500 Subject: [PATCH 49/57] remove unneeded newlines --- core/data/tests/anonymous_struct_with_rename/output.py | 3 --- core/data/tests/can_apply_prefix_correctly/output.py | 1 - core/data/tests/can_generate_algebraic_enum/output.py | 1 - .../output.py | 2 -- .../tests/can_generate_empty_algebraic_enum/output.py | 1 - .../can_generate_simple_struct_with_a_comment/output.py | 2 -- core/data/tests/can_generate_slice_of_user_type/output.py | 1 - .../can_generate_struct_with_skipped_fields/output.py | 1 - core/data/tests/can_generate_unit_structs/output.py | 1 - core/data/tests/can_handle_anonymous_struct/output.py | 4 ---- core/data/tests/can_handle_serde_rename/output.py | 2 -- core/data/tests/can_handle_serde_rename_all/output.py | 2 -- .../tests/can_handle_serde_rename_on_top_level/output.py | 2 -- core/data/tests/can_handle_unit_type/output.py | 1 - .../tests/can_recognize_types_inside_modules/output.py | 4 ---- core/data/tests/excluded_by_target_os/output.py | 8 -------- core/data/tests/generate_types/output.py | 2 -- .../generates_empty_structs_and_initializers/output.py | 1 - core/data/tests/kebab_case_rename/output.py | 1 - core/data/tests/orders_types/output.py | 5 ----- core/data/tests/recursive_enum_decorator/output.py | 2 -- core/data/tests/resolves_qualified_type/output.py | 1 - core/data/tests/serialize_field_as/output.py | 1 - core/data/tests/serialize_type_alias/output.py | 4 ---- core/data/tests/smart_pointers/output.py | 6 ------ .../tests/test_algebraic_enum_case_name_support/output.py | 1 - core/data/tests/test_generate_char/output.py | 1 - core/data/tests/test_i54_u53_type/output.py | 1 - core/data/tests/test_optional_type_alias/output.py | 3 --- core/data/tests/test_serde_default_struct/output.py | 1 - core/data/tests/test_serde_url/output.py | 1 - core/data/tests/test_serialized_as/output.py | 2 -- core/data/tests/test_serialized_as_tuple/output.py | 1 - core/data/tests/test_type_alias/output.py | 2 -- .../tests/use_correct_decoded_variable_name/output.py | 1 - core/data/tests/use_correct_integer_types/output.py | 1 - core/src/language/python.rs | 4 ++-- 37 files changed, 2 insertions(+), 76 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index b701b65a..c1c20836 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -13,7 +13,6 @@ class AnonymousStructWithRenameListInner(BaseModel): """ list: List[str] - class AnonymousStructWithRenameLongFieldNamesInner(BaseModel): """ Generated type representing the anonymous struct variant `LongFieldNames` of the `AnonymousStructWithRename` Rust enum @@ -24,7 +23,6 @@ class AnonymousStructWithRenameLongFieldNamesInner(BaseModel): and_: bool = Field(alias="and") but_one_more: List[str] - class AnonymousStructWithRenameKebabCaseInner(BaseModel): """ Generated type representing the anonymous struct variant `KebabCase` of the `AnonymousStructWithRename` Rust enum @@ -35,7 +33,6 @@ class AnonymousStructWithRenameKebabCaseInner(BaseModel): camel_case_string_field: str = Field(alias="camelCaseStringField") something_else: bool = Field(alias="something-else") - class AnonymousStructWithRenameList(BaseModel): AnonymousStructWithRenameTypes: Literal["list"] = "list" content: AnonymousStructWithRenameListInner diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index 1bf22790..ba3bc3ce 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -10,7 +10,6 @@ class ItemDetailsFieldValue(BaseModel): hello: str - class AdvancedColorsString(BaseModel): AdvancedColorsTypes: Literal["String"] = "String" c: str diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 37f4db6d..7550198d 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -12,7 +12,6 @@ class ItemDetailsFieldValue(BaseModel): Struct comment """ pass - """ This is a case comment """ diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 0ea777c9..6083b192 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -16,7 +16,6 @@ class AutofilledByUsInner(BaseModel): The UUID for the fill """ - class AutofilledBySomethingElseInner(BaseModel): """ Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum @@ -26,7 +25,6 @@ class AutofilledBySomethingElseInner(BaseModel): The UUID for the fill """ - """ This field was autofilled by us """ diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index ae8a2c45..bc343c23 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -9,7 +9,6 @@ class AddressDetails(BaseModel): pass - class AddressFixedAddress(BaseModel): AddressTypes: Literal["FixedAddress"] = "FixedAddress" content: AddressDetails diff --git a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py index e179dc69..0703f341 100644 --- a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py +++ b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py @@ -9,7 +9,6 @@ class Location(BaseModel): pass - class Person(BaseModel): """ This is a comment. @@ -23,4 +22,3 @@ class Person(BaseModel): emails: List[str] location: Location - diff --git a/core/data/tests/can_generate_slice_of_user_type/output.py b/core/data/tests/can_generate_slice_of_user_type/output.py index 620a6bac..5a523f4a 100644 --- a/core/data/tests/can_generate_slice_of_user_type/output.py +++ b/core/data/tests/can_generate_slice_of_user_type/output.py @@ -10,4 +10,3 @@ class Video(BaseModel): tags: List[Tag] - diff --git a/core/data/tests/can_generate_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_struct_with_skipped_fields/output.py index ddfbf28f..accea552 100644 --- a/core/data/tests/can_generate_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_struct_with_skipped_fields/output.py @@ -10,4 +10,3 @@ class MyStruct(BaseModel): a: int c: int - diff --git a/core/data/tests/can_generate_unit_structs/output.py b/core/data/tests/can_generate_unit_structs/output.py index a77d85ff..c0b84425 100644 --- a/core/data/tests/can_generate_unit_structs/output.py +++ b/core/data/tests/can_generate_unit_structs/output.py @@ -8,4 +8,3 @@ class UnitStruct(BaseModel): pass - diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 069a7e34..d5e55c0e 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -16,7 +16,6 @@ class AutofilledByUsInner(BaseModel): The UUID for the fill """ - class AutofilledBySomethingElseInner(BaseModel): """ Generated type representing the anonymous struct variant `SomethingElse` of the `AutofilledBy` Rust enum @@ -30,7 +29,6 @@ class AutofilledBySomethingElseInner(BaseModel): Some other thing """ - """ This field was autofilled by us """ @@ -55,7 +53,6 @@ class EnumWithManyVariantsAnonVariantInner(BaseModel): """ uuid: str - class EnumWithManyVariantsAnotherAnonVariantInner(BaseModel): """ Generated type representing the anonymous struct variant `AnotherAnonVariant` of the `EnumWithManyVariants` Rust enum @@ -63,7 +60,6 @@ class EnumWithManyVariantsAnotherAnonVariantInner(BaseModel): uuid: str thing: int - class EnumWithManyVariantsUnitVariant(BaseModel): EnumWithManyVariantsTypes: Literal["UnitVariant"] = "UnitVariant" diff --git a/core/data/tests/can_handle_serde_rename/output.py b/core/data/tests/can_handle_serde_rename/output.py index da438dc7..5d08609f 100644 --- a/core/data/tests/can_handle_serde_rename/output.py +++ b/core/data/tests/can_handle_serde_rename/output.py @@ -9,7 +9,6 @@ class OtherType(BaseModel): pass - class Person(BaseModel): """ This is a comment. @@ -23,4 +22,3 @@ class Person(BaseModel): non_standard_data_type: OtherType = Field(alias="nonStandardDataType") non_standard_data_type_in_array: Optional[List[OtherType]] = Field(alias="nonStandardDataTypeInArray", default=None) - diff --git a/core/data/tests/can_handle_serde_rename_all/output.py b/core/data/tests/can_handle_serde_rename_all/output.py index fc8f4cec..b39990ca 100644 --- a/core/data/tests/can_handle_serde_rename_all/output.py +++ b/core/data/tests/can_handle_serde_rename_all/output.py @@ -19,7 +19,6 @@ class Person(BaseModel): extra_special_field_1: int = Field(alias="extraSpecialField1") extra_special_field_2: Optional[List[str]] = Field(alias="extraSpecialField2", default=None) - class Person2(BaseModel): """ This is a Person2 struct with UPPERCASE rename @@ -30,4 +29,3 @@ class Person2(BaseModel): last_name: str = Field(alias="LAST_NAME") age: int = Field(alias="AGE") - diff --git a/core/data/tests/can_handle_serde_rename_on_top_level/output.py b/core/data/tests/can_handle_serde_rename_on_top_level/output.py index 47ae8158..1d27ede4 100644 --- a/core/data/tests/can_handle_serde_rename_on_top_level/output.py +++ b/core/data/tests/can_handle_serde_rename_on_top_level/output.py @@ -9,7 +9,6 @@ class OtherType(BaseModel): pass - class PersonTwo(BaseModel): """ This is a comment. @@ -23,4 +22,3 @@ class PersonTwo(BaseModel): non_standard_data_type: OtherType = Field(alias="nonStandardDataType") non_standard_data_type_in_array: Optional[List[OtherType]] = Field(alias="nonStandardDataTypeInArray", default=None) - diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index a6ffd367..49e08bda 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -15,7 +15,6 @@ class StructHasVoidType(BaseModel): this_is_a_unit: None = Field(alias="thisIsAUnit") - class EnumHasVoidTypeHasAUnit(BaseModel): EnumHasVoidTypeTypes: Literal["hasAUnit"] = "hasAUnit" content: None diff --git a/core/data/tests/can_recognize_types_inside_modules/output.py b/core/data/tests/can_recognize_types_inside_modules/output.py index 60f869e8..92e7bec8 100644 --- a/core/data/tests/can_recognize_types_inside_modules/output.py +++ b/core/data/tests/can_recognize_types_inside_modules/output.py @@ -9,16 +9,12 @@ class A(BaseModel): field: int - class AB(BaseModel): field: int - class ABC(BaseModel): field: int - class OutsideOfModules(BaseModel): field: int - diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 37cc7e57..3447fb38 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -13,22 +13,16 @@ class DefinedTwice(BaseModel): field_1: str = Field(alias="field1") - class Excluded(BaseModel): pass - class ManyStruct(BaseModel): pass - class MultipleTargets(BaseModel): pass - class NestedNotTarget1(BaseModel): pass - class OtherExcluded(BaseModel): pass - class SomeEnum(str, Enum): pass class TestEnumVariant7Inner(BaseModel): @@ -39,7 +33,6 @@ class TestEnumVariant7Inner(BaseModel): field_1: str = Field(alias="field1") - class TestEnumVariant9Inner(BaseModel): """ Generated type representing the anonymous struct variant `Variant9` of the `TestEnum` Rust enum @@ -48,7 +41,6 @@ class TestEnumVariant9Inner(BaseModel): field_2: str = Field(alias="field2") - class TestEnumVariant1(BaseModel): TestEnumTypes: Literal["Variant1"] = "Variant1" diff --git a/core/data/tests/generate_types/output.py b/core/data/tests/generate_types/output.py index 2f12a676..48ea1a2f 100644 --- a/core/data/tests/generate_types/output.py +++ b/core/data/tests/generate_types/output.py @@ -9,7 +9,6 @@ class CustomType(BaseModel): pass - class Types(BaseModel): model_config = ConfigDict(populate_by_name=True) @@ -24,4 +23,3 @@ class Types(BaseModel): optional_dictionary: Optional[Dict[str, int]] = Field(default=None) custom_type: CustomType - diff --git a/core/data/tests/generates_empty_structs_and_initializers/output.py b/core/data/tests/generates_empty_structs_and_initializers/output.py index 8e9c1c6d..aea6ab33 100644 --- a/core/data/tests/generates_empty_structs_and_initializers/output.py +++ b/core/data/tests/generates_empty_structs_and_initializers/output.py @@ -8,4 +8,3 @@ class MyEmptyStruct(BaseModel): pass - diff --git a/core/data/tests/kebab_case_rename/output.py b/core/data/tests/kebab_case_rename/output.py index 3195e7ee..d6ccdec8 100644 --- a/core/data/tests/kebab_case_rename/output.py +++ b/core/data/tests/kebab_case_rename/output.py @@ -17,4 +17,3 @@ class Things(BaseModel): some_label: Optional[str] = Field(alias="label", default=None) label_left: Optional[str] = Field(alias="label-left", default=None) - diff --git a/core/data/tests/orders_types/output.py b/core/data/tests/orders_types/output.py index ec85655d..ba841ec2 100644 --- a/core/data/tests/orders_types/output.py +++ b/core/data/tests/orders_types/output.py @@ -10,29 +10,24 @@ class A(BaseModel): field: int - class B(BaseModel): model_config = ConfigDict(populate_by_name=True) depends_on: A = Field(alias="dependsOn") - class C(BaseModel): model_config = ConfigDict(populate_by_name=True) depends_on: B = Field(alias="dependsOn") - class E(BaseModel): model_config = ConfigDict(populate_by_name=True) depends_on: D = Field(alias="dependsOn") - class D(BaseModel): model_config = ConfigDict(populate_by_name=True) depends_on: C = Field(alias="dependsOn") also_depends_on: Optional[E] = Field(alias="alsoDependsOn", default=None) - diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index b04799f9..f7cc6ff3 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -13,14 +13,12 @@ class MoreOptionsExactlyInner(BaseModel): """ config: str - class MoreOptionsBuiltInner(BaseModel): """ Generated type representing the anonymous struct variant `Built` of the `MoreOptions` Rust enum """ top: MoreOptions - class MoreOptionsNews(BaseModel): MoreOptionsTypes: Literal["news"] = "news" content: bool diff --git a/core/data/tests/resolves_qualified_type/output.py b/core/data/tests/resolves_qualified_type/output.py index 026765ac..c17b6aac 100644 --- a/core/data/tests/resolves_qualified_type/output.py +++ b/core/data/tests/resolves_qualified_type/output.py @@ -15,4 +15,3 @@ class QualifiedTypes(BaseModel): qualified_optional: Optional[str] = Field(default=None) qualfied_optional_hashmap_vec: Optional[Dict[str, List[str]]] = Field(default=None) - diff --git a/core/data/tests/serialize_field_as/output.py b/core/data/tests/serialize_field_as/output.py index effbe952..fc6aeae0 100644 --- a/core/data/tests/serialize_field_as/output.py +++ b/core/data/tests/serialize_field_as/output.py @@ -12,4 +12,3 @@ class EditItemViewModelSaveRequest(BaseModel): values: List[EditItemSaveValue] fill_action: Optional[AutoFillItemActionRequest] = Field(default=None) - diff --git a/core/data/tests/serialize_type_alias/output.py b/core/data/tests/serialize_type_alias/output.py index 63258c3c..be693ae3 100644 --- a/core/data/tests/serialize_type_alias/output.py +++ b/core/data/tests/serialize_type_alias/output.py @@ -8,16 +8,12 @@ Uuid = str - AccountUuid = Uuid - """ Unique identifier for an Account """ AlsoString = str - ItemUuid = str - diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 6b290653..2d13981b 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -15,7 +15,6 @@ class ArcyColors(BaseModel): blue: str green: List[str] - class CellyColors(BaseModel): """ This is a comment. @@ -23,21 +22,18 @@ class CellyColors(BaseModel): red: str blue: List[str] - class CowyColors(BaseModel): """ This is a comment. """ lifetime: str - class LockyColors(BaseModel): """ This is a comment. """ red: str - class MutexyColors(BaseModel): """ This is a comment. @@ -45,7 +41,6 @@ class MutexyColors(BaseModel): blue: List[str] green: str - class RcyColors(BaseModel): """ This is a comment. @@ -54,7 +49,6 @@ class RcyColors(BaseModel): blue: List[str] green: str - class BoxyColorsRed(BaseModel): BoxyColorsTypes: Literal["Red"] = "Red" diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index f9362543..2fa88be2 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -9,7 +9,6 @@ class ItemDetailsFieldValue(BaseModel): pass - class AdvancedColorsString(BaseModel): AdvancedColorsTypes: Literal["string"] = "string" content: str diff --git a/core/data/tests/test_generate_char/output.py b/core/data/tests/test_generate_char/output.py index 8c6e84c0..1fe4da98 100644 --- a/core/data/tests/test_generate_char/output.py +++ b/core/data/tests/test_generate_char/output.py @@ -9,4 +9,3 @@ class MyType(BaseModel): field: str - diff --git a/core/data/tests/test_i54_u53_type/output.py b/core/data/tests/test_i54_u53_type/output.py index af49359c..a7d123af 100644 --- a/core/data/tests/test_i54_u53_type/output.py +++ b/core/data/tests/test_i54_u53_type/output.py @@ -10,4 +10,3 @@ class Foo(BaseModel): a: int b: int - diff --git a/core/data/tests/test_optional_type_alias/output.py b/core/data/tests/test_optional_type_alias/output.py index a3169eac..31ac4162 100644 --- a/core/data/tests/test_optional_type_alias/output.py +++ b/core/data/tests/test_optional_type_alias/output.py @@ -9,12 +9,9 @@ OptionalU16 = Optional[int] - OptionalU32 = Optional[int] - class FooBar(BaseModel): foo: OptionalU32 bar: OptionalU16 - diff --git a/core/data/tests/test_serde_default_struct/output.py b/core/data/tests/test_serde_default_struct/output.py index 35438bac..8c6849b3 100644 --- a/core/data/tests/test_serde_default_struct/output.py +++ b/core/data/tests/test_serde_default_struct/output.py @@ -10,4 +10,3 @@ class Foo(BaseModel): bar: Optional[bool] = Field(default=None) - diff --git a/core/data/tests/test_serde_url/output.py b/core/data/tests/test_serde_url/output.py index 602e52cd..e182c123 100644 --- a/core/data/tests/test_serde_url/output.py +++ b/core/data/tests/test_serde_url/output.py @@ -10,4 +10,3 @@ class Foo(BaseModel): url: AnyUrl - diff --git a/core/data/tests/test_serialized_as/output.py b/core/data/tests/test_serialized_as/output.py index 28a0cc6b..639fb48b 100644 --- a/core/data/tests/test_serialized_as/output.py +++ b/core/data/tests/test_serialized_as/output.py @@ -8,10 +8,8 @@ ItemId = str - Options = str - """ Options that you could pick """ diff --git a/core/data/tests/test_serialized_as_tuple/output.py b/core/data/tests/test_serialized_as_tuple/output.py index a6208fe4..9a1e8bea 100644 --- a/core/data/tests/test_serialized_as_tuple/output.py +++ b/core/data/tests/test_serialized_as_tuple/output.py @@ -8,4 +8,3 @@ ItemId = str - diff --git a/core/data/tests/test_type_alias/output.py b/core/data/tests/test_type_alias/output.py index fce5ad2b..6d48e621 100644 --- a/core/data/tests/test_type_alias/output.py +++ b/core/data/tests/test_type_alias/output.py @@ -8,8 +8,6 @@ Bar = str - class Foo(BaseModel): bar: Bar - diff --git a/core/data/tests/use_correct_decoded_variable_name/output.py b/core/data/tests/use_correct_decoded_variable_name/output.py index 8e9c1c6d..aea6ab33 100644 --- a/core/data/tests/use_correct_decoded_variable_name/output.py +++ b/core/data/tests/use_correct_decoded_variable_name/output.py @@ -8,4 +8,3 @@ class MyEmptyStruct(BaseModel): pass - diff --git a/core/data/tests/use_correct_integer_types/output.py b/core/data/tests/use_correct_integer_types/output.py index 821fc38c..caebe2b1 100644 --- a/core/data/tests/use_correct_integer_types/output.py +++ b/core/data/tests/use_correct_integer_types/output.py @@ -17,4 +17,3 @@ class Foo(BaseModel): f: int g: int - diff --git a/core/src/language/python.rs b/core/src/language/python.rs index d00ca7c2..43bdcfbd 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -225,7 +225,7 @@ impl Language for Python { writeln!( w, - "{}{} = {}\n\n", + "{}{} = {}\n", ty.id.renamed, (!ty.generic_types.is_empty()) .then(|| format!("[{}]", ty.generic_types.join(", "))) @@ -265,7 +265,7 @@ impl Language for Python { if rs.fields.is_empty() { write!(w, " pass")? } - write!(w, "\n\n")?; + writeln!(w)?; self.add_import("pydantic".to_string(), "BaseModel".to_string()); Ok(()) } From 4a0bb0e1911b9e3934c3e56f23dffc9d9454bde9 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 12:32:52 -0500 Subject: [PATCH 50/57] fix to confrom python's wonderful doc conventions --- .../can_generate_algebraic_enum/output.py | 28 +++++++++---------- .../output.py | 16 +++++------ .../can_handle_anonymous_struct/output.py | 20 ++++++------- .../data/tests/can_handle_unit_type/output.py | 4 +-- .../serialize_anonymous_field_as/output.py | 6 ++-- core/data/tests/smart_pointers/output.py | 4 +-- core/src/language/python.rs | 13 +++++---- 7 files changed, 40 insertions(+), 51 deletions(-) diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 7550198d..220d30ce 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -12,10 +12,10 @@ class ItemDetailsFieldValue(BaseModel): Struct comment """ pass -""" -This is a case comment -""" class AdvancedColorsString(BaseModel): + """ + This is a case comment + """ AdvancedColorsTypes: Literal["String"] = "String" content: str @@ -31,21 +31,19 @@ class AdvancedColorsNumberArray(BaseModel): AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" content: List[int] -""" -Comment on the last element -""" class AdvancedColorsReallyCoolType(BaseModel): + """ + Comment on the last element + """ AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" content: ItemDetailsFieldValue -""" -Enum comment -""" +# Enum comment AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] -""" -This is a case comment -""" class AdvancedColors2String(BaseModel): + """ + This is a case comment + """ AdvancedColors2Types: Literal["string"] = "string" content: str @@ -57,10 +55,10 @@ class AdvancedColors2NumberArray(BaseModel): AdvancedColors2Types: Literal["number-array"] = "number-array" content: List[int] -""" -Comment on the last element -""" class AdvancedColors2ReallyCoolType(BaseModel): + """ + Comment on the last element + """ AdvancedColors2Types: Literal["really-cool-type"] = "really-cool-type" content: ItemDetailsFieldValue diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index 6083b192..e39230d5 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -25,21 +25,19 @@ class AutofilledBySomethingElseInner(BaseModel): The UUID for the fill """ -""" -This field was autofilled by us -""" class AutofilledByUs(BaseModel): + """ + This field was autofilled by us + """ AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner -""" -Something else autofilled this field -""" class AutofilledBySomethingElse(BaseModel): + """ + Something else autofilled this field + """ AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner -""" -Enum keeping track of who autofilled a field -""" +# Enum keeping track of who autofilled a field AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index d5e55c0e..1652e45d 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -29,23 +29,21 @@ class AutofilledBySomethingElseInner(BaseModel): Some other thing """ -""" -This field was autofilled by us -""" class AutofilledByUs(BaseModel): + """ + This field was autofilled by us + """ AutofilledByTypes: Literal["Us"] = "Us" content: AutofilledByUsInner -""" -Something else autofilled this field -""" class AutofilledBySomethingElse(BaseModel): + """ + Something else autofilled this field + """ AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" content: AutofilledBySomethingElseInner -""" -Enum keeping track of who autofilled a field -""" +# Enum keeping track of who autofilled a field AutofilledBy = Union[AutofilledByUs, AutofilledBySomethingElse] class EnumWithManyVariantsAnonVariantInner(BaseModel): """ @@ -82,7 +80,5 @@ class EnumWithManyVariantsAnotherAnonVariant(BaseModel): EnumWithManyVariantsTypes: Literal["AnotherAnonVariant"] = "AnotherAnonVariant" content: EnumWithManyVariantsAnotherAnonVariantInner -""" -This is a comment (yareek sameek wuz here) -""" +# This is a comment (yareek sameek wuz here) EnumWithManyVariants = Union[EnumWithManyVariantsUnitVariant, EnumWithManyVariantsTupleVariantString, EnumWithManyVariantsAnonVariant, EnumWithManyVariantsTupleVariantInt, EnumWithManyVariantsAnotherUnitVariant, EnumWithManyVariantsAnotherAnonVariant] diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index 49e08bda..b97ef06c 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -19,7 +19,5 @@ class EnumHasVoidTypeHasAUnit(BaseModel): EnumHasVoidTypeTypes: Literal["hasAUnit"] = "hasAUnit" content: None -""" -This enum has a variant associated with unit data -""" +# This enum has a variant associated with unit data EnumHasVoidType = EnumHasVoidTypeHasAUnit diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 87c06a9d..f1fd7147 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -7,10 +7,10 @@ from typing import Literal, Union -""" -The associated String contains some opaque context -""" class SomeEnumContext(BaseModel): + """ + The associated String contains some opaque context + """ SomeEnumTypes: Literal["Context"] = "Context" content: str diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index 2d13981b..b23207bf 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -59,7 +59,5 @@ class BoxyColorsGreen(BaseModel): BoxyColorsTypes: Literal["Green"] = "Green" content: str -""" -This is a comment. -""" +# This is a comment. BoxyColors = Union[BoxyColorsRed, BoxyColorsBlue, BoxyColorsGreen] diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 43bdcfbd..7eaed549 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -487,10 +487,13 @@ impl Python { content_key: &str, content_type: Option<&str>, content_value: Option<&str>, + comments: &[String], w: &mut dyn Write, ) -> std::io::Result<()> { self.add_import("typing".to_string(), "Literal".to_string()); writeln!(w, "class {class_name}(BaseModel):")?; + self.write_comments(w, true, comments, 1)?; + writeln!(w, " {tag_key}: Literal[{tag_value}] = {tag_value}",)?; if content_type.is_none() && content_value.is_none() { return Ok(()); @@ -547,7 +550,6 @@ impl Python { union_members.push(variant_class_name.clone()); match variant { RustEnumVariant::Unit(variant_shared) => { - self.write_comments(w, true, &variant_shared.comments, 0)?; self.write_variant_class( &variant_class_name, &enum_type_class_name, @@ -555,6 +557,7 @@ impl Python { content_key, None, None, + &variant_shared.comments, w, )?; writeln!(w)?; @@ -563,8 +566,6 @@ impl Python { ty, shared: variant_shared, } => { - self.write_comments(w, true, &variant_shared.comments, 0)?; - let tuple_name = self .format_type(ty, shared.generic_types.as_slice()) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; @@ -575,6 +576,7 @@ impl Python { content_key, Some(&tuple_name), None, + &variant_shared.comments, w, )?; writeln!(w)?; @@ -583,8 +585,6 @@ impl Python { shared: variant_shared, .. } => { - self.write_comments(w, true, &variant_shared.comments, 0)?; - // writing is taken care of by write_types_for_anonymous_structs in write_enum let variant_class_inner_name = make_struct_name(&variant_shared.id.original); @@ -595,13 +595,14 @@ impl Python { content_key, Some(&variant_class_inner_name), None, + &variant_shared.comments, w, )?; writeln!(w)?; } } } - self.write_comments(w, true, &shared.comments, 0)?; + self.write_comments(w, false, &shared.comments, 0)?; if union_members.len() == 1 { writeln!(w, "{enum_name} = {}", union_members[0])?; } else { From a49914a0a8ae9f9445044a64fb92b7b7d4981a59 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Fri, 15 Nov 2024 14:05:43 -0500 Subject: [PATCH 51/57] use enum for type --- .../anonymous_struct_with_rename/output.py | 12 ++++-- .../can_apply_prefix_correctly/output.py | 21 ++++++++--- .../can_generate_algebraic_enum/output.py | 32 +++++++++++----- .../output.py | 9 ++++- .../output.py | 9 ++++- .../output.py | 9 ++++- .../can_handle_anonymous_struct/output.py | 29 +++++++++++---- .../data/tests/can_handle_unit_type/output.py | 6 ++- .../tests/excluded_by_target_os/output.py | 17 ++++++--- .../tests/recursive_enum_decorator/output.py | 23 +++++++++--- .../serialize_anonymous_field_as/output.py | 9 ++++- core/data/tests/smart_pointers/output.py | 12 ++++-- .../output.py | 15 ++++++-- core/src/language/python.rs | 37 ++++++++++++++----- 14 files changed, 177 insertions(+), 63 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index c1c20836..da9bfbad 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel, ConfigDict, Field from typing import List, Literal, Union @@ -33,16 +34,21 @@ class AnonymousStructWithRenameKebabCaseInner(BaseModel): camel_case_string_field: str = Field(alias="camelCaseStringField") something_else: bool = Field(alias="something-else") +class AnonymousStructWithRenameTypes(str, Enum): + LIST = "list" + LONG_FIELD_NAMES = "longFieldNames" + KEBAB_CASE = "kebabCase" + class AnonymousStructWithRenameList(BaseModel): - AnonymousStructWithRenameTypes: Literal["list"] = "list" + type: Literal[AnonymousStructWithRenameTypes.LIST] = AnonymousStructWithRenameTypes.LIST content: AnonymousStructWithRenameListInner class AnonymousStructWithRenameLongFieldNames(BaseModel): - AnonymousStructWithRenameTypes: Literal["longFieldNames"] = "longFieldNames" + type: Literal[AnonymousStructWithRenameTypes.LONG_FIELD_NAMES] = AnonymousStructWithRenameTypes.LONG_FIELD_NAMES content: AnonymousStructWithRenameLongFieldNamesInner class AnonymousStructWithRenameKebabCase(BaseModel): - AnonymousStructWithRenameTypes: Literal["kebabCase"] = "kebabCase" + type: Literal[AnonymousStructWithRenameTypes.KEBAB_CASE] = AnonymousStructWithRenameTypes.KEBAB_CASE content: AnonymousStructWithRenameKebabCaseInner AnonymousStructWithRename = Union[AnonymousStructWithRenameList, AnonymousStructWithRenameLongFieldNames, AnonymousStructWithRenameKebabCase] diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index ba3bc3ce..b5f7c805 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Dict, List, Literal, Union @@ -10,28 +11,36 @@ class ItemDetailsFieldValue(BaseModel): hello: str +class AdvancedColorsTypes(str, Enum): + STRING = "String" + NUMBER = "Number" + NUMBER_ARRAY = "NumberArray" + REALLY_COOL_TYPE = "ReallyCoolType" + ARRAY_REALLY_COOL_TYPE = "ArrayReallyCoolType" + DICTIONARY_REALLY_COOL_TYPE = "DictionaryReallyCoolType" + class AdvancedColorsString(BaseModel): - AdvancedColorsTypes: Literal["String"] = "String" + t: Literal[AdvancedColorsTypes.STRING] = AdvancedColorsTypes.STRING c: str class AdvancedColorsNumber(BaseModel): - AdvancedColorsTypes: Literal["Number"] = "Number" + t: Literal[AdvancedColorsTypes.NUMBER] = AdvancedColorsTypes.NUMBER c: int class AdvancedColorsNumberArray(BaseModel): - AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" + t: Literal[AdvancedColorsTypes.NUMBER_ARRAY] = AdvancedColorsTypes.NUMBER_ARRAY c: List[int] class AdvancedColorsReallyCoolType(BaseModel): - AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" + t: Literal[AdvancedColorsTypes.REALLY_COOL_TYPE] = AdvancedColorsTypes.REALLY_COOL_TYPE c: ItemDetailsFieldValue class AdvancedColorsArrayReallyCoolType(BaseModel): - AdvancedColorsTypes: Literal["ArrayReallyCoolType"] = "ArrayReallyCoolType" + t: Literal[AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE] = AdvancedColorsTypes.ARRAY_REALLY_COOL_TYPE c: List[ItemDetailsFieldValue] class AdvancedColorsDictionaryReallyCoolType(BaseModel): - AdvancedColorsTypes: Literal["DictionaryReallyCoolType"] = "DictionaryReallyCoolType" + t: Literal[AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE] = AdvancedColorsTypes.DICTIONARY_REALLY_COOL_TYPE c: Dict[str, ItemDetailsFieldValue] AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType, AdvancedColorsArrayReallyCoolType, AdvancedColorsDictionaryReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index 220d30ce..f79aad55 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union @@ -12,54 +13,67 @@ class ItemDetailsFieldValue(BaseModel): Struct comment """ pass +class AdvancedColorsTypes(str, Enum): + STRING = "String" + NUMBER = "Number" + UNSIGNED_NUMBER = "UnsignedNumber" + NUMBER_ARRAY = "NumberArray" + REALLY_COOL_TYPE = "ReallyCoolType" + class AdvancedColorsString(BaseModel): """ This is a case comment """ - AdvancedColorsTypes: Literal["String"] = "String" + type: Literal[AdvancedColorsTypes.STRING] = AdvancedColorsTypes.STRING content: str class AdvancedColorsNumber(BaseModel): - AdvancedColorsTypes: Literal["Number"] = "Number" + type: Literal[AdvancedColorsTypes.NUMBER] = AdvancedColorsTypes.NUMBER content: int class AdvancedColorsUnsignedNumber(BaseModel): - AdvancedColorsTypes: Literal["UnsignedNumber"] = "UnsignedNumber" + type: Literal[AdvancedColorsTypes.UNSIGNED_NUMBER] = AdvancedColorsTypes.UNSIGNED_NUMBER content: int class AdvancedColorsNumberArray(BaseModel): - AdvancedColorsTypes: Literal["NumberArray"] = "NumberArray" + type: Literal[AdvancedColorsTypes.NUMBER_ARRAY] = AdvancedColorsTypes.NUMBER_ARRAY content: List[int] class AdvancedColorsReallyCoolType(BaseModel): """ Comment on the last element """ - AdvancedColorsTypes: Literal["ReallyCoolType"] = "ReallyCoolType" + type: Literal[AdvancedColorsTypes.REALLY_COOL_TYPE] = AdvancedColorsTypes.REALLY_COOL_TYPE content: ItemDetailsFieldValue # Enum comment AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsUnsignedNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] +class AdvancedColors2Types(str, Enum): + STRING = "string" + NUMBER = "number" + NUMBER_ARRAY = "number-array" + REALLY_COOL_TYPE = "really-cool-type" + class AdvancedColors2String(BaseModel): """ This is a case comment """ - AdvancedColors2Types: Literal["string"] = "string" + type: Literal[AdvancedColors2Types.STRING] = AdvancedColors2Types.STRING content: str class AdvancedColors2Number(BaseModel): - AdvancedColors2Types: Literal["number"] = "number" + type: Literal[AdvancedColors2Types.NUMBER] = AdvancedColors2Types.NUMBER content: int class AdvancedColors2NumberArray(BaseModel): - AdvancedColors2Types: Literal["number-array"] = "number-array" + type: Literal[AdvancedColors2Types.NUMBER_ARRAY] = AdvancedColors2Types.NUMBER_ARRAY content: List[int] class AdvancedColors2ReallyCoolType(BaseModel): """ Comment on the last element """ - AdvancedColors2Types: Literal["really-cool-type"] = "really-cool-type" + type: Literal[AdvancedColors2Types.REALLY_COOL_TYPE] = AdvancedColors2Types.REALLY_COOL_TYPE content: ItemDetailsFieldValue AdvancedColors2 = Union[AdvancedColors2String, AdvancedColors2Number, AdvancedColors2NumberArray, AdvancedColors2ReallyCoolType] diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index ba4c3cc7..f023d26a 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -3,15 +3,20 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union +class SomeEnumTypes(str, Enum): + A = "A" + C = "C" + class SomeEnumA(BaseModel): - SomeEnumTypes: Literal["A"] = "A" + type: Literal[SomeEnumTypes.A] = SomeEnumTypes.A class SomeEnumC(BaseModel): - SomeEnumTypes: Literal["C"] = "C" + type: Literal[SomeEnumTypes.C] = SomeEnumTypes.C content: int SomeEnum = Union[SomeEnumA, SomeEnumC] diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index e39230d5..bc0cd8a0 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -25,18 +26,22 @@ class AutofilledBySomethingElseInner(BaseModel): The UUID for the fill """ +class AutofilledByTypes(str, Enum): + US = "Us" + SOMETHING_ELSE = "SomethingElse" + class AutofilledByUs(BaseModel): """ This field was autofilled by us """ - AutofilledByTypes: Literal["Us"] = "Us" + type: Literal[AutofilledByTypes.US] = AutofilledByTypes.US content: AutofilledByUsInner class AutofilledBySomethingElse(BaseModel): """ Something else autofilled this field """ - AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" + type: Literal[AutofilledByTypes.SOMETHING_ELSE] = AutofilledByTypes.SOMETHING_ELSE content: AutofilledBySomethingElseInner # Enum keeping track of who autofilled a field diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index bc343c23..160b7be2 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -3,17 +3,22 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union class AddressDetails(BaseModel): pass +class AddressTypes(str, Enum): + FIXED_ADDRESS = "FixedAddress" + NO_FIXED_ADDRESS = "NoFixedAddress" + class AddressFixedAddress(BaseModel): - AddressTypes: Literal["FixedAddress"] = "FixedAddress" + type: Literal[AddressTypes.FIXED_ADDRESS] = AddressTypes.FIXED_ADDRESS content: AddressDetails class AddressNoFixedAddress(BaseModel): - AddressTypes: Literal["NoFixedAddress"] = "NoFixedAddress" + type: Literal[AddressTypes.NO_FIXED_ADDRESS] = AddressTypes.NO_FIXED_ADDRESS Address = Union[AddressFixedAddress, AddressNoFixedAddress] diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index 1652e45d..e988f05d 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -29,18 +30,22 @@ class AutofilledBySomethingElseInner(BaseModel): Some other thing """ +class AutofilledByTypes(str, Enum): + US = "Us" + SOMETHING_ELSE = "SomethingElse" + class AutofilledByUs(BaseModel): """ This field was autofilled by us """ - AutofilledByTypes: Literal["Us"] = "Us" + type: Literal[AutofilledByTypes.US] = AutofilledByTypes.US content: AutofilledByUsInner class AutofilledBySomethingElse(BaseModel): """ Something else autofilled this field """ - AutofilledByTypes: Literal["SomethingElse"] = "SomethingElse" + type: Literal[AutofilledByTypes.SOMETHING_ELSE] = AutofilledByTypes.SOMETHING_ELSE content: AutofilledBySomethingElseInner # Enum keeping track of who autofilled a field @@ -58,26 +63,34 @@ class EnumWithManyVariantsAnotherAnonVariantInner(BaseModel): uuid: str thing: int +class EnumWithManyVariantsTypes(str, Enum): + UNIT_VARIANT = "UnitVariant" + TUPLE_VARIANT_STRING = "TupleVariantString" + ANON_VARIANT = "AnonVariant" + TUPLE_VARIANT_INT = "TupleVariantInt" + ANOTHER_UNIT_VARIANT = "AnotherUnitVariant" + ANOTHER_ANON_VARIANT = "AnotherAnonVariant" + class EnumWithManyVariantsUnitVariant(BaseModel): - EnumWithManyVariantsTypes: Literal["UnitVariant"] = "UnitVariant" + type: Literal[EnumWithManyVariantsTypes.UNIT_VARIANT] = EnumWithManyVariantsTypes.UNIT_VARIANT class EnumWithManyVariantsTupleVariantString(BaseModel): - EnumWithManyVariantsTypes: Literal["TupleVariantString"] = "TupleVariantString" + type: Literal[EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING] = EnumWithManyVariantsTypes.TUPLE_VARIANT_STRING content: str class EnumWithManyVariantsAnonVariant(BaseModel): - EnumWithManyVariantsTypes: Literal["AnonVariant"] = "AnonVariant" + type: Literal[EnumWithManyVariantsTypes.ANON_VARIANT] = EnumWithManyVariantsTypes.ANON_VARIANT content: EnumWithManyVariantsAnonVariantInner class EnumWithManyVariantsTupleVariantInt(BaseModel): - EnumWithManyVariantsTypes: Literal["TupleVariantInt"] = "TupleVariantInt" + type: Literal[EnumWithManyVariantsTypes.TUPLE_VARIANT_INT] = EnumWithManyVariantsTypes.TUPLE_VARIANT_INT content: int class EnumWithManyVariantsAnotherUnitVariant(BaseModel): - EnumWithManyVariantsTypes: Literal["AnotherUnitVariant"] = "AnotherUnitVariant" + type: Literal[EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT] = EnumWithManyVariantsTypes.ANOTHER_UNIT_VARIANT class EnumWithManyVariantsAnotherAnonVariant(BaseModel): - EnumWithManyVariantsTypes: Literal["AnotherAnonVariant"] = "AnotherAnonVariant" + type: Literal[EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT] = EnumWithManyVariantsTypes.ANOTHER_ANON_VARIANT content: EnumWithManyVariantsAnotherAnonVariantInner # This is a comment (yareek sameek wuz here) diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index b97ef06c..2d74dce9 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel, ConfigDict, Field from typing import Literal @@ -15,8 +16,11 @@ class StructHasVoidType(BaseModel): this_is_a_unit: None = Field(alias="thisIsAUnit") +class EnumHasVoidTypeTypes(str, Enum): + HAS_A_UNIT = "hasAUnit" + class EnumHasVoidTypeHasAUnit(BaseModel): - EnumHasVoidTypeTypes: Literal["hasAUnit"] = "hasAUnit" + type: Literal[EnumHasVoidTypeTypes.HAS_A_UNIT] = EnumHasVoidTypeTypes.HAS_A_UNIT content: None # This enum has a variant associated with unit data diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index 3447fb38..dd85551f 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -41,21 +41,28 @@ class TestEnumVariant9Inner(BaseModel): field_2: str = Field(alias="field2") +class TestEnumTypes(str, Enum): + VARIANT_1 = "Variant1" + VARIANT_5 = "Variant5" + VARIANT_7 = "Variant7" + VARIANT_8 = "Variant8" + VARIANT_9 = "Variant9" + class TestEnumVariant1(BaseModel): - TestEnumTypes: Literal["Variant1"] = "Variant1" + type: Literal[TestEnumTypes.VARIANT_1] = TestEnumTypes.VARIANT_1 class TestEnumVariant5(BaseModel): - TestEnumTypes: Literal["Variant5"] = "Variant5" + type: Literal[TestEnumTypes.VARIANT_5] = TestEnumTypes.VARIANT_5 class TestEnumVariant7(BaseModel): - TestEnumTypes: Literal["Variant7"] = "Variant7" + type: Literal[TestEnumTypes.VARIANT_7] = TestEnumTypes.VARIANT_7 content: TestEnumVariant7Inner class TestEnumVariant8(BaseModel): - TestEnumTypes: Literal["Variant8"] = "Variant8" + type: Literal[TestEnumTypes.VARIANT_8] = TestEnumTypes.VARIANT_8 class TestEnumVariant9(BaseModel): - TestEnumTypes: Literal["Variant9"] = "Variant9" + type: Literal[TestEnumTypes.VARIANT_9] = TestEnumTypes.VARIANT_9 content: TestEnumVariant9Inner TestEnum = Union[TestEnumVariant1, TestEnumVariant5, TestEnumVariant7, TestEnumVariant8, TestEnumVariant9] diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index f7cc6ff3..de4b2417 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union @@ -19,29 +20,39 @@ class MoreOptionsBuiltInner(BaseModel): """ top: MoreOptions +class MoreOptionsTypes(str, Enum): + NEWS = "news" + EXACTLY = "exactly" + BUILT = "built" + class MoreOptionsNews(BaseModel): - MoreOptionsTypes: Literal["news"] = "news" + type: Literal[MoreOptionsTypes.NEWS] = MoreOptionsTypes.NEWS content: bool class MoreOptionsExactly(BaseModel): - MoreOptionsTypes: Literal["exactly"] = "exactly" + type: Literal[MoreOptionsTypes.EXACTLY] = MoreOptionsTypes.EXACTLY content: MoreOptionsExactlyInner class MoreOptionsBuilt(BaseModel): - MoreOptionsTypes: Literal["built"] = "built" + type: Literal[MoreOptionsTypes.BUILT] = MoreOptionsTypes.BUILT content: MoreOptionsBuiltInner MoreOptions = Union[MoreOptionsNews, MoreOptionsExactly, MoreOptionsBuilt] +class OptionsTypes(str, Enum): + RED = "red" + BANANA = "banana" + VERMONT = "vermont" + class OptionsRed(BaseModel): - OptionsTypes: Literal["red"] = "red" + type: Literal[OptionsTypes.RED] = OptionsTypes.RED content: bool class OptionsBanana(BaseModel): - OptionsTypes: Literal["banana"] = "banana" + type: Literal[OptionsTypes.BANANA] = OptionsTypes.BANANA content: str class OptionsVermont(BaseModel): - OptionsTypes: Literal["vermont"] = "vermont" + type: Literal[OptionsTypes.VERMONT] = OptionsTypes.VERMONT content: Options Options = Union[OptionsRed, OptionsBanana, OptionsVermont] diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index f1fd7147..921258d5 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -3,19 +3,24 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import Literal, Union +class SomeEnumTypes(str, Enum): + CONTEXT = "Context" + OTHER = "Other" + class SomeEnumContext(BaseModel): """ The associated String contains some opaque context """ - SomeEnumTypes: Literal["Context"] = "Context" + type: Literal[SomeEnumTypes.CONTEXT] = SomeEnumTypes.CONTEXT content: str class SomeEnumOther(BaseModel): - SomeEnumTypes: Literal["Other"] = "Other" + type: Literal[SomeEnumTypes.OTHER] = SomeEnumTypes.OTHER content: int SomeEnum = Union[SomeEnumContext, SomeEnumOther] diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index b23207bf..f4fae9cf 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union @@ -49,14 +50,19 @@ class RcyColors(BaseModel): blue: List[str] green: str +class BoxyColorsTypes(str, Enum): + RED = "Red" + BLUE = "Blue" + GREEN = "Green" + class BoxyColorsRed(BaseModel): - BoxyColorsTypes: Literal["Red"] = "Red" + type: Literal[BoxyColorsTypes.RED] = BoxyColorsTypes.RED class BoxyColorsBlue(BaseModel): - BoxyColorsTypes: Literal["Blue"] = "Blue" + type: Literal[BoxyColorsTypes.BLUE] = BoxyColorsTypes.BLUE class BoxyColorsGreen(BaseModel): - BoxyColorsTypes: Literal["Green"] = "Green" + type: Literal[BoxyColorsTypes.GREEN] = BoxyColorsTypes.GREEN content: str # This is a comment. diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index 2fa88be2..dded6ce1 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -3,26 +3,33 @@ """ from __future__ import annotations +from enum import Enum from pydantic import BaseModel from typing import List, Literal, Union class ItemDetailsFieldValue(BaseModel): pass +class AdvancedColorsTypes(str, Enum): + STRING = "string" + NUMBER = "number" + NUMBER_ARRAY = "number-array" + REALLY_COOL_TYPE = "reallyCoolType" + class AdvancedColorsString(BaseModel): - AdvancedColorsTypes: Literal["string"] = "string" + type: Literal[AdvancedColorsTypes.STRING] = AdvancedColorsTypes.STRING content: str class AdvancedColorsNumber(BaseModel): - AdvancedColorsTypes: Literal["number"] = "number" + type: Literal[AdvancedColorsTypes.NUMBER] = AdvancedColorsTypes.NUMBER content: int class AdvancedColorsNumberArray(BaseModel): - AdvancedColorsTypes: Literal["number-array"] = "number-array" + type: Literal[AdvancedColorsTypes.NUMBER_ARRAY] = AdvancedColorsTypes.NUMBER_ARRAY content: List[int] class AdvancedColorsReallyCoolType(BaseModel): - AdvancedColorsTypes: Literal["reallyCoolType"] = "reallyCoolType" + type: Literal[AdvancedColorsTypes.REALLY_COOL_TYPE] = AdvancedColorsTypes.REALLY_COOL_TYPE content: ItemDetailsFieldValue AdvancedColors = Union[AdvancedColorsString, AdvancedColorsNumber, AdvancedColorsNumberArray, AdvancedColorsReallyCoolType] diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 7eaed549..058fc55f 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -529,7 +529,8 @@ impl Python { .cloned() .for_each(|v| self.add_type_var(v)); self.add_import("pydantic".to_string(), "BaseModel".to_string()); - let enum_type_class_name = format!("{}Types", shared.id.renamed); + // all the types and class names for the enum variants in tuple + // (type_name, class_name) let all_enum_variants_name = shared .variants .iter() @@ -540,11 +541,27 @@ impl Python { }) .map(|name| (name.to_case(Case::Snake).to_uppercase(), name)) .collect::>(); - // all the types and class names for the enum variants in tuple - // (type_name, class_name) + let enum_type_class_name = format!("{}Types", shared.id.renamed); + self.add_import("enum".to_string(), "Enum".to_string()); + // write "types" class: a union of all the enum variants + writeln!(w, "class {}(str, Enum):", enum_type_class_name)?; + writeln!( + w, + "{}", + all_enum_variants_name + .iter() + .map(|(type_key_name, type_string)| format!( + " {type_key_name} = \"{type_string}\"" + )) + .collect::>() + .join("\n") + )?; + writeln!(w)?; + let mut union_members = Vec::new(); // write each of the enum variant as a class: - for (variant, (.., type_value)) in shared.variants.iter().zip(all_enum_variants_name.iter()) + for (variant, (type_key_name, ..)) in + shared.variants.iter().zip(all_enum_variants_name.iter()) { let variant_class_name = format!("{enum_name}{}", &variant.shared().id.original); union_members.push(variant_class_name.clone()); @@ -552,8 +569,8 @@ impl Python { RustEnumVariant::Unit(variant_shared) => { self.write_variant_class( &variant_class_name, - &enum_type_class_name, - format!("\"{type_value}\"",).as_str(), + &tag_key, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, None, None, @@ -571,8 +588,8 @@ impl Python { .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; self.write_variant_class( &variant_class_name, - &enum_type_class_name, - format!("\"{type_value}\"",).as_str(), + &tag_key, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, Some(&tuple_name), None, @@ -590,8 +607,8 @@ impl Python { self.write_variant_class( &variant_class_name, - &enum_type_class_name, - format!("\"{type_value}\"",).as_str(), + &tag_key, + format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, Some(&variant_class_inner_name), None, From 3df97f9b51058963d1032ba86026d8edd7008580 Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Wed, 4 Dec 2024 12:04:50 -0500 Subject: [PATCH 52/57] write comments properly for unit enum --- core/data/tests/can_generate_bare_string_enum/output.py | 3 +++ core/data/tests/can_generate_simple_enum/output.py | 7 +++++++ core/data/tests/can_handle_quote_in_serde_rename/output.py | 3 +++ .../enum_is_properly_named_with_serde_overrides/output.py | 7 +++++++ .../tests/test_simple_enum_case_name_support/output.py | 3 +++ core/src/language/python.rs | 7 +++++-- 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index 3fcf3636..bc11925f 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -7,6 +7,9 @@ class Colors(str, Enum): + """ + This is a comment. + """ RED = "Red" BLUE = "Blue" GREEN = "Green" diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index 3fcf3636..716cd0d0 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -7,6 +7,13 @@ class Colors(str, Enum): + """ + This is a comment. + Continued lovingly here + """ RED = "Red" BLUE = "Blue" GREEN = "Green" + """ + Green is a cool color + """ diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index 3b96d390..081a3ec6 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -7,4 +7,7 @@ class Colors(str, Enum): + """ + This is a comment. + """ GREEN = "Green\"" diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index c25f2e3a..17694078 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -7,6 +7,13 @@ class Colors(str, Enum): + """ + This is a comment. + Continued lovingly here + """ RED = "red" BLUE = "blue" GREEN = "green-like" + """ + Green is a cool color + """ diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index ae314bd0..ec344a3e 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -7,6 +7,9 @@ class Colors(str, Enum): + """ + This is a comment. + """ RED = "red" BLUE = "blue-ish" GREEN = "Green" diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 058fc55f..15fccbbc 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -283,6 +283,8 @@ impl Language for Python { RustEnum::Unit(shared) => { self.add_import("enum".to_string(), "Enum".to_string()); writeln!(w, "class {}(str, Enum):", shared.id.renamed)?; + // let comment = shared.comments.join("\n"); + self.write_comments(w, true, &shared.comments, 1)?; if shared.variants.is_empty() { writeln!(w, " pass")?; } else { @@ -295,9 +297,10 @@ impl Language for Python { RustEnumVariant::Unit(v) => { v.id.renamed.replace("\"", "\\\"") } - _ => panic!(), + _ => unreachable!("Only unit variants are allowed here"), } - ) + )?; + self.write_comments(w, true, &v.shared().comments, 1) })? }; } From 7ac735761901753fc0e0c8638ccbb1f479f9d1bb Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Wed, 4 Dec 2024 18:47:15 +0100 Subject: [PATCH 53/57] Bring codebase up to date with upstream --- core/src/language/python.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 15fccbbc..ac883993 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -572,7 +572,7 @@ impl Python { RustEnumVariant::Unit(variant_shared) => { self.write_variant_class( &variant_class_name, - &tag_key, + tag_key, format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, None, @@ -591,7 +591,7 @@ impl Python { .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; self.write_variant_class( &variant_class_name, - &tag_key, + tag_key, format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, Some(&tuple_name), @@ -610,7 +610,7 @@ impl Python { self.write_variant_class( &variant_class_name, - &tag_key, + tag_key, format!("{enum_type_class_name}.{type_key_name}",).as_str(), content_key, Some(&variant_class_inner_name), @@ -689,6 +689,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: false, }, ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { id: "str".to_string(), @@ -712,6 +713,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: false, }, ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { id: "str".to_string(), @@ -737,6 +739,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: false, }, ty: RustType::Simple { id: "str".to_string(), @@ -760,6 +763,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: false, }, ty: RustType::Simple { id: "str".to_string(), From 8214fae7c578d3edce40489e59d75432f32b5afa Mon Sep 17 00:00:00 2001 From: Peter Jiang <2892084432j@gmail.com> Date: Wed, 4 Dec 2024 13:05:45 -0500 Subject: [PATCH 54/57] handle version header correctly --- .../tests/anonymous_struct_with_rename/output.py | 3 --- .../tests/can_apply_prefix_correctly/output.py | 3 --- .../tests/can_generate_algebraic_enum/output.py | 3 --- .../output.py | 3 --- .../output.py | 3 --- .../tests/can_generate_bare_string_enum/output.py | 3 --- .../can_generate_empty_algebraic_enum/output.py | 3 --- .../data/tests/can_generate_simple_enum/output.py | 3 --- .../output.py | 3 --- .../can_generate_slice_of_user_type/output.py | 3 --- .../output.py | 3 --- .../tests/can_generate_unit_structs/output.py | 3 --- .../tests/can_handle_anonymous_struct/output.py | 3 --- .../can_handle_quote_in_serde_rename/output.py | 3 --- core/data/tests/can_handle_serde_rename/output.py | 3 --- .../tests/can_handle_serde_rename_all/output.py | 3 --- .../output.py | 3 --- core/data/tests/can_handle_unit_type/output.py | 3 --- .../can_recognize_types_inside_modules/output.py | 3 --- .../output.py | 3 --- core/data/tests/excluded_by_target_os/output.py | 12 +++++++++--- core/data/tests/generate_types/output.py | 3 --- .../output.py | 3 --- core/data/tests/kebab_case_rename/output.py | 3 --- core/data/tests/orders_types/output.py | 3 --- .../data/tests/recursive_enum_decorator/output.py | 3 --- core/data/tests/resolves_qualified_type/output.py | 3 --- .../tests/serialize_anonymous_field_as/output.py | 3 --- core/data/tests/serialize_field_as/output.py | 3 --- core/data/tests/serialize_type_alias/output.py | 3 --- core/data/tests/smart_pointers/output.py | 3 --- .../output.py | 3 --- core/data/tests/test_generate_char/output.py | 3 --- core/data/tests/test_i54_u53_type/output.py | 3 --- .../data/tests/test_optional_type_alias/output.py | 3 --- .../tests/test_serde_default_struct/output.py | 3 --- core/data/tests/test_serde_url/output.py | 3 --- core/data/tests/test_serialized_as/output.py | 3 --- .../data/tests/test_serialized_as_tuple/output.py | 3 --- .../test_simple_enum_case_name_support/output.py | 3 --- core/data/tests/test_type_alias/output.py | 3 --- .../use_correct_decoded_variable_name/output.py | 3 --- .../tests/use_correct_integer_types/output.py | 3 --- core/src/language/python.rs | 15 ++++++++++++--- core/tests/snapshot_tests.rs | 1 + 45 files changed, 22 insertions(+), 132 deletions(-) diff --git a/core/data/tests/anonymous_struct_with_rename/output.py b/core/data/tests/anonymous_struct_with_rename/output.py index da9bfbad..3f42fdf0 100644 --- a/core/data/tests/anonymous_struct_with_rename/output.py +++ b/core/data/tests/anonymous_struct_with_rename/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_apply_prefix_correctly/output.py b/core/data/tests/can_apply_prefix_correctly/output.py index b5f7c805..0225ea39 100644 --- a/core/data/tests/can_apply_prefix_correctly/output.py +++ b/core/data/tests/can_apply_prefix_correctly/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_algebraic_enum/output.py b/core/data/tests/can_generate_algebraic_enum/output.py index f79aad55..dcda570c 100644 --- a/core/data/tests/can_generate_algebraic_enum/output.py +++ b/core/data/tests/can_generate_algebraic_enum/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py index f023d26a..4a197c42 100644 --- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py +++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py index bc0cd8a0..ec8212ae 100644 --- a/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_anonymous_struct_with_skipped_fields/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_bare_string_enum/output.py b/core/data/tests/can_generate_bare_string_enum/output.py index bc11925f..1fc81a20 100644 --- a/core/data/tests/can_generate_bare_string_enum/output.py +++ b/core/data/tests/can_generate_bare_string_enum/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.py b/core/data/tests/can_generate_empty_algebraic_enum/output.py index 160b7be2..d6af1314 100644 --- a/core/data/tests/can_generate_empty_algebraic_enum/output.py +++ b/core/data/tests/can_generate_empty_algebraic_enum/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_simple_enum/output.py b/core/data/tests/can_generate_simple_enum/output.py index 716cd0d0..8753e0b2 100644 --- a/core/data/tests/can_generate_simple_enum/output.py +++ b/core/data/tests/can_generate_simple_enum/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py index 0703f341..ef909a95 100644 --- a/core/data/tests/can_generate_simple_struct_with_a_comment/output.py +++ b/core/data/tests/can_generate_simple_struct_with_a_comment/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, Field diff --git a/core/data/tests/can_generate_slice_of_user_type/output.py b/core/data/tests/can_generate_slice_of_user_type/output.py index 5a523f4a..b7d7f1f1 100644 --- a/core/data/tests/can_generate_slice_of_user_type/output.py +++ b/core/data/tests/can_generate_slice_of_user_type/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/can_generate_struct_with_skipped_fields/output.py b/core/data/tests/can_generate_struct_with_skipped_fields/output.py index accea552..0def91cc 100644 --- a/core/data/tests/can_generate_struct_with_skipped_fields/output.py +++ b/core/data/tests/can_generate_struct_with_skipped_fields/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/can_generate_unit_structs/output.py b/core/data/tests/can_generate_unit_structs/output.py index c0b84425..cbc3a261 100644 --- a/core/data/tests/can_generate_unit_structs/output.py +++ b/core/data/tests/can_generate_unit_structs/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/can_handle_anonymous_struct/output.py b/core/data/tests/can_handle_anonymous_struct/output.py index e988f05d..9f0827bd 100644 --- a/core/data/tests/can_handle_anonymous_struct/output.py +++ b/core/data/tests/can_handle_anonymous_struct/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_handle_quote_in_serde_rename/output.py b/core/data/tests/can_handle_quote_in_serde_rename/output.py index 081a3ec6..ab26d652 100644 --- a/core/data/tests/can_handle_quote_in_serde_rename/output.py +++ b/core/data/tests/can_handle_quote_in_serde_rename/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_handle_serde_rename/output.py b/core/data/tests/can_handle_serde_rename/output.py index 5d08609f..403df44c 100644 --- a/core/data/tests/can_handle_serde_rename/output.py +++ b/core/data/tests/can_handle_serde_rename/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/can_handle_serde_rename_all/output.py b/core/data/tests/can_handle_serde_rename_all/output.py index b39990ca..ea53a6e5 100644 --- a/core/data/tests/can_handle_serde_rename_all/output.py +++ b/core/data/tests/can_handle_serde_rename_all/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/can_handle_serde_rename_on_top_level/output.py b/core/data/tests/can_handle_serde_rename_on_top_level/output.py index 1d27ede4..522125f5 100644 --- a/core/data/tests/can_handle_serde_rename_on_top_level/output.py +++ b/core/data/tests/can_handle_serde_rename_on_top_level/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/can_handle_unit_type/output.py b/core/data/tests/can_handle_unit_type/output.py index 2d74dce9..7ac76a52 100644 --- a/core/data/tests/can_handle_unit_type/output.py +++ b/core/data/tests/can_handle_unit_type/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/can_recognize_types_inside_modules/output.py b/core/data/tests/can_recognize_types_inside_modules/output.py index 92e7bec8..ba3b13aa 100644 --- a/core/data/tests/can_recognize_types_inside_modules/output.py +++ b/core/data/tests/can_recognize_types_inside_modules/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py index 17694078..9ff9ac9d 100644 --- a/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py +++ b/core/data/tests/enum_is_properly_named_with_serde_overrides/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/excluded_by_target_os/output.py b/core/data/tests/excluded_by_target_os/output.py index dd85551f..dc84b7f7 100644 --- a/core/data/tests/excluded_by_target_os/output.py +++ b/core/data/tests/excluded_by_target_os/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum @@ -8,6 +5,12 @@ from typing import Literal, Union +class AlwaysAccept(BaseModel): + """ + A struct with no target_os. Should be generated when + we use --target-os. + """ + pass class DefinedTwice(BaseModel): model_config = ConfigDict(populate_by_name=True) @@ -23,6 +26,9 @@ class NestedNotTarget1(BaseModel): pass class OtherExcluded(BaseModel): pass +class AlwaysAcceptEnum(str, Enum): + VARIANT1 = "Variant1" + VARIANT2 = "Variant2" class SomeEnum(str, Enum): pass class TestEnumVariant7Inner(BaseModel): diff --git a/core/data/tests/generate_types/output.py b/core/data/tests/generate_types/output.py index 48ea1a2f..542a6e50 100644 --- a/core/data/tests/generate_types/output.py +++ b/core/data/tests/generate_types/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/generates_empty_structs_and_initializers/output.py b/core/data/tests/generates_empty_structs_and_initializers/output.py index aea6ab33..503f97ac 100644 --- a/core/data/tests/generates_empty_structs_and_initializers/output.py +++ b/core/data/tests/generates_empty_structs_and_initializers/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/kebab_case_rename/output.py b/core/data/tests/kebab_case_rename/output.py index d6ccdec8..5d79b45b 100644 --- a/core/data/tests/kebab_case_rename/output.py +++ b/core/data/tests/kebab_case_rename/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/orders_types/output.py b/core/data/tests/orders_types/output.py index ba841ec2..08c773e4 100644 --- a/core/data/tests/orders_types/output.py +++ b/core/data/tests/orders_types/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, ConfigDict, Field diff --git a/core/data/tests/recursive_enum_decorator/output.py b/core/data/tests/recursive_enum_decorator/output.py index de4b2417..28298663 100644 --- a/core/data/tests/recursive_enum_decorator/output.py +++ b/core/data/tests/recursive_enum_decorator/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/resolves_qualified_type/output.py b/core/data/tests/resolves_qualified_type/output.py index c17b6aac..d6d3a21c 100644 --- a/core/data/tests/resolves_qualified_type/output.py +++ b/core/data/tests/resolves_qualified_type/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, Field diff --git a/core/data/tests/serialize_anonymous_field_as/output.py b/core/data/tests/serialize_anonymous_field_as/output.py index 921258d5..a09df09e 100644 --- a/core/data/tests/serialize_anonymous_field_as/output.py +++ b/core/data/tests/serialize_anonymous_field_as/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/serialize_field_as/output.py b/core/data/tests/serialize_field_as/output.py index fc6aeae0..0ddc8aac 100644 --- a/core/data/tests/serialize_field_as/output.py +++ b/core/data/tests/serialize_field_as/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, Field diff --git a/core/data/tests/serialize_type_alias/output.py b/core/data/tests/serialize_type_alias/output.py index be693ae3..8cf89496 100644 --- a/core/data/tests/serialize_type_alias/output.py +++ b/core/data/tests/serialize_type_alias/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations diff --git a/core/data/tests/smart_pointers/output.py b/core/data/tests/smart_pointers/output.py index f4fae9cf..1fd72ab4 100644 --- a/core/data/tests/smart_pointers/output.py +++ b/core/data/tests/smart_pointers/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/test_algebraic_enum_case_name_support/output.py b/core/data/tests/test_algebraic_enum_case_name_support/output.py index dded6ce1..4d871b30 100644 --- a/core/data/tests/test_algebraic_enum_case_name_support/output.py +++ b/core/data/tests/test_algebraic_enum_case_name_support/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/test_generate_char/output.py b/core/data/tests/test_generate_char/output.py index 1fe4da98..e2027f48 100644 --- a/core/data/tests/test_generate_char/output.py +++ b/core/data/tests/test_generate_char/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/test_i54_u53_type/output.py b/core/data/tests/test_i54_u53_type/output.py index a7d123af..f0ca68ae 100644 --- a/core/data/tests/test_i54_u53_type/output.py +++ b/core/data/tests/test_i54_u53_type/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/test_optional_type_alias/output.py b/core/data/tests/test_optional_type_alias/output.py index 31ac4162..0e48507a 100644 --- a/core/data/tests/test_optional_type_alias/output.py +++ b/core/data/tests/test_optional_type_alias/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/test_serde_default_struct/output.py b/core/data/tests/test_serde_default_struct/output.py index 8c6849b3..00fab78a 100644 --- a/core/data/tests/test_serde_default_struct/output.py +++ b/core/data/tests/test_serde_default_struct/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel, Field diff --git a/core/data/tests/test_serde_url/output.py b/core/data/tests/test_serde_url/output.py index e182c123..3e257f4e 100644 --- a/core/data/tests/test_serde_url/output.py +++ b/core/data/tests/test_serde_url/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/test_serialized_as/output.py b/core/data/tests/test_serialized_as/output.py index 639fb48b..8b6055f2 100644 --- a/core/data/tests/test_serialized_as/output.py +++ b/core/data/tests/test_serialized_as/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations diff --git a/core/data/tests/test_serialized_as_tuple/output.py b/core/data/tests/test_serialized_as_tuple/output.py index 9a1e8bea..60572aa9 100644 --- a/core/data/tests/test_serialized_as_tuple/output.py +++ b/core/data/tests/test_serialized_as_tuple/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations diff --git a/core/data/tests/test_simple_enum_case_name_support/output.py b/core/data/tests/test_simple_enum_case_name_support/output.py index ec344a3e..5188df74 100644 --- a/core/data/tests/test_simple_enum_case_name_support/output.py +++ b/core/data/tests/test_simple_enum_case_name_support/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from enum import Enum diff --git a/core/data/tests/test_type_alias/output.py b/core/data/tests/test_type_alias/output.py index 6d48e621..8fff2df1 100644 --- a/core/data/tests/test_type_alias/output.py +++ b/core/data/tests/test_type_alias/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/use_correct_decoded_variable_name/output.py b/core/data/tests/use_correct_decoded_variable_name/output.py index aea6ab33..503f97ac 100644 --- a/core/data/tests/use_correct_decoded_variable_name/output.py +++ b/core/data/tests/use_correct_decoded_variable_name/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/data/tests/use_correct_integer_types/output.py b/core/data/tests/use_correct_integer_types/output.py index caebe2b1..450eef59 100644 --- a/core/data/tests/use_correct_integer_types/output.py +++ b/core/data/tests/use_correct_integer_types/output.py @@ -1,6 +1,3 @@ -""" - Generated by typeshare 1.12.0 -""" from __future__ import annotations from pydantic import BaseModel diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 15fccbbc..47ddd9b1 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -77,6 +77,9 @@ pub struct Python { /// such that it can be read top to bottom /// globals: HashMap>, pub type_variables: HashSet, + /// Whether or not to exclude the version header that normally appears at the top of generated code. + /// If you aren't generating a snapshot test, this setting can just be left as a default (false) + pub no_version_header: bool, } impl Language for Python { @@ -212,9 +215,11 @@ impl Language for Python { } fn begin_file(&mut self, w: &mut dyn Write, _parsed_data: &ParsedData) -> std::io::Result<()> { - writeln!(w, "\"\"\"")?; - writeln!(w, " Generated by typeshare {}", env!("CARGO_PKG_VERSION"))?; - writeln!(w, "\"\"\"")?; + if !self.no_version_header { + writeln!(w, "\"\"\"")?; + writeln!(w, " Generated by typeshare {}", env!("CARGO_PKG_VERSION"))?; + writeln!(w, "\"\"\"")?; + } Ok(()) } @@ -689,6 +694,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: true, }, ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { id: "str".to_string(), @@ -712,6 +718,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: true, }, ty: RustType::Special(SpecialRustType::Option(Box::new(RustType::Simple { id: "str".to_string(), @@ -737,6 +744,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: true, }, ty: RustType::Simple { id: "str".to_string(), @@ -760,6 +768,7 @@ mod test { id: Id { original: "field".to_string(), renamed: "field".to_string(), + serde_rename: true, }, ty: RustType::Simple { id: "str".to_string(), diff --git a/core/tests/snapshot_tests.rs b/core/tests/snapshot_tests.rs index 5c0f6050..8724de36 100644 --- a/core/tests/snapshot_tests.rs +++ b/core/tests/snapshot_tests.rs @@ -216,6 +216,7 @@ macro_rules! language_instance { (python {$($field:ident: $val:expr),* $(,)?}) => { #[allow(clippy::needless_update)] Box::new(typeshare_core::language::Python { + no_version_header: true, $($field: $val,)* ..Default::default() }) From 5522caf1c94576f5a34a07c7a7712b5f2a9b98b5 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Wed, 4 Dec 2024 19:16:29 +0100 Subject: [PATCH 55/57] Format Python typeshare --- core/src/language/python.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 1cb81ab9..9642f793 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -627,6 +627,7 @@ impl Python { } } } + self.write_comments(w, false, &shared.comments, 0)?; if union_members.len() == 1 { writeln!(w, "{enum_name} = {}", union_members[0])?; From c6669fc2d8f5426555e075fd40d894ee36809758 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Wed, 4 Dec 2024 19:17:26 +0100 Subject: [PATCH 56/57] Format Python typeshare --- core/src/language/python.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 9642f793..551da068 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -627,7 +627,7 @@ impl Python { } } } - + self.write_comments(w, false, &shared.comments, 0)?; if union_members.len() == 1 { writeln!(w, "{enum_name} = {}", union_members[0])?; From aa12f2bacb180adc97d7bc70f50f823f407a4972 Mon Sep 17 00:00:00 2001 From: Horia Culea Date: Thu, 5 Dec 2024 15:41:53 +0100 Subject: [PATCH 57/57] Link issue with Python limitations in README.md --- README.md | 3 ++- core/src/language/python.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 203b0865..327d0976 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,8 @@ Are you getting weird deserialization issues? Did our procedural macro throw a c - Swift - Typescript - Go** -- Python** +- Python** (see list of limitations [here](https://github.com/1Password/typeshare/issues/217)) + If there is a language that you want Typeshare to generate definitions for, you can either: 1. Open an issue in this repository requesting your language of choice. diff --git a/core/src/language/python.rs b/core/src/language/python.rs index 551da068..0e0a9f1a 100644 --- a/core/src/language/python.rs +++ b/core/src/language/python.rs @@ -335,7 +335,8 @@ impl Language for Python { _writer: &mut dyn Write, _imports: super::ScopedCrateTypes<'_>, ) -> std::io::Result<()> { - todo!() + // TODO: to implement when adding suport for outputting to multiple files. + Ok(()) } }