1use std::{collections::HashMap, default::Default};
2
3use tracing::info;
4use proc_macro2::TokenStream;
5use pyo3::{Borrowed, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
6use quote::{format_ident, quote};
7use serde::{Deserialize, Serialize};
8
9use crate::{CodeGen, CodeGenContext, Name, Object, PythonOptions, Statement, StatementType, ExprType, SymbolTableScopes};
10
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub enum Type {
14 Unimplemented,
15}
16
17impl<'a, 'py> FromPyObject<'a, 'py> for Type {
18 type Error = pyo3::PyErr;
19 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
20 info!("Type: {:?}", ob);
21 Ok(Type::Unimplemented)
22 }
23}
24
25#[derive(Clone, Debug, Default, Serialize, Deserialize)]
27pub struct RawModule {
28 pub body: Vec<Statement>,
29 pub type_ignores: Vec<Type>,
30}
31
32impl<'a, 'py> FromPyObject<'a, 'py> for RawModule {
36 type Error = pyo3::PyErr;
37 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
38 let body_attr = ob
39 .getattr("body")
40 .map_err(|e| crate::extraction_failure("module body", &ob, e))?;
41 let body_list: Vec<pyo3::Bound<PyAny>> = body_attr
42 .extract()
43 .map_err(|e| crate::extraction_failure("module body", &ob, e))?;
44
45 let mut body = Vec::with_capacity(body_list.len());
46 for stmt in &body_list {
47 body.push(Statement::extract(stmt.as_borrowed())?);
48 }
49
50 let type_ignores = ob
51 .getattr("type_ignores")
52 .and_then(|t| t.extract())
53 .unwrap_or_default();
54
55 Ok(Self { body, type_ignores })
56 }
57}
58
59#[derive(Clone, Debug, Default, Serialize, Deserialize)]
61pub struct Module {
62 pub raw: RawModule,
63 pub name: Option<Name>,
64 pub doc: Option<String>,
65 pub filename: Option<String>,
66 pub attributes: HashMap<Name, String>,
67}
68
69impl<'a, 'py> FromPyObject<'a, 'py> for Module {
70 type Error = pyo3::PyErr;
71 fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
72 let raw_module = ob.extract()?;
75
76 Ok(Self {
77 raw: raw_module,
78 ..Default::default()
79 })
80 }
81}
82
83impl CodeGen for Module {
84 type Context = CodeGenContext;
85 type Options = PythonOptions;
86 type SymbolTable = SymbolTableScopes;
87
88 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
89 let mut symbols = symbols;
90 symbols.new_scope();
91 for s in self.raw.body {
92 symbols = s.clone().find_symbols(symbols);
93 }
94 symbols
95 }
96
97 fn to_rust(
98 self,
99 ctx: Self::Context,
100 options: Self::Options,
101 symbols: Self::SymbolTable,
102 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
103 let mut stream = TokenStream::new();
104
105 let module_filename = self
108 .filename
109 .clone()
110 .or_else(|| self.name.as_ref().map(|n| format!("{}.py", n.id)))
111 .unwrap_or_else(|| "<module>".to_string());
112
113 if let Some(docstring) = self.get_module_docstring() {
115 if self.raw.body.len() > 1 || self.looks_like_module_docstring() {
117 let doc_lines: Vec<_> = docstring
118 .lines()
119 .map(|line| {
120 if line.trim().is_empty() {
121 quote! { #![doc = ""] }
122 } else {
123 let doc_line = format!("{}", line);
124 quote! { #![doc = #doc_line] }
125 }
126 })
127 .collect();
128 stream.extend(quote! { #(#doc_lines)* });
129
130 let generated_comment =
132 format!("Generated from Python file: {}", module_filename);
133 stream.extend(quote! { #![doc = #generated_comment] });
134 }
135 }
136
137 if options.with_std_python {
138 stream.extend(quote!(use stdpython::*;));
141 }
142
143 if options.no_std {
151 stream.extend(quote! {
152 extern crate alloc;
153 #[allow(unused_imports)]
154 use alloc::{
155 format, vec, borrow::ToOwned, string::String, string::ToString,
156 vec::Vec,
157 };
158 });
159 }
160
161 let needs_async_runtime = self.raw.body.iter().any(|s| {
164 matches!(&s.statement, crate::StatementType::AsyncFunctionDef(_))
165 });
166
167 if needs_async_runtime {
168 let runtime_import = format_ident!("{}", options.async_runtime.import());
169 stream.extend(quote!(use #runtime_import;));
170 }
171
172 let mut main_body_stmts = Vec::new();
173 let mut has_main_code = false;
174 let mut has_async_functions = false;
175 let mut module_init_stmts = Vec::new();
176 let mut has_module_init_code = false;
177 let mut is_simple_main_call_pattern = false;
178 let mut main_body_raw: Vec<crate::Statement> = Vec::new();
182 let mut module_init_raw: Vec<crate::Statement> = Vec::new();
183
184 let mut module_assign_counts: std::collections::HashMap<String, usize> =
194 std::collections::HashMap::new();
195 count_module_stores(&self.raw.body, &mut module_assign_counts);
196
197 let user_main_returns_value = self.raw.body.iter().any(|s| {
202 matches!(
203 &s.statement,
204 crate::StatementType::FunctionDef(f)
205 if f.name == "main" && f.resolved_return_type().is_some()
206 )
207 });
208
209 for s in self.raw.body {
210 if let crate::StatementType::AsyncFunctionDef(_) = &s.statement {
212 has_async_functions = true;
213 }
214
215 if let crate::StatementType::If(if_stmt) = &s.statement {
217 let test_str = format!("{:?}", if_stmt.test);
218 if test_str.contains("__name__") && test_str.contains("__main__") {
219 let is_simple_main_call = Self::is_simple_main_call_block(&if_stmt.body)
221 && !user_main_returns_value;
222
223 if is_simple_main_call {
224 has_main_code = true;
227 is_simple_main_call_pattern = true;
228 } else {
230 for body_stmt in &if_stmt.body {
232 let stmt_token = body_stmt
233 .clone()
234 .to_rust(ctx.clone(), options.clone(), symbols.clone())
235 .map_err(|e| wrap_module_error(&module_filename, e))?;
236 if !stmt_token.to_string().trim().is_empty() {
237 main_body_stmts.push(stmt_token);
238 main_body_raw.push(body_stmt.clone());
239 has_main_code = true;
240 }
241 }
242 }
243 continue;
245 }
246 }
247
248 if let crate::StatementType::Assign(a) = &s.statement {
251 if let [crate::ExprType::Name(n)] = a.targets.as_slice() {
252 if module_assign_counts.get(&n.id) == Some(&1) {
253 if let Some(ty) = const_static_type(&a.value) {
254 let ident = crate::safe_ident(&n.id);
255 let value = a.value.clone().to_rust(
256 ctx.clone(),
257 options.clone(),
258 symbols.clone(),
259 )?;
260 stream.extend(quote!(pub static #ident: #ty = #value;));
261 continue;
262 }
263 }
264 }
265 }
266
267 let is_declaration = Self::is_declaration_statement(&s.statement);
269
270 let statement = s
271 .clone()
272 .to_rust(ctx.clone(), options.clone(), symbols.clone())
273 .map_err(|e| wrap_module_error(&module_filename, e))?;
274
275 if statement.to_string() != "" {
276 if is_declaration {
277 stream.extend(statement);
279 } else {
280 module_init_stmts.push(statement);
282 module_init_raw.push(s.clone());
283 has_module_init_code = true;
284 }
285 }
286 }
287
288 let init_decls = hoisted_declarations(&module_init_raw, &ctx, &symbols);
291 if !init_decls.is_empty() {
292 module_init_stmts.insert(0, init_decls);
293 }
294 let main_decls = hoisted_declarations(&main_body_raw, &ctx, &symbols);
295 if !main_decls.is_empty() {
296 main_body_stmts.insert(0, main_decls);
297 }
298
299 if has_module_init_code {
303 stream.extend(quote! {
304 fn __module_init__() -> Result<(), PyException> {
305 #(#module_init_stmts;)*
306 Ok(())
307 }
308 });
309 }
310
311 if has_main_code && options.no_std {
315 return Err(
316 "`if __name__ == \"__main__\":` needs a process entry point, which \
317 the no_std profile does not provide; remove the block (convert the \
318 module as a library) or convert without the no_std profile"
319 .to_string()
320 .into(),
321 );
322 }
323
324 if has_main_code {
326 if is_simple_main_call_pattern {
327 let stream_str = stream.to_string();
330
331 let user_main_is_async = stream_str.contains("pub async fn main (");
333
334 if user_main_is_async {
335 let runtime_attr = options.async_runtime.main_attribute();
337 let attr_tokens: proc_macro2::TokenStream = runtime_attr.parse()
338 .unwrap_or_else(|_| quote!(tokio::main)); let new_stream_str = stream_str
342 .replace("pub async fn main (", &format!("#[{}] async fn main(", runtime_attr));
343 stream = new_stream_str.parse::<proc_macro2::TokenStream>()
344 .unwrap_or_else(|_| stream);
345
346 if has_module_init_code {
348 let renamed_stream_str = Self::rename_main_function_and_references(&stream_str);
351 stream = renamed_stream_str.parse::<proc_macro2::TokenStream>()
352 .unwrap_or_else(|_| stream);
353
354 stream.extend(quote! {
355 #[#attr_tokens]
356 async fn main() {
357 let __rython_result: Result<(), PyException> = async {
358 __module_init__()?;
359 python_main().await?;
360 Ok(())
361 }.await;
362 if let Err(e) = __rython_result {
363 eprintln!("{}", e);
364 std::process::exit(1);
365 }
366 }
367 });
368 }
369 } else {
370 let new_stream_str = Self::convert_python_main_to_rust_entry_point(&stream_str);
373 stream = new_stream_str.parse::<proc_macro2::TokenStream>()
374 .unwrap_or_else(|_| stream);
375
376 if has_module_init_code {
378 let renamed_stream_str = Self::rename_main_function_and_references(&stream_str);
380 stream = renamed_stream_str.parse::<proc_macro2::TokenStream>()
381 .unwrap_or_else(|_| stream);
382
383 stream.extend(quote! {
384 fn main() {
385 let __rython_result = (|| -> Result<(), PyException> {
386 __module_init__()?;
387 python_main()?;
388 Ok(())
389 })();
390 if let Err(e) = __rython_result {
391 eprintln!("{}", e);
392 std::process::exit(1);
393 }
394 }
395 });
396 }
397 }
398 } else {
399 let stream_str = stream.to_string();
401 let has_python_main = stream_str.contains("pub fn main (") || stream_str.contains("pub async fn main (");
402
403 if has_python_main {
404 let new_stream_str = Self::rename_main_function_and_references(&stream_str);
406 stream = new_stream_str.parse::<proc_macro2::TokenStream>()
407 .unwrap_or_else(|_| stream);
408
409 for stmt in &mut main_body_stmts {
411 let stmt_str = stmt.to_string();
412 let updated_stmt_str = Self::update_main_references(&stmt_str);
413 if updated_stmt_str != stmt_str {
414 if let Ok(new_stmt) = updated_stmt_str.parse::<proc_macro2::TokenStream>() {
415 *stmt = new_stmt;
416 }
417 }
418 }
419 }
420
421 if needs_async_runtime || has_async_functions {
423 let runtime_attr = options.async_runtime.main_attribute();
425 let attr_tokens: proc_macro2::TokenStream = runtime_attr.parse()
426 .unwrap_or_else(|_| quote!(tokio::main)); let init_call = if has_module_init_code {
429 quote!(__module_init__()?;)
430 } else {
431 quote!()
432 };
433 stream.extend(quote! {
434 #[#attr_tokens]
435 async fn main() {
436 let __rython_result: Result<(), PyException> = async {
437 #init_call
438 #(#main_body_stmts;)*
439 Ok(())
440 }.await;
441 if let Err(e) = __rython_result {
442 eprintln!("{}", e);
443 std::process::exit(1);
444 }
445 }
446 });
447 } else {
448 let init_call = if has_module_init_code {
449 quote!(__module_init__()?;)
450 } else {
451 quote!()
452 };
453 stream.extend(quote! {
454 fn main() {
455 let __rython_result = (|| -> Result<(), PyException> {
456 #init_call
457 #(#main_body_stmts;)*
458 Ok(())
459 })();
460 if let Err(e) = __rython_result {
461 eprintln!("{}", e);
462 std::process::exit(1);
463 }
464 }
465 });
466 }
467 }
468 } else if has_module_init_code {
469 stream.extend(quote! {
472 fn main() {
473 if let Err(e) = __module_init__() {
474 eprintln!("{}", e);
475 std::process::exit(1);
476 }
477 }
478 });
479 }
480 Ok(stream)
481 }
482}
483
484fn count_module_stores(
492 body: &[crate::Statement],
493 counts: &mut std::collections::HashMap<String, usize>,
494) {
495 fn bump_target(target: &crate::ExprType, by: usize, counts: &mut std::collections::HashMap<String, usize>) {
496 match target {
497 crate::ExprType::Name(n) => {
498 *counts.entry(n.id.clone()).or_insert(0) += by;
499 }
500 crate::ExprType::Tuple(t) => {
501 for elt in &t.elts {
502 bump_target(elt, by, counts);
503 }
504 }
505 _ => {}
506 }
507 }
508 for s in body {
509 match &s.statement {
510 crate::StatementType::Assign(a) => {
511 for target in &a.targets {
512 bump_target(target, 1, counts);
513 }
514 }
515 crate::StatementType::AugAssign(a) => bump_target(&a.target, 2, counts),
516 crate::StatementType::If(i) => {
517 let mut nested = std::collections::HashMap::new();
518 count_module_stores(&i.body, &mut nested);
519 count_module_stores(&i.orelse, &mut nested);
520 for (name, n) in nested {
521 *counts.entry(name).or_insert(0) += n * 2;
522 }
523 }
524 crate::StatementType::While(w) => {
525 let mut nested = std::collections::HashMap::new();
526 count_module_stores(&w.body, &mut nested);
527 count_module_stores(&w.orelse, &mut nested);
528 for (name, n) in nested {
529 *counts.entry(name).or_insert(0) += n * 2;
530 }
531 }
532 crate::StatementType::For(f) => {
533 bump_target(&f.target, 2, counts);
534 let mut nested = std::collections::HashMap::new();
535 count_module_stores(&f.body, &mut nested);
536 count_module_stores(&f.orelse, &mut nested);
537 for (name, n) in nested {
538 *counts.entry(name).or_insert(0) += n * 2;
539 }
540 }
541 crate::StatementType::With(w) => {
542 for item in &w.items {
543 if let Some(vars) = &item.optional_vars {
544 bump_target(vars, 2, counts);
545 }
546 }
547 let mut nested = std::collections::HashMap::new();
548 count_module_stores(&w.body, &mut nested);
549 for (name, n) in nested {
550 *counts.entry(name).or_insert(0) += n * 2;
551 }
552 }
553 crate::StatementType::Try(t) => {
554 let mut nested = std::collections::HashMap::new();
555 count_module_stores(&t.body, &mut nested);
556 for h in &t.handlers {
557 count_module_stores(&h.body, &mut nested);
558 }
559 count_module_stores(&t.orelse, &mut nested);
560 count_module_stores(&t.finalbody, &mut nested);
561 for (name, n) in nested {
562 *counts.entry(name).or_insert(0) += n * 2;
563 }
564 }
565 _ => {}
567 }
568 }
569}
570
571fn const_static_type(value: &crate::ExprType) -> Option<TokenStream> {
577 match value {
578 crate::ExprType::Constant(c) => match &c.0 {
579 Some(litrs::Literal::Integer(_)) => Some(quote!(i64)),
580 Some(litrs::Literal::Float(_)) => Some(quote!(f64)),
581 Some(litrs::Literal::Bool(_)) => Some(quote!(bool)),
582 Some(litrs::Literal::String(_)) => Some(quote!(&'static str)),
583 _ => None,
584 },
585 crate::ExprType::UnaryOp(op) => {
586 if !matches!(op.op, crate::ast::tree::unary_op::Ops::USub) {
587 return None;
588 }
589 match const_static_type(&op.operand) {
590 Some(ty) if ty.to_string() == "i64" || ty.to_string() == "f64" => Some(ty),
591 _ => None,
592 }
593 }
594 _ => None,
595 }
596}
597
598fn hoisted_declarations(
602 body: &[crate::Statement],
603 ctx: &crate::CodeGenContext,
604 symbols: &crate::SymbolTableScopes,
605) -> TokenStream {
606 let mut symbols = symbols.clone();
609 for s in body {
610 symbols = s.clone().find_symbols(symbols);
611 }
612 let scope =
613 crate::analyze_scope_with(body, &[], &crate::class_call_resolver(ctx, &symbols));
614 let mut out = TokenStream::new();
615 for name in &scope.assigned {
616 let ident = crate::safe_ident(name);
617 if scope.needs_mut.contains(name) {
618 out.extend(quote!(let mut #ident;));
619 } else {
620 out.extend(quote!(let #ident;));
621 }
622 }
623 out
624}
625
626fn wrap_module_error(
632 filename: &str,
633 e: Box<dyn std::error::Error>,
634) -> Box<dyn std::error::Error> {
635 if let Some(inner) = e.downcast_ref::<crate::Error>() {
636 let message = inner.get_field("message").unwrap_or_default().to_string();
637 let location = inner
638 .get_field("location")
639 .unwrap_or("<module>")
640 .replace("<module>", filename);
641 let help = inner.get_field("help").unwrap_or_default().to_string();
642 return Box::from(crate::codegen_error(
643 crate::SourceLocation::new(location),
644 message,
645 help,
646 ));
647 }
648 Box::from(crate::codegen_error(
649 crate::SourceLocation::new(filename),
650 crate::format_error_chain(e.as_ref()),
651 "",
652 ))
653}
654
655impl Module {
656 fn is_simple_main_call_block(body: &[crate::Statement]) -> bool {
662 if body.len() != 1 {
664 return false;
665 }
666
667 let stmt = &body[0];
668 match &stmt.statement {
669 crate::StatementType::Expr(expr) => {
671 Self::is_main_function_call(&expr.value)
672 },
673 crate::StatementType::Assign(assign) => {
675 assign.targets.len() == 1 && Self::is_main_function_call(&assign.value)
677 },
678 crate::StatementType::Call(call) => {
680 call.args.iter().any(|arg| Self::is_main_function_call(arg))
682 },
683 _ => false,
684 }
685 }
686
687 fn is_main_function_call(expr: &crate::ExprType) -> bool {
689 match expr {
690 crate::ExprType::Call(call) => {
691 match call.func.as_ref() {
692 crate::ExprType::Name(name) => name.id == "main",
693 _ => false,
694 }
695 },
696 _ => false,
697 }
698 }
699
700 fn is_declaration_statement(stmt_type: &crate::StatementType) -> bool {
702 use crate::StatementType::*;
703 match stmt_type {
704 FunctionDef(_) | AsyncFunctionDef(_) | ClassDef(_) | Import(_) | ImportFrom(_) => true,
706
707 Expr(expr) => Self::is_simple_expression(&expr.value),
710
711 Assign(_) | AugAssign(_) | Call(_) | Return(_) |
713 If(_) | For(_) | While(_) | Try(_) | With(_) | AsyncWith(_) | AsyncFor(_) |
714 Raise(_) | Assert { .. } | Pass | Break | Continue => false,
715
716 Unimplemented(_) => false,
718 }
719 }
720
721 fn is_simple_expression(expr: &crate::ExprType) -> bool {
723 use crate::ExprType::*;
724 match expr {
725 Constant(_) | Name(_) | NoneType(_) => true,
727
728 UnaryOp(_) => true,
730
731 Call(_) | BinOp(_) | Compare(_) | BoolOp(_) |
733 IfExp(_) | Dict(_) | Set(_) | List(_) | Tuple(_) | ListComp(_) |
734 Lambda(_) | Attribute(_) | Subscript(_) | Starred(_) |
735 DictComp(_) | SetComp(_) | GeneratorExp(_) | Await(_) |
736 Yield(_) | YieldFrom(_) | FormattedValue(_) | JoinedStr(_) |
737 NamedExpr(_) => false,
738
739 Unimplemented(_) | Unknown => false,
741 }
742 }
743
744 fn rename_main_function_and_references(code: &str) -> String {
746 let code = code
748 .replace("pub async fn main (", "pub async fn python_main (")
749 .replace("pub fn main (", "pub fn python_main (");
750
751 Self::update_main_references(&code)
753 }
754
755 fn convert_python_main_to_rust_entry_point(code: &str) -> String {
758 use regex::Regex;
759
760 let code = code.replace("pub fn main (", "fn main(");
762
763 let main_fn_pattern = Regex::new(r"fn main\(\s*\)\s*\{([^}]*)\}").unwrap();
766
767 if let Some(captures) = main_fn_pattern.captures(&code) {
768 let body = captures.get(1).map_or("", |m| m.as_str());
769
770 if body.contains("return ") {
772 let new_code = code.replace("fn main(", "fn python_main(");
774 format!("{}\n\nfn main() {{\n let _ = python_main();\n}}", new_code)
775 } else {
776 code
778 }
779 } else {
780 code
782 }
783 }
784
785 fn update_main_references(code: &str) -> String {
788 use regex::Regex;
789
790 let call_pattern = Regex::new(r"\bmain\s*\(").unwrap();
793 let mut result = call_pattern.replace_all(code, "python_main(").to_string();
794
795 let method_pattern = Regex::new(r"\.call_main\s*\(").unwrap();
797 result = method_pattern.replace_all(&result, ".call_python_main(").to_string();
798
799 let assignment_pattern = Regex::new(r"=\s+main\b").unwrap();
802 result = assignment_pattern.replace_all(&result, "= python_main").to_string();
803
804 let return_pattern = Regex::new(r"return\s+main\b").unwrap();
806 result = return_pattern.replace_all(&result, "return python_main").to_string();
807
808 result
809 }
810
811 fn get_module_docstring(&self) -> Option<String> {
812 if self.raw.body.is_empty() {
813 return None;
814 }
815
816 let first_stmt = &self.raw.body[0];
818 match &first_stmt.statement {
819 StatementType::Expr(expr) => match &expr.value {
820 ExprType::Constant(c) => {
821 let raw_string = c.to_string();
822 Some(self.format_module_docstring(&raw_string))
823 },
824 _ => None,
825 },
826 _ => None,
827 }
828 }
829
830 fn format_module_docstring(&self, raw: &str) -> String {
831 let content = raw.trim_matches('"');
833
834 let lines: Vec<&str> = content.lines().collect();
836 if lines.is_empty() {
837 return String::new();
838 }
839
840 let mut formatted = Vec::new();
842
843 for line in lines {
844 let cleaned = line.trim();
845 if !cleaned.is_empty() {
846 formatted.push(cleaned.to_string());
847 } else {
848 formatted.push(String::new());
849 }
850 }
851
852 formatted.join("\n")
853 }
854
855 fn looks_like_module_docstring(&self) -> bool {
856 if self.raw.body.is_empty() {
857 return false;
858 }
859
860 let first_stmt = &self.raw.body[0];
862 if let StatementType::Expr(expr) = &first_stmt.statement {
863 if let ExprType::Constant(c) = &expr.value {
864 let raw_string = c.to_string();
865 let content = raw_string.trim_matches('"');
866
867 return content.lines().count() > 1
872 || content.to_lowercase().contains("module")
873 || content.to_lowercase().contains("this ")
874 || content.len() > 50; }
876 }
877 false
878 }
879}
880
881impl Object for Module {
882 fn __dir__(&self) -> Vec<impl AsRef<str>> {
884 vec![
886 "__class__",
887 "__class_getitem__",
888 "__contains__",
889 "__delattr__",
890 "__delitem__",
891 "__dir__",
892 "__doc__",
893 "__eq__",
894 "__format__",
895 "__ge__",
896 "__getattribute__",
897 "__getitem__",
898 "__getstate__",
899 "__gt__",
900 "__hash__",
901 "__init__",
902 "__init_subclass__",
903 "__ior__",
904 "__iter__",
905 "__le__",
906 "__len__",
907 "__lt__",
908 "__ne__",
909 "__new__",
910 "__or__",
911 "__reduce__",
912 "__reduce_ex__",
913 "__repr__",
914 "__reversed__",
915 "__ror__",
916 "__setattr__",
917 "__setitem__",
918 "__sizeof__",
919 "__str__",
920 "__subclasshook__",
921 "clear",
922 "copy",
923 "fromkeys",
924 "get",
925 "items",
926 "keys",
927 "pop",
928 "popitem",
929 "setdefault",
930 "update",
931 "values",
932 ]
933 }
934}
935
936#[cfg(test)]
937mod tests {
938 use super::*;
939
940 #[test]
941 fn can_we_print() {
942 let options = PythonOptions::default();
943 let result = crate::parse(
944 "#test comment
945def foo():
946 print(\"Test print.\")
947",
948 "test_case.py",
949 )
950 .unwrap();
951 info!("Python tree: {:?}", result);
952 let code = result.to_rust(
955 CodeGenContext::Module("test_case".to_string()),
956 options,
957 SymbolTableScopes::new(),
958 );
959 info!("module: {:?}", code);
960 }
961
962 #[test]
963 fn can_we_import() {
964 let result = crate::parse("import ast", "ast.py").unwrap();
965 let options = PythonOptions::default();
966 info!("{:?}", result);
967
968 let code = result.to_rust(
969 CodeGenContext::Module("test_case".to_string()),
970 options,
971 SymbolTableScopes::new(),
972 );
973 info!("module: {:?}", code);
974 }
975
976 #[test]
977 fn can_we_import2() {
978 let result = crate::parse("import ast as test", "ast.py").unwrap();
979 let options = PythonOptions::default();
980 info!("{:?}", result);
981
982 let code = result.to_rust(
983 CodeGenContext::Module("test_case".to_string()),
984 options,
985 SymbolTableScopes::new(),
986 );
987 info!("module: {:?}", code);
988 }
989}