1use crate::lcnf::*;
6
7use super::types::{
8 FStringPart, MatchArm, PythonBackend, PythonClass, PythonClassVar, PythonExpr, PythonFunction,
9 PythonLit, PythonModule, PythonParam, PythonStmt, PythonType,
10};
11
12pub type FromImports = Vec<(String, Vec<(String, Option<String>)>)>;
13pub(super) fn indent_str(level: usize) -> String {
14 " ".repeat(level)
15}
16pub(super) fn format_from_import(module: &str, names: &[(String, Option<String>)]) -> String {
17 if names.len() == 1 && names[0].0 == "*" {
18 return format!("from {} import *", module);
19 }
20 let names_str: Vec<String> = names
21 .iter()
22 .map(|(n, a)| match a {
23 Some(alias) => format!("{} as {}", n, alias),
24 None => n.clone(),
25 })
26 .collect();
27 if names_str.len() > 4 {
28 let joined = names_str.join(",\n ");
29 format!("from {} import (\n {}\n)", module, joined)
30 } else {
31 format!("from {} import {}", module, names_str.join(", "))
32 }
33}
34pub(super) fn emit_docstring(doc: &str, indent: usize) -> String {
35 let pad = indent_str(indent);
36 format!("{}\"\"\"{}\"\"\"", pad, doc)
37}
38pub(super) fn emit_function(func: &PythonFunction, indent: usize) -> String {
39 let pad = indent_str(indent);
40 let mut out = String::new();
41 for dec in &func.decorators {
42 out.push_str(&format!("{}@{}\n", pad, dec));
43 }
44 if func.is_classmethod && !func.decorators.contains(&"classmethod".to_string()) {
45 out.push_str(&format!("{}@classmethod\n", pad));
46 }
47 if func.is_staticmethod && !func.decorators.contains(&"staticmethod".to_string()) {
48 out.push_str(&format!("{}@staticmethod\n", pad));
49 }
50 if func.is_async {
51 out.push_str(&format!("{}async def {}(", pad, func.name));
52 } else {
53 out.push_str(&format!("{}def {}(", pad, func.name));
54 }
55 let params_str: Vec<String> = func.params.iter().map(|p| p.to_string()).collect();
56 out.push_str(¶ms_str.join(", "));
57 out.push(')');
58 if let Some(ret) = &func.return_type {
59 out.push_str(&format!(" -> {}", ret));
60 }
61 out.push_str(":\n");
62 if func.body.is_empty() {
63 out.push_str(&format!("{} pass\n", pad));
64 } else {
65 for stmt in &func.body {
66 out.push_str(&emit_stmt(stmt, indent + 1));
67 out.push('\n');
68 }
69 }
70 out
71}
72pub(super) fn emit_class(cls: &PythonClass, indent: usize) -> String {
73 let pad = indent_str(indent);
74 let mut out = String::new();
75 if cls.is_dataclass && !cls.decorators.contains(&"dataclass".to_string()) {
76 out.push_str(&format!("{}@dataclass\n", pad));
77 }
78 for dec in &cls.decorators {
79 out.push_str(&format!("{}@{}\n", pad, dec));
80 }
81 out.push_str(&format!("{}class {}", pad, cls.name));
82 let mut bases = cls.bases.clone();
83 if cls.is_abstract && !bases.contains(&"ABC".to_string()) {
84 bases.push("ABC".to_string());
85 }
86 if !bases.is_empty() {
87 out.push_str(&format!("({})", bases.join(", ")));
88 }
89 out.push_str(":\n");
90 if let Some(doc) = &cls.docstring {
91 out.push_str(&emit_docstring(doc, indent + 1));
92 out.push('\n');
93 }
94 let mut body_empty = true;
95 for cv in &cls.class_vars {
96 body_empty = false;
97 let inner_pad = indent_str(indent + 1);
98 match &cv.default {
99 Some(default) => {
100 out.push_str(&format!(
101 "{}{}: {} = {}\n",
102 inner_pad, cv.name, cv.annotation, default
103 ));
104 }
105 None => {
106 out.push_str(&format!("{}{}: {}\n", inner_pad, cv.name, cv.annotation));
107 }
108 }
109 }
110 for method in &cls.methods {
111 body_empty = false;
112 out.push('\n');
113 out.push_str(&emit_function(method, indent + 1));
114 }
115 if body_empty {
116 out.push_str(&format!("{} pass\n", pad));
117 }
118 out
119}
120pub(super) fn emit_match_arm(arm: &MatchArm, indent: usize) -> String {
121 let pad = indent_str(indent);
122 let mut out = String::new();
123 match &arm.guard {
124 Some(guard) => out.push_str(&format!("{}case {} if {}:\n", pad, arm.pattern, guard)),
125 None => out.push_str(&format!("{}case {}:\n", pad, arm.pattern)),
126 }
127 if arm.body.is_empty() {
128 out.push_str(&format!("{} pass\n", pad));
129 } else {
130 for stmt in &arm.body {
131 out.push_str(&emit_stmt(stmt, indent + 1));
132 out.push('\n');
133 }
134 }
135 out
136}
137pub(super) fn emit_stmt(stmt: &PythonStmt, indent: usize) -> String {
138 let pad = indent_str(indent);
139 match stmt {
140 PythonStmt::Expr(e) => format!("{}{}", pad, e),
141 PythonStmt::Assign(targets, value) => {
142 let tgts: Vec<String> = targets.iter().map(|t| format!("{}", t)).collect();
143 format!("{}{} = {}", pad, tgts.join(" = "), value)
144 }
145 PythonStmt::AugAssign(target, op, value) => {
146 format!("{}{} {}= {}", pad, target, op, value)
147 }
148 PythonStmt::AnnAssign(name, ty, value) => match value {
149 Some(v) => format!("{}{}: {} = {}", pad, name, ty, v),
150 None => format!("{}{}: {}", pad, name, ty),
151 },
152 PythonStmt::If(cond, then_body, elif_branches, else_body) => {
153 let mut out = format!("{}if {}:\n", pad, cond);
154 if then_body.is_empty() {
155 out.push_str(&format!("{} pass\n", pad));
156 } else {
157 for s in then_body {
158 out.push_str(&emit_stmt(s, indent + 1));
159 out.push('\n');
160 }
161 }
162 for (elif_cond, elif_body) in elif_branches {
163 out.push_str(&format!("{}elif {}:\n", pad, elif_cond));
164 if elif_body.is_empty() {
165 out.push_str(&format!("{} pass\n", pad));
166 } else {
167 for s in elif_body {
168 out.push_str(&emit_stmt(s, indent + 1));
169 out.push('\n');
170 }
171 }
172 }
173 if !else_body.is_empty() {
174 out.push_str(&format!("{}else:\n", pad));
175 for s in else_body {
176 out.push_str(&emit_stmt(s, indent + 1));
177 out.push('\n');
178 }
179 }
180 if out.ends_with('\n') {
181 out.pop();
182 }
183 out
184 }
185 PythonStmt::For(var, iter, body, else_body) => {
186 let mut out = format!("{}for {} in {}:\n", pad, var, iter);
187 if body.is_empty() {
188 out.push_str(&format!("{} pass\n", pad));
189 } else {
190 for s in body {
191 out.push_str(&emit_stmt(s, indent + 1));
192 out.push('\n');
193 }
194 }
195 if !else_body.is_empty() {
196 out.push_str(&format!("{}else:\n", pad));
197 for s in else_body {
198 out.push_str(&emit_stmt(s, indent + 1));
199 out.push('\n');
200 }
201 }
202 if out.ends_with('\n') {
203 out.pop();
204 }
205 out
206 }
207 PythonStmt::While(cond, body, else_body) => {
208 let mut out = format!("{}while {}:\n", pad, cond);
209 if body.is_empty() {
210 out.push_str(&format!("{} pass\n", pad));
211 } else {
212 for s in body {
213 out.push_str(&emit_stmt(s, indent + 1));
214 out.push('\n');
215 }
216 }
217 if !else_body.is_empty() {
218 out.push_str(&format!("{}else:\n", pad));
219 for s in else_body {
220 out.push_str(&emit_stmt(s, indent + 1));
221 out.push('\n');
222 }
223 }
224 if out.ends_with('\n') {
225 out.pop();
226 }
227 out
228 }
229 PythonStmt::With(items, body) => {
230 let items_str: Vec<String> = items
231 .iter()
232 .map(|(e, alias)| match alias {
233 Some(a) => format!("{} as {}", e, a),
234 None => format!("{}", e),
235 })
236 .collect();
237 let mut out = format!("{}with {}:\n", pad, items_str.join(", "));
238 if body.is_empty() {
239 out.push_str(&format!("{} pass\n", pad));
240 } else {
241 for s in body {
242 out.push_str(&emit_stmt(s, indent + 1));
243 out.push('\n');
244 }
245 }
246 if out.ends_with('\n') {
247 out.pop();
248 }
249 out
250 }
251 PythonStmt::Try(try_body, except_clauses, else_body, finally_body) => {
252 let mut out = format!("{}try:\n", pad);
253 if try_body.is_empty() {
254 out.push_str(&format!("{} pass\n", pad));
255 } else {
256 for s in try_body {
257 out.push_str(&emit_stmt(s, indent + 1));
258 out.push('\n');
259 }
260 }
261 for (exc_type, exc_name, exc_body) in except_clauses {
262 match (exc_type, exc_name) {
263 (Some(t), Some(n)) => out.push_str(&format!("{}except {} as {}:\n", pad, t, n)),
264 (Some(t), None) => out.push_str(&format!("{}except {}:\n", pad, t)),
265 (None, _) => out.push_str(&format!("{}except:\n", pad)),
266 }
267 if exc_body.is_empty() {
268 out.push_str(&format!("{} pass\n", pad));
269 } else {
270 for s in exc_body {
271 out.push_str(&emit_stmt(s, indent + 1));
272 out.push('\n');
273 }
274 }
275 }
276 if !else_body.is_empty() {
277 out.push_str(&format!("{}else:\n", pad));
278 for s in else_body {
279 out.push_str(&emit_stmt(s, indent + 1));
280 out.push('\n');
281 }
282 }
283 if !finally_body.is_empty() {
284 out.push_str(&format!("{}finally:\n", pad));
285 for s in finally_body {
286 out.push_str(&emit_stmt(s, indent + 1));
287 out.push('\n');
288 }
289 }
290 if out.ends_with('\n') {
291 out.pop();
292 }
293 out
294 }
295 PythonStmt::Return(expr) => match expr {
296 Some(e) => format!("{}return {}", pad, e),
297 None => format!("{}return", pad),
298 },
299 PythonStmt::Raise(expr) => match expr {
300 Some(e) => format!("{}raise {}", pad, e),
301 None => format!("{}raise", pad),
302 },
303 PythonStmt::Del(targets) => {
304 let tgts: Vec<String> = targets.iter().map(|t| format!("{}", t)).collect();
305 format!("{}del {}", pad, tgts.join(", "))
306 }
307 PythonStmt::Pass => format!("{}pass", pad),
308 PythonStmt::Break => format!("{}break", pad),
309 PythonStmt::Continue => format!("{}continue", pad),
310 PythonStmt::Import(names) => {
311 let parts: Vec<String> = names
312 .iter()
313 .map(|(n, a)| match a {
314 Some(alias) => format!("{} as {}", n, alias),
315 None => n.clone(),
316 })
317 .collect();
318 format!("{}import {}", pad, parts.join(", "))
319 }
320 PythonStmt::From(module, names) => {
321 format!("{}{}", pad, format_from_import(module, names))
322 }
323 PythonStmt::ClassDef(cls) => {
324 let text = emit_class(cls, indent);
325 text.trim_end_matches('\n').to_string()
326 }
327 PythonStmt::FuncDef(func) => {
328 let text = emit_function(func, indent);
329 text.trim_end_matches('\n').to_string()
330 }
331 PythonStmt::AsyncFuncDef(func) => {
332 let mut f = func.clone();
333 f.is_async = true;
334 let text = emit_function(&f, indent);
335 text.trim_end_matches('\n').to_string()
336 }
337 PythonStmt::Docstring(doc) => emit_docstring(doc, indent),
338 PythonStmt::Assert(expr, msg) => match msg {
339 Some(m) => format!("{}assert {}, {}", pad, expr, m),
340 None => format!("{}assert {}", pad, expr),
341 },
342 PythonStmt::Global(names) => format!("{}global {}", pad, names.join(", ")),
343 PythonStmt::Nonlocal(names) => format!("{}nonlocal {}", pad, names.join(", ")),
344 PythonStmt::Match(subject, arms) => {
345 let mut out = format!("{}match {}:\n", pad, subject);
346 for arm in arms {
347 out.push_str(&emit_match_arm(arm, indent + 1));
348 }
349 if out.ends_with('\n') {
350 out.pop();
351 }
352 out
353 }
354 PythonStmt::Raw(text) => format!("{}{}", pad, text),
355 }
356}
357pub const PYTHON_KEYWORDS: &[&str] = &[
359 "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", "continue",
360 "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import",
361 "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while",
362 "with", "yield", "match", "case", "type",
363];
364#[cfg(test)]
365mod tests {
366 use super::*;
367 #[test]
368 pub(super) fn test_type_int() {
369 assert_eq!(PythonType::Int.to_string(), "int");
370 }
371 #[test]
372 pub(super) fn test_type_float() {
373 assert_eq!(PythonType::Float.to_string(), "float");
374 }
375 #[test]
376 pub(super) fn test_type_str() {
377 assert_eq!(PythonType::Str.to_string(), "str");
378 }
379 #[test]
380 pub(super) fn test_type_bool() {
381 assert_eq!(PythonType::Bool.to_string(), "bool");
382 }
383 #[test]
384 pub(super) fn test_type_none() {
385 assert_eq!(PythonType::None_.to_string(), "None");
386 }
387 #[test]
388 pub(super) fn test_type_list() {
389 let t = PythonType::List(Box::new(PythonType::Int));
390 assert_eq!(t.to_string(), "list[int]");
391 }
392 #[test]
393 pub(super) fn test_type_dict() {
394 let t = PythonType::Dict(Box::new(PythonType::Str), Box::new(PythonType::Int));
395 assert_eq!(t.to_string(), "dict[str, int]");
396 }
397 #[test]
398 pub(super) fn test_type_tuple() {
399 let t = PythonType::Tuple(vec![PythonType::Int, PythonType::Str, PythonType::Bool]);
400 assert_eq!(t.to_string(), "tuple[int, str, bool]");
401 }
402 #[test]
403 pub(super) fn test_type_optional() {
404 let t = PythonType::Optional(Box::new(PythonType::Int));
405 assert_eq!(t.to_string(), "int | None");
406 }
407 #[test]
408 pub(super) fn test_type_union() {
409 let t = PythonType::Union(vec![PythonType::Int, PythonType::Str]);
410 assert_eq!(t.to_string(), "int | str");
411 }
412 #[test]
413 pub(super) fn test_type_custom() {
414 let t = PythonType::Custom("MyClass".to_string());
415 assert_eq!(t.to_string(), "MyClass");
416 }
417 #[test]
418 pub(super) fn test_type_any() {
419 assert_eq!(PythonType::Any.to_string(), "Any");
420 }
421 #[test]
422 pub(super) fn test_lit_int() {
423 assert_eq!(PythonLit::Int(42).to_string(), "42");
424 }
425 #[test]
426 pub(super) fn test_lit_float() {
427 assert_eq!(PythonLit::Float(3.14).to_string(), "3.14");
428 }
429 #[test]
430 pub(super) fn test_lit_float_whole() {
431 assert_eq!(PythonLit::Float(2.0).to_string(), "2.0");
432 }
433 #[test]
434 pub(super) fn test_lit_str() {
435 assert_eq!(PythonLit::Str("hello".to_string()).to_string(), "\"hello\"");
436 }
437 #[test]
438 pub(super) fn test_lit_str_escapes() {
439 let s = PythonLit::Str("a\"b\\c\nd".to_string()).to_string();
440 assert!(s.contains("\\\""));
441 assert!(s.contains("\\\\"));
442 assert!(s.contains("\\n"));
443 }
444 #[test]
445 pub(super) fn test_lit_bool_true() {
446 assert_eq!(PythonLit::Bool(true).to_string(), "True");
447 }
448 #[test]
449 pub(super) fn test_lit_bool_false() {
450 assert_eq!(PythonLit::Bool(false).to_string(), "False");
451 }
452 #[test]
453 pub(super) fn test_lit_none() {
454 assert_eq!(PythonLit::None.to_string(), "None");
455 }
456 #[test]
457 pub(super) fn test_lit_ellipsis() {
458 assert_eq!(PythonLit::Ellipsis.to_string(), "...");
459 }
460 #[test]
461 pub(super) fn test_expr_var() {
462 assert_eq!(PythonExpr::Var("x".to_string()).to_string(), "x");
463 }
464 #[test]
465 pub(super) fn test_expr_binop() {
466 let e = PythonExpr::BinOp(
467 "+".to_string(),
468 Box::new(PythonExpr::Var("x".to_string())),
469 Box::new(PythonExpr::Lit(PythonLit::Int(1))),
470 );
471 assert_eq!(e.to_string(), "(x + 1)");
472 }
473 #[test]
474 pub(super) fn test_expr_unary_not() {
475 let e = PythonExpr::UnaryOp(
476 "not".to_string(),
477 Box::new(PythonExpr::Var("x".to_string())),
478 );
479 assert_eq!(e.to_string(), "not x");
480 }
481 #[test]
482 pub(super) fn test_expr_unary_neg() {
483 let e = PythonExpr::UnaryOp("-".to_string(), Box::new(PythonExpr::Var("x".to_string())));
484 assert_eq!(e.to_string(), "-x");
485 }
486 #[test]
487 pub(super) fn test_expr_call_no_args() {
488 let e = PythonExpr::Call(Box::new(PythonExpr::Var("foo".to_string())), vec![], vec![]);
489 assert_eq!(e.to_string(), "foo()");
490 }
491 #[test]
492 pub(super) fn test_expr_call_with_args() {
493 let e = PythonExpr::Call(
494 Box::new(PythonExpr::Var("foo".to_string())),
495 vec![
496 PythonExpr::Lit(PythonLit::Int(1)),
497 PythonExpr::Lit(PythonLit::Int(2)),
498 ],
499 vec![],
500 );
501 assert_eq!(e.to_string(), "foo(1, 2)");
502 }
503 #[test]
504 pub(super) fn test_expr_call_with_kwargs() {
505 let e = PythonExpr::Call(
506 Box::new(PythonExpr::Var("print".to_string())),
507 vec![PythonExpr::Lit(PythonLit::Str("hi".to_string()))],
508 vec![(
509 "end".to_string(),
510 PythonExpr::Lit(PythonLit::Str("".to_string())),
511 )],
512 );
513 let s = e.to_string();
514 assert!(s.contains("end=\"\""));
515 }
516 #[test]
517 pub(super) fn test_expr_attr() {
518 let e = PythonExpr::Attr(
519 Box::new(PythonExpr::Var("obj".to_string())),
520 "field".to_string(),
521 );
522 assert_eq!(e.to_string(), "obj.field");
523 }
524 #[test]
525 pub(super) fn test_expr_subscript() {
526 let e = PythonExpr::Subscript(
527 Box::new(PythonExpr::Var("lst".to_string())),
528 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
529 );
530 assert_eq!(e.to_string(), "lst[0]");
531 }
532 #[test]
533 pub(super) fn test_expr_lambda() {
534 let e = PythonExpr::Lambda(
535 vec!["x".to_string(), "y".to_string()],
536 Box::new(PythonExpr::BinOp(
537 "+".to_string(),
538 Box::new(PythonExpr::Var("x".to_string())),
539 Box::new(PythonExpr::Var("y".to_string())),
540 )),
541 );
542 assert_eq!(e.to_string(), "lambda x, y: (x + y)");
543 }
544 #[test]
545 pub(super) fn test_expr_if_expr() {
546 let e = PythonExpr::IfExpr(
547 Box::new(PythonExpr::Lit(PythonLit::Int(1))),
548 Box::new(PythonExpr::Var("cond".to_string())),
549 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
550 );
551 assert_eq!(e.to_string(), "1 if cond else 0");
552 }
553 #[test]
554 pub(super) fn test_expr_list_comp() {
555 let e = PythonExpr::ListComp(
556 Box::new(PythonExpr::BinOp(
557 "*".to_string(),
558 Box::new(PythonExpr::Var("x".to_string())),
559 Box::new(PythonExpr::Lit(PythonLit::Int(2))),
560 )),
561 "x".to_string(),
562 Box::new(PythonExpr::Var("lst".to_string())),
563 None,
564 );
565 assert_eq!(e.to_string(), "[(x * 2) for x in lst]");
566 }
567 #[test]
568 pub(super) fn test_expr_list_comp_with_filter() {
569 let e = PythonExpr::ListComp(
570 Box::new(PythonExpr::Var("x".to_string())),
571 "x".to_string(),
572 Box::new(PythonExpr::Var("lst".to_string())),
573 Some(Box::new(PythonExpr::BinOp(
574 ">".to_string(),
575 Box::new(PythonExpr::Var("x".to_string())),
576 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
577 ))),
578 );
579 assert_eq!(e.to_string(), "[x for x in lst if (x > 0)]");
580 }
581 #[test]
582 pub(super) fn test_expr_dict_comp() {
583 let e = PythonExpr::DictComp(
584 Box::new(PythonExpr::Var("k".to_string())),
585 Box::new(PythonExpr::Var("v".to_string())),
586 "k".to_string(),
587 "v".to_string(),
588 Box::new(PythonExpr::Call(
589 Box::new(PythonExpr::Attr(
590 Box::new(PythonExpr::Var("d".to_string())),
591 "items".to_string(),
592 )),
593 vec![],
594 vec![],
595 )),
596 );
597 assert_eq!(e.to_string(), "{k: v for k, v in d.items()}");
598 }
599 #[test]
600 pub(super) fn test_expr_tuple_empty() {
601 let e = PythonExpr::Tuple(vec![]);
602 assert_eq!(e.to_string(), "()");
603 }
604 #[test]
605 pub(super) fn test_expr_tuple_single() {
606 let e = PythonExpr::Tuple(vec![PythonExpr::Lit(PythonLit::Int(1))]);
607 assert_eq!(e.to_string(), "(1,)");
608 }
609 #[test]
610 pub(super) fn test_expr_tuple_multi() {
611 let e = PythonExpr::Tuple(vec![
612 PythonExpr::Lit(PythonLit::Int(1)),
613 PythonExpr::Lit(PythonLit::Int(2)),
614 ]);
615 assert_eq!(e.to_string(), "(1, 2)");
616 }
617 #[test]
618 pub(super) fn test_expr_list() {
619 let e = PythonExpr::List(vec![
620 PythonExpr::Lit(PythonLit::Int(1)),
621 PythonExpr::Lit(PythonLit::Int(2)),
622 PythonExpr::Lit(PythonLit::Int(3)),
623 ]);
624 assert_eq!(e.to_string(), "[1, 2, 3]");
625 }
626 #[test]
627 pub(super) fn test_expr_dict() {
628 let e = PythonExpr::Dict(vec![(
629 PythonExpr::Lit(PythonLit::Str("key".to_string())),
630 PythonExpr::Lit(PythonLit::Int(42)),
631 )]);
632 assert_eq!(e.to_string(), "{\"key\": 42}");
633 }
634 #[test]
635 pub(super) fn test_expr_set_empty() {
636 let e = PythonExpr::Set(vec![]);
637 assert_eq!(e.to_string(), "set()");
638 }
639 #[test]
640 pub(super) fn test_expr_set_nonempty() {
641 let e = PythonExpr::Set(vec![
642 PythonExpr::Lit(PythonLit::Int(1)),
643 PythonExpr::Lit(PythonLit::Int(2)),
644 ]);
645 assert_eq!(e.to_string(), "{1, 2}");
646 }
647 #[test]
648 pub(super) fn test_expr_await() {
649 let e = PythonExpr::Await(Box::new(PythonExpr::Var("coro".to_string())));
650 assert_eq!(e.to_string(), "await coro");
651 }
652 #[test]
653 pub(super) fn test_expr_yield() {
654 let e = PythonExpr::Yield(Some(Box::new(PythonExpr::Lit(PythonLit::Int(42)))));
655 assert_eq!(e.to_string(), "yield 42");
656 }
657 #[test]
658 pub(super) fn test_expr_yield_none() {
659 let e = PythonExpr::Yield(None);
660 assert_eq!(e.to_string(), "yield");
661 }
662 #[test]
663 pub(super) fn test_expr_fstring() {
664 let e = PythonExpr::FString(vec![
665 FStringPart::Literal("hello ".to_string()),
666 FStringPart::Expr(PythonExpr::Var("name".to_string())),
667 FStringPart::Literal("!".to_string()),
668 ]);
669 assert_eq!(e.to_string(), "f\"hello {name}!\"");
670 }
671 #[test]
672 pub(super) fn test_expr_fstring_with_format() {
673 let e = PythonExpr::FString(vec![FStringPart::ExprWithFormat(
674 PythonExpr::Var("pi".to_string()),
675 ".2f".to_string(),
676 )]);
677 assert_eq!(e.to_string(), "f\"{pi:.2f}\"");
678 }
679 #[test]
680 pub(super) fn test_expr_walrus() {
681 let e = PythonExpr::Walrus(
682 "n".to_string(),
683 Box::new(PythonExpr::Call(
684 Box::new(PythonExpr::Var("len".to_string())),
685 vec![PythonExpr::Var("lst".to_string())],
686 vec![],
687 )),
688 );
689 assert_eq!(e.to_string(), "(n := len(lst))");
690 }
691 #[test]
692 pub(super) fn test_stmt_pass() {
693 assert_eq!(emit_stmt(&PythonStmt::Pass, 0), "pass");
694 }
695 #[test]
696 pub(super) fn test_stmt_return_none() {
697 assert_eq!(emit_stmt(&PythonStmt::Return(None), 0), "return");
698 }
699 #[test]
700 pub(super) fn test_stmt_return_expr() {
701 let s = PythonStmt::Return(Some(PythonExpr::Lit(PythonLit::Int(42))));
702 assert_eq!(emit_stmt(&s, 0), "return 42");
703 }
704 #[test]
705 pub(super) fn test_stmt_assign() {
706 let s = PythonStmt::Assign(
707 vec![PythonExpr::Var("x".to_string())],
708 PythonExpr::Lit(PythonLit::Int(5)),
709 );
710 assert_eq!(emit_stmt(&s, 0), "x = 5");
711 }
712 #[test]
713 pub(super) fn test_stmt_ann_assign() {
714 let s = PythonStmt::AnnAssign(
715 "x".to_string(),
716 PythonType::Int,
717 Some(PythonExpr::Lit(PythonLit::Int(0))),
718 );
719 assert_eq!(emit_stmt(&s, 0), "x: int = 0");
720 }
721 #[test]
722 pub(super) fn test_stmt_ann_assign_no_value() {
723 let s = PythonStmt::AnnAssign("y".to_string(), PythonType::Str, None);
724 assert_eq!(emit_stmt(&s, 0), "y: str");
725 }
726 #[test]
727 pub(super) fn test_stmt_aug_assign() {
728 let s = PythonStmt::AugAssign(
729 PythonExpr::Var("x".to_string()),
730 "+".to_string(),
731 PythonExpr::Lit(PythonLit::Int(1)),
732 );
733 assert_eq!(emit_stmt(&s, 0), "x += 1");
734 }
735 #[test]
736 pub(super) fn test_stmt_if_simple() {
737 let s = PythonStmt::If(
738 PythonExpr::Var("cond".to_string()),
739 vec![PythonStmt::Pass],
740 vec![],
741 vec![],
742 );
743 let out = emit_stmt(&s, 0);
744 assert!(out.contains("if cond:"));
745 assert!(out.contains("pass"));
746 }
747 #[test]
748 pub(super) fn test_stmt_if_else() {
749 let s = PythonStmt::If(
750 PythonExpr::Var("cond".to_string()),
751 vec![PythonStmt::Return(Some(PythonExpr::Lit(PythonLit::Int(1))))],
752 vec![],
753 vec![PythonStmt::Return(Some(PythonExpr::Lit(PythonLit::Int(0))))],
754 );
755 let out = emit_stmt(&s, 0);
756 assert!(out.contains("if cond:"));
757 assert!(out.contains("else:"));
758 assert!(out.contains("return 1"));
759 assert!(out.contains("return 0"));
760 }
761 #[test]
762 pub(super) fn test_stmt_for() {
763 let s = PythonStmt::For(
764 "x".to_string(),
765 PythonExpr::Var("items".to_string()),
766 vec![PythonStmt::Pass],
767 vec![],
768 );
769 let out = emit_stmt(&s, 0);
770 assert!(out.contains("for x in items:"));
771 }
772 #[test]
773 pub(super) fn test_stmt_while() {
774 let s = PythonStmt::While(
775 PythonExpr::Lit(PythonLit::Bool(true)),
776 vec![PythonStmt::Break],
777 vec![],
778 );
779 let out = emit_stmt(&s, 0);
780 assert!(out.contains("while True:"));
781 assert!(out.contains("break"));
782 }
783 #[test]
784 pub(super) fn test_stmt_with() {
785 let s = PythonStmt::With(
786 vec![(
787 PythonExpr::Call(
788 Box::new(PythonExpr::Var("open".to_string())),
789 vec![PythonExpr::Lit(PythonLit::Str("f.txt".to_string()))],
790 vec![],
791 ),
792 Some("fh".to_string()),
793 )],
794 vec![PythonStmt::Pass],
795 );
796 let out = emit_stmt(&s, 0);
797 assert!(out.contains("with open(\"f.txt\") as fh:"));
798 }
799 #[test]
800 pub(super) fn test_stmt_try_except() {
801 let s = PythonStmt::Try(
802 vec![PythonStmt::Pass],
803 vec![(
804 Some(PythonExpr::Var("ValueError".to_string())),
805 Some("e".to_string()),
806 vec![PythonStmt::Pass],
807 )],
808 vec![],
809 vec![],
810 );
811 let out = emit_stmt(&s, 0);
812 assert!(out.contains("try:"));
813 assert!(out.contains("except ValueError as e:"));
814 }
815 #[test]
816 pub(super) fn test_stmt_raise() {
817 let s = PythonStmt::Raise(Some(PythonExpr::Call(
818 Box::new(PythonExpr::Var("ValueError".to_string())),
819 vec![PythonExpr::Lit(PythonLit::Str("bad".to_string()))],
820 vec![],
821 )));
822 let out = emit_stmt(&s, 0);
823 assert!(out.contains("raise ValueError(\"bad\")"));
824 }
825 #[test]
826 pub(super) fn test_stmt_import() {
827 let s = PythonStmt::Import(vec![("os".to_string(), None)]);
828 assert_eq!(emit_stmt(&s, 0), "import os");
829 }
830 #[test]
831 pub(super) fn test_stmt_import_alias() {
832 let s = PythonStmt::Import(vec![("numpy".to_string(), Some("np".to_string()))]);
833 assert_eq!(emit_stmt(&s, 0), "import numpy as np");
834 }
835 #[test]
836 pub(super) fn test_stmt_from_import() {
837 let s = PythonStmt::From(
838 "os.path".to_string(),
839 vec![("join".to_string(), None), ("exists".to_string(), None)],
840 );
841 let out = emit_stmt(&s, 0);
842 assert!(out.contains("from os.path import"));
843 assert!(out.contains("join"));
844 assert!(out.contains("exists"));
845 }
846 #[test]
847 pub(super) fn test_stmt_delete() {
848 let s = PythonStmt::Del(vec![PythonExpr::Var("x".to_string())]);
849 assert_eq!(emit_stmt(&s, 0), "del x");
850 }
851 #[test]
852 pub(super) fn test_stmt_assert() {
853 let s = PythonStmt::Assert(
854 PythonExpr::BinOp(
855 "==".to_string(),
856 Box::new(PythonExpr::Var("x".to_string())),
857 Box::new(PythonExpr::Lit(PythonLit::Int(1))),
858 ),
859 None,
860 );
861 let out = emit_stmt(&s, 0);
862 assert!(out.contains("assert (x == 1)"));
863 }
864 #[test]
865 pub(super) fn test_stmt_global() {
866 let s = PythonStmt::Global(vec!["x".to_string(), "y".to_string()]);
867 assert_eq!(emit_stmt(&s, 0), "global x, y");
868 }
869 #[test]
870 pub(super) fn test_stmt_nonlocal() {
871 let s = PythonStmt::Nonlocal(vec!["count".to_string()]);
872 assert_eq!(emit_stmt(&s, 0), "nonlocal count");
873 }
874 #[test]
875 pub(super) fn test_stmt_match() {
876 let s = PythonStmt::Match(
877 PythonExpr::Var("cmd".to_string()),
878 vec![
879 MatchArm {
880 pattern: "\"quit\"".to_string(),
881 guard: None,
882 body: vec![PythonStmt::Return(None)],
883 },
884 MatchArm {
885 pattern: "_".to_string(),
886 guard: None,
887 body: vec![PythonStmt::Pass],
888 },
889 ],
890 );
891 let out = emit_stmt(&s, 0);
892 assert!(out.contains("match cmd:"));
893 assert!(out.contains("case \"quit\":"));
894 assert!(out.contains("case _:"));
895 }
896 #[test]
897 pub(super) fn test_stmt_docstring() {
898 let s = PythonStmt::Docstring("This is a docstring.".to_string());
899 let out = emit_stmt(&s, 0);
900 assert!(out.contains("\"\"\"This is a docstring.\"\"\""));
901 }
902 #[test]
903 pub(super) fn test_function_simple() {
904 let mut func = PythonFunction::new("add");
905 func.params = vec![
906 PythonParam::typed("x", PythonType::Int),
907 PythonParam::typed("y", PythonType::Int),
908 ];
909 func.return_type = Some(PythonType::Int);
910 func.body = vec![PythonStmt::Return(Some(PythonExpr::BinOp(
911 "+".to_string(),
912 Box::new(PythonExpr::Var("x".to_string())),
913 Box::new(PythonExpr::Var("y".to_string())),
914 )))];
915 let out = emit_function(&func, 0);
916 assert!(out.contains("def add(x: int, y: int) -> int:"));
917 assert!(out.contains("return (x + y)"));
918 }
919 #[test]
920 pub(super) fn test_function_async() {
921 let mut func = PythonFunction::new("fetch");
922 func.params = vec![PythonParam::typed("url", PythonType::Str)];
923 func.return_type = Some(PythonType::Custom("bytes".to_string()));
924 func.is_async = true;
925 func.body = vec![PythonStmt::Return(Some(PythonExpr::Await(Box::new(
926 PythonExpr::Var("response".to_string()),
927 ))))];
928 let out = emit_function(&func, 0);
929 assert!(out.contains("async def fetch(url: str) -> bytes:"));
930 assert!(out.contains("return await response"));
931 }
932 #[test]
933 pub(super) fn test_function_with_decorator() {
934 let mut func = PythonFunction::new("value");
935 func.params = vec![PythonParam::typed(
936 "self",
937 PythonType::Custom("Self".to_string()),
938 )];
939 func.return_type = Some(PythonType::Int);
940 func.decorators = vec!["property".to_string()];
941 func.body = vec![PythonStmt::Return(Some(PythonExpr::Attr(
942 Box::new(PythonExpr::Var("self".to_string())),
943 "_value".to_string(),
944 )))];
945 let out = emit_function(&func, 0);
946 assert!(out.contains("@property"));
947 assert!(out.contains("def value(self: Self) -> int:"));
948 }
949 #[test]
950 pub(super) fn test_function_empty_body_pass() {
951 let func = PythonFunction::new("noop");
952 let out = emit_function(&func, 0);
953 assert!(out.contains("def noop():"));
954 assert!(out.contains("pass"));
955 }
956 #[test]
957 pub(super) fn test_class_simple() {
958 let cls = PythonClass::new("MyClass");
959 let out = emit_class(&cls, 0);
960 assert!(out.contains("class MyClass:"));
961 assert!(out.contains("pass"));
962 }
963 #[test]
964 pub(super) fn test_class_with_bases() {
965 let mut cls = PythonClass::new("MyError");
966 cls.bases = vec!["Exception".to_string()];
967 let out = emit_class(&cls, 0);
968 assert!(out.contains("class MyError(Exception):"));
969 }
970 #[test]
971 pub(super) fn test_class_dataclass() {
972 let mut cls = PythonClass::new("Point");
973 cls.is_dataclass = true;
974 cls.class_vars = vec![
975 PythonClassVar {
976 name: "x".to_string(),
977 annotation: PythonType::Float,
978 default: None,
979 },
980 PythonClassVar {
981 name: "y".to_string(),
982 annotation: PythonType::Float,
983 default: Some(PythonExpr::Lit(PythonLit::Float(0.0))),
984 },
985 ];
986 let out = emit_class(&cls, 0);
987 assert!(out.contains("@dataclass"));
988 assert!(out.contains("class Point:"));
989 assert!(out.contains("x: float"));
990 assert!(out.contains("y: float = 0.0"));
991 }
992 #[test]
993 pub(super) fn test_class_with_method() {
994 let mut cls = PythonClass::new("Counter");
995 let mut method = PythonFunction::new("increment");
996 method.params = vec![PythonParam::simple("self")];
997 method.return_type = Some(PythonType::None_);
998 method.body = vec![PythonStmt::AugAssign(
999 PythonExpr::Attr(
1000 Box::new(PythonExpr::Var("self".to_string())),
1001 "count".to_string(),
1002 ),
1003 "+".to_string(),
1004 PythonExpr::Lit(PythonLit::Int(1)),
1005 )];
1006 cls.methods.push(method);
1007 let out = emit_class(&cls, 0);
1008 assert!(out.contains("class Counter:"));
1009 assert!(out.contains("def increment(self) -> None:"));
1010 }
1011 #[test]
1012 pub(super) fn test_class_abstract() {
1013 let mut cls = PythonClass::new("Animal");
1014 cls.is_abstract = true;
1015 let out = emit_class(&cls, 0);
1016 assert!(out.contains("class Animal(ABC):"));
1017 }
1018 #[test]
1019 pub(super) fn test_module_empty() {
1020 let module = PythonModule::new();
1021 let out = module.emit();
1022 assert!(out.is_empty() || !out.contains("syntax error"));
1023 }
1024 #[test]
1025 pub(super) fn test_module_with_imports() {
1026 let mut module = PythonModule::new();
1027 module.add_import("os", None);
1028 module.add_import("sys", None);
1029 let out = module.emit();
1030 assert!(out.contains("import os"));
1031 assert!(out.contains("import sys"));
1032 }
1033 #[test]
1034 pub(super) fn test_module_with_from_imports() {
1035 let mut module = PythonModule::new();
1036 module.add_from_import("pathlib", vec![("Path".to_string(), None)]);
1037 let out = module.emit();
1038 assert!(out.contains("from pathlib import Path"));
1039 }
1040 #[test]
1041 pub(super) fn test_module_with_all_exports() {
1042 let mut module = PythonModule::new();
1043 module.all_exports = vec!["MyClass".to_string(), "my_func".to_string()];
1044 let out = module.emit();
1045 assert!(out.contains("__all__"));
1046 assert!(out.contains("\"MyClass\""));
1047 assert!(out.contains("\"my_func\""));
1048 }
1049 #[test]
1050 pub(super) fn test_module_with_docstring() {
1051 let mut module = PythonModule::new();
1052 module.module_docstring = Some("My module documentation.".to_string());
1053 let out = module.emit();
1054 assert!(out.contains("\"\"\"My module documentation.\"\"\""));
1055 }
1056 #[test]
1057 pub(super) fn test_mangle_dots() {
1058 let backend = PythonBackend::new();
1059 assert_eq!(backend.mangle_name("Nat.add"), "Nat_add");
1060 }
1061 #[test]
1062 pub(super) fn test_mangle_keyword() {
1063 let backend = PythonBackend::new();
1064 assert_eq!(backend.mangle_name("class"), "_class");
1065 }
1066 #[test]
1067 pub(super) fn test_mangle_digit_start() {
1068 let backend = PythonBackend::new();
1069 assert_eq!(backend.mangle_name("3d"), "_3d");
1070 }
1071 #[test]
1072 pub(super) fn test_mangle_prime() {
1073 let backend = PythonBackend::new();
1074 assert_eq!(backend.mangle_name("f'"), "f_");
1075 }
1076 #[test]
1077 pub(super) fn test_mangle_empty() {
1078 let backend = PythonBackend::new();
1079 assert_eq!(backend.mangle_name(""), "_anon");
1080 }
1081 #[test]
1082 pub(super) fn test_param_simple() {
1083 let p = PythonParam::simple("x");
1084 assert_eq!(p.to_string(), "x");
1085 }
1086 #[test]
1087 pub(super) fn test_param_typed() {
1088 let p = PythonParam::typed("count", PythonType::Int);
1089 assert_eq!(p.to_string(), "count: int");
1090 }
1091 #[test]
1092 pub(super) fn test_param_with_default() {
1093 let mut p = PythonParam::typed("n", PythonType::Int);
1094 p.default = Some(PythonExpr::Lit(PythonLit::Int(0)));
1095 assert_eq!(p.to_string(), "n: int = 0");
1096 }
1097 #[test]
1098 pub(super) fn test_param_vararg() {
1099 let mut p = PythonParam::simple("args");
1100 p.is_vararg = true;
1101 assert_eq!(p.to_string(), "*args");
1102 }
1103 #[test]
1104 pub(super) fn test_param_kwarg() {
1105 let mut p = PythonParam::simple("kwargs");
1106 p.is_kwarg = true;
1107 assert_eq!(p.to_string(), "**kwargs");
1108 }
1109 #[test]
1110 pub(super) fn test_full_module_with_dataclass_and_function() {
1111 let mut module = PythonModule::new();
1112 module.add_import("dataclasses", None);
1113 module.add_from_import("dataclasses", vec![("dataclass".to_string(), None)]);
1114 let mut cls = PythonClass::new("Point");
1115 cls.is_dataclass = true;
1116 cls.class_vars = vec![
1117 PythonClassVar {
1118 name: "x".to_string(),
1119 annotation: PythonType::Float,
1120 default: None,
1121 },
1122 PythonClassVar {
1123 name: "y".to_string(),
1124 annotation: PythonType::Float,
1125 default: None,
1126 },
1127 ];
1128 module.add_class(cls);
1129 let mut func = PythonFunction::new("distance");
1130 func.params = vec![
1131 PythonParam::typed("p1", PythonType::Custom("Point".to_string())),
1132 PythonParam::typed("p2", PythonType::Custom("Point".to_string())),
1133 ];
1134 func.return_type = Some(PythonType::Float);
1135 func.body = vec![
1136 PythonStmt::AnnAssign(
1137 "dx".to_string(),
1138 PythonType::Float,
1139 Some(PythonExpr::BinOp(
1140 "-".to_string(),
1141 Box::new(PythonExpr::Attr(
1142 Box::new(PythonExpr::Var("p1".to_string())),
1143 "x".to_string(),
1144 )),
1145 Box::new(PythonExpr::Attr(
1146 Box::new(PythonExpr::Var("p2".to_string())),
1147 "x".to_string(),
1148 )),
1149 )),
1150 ),
1151 PythonStmt::Return(Some(PythonExpr::Call(
1152 Box::new(PythonExpr::Attr(
1153 Box::new(PythonExpr::Var("math".to_string())),
1154 "sqrt".to_string(),
1155 )),
1156 vec![PythonExpr::BinOp(
1157 "+".to_string(),
1158 Box::new(PythonExpr::BinOp(
1159 "**".to_string(),
1160 Box::new(PythonExpr::Var("dx".to_string())),
1161 Box::new(PythonExpr::Lit(PythonLit::Int(2))),
1162 )),
1163 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
1164 )],
1165 vec![],
1166 ))),
1167 ];
1168 module.add_function(func);
1169 let out = module.emit();
1170 assert!(out.contains("@dataclass"));
1171 assert!(out.contains("class Point:"));
1172 assert!(out.contains("x: float"));
1173 assert!(out.contains("def distance(p1: Point, p2: Point) -> float:"));
1174 assert!(out.contains("dx: float = (p1.x - p2.x)"));
1175 }
1176 #[test]
1177 pub(super) fn test_match_with_guard() {
1178 let s = PythonStmt::Match(
1179 PythonExpr::Var("point".to_string()),
1180 vec![MatchArm {
1181 pattern: "Point(x, y)".to_string(),
1182 guard: Some(PythonExpr::BinOp(
1183 ">".to_string(),
1184 Box::new(PythonExpr::Var("x".to_string())),
1185 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
1186 )),
1187 body: vec![PythonStmt::Pass],
1188 }],
1189 );
1190 let out = emit_stmt(&s, 0);
1191 assert!(out.contains("match point:"));
1192 assert!(out.contains("case Point(x, y) if (x > 0):"));
1193 }
1194 #[test]
1195 pub(super) fn test_indented_function_in_class() {
1196 let mut cls = PythonClass::new("Foo");
1197 let mut method = PythonFunction::new("bar");
1198 method.params = vec![PythonParam::simple("self")];
1199 method.body = vec![PythonStmt::Return(Some(PythonExpr::Lit(PythonLit::Int(
1200 42,
1201 ))))];
1202 cls.methods.push(method);
1203 let out = emit_class(&cls, 0);
1204 assert!(out.contains(" def bar(self):"));
1205 assert!(out.contains(" return 42"));
1206 }
1207 #[test]
1208 pub(super) fn test_type_set() {
1209 let t = PythonType::Set(Box::new(PythonType::Str));
1210 assert_eq!(t.to_string(), "set[str]");
1211 }
1212 #[test]
1213 pub(super) fn test_type_iterator() {
1214 let t = PythonType::Iterator(Box::new(PythonType::Int));
1215 assert_eq!(t.to_string(), "Iterator[int]");
1216 }
1217 #[test]
1218 pub(super) fn test_type_callable() {
1219 assert_eq!(PythonType::Callable.to_string(), "Callable");
1220 }
1221 #[test]
1222 pub(super) fn test_expr_yield_from() {
1223 let e = PythonExpr::YieldFrom(Box::new(PythonExpr::Var("gen".to_string())));
1224 assert_eq!(e.to_string(), "yield from gen");
1225 }
1226 #[test]
1227 pub(super) fn test_expr_slice() {
1228 let e = PythonExpr::Slice(
1229 Some(Box::new(PythonExpr::Lit(PythonLit::Int(1)))),
1230 Some(Box::new(PythonExpr::Lit(PythonLit::Int(5)))),
1231 None,
1232 );
1233 assert_eq!(e.to_string(), "1:5");
1234 }
1235 #[test]
1236 pub(super) fn test_expr_slice_with_step() {
1237 let e = PythonExpr::Slice(
1238 None,
1239 None,
1240 Some(Box::new(PythonExpr::Lit(PythonLit::Int(2)))),
1241 );
1242 assert_eq!(e.to_string(), "::2");
1243 }
1244 #[test]
1245 pub(super) fn test_expr_set_comp() {
1246 let e = PythonExpr::SetComp(
1247 Box::new(PythonExpr::Var("x".to_string())),
1248 "x".to_string(),
1249 Box::new(PythonExpr::Var("items".to_string())),
1250 None,
1251 );
1252 assert_eq!(e.to_string(), "{x for x in items}");
1253 }
1254 #[test]
1255 pub(super) fn test_expr_gen_expr() {
1256 let e = PythonExpr::GenExpr(
1257 Box::new(PythonExpr::Var("x".to_string())),
1258 "x".to_string(),
1259 Box::new(PythonExpr::Var("nums".to_string())),
1260 Some(Box::new(PythonExpr::BinOp(
1261 ">".to_string(),
1262 Box::new(PythonExpr::Var("x".to_string())),
1263 Box::new(PythonExpr::Lit(PythonLit::Int(0))),
1264 ))),
1265 );
1266 assert_eq!(e.to_string(), "(x for x in nums if (x > 0))");
1267 }
1268 #[test]
1269 pub(super) fn test_stmt_continue_break() {
1270 assert_eq!(emit_stmt(&PythonStmt::Continue, 0), "continue");
1271 assert_eq!(emit_stmt(&PythonStmt::Break, 0), "break");
1272 }
1273 #[test]
1274 pub(super) fn test_stmt_indented() {
1275 let s = PythonStmt::Return(Some(PythonExpr::Lit(PythonLit::Int(1))));
1276 assert_eq!(emit_stmt(&s, 1), " return 1");
1277 assert_eq!(emit_stmt(&s, 2), " return 1");
1278 }
1279 #[test]
1280 pub(super) fn test_fresh_var() {
1281 let mut backend = PythonBackend::new();
1282 assert_eq!(backend.fresh_var(), "_t0");
1283 assert_eq!(backend.fresh_var(), "_t1");
1284 assert_eq!(backend.fresh_var(), "_t2");
1285 }
1286 #[test]
1287 pub(super) fn test_module_default() {
1288 let m = PythonModule::default();
1289 assert!(m.imports.is_empty());
1290 assert!(m.functions.is_empty());
1291 assert!(m.classes.is_empty());
1292 }
1293 #[test]
1294 pub(super) fn test_backend_default() {
1295 let b = PythonBackend::default();
1296 assert_eq!(b.fresh_counter, 0);
1297 assert!(b.fn_map.is_empty());
1298 }
1299}