diff --git a/crates/erg_common/python_util.rs b/crates/erg_common/python_util.rs index d18e8e1f7..19bae09e1 100644 --- a/crates/erg_common/python_util.rs +++ b/crates/erg_common/python_util.rs @@ -596,13 +596,33 @@ fn get_poetry_virtualenv_path() -> Option { let out = if cfg!(windows) { Command::new("cmd") .arg("/C") - .arg("poetry env info -p") + .args(["poetry", "env", "info", "-p"]) .output() .ok()? } else { Command::new("sh") .arg("-c") - .arg("poetry env info -p") + .args(["poetry", "env", "info", "-p"]) + .output() + .ok()? + }; + let path = String::from_utf8(out.stdout).ok()?; + Path::new(path.trim()) + .exists() + .then_some(path.trim().to_string()) +} + +fn get_uv_python_venv_path() -> Option { + let out = if cfg!(windows) { + Command::new("cmd") + .arg("/C") + .args(["uv", "python", "find"]) + .output() + .ok()? + } else { + Command::new("sh") + .arg("-c") + .args(["uv", "python", "find"]) .output() .ok()? }; @@ -616,19 +636,32 @@ pub fn _opt_which_python() -> Result { if let Some(path) = which_python_from_toml() { return Ok(path); } - if Path::new("./.venv/bin/python").is_file() { - let path = canonicalize("./.venv/bin/python").unwrap(); - return Ok(path.to_string_lossy().to_string()); - } else if let Some(path) = get_poetry_virtualenv_path() { + if let Some(path) = get_poetry_virtualenv_path() { return Ok(format!("{path}/bin/python")); + } else if let Some(path) = get_uv_python_venv_path() { + return Ok(path); + } + + let path = if cfg!(windows) { + r".venv\Scripts\python.exe" + } else { + ".venv/bin/python" + }; + if Path::new(&path).is_file() { + let path = canonicalize(path).unwrap(); + return Ok(path.to_string_lossy().to_string()); } - let (cmd, python) = if cfg!(windows) { - ("where", "python") + let out = if cfg!(windows) { + Command::new("cmd") + .arg("/C") + .arg("where") + .arg("python") + .output() } else { - ("which", "python3") + Command::new("sh").arg("-c").arg("which python3").output() }; - let Ok(out) = Command::new(cmd).arg(python).output() else { - return Err(format!("{}: {python} not found", fn_name_full!())); + let Ok(out) = out else { + return Err(format!("{}: python not found", fn_name_full!())); }; let Ok(res) = String::from_utf8(out.stdout) else { return Err(format!( @@ -638,7 +671,7 @@ pub fn _opt_which_python() -> Result { }; let res = res.split('\n').next().unwrap_or("").replace('\r', ""); if res.is_empty() { - return Err(format!("{}: {python} not found", fn_name_full!())); + return Err(format!("{}: python not found", fn_name_full!())); } else if res.contains("pyenv") && cfg!(windows) { // because pyenv-win does not support `-c` option return Err("cannot use pyenv-win".into()); diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index b4ec235d2..eba6f0968 100644 --- a/crates/erg_compiler/codegen.rs +++ b/crates/erg_compiler/codegen.rs @@ -40,8 +40,8 @@ use crate::hir::GlobSignature; use crate::hir::ListWithLength; use crate::hir::{ Accessor, Args, BinOp, Block, Call, ClassDef, Def, DefBody, Dict, Expr, GuardClause, - Identifier, Lambda, List, Literal, NonDefaultParamSignature, Params, PatchDef, PosArg, ReDef, - Record, Set, Signature, SubrSignature, Tuple, UnaryOp, VarSignature, HIR, + Identifier, KwArg, Lambda, List, Literal, NonDefaultParamSignature, Params, PatchDef, PosArg, + ReDef, Record, Set, Signature, SubrSignature, Tuple, UnaryOp, VarSignature, HIR, }; use crate::ty::codeobj::{CodeObj, CodeObjFlags, MakeFunctionFlags}; use crate::ty::value::{GenTypeObj, ValueObj}; @@ -3976,6 +3976,31 @@ impl PyCodeGenerator { self.emit_import_all_instr(erg_std_mod); } + fn load_encoding_utf8(&mut self) { + let no_std = self.cfg.no_std; + self.cfg.no_std = true; + self.load_sys_encoding(); + self.cfg.no_std = no_std; + } + + fn load_sys_encoding(&mut self) { + self.load_reconfigure(); + let tk_utf8 = Token::new(TokenKind::StrLit, "utf-8", 0, 0); + let expr_utf8 = Expr::Literal(Literal::new(ValueObj::Str("utf-8".into()), tk_utf8)); + let tk_encoding = Token::new(TokenKind::StrLit, "encoding", 0, 0); + let args = Args::new( + vec![], + None, + vec![KwArg::new(tk_encoding, expr_utf8)], + None, + None, + ); + self.emit_load_name_instr(Identifier::private("#stdout")); + self.emit_load_method_instr(Identifier::static_public("reconfigure"), BoundAttr); + self.emit_args_311(args, AccessKind::BoundAttr); + self.emit_pop_top(); + } + fn load_record_type(&mut self) { self.emit_global_import_items( Identifier::static_public("collections"), @@ -4002,6 +4027,16 @@ impl PyCodeGenerator { ); } + fn load_reconfigure(&mut self) { + self.emit_global_import_items( + Identifier::static_public("sys"), + vec![( + Identifier::static_public("stdout"), + Some(Identifier::private("#stdout")), + )], + ); + } + fn load_union(&mut self) { self.emit_global_import_items( Identifier::static_public("_erg_type"), @@ -4062,6 +4097,9 @@ impl PyCodeGenerator { if !self.cfg.no_std && !self.prelude_loaded { self.load_prelude(); } + if !self.cfg.no_std && !self.cfg.input.is_repl() { + self.load_encoding_utf8(); + } for chunk in hir.module.into_iter() { self.emit_chunk(chunk); // TODO: discard diff --git a/tests/test.rs b/tests/test.rs index cc90e6ae0..f2a6ad47e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -183,12 +183,7 @@ fn exec_fib() -> Result<(), ()> { #[test] fn exec_helloworld() -> Result<(), ()> { - // HACK: When running the test with Windows, the exit code is 1 (the cause is unknown) - if cfg!(windows) && env_python_version().unwrap().minor >= Some(8) { - expect_end_with("examples/helloworld.er", 0, 1) - } else { - expect_success("examples/helloworld.er", 0) - } + expect_success("examples/helloworld.er", 0) } #[test]