From aca156589b47d41c439b409abe212eec47c1a6f3 Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Wed, 8 Jan 2025 23:01:32 +0900 Subject: [PATCH 1/6] fix: get_uv_python_venv_path if uv isn't installed Co-authored-by: Shunsuke Shibayama --- crates/erg_common/python_util.rs | 57 +++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 12 deletions(-) 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()); From 4b180766331a81aeff1ad77301a18f852ead71fd Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Sat, 18 Jan 2025 14:08:12 +0900 Subject: [PATCH 2/6] fix(compiler): load utf-8 encoding if not RELP --- crates/erg_compiler/codegen.rs | 45 ++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index b4ec235d2..be14ced2a 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,44 @@ 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.emit_global_import_items( + Identifier::static_public("sys"), + vec![( + Identifier::static_public("stdout"), + Some(Identifier::private("stdout")), + )], + ); + self.emit_load_name_instr(Identifier::private("stdout")); + self.emit_load_method_instr(Identifier::static_public("reconfigure"), BoundAttr); + let tk_encoding = Token::new_fake(TokenKind::StrLit, "encoding", 0, 0, 0); + let tk_utf8 = Token::new_fake(TokenKind::StrLit, "utf-8", 0, 0, 0); + let expr_utf8 = Expr::Literal(Literal::new(ValueObj::Str("utf-8".into()), tk_utf8)); + let kwarg = KwArg::new(tk_encoding, expr_utf8); + let mut args = Args::new(vec![], None, vec![kwarg], None, None); + let argc = args.len(); + let mut kws = Vec::with_capacity(args.kw_len()); + while let Some(arg) = args.try_remove_kw(0) { + kws.push(ValueObj::Str(arg.keyword.content)); + self.emit_expr(arg.expr); + } + + self.emit_call_kw_instr(argc, kws); + let kwsc = if self.py_version.minor >= Some(11) { + 0 + } else { + 1 + }; + self.stack_dec_n((1 + argc + kwsc) - 1); + } + fn load_record_type(&mut self) { self.emit_global_import_items( Identifier::static_public("collections"), @@ -4062,6 +4100,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 From cb3099883a48d5837d7b6bde11dd3fb311e06452 Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Sun, 2 Mar 2025 22:27:38 +0900 Subject: [PATCH 3/6] refactor(compiler): use existing methods --- crates/erg_compiler/codegen.rs | 49 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index be14ced2a..eba6f0968 100644 --- a/crates/erg_compiler/codegen.rs +++ b/crates/erg_compiler/codegen.rs @@ -3984,34 +3984,21 @@ impl PyCodeGenerator { } fn load_sys_encoding(&mut self) { - self.emit_global_import_items( - Identifier::static_public("sys"), - vec![( - Identifier::static_public("stdout"), - Some(Identifier::private("stdout")), - )], + 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_name_instr(Identifier::private("#stdout")); self.emit_load_method_instr(Identifier::static_public("reconfigure"), BoundAttr); - let tk_encoding = Token::new_fake(TokenKind::StrLit, "encoding", 0, 0, 0); - let tk_utf8 = Token::new_fake(TokenKind::StrLit, "utf-8", 0, 0, 0); - let expr_utf8 = Expr::Literal(Literal::new(ValueObj::Str("utf-8".into()), tk_utf8)); - let kwarg = KwArg::new(tk_encoding, expr_utf8); - let mut args = Args::new(vec![], None, vec![kwarg], None, None); - let argc = args.len(); - let mut kws = Vec::with_capacity(args.kw_len()); - while let Some(arg) = args.try_remove_kw(0) { - kws.push(ValueObj::Str(arg.keyword.content)); - self.emit_expr(arg.expr); - } - - self.emit_call_kw_instr(argc, kws); - let kwsc = if self.py_version.minor >= Some(11) { - 0 - } else { - 1 - }; - self.stack_dec_n((1 + argc + kwsc) - 1); + self.emit_args_311(args, AccessKind::BoundAttr); + self.emit_pop_top(); } fn load_record_type(&mut self) { @@ -4040,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"), From a4c1fdaf196702eaf3e629b674d2c60de67b6cd3 Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Sun, 2 Mar 2025 22:45:12 +0900 Subject: [PATCH 4/6] test: remove Windows-specific hack --- tests/test.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index a33429397..342538fb8 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] From 74b4e17e219021e4d59a34bfb7d558f594ea51e2 Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Wed, 31 Dec 2025 16:16:06 +0900 Subject: [PATCH 5/6] refactor(compiler): streamline sys encoding loading process --- crates/erg_compiler/codegen.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index eba6f0968..9c5d31e63 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, KwArg, Lambda, List, Literal, NonDefaultParamSignature, Params, PatchDef, PosArg, - ReDef, Record, Set, Signature, SubrSignature, Tuple, UnaryOp, VarSignature, HIR, + Identifier, 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}; @@ -3985,19 +3985,22 @@ impl PyCodeGenerator { 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_load_const("utf-8"); + let kws = vec![ValueObj::Str("encoding".into())]; + if self.py_version.minor >= Some(11) { + let idx = self.register_const(kws); + self.write_instr(Opcode311::KW_NAMES); + self.write_arg(idx); + self.emit_precall_and_call(1); + self.stack_dec(); + } else { + self.emit_load_const(kws); + self.write_instr(Opcode310::CALL_FUNCTION_KW); + self.write_arg(1); + self.stack_dec_n(2); + } self.emit_pop_top(); } From 1bfd3826cea5853ddbfc4888d515efa4f705f0fe Mon Sep 17 00:00:00 2001 From: GreasySlug <9619abgoni@gmail.com> Date: Wed, 31 Dec 2025 16:22:34 +0900 Subject: [PATCH 6/6] refactor(common): reorder virtual env path checks for clarity --- crates/erg_common/python_util.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/erg_common/python_util.rs b/crates/erg_common/python_util.rs index 19bae09e1..894210298 100644 --- a/crates/erg_common/python_util.rs +++ b/crates/erg_common/python_util.rs @@ -636,21 +636,21 @@ pub fn _opt_which_python() -> Result { if let Some(path) = which_python_from_toml() { return Ok(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) { + let venv_path = if cfg!(windows) { r".venv\Scripts\python.exe" } else { ".venv/bin/python" }; - if Path::new(&path).is_file() { - let path = canonicalize(path).unwrap(); + if Path::new(&venv_path).is_file() { + let path = canonicalize(venv_path).unwrap(); return Ok(path.to_string_lossy().to_string()); } + if let Some(path) = get_poetry_virtualenv_path() { + return Ok(format!("{path}/bin/python")); + } + if let Some(path) = get_uv_python_venv_path() { + return Ok(path); + } let out = if cfg!(windows) { Command::new("cmd") .arg("/C")