From 733d544802c2ed9c6eea551a039a644fb9aae990 Mon Sep 17 00:00:00 2001 From: snowapril Date: Sat, 6 Nov 2021 16:12:11 +0900 Subject: [PATCH 01/40] Wrap `ItemFn` and #[pyattr] with `static_cell!` When we have to add #[pyattr] with function, that function must be called only once when module is imported. In previous, we did it with add static_cell for each function or skip it(undesired). I thought these static_cell stmt for all of each function in modules are quite duplicated. Thus, I add wrapping code when the given item is `ItemFn` and have `#[pyattr]`. Signed-off-by: snowapril --- derive/src/pymodule.rs | 37 ++++++++++++++++++++++++++++++++++++- derive/src/util.rs | 2 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index d706da7c6..abeeb09b3 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -1,6 +1,6 @@ use crate::error::Diagnostic; use crate::util::{ - iter_use_idents, pyclass_ident_and_attrs, text_signature, AttributeExt, ClassItemMeta, + iter_use_idents, path_eq, pyclass_ident_and_attrs, text_signature, AttributeExt, ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, }; @@ -37,6 +37,41 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result { + unreachable!("#[pyattr] attached function must have return type.") + } + syn::ReturnType::Type(_, ty) => ty, + }; + let stmt: syn::Stmt = parse_quote! { + { + rustpython_common::static_cell! { + static ERROR: #return_type; + } + ERROR + .get_or_init(|| { + #(#stmts)* + }) + .clone() + } + }; + block.stmts = vec![stmt]; + } + } let (pyitems, cfgs) = attrs_to_module_items(attrs, new_module_item)?; for pyitem in pyitems.iter().rev() { let r = pyitem.gen_module_item(ModuleItemArgs { diff --git a/derive/src/util.rs b/derive/src/util.rs index ed790d810..1e494de85 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -226,7 +226,7 @@ pub(crate) trait ItemMeta: Sized { pub(crate) struct SimpleItemMeta(pub ItemMetaInner); impl ItemMeta for SimpleItemMeta { - const ALLOWED_NAMES: &'static [&'static str] = &["name"]; + const ALLOWED_NAMES: &'static [&'static str] = &["name", "once"]; fn from_inner(inner: ItemMetaInner) -> Self { Self(inner) From 5e8d3bcacb0d3e6c5eb46b2bac0e8558f3abb5dc Mon Sep 17 00:00:00 2001 From: snowapril Date: Sun, 7 Nov 2021 19:50:10 +0900 Subject: [PATCH 02/40] Remove duplicated static_cell! stmts in each modules Signed-off-by: snowapril --- stdlib/src/binascii.rs | 42 +++++++------------ stdlib/src/csv.rs | 2 +- stdlib/src/socket.rs | 63 ++++++++++------------------ stdlib/src/ssl.rs | 87 ++++++++++----------------------------- stdlib/src/termios.rs | 21 ++++------ stdlib/src/zlib.rs | 2 +- vm/src/stdlib/pystruct.rs | 21 ++++------ 7 files changed, 73 insertions(+), 165 deletions(-) diff --git a/stdlib/src/binascii.rs b/stdlib/src/binascii.rs index 4e3a53635..59526c193 100644 --- a/stdlib/src/binascii.rs +++ b/stdlib/src/binascii.rs @@ -11,38 +11,24 @@ mod decl { }; use itertools::Itertools; - #[pyattr(name = "Error")] + #[pyattr(name = "Error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static BINASCII_ERROR: PyTypeRef; - } - BINASCII_ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("binascii"), - "Error", - &vm.ctx.exceptions.value_error, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("binascii"), + "Error", + &vm.ctx.exceptions.value_error, + Default::default(), + ) } - #[pyattr(name = "Incomplete")] + #[pyattr(name = "Incomplete", once)] fn incomplete_type(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static BINASCII_INCOMPLTE: PyTypeRef; - } - BINASCII_INCOMPLTE - .get_or_init(|| { - vm.ctx.new_class( - Some("binascii"), - "Incomplete", - &vm.ctx.exceptions.exception_type, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("binascii"), + "Incomplete", + &vm.ctx.exceptions.exception_type, + Default::default(), + ) } fn hex_nibble(n: u8) -> u8 { diff --git a/stdlib/src/csv.rs b/stdlib/src/csv.rs index f89238ed1..2e8a44f8d 100644 --- a/stdlib/src/csv.rs +++ b/stdlib/src/csv.rs @@ -30,7 +30,7 @@ mod _csv { #[pyattr] const QUOTE_NONE: i32 = QuoteStyle::None as i32; - #[pyattr(name = "Error")] + #[pyattr(name = "Error", once)] fn error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("_csv.Error", &vm.ctx.exceptions.exception_type).unwrap() } diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 4de71fdd0..521531a42 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -80,53 +80,32 @@ mod _socket { vm.ctx.exceptions.os_error.clone() } - #[pyattr] + #[pyattr(once)] fn timeout(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("socket"), - "timeout", - &vm.ctx.exceptions.os_error, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("socket"), + "timeout", + &vm.ctx.exceptions.os_error, + Default::default(), + ) } - #[pyattr] + #[pyattr(once)] fn herror(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("socket"), - "herror", - &vm.ctx.exceptions.os_error, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("socket"), + "herror", + &vm.ctx.exceptions.os_error, + Default::default(), + ) } - #[pyattr] + #[pyattr(once)] fn gaierror(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("socket"), - "gaierror", - &vm.ctx.exceptions.os_error, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("socket"), + "gaierror", + &vm.ctx.exceptions.os_error, + Default::default(), + ) } #[pyfunction] diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index 8709839d2..e7f00a6b6 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -174,90 +174,47 @@ mod _ssl { parse_version_info(openssl_api_version) } - #[pyattr(name = "SSLError")] + #[pyattr(name = "SSLError", once)] fn ssl_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - PyType::new_simple_ref("ssl.SSLError", &vm.ctx.exceptions.os_error).unwrap() - }) - .clone() + PyType::new_simple_ref("ssl.SSLError", &vm.ctx.exceptions.os_error).unwrap() } - #[pyattr(name = "SSLCertVerificationError")] + #[pyattr(name = "SSLCertVerificationError", once)] fn ssl_cert_verification_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - let ssl_error = ssl_error(vm); - PyType::new_ref( - "ssl.SSLCertVerificationError", - vec![ssl_error, vm.ctx.exceptions.value_error.clone()], - Default::default(), - PyBaseException::make_slots(), - vm.ctx.types.type_type.clone(), - ) - .unwrap() - }) - .clone() + let ssl_error = ssl_error(vm); + PyType::new_ref( + "ssl.SSLCertVerificationError", + vec![ssl_error, vm.ctx.exceptions.value_error.clone()], + Default::default(), + PyBaseException::make_slots(), + vm.ctx.types.type_type.clone(), + ) + .unwrap() } - #[pyattr(name = "SSLZeroReturnError")] + #[pyattr(name = "SSLZeroReturnError", once)] fn ssl_zero_return_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - PyType::new_simple_ref("ssl.SSLZeroReturnError", &ssl_error(vm)).unwrap() - }) - .clone() + PyType::new_simple_ref("ssl.SSLZeroReturnError", &ssl_error(vm)).unwrap() } - #[pyattr(name = "SSLWantReadError")] + #[pyattr(name = "SSLWantReadError", once)] fn ssl_want_read_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| PyType::new_simple_ref("ssl.SSLWantReadError", &ssl_error(vm)).unwrap()) - .clone() + PyType::new_simple_ref("ssl.SSLWantReadError", &ssl_error(vm)).unwrap() } - #[pyattr(name = "SSLWantWriteError")] + #[pyattr(name = "SSLWantWriteError", once)] fn ssl_want_write_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| { - PyType::new_simple_ref("ssl.SSLWantWriteError", &ssl_error(vm)).unwrap() - }) - .clone() + PyType::new_simple_ref("ssl.SSLWantWriteError", &ssl_error(vm)).unwrap() } - #[pyattr(name = "SSLSyscallError")] + #[pyattr(name = "SSLSyscallError", once)] fn ssl_syscall_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| PyType::new_simple_ref("ssl.SSLSyscallError", &ssl_error(vm)).unwrap()) - .clone() + PyType::new_simple_ref("ssl.SSLSyscallError", &ssl_error(vm)).unwrap() } - #[pyattr(name = "SSLEOFError")] + #[pyattr(name = "SSLEOFError", once)] fn ssl_eof_error(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static ERROR: PyTypeRef; - } - ERROR - .get_or_init(|| PyType::new_simple_ref("ssl.SSLEOFError", &ssl_error(vm)).unwrap()) - .clone() + PyType::new_simple_ref("ssl.SSLEOFError", &ssl_error(vm)).unwrap() } type OpensslVersionInfo = (u8, u8, u8, u8, u8); diff --git a/stdlib/src/termios.rs b/stdlib/src/termios.rs index 97ccdac29..1f203bb29 100644 --- a/stdlib/src/termios.rs +++ b/stdlib/src/termios.rs @@ -100,20 +100,13 @@ mod termios { ) } - #[pyattr(name = "error")] + #[pyattr(name = "error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static TERMIOS_ERROR: PyTypeRef; - } - TERMIOS_ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("termios"), - "error", - &vm.ctx.exceptions.os_error, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("termios"), + "error", + &vm.ctx.exceptions.os_error, + Default::default(), + ) } } diff --git a/stdlib/src/zlib.rs b/stdlib/src/zlib.rs index 492fc60e3..d466e248b 100644 --- a/stdlib/src/zlib.rs +++ b/stdlib/src/zlib.rs @@ -53,7 +53,7 @@ mod zlib { #[pyattr] const DEF_MEM_LEVEL: u8 = 8; - #[pyattr] + #[pyattr(once)] fn error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("zlib.error", &vm.ctx.exceptions.exception_type).unwrap() } diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 7530a303c..91e376cc0 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -960,21 +960,14 @@ pub(crate) mod _struct { #[pyfunction] fn _clearcache() {} - #[pyattr(name = "error")] + #[pyattr(name = "error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - rustpython_common::static_cell! { - static STRUCT_ERROR: PyTypeRef; - } - STRUCT_ERROR - .get_or_init(|| { - vm.ctx.new_class( - Some("struct"), - "error", - &vm.ctx.exceptions.exception_type, - Default::default(), - ) - }) - .clone() + vm.ctx.new_class( + Some("struct"), + "error", + &vm.ctx.exceptions.exception_type, + Default::default(), + ) } fn new_struct_error(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef { From fee42a1074f621f0295c830fea5b6aa352cc4530 Mon Sep 17 00:00:00 2001 From: snowapril Date: Sat, 6 Nov 2021 16:17:11 +0900 Subject: [PATCH 03/40] Add docs for each error in ssl module Signed-off-by: snowapril --- stdlib/src/ssl.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index e7f00a6b6..6cf0b55e3 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -174,11 +174,13 @@ mod _ssl { parse_version_info(openssl_api_version) } + /// An error occurred in the SSL implementation. #[pyattr(name = "SSLError", once)] fn ssl_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLError", &vm.ctx.exceptions.os_error).unwrap() } + /// A certificate could not be verified. #[pyattr(name = "SSLCertVerificationError", once)] fn ssl_cert_verification_error(vm: &VirtualMachine) -> PyTypeRef { let ssl_error = ssl_error(vm); @@ -192,26 +194,31 @@ mod _ssl { .unwrap() } + /// SSL/TLS session closed cleanly. #[pyattr(name = "SSLZeroReturnError", once)] fn ssl_zero_return_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLZeroReturnError", &ssl_error(vm)).unwrap() } + /// Non-blocking SSL socket needs to read more data before the requested operation can be completed. #[pyattr(name = "SSLWantReadError", once)] fn ssl_want_read_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLWantReadError", &ssl_error(vm)).unwrap() } + /// Non-blocking SSL socket needs to write more data before the requested operation can be completed. #[pyattr(name = "SSLWantWriteError", once)] fn ssl_want_write_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLWantWriteError", &ssl_error(vm)).unwrap() } + /// System error when attempting SSL operation. #[pyattr(name = "SSLSyscallError", once)] fn ssl_syscall_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLSyscallError", &ssl_error(vm)).unwrap() } + /// SSL/TLS connection terminated abruptly. #[pyattr(name = "SSLEOFError", once)] fn ssl_eof_error(vm: &VirtualMachine) -> PyTypeRef { PyType::new_simple_ref("ssl.SSLEOFError", &ssl_error(vm)).unwrap() From 59582ed295a827225fe8bc393deff828be590b4d Mon Sep 17 00:00:00 2001 From: snowapril Date: Sat, 6 Nov 2021 17:17:31 +0900 Subject: [PATCH 04/40] Add new_exception_type for module-level exception At previous, `vm.ctx.new_class` did role for adding exception in module-level. This commit add `new_exception_type` for doing that part of role. As cpython implementation, if no bases is given, pass `exception_type` by default Signed-off-by: snowapril --- vm/src/pyobject.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d7cff5d60..0d78ba471 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -12,9 +12,9 @@ use crate::{ builtinfunc::{PyBuiltinFunction, PyBuiltinMethod, PyNativeFuncDef}, bytes, getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetSet}, - object, pystr, PyBaseExceptionRef, PyBoundMethod, PyDict, PyDictRef, PyEllipsis, PyFloat, - PyFrozenSet, PyGenericAlias, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented, - PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef, + object, pystr, PyBaseException, PyBaseExceptionRef, PyBoundMethod, PyDict, PyDictRef, + PyEllipsis, PyFloat, PyFrozenSet, PyGenericAlias, PyInt, PyIntRef, PyList, PyListRef, + PyNone, PyNotImplemented, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef, }, dictdatatype::Dict, exceptions, @@ -242,6 +242,30 @@ impl PyContext { .unwrap() } + pub fn new_exception_type( + &self, + module: &str, + name: &str, + bases: Option>, + ) -> PyTypeRef { + let bases = if let Some(bases) = bases { + bases + } else { + vec![self.exceptions.exception_type.clone()] + }; + let mut attrs = PyAttributes::default(); + attrs.insert("__module__".to_string(), self.new_str(module).into()); + + PyType::new_ref( + name, + bases, + attrs, + PyBaseException::make_slots(), + self.types.type_type.clone(), + ) + .unwrap() + } + #[inline] pub fn make_funcdef(&self, name: impl Into, f: F) -> PyNativeFuncDef where From ab1693e1317d01c5bec59989bd9993d046078cb4 Mon Sep 17 00:00:00 2001 From: snowapril Date: Sat, 6 Nov 2021 17:20:00 +0900 Subject: [PATCH 05/40] Replace `new_class` to `new_exception_type` Replace `new_class` method call for module-level exceptions to newly added `new_exception_type` Signed-off-by: snowapril --- stdlib/src/binascii.rs | 14 ++++---------- stdlib/src/csv.rs | 8 ++++++-- stdlib/src/socket.rs | 21 +++++++++------------ stdlib/src/ssl.rs | 34 +++++++++++++++++++--------------- stdlib/src/termios.rs | 7 +++---- stdlib/src/zlib.rs | 8 ++++++-- vm/src/stdlib/pystruct.rs | 7 +------ 7 files changed, 48 insertions(+), 51 deletions(-) diff --git a/stdlib/src/binascii.rs b/stdlib/src/binascii.rs index 59526c193..2130b5ddc 100644 --- a/stdlib/src/binascii.rs +++ b/stdlib/src/binascii.rs @@ -13,22 +13,16 @@ mod decl { #[pyattr(name = "Error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("binascii"), + vm.ctx.new_exception_type( + "binascii", "Error", - &vm.ctx.exceptions.value_error, - Default::default(), + Some(vec![vm.ctx.exceptions.value_error.clone()]), ) } #[pyattr(name = "Incomplete", once)] fn incomplete_type(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("binascii"), - "Incomplete", - &vm.ctx.exceptions.exception_type, - Default::default(), - ) + vm.ctx.new_exception_type("binascii", "Incomplete", None) } fn hex_nibble(n: u8) -> u8 { diff --git a/stdlib/src/csv.rs b/stdlib/src/csv.rs index 2e8a44f8d..a9fc1f1c9 100644 --- a/stdlib/src/csv.rs +++ b/stdlib/src/csv.rs @@ -11,7 +11,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef { mod _csv { use crate::common::lock::PyMutex; use crate::vm::{ - builtins::{PyStr, PyStrRef, PyType, PyTypeRef}, + builtins::{PyStr, PyStrRef, PyTypeRef}, function::{ArgIterable, ArgumentError, FromArgs, FuncArgs}, match_class, protocol::{PyIter, PyIterReturn}, @@ -32,7 +32,11 @@ mod _csv { #[pyattr(name = "Error", once)] fn error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("_csv.Error", &vm.ctx.exceptions.exception_type).unwrap() + vm.ctx.new_exception_type( + "_csv", + "Error", + Some(vec![vm.ctx.exceptions.exception_type.clone()]), + ) } #[pyfunction] diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 521531a42..9543c02b2 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -82,29 +82,26 @@ mod _socket { #[pyattr(once)] fn timeout(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("socket"), + vm.ctx.new_exception_type( + "socket", "timeout", - &vm.ctx.exceptions.os_error, - Default::default(), + Some(vec![vm.ctx.exceptions.os_error.clone()]), ) } #[pyattr(once)] fn herror(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("socket"), + vm.ctx.new_exception_type( + "socket", "herror", - &vm.ctx.exceptions.os_error, - Default::default(), + Some(vec![vm.ctx.exceptions.os_error.clone()]), ) } #[pyattr(once)] fn gaierror(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("socket"), + vm.ctx.new_exception_type( + "socket", "gaierror", - &vm.ctx.exceptions.os_error, - Default::default(), + Some(vec![vm.ctx.exceptions.os_error.clone()]), ) } diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index 6cf0b55e3..5230ba865 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -30,7 +30,7 @@ mod _ssl { }, socket::{self, PySocket}, vm::{ - builtins::{PyBaseException, PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef, PyWeak}, + builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef, PyWeak}, exceptions, function::{ ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, IntoPyException, @@ -39,7 +39,7 @@ mod _ssl { stdlib::os::PyPathLike, types::Constructor, utils::{Either, ToCString}, - ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, + ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, }, }; use crossbeam_utils::atomic::AtomicCell; @@ -177,45 +177,49 @@ mod _ssl { /// An error occurred in the SSL implementation. #[pyattr(name = "SSLError", once)] fn ssl_error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("ssl.SSLError", &vm.ctx.exceptions.os_error).unwrap() + vm.ctx.new_exception_type( + "ssl", + "SSLError", + Some(vec![vm.ctx.exceptions.os_error.clone()]), + ) } /// A certificate could not be verified. #[pyattr(name = "SSLCertVerificationError", once)] fn ssl_cert_verification_error(vm: &VirtualMachine) -> PyTypeRef { - let ssl_error = ssl_error(vm); - PyType::new_ref( - "ssl.SSLCertVerificationError", - vec![ssl_error, vm.ctx.exceptions.value_error.clone()], - Default::default(), - PyBaseException::make_slots(), - vm.ctx.types.type_type.clone(), + vm.ctx.new_exception_type( + "ssl", + "SSLCertVerificationError", + Some(vec![ssl_error(vm), vm.ctx.exceptions.value_error.clone()]), ) - .unwrap() } /// SSL/TLS session closed cleanly. #[pyattr(name = "SSLZeroReturnError", once)] fn ssl_zero_return_error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("ssl.SSLZeroReturnError", &ssl_error(vm)).unwrap() + vm.ctx + .new_exception_type("ssl", "SSLZeroReturnError", Some(vec![ssl_error(vm)])) } /// Non-blocking SSL socket needs to read more data before the requested operation can be completed. #[pyattr(name = "SSLWantReadError", once)] fn ssl_want_read_error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("ssl.SSLWantReadError", &ssl_error(vm)).unwrap() + vm.ctx + .new_exception_type("ssl", "SSLWantReadError", Some(vec![ssl_error(vm)])) } /// Non-blocking SSL socket needs to write more data before the requested operation can be completed. #[pyattr(name = "SSLWantWriteError", once)] fn ssl_want_write_error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("ssl.SSLWantWriteError", &ssl_error(vm)).unwrap() + vm.ctx + .new_exception_type("ssl", "SSLWantWriteError", Some(vec![ssl_error(vm)])) } /// System error when attempting SSL operation. #[pyattr(name = "SSLSyscallError", once)] fn ssl_syscall_error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("ssl.SSLSyscallError", &ssl_error(vm)).unwrap() + vm.ctx + .new_exception_type("ssl", "SSLSyscallError", Some(vec![ssl_error(vm)])) } /// SSL/TLS connection terminated abruptly. diff --git a/stdlib/src/termios.rs b/stdlib/src/termios.rs index 1f203bb29..507a655fa 100644 --- a/stdlib/src/termios.rs +++ b/stdlib/src/termios.rs @@ -102,11 +102,10 @@ mod termios { #[pyattr(name = "error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("termios"), + vm.ctx.new_exception_type( + "termios", "error", - &vm.ctx.exceptions.os_error, - Default::default(), + Some(vec![vm.ctx.exceptions.os_error.clone()]), ) } } diff --git a/stdlib/src/zlib.rs b/stdlib/src/zlib.rs index d466e248b..d2f44de20 100644 --- a/stdlib/src/zlib.rs +++ b/stdlib/src/zlib.rs @@ -4,7 +4,7 @@ pub(crate) use zlib::make_module; mod zlib { use crate::common::lock::PyMutex; use crate::vm::{ - builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyIntRef, PyType, PyTypeRef}, + builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyIntRef, PyTypeRef}, function::{ArgBytesLike, OptionalArg}, IntoPyRef, PyResult, PyValue, VirtualMachine, }; @@ -55,7 +55,11 @@ mod zlib { #[pyattr(once)] fn error(vm: &VirtualMachine) -> PyTypeRef { - PyType::new_simple_ref("zlib.error", &vm.ctx.exceptions.exception_type).unwrap() + vm.ctx.new_exception_type( + "zlib", + "error", + Some(vec![vm.ctx.exceptions.exception_type.clone()]), + ) } /// Compute an Adler-32 checksum of data. diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index 91e376cc0..bec416b06 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -962,12 +962,7 @@ pub(crate) mod _struct { #[pyattr(name = "error", once)] fn error_type(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.new_class( - Some("struct"), - "error", - &vm.ctx.exceptions.exception_type, - Default::default(), - ) + vm.ctx.new_exception_type("struct", "error", None) } fn new_struct_error(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef { From 97f3434c0a4f96e30bb7f89e1ec8c87edd5d221f Mon Sep 17 00:00:00 2001 From: snowapril Date: Thu, 11 Nov 2021 17:01:14 +0900 Subject: [PATCH 06/40] Add `AttrItemMeta` for handling attribute item attathced with `once` Signed-off-by: snowapril --- derive/src/pymodule.rs | 79 ++++++++++++++++++------------------------ derive/src/util.rs | 13 +++++++ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index abeeb09b3..28124aeca 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -1,7 +1,7 @@ use crate::error::Diagnostic; use crate::util::{ - iter_use_idents, path_eq, pyclass_ident_and_attrs, text_signature, AttributeExt, ClassItemMeta, - ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, + iter_use_idents, pyclass_ident_and_attrs, text_signature, AttrItemMeta, AttributeExt, + ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, }; use proc_macro2::TokenStream; @@ -37,41 +37,6 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result { - unreachable!("#[pyattr] attached function must have return type.") - } - syn::ReturnType::Type(_, ty) => ty, - }; - let stmt: syn::Stmt = parse_quote! { - { - rustpython_common::static_cell! { - static ERROR: #return_type; - } - ERROR - .get_or_init(|| { - #(#stmts)* - }) - .clone() - } - }; - block.stmts = vec![stmt]; - } - } let (pyitems, cfgs) = attrs_to_module_items(attrs, new_module_item)?; for pyitem in pyitems.iter().rev() { let r = pyitem.gen_module_item(ModuleItemArgs { @@ -276,7 +241,7 @@ impl ContentItem for AttributeItem { } struct ModuleItemArgs<'a> { - item: &'a Item, + item: &'a mut Item, attrs: &'a mut Vec, context: &'a mut ModuleContext, cfgs: &'a [Attribute], @@ -405,15 +370,36 @@ impl ModuleItem for AttributeItem { fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> { let cfgs = args.cfgs.to_vec(); let attr = args.attrs.remove(self.index()); - let get_py_name = |attr: &Attribute, ident: &Ident| -> Result<_> { - let item_meta = SimpleItemMeta::from_attr(ident.clone(), attr)?; - let py_name = item_meta.simple_name()?; - Ok(py_name) - }; let (py_name, tokens) = match args.item { - Item::Fn(syn::ItemFn { sig, .. }) => { + Item::Fn(syn::ItemFn { sig, block, .. }) => { let ident = &sig.ident; - let py_name = get_py_name(&attr, ident)?; + // If `once` keyword is in #[pyattr], + // wrapping it with static_cell for preventing it from using it as function + let attr_meta = AttrItemMeta::from_attr(ident.clone(), &attr)?; + if attr_meta.inner()._bool("once")? { + let stmts = &block.stmts; + let return_type = match &sig.output { + syn::ReturnType::Default => { + unreachable!("#[pyattr] attached function must have return type.") + } + syn::ReturnType::Type(_, ty) => ty, + }; + let stmt: syn::Stmt = parse_quote! { + { + rustpython_common::static_cell! { + static ERROR: #return_type; + } + ERROR + .get_or_init(|| { + #(#stmts)* + }) + .clone() + } + }; + block.stmts = vec![stmt]; + } + + let py_name = attr_meta.simple_name()?; ( py_name.clone(), quote_spanned! { ident.span() => @@ -422,7 +408,8 @@ impl ModuleItem for AttributeItem { ) } Item::Const(syn::ItemConst { ident, .. }) => { - let py_name = get_py_name(&attr, ident)?; + let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr)?; + let py_name = item_meta.simple_name()?; ( py_name.clone(), quote_spanned! { ident.span() => diff --git a/derive/src/util.rs b/derive/src/util.rs index 1e494de85..f104c03c9 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -226,6 +226,19 @@ pub(crate) trait ItemMeta: Sized { pub(crate) struct SimpleItemMeta(pub ItemMetaInner); impl ItemMeta for SimpleItemMeta { + const ALLOWED_NAMES: &'static [&'static str] = &["name"]; + + fn from_inner(inner: ItemMetaInner) -> Self { + Self(inner) + } + fn inner(&self) -> &ItemMetaInner { + &self.0 + } +} + +pub(crate) struct AttrItemMeta(pub ItemMetaInner); + +impl ItemMeta for AttrItemMeta { const ALLOWED_NAMES: &'static [&'static str] = &["name", "once"]; fn from_inner(inner: ItemMetaInner) -> Self { From 4bcf317fbae96b44a58af3fe0e5c941d427255dc Mon Sep 17 00:00:00 2001 From: Dean Li Date: Wed, 10 Nov 2021 23:28:18 +0800 Subject: [PATCH 07/40] test: move os_helper out as py3.10 --- Lib/test/support/__init__.py | 841 ++++++++++++---------------------- Lib/test/support/os_helper.py | 617 +++++++++++++++++++++++++ 2 files changed, 908 insertions(+), 550 deletions(-) create mode 100644 Lib/test/support/os_helper.py diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1027e93df..af8936155 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -4,7 +4,6 @@ if __name__ != 'test.support': raise ImportError('support must be imported from the test package') import asyncio.events -import collections.abc import contextlib import errno import faulthandler @@ -163,6 +162,13 @@ SHORT_TIMEOUT = 30.0 # option. LONG_TIMEOUT = 5 * 60.0 +# TEST_HOME_DIR refers to the top level directory of the "test" package +# that contains Python's regression test suite +TEST_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR) +STDLIB_DIR = os.path.dirname(TEST_HOME_DIR) +REPO_ROOT = os.path.dirname(STDLIB_DIR) + class Error(Exception): """Base class for regression test exceptions.""" @@ -170,6 +176,17 @@ class Error(Exception): class TestFailed(Error): """Test failed.""" +class TestFailedWithDetails(TestFailed): + """Test failed.""" + def __init__(self, msg, errors, failures): + self.msg = msg + self.errors = errors + self.failures = failures + super().__init__(msg, errors, failures) + + def __str__(self): + return self.msg + class TestDidNotRun(Error): """Test did not run any subtests.""" @@ -279,9 +296,7 @@ def load_package_tests(pkg_dir, loader, standard_tests, pattern): """ if pattern is None: pattern = "test*" - top_dir = os.path.dirname( # Lib - os.path.dirname( # test - os.path.dirname(__file__))) # support + top_dir = STDLIB_DIR package_tests = loader.discover(start_dir=pkg_dir, top_level_dir=top_dir, pattern=pattern) @@ -442,7 +457,7 @@ if sys.platform.startswith("win"): try: mode = os.lstat(fullname).st_mode except OSError as exc: - print("support.rmtree(): os.lstat(%r) failed with %s" % (fullname, exc), + print("os_helper.rmtree(): os.lstat(%r) failed with %s" % (fullname, exc), file=sys.__stderr__) mode = 0 if stat.S_ISDIR(mode): @@ -545,8 +560,11 @@ def forget(modname): def _is_gui_available(): if hasattr(_is_gui_available, 'result'): return _is_gui_available.result + import platform reason = None - if sys.platform.startswith('win'): + if sys.platform.startswith('win') and platform.win32_is_iot(): + reason = "gui is not available on Windows IoT Core" + elif sys.platform.startswith('win'): # if Python is running as a service (such as the buildbot service), # gui interaction may be disallowed import ctypes @@ -640,25 +658,25 @@ def _requires_unix_version(sysname, min_version): For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if the FreeBSD version is less than 7.2. """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if platform.system() == sysname: - version_txt = platform.release().split('-', 1)[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "%s version %s or higher required, not %s" - % (sysname, min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator + import platform + min_version_txt = '.'.join(map(str, min_version)) + version_txt = platform.release().split('-', 1)[0] + if platform.system() == sysname: + try: + version = tuple(map(int, version_txt.split('.'))) + except ValueError: + skip = False + else: + skip = version < min_version + else: + skip = False + + return unittest.skipIf( + skip, + f"{sysname} version {min_version_txt} or higher required, not " + f"{version_txt}" + ) + def requires_freebsd_version(*min_version): """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is @@ -689,6 +707,7 @@ def requires_mac_ver(*min_version): @functools.wraps(func) def wrapper(*args, **kw): if sys.platform == 'darwin': + import platform version_txt = platform.mac_ver()[0] try: version = tuple(map(int, version_txt.split('.'))) @@ -706,6 +725,16 @@ def requires_mac_ver(*min_version): return decorator +def skip_if_buildbot(reason=None): + """Decorator raising SkipTest if running on a buildbot.""" + if not reason: + reason = 'not suitable for buildbots' + if sys.platform == 'win32': + isbuildbot = os.environ.get('USERNAME') == 'Buildbot' + else: + isbuildbot = os.environ.get('USER') == 'buildbot' + return unittest.skipIf(isbuildbot, reason) + def requires_hashdigest(digestname, openssl=None): """Decorator raising SkipTest if a hashing algorithm is not available @@ -888,8 +917,8 @@ PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 # A constant likely larger than the underlying OS socket buffer size, to make # writes blocking. # The socket buffer sizes can usually be tuned system-wide (e.g. through sysctl -# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 -# for a discussion of this number). +# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 +# for a discussion of this number. SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1 # # decorator for skipping tests on non-IEEE 754 platforms @@ -910,156 +939,15 @@ is_jython = sys.platform.startswith('java') is_android = hasattr(sys, 'getandroidapilevel') -if sys.platform != 'win32': +if sys.platform not in ('win32', 'vxworks'): unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -# Filename used for testing -if os.name == 'java': - # Jython disallows @ in module names - TESTFN = '$test' -else: - TESTFN = '@test' - -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN = "{}_{}_tmp".format(TESTFN, os.getpid()) - # Define the URL of a dedicated HTTP server for the network tests. # The URL must use clear-text HTTP: no redirection to encrypted HTTPS. TEST_HTTP_URL = "http://www.pythontest.net" -# FS_NONASCII: non-ASCII character encodable by os.fsencode(), -# or None if there is no such character. -FS_NONASCII = None -for character in ( - # First try printable and common characters to have a readable filename. - # For each character, the encoding list are just example of encodings able - # to encode the character (the list is not exhaustive). - - # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 - '\u00E6', - # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 - '\u0130', - # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 - '\u0141', - # U+03C6 (Greek Small Letter Phi): cp1253 - '\u03C6', - # U+041A (Cyrillic Capital Letter Ka): cp1251 - '\u041A', - # U+05D0 (Hebrew Letter Alef): Encodable to cp424 - '\u05D0', - # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic - '\u060C', - # U+062A (Arabic Letter Teh): cp720 - '\u062A', - # U+0E01 (Thai Character Ko Kai): cp874 - '\u0E01', - - # Then try more "special" characters. "special" because they may be - # interpreted or displayed differently depending on the exact locale - # encoding and the font. - - # U+00A0 (No-Break Space) - '\u00A0', - # U+20AC (Euro Sign) - '\u20AC', -): - try: - # If Python is set up to use the legacy 'mbcs' in Windows, - # 'replace' error mode is used, and encode() returns b'?' - # for characters missing in the ANSI codepage - if os.fsdecode(os.fsencode(character)) != character: - raise UnicodeError - except UnicodeError: - pass - else: - FS_NONASCII = character - break - -# TESTFN_UNICODE is a non-ascii filename -TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f" -if sys.platform == 'darwin': - # In Mac OS X's VFS API file names are, by definition, canonically - # decomposed Unicode, encoded using UTF-8. See QA1173: - # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html - import unicodedata - TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) -TESTFN_ENCODING = sys.getfilesystemencoding() - -# TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be -# encoded by the filesystem encoding (in strict mode). It can be None if we -# cannot generate such filename. -TESTFN_UNENCODABLE = None -if os.name == 'nt': - # skip win32s (0) or Windows 9x/ME (1) - if sys.getwindowsversion().platform >= 2: - # Different kinds of characters from various languages to minimize the - # probability that the whole name is encodable to MBCS (issue #9819) - TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" - try: - TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) - except UnicodeEncodeError: - pass - else: - print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). ' - 'Unicode filename tests may not be effective' - % (TESTFN_UNENCODABLE, TESTFN_ENCODING), - file=sys.__stderr__) - TESTFN_UNENCODABLE = None -# # Mac OS X denies unencodable filenames (invalid utf-8) -# elif sys.platform != 'darwin': -# try: -# # ascii and utf-8 cannot encode the byte 0xff -# b'\xff'.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# # 0xff will be encoded using the surrogate character u+DCFF -# TESTFN_UNENCODABLE = TESTFN \ -# + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape') -# else: -# # File system encoding (eg. ISO-8859-* encodings) can encode -# # the byte 0xff. Skip some unicode filename tests. -# pass - -# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be -# decoded from the filesystem encoding (in strict mode). It can be None if we -# cannot generate such filename (ex: the latin1 encoding can decode any byte -# sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks -# to the surrogateescape error handler (PEP 383), but not from the filesystem -# encoding in strict mode. -TESTFN_UNDECODABLE = None -for name in ( - # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows - # accepts it to create a file or a directory, or don't accept to enter to - # such directory (when the bytes name is used). So test b'\xe7' first: it is - # not decodable from cp932. - b'\xe7w\xf0', - # undecodable from ASCII, UTF-8 - b'\xff', - # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 - # and cp857 - b'\xae\xd5' - # undecodable from UTF-8 (UNIX and Mac OS X) - b'\xed\xb2\x80', b'\xed\xb4\x80', - # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, - # cp1253, cp1254, cp1255, cp1257, cp1258 - b'\x81\x98', -): - try: - name.decode(TESTFN_ENCODING) - except UnicodeDecodeError: - TESTFN_UNDECODABLE = os.fsencode(TESTFN) + name - break - -if FS_NONASCII: - TESTFN_NONASCII = TESTFN + '-' + FS_NONASCII -else: - TESTFN_NONASCII = None - -# Save the initial cwd -SAVEDCWD = os.getcwd() - # Set by libregrtest/main.py so we can skip tests that are not # useful for PGO PGO = False @@ -1068,121 +956,28 @@ PGO = False # PGO task. If this is True, PGO is also True. PGO_EXTENDED = False -@contextlib.contextmanager -def temp_dir(path=None, quiet=False): - """Return a context manager that creates a temporary directory. - - Arguments: - - path: the directory to create temporarily. If omitted or None, - defaults to creating a temporary directory using tempfile.mkdtemp. - - quiet: if False (the default), the context manager raises an exception - on error. Otherwise, if the path is specified and cannot be - created, only a warning is issued. - - """ - dir_created = False - if path is None: - path = tempfile.mkdtemp() - dir_created = True - path = os.path.realpath(path) - else: - try: - os.mkdir(path) - dir_created = True - except OSError as exc: - if not quiet: - raise - warnings.warn(f'tests may fail, unable to create ' - f'temporary directory {path!r}: {exc}', - RuntimeWarning, stacklevel=3) - if dir_created: - pid = os.getpid() - try: - yield path - finally: - # In case the process forks, let only the parent remove the - # directory. The child has a different process id. (bpo-30028) - if dir_created and pid == os.getpid(): - try: - rmtree(path) - except OSError as exc: - # XXX RUSTPYTHON: something something async file removal? - # also part of the thing with rmtree() - # throwing PermissionError, I think - if os.path.exists(path): - if not quiet: - raise - warnings.warn(f'unable to remove temporary' - f'directory {path!r}: {exc}', - RuntimeWarning, stacklevel=3) - -@contextlib.contextmanager -def change_cwd(path, quiet=False): - """Return a context manager that changes the current working directory. - - Arguments: - - path: the directory to use as the temporary current working directory. - - quiet: if False (the default), the context manager raises an exception - on error. Otherwise, it issues only a warning and keeps the current - working directory the same. - - """ - saved_dir = os.getcwd() - try: - os.chdir(os.path.realpath(path)) - except OSError as exc: - if not quiet: - raise - warnings.warn(f'tests may fail, unable to change the current working ' - f'directory to {path!r}: {exc}', - RuntimeWarning, stacklevel=3) - try: - yield os.getcwd() - finally: - os.chdir(saved_dir) - - -@contextlib.contextmanager -def temp_cwd(name='tempcwd', quiet=False): - """ - Context manager that temporarily creates and changes the CWD. - - The function temporarily changes the current working directory - after creating a temporary directory in the current directory with - name *name*. If *name* is None, the temporary directory is - created using tempfile.mkdtemp. - - If *quiet* is False (default) and it is not possible to - create or change the CWD, an error is raised. If *quiet* is True, - only a warning is raised and the original CWD is used. - - """ - with temp_dir(path=name, quiet=quiet) as temp_path: - with change_cwd(temp_path, quiet=quiet) as cwd_dir: - yield cwd_dir - -if hasattr(os, "umask"): - @contextlib.contextmanager - def temp_umask(umask): - """Context manager that temporarily sets the process umask.""" - oldmask = os.umask(umask) - try: - yield - finally: - os.umask(oldmask) - -# TEST_HOME_DIR refers to the top level directory of the "test" package -# that contains Python's regression test suite -TEST_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__)) -TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR) - # TEST_DATA_DIR is used as a target download location for remote resources TEST_DATA_DIR = os.path.join(TEST_HOME_DIR, "data") + +def darwin_malloc_err_warning(test_name): + """Assure user that loud errors generated by macOS libc's malloc are + expected.""" + if sys.platform != 'darwin': + return + + import shutil + msg = ' NOTICE ' + detail = (f'{test_name} may generate "malloc can\'t allocate region"\n' + 'warnings on macOS systems. This behavior is known. Do not\n' + 'report a bug unless tests are also failing. See bpo-40928.') + + padding, _ = shutil.get_terminal_size() + print(msg.center(padding, '-')) + print(detail) + print('-' * padding) + + def findfile(filename, subdir=None): """Try to find a file on sys.path or in the test directory. If it is not found the argument passed to the function is returned (this does not @@ -1201,10 +996,6 @@ def findfile(filename, subdir=None): if os.path.exists(fn): return fn return filename -def create_empty_file(filename): - """Create an empty file. If the file already exists, truncate it.""" - fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) - os.close(fd) def sortdict(dict): "Like repr(dict), but in sorted order." @@ -1213,19 +1004,6 @@ def sortdict(dict): withcommas = ", ".join(reprpairs) return "{%s}" % withcommas -def make_bad_fd(): - """ - Create an invalid file descriptor by opening and closing a file and return - its fd. - """ - file = open(TESTFN, "wb") - try: - return file.fileno() - finally: - file.close() - unlink(TESTFN) - - def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=None): with testcase.assertRaisesRegex(SyntaxError, errtext) as cm: compile(statement, '', 'exec') @@ -1266,6 +1044,11 @@ def check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=No def open_urlresource(url, *args, **kw): import urllib.request, urllib.parse + from .os_helper import unlink + try: + import gzip + except ImportError: + gzip = None check = kw.pop('check', None) @@ -1296,7 +1079,7 @@ def open_urlresource(url, *args, **kw): opener = urllib.request.build_opener() if gzip: opener.addheaders.append(('Accept-Encoding', 'gzip')) - f = opener.open(url, timeout=15) + f = opener.open(url, timeout=INTERNET_TIMEOUT) if gzip and f.headers.get('Content-Encoding') == 'gzip': f = gzip.GzipFile(fileobj=f) try: @@ -1474,59 +1257,6 @@ class CleanImport(object): sys.modules.update(self.original_modules) -class EnvironmentVarGuard(collections.abc.MutableMapping): - - """Class to help protect the environment variable properly. Can be used as - a context manager.""" - - def __init__(self): - self._environ = os.environ - self._changed = {} - - def __getitem__(self, envvar): - return self._environ[envvar] - - def __setitem__(self, envvar, value): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - self._environ[envvar] = value - - def __delitem__(self, envvar): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - if envvar in self._environ: - del self._environ[envvar] - - def keys(self): - return self._environ.keys() - - def __iter__(self): - return iter(self._environ) - - def __len__(self): - return len(self._environ) - - def set(self, envvar, value): - self[envvar] = value - - def unset(self, envvar): - del self[envvar] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - for (k, v) in self._changed.items(): - if v is None: - if k in self._environ: - del self._environ[k] - else: - self._environ[k] = v - os.environ = self._environ - - class DirsOnSysPath(object): """Context manager to temporarily add directories to sys.path. @@ -1737,6 +1467,7 @@ def gc_collect(): longer than expected. This function tries its best to force all garbage objects to disappear. """ + # TODO: RUSTPYTHON (comment out before) # gc.collect() # if is_jython: # time.sleep(0.1) @@ -1746,6 +1477,7 @@ def gc_collect(): @contextlib.contextmanager def disable_gc(): + # TODO: RUSTPYTHON (comment out before) # have_gc = gc.isenabled() # gc.disable() # try: @@ -1784,12 +1516,15 @@ _TPFLAGS_HAVE_GC = 1<<14 _TPFLAGS_HEAPTYPE = 1<<9 def check_sizeof(test, o, size): - import _testcapi + try: + import _testinternalcapi + except ImportError: + raise unittest.SkipTest("_testinternalcapi required") result = sys.getsizeof(o) # add GC header size if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))): - size += _testcapi.SIZEOF_PYGC_HEAD + size += _testinternalcapi.SIZEOF_PYGC_HEAD msg = 'wrong size for %s: got %d, expected %d' \ % (type(o), result, size) test.assertEqual(result, size, msg) @@ -1798,9 +1533,8 @@ def check_sizeof(test, o, size): # Decorator for running a function in a different locale, correctly resetting # it afterwards. +@contextlib.contextmanager def run_with_locale(catstr, *locales): - def decorator(func): - def inner(*args, **kwds): try: import locale category = getattr(locale, catstr) @@ -1819,16 +1553,11 @@ def run_with_locale(catstr, *locales): except: pass - # now run the function, resetting the locale on exceptions try: - return func(*args, **kwds) + yield finally: if locale and orig_locale: locale.setlocale(category, orig_locale) - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator #======================================================================= # Decorator for running a function in a specific timezone, correctly @@ -1907,6 +1636,7 @@ class _MemoryWatchdog: self.started = False def start(self): + import warnings try: f = open(self.procfile, 'r') except OSError as e: @@ -1915,6 +1645,7 @@ class _MemoryWatchdog: sys.stderr.flush() return + import subprocess with f: watchdog_script = findfile("memory_watchdog.py") self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], @@ -2048,7 +1779,7 @@ def check_impl_detail(**guards): if check_impl_detail(cpython=False): # everywhere except on CPython """ guards, default = _parse_guards(guards) - return guards.get(platform.python_implementation().lower(), default) + return guards.get(sys.implementation.name, default) def no_tracing(func): @@ -2111,7 +1842,9 @@ def _run_suite(suite): else: err = "multiple errors occurred" if not verbose: err += "; run in verbose mode for details" - raise TestFailed(err) + errors = [(str(tc), exc_str) for tc, exc_str in result.errors] + failures = [(str(tc), exc_str) for tc, exc_str in result.failures] + raise TestFailedWithDetails(err, errors, failures) # By default, don't filter tests @@ -2182,6 +1915,7 @@ def _compile_match_function(patterns): # The test.bisect_cmd utility only uses such full test identifiers. func = set(patterns).__contains__ else: + import fnmatch regex = '|'.join(map(fnmatch.translate, patterns)) # The search *is* case sensitive on purpose: # don't use flags=re.IGNORECASE @@ -2206,17 +1940,18 @@ def _compile_match_function(patterns): def run_unittest(*classes): """Run tests from unittest.TestCase-derived classes.""" valid_types = (unittest.TestSuite, unittest.TestCase) + loader = unittest.TestLoader() suite = unittest.TestSuite() for cls in classes: if isinstance(cls, str): if cls in sys.modules: - suite.addTest(unittest.findTestCases(sys.modules[cls])) + suite.addTest(loader.loadTestsFromModule(sys.modules[cls])) else: raise ValueError("str arguments must be keys in sys.modules") elif isinstance(cls, valid_types): suite.addTest(cls) else: - suite.addTest(unittest.makeSuite(cls)) + suite.addTest(loader.loadTestsFromTestCase(cls)) _filter_suite(suite, match_test) _run_suite(suite) @@ -2270,11 +2005,24 @@ def run_doctest(module, verbosity=None, optionflags=0): #======================================================================= # Support for saving and restoring the imported modules. +def flush_std_streams(): + if sys.stdout is not None: + sys.stdout.flush() + if sys.stderr is not None: + sys.stderr.flush() + + def print_warning(msg): - # bpo-39983: Print into sys.__stderr__ to display the warning even - # when sys.stderr is captured temporarily by a test + # bpo-45410: Explicitly flush stdout to keep logs in order + flush_std_streams() + stream = print_warning.orig_stderr for line in msg.splitlines(): - print(f"Warning -- {line}", file=sys.__stderr__, flush=True) + print(f"Warning -- {line}", file=stream) + stream.flush() + +# bpo-39983: Store the original sys.stderr at Python startup to be able to +# log warnings even if sys.stderr is captured temporarily by a test. +print_warning.orig_stderr = sys.stderr def modules_setup(): return sys.modules.copy(), @@ -2302,7 +2050,7 @@ def modules_cleanup(oldmodules): # to check if a test modified the environment. The flag should be set to False # before running a new test. # -# For example, threading_cleanup() sets the flag is the function fails +# For example, threading_helper.threading_cleanup() sets the flag is the function fails # to cleanup threads. environment_altered = False @@ -2548,11 +2296,13 @@ requires_type_collecting = unittest.skipIf(hasattr(sys, 'getcounts'), def args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current settings in sys.flags and sys.warnoptions.""" + import subprocess return subprocess._args_from_interpreter_flags() def optim_args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current optimization settings in sys.flags.""" + import subprocess return subprocess._optim_args_from_interpreter_flags() #============================================================ @@ -2621,28 +2371,6 @@ class Matcher(object): return result -_can_symlink = None -def can_symlink(): - global _can_symlink - if _can_symlink is not None: - return _can_symlink - symlink_path = TESTFN + "can_symlink" - try: - os.symlink(TESTFN, symlink_path) - can = True - except (OSError, NotImplementedError, AttributeError): - can = False - else: - os.remove(symlink_path) - _can_symlink = can - return can - -def skip_unless_symlink(test): - """Skip decorator for tests that require functional symlink""" - ok = can_symlink() - msg = "Requires functional symlink implementation" - return test if ok else unittest.skip(msg)(test) - _buggy_ucrt = None def skip_if_buggy_ucrt_strfptime(test): """ @@ -2654,6 +2382,7 @@ def skip_if_buggy_ucrt_strfptime(test): See bpo-37552 [Windows] strptime/strftime return invalid results with UCRT version 17763.615 """ + import locale global _buggy_ucrt if _buggy_ucrt is None: if(sys.platform == 'win32' and @@ -2667,6 +2396,8 @@ def skip_if_buggy_ucrt_strfptime(test): class PythonSymlink: """Creates a symlink for the current Python executable""" def __init__(self, link=None): + from .os_helper import TESTFN + self.link = link or os.path.abspath(TESTFN) self._linked = [] self.real = os.path.realpath(sys.executable) @@ -2681,6 +2412,7 @@ class PythonSymlink: if sys.platform == "win32": def _platform_specific(self): + import glob import _winapi if os.path.lexists(self.real) and not os.path.exists(self.real): @@ -2695,7 +2427,7 @@ class PythonSymlink: dll, os.path.join(dest_dir, os.path.basename(dll)) )) - for runtime in glob.glob(os.path.join(src_dir, "vcruntime*.dll")): + for runtime in glob.glob(os.path.join(glob.escape(src_dir), "vcruntime*.dll")): self._also_link.append(( runtime, os.path.join(dest_dir, os.path.basename(runtime)) @@ -2704,7 +2436,7 @@ class PythonSymlink: self._env = {k.upper(): os.getenv(k) for k in os.environ} self._env["PYTHONHOME"] = os.path.dirname(self.real) if sysconfig.is_python_build(True): - self._env["PYTHONPATH"] = os.path.dirname(os.__file__) + self._env["PYTHONPATH"] = STDLIB_DIR def __enter__(self): os.symlink(self.real, self.link) @@ -2723,6 +2455,7 @@ class PythonSymlink: print("failed to clean up {}: {}".format(link, ex)) def _call(self, python, args, env, returncode): + import subprocess cmd = [python, *args] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) @@ -2742,43 +2475,6 @@ class PythonSymlink: return self._call(self.link, args, self._env, returncode) -_can_xattr = None -def can_xattr(): - global _can_xattr - if _can_xattr is not None: - return _can_xattr - if not hasattr(os, "setxattr"): - can = False - else: - tmp_dir = tempfile.mkdtemp() - tmp_fp, tmp_name = tempfile.mkstemp(dir=tmp_dir) - try: - with open(TESTFN, "wb") as fp: - try: - # TESTFN & tempfile may use different file systems with - # different capabilities - os.setxattr(tmp_fp, b"user.test", b"") - os.setxattr(tmp_name, b"trusted.foo", b"42") - os.setxattr(fp.fileno(), b"user.test", b"") - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match(r"2.6.(\d{1,2})", kernel_version) - can = m is None or int(m.group(1)) >= 39 - except OSError: - can = False - finally: - unlink(TESTFN) - unlink(tmp_name) - rmdir(tmp_dir) - _can_xattr = can - return can - -def skip_unless_xattr(test): - """Skip decorator for tests that require functional extended attributes""" - ok = can_xattr() - msg = "no non-broken extended attribute support" - return test if ok else unittest.skip(msg)(test) - def skip_if_pgo_task(test): """Skip decorator for tests not run in (non-extended) PGO task""" ok = not PGO or PGO_EXTENDED @@ -2788,6 +2484,7 @@ def skip_if_pgo_task(test): _bind_nix_socket_error = None def skip_unless_bind_unix_socket(test): """Decorator for tests requiring a functional bind() for unix sockets.""" + from .os_helper import TESTFN if not hasattr(socket, 'AF_UNIX'): return unittest.skip('No UNIX Sockets')(test) global _bind_nix_socket_error @@ -2808,18 +2505,6 @@ def skip_unless_bind_unix_socket(test): return test -def fs_is_case_insensitive(directory): - """Detects if the file system for the specified directory is case-insensitive.""" - with tempfile.NamedTemporaryFile(dir=directory) as base: - base_path = base.name - case_path = base_path.upper() - if case_path == base_path: - case_path = base_path.lower() - try: - return os.path.samefile(base_path, case_path) - except FileNotFoundError: - return False - def detect_api_mismatch(ref_api, other_api, *, ignore=()): """Returns the set of items in ref_api not in other_api, except for a @@ -2837,7 +2522,7 @@ def detect_api_mismatch(ref_api, other_api, *, ignore=()): def check__all__(test_case, module, name_of_module=None, extra=(), - blacklist=()): + not_exported=()): """Assert that the __all__ variable of 'module' contains all public names. The module's public names (its API) are detected automatically based on @@ -2854,7 +2539,7 @@ def check__all__(test_case, module, name_of_module=None, extra=(), '__module__' attribute. If provided, it will be added to the automatically detected ones. - The 'blacklist' argument can be a set of names that must not be treated + The 'not_exported' argument can be a set of names that must not be treated as part of the public API even though their names indicate otherwise. Usage: @@ -2870,10 +2555,10 @@ def check__all__(test_case, module, name_of_module=None, extra=(), class OtherTestCase(unittest.TestCase): def test__all__(self): extra = {'BAR_CONST', 'FOO_CONST'} - blacklist = {'baz'} # Undocumented name. + not_exported = {'baz'} # Undocumented name. # bar imports part of its API from _bar. support.check__all__(self, bar, ('bar', '_bar'), - extra=extra, blacklist=blacklist) + extra=extra, not_exported=not_exported) """ @@ -2885,7 +2570,7 @@ def check__all__(test_case, module, name_of_module=None, extra=(), expected = set(extra) for name in dir(module): - if name.startswith('_') or name in blacklist: + if name.startswith('_') or name in not_exported: continue obj = getattr(module, name) if (getattr(obj, '__module__', None) in name_of_module or @@ -2895,6 +2580,27 @@ def check__all__(test_case, module, name_of_module=None, extra=(), test_case.assertCountEqual(module.__all__, expected) +def suppress_msvcrt_asserts(verbose=False): + try: + import msvcrt + except ImportError: + return + + msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS + | msvcrt.SEM_NOALIGNMENTFAULTEXCEPT + | msvcrt.SEM_NOGPFAULTERRORBOX + | msvcrt.SEM_NOOPENFILEERRORBOX) + + # CrtSetReportMode() is only available in debug build + if hasattr(msvcrt, 'CrtSetReportMode'): + for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: + if verbose: + msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) + msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) + else: + msvcrt.CrtSetReportMode(m, 0) + + class SuppressCrashReport: """Try to prevent a crash report from popping up. @@ -2906,30 +2612,25 @@ class SuppressCrashReport: def __enter__(self): """On Windows, disable Windows Error Reporting dialogs using - SetErrorMode. + SetErrorMode() and CrtSetReportMode(). On UNIX, try to save the previous core file size limit, then set soft limit to 0. """ if sys.platform.startswith('win'): # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx - # GetErrorMode is not available on Windows XP and Windows Server 2003, - # but SetErrorMode returns the previous value, so we can use that - import ctypes - self._k32 = ctypes.windll.kernel32 - SEM_NOGPFAULTERRORBOX = 0x02 - self.old_value = self._k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) - self._k32.SetErrorMode(self.old_value | SEM_NOGPFAULTERRORBOX) - - # Suppress assert dialogs in debug builds - # (see http://bugs.python.org/issue23314) try: import msvcrt - msvcrt.CrtSetReportMode - except (AttributeError, ImportError): - # no msvcrt or a release build - pass - else: + except ImportError: + return + + self.old_value = msvcrt.GetErrorMode() + + msvcrt.SetErrorMode(self.old_value | msvcrt.SEM_NOGPFAULTERRORBOX) + + # bpo-23314: Suppress assert dialogs in debug builds. + # CrtSetReportMode() is only available in debug build. + if hasattr(msvcrt, 'CrtSetReportMode'): self.old_modes = {} for report_type in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, @@ -2941,15 +2642,21 @@ class SuppressCrashReport: self.old_modes[report_type] = old_mode, old_file else: - if resource is not None: + try: + import resource + self.resource = resource + except ImportError: + self.resource = None + if self.resource is not None: try: - self.old_value = resource.getrlimit(resource.RLIMIT_CORE) - resource.setrlimit(resource.RLIMIT_CORE, + self.old_value = self.resource.getrlimit(self.resource.RLIMIT_CORE) + self.resource.setrlimit(self.resource.RLIMIT_CORE, (0, self.old_value[1])) except (ValueError, OSError): pass if sys.platform == 'darwin': + import subprocess # Check if the 'Crash Reporter' on OSX was configured # in 'Developer' mode and warn that it will get triggered # when it is. @@ -2975,17 +2682,17 @@ class SuppressCrashReport: return if sys.platform.startswith('win'): - self._k32.SetErrorMode(self.old_value) + import msvcrt + msvcrt.SetErrorMode(self.old_value) if self.old_modes: - import msvcrt for report_type, (old_mode, old_file) in self.old_modes.items(): msvcrt.CrtSetReportMode(report_type, old_mode) msvcrt.CrtSetReportFile(report_type, old_file) else: - if resource is not None: + if self.resource is not None: try: - resource.setrlimit(resource.RLIMIT_CORE, self.old_value) + self.resource.setrlimit(self.resource.RLIMIT_CORE, self.old_value) except (ValueError, OSError): pass @@ -3044,6 +2751,7 @@ def run_in_subinterp(code): return _testcapi.run_in_subinterp(code) +# TODO: RUSTPYTHON (comment out before) # def check_free_after_iterating(test, iter, cls, args=()): # class A(cls): # def __del__(self): @@ -3072,9 +2780,16 @@ def missing_compiler_executable(cmd_names=[]): missing. """ - from distutils import ccompiler, sysconfig, spawn + # TODO (PEP 632): alternate check without using distutils + from distutils import ccompiler, sysconfig, spawn, errors compiler = ccompiler.new_compiler() sysconfig.customize_compiler(compiler) + if compiler.compiler_type == "msvc": + # MSVC has no executables, so check whether initialization succeeds + try: + compiler.initialize() + except errors.DistutilsPlatformError: + return "msvc" for name in compiler.executables: if cmd_names and name not in cmd_names: continue @@ -3096,6 +2811,7 @@ def setswitchinterval(interval): if is_android and interval < minimum_interval: global _is_android_emulator if _is_android_emulator is None: + import subprocess _is_android_emulator = (subprocess.check_output( ['getprop', 'ro.kernel.qemu']).strip() == b'1') if _is_android_emulator: @@ -3105,6 +2821,8 @@ def setswitchinterval(interval): @contextlib.contextmanager def disable_faulthandler(): + import faulthandler + # use sys.__stderr__ instead of sys.stderr, since regrtest replaces # sys.stderr with a StringIO which has no file descriptor when a test # is run with -W/--verbose3. @@ -3119,65 +2837,6 @@ def disable_faulthandler(): faulthandler.enable(file=fd, all_threads=True) -def fd_count(): - """Count the number of open file descriptors. - """ - if sys.platform.startswith(('linux', 'freebsd')): - try: - names = os.listdir("/proc/self/fd") - # Subtract one because listdir() internally opens a file - # descriptor to list the content of the /proc/self/fd/ directory. - return len(names) - 1 - except FileNotFoundError: - pass - - MAXFD = 256 - if hasattr(os, 'sysconf'): - try: - MAXFD = os.sysconf("SC_OPEN_MAX") - except OSError: - pass - - old_modes = None - if sys.platform == 'win32': - # bpo-25306, bpo-31009: Call CrtSetReportMode() to not kill the process - # on invalid file descriptor if Python is compiled in debug mode - try: - import msvcrt - msvcrt.CrtSetReportMode - except (AttributeError, ImportError): - # no msvcrt or a release build - pass - else: - old_modes = {} - for report_type in (msvcrt.CRT_WARN, - msvcrt.CRT_ERROR, - msvcrt.CRT_ASSERT): - old_modes[report_type] = msvcrt.CrtSetReportMode(report_type, 0) - - try: - count = 0 - for fd in range(MAXFD): - try: - # Prefer dup() over fstat(). fstat() can require input/output - # whereas dup() doesn't. - fd2 = os.dup(fd) - except OSError as e: - if e.errno != errno.EBADF: - raise - else: - os.close(fd2) - count += 1 - finally: - if old_modes is not None: - for report_type in (msvcrt.CRT_WARN, - msvcrt.CRT_ERROR, - msvcrt.CRT_ASSERT): - msvcrt.CrtSetReportMode(report_type, old_modes[report_type]) - - return count - - class SaveSignals: """ Save and restore signal handlers. @@ -3222,24 +2881,6 @@ def with_pymalloc(): return _testcapi.WITH_PYMALLOC -class FakePath: - """Simple implementing of the path protocol. - """ - def __init__(self, path): - self.path = path - - def __repr__(self): - return f'' - - def __fspath__(self): - if (isinstance(self.path, BaseException) or - isinstance(self.path, type) and - issubclass(self.path, BaseException)): - raise self.path - else: - return self.path - - class _ALWAYS_EQ: """ Object that is equal to anything. @@ -3251,6 +2892,19 @@ class _ALWAYS_EQ: ALWAYS_EQ = _ALWAYS_EQ() +class _NEVER_EQ: + """ + Object that is not equal to anything. + """ + def __eq__(self, other): + return False + def __ne__(self, other): + return True + def __hash__(self): + return 1 + +NEVER_EQ = _NEVER_EQ() + @functools.total_ordering class _LARGEST: """ @@ -3277,6 +2931,7 @@ SMALLEST = _SMALLEST() def maybe_get_event_loop_policy(): """Return the global event loop policy if one is set, else return None.""" + import asyncio.events return asyncio.events._event_loop_policy # Helpers for testing hashing. @@ -3426,7 +3081,9 @@ def wait_process(pid, *, exitcode, timeout=None): """ Wait until process pid completes and check that the process exit code is exitcode. + Raise an AssertionError if the process exit code is not equal to exitcode. + If the process runs longer than timeout seconds (SHORT_TIMEOUT by default), kill the process (if signal.SIGKILL is available) and raise an AssertionError. The timeout feature is not available on Windows. @@ -3470,4 +3127,88 @@ def wait_process(pid, *, exitcode, timeout=None): # sanity check: it should not fail in practice if pid2 != pid: - raise AssertionError(f"pid {pid2} != pid {pid}") \ No newline at end of file + raise AssertionError(f"pid {pid2} != pid {pid}") + +def skip_if_broken_multiprocessing_synchronize(): + """ + Skip tests if the multiprocessing.synchronize module is missing, if there + is no available semaphore implementation, or if creating a lock raises an + OSError (on Linux only). + """ + from .import_helper import import_module + + # Skip tests if the _multiprocessing extension is missing. + import_module('_multiprocessing') + + # Skip tests if there is no available semaphore implementation: + # multiprocessing.synchronize requires _multiprocessing.SemLock. + synchronize = import_module('multiprocessing.synchronize') + + if sys.platform == "linux": + try: + # bpo-38377: On Linux, creating a semaphore fails with OSError + # if the current user does not have the permission to create + # a file in /dev/shm/ directory. + synchronize.Lock(ctx=None) + except OSError as exc: + raise unittest.SkipTest(f"broken multiprocessing SemLock: {exc!r}") + + +def check_disallow_instantiation(testcase, tp, *args, **kwds): + """ + Check that given type cannot be instantiated using *args and **kwds. + + See bpo-43916: Add Py_TPFLAGS_DISALLOW_INSTANTIATION type flag. + """ + mod = tp.__module__ + name = tp.__name__ + if mod != 'builtins': + qualname = f"{mod}.{name}" + else: + qualname = f"{name}" + msg = f"cannot create '{re.escape(qualname)}' instances" + testcase.assertRaisesRegex(TypeError, msg, tp, *args, **kwds) + +@contextlib.contextmanager +def infinite_recursion(max_depth=75): + """Set a lower limit for tests that interact with infinite recursions + (e.g test_ast.ASTHelpers_Test.test_recursion_direct) since on some + debug windows builds, due to not enough functions being inlined the + stack size might not handle the default recursion limit (1000). See + bpo-11105 for details.""" + + original_depth = sys.getrecursionlimit() + try: + sys.setrecursionlimit(max_depth) + yield + finally: + sys.setrecursionlimit(original_depth) + +def ignore_deprecations_from(module: str, *, like: str) -> object: + token = object() + warnings.filterwarnings( + "ignore", + category=DeprecationWarning, + module=module, + message=like + fr"(?#support{id(token)})", + ) + return token + +def clear_ignored_deprecations(*tokens: object) -> None: + if not tokens: + raise ValueError("Provide token or tokens returned by ignore_deprecations_from") + + new_filters = [] + endswith = tuple(rf"(?#support{id(token)})" for token in tokens) + for action, message, category, module, lineno in warnings.filters: + if action == "ignore" and category is DeprecationWarning: + if isinstance(message, re.Pattern): + msg = message.pattern + else: + msg = message or "" + if msg.endswith(endswith): + continue + new_filters.append((action, message, category, module, lineno)) + if warnings.filters != new_filters: + warnings.filters[:] = new_filters + warnings._filters_mutated() diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py new file mode 100644 index 000000000..75f9b6c1d --- /dev/null +++ b/Lib/test/support/os_helper.py @@ -0,0 +1,617 @@ +import collections.abc +import contextlib +import errno +import os +import re +import stat +import sys +import time +import unittest +import warnings + + +# Filename used for testing +if os.name == 'java': + # Jython disallows @ in module names + TESTFN_ASCII = '$test' +else: + TESTFN_ASCII = '@test' + +# Disambiguate TESTFN for parallel testing, while letting it remain a valid +# module name. +TESTFN_ASCII = "{}_{}_tmp".format(TESTFN_ASCII, os.getpid()) + +# TESTFN_UNICODE is a non-ascii filename +TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" +if sys.platform == 'darwin': + # In Mac OS X's VFS API file names are, by definition, canonically + # decomposed Unicode, encoded using UTF-8. See QA1173: + # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html + import unicodedata + TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) + +# TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be +# encoded by the filesystem encoding (in strict mode). It can be None if we +# cannot generate such filename. +TESTFN_UNENCODABLE = None +if os.name == 'nt': + # skip win32s (0) or Windows 9x/ME (1) + if sys.getwindowsversion().platform >= 2: + # Different kinds of characters from various languages to minimize the + # probability that the whole name is encodable to MBCS (issue #9819) + TESTFN_UNENCODABLE = TESTFN_ASCII + "-\u5171\u0141\u2661\u0363\uDC80" + try: + TESTFN_UNENCODABLE.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + pass + else: + print('WARNING: The filename %r CAN be encoded by the filesystem ' + 'encoding (%s). Unicode filename tests may not be effective' + % (TESTFN_UNENCODABLE, sys.getfilesystemencoding())) + TESTFN_UNENCODABLE = None +# TODO: RUSTPYTHON comment out before +# # Mac OS X denies unencodable filenames (invalid utf-8) +# elif sys.platform != 'darwin': +# try: +# # ascii and utf-8 cannot encode the byte 0xff +# b'\xff'.decode(sys.getfilesystemencoding()) +# except UnicodeDecodeError: +# # 0xff will be encoded using the surrogate character u+DCFF +# TESTFN_UNENCODABLE = TESTFN_ASCII \ +# + b'-\xff'.decode(sys.getfilesystemencoding(), 'surrogateescape') +# else: +# # File system encoding (eg. ISO-8859-* encodings) can encode +# # the byte 0xff. Skip some unicode filename tests. +# pass + +# FS_NONASCII: non-ASCII character encodable by os.fsencode(), +# or an empty string if there is no such character. +FS_NONASCII = '' +for character in ( + # First try printable and common characters to have a readable filename. + # For each character, the encoding list are just example of encodings able + # to encode the character (the list is not exhaustive). + + # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 + '\u00E6', + # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 + '\u0130', + # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 + '\u0141', + # U+03C6 (Greek Small Letter Phi): cp1253 + '\u03C6', + # U+041A (Cyrillic Capital Letter Ka): cp1251 + '\u041A', + # U+05D0 (Hebrew Letter Alef): Encodable to cp424 + '\u05D0', + # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic + '\u060C', + # U+062A (Arabic Letter Teh): cp720 + '\u062A', + # U+0E01 (Thai Character Ko Kai): cp874 + '\u0E01', + + # Then try more "special" characters. "special" because they may be + # interpreted or displayed differently depending on the exact locale + # encoding and the font. + + # U+00A0 (No-Break Space) + '\u00A0', + # U+20AC (Euro Sign) + '\u20AC', +): + try: + # If Python is set up to use the legacy 'mbcs' in Windows, + # 'replace' error mode is used, and encode() returns b'?' + # for characters missing in the ANSI codepage + if os.fsdecode(os.fsencode(character)) != character: + raise UnicodeError + except UnicodeError: + pass + else: + FS_NONASCII = character + break + +# Save the initial cwd +SAVEDCWD = os.getcwd() + +# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be +# decoded from the filesystem encoding (in strict mode). It can be None if we +# cannot generate such filename (ex: the latin1 encoding can decode any byte +# sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks +# to the surrogateescape error handler (PEP 383), but not from the filesystem +# encoding in strict mode. +TESTFN_UNDECODABLE = None +for name in ( + # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows + # accepts it to create a file or a directory, or don't accept to enter to + # such directory (when the bytes name is used). So test b'\xe7' first: + # it is not decodable from cp932. + b'\xe7w\xf0', + # undecodable from ASCII, UTF-8 + b'\xff', + # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 + # and cp857 + b'\xae\xd5' + # undecodable from UTF-8 (UNIX and Mac OS X) + b'\xed\xb2\x80', b'\xed\xb4\x80', + # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, + # cp1253, cp1254, cp1255, cp1257, cp1258 + b'\x81\x98', +): + try: + name.decode(sys.getfilesystemencoding()) + except UnicodeDecodeError: + TESTFN_UNDECODABLE = os.fsencode(TESTFN_ASCII) + name + break + +if FS_NONASCII: + TESTFN_NONASCII = TESTFN_ASCII + FS_NONASCII +else: + TESTFN_NONASCII = None +TESTFN = TESTFN_NONASCII or TESTFN_ASCII + + +def make_bad_fd(): + """ + Create an invalid file descriptor by opening and closing a file and return + its fd. + """ + file = open(TESTFN, "wb") + try: + return file.fileno() + finally: + file.close() + unlink(TESTFN) + + +_can_symlink = None + + +def can_symlink(): + global _can_symlink + if _can_symlink is not None: + return _can_symlink + symlink_path = TESTFN + "can_symlink" + try: + os.symlink(TESTFN, symlink_path) + can = True + except (OSError, NotImplementedError, AttributeError): + can = False + else: + os.remove(symlink_path) + _can_symlink = can + return can + + +def skip_unless_symlink(test): + """Skip decorator for tests that require functional symlink""" + ok = can_symlink() + msg = "Requires functional symlink implementation" + return test if ok else unittest.skip(msg)(test) + + +_can_xattr = None + + +def can_xattr(): + import tempfile + global _can_xattr + if _can_xattr is not None: + return _can_xattr + if not hasattr(os, "setxattr"): + can = False + else: + import platform + tmp_dir = tempfile.mkdtemp() + tmp_fp, tmp_name = tempfile.mkstemp(dir=tmp_dir) + try: + with open(TESTFN, "wb") as fp: + try: + # TESTFN & tempfile may use different file systems with + # different capabilities + os.setxattr(tmp_fp, b"user.test", b"") + os.setxattr(tmp_name, b"trusted.foo", b"42") + os.setxattr(fp.fileno(), b"user.test", b"") + # Kernels < 2.6.39 don't respect setxattr flags. + kernel_version = platform.release() + m = re.match(r"2.6.(\d{1,2})", kernel_version) + can = m is None or int(m.group(1)) >= 39 + except OSError: + can = False + finally: + unlink(TESTFN) + unlink(tmp_name) + rmdir(tmp_dir) + _can_xattr = can + return can + + +def skip_unless_xattr(test): + """Skip decorator for tests that require functional extended attributes""" + ok = can_xattr() + msg = "no non-broken extended attribute support" + return test if ok else unittest.skip(msg)(test) + + +def unlink(filename): + try: + _unlink(filename) + except (FileNotFoundError, NotADirectoryError): + pass + + +if sys.platform.startswith("win"): + def _waitfor(func, pathname, waitall=False): + # Perform the operation + func(pathname) + # Now setup the wait loop + if waitall: + dirname = pathname + else: + dirname, name = os.path.split(pathname) + dirname = dirname or '.' + # Check for `pathname` to be removed from the filesystem. + # The exponential backoff of the timeout amounts to a total + # of ~1 second after which the deletion is probably an error + # anyway. + # Testing on an i7@4.3GHz shows that usually only 1 iteration is + # required when contention occurs. + timeout = 0.001 + while timeout < 1.0: + # Note we are only testing for the existence of the file(s) in + # the contents of the directory regardless of any security or + # access rights. If we have made it this far, we have sufficient + # permissions to do that much using Python's equivalent of the + # Windows API FindFirstFile. + # Other Windows APIs can fail or give incorrect results when + # dealing with files that are pending deletion. + L = os.listdir(dirname) + if not (L if waitall else name in L): + return + # Increase the timeout and try again + time.sleep(timeout) + timeout *= 2 + warnings.warn('tests may fail, delete still pending for ' + pathname, + RuntimeWarning, stacklevel=4) + + def _unlink(filename): + _waitfor(os.unlink, filename) + + def _rmdir(dirname): + _waitfor(os.rmdir, dirname) + + def _rmtree(path): + from test.support import _force_run + + def _rmtree_inner(path): + for name in _force_run(path, os.listdir, path): + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except OSError as exc: + print("os_helper.rmtree(): os.lstat(%r) failed with %s" + % (fullname, exc), + file=sys.__stderr__) + mode = 0 + if stat.S_ISDIR(mode): + _waitfor(_rmtree_inner, fullname, waitall=True) + _force_run(fullname, os.rmdir, fullname) + else: + _force_run(fullname, os.unlink, fullname) + _waitfor(_rmtree_inner, path, waitall=True) + _waitfor(lambda p: _force_run(p, os.rmdir, p), path) + + def _longpath(path): + try: + import ctypes + except ImportError: + # No ctypes means we can't expands paths. + pass + else: + buffer = ctypes.create_unicode_buffer(len(path) * 2) + length = ctypes.windll.kernel32.GetLongPathNameW(path, buffer, + len(buffer)) + if length: + return buffer[:length] + return path +else: + _unlink = os.unlink + _rmdir = os.rmdir + + def _rmtree(path): + import shutil + try: + shutil.rmtree(path) + return + except OSError: + pass + + def _rmtree_inner(path): + from test.support import _force_run + for name in _force_run(path, os.listdir, path): + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except OSError: + mode = 0 + if stat.S_ISDIR(mode): + _rmtree_inner(fullname) + _force_run(path, os.rmdir, fullname) + else: + _force_run(path, os.unlink, fullname) + _rmtree_inner(path) + os.rmdir(path) + + def _longpath(path): + return path + + +def rmdir(dirname): + try: + _rmdir(dirname) + except FileNotFoundError: + pass + + +def rmtree(path): + try: + _rmtree(path) + except FileNotFoundError: + pass + + +@contextlib.contextmanager +def temp_dir(path=None, quiet=False): + """Return a context manager that creates a temporary directory. + + Arguments: + + path: the directory to create temporarily. If omitted or None, + defaults to creating a temporary directory using tempfile.mkdtemp. + + quiet: if False (the default), the context manager raises an exception + on error. Otherwise, if the path is specified and cannot be + created, only a warning is issued. + + """ + import tempfile + dir_created = False + if path is None: + path = tempfile.mkdtemp() + dir_created = True + path = os.path.realpath(path) + else: + try: + os.mkdir(path) + dir_created = True + except OSError as exc: + if not quiet: + raise + warnings.warn(f'tests may fail, unable to create ' + f'temporary directory {path!r}: {exc}', + RuntimeWarning, stacklevel=3) + if dir_created: + pid = os.getpid() + try: + yield path + finally: + # In case the process forks, let only the parent remove the + # directory. The child has a different process id. (bpo-30028) + if dir_created and pid == os.getpid(): + rmtree(path) + + +@contextlib.contextmanager +def change_cwd(path, quiet=False): + """Return a context manager that changes the current working directory. + + Arguments: + + path: the directory to use as the temporary current working directory. + + quiet: if False (the default), the context manager raises an exception + on error. Otherwise, it issues only a warning and keeps the current + working directory the same. + + """ + saved_dir = os.getcwd() + try: + os.chdir(os.path.realpath(path)) + except OSError as exc: + if not quiet: + raise + warnings.warn(f'tests may fail, unable to change the current working ' + f'directory to {path!r}: {exc}', + RuntimeWarning, stacklevel=3) + try: + yield os.getcwd() + finally: + os.chdir(saved_dir) + + +@contextlib.contextmanager +def temp_cwd(name='tempcwd', quiet=False): + """ + Context manager that temporarily creates and changes the CWD. + + The function temporarily changes the current working directory + after creating a temporary directory in the current directory with + name *name*. If *name* is None, the temporary directory is + created using tempfile.mkdtemp. + + If *quiet* is False (default) and it is not possible to + create or change the CWD, an error is raised. If *quiet* is True, + only a warning is raised and the original CWD is used. + + """ + with temp_dir(path=name, quiet=quiet) as temp_path: + with change_cwd(temp_path, quiet=quiet) as cwd_dir: + yield cwd_dir + + +def create_empty_file(filename): + """Create an empty file. If the file already exists, truncate it.""" + fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) + os.close(fd) + + +def fs_is_case_insensitive(directory): + """Detects if the file system for the specified directory + is case-insensitive.""" + import tempfile + with tempfile.NamedTemporaryFile(dir=directory) as base: + base_path = base.name + case_path = base_path.upper() + if case_path == base_path: + case_path = base_path.lower() + try: + return os.path.samefile(base_path, case_path) + except FileNotFoundError: + return False + + +class FakePath: + """Simple implementing of the path protocol. + """ + def __init__(self, path): + self.path = path + + def __repr__(self): + return f'' + + def __fspath__(self): + if (isinstance(self.path, BaseException) or + isinstance(self.path, type) and + issubclass(self.path, BaseException)): + raise self.path + else: + return self.path + + +def fd_count(): + """Count the number of open file descriptors. + """ + if sys.platform.startswith(('linux', 'freebsd')): + try: + names = os.listdir("/proc/self/fd") + # Subtract one because listdir() internally opens a file + # descriptor to list the content of the /proc/self/fd/ directory. + return len(names) - 1 + except FileNotFoundError: + pass + + MAXFD = 256 + if hasattr(os, 'sysconf'): + try: + MAXFD = os.sysconf("SC_OPEN_MAX") + except OSError: + pass + + old_modes = None + if sys.platform == 'win32': + # bpo-25306, bpo-31009: Call CrtSetReportMode() to not kill the process + # on invalid file descriptor if Python is compiled in debug mode + try: + import msvcrt + msvcrt.CrtSetReportMode + except (AttributeError, ImportError): + # no msvcrt or a release build + pass + else: + old_modes = {} + for report_type in (msvcrt.CRT_WARN, + msvcrt.CRT_ERROR, + msvcrt.CRT_ASSERT): + old_modes[report_type] = msvcrt.CrtSetReportMode(report_type, + 0) + + try: + count = 0 + for fd in range(MAXFD): + try: + # Prefer dup() over fstat(). fstat() can require input/output + # whereas dup() doesn't. + fd2 = os.dup(fd) + except OSError as e: + if e.errno != errno.EBADF: + raise + else: + os.close(fd2) + count += 1 + finally: + if old_modes is not None: + for report_type in (msvcrt.CRT_WARN, + msvcrt.CRT_ERROR, + msvcrt.CRT_ASSERT): + msvcrt.CrtSetReportMode(report_type, old_modes[report_type]) + + return count + + +if hasattr(os, "umask"): + @contextlib.contextmanager + def temp_umask(umask): + """Context manager that temporarily sets the process umask.""" + oldmask = os.umask(umask) + try: + yield + finally: + os.umask(oldmask) + + +class EnvironmentVarGuard(collections.abc.MutableMapping): + + """Class to help protect the environment variable properly. Can be used as + a context manager.""" + + def __init__(self): + self._environ = os.environ + self._changed = {} + + def __getitem__(self, envvar): + return self._environ[envvar] + + def __setitem__(self, envvar, value): + # Remember the initial value on the first access + if envvar not in self._changed: + self._changed[envvar] = self._environ.get(envvar) + self._environ[envvar] = value + + def __delitem__(self, envvar): + # Remember the initial value on the first access + if envvar not in self._changed: + self._changed[envvar] = self._environ.get(envvar) + if envvar in self._environ: + del self._environ[envvar] + + def keys(self): + return self._environ.keys() + + def __iter__(self): + return iter(self._environ) + + def __len__(self): + return len(self._environ) + + def set(self, envvar, value): + self[envvar] = value + + def unset(self, envvar): + del self[envvar] + + def copy(self): + # We do what os.environ.copy() does. + return dict(self) + + def __enter__(self): + return self + + def __exit__(self, *ignore_exc): + for (k, v) in self._changed.items(): + if v is None: + if k in self._environ: + del self._environ[k] + else: + self._environ[k] = v + os.environ = self._environ From ce75d5c6a2b695221a26fb3f4aa1f72d0178486a Mon Sep 17 00:00:00 2001 From: Dean Li Date: Wed, 10 Nov 2021 23:29:00 +0800 Subject: [PATCH 08/40] test: os use os_helper --- Lib/test/test_os.py | 1163 ++++++++++++++++++++++++++++++------------- 1 file changed, 811 insertions(+), 352 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index a58abef6e..c66045f4b 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2,8 +2,6 @@ # does add tests for a few functions which have been determined to be more # portable than they had been thought to be. -import asynchat -import asyncore import codecs import contextlib import decimal @@ -12,25 +10,41 @@ import fnmatch import fractions import itertools import locale +# TODO: RUSTPYTHON # import mmap import os import pickle +import select import shutil import signal import socket import stat +import struct import subprocess import sys import sysconfig import tempfile import threading import time +import types import unittest import uuid import warnings from test import support +# TODO: RUSTPYTHON +# from test.support import import_helper +from test.support import os_helper +# TODO: RUSTPYTHON +# from test.support import socket_helper +# from test.support import threading_helper +# from test.support import warnings_helper from platform import win32_is_iot +with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + import asynchat + import asyncore + try: import resource except ImportError: @@ -53,8 +67,10 @@ try: except ImportError: INT_MAX = PY_SSIZE_T_MAX = sys.maxsize + from test.support.script_helper import assert_python_ok -from test.support import unix_shell, FakePath +from test.support import unix_shell +from test.support.os_helper import FakePath root_in_posix = False @@ -83,6 +99,11 @@ def create_file(filename, content=b'content'): fp.write(content) +# bpo-41625: On AIX, splice() only works with a socket, not with a pipe. +requires_splice_pipe = unittest.skipIf(sys.platform.startswith("aix"), + 'on AIX, splice() only accepts sockets') + + class MiscTests(unittest.TestCase): def test_getcwd(self): cwd = os.getcwd() @@ -101,12 +122,16 @@ class MiscTests(unittest.TestCase): # than MAX_PATH if long paths support is disabled: # see RtlAreLongPathsEnabled(). min_len = 2000 # characters + # On VxWorks, PATH_MAX is defined as 1024 bytes. Creating a path + # longer than PATH_MAX will fail. + if sys.platform == 'vxworks': + min_len = 1000 dirlen = 200 # characters dirname = 'python_test_dir_' dirname = dirname + ('a' * (dirlen - len(dirname))) with tempfile.TemporaryDirectory() as tmpdir: - with support.change_cwd(tmpdir) as path: + with os_helper.change_cwd(tmpdir) as path: expected = path while True: @@ -150,17 +175,17 @@ class MiscTests(unittest.TestCase): # Tests creating TESTFN class FileTests(unittest.TestCase): def setUp(self): - if os.path.lexists(support.TESTFN): - os.unlink(support.TESTFN) + if os.path.lexists(os_helper.TESTFN): + os.unlink(os_helper.TESTFN) tearDown = setUp def test_access(self): - f = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) + f = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) os.close(f) - self.assertTrue(os.access(support.TESTFN, os.W_OK)) + self.assertTrue(os.access(os_helper.TESTFN, os.W_OK)) def test_closerange(self): - first = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) + first = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) # We must allocate two consecutive file descriptors, otherwise # it will mess up other file descriptors (perhaps even the three # standard ones). @@ -182,14 +207,14 @@ class FileTests(unittest.TestCase): @support.cpython_only def test_rename(self): - path = support.TESTFN + path = os_helper.TESTFN old = sys.getrefcount(path) self.assertRaises(TypeError, os.rename, path, 0) new = sys.getrefcount(path) self.assertEqual(old, new) def test_read(self): - with open(support.TESTFN, "w+b") as fobj: + with open(os_helper.TESTFN, "w+b") as fobj: fobj.write(b"spam") fobj.flush() fd = fobj.fileno() @@ -205,12 +230,12 @@ class FileTests(unittest.TestCase): "needs INT_MAX < PY_SSIZE_T_MAX") @support.bigmemtest(size=INT_MAX + 10, memuse=1, dry_run=False) def test_large_read(self, size): - self.addCleanup(support.unlink, support.TESTFN) - create_file(support.TESTFN, b'test') + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + create_file(os_helper.TESTFN, b'test') # Issue #21932: Make sure that os.read() does not raise an # OverflowError for size larger than INT_MAX - with open(support.TESTFN, "rb") as fp: + with open(os_helper.TESTFN, "rb") as fp: data = os.read(fp.fileno(), size) # The test does not try to read more than 2 GiB at once because the @@ -219,13 +244,13 @@ class FileTests(unittest.TestCase): def test_write(self): # os.write() accepts bytes- and buffer-like objects but not strings - fd = os.open(support.TESTFN, os.O_CREAT | os.O_WRONLY) + fd = os.open(os_helper.TESTFN, os.O_CREAT | os.O_WRONLY) self.assertRaises(TypeError, os.write, fd, "beans") os.write(fd, b"bacon\n") os.write(fd, bytearray(b"eggs\n")) os.write(fd, memoryview(b"spam\n")) os.close(fd) - with open(support.TESTFN, "rb") as fobj: + with open(os_helper.TESTFN, "rb") as fobj: self.assertEqual(fobj.read().splitlines(), [b"bacon", b"eggs", b"spam"]) @@ -249,12 +274,12 @@ class FileTests(unittest.TestCase): self.write_windows_console(sys.executable, "-u", "-c", code) def fdopen_helper(self, *args): - fd = os.open(support.TESTFN, os.O_RDONLY) - f = os.fdopen(fd, *args) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) + f = os.fdopen(fd, *args, encoding="utf-8") f.close() def test_fdopen(self): - fd = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) + fd = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) os.close(fd) self.fdopen_helper() @@ -262,16 +287,16 @@ class FileTests(unittest.TestCase): self.fdopen_helper('r', 100) def test_replace(self): - TESTFN2 = support.TESTFN + ".2" - self.addCleanup(support.unlink, support.TESTFN) - self.addCleanup(support.unlink, TESTFN2) + TESTFN2 = os_helper.TESTFN + ".2" + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(os_helper.unlink, TESTFN2) - create_file(support.TESTFN, b"1") + create_file(os_helper.TESTFN, b"1") create_file(TESTFN2, b"2") - os.replace(support.TESTFN, TESTFN2) - self.assertRaises(FileNotFoundError, os.stat, support.TESTFN) - with open(TESTFN2, 'r') as f: + os.replace(os_helper.TESTFN, TESTFN2) + self.assertRaises(FileNotFoundError, os.stat, os_helper.TESTFN) + with open(TESTFN2, 'r', encoding='utf-8') as f: self.assertEqual(f.read(), "1") def test_open_keywords(self): @@ -282,7 +307,7 @@ class FileTests(unittest.TestCase): def test_symlink_keywords(self): symlink = support.get_attribute(os, "symlink") try: - symlink(src='target', dst=support.TESTFN, + symlink(src='target', dst=os_helper.TESTFN, target_is_directory=False, dir_fd=None) except (NotImplementedError, OSError): pass # No OS support or unprivileged user @@ -294,18 +319,18 @@ class FileTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'copy_file_range'), 'test needs os.copy_file_range()') def test_copy_file_range(self): - TESTFN2 = support.TESTFN + ".3" + TESTFN2 = os_helper.TESTFN + ".3" data = b'0123456789' - create_file(support.TESTFN, data) - self.addCleanup(support.unlink, support.TESTFN) + create_file(os_helper.TESTFN, data) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) - in_file = open(support.TESTFN, 'rb') + in_file = open(os_helper.TESTFN, 'rb') self.addCleanup(in_file.close) in_fd = in_file.fileno() out_file = open(TESTFN2, 'w+b') - self.addCleanup(support.unlink, TESTFN2) + self.addCleanup(os_helper.unlink, TESTFN2) self.addCleanup(out_file.close) out_fd = out_file.fileno() @@ -328,21 +353,21 @@ class FileTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'copy_file_range'), 'test needs os.copy_file_range()') def test_copy_file_range_offset(self): - TESTFN4 = support.TESTFN + ".4" + TESTFN4 = os_helper.TESTFN + ".4" data = b'0123456789' bytes_to_copy = 6 in_skip = 3 out_seek = 5 - create_file(support.TESTFN, data) - self.addCleanup(support.unlink, support.TESTFN) + create_file(os_helper.TESTFN, data) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) - in_file = open(support.TESTFN, 'rb') + in_file = open(os_helper.TESTFN, 'rb') self.addCleanup(in_file.close) in_fd = in_file.fileno() out_file = open(TESTFN4, 'w+b') - self.addCleanup(support.unlink, TESTFN4) + self.addCleanup(os_helper.unlink, TESTFN4) self.addCleanup(out_file.close) out_fd = out_file.fileno() @@ -371,11 +396,131 @@ class FileTests(unittest.TestCase): self.assertEqual(read[out_seek:], data[in_skip:in_skip+i]) + @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') + def test_splice_invalid_values(self): + with self.assertRaises(ValueError): + os.splice(0, 1, -10) + + @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') + @requires_splice_pipe + def test_splice(self): + TESTFN2 = os_helper.TESTFN + ".3" + data = b'0123456789' + + create_file(os_helper.TESTFN, data) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + + in_file = open(os_helper.TESTFN, 'rb') + self.addCleanup(in_file.close) + in_fd = in_file.fileno() + + read_fd, write_fd = os.pipe() + self.addCleanup(lambda: os.close(read_fd)) + self.addCleanup(lambda: os.close(write_fd)) + + try: + i = os.splice(in_fd, write_fd, 5) + except OSError as e: + # Handle the case in which Python was compiled + # in a system with the syscall but without support + # in the kernel. + if e.errno != errno.ENOSYS: + raise + self.skipTest(e) + else: + # The number of copied bytes can be less than + # the number of bytes originally requested. + self.assertIn(i, range(0, 6)); + + self.assertEqual(os.read(read_fd, 100), data[:i]) + + @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') + @requires_splice_pipe + def test_splice_offset_in(self): + TESTFN4 = os_helper.TESTFN + ".4" + data = b'0123456789' + bytes_to_copy = 6 + in_skip = 3 + + create_file(os_helper.TESTFN, data) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + + in_file = open(os_helper.TESTFN, 'rb') + self.addCleanup(in_file.close) + in_fd = in_file.fileno() + + read_fd, write_fd = os.pipe() + self.addCleanup(lambda: os.close(read_fd)) + self.addCleanup(lambda: os.close(write_fd)) + + try: + i = os.splice(in_fd, write_fd, bytes_to_copy, offset_src=in_skip) + except OSError as e: + # Handle the case in which Python was compiled + # in a system with the syscall but without support + # in the kernel. + if e.errno != errno.ENOSYS: + raise + self.skipTest(e) + else: + # The number of copied bytes can be less than + # the number of bytes originally requested. + self.assertIn(i, range(0, bytes_to_copy+1)); + + read = os.read(read_fd, 100) + # 012 are skipped (in_skip) + # 345678 are copied in the file (in_skip + bytes_to_copy) + self.assertEqual(read, data[in_skip:in_skip+i]) + + @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') + @requires_splice_pipe + def test_splice_offset_out(self): + TESTFN4 = os_helper.TESTFN + ".4" + data = b'0123456789' + bytes_to_copy = 6 + out_seek = 3 + + create_file(os_helper.TESTFN, data) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + + read_fd, write_fd = os.pipe() + self.addCleanup(lambda: os.close(read_fd)) + self.addCleanup(lambda: os.close(write_fd)) + os.write(write_fd, data) + + out_file = open(TESTFN4, 'w+b') + self.addCleanup(os_helper.unlink, TESTFN4) + self.addCleanup(out_file.close) + out_fd = out_file.fileno() + + try: + i = os.splice(read_fd, out_fd, bytes_to_copy, offset_dst=out_seek) + except OSError as e: + # Handle the case in which Python was compiled + # in a system with the syscall but without support + # in the kernel. + if e.errno != errno.ENOSYS: + raise + self.skipTest(e) + else: + # The number of copied bytes can be less than + # the number of bytes originally requested. + self.assertIn(i, range(0, bytes_to_copy+1)); + + with open(TESTFN4, 'rb') as in_file: + read = in_file.read() + # seeked bytes (5) are zero'ed + self.assertEqual(read[:out_seek], b'\x00'*out_seek) + # 012 are skipped (in_skip) + # 345678 are copied in the file (in_skip + bytes_to_copy) + self.assertEqual(read[out_seek:], data[:i]) + + # Test attributes on return values from os.*stat* family. class StatAttributeTests(unittest.TestCase): def setUp(self): - self.fname = support.TESTFN - self.addCleanup(support.unlink, self.fname) + self.fname = os_helper.TESTFN + self.addCleanup(os_helper.unlink, self.fname) create_file(self.fname, b"ABC") def check_stat_attributes(self, fname): @@ -560,7 +705,7 @@ class StatAttributeTests(unittest.TestCase): 0) # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) - dirname = support.TESTFN + "dir" + dirname = os_helper.TESTFN + "dir" os.mkdir(dirname) self.addCleanup(os.rmdir, dirname) @@ -577,7 +722,7 @@ class StatAttributeTests(unittest.TestCase): # os.environ['TEMP'] should be located on a volume that # supports file ACLs. fname = os.path.join(os.environ['TEMP'], self.fname) - self.addCleanup(support.unlink, fname) + self.addCleanup(os_helper.unlink, fname) create_file(fname, b'ABC') # Deny the right to [S]YNCHRONIZE on the file to # force CreateFile to fail with ERROR_ACCESS_DENIED. @@ -602,10 +747,10 @@ class StatAttributeTests(unittest.TestCase): class UtimeTests(unittest.TestCase): def setUp(self): - self.dirname = support.TESTFN + self.dirname = os_helper.TESTFN self.fname = os.path.join(self.dirname, "f1") - self.addCleanup(support.rmtree, self.dirname) + self.addCleanup(os_helper.rmtree, self.dirname) os.mkdir(self.dirname) create_file(self.fname) @@ -824,185 +969,311 @@ class UtimeTests(unittest.TestCase): from test import mapping_tests -# TODO: RUSTPYTHON (KeyError: 'surrogateescape') -# class EnvironTests(mapping_tests.BasicTestMappingProtocol): -# """check that os.environ object conform to mapping protocol""" -# type2test = None +class EnvironTests(mapping_tests.BasicTestMappingProtocol): + """check that os.environ object conform to mapping protocol""" + type2test = None -# def setUp(self): -# self.__save = dict(os.environ) -# if os.supports_bytes_environ: -# self.__saveb = dict(os.environb) -# for key, value in self._reference().items(): -# os.environ[key] = value + def setUp(self): + self.__save = dict(os.environ) + if os.supports_bytes_environ: + self.__saveb = dict(os.environb) + for key, value in self._reference().items(): + os.environ[key] = value -# def tearDown(self): -# os.environ.clear() -# os.environ.update(self.__save) -# if os.supports_bytes_environ: -# os.environb.clear() -# os.environb.update(self.__saveb) + def tearDown(self): + os.environ.clear() + os.environ.update(self.__save) + if os.supports_bytes_environ: + os.environb.clear() + os.environb.update(self.__saveb) -# def _reference(self): -# return {"KEY1":"VALUE1", "KEY2":"VALUE2", "KEY3":"VALUE3"} + def _reference(self): + return {"KEY1":"VALUE1", "KEY2":"VALUE2", "KEY3":"VALUE3"} -# def _empty_mapping(self): -# os.environ.clear() -# return os.environ + def _empty_mapping(self): + os.environ.clear() + return os.environ -# # Bug 1110478 -# @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), -# 'requires a shell') -# def test_update2(self): -# os.environ.clear() -# os.environ.update(HELLO="World") -# with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: -# value = popen.read().strip() -# self.assertEqual(value, "World") + # Bug 1110478 + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') + @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") + def test_update2(self): + os.environ.clear() + os.environ.update(HELLO="World") + with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: + value = popen.read().strip() + self.assertEqual(value, "World") -# @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), -# 'requires a shell') -# def test_os_popen_iter(self): -# with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" -# % unix_shell) as popen: -# it = iter(popen) -# self.assertEqual(next(it), "line1\n") -# self.assertEqual(next(it), "line2\n") -# self.assertEqual(next(it), "line3\n") -# self.assertRaises(StopIteration, next, it) + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') + @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") + def test_os_popen_iter(self): + with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" + % unix_shell) as popen: + it = iter(popen) + self.assertEqual(next(it), "line1\n") + self.assertEqual(next(it), "line2\n") + self.assertEqual(next(it), "line3\n") + self.assertRaises(StopIteration, next, it) -# # Verify environ keys and values from the OS are of the -# # correct str type. -# def test_keyvalue_types(self): -# for key, val in os.environ.items(): -# self.assertEqual(type(key), str) -# self.assertEqual(type(val), str) + # Verify environ keys and values from the OS are of the + # correct str type. + def test_keyvalue_types(self): + for key, val in os.environ.items(): + self.assertEqual(type(key), str) + self.assertEqual(type(val), str) -# def test_items(self): -# for key, value in self._reference().items(): -# self.assertEqual(os.environ.get(key), value) + def test_items(self): + for key, value in self._reference().items(): + self.assertEqual(os.environ.get(key), value) -# # Issue 7310 -# def test___repr__(self): -# """Check that the repr() of os.environ looks like environ({...}).""" -# env = os.environ -# self.assertEqual(repr(env), 'environ({{{}}})'.format(', '.join( -# '{!r}: {!r}'.format(key, value) -# for key, value in env.items()))) + # Issue 7310 + def test___repr__(self): + """Check that the repr() of os.environ looks like environ({...}).""" + env = os.environ + formatted_items = ", ".join( + f"{key!r}: {value!r}" + for key, value in env.items() + ) + self.assertEqual(repr(env), f"environ({{{formatted_items}}})") -# def test_get_exec_path(self): -# defpath_list = os.defpath.split(os.pathsep) -# test_path = ['/monty', '/python', '', '/flying/circus'] -# test_env = {'PATH': os.pathsep.join(test_path)} + def test_get_exec_path(self): + defpath_list = os.defpath.split(os.pathsep) + test_path = ['/monty', '/python', '', '/flying/circus'] + test_env = {'PATH': os.pathsep.join(test_path)} -# saved_environ = os.environ -# try: -# os.environ = dict(test_env) -# # Test that defaulting to os.environ works. -# self.assertSequenceEqual(test_path, os.get_exec_path()) -# self.assertSequenceEqual(test_path, os.get_exec_path(env=None)) -# finally: -# os.environ = saved_environ + saved_environ = os.environ + try: + os.environ = dict(test_env) + # Test that defaulting to os.environ works. + self.assertSequenceEqual(test_path, os.get_exec_path()) + self.assertSequenceEqual(test_path, os.get_exec_path(env=None)) + finally: + os.environ = saved_environ -# # No PATH environment variable -# self.assertSequenceEqual(defpath_list, os.get_exec_path({})) -# # Empty PATH environment variable -# self.assertSequenceEqual(('',), os.get_exec_path({'PATH':''})) -# # Supplied PATH environment variable -# self.assertSequenceEqual(test_path, os.get_exec_path(test_env)) + # No PATH environment variable + self.assertSequenceEqual(defpath_list, os.get_exec_path({})) + # Empty PATH environment variable + self.assertSequenceEqual(('',), os.get_exec_path({'PATH':''})) + # Supplied PATH environment variable + self.assertSequenceEqual(test_path, os.get_exec_path(test_env)) -# if os.supports_bytes_environ: -# # env cannot contain 'PATH' and b'PATH' keys -# try: -# # ignore BytesWarning warning -# with warnings.catch_warnings(record=True): -# mixed_env = {'PATH': '1', b'PATH': b'2'} -# except BytesWarning: -# # mixed_env cannot be created with python -bb -# pass -# else: -# self.assertRaises(ValueError, os.get_exec_path, mixed_env) + if os.supports_bytes_environ: + # env cannot contain 'PATH' and b'PATH' keys + try: + # ignore BytesWarning warning + with warnings.catch_warnings(record=True): + mixed_env = {'PATH': '1', b'PATH': b'2'} + except BytesWarning: + # mixed_env cannot be created with python -bb + pass + else: + self.assertRaises(ValueError, os.get_exec_path, mixed_env) -# # bytes key and/or value -# self.assertSequenceEqual(os.get_exec_path({b'PATH': b'abc'}), -# ['abc']) -# self.assertSequenceEqual(os.get_exec_path({b'PATH': 'abc'}), -# ['abc']) -# self.assertSequenceEqual(os.get_exec_path({'PATH': b'abc'}), -# ['abc']) + # bytes key and/or value + self.assertSequenceEqual(os.get_exec_path({b'PATH': b'abc'}), + ['abc']) + self.assertSequenceEqual(os.get_exec_path({b'PATH': 'abc'}), + ['abc']) + self.assertSequenceEqual(os.get_exec_path({'PATH': b'abc'}), + ['abc']) -# @unittest.skipUnless(os.supports_bytes_environ, -# "os.environb required for this test.") -# def test_environb(self): -# # os.environ -> os.environb -# value = 'euro\u20ac' -# try: -# value_bytes = value.encode(sys.getfilesystemencoding(), -# 'surrogateescape') -# except UnicodeEncodeError: -# msg = "U+20AC character is not encodable to %s" % ( -# sys.getfilesystemencoding(),) -# self.skipTest(msg) -# os.environ['unicode'] = value -# self.assertEqual(os.environ['unicode'], value) -# self.assertEqual(os.environb[b'unicode'], value_bytes) + @unittest.skipUnless(os.supports_bytes_environ, + "os.environb required for this test.") + # TODO: RUSTPYTHON (KeyError: 'surrogateescape') + @unittest.expectedFailure + def test_environb(self): + # os.environ -> os.environb + value = 'euro\u20ac' + try: + value_bytes = value.encode(sys.getfilesystemencoding(), + 'surrogateescape') + except UnicodeEncodeError: + msg = "U+20AC character is not encodable to %s" % ( + sys.getfilesystemencoding(),) + self.skipTest(msg) + os.environ['unicode'] = value + self.assertEqual(os.environ['unicode'], value) + self.assertEqual(os.environb[b'unicode'], value_bytes) -# # os.environb -> os.environ -# value = b'\xff' -# os.environb[b'bytes'] = value -# self.assertEqual(os.environb[b'bytes'], value) -# value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape') -# self.assertEqual(os.environ['bytes'], value_str) + # os.environb -> os.environ + value = b'\xff' + os.environb[b'bytes'] = value + self.assertEqual(os.environb[b'bytes'], value) + value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape') + self.assertEqual(os.environ['bytes'], value_str) -# # On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415). -# @support.requires_mac_ver(10, 6) -# def test_unset_error(self): -# if sys.platform == "win32": -# # an environment variable is limited to 32,767 characters -# key = 'x' * 50000 -# self.assertRaises(ValueError, os.environ.__delitem__, key) -# else: -# # "=" is not allowed in a variable name -# key = 'key=' -# self.assertRaises(OSError, os.environ.__delitem__, key) + def test_putenv_unsetenv(self): + name = "PYTHONTESTVAR" + value = "testvalue" + code = f'import os; print(repr(os.environ.get({name!r})))' -# def test_key_type(self): -# missing = 'missingkey' -# self.assertNotIn(missing, os.environ) + with os_helper.EnvironmentVarGuard() as env: + env.pop(name, None) -# with self.assertRaises(KeyError) as cm: -# os.environ[missing] -# self.assertIs(cm.exception.args[0], missing) -# self.assertTrue(cm.exception.__suppress_context__) + os.putenv(name, value) + proc = subprocess.run([sys.executable, '-c', code], check=True, + stdout=subprocess.PIPE, text=True) + self.assertEqual(proc.stdout.rstrip(), repr(value)) -# with self.assertRaises(KeyError) as cm: -# del os.environ[missing] -# self.assertIs(cm.exception.args[0], missing) -# self.assertTrue(cm.exception.__suppress_context__) + os.unsetenv(name) + proc = subprocess.run([sys.executable, '-c', code], check=True, + stdout=subprocess.PIPE, text=True) + self.assertEqual(proc.stdout.rstrip(), repr(None)) -# def _test_environ_iteration(self, collection): -# iterator = iter(collection) -# new_key = "__new_key__" + # On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415). + @support.requires_mac_ver(10, 6) + def test_putenv_unsetenv_error(self): + # Empty variable name is invalid. + # "=" and null character are not allowed in a variable name. + for name in ('', '=name', 'na=me', 'name=', 'name\0', 'na\0me'): + self.assertRaises((OSError, ValueError), os.putenv, name, "value") + self.assertRaises((OSError, ValueError), os.unsetenv, name) -# next(iterator) # start iteration over os.environ.items + if sys.platform == "win32": + # On Windows, an environment variable string ("name=value" string) + # is limited to 32,767 characters + longstr = 'x' * 32_768 + self.assertRaises(ValueError, os.putenv, longstr, "1") + self.assertRaises(ValueError, os.putenv, "X", longstr) + self.assertRaises(ValueError, os.unsetenv, longstr) -# # add a new key in os.environ mapping -# os.environ[new_key] = "test_environ_iteration" + def test_key_type(self): + missing = 'missingkey' + self.assertNotIn(missing, os.environ) -# try: -# next(iterator) # force iteration over modified mapping -# self.assertEqual(os.environ[new_key], "test_environ_iteration") -# finally: -# del os.environ[new_key] + with self.assertRaises(KeyError) as cm: + os.environ[missing] + self.assertIs(cm.exception.args[0], missing) + self.assertTrue(cm.exception.__suppress_context__) -# def test_iter_error_when_changing_os_environ(self): -# self._test_environ_iteration(os.environ) + with self.assertRaises(KeyError) as cm: + del os.environ[missing] + self.assertIs(cm.exception.args[0], missing) + self.assertTrue(cm.exception.__suppress_context__) -# def test_iter_error_when_changing_os_environ_items(self): -# self._test_environ_iteration(os.environ.items()) + def _test_environ_iteration(self, collection): + iterator = iter(collection) + new_key = "__new_key__" -# def test_iter_error_when_changing_os_environ_values(self): -# self._test_environ_iteration(os.environ.values()) + next(iterator) # start iteration over os.environ.items + + # add a new key in os.environ mapping + os.environ[new_key] = "test_environ_iteration" + + try: + next(iterator) # force iteration over modified mapping + self.assertEqual(os.environ[new_key], "test_environ_iteration") + finally: + del os.environ[new_key] + + def test_iter_error_when_changing_os_environ(self): + self._test_environ_iteration(os.environ) + + def test_iter_error_when_changing_os_environ_items(self): + self._test_environ_iteration(os.environ.items()) + + def test_iter_error_when_changing_os_environ_values(self): + self._test_environ_iteration(os.environ.values()) + + def _test_underlying_process_env(self, var, expected): + if not (unix_shell and os.path.exists(unix_shell)): + return + + with os.popen(f"{unix_shell} -c 'echo ${var}'") as popen: + value = popen.read().strip() + + self.assertEqual(expected, value) + + # TODO: RUSTPYTHON (KeyError: 'surrogateescape') + @unittest.expectedFailure + def test_or_operator(self): + overridden_key = '_TEST_VAR_' + original_value = 'original_value' + os.environ[overridden_key] = original_value + + new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} + expected = dict(os.environ) + expected.update(new_vars_dict) + + actual = os.environ | new_vars_dict + self.assertDictEqual(expected, actual) + self.assertEqual('3', actual[overridden_key]) + + new_vars_items = new_vars_dict.items() + self.assertIs(NotImplemented, os.environ.__or__(new_vars_items)) + + self._test_underlying_process_env('_A_', '') + self._test_underlying_process_env(overridden_key, original_value) + + # TODO: RUSTPYTHON (KeyError: 'surrogateescape') + @unittest.expectedFailure + def test_ior_operator(self): + overridden_key = '_TEST_VAR_' + os.environ[overridden_key] = 'original_value' + + new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} + expected = dict(os.environ) + expected.update(new_vars_dict) + + os.environ |= new_vars_dict + self.assertEqual(expected, os.environ) + self.assertEqual('3', os.environ[overridden_key]) + + self._test_underlying_process_env('_A_', '1') + self._test_underlying_process_env(overridden_key, '3') + + def test_ior_operator_invalid_dicts(self): + os_environ_copy = os.environ.copy() + with self.assertRaises(TypeError): + dict_with_bad_key = {1: '_A_'} + os.environ |= dict_with_bad_key + + with self.assertRaises(TypeError): + dict_with_bad_val = {'_A_': 1} + os.environ |= dict_with_bad_val + + # Check nothing was added. + self.assertEqual(os_environ_copy, os.environ) + + # TODO: RUSTPYTHON (KeyError: 'surrogateescape') + @unittest.expectedFailure + def test_ior_operator_key_value_iterable(self): + overridden_key = '_TEST_VAR_' + os.environ[overridden_key] = 'original_value' + + new_vars_items = (('_A_', '1'), ('_B_', '2'), (overridden_key, '3')) + expected = dict(os.environ) + expected.update(new_vars_items) + + os.environ |= new_vars_items + self.assertEqual(expected, os.environ) + self.assertEqual('3', os.environ[overridden_key]) + + self._test_underlying_process_env('_A_', '1') + self._test_underlying_process_env(overridden_key, '3') + + # TODO: RUSTPYTHON (KeyError: 'surrogateescape') + @unittest.expectedFailure + def test_ror_operator(self): + overridden_key = '_TEST_VAR_' + original_value = 'original_value' + os.environ[overridden_key] = original_value + + new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} + expected = dict(new_vars_dict) + expected.update(os.environ) + + actual = new_vars_dict | os.environ + self.assertDictEqual(expected, actual) + self.assertEqual(original_value, actual[overridden_key]) + + new_vars_items = new_vars_dict.items() + self.assertIs(NotImplemented, os.environ.__ror__(new_vars_items)) + + self._test_underlying_process_env('_A_', '') + self._test_underlying_process_env(overridden_key, original_value) class WalkTests(unittest.TestCase): @@ -1017,7 +1288,7 @@ class WalkTests(unittest.TestCase): def setUp(self): join = os.path.join - self.addCleanup(support.rmtree, support.TESTFN) + self.addCleanup(os_helper.rmtree, os_helper.TESTFN) # Build: # TESTFN/ @@ -1036,7 +1307,7 @@ class WalkTests(unittest.TestCase): # broken_link3 # TEST2/ # tmp4 a lone file - self.walk_path = join(support.TESTFN, "TEST1") + self.walk_path = join(os_helper.TESTFN, "TEST1") self.sub1_path = join(self.walk_path, "SUB1") self.sub11_path = join(self.sub1_path, "SUB11") sub2_path = join(self.walk_path, "SUB2") @@ -1046,8 +1317,8 @@ class WalkTests(unittest.TestCase): tmp3_path = join(sub2_path, "tmp3") tmp5_path = join(sub21_path, "tmp3") self.link_path = join(sub2_path, "link") - t2_path = join(support.TESTFN, "TEST2") - tmp4_path = join(support.TESTFN, "TEST2", "tmp4") + t2_path = join(os_helper.TESTFN, "TEST2") + tmp4_path = join(os_helper.TESTFN, "TEST2", "tmp4") broken_link_path = join(sub2_path, "broken_link") broken_link2_path = join(sub2_path, "broken_link2") broken_link3_path = join(sub2_path, "broken_link3") @@ -1059,10 +1330,10 @@ class WalkTests(unittest.TestCase): os.makedirs(t2_path) for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path, tmp5_path: - with open(path, "x") as f: + with open(path, "x", encoding='utf-8') as f: f.write("I'm " + path + " and proud of it. Blame test_os.\n") - if support.can_symlink(): + if os_helper.can_symlink(): os.symlink(os.path.abspath(t2_path), self.link_path) os.symlink('broken', broken_link_path, True) os.symlink(join('tmp3', 'broken'), broken_link2_path, True) @@ -1145,7 +1416,7 @@ class WalkTests(unittest.TestCase): self.sub2_tree) def test_walk_symlink(self): - if not support.can_symlink(): + if not os_helper.can_symlink(): self.skipTest("need symlink support") # Walk, following symlinks. @@ -1181,7 +1452,7 @@ class WalkTests(unittest.TestCase): def test_walk_many_open_files(self): depth = 30 - base = os.path.join(support.TESTFN, 'deep') + base = os.path.join(os_helper.TESTFN, 'deep') p = os.path.join(base, *(['d']*depth)) os.makedirs(p) @@ -1231,13 +1502,13 @@ class FwalkTests(WalkTests): self.assertEqual(expected[root], (set(dirs), set(files))) def test_compare_to_walk(self): - kwargs = {'top': support.TESTFN} + kwargs = {'top': os_helper.TESTFN} self._compare_to_walk(kwargs, kwargs) def test_dir_fd(self): try: fd = os.open(".", os.O_RDONLY) - walk_kwargs = {'top': support.TESTFN} + walk_kwargs = {'top': os_helper.TESTFN} fwalk_kwargs = walk_kwargs.copy() fwalk_kwargs['dir_fd'] = fd self._compare_to_walk(walk_kwargs, fwalk_kwargs) @@ -1247,7 +1518,7 @@ class FwalkTests(WalkTests): def test_yields_correct_dir_fd(self): # check returned file descriptors for topdown, follow_symlinks in itertools.product((True, False), repeat=2): - args = support.TESTFN, topdown, None + args = os_helper.TESTFN, topdown, None for root, dirs, files, rootfd in self.fwalk(*args, follow_symlinks=follow_symlinks): # check that the FD is valid os.fstat(rootfd) @@ -1263,7 +1534,7 @@ class FwalkTests(WalkTests): minfd = os.dup(1) os.close(minfd) for i in range(256): - for x in self.fwalk(support.TESTFN): + for x in self.fwalk(os_helper.TESTFN): pass newfd = os.dup(1) self.addCleanup(os.close, newfd) @@ -1321,10 +1592,10 @@ class BytesFwalkTests(FwalkTests): class MakedirTests(unittest.TestCase): def setUp(self): - os.mkdir(support.TESTFN) + os.mkdir(os_helper.TESTFN) def test_makedir(self): - base = support.TESTFN + base = os_helper.TESTFN path = os.path.join(base, 'dir1', 'dir2', 'dir3') os.makedirs(path) # Should work path = os.path.join(base, 'dir1', 'dir2', 'dir3', 'dir4') @@ -1339,8 +1610,8 @@ class MakedirTests(unittest.TestCase): os.makedirs(path) def test_mode(self): - with support.temp_umask(0o002): - base = support.TESTFN + with os_helper.temp_umask(0o002): + base = os_helper.TESTFN parent = os.path.join(base, 'dir1') path = os.path.join(parent, 'dir2') os.makedirs(path, 0o555) @@ -1351,7 +1622,7 @@ class MakedirTests(unittest.TestCase): self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775) def test_exist_ok_existing_directory(self): - path = os.path.join(support.TESTFN, 'dir1') + path = os.path.join(os_helper.TESTFN, 'dir1') mode = 0o777 old_mask = os.umask(0o022) os.makedirs(path, mode) @@ -1365,18 +1636,18 @@ class MakedirTests(unittest.TestCase): os.makedirs(os.path.abspath('/'), exist_ok=True) def test_exist_ok_s_isgid_directory(self): - path = os.path.join(support.TESTFN, 'dir1') + path = os.path.join(os_helper.TESTFN, 'dir1') S_ISGID = stat.S_ISGID mode = 0o777 old_mask = os.umask(0o022) try: existing_testfn_mode = stat.S_IMODE( - os.lstat(support.TESTFN).st_mode) + os.lstat(os_helper.TESTFN).st_mode) try: - os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID) + os.chmod(os_helper.TESTFN, existing_testfn_mode | S_ISGID) except PermissionError: raise unittest.SkipTest('Cannot set S_ISGID for dir.') - if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID): + if (os.lstat(os_helper.TESTFN).st_mode & S_ISGID != S_ISGID): raise unittest.SkipTest('No support for S_ISGID dir mode.') # The os should apply S_ISGID from the parent dir for us, but # this test need not depend on that behavior. Be explicit. @@ -1392,9 +1663,9 @@ class MakedirTests(unittest.TestCase): os.umask(old_mask) def test_exist_ok_existing_regular_file(self): - base = support.TESTFN - path = os.path.join(support.TESTFN, 'dir1') - with open(path, 'w') as f: + base = os_helper.TESTFN + path = os.path.join(os_helper.TESTFN, 'dir1') + with open(path, 'w', encoding='utf-8') as f: f.write('abc') self.assertRaises(OSError, os.makedirs, path) self.assertRaises(OSError, os.makedirs, path, exist_ok=False) @@ -1402,12 +1673,12 @@ class MakedirTests(unittest.TestCase): os.remove(path) def tearDown(self): - path = os.path.join(support.TESTFN, 'dir1', 'dir2', 'dir3', + path = os.path.join(os_helper.TESTFN, 'dir1', 'dir2', 'dir3', 'dir4', 'dir5', 'dir6') # If the tests failed, the bottom-most directory ('../dir6') # may not have been created, so we look for the outermost directory # that exists. - while not os.path.exists(path) and path != support.TESTFN: + while not os.path.exists(path) and path != os_helper.TESTFN: path = os.path.dirname(path) os.removedirs(path) @@ -1418,17 +1689,17 @@ class ChownFileTests(unittest.TestCase): @classmethod def setUpClass(cls): - os.mkdir(support.TESTFN) + os.mkdir(os_helper.TESTFN) def test_chown_uid_gid_arguments_must_be_index(self): - stat = os.stat(support.TESTFN) + stat = os.stat(os_helper.TESTFN) uid = stat.st_uid gid = stat.st_gid for value in (-1.0, -1j, decimal.Decimal(-1), fractions.Fraction(-2, 2)): - self.assertRaises(TypeError, os.chown, support.TESTFN, value, gid) - self.assertRaises(TypeError, os.chown, support.TESTFN, uid, value) - self.assertIsNone(os.chown(support.TESTFN, uid, gid)) - self.assertIsNone(os.chown(support.TESTFN, -1, -1)) + self.assertRaises(TypeError, os.chown, os_helper.TESTFN, value, gid) + self.assertRaises(TypeError, os.chown, os_helper.TESTFN, uid, value) + self.assertIsNone(os.chown(os_helper.TESTFN, uid, gid)) + self.assertIsNone(os.chown(os_helper.TESTFN, -1, -1)) @unittest.skipUnless(hasattr(os, 'getgroups'), 'need os.getgroups') def test_chown_gid(self): @@ -1437,61 +1708,61 @@ class ChownFileTests(unittest.TestCase): self.skipTest("test needs at least 2 groups") gid_1, gid_2 = groups[:2] - uid = os.stat(support.TESTFN).st_uid + uid = os.stat(os_helper.TESTFN).st_uid - os.chown(support.TESTFN, uid, gid_1) - gid = os.stat(support.TESTFN).st_gid + os.chown(os_helper.TESTFN, uid, gid_1) + gid = os.stat(os_helper.TESTFN).st_gid self.assertEqual(gid, gid_1) - os.chown(support.TESTFN, uid, gid_2) - gid = os.stat(support.TESTFN).st_gid + os.chown(os_helper.TESTFN, uid, gid_2) + gid = os.stat(os_helper.TESTFN).st_gid self.assertEqual(gid, gid_2) @unittest.skipUnless(root_in_posix and len(all_users) > 1, "test needs root privilege and more than one user") def test_chown_with_root(self): uid_1, uid_2 = all_users[:2] - gid = os.stat(support.TESTFN).st_gid - os.chown(support.TESTFN, uid_1, gid) - uid = os.stat(support.TESTFN).st_uid + gid = os.stat(os_helper.TESTFN).st_gid + os.chown(os_helper.TESTFN, uid_1, gid) + uid = os.stat(os_helper.TESTFN).st_uid self.assertEqual(uid, uid_1) - os.chown(support.TESTFN, uid_2, gid) - uid = os.stat(support.TESTFN).st_uid + os.chown(os_helper.TESTFN, uid_2, gid) + uid = os.stat(os_helper.TESTFN).st_uid self.assertEqual(uid, uid_2) @unittest.skipUnless(not root_in_posix and len(all_users) > 1, "test needs non-root account and more than one user") def test_chown_without_permission(self): uid_1, uid_2 = all_users[:2] - gid = os.stat(support.TESTFN).st_gid + gid = os.stat(os_helper.TESTFN).st_gid with self.assertRaises(PermissionError): - os.chown(support.TESTFN, uid_1, gid) - os.chown(support.TESTFN, uid_2, gid) + os.chown(os_helper.TESTFN, uid_1, gid) + os.chown(os_helper.TESTFN, uid_2, gid) @classmethod def tearDownClass(cls): - os.rmdir(support.TESTFN) + os.rmdir(os_helper.TESTFN) class RemoveDirsTests(unittest.TestCase): def setUp(self): - os.makedirs(support.TESTFN) + os.makedirs(os_helper.TESTFN) def tearDown(self): - support.rmtree(support.TESTFN) + os_helper.rmtree(os_helper.TESTFN) def test_remove_all(self): - dira = os.path.join(support.TESTFN, 'dira') + dira = os.path.join(os_helper.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) os.removedirs(dirb) self.assertFalse(os.path.exists(dirb)) self.assertFalse(os.path.exists(dira)) - self.assertFalse(os.path.exists(support.TESTFN)) + self.assertFalse(os.path.exists(os_helper.TESTFN)) def test_remove_partial(self): - dira = os.path.join(support.TESTFN, 'dira') + dira = os.path.join(os_helper.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) @@ -1499,10 +1770,10 @@ class RemoveDirsTests(unittest.TestCase): os.removedirs(dirb) self.assertFalse(os.path.exists(dirb)) self.assertTrue(os.path.exists(dira)) - self.assertTrue(os.path.exists(support.TESTFN)) + self.assertTrue(os.path.exists(os_helper.TESTFN)) def test_remove_nothing(self): - dira = os.path.join(support.TESTFN, 'dira') + dira = os.path.join(os_helper.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) @@ -1511,7 +1782,7 @@ class RemoveDirsTests(unittest.TestCase): os.removedirs(dirb) self.assertTrue(os.path.exists(dirb)) self.assertTrue(os.path.exists(dira)) - self.assertTrue(os.path.exists(support.TESTFN)) + self.assertTrue(os.path.exists(os_helper.TESTFN)) class DevNullTests(unittest.TestCase): @@ -1649,8 +1920,8 @@ class URandomFDTests(unittest.TestCase): def test_urandom_fd_reopened(self): # Issue #21207: urandom() should detect its fd to /dev/urandom # changed to something else, and reopen it. - self.addCleanup(support.unlink, support.TESTFN) - create_file(support.TESTFN, b"x" * 256) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + create_file(os_helper.TESTFN, b"x" * 256) code = """if 1: import os @@ -1676,7 +1947,7 @@ class URandomFDTests(unittest.TestCase): os.dup2(new_fd, fd) sys.stdout.buffer.write(os.urandom(4)) sys.stdout.buffer.write(os.urandom(4)) - """.format(TESTFN=support.TESTFN) + """.format(TESTFN=os_helper.TESTFN) rc, out, err = assert_python_ok('-Sc', code) self.assertEqual(len(out), 8) self.assertNotEqual(out[0:4], out[4:8]) @@ -1828,40 +2099,40 @@ class ExecTests(unittest.TestCase): class Win32ErrorTests(unittest.TestCase): def setUp(self): try: - os.stat(support.TESTFN) + os.stat(os_helper.TESTFN) except FileNotFoundError: exists = False except OSError as exc: exists = True self.fail("file %s must not exist; os.stat failed with %s" - % (support.TESTFN, exc)) + % (os_helper.TESTFN, exc)) else: - self.fail("file %s must not exist" % support.TESTFN) + self.fail("file %s must not exist" % os_helper.TESTFN) def test_rename(self): - self.assertRaises(OSError, os.rename, support.TESTFN, support.TESTFN+".bak") + self.assertRaises(OSError, os.rename, os_helper.TESTFN, os_helper.TESTFN+".bak") def test_remove(self): - self.assertRaises(OSError, os.remove, support.TESTFN) + self.assertRaises(OSError, os.remove, os_helper.TESTFN) def test_chdir(self): - self.assertRaises(OSError, os.chdir, support.TESTFN) + self.assertRaises(OSError, os.chdir, os_helper.TESTFN) def test_mkdir(self): - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(support.TESTFN, "x") as f: - self.assertRaises(OSError, os.mkdir, support.TESTFN) + with open(os_helper.TESTFN, "x") as f: + self.assertRaises(OSError, os.mkdir, os_helper.TESTFN) def test_utime(self): - self.assertRaises(OSError, os.utime, support.TESTFN, None) + self.assertRaises(OSError, os.utime, os_helper.TESTFN, None) def test_chmod(self): - self.assertRaises(OSError, os.chmod, support.TESTFN, 0) + self.assertRaises(OSError, os.chmod, os_helper.TESTFN, 0) class TestInvalidFD(unittest.TestCase): - singles = ["fchdir", "dup", "fdopen", "fdatasync", "fstat", + singles = ["fchdir", "dup", "fdatasync", "fstat", "fstatvfs", "fsync", "tcgetpgrp", "ttyname"] #singles.append("close") #We omit close because it doesn't raise an exception on some platforms @@ -1869,28 +2140,29 @@ class TestInvalidFD(unittest.TestCase): def helper(self): if hasattr(os, f): self.check(getattr(os, f)) - - return helper for f in singles: locals()["test_"+f] = get_single(f) - def check(self, f, *args): + def check(self, f, *args, **kwargs): try: - f(support.make_bad_fd(), *args) + f(os_helper.make_bad_fd(), *args, **kwargs) except OSError as e: self.assertEqual(e.errno, errno.EBADF) else: self.fail("%r didn't raise an OSError with a bad file descriptor" % f) + def test_fdopen(self): + self.check(os.fdopen, encoding="utf-8") + @unittest.skipUnless(hasattr(os, 'isatty'), 'test needs os.isatty()') def test_isatty(self): - self.assertEqual(os.isatty(support.make_bad_fd()), False) + self.assertEqual(os.isatty(os_helper.make_bad_fd()), False) @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') def test_closerange(self): - fd = support.make_bad_fd() + fd = os_helper.make_bad_fd() # Make sure none of the descriptors we are about to close are # currently valid (issue 6542). for i in range(10): @@ -1964,8 +2236,8 @@ class TestInvalidFD(unittest.TestCase): class LinkTests(unittest.TestCase): def setUp(self): - self.file1 = support.TESTFN - self.file2 = os.path.join(support.TESTFN + "2") + self.file1 = os_helper.TESTFN + self.file2 = os.path.join(os_helper.TESTFN + "2") def tearDown(self): for file in (self.file1, self.file2): @@ -1979,7 +2251,7 @@ class LinkTests(unittest.TestCase): os.link(file1, file2) except PermissionError as e: self.skipTest('os.link(): %s' % e) - with open(file1, "r") as f1, open(file2, "r") as f2: + with open(file1, "rb") as f1, open(file2, "rb") as f2: self.assertTrue(os.path.sameopenfile(f1.fileno(), f2.fileno())) def test_link(self): @@ -2070,12 +2342,12 @@ class PosixUidGidTests(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "Posix specific tests") class Pep383Tests(unittest.TestCase): def setUp(self): - if support.TESTFN_UNENCODABLE: - self.dir = support.TESTFN_UNENCODABLE - elif support.TESTFN_NONASCII: - self.dir = support.TESTFN_NONASCII + if os_helper.TESTFN_UNENCODABLE: + self.dir = os_helper.TESTFN_UNENCODABLE + elif os_helper.TESTFN_NONASCII: + self.dir = os_helper.TESTFN_NONASCII else: - self.dir = support.TESTFN + self.dir = os_helper.TESTFN self.bdir = os.fsencode(self.dir) bytesfn = [] @@ -2085,11 +2357,11 @@ class Pep383Tests(unittest.TestCase): except UnicodeEncodeError: return bytesfn.append(fn) - add_filename(support.TESTFN_UNICODE) - if support.TESTFN_UNENCODABLE: - add_filename(support.TESTFN_UNENCODABLE) - if support.TESTFN_NONASCII: - add_filename(support.TESTFN_NONASCII) + add_filename(os_helper.TESTFN_UNICODE) + if os_helper.TESTFN_UNENCODABLE: + add_filename(os_helper.TESTFN_UNENCODABLE) + if os_helper.TESTFN_NONASCII: + add_filename(os_helper.TESTFN_NONASCII) if not bytesfn: self.skipTest("couldn't create any non-ascii filename") @@ -2097,7 +2369,7 @@ class Pep383Tests(unittest.TestCase): os.mkdir(self.dir) try: for fn in bytesfn: - support.create_empty_file(os.path.join(self.bdir, fn)) + os_helper.create_empty_file(os.path.join(self.bdir, fn)) fn = os.fsdecode(fn) if fn in self.unicodefn: raise ValueError("duplicate filename") @@ -2265,41 +2537,41 @@ class Win32ListdirTests(unittest.TestCase): self.created_paths = [] for i in range(2): dir_name = 'SUB%d' % i - dir_path = os.path.join(support.TESTFN, dir_name) + dir_path = os.path.join(os_helper.TESTFN, dir_name) file_name = 'FILE%d' % i - file_path = os.path.join(support.TESTFN, file_name) + file_path = os.path.join(os_helper.TESTFN, file_name) os.makedirs(dir_path) - with open(file_path, 'w') as f: + with open(file_path, 'w', encoding='utf-8') as f: f.write("I'm %s and proud of it. Blame test_os.\n" % file_path) self.created_paths.extend([dir_name, file_name]) self.created_paths.sort() def tearDown(self): - shutil.rmtree(support.TESTFN) + shutil.rmtree(os_helper.TESTFN) def test_listdir_no_extended_path(self): """Test when the path is not an "extended" path.""" # unicode self.assertEqual( - sorted(os.listdir(support.TESTFN)), + sorted(os.listdir(os_helper.TESTFN)), self.created_paths) # bytes self.assertEqual( - sorted(os.listdir(os.fsencode(support.TESTFN))), + sorted(os.listdir(os.fsencode(os_helper.TESTFN))), [os.fsencode(path) for path in self.created_paths]) def test_listdir_extended_path(self): """Test when the path starts with '\\\\?\\'.""" # See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath # unicode - path = '\\\\?\\' + os.path.abspath(support.TESTFN) + path = '\\\\?\\' + os.path.abspath(os_helper.TESTFN) self.assertEqual( sorted(os.listdir(path)), self.created_paths) # bytes - path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) + path = b'\\\\?\\' + os.fsencode(os.path.abspath(os_helper.TESTFN)) self.assertEqual( sorted(os.listdir(path)), [os.fsencode(path) for path in self.created_paths]) @@ -2342,32 +2614,32 @@ class ReadlinkTests(unittest.TestCase): self.assertRaises(FileNotFoundError, os.readlink, FakePath('missing-link')) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_pathlike(self): os.symlink(self.filelink_target, self.filelink) - self.addCleanup(support.unlink, self.filelink) + self.addCleanup(os_helper.unlink, self.filelink) filelink = FakePath(self.filelink) self.assertPathEqual(os.readlink(filelink), self.filelink_target) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_pathlike_bytes(self): os.symlink(self.filelinkb_target, self.filelinkb) - self.addCleanup(support.unlink, self.filelinkb) + self.addCleanup(os_helper.unlink, self.filelinkb) path = os.readlink(FakePath(self.filelinkb)) self.assertPathEqual(path, self.filelinkb_target) self.assertIsInstance(path, bytes) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_bytes(self): os.symlink(self.filelinkb_target, self.filelinkb) - self.addCleanup(support.unlink, self.filelinkb) + self.addCleanup(os_helper.unlink, self.filelinkb) path = os.readlink(self.filelinkb) self.assertPathEqual(path, self.filelinkb_target) self.assertIsInstance(path, bytes) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -@support.skip_unless_symlink +@os_helper.skip_unless_symlink class Win32SymlinkTests(unittest.TestCase): filelink = 'filelinktest' filelink_target = os.path.abspath(__file__) @@ -2438,10 +2710,10 @@ class Win32SymlinkTests(unittest.TestCase): self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) def test_12084(self): - level1 = os.path.abspath(support.TESTFN) + level1 = os.path.abspath(os_helper.TESTFN) level2 = os.path.join(level1, "level2") level3 = os.path.join(level2, "level3") - self.addCleanup(support.rmtree, level1) + self.addCleanup(os_helper.rmtree, level1) os.mkdir(level1) os.mkdir(level2) @@ -2627,7 +2899,7 @@ class Win32NtTests(unittest.TestCase): self.assertEqual(0, handle_delta) -@support.skip_unless_symlink +@os_helper.skip_unless_symlink class NonLocalSymlinkTests(unittest.TestCase): def setUp(self): @@ -2676,6 +2948,7 @@ class FSEncodingTests(unittest.TestCase): class DeviceEncodingTests(unittest.TestCase): + # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'device_encoding') @unittest.expectedFailure def test_bad_fd(self): @@ -2701,7 +2974,7 @@ class PidTests(unittest.TestCase): # We are the parent of our subprocess self.assertEqual(int(stdout), os.getpid()) - def check_waitpid(self, code, exitcode): + def check_waitpid(self, code, exitcode, callback=None): if sys.platform == 'win32': # On Windows, os.spawnv() simply joins arguments with spaces: # arguments need to be quoted @@ -2710,12 +2983,13 @@ class PidTests(unittest.TestCase): args = [sys.executable, '-c', code] pid = os.spawnv(os.P_NOWAIT, sys.executable, args) + if callback is not None: + callback(pid) + + # don't use support.wait_process() to test directly os.waitpid() + # and os.waitstatus_to_exitcode() pid2, status = os.waitpid(pid, 0) - if sys.platform == 'win32': - self.assertEqual(status, exitcode << 8) - else: - self.assertTrue(os.WIFEXITED(status), status) - self.assertEqual(os.WEXITSTATUS(status), exitcode) + self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) self.assertEqual(pid2, pid) # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') @@ -2725,25 +2999,55 @@ class PidTests(unittest.TestCase): # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') @unittest.expectedFailure - def test_waitpid_exitcode(self): + def test_waitstatus_to_exitcode(self): exitcode = 23 code = f'import sys; sys.exit({exitcode})' self.check_waitpid(code, exitcode=exitcode) + with self.assertRaises(TypeError): + os.waitstatus_to_exitcode(0.0) + @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') def test_waitpid_windows(self): - # bpo-40138: test os.waitpid() with exit code larger than INT_MAX. + # bpo-40138: test os.waitpid() and os.waitstatus_to_exitcode() + # with exit code larger than INT_MAX. STATUS_CONTROL_C_EXIT = 0xC000013A code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})' self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT) + @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') + def test_waitstatus_to_exitcode_windows(self): + max_exitcode = 2 ** 32 - 1 + for exitcode in (0, 1, 5, max_exitcode): + self.assertEqual(os.waitstatus_to_exitcode(exitcode << 8), + exitcode) + + # invalid values + with self.assertRaises(ValueError): + os.waitstatus_to_exitcode((max_exitcode + 1) << 8) + with self.assertRaises(OverflowError): + os.waitstatus_to_exitcode(-1) + + # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') + @unittest.expectedFailure + # Skip the test on Windows + @unittest.skipUnless(hasattr(signal, 'SIGKILL'), 'need signal.SIGKILL') + def test_waitstatus_to_exitcode_kill(self): + code = f'import time; time.sleep({support.LONG_TIMEOUT})' + signum = signal.SIGKILL + + def kill_process(pid): + os.kill(pid, signum) + + self.check_waitpid(code, exitcode=-signum, callback=kill_process) + class SpawnTests(unittest.TestCase): def create_args(self, *, with_env=False, use_bytes=False): self.exitcode = 17 - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) if not with_env: code = 'import sys; sys.exit(%s)' % self.exitcode @@ -2756,7 +3060,7 @@ class SpawnTests(unittest.TestCase): code = ('import sys, os; magic = os.environ[%r]; sys.exit(%s)' % (self.key, self.exitcode)) - with open(filename, "w") as fp: + with open(filename, "w", encoding="utf-8") as fp: fp.write(code) args = [sys.executable, filename] @@ -2797,6 +3101,10 @@ class SpawnTests(unittest.TestCase): exitcode = os.spawnv(os.P_WAIT, args[0], args) self.assertEqual(exitcode, self.exitcode) + # Test for PyUnicode_FSConverter() + exitcode = os.spawnv(os.P_WAIT, FakePath(args[0]), args) + self.assertEqual(exitcode, self.exitcode) + @requires_os_func('spawnve') def test_spawnve(self): args = self.create_args(with_env=True) @@ -2819,14 +3127,7 @@ class SpawnTests(unittest.TestCase): def test_nowait(self): args = self.create_args() pid = os.spawnv(os.P_NOWAIT, args[0], args) - result = os.waitpid(pid, 0) - self.assertEqual(result[0], pid) - status = result[1] - if hasattr(os, 'WIFEXITED'): - self.assertTrue(os.WIFEXITED(status)) - self.assertEqual(os.WEXITSTATUS(status), self.exitcode) - else: - self.assertEqual(status, self.exitcode << 8) + support.wait_process(pid, exitcode=self.exitcode) @requires_os_func('spawnve') def test_spawnve_bytes(self): @@ -2897,9 +3198,9 @@ class SpawnTests(unittest.TestCase): self.assertEqual(exitcode, 127) # equal character in the environment variable value - filename = support.TESTFN - self.addCleanup(support.unlink, filename) - with open(filename, "w") as fp: + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + with open(filename, "w", encoding="utf-8") as fp: fp.write('import sys, os\n' 'if os.getenv("FRUIT") != "orange=lemon":\n' ' raise AssertionError') @@ -3053,12 +3354,12 @@ class TestSendfile(unittest.TestCase): @classmethod def setUpClass(cls): cls.key = support.threading_setup() - create_file(support.TESTFN, cls.DATA) + create_file(os_helper.TESTFN, cls.DATA) @classmethod def tearDownClass(cls): support.threading_cleanup(*cls.key) - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def setUp(self): self.server = SendfileTestServer((support.HOST, 0)) @@ -3069,7 +3370,7 @@ class TestSendfile(unittest.TestCase): # synchronize by waiting for "220 ready" response self.client.recv(1024) self.sockno = self.client.fileno() - self.file = open(support.TESTFN, 'rb') + self.file = open(os_helper.TESTFN, 'rb') self.fileno = self.file.fileno() def tearDown(self): @@ -3201,10 +3502,10 @@ class TestSendfile(unittest.TestCase): @requires_headers_trailers def test_trailers(self): - TESTFN2 = support.TESTFN + "2" + TESTFN2 = os_helper.TESTFN + "2" file_data = b"abcdef" - self.addCleanup(support.unlink, TESTFN2) + self.addCleanup(os_helper.unlink, TESTFN2) create_file(TESTFN2, file_data) with open(TESTFN2, 'rb') as f: @@ -3268,8 +3569,8 @@ def supports_extended_attributes(): class ExtendedAttributeTests(unittest.TestCase): def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwargs): - fn = support.TESTFN - self.addCleanup(support.unlink, fn) + fn = os_helper.TESTFN + self.addCleanup(os_helper.unlink, fn) create_file(fn) with self.assertRaises(OSError) as cm: @@ -3317,10 +3618,10 @@ class ExtendedAttributeTests(unittest.TestCase): def _check_xattrs(self, *args, **kwargs): self._check_xattrs_str(str, *args, **kwargs) - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) self._check_xattrs_str(os.fsencode, *args, **kwargs) - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_simple(self): self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, @@ -3374,7 +3675,11 @@ class TermsizeTests(unittest.TestCase): should work too. """ try: - size = subprocess.check_output(['stty', 'size']).decode().split() + size = ( + subprocess.check_output( + ["stty", "size"], stderr=subprocess.DEVNULL, text=True + ).split() + ) except (FileNotFoundError, subprocess.CalledProcessError, PermissionError): self.skipTest("stty invocation failed") @@ -3408,6 +3713,89 @@ class MemfdCreateTests(unittest.TestCase): self.assertFalse(os.get_inheritable(fd2)) +@unittest.skipUnless(hasattr(os, 'eventfd'), 'requires os.eventfd') +@support.requires_linux_version(2, 6, 30) +class EventfdTests(unittest.TestCase): + def test_eventfd_initval(self): + def pack(value): + """Pack as native uint64_t + """ + return struct.pack("@Q", value) + size = 8 # read/write 8 bytes + initval = 42 + fd = os.eventfd(initval) + self.assertNotEqual(fd, -1) + self.addCleanup(os.close, fd) + self.assertFalse(os.get_inheritable(fd)) + + # test with raw read/write + res = os.read(fd, size) + self.assertEqual(res, pack(initval)) + + os.write(fd, pack(23)) + res = os.read(fd, size) + self.assertEqual(res, pack(23)) + + os.write(fd, pack(40)) + os.write(fd, pack(2)) + res = os.read(fd, size) + self.assertEqual(res, pack(42)) + + # test with eventfd_read/eventfd_write + os.eventfd_write(fd, 20) + os.eventfd_write(fd, 3) + res = os.eventfd_read(fd) + self.assertEqual(res, 23) + + def test_eventfd_semaphore(self): + initval = 2 + flags = os.EFD_CLOEXEC | os.EFD_SEMAPHORE | os.EFD_NONBLOCK + fd = os.eventfd(initval, flags) + self.assertNotEqual(fd, -1) + self.addCleanup(os.close, fd) + + # semaphore starts has initval 2, two reads return '1' + res = os.eventfd_read(fd) + self.assertEqual(res, 1) + res = os.eventfd_read(fd) + self.assertEqual(res, 1) + # third read would block + with self.assertRaises(BlockingIOError): + os.eventfd_read(fd) + with self.assertRaises(BlockingIOError): + os.read(fd, 8) + + # increase semaphore counter, read one + os.eventfd_write(fd, 1) + res = os.eventfd_read(fd) + self.assertEqual(res, 1) + # next read would block, too + with self.assertRaises(BlockingIOError): + os.eventfd_read(fd) + + def test_eventfd_select(self): + flags = os.EFD_CLOEXEC | os.EFD_NONBLOCK + fd = os.eventfd(0, flags) + self.assertNotEqual(fd, -1) + self.addCleanup(os.close, fd) + + # counter is zero, only writeable + rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) + self.assertEqual((rfd, wfd, xfd), ([], [fd], [])) + + # counter is non-zero, read and writeable + os.eventfd_write(fd, 23) + rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) + self.assertEqual((rfd, wfd, xfd), ([fd], [fd], [])) + self.assertEqual(os.eventfd_read(fd), 23) + + # counter at max, only readable + os.eventfd_write(fd, (2**64) - 2) + rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) + self.assertEqual((rfd, wfd, xfd), ([fd], [], [])) + os.eventfd_read(fd) + + class OSErrorTests(unittest.TestCase): def setUp(self): class Str(str): @@ -3415,16 +3803,16 @@ class OSErrorTests(unittest.TestCase): self.bytes_filenames = [] self.unicode_filenames = [] - if support.TESTFN_UNENCODABLE is not None: - decoded = support.TESTFN_UNENCODABLE + if os_helper.TESTFN_UNENCODABLE is not None: + decoded = os_helper.TESTFN_UNENCODABLE else: - decoded = support.TESTFN + decoded = os_helper.TESTFN self.unicode_filenames.append(decoded) self.unicode_filenames.append(Str(decoded)) - if support.TESTFN_UNDECODABLE is not None: - encoded = support.TESTFN_UNDECODABLE + if os_helper.TESTFN_UNDECODABLE is not None: + encoded = os_helper.TESTFN_UNDECODABLE else: - encoded = os.fsencode(support.TESTFN) + encoded = os.fsencode(os_helper.TESTFN) self.bytes_filenames.append(encoded) self.bytes_filenames.append(bytearray(encoded)) self.bytes_filenames.append(memoryview(encoded)) @@ -3546,6 +3934,33 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC, 0) + @unittest.skipUnless(hasattr(os, 'O_PATH'), "need os.O_PATH") + def test_get_set_inheritable_o_path(self): + fd = os.open(__file__, os.O_PATH) + self.addCleanup(os.close, fd) + self.assertEqual(os.get_inheritable(fd), False) + + os.set_inheritable(fd, True) + self.assertEqual(os.get_inheritable(fd), True) + + os.set_inheritable(fd, False) + self.assertEqual(os.get_inheritable(fd), False) + + def test_get_set_inheritable_badf(self): + fd = os_helper.make_bad_fd() + + with self.assertRaises(OSError) as ctx: + os.get_inheritable(fd) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + with self.assertRaises(OSError) as ctx: + os.set_inheritable(fd, True) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + with self.assertRaises(OSError) as ctx: + os.set_inheritable(fd, False) + self.assertEqual(ctx.exception.errno, errno.EBADF) + def test_open(self): fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd) @@ -3614,22 +4029,24 @@ class PathTConverterTests(unittest.TestCase): # function, cleanup function) functions = [ ('stat', True, (), None), - ('lstat', True, (), None), + ('lstat', False, (), None), ('access', False, (os.F_OK,), None), ('chflags', False, (0,), None), ('lchflags', False, (0,), None), ('open', False, (0,), getattr(os, 'close', None)), ] + # TODO: RUSTPYTHON (AssertionError: TypeError not raised) + @unittest.expectedFailure def test_path_t_converter(self): - str_filename = support.TESTFN + str_filename = os_helper.TESTFN if os.name == 'nt': bytes_fspath = bytes_filename = None else: - bytes_filename = support.TESTFN.encode('ascii') + bytes_filename = os.fsencode(os_helper.TESTFN) bytes_fspath = FakePath(bytes_filename) fd = os.open(FakePath(str_filename), os.O_WRONLY|os.O_CREAT) - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) self.addCleanup(os.close, fd) int_fspath = FakePath(fd) @@ -3697,13 +4114,35 @@ class ExportsTests(unittest.TestCase): self.assertIn('walk', os.__all__) +class TestDirEntry(unittest.TestCase): + def setUp(self): + self.path = os.path.realpath(os_helper.TESTFN) + self.addCleanup(os_helper.rmtree, self.path) + os.mkdir(self.path) + + # TODO: RUSTPYTHON (AssertionError: TypeError not raised by DirEntry) + @unittest.expectedFailure + def test_uninstantiable(self): + self.assertRaises(TypeError, os.DirEntry) + + # TODO: RUSTPYTHON (pickle.PicklingError: Can't pickle : it's not found as _os.DirEntry) + @unittest.expectedFailure + def test_unpickable(self): + filename = create_file(os.path.join(self.path, "file.txt"), b'python') + entry = [entry for entry in os.scandir(self.path)].pop() + self.assertIsInstance(entry, os.DirEntry) + self.assertEqual(entry.name, "file.txt") + import pickle + self.assertRaises(TypeError, pickle.dumps, entry, filename) + + class TestScandir(unittest.TestCase): check_no_resource_warning = support.check_no_resource_warning def setUp(self): - self.path = os.path.realpath(support.TESTFN) + self.path = os.path.realpath(os_helper.TESTFN) self.bytes_path = os.fsencode(self.path) - self.addCleanup(support.rmtree, self.path) + self.addCleanup(os_helper.rmtree, self.path) os.mkdir(self.path) def create_file(self, name="file.txt"): @@ -3731,6 +4170,20 @@ class TestScandir(unittest.TestCase): else: self.assertEqual(stat1, stat2) + # TODO: RUSTPPYTHON (AssertionError: TypeError not raised by ScandirIter) + @unittest.expectedFailure + def test_uninstantiable(self): + scandir_iter = os.scandir(self.path) + self.assertRaises(TypeError, type(scandir_iter)) + scandir_iter.close() + + def test_unpickable(self): + filename = self.create_file("file.txt") + scandir_iter = os.scandir(self.path) + import pickle + self.assertRaises(TypeError, pickle.dumps, scandir_iter, filename) + scandir_iter.close() + def check_entry(self, entry, name, is_dir, is_file, is_symlink): self.assertIsInstance(entry, os.DirEntry) self.assertEqual(entry.name, name) @@ -3762,7 +4215,7 @@ class TestScandir(unittest.TestCase): @unittest.skipIf(sys.platform == "linux", "TODO: RUSTPYTHON, flaky test") def test_attributes(self): link = hasattr(os, 'link') - symlink = support.can_symlink() + symlink = os_helper.can_symlink() dirname = os.path.join(self.path, "dir") os.mkdir(dirname) @@ -3886,7 +4339,7 @@ class TestScandir(unittest.TestCase): self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False) def test_broken_symlink(self): - if not support.can_symlink(): + if not os_helper.can_symlink(): return self.skipTest('cannot create symbolic link') filename = self.create_file("file.txt") @@ -3936,14 +4389,15 @@ class TestScandir(unittest.TestCase): self.assertIs(type(entry.name), bytes) self.assertIs(type(entry.path), bytes) - # TODO: RUSTPYTHON (scandir needs to have supports_fd) - @unittest.skipUnless(os.scandir in os.supports_fd, - 'fd support for scandir required for this test.') + # TODO: RUSTPYTHON + @unittest.expectedFailure + @unittest.skipUnless(os.listdir in os.supports_fd, + 'fd support for listdir required for this test.') def test_fd(self): self.assertIn(os.scandir, os.supports_fd) self.create_file('file.txt') expected_names = ['file.txt'] - if support.can_symlink(): + if os_helper.can_symlink(): os.symlink('file.txt', os.path.join(self.path, 'link')) expected_names.append('link') @@ -4091,6 +4545,11 @@ class TestPEP519(unittest.TestCase): self.assertFalse(issubclass(FakePath, A)) self.assertTrue(issubclass(FakePath, os.PathLike)) + # TODO: RUSTPYTHON (TypeError: 'ABCMeta' object is not subscriptable) + @unittest.expectedFailure + def test_pathlike_class_getitem(self): + self.assertIsInstance(os.PathLike[bytes], types.GenericAlias) + class TimesTests(unittest.TestCase): def test_times(self): From 49a5805d11213cc85457463994e7eb78f71553ed Mon Sep 17 00:00:00 2001 From: Dean Li Date: Thu, 11 Nov 2021 22:38:47 +0800 Subject: [PATCH 09/40] test: use os_helper --- Lib/test/libregrtest/cmdline.py | 3 +- Lib/test/libregrtest/main.py | 9 +- Lib/test/libregrtest/refleak.py | 2 +- Lib/test/libregrtest/runtest.py | 3 +- Lib/test/libregrtest/runtest_mp.py | 2 +- Lib/test/libregrtest/save_env.py | 7 +- Lib/test/libregrtest/win_utils.py | 2 +- Lib/test/list_tests.py | 7 +- Lib/test/test__osx_support.py | 3 +- Lib/test/test_argparse.py | 5 +- Lib/test/test_array.py | 20 +- Lib/test/test_asyncore.py | 23 +- Lib/test/test_base64.py | 14 +- Lib/test/test_binhex.py | 13 +- Lib/test/test_bool.py | 11 +- Lib/test/test_buffer.py | 3 +- Lib/test/test_bufio.py | 9 +- Lib/test/test_builtin.py | 4 +- Lib/test/test_calendar.py | 4 +- Lib/test/test_cgi.py | 4 +- Lib/test/test_cgitb.py | 2 +- Lib/test/test_cmd_line.py | 13 +- Lib/test/test_cmd_line_script.py | 87 ++--- Lib/test/test_codecs.py | 25 +- Lib/test/test_compile.py | 3 +- Lib/test/test_complex.py | 7 +- Lib/test/test_configparser.py | 11 +- Lib/test/test_contextlib.py | 3 +- Lib/test/test_dbm.py | 11 +- Lib/test/test_dbm_dumb.py | 11 +- Lib/test/test_deque.py | 23 +- Lib/test/test_exceptions.py | 11 +- Lib/test/test_file.py | 7 +- Lib/test/test_fileio.py | 5 +- Lib/test/test_ftplib.py | 4 +- Lib/test/test_genericpath.py | 91 ++--- Lib/test/test_getopt.py | 3 +- Lib/test/test_glob.py | 4 +- Lib/test/test_gzip.py | 20 +- Lib/test/test_http_cookiejar.py | 15 +- Lib/test/test_httplib.py | 13 +- Lib/test/test_httpservers.py | 15 +- Lib/test/test_imp.py | 30 +- .../extension/test_case_sensitivity.py | 5 +- Lib/test/test_importlib/test_api.py | 7 +- Lib/test/test_importlib/util.py | 5 +- Lib/test/test_io.py | 235 ++++++------ Lib/test/test_iter.py | 3 +- Lib/test/test_json/test_tool.py | 5 +- Lib/test/test_linecache.py | 13 +- Lib/test/test_mimetypes.py | 4 +- Lib/test/test_netrc.py | 19 +- Lib/test/test_ntpath.py | 94 ++--- Lib/test/test_optparse.py | 33 +- Lib/test/test_os.py | 4 +- Lib/test/test_pathlib.py | 59 +-- Lib/test/test_pkgimport.py | 2 +- Lib/test/test_platform.py | 11 +- Lib/test/test_posix.py | 337 +++++++++--------- Lib/test/test_posixpath.py | 89 ++--- Lib/test/test_py_compile.py | 7 +- Lib/test/test_regrtest.py | 35 +- Lib/test/test_reprlib.py | 3 +- Lib/test/test_resource.py | 5 +- Lib/test/test_runpy.py | 4 +- Lib/test/test_set.py | 13 +- Lib/test/test_shelve.py | 5 +- Lib/test/test_shutil.py | 139 ++++---- Lib/test/test_signal.py | 3 +- Lib/test/test_socket.py | 55 +-- Lib/test/test_socketserver.py | 10 +- Lib/test/test_stat.py | 3 +- Lib/test/test_subprocess.py | 13 +- Lib/test/test_support.py | 66 ++-- Lib/test/test_sys.py | 7 +- Lib/test/test_sysconfig.py | 7 +- Lib/test/test_tarfile.py | 104 +++--- Lib/test/test_tempfile.py | 12 +- Lib/test/test_threading.py | 4 +- Lib/test/test_tokenize.py | 5 +- Lib/test/test_trace.py | 3 +- Lib/test/test_traceback.py | 3 +- Lib/test/test_unicode_file.py | 5 +- Lib/test/test_unicode_file_functions.py | 17 +- Lib/test/test_urllib.py | 33 +- Lib/test/test_urllib2.py | 3 +- Lib/test/test_urllib2net.py | 3 +- Lib/test/test_urllibnet.py | 7 +- Lib/test/test_uu.py | 9 +- Lib/test/test_venv.py | 2 +- Lib/test/test_webbrowser.py | 7 +- Lib/test/test_xml_etree.py | 15 +- Lib/test/test_xmlrpc.py | 5 +- Lib/test/test_zipfile.py | 4 +- Lib/test/test_zipimport.py | 35 +- 95 files changed, 1106 insertions(+), 1032 deletions(-) diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index cb09ee0e0..0a97c8c19 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -2,6 +2,7 @@ import argparse import os import sys from test import support +from test.support import os_helper USAGE = """\ @@ -280,7 +281,7 @@ def _create_parser(): def relative_filename(string): # CWD is replaced with a temporary dir before calling main(), so we # join it with the saved CWD so it ends up where the user expects. - return os.path.join(support.SAVEDCWD, string) + return os.path.join(os_helper.SAVEDCWD, string) def huntrleaks(string): diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 98559e38a..0f76a53de 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -20,6 +20,7 @@ from test.libregrtest.runtest import ( from test.libregrtest.setup import setup_tests from test.libregrtest.utils import removepy, count, format_duration, printlist from test import support +from test.support import os_helper # When tests are run from the Python build directory, it is best practice @@ -199,7 +200,7 @@ class Regrtest: # regex to match 'test_builtin' in line: # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec' regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b') - with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp: + with open(os.path.join(os_helper.SAVEDCWD, self.ns.fromfile)) as fp: for line in fp: line = line.split('#', 1)[0] line = line.strip() @@ -542,7 +543,7 @@ class Regrtest: for k, v in totals.items(): root.set(k, str(v)) - xmlpath = os.path.join(support.SAVEDCWD, self.ns.xmlpath) + xmlpath = os.path.join(os_helper.SAVEDCWD, self.ns.xmlpath) with open(xmlpath, 'wb') as f: for s in ET.tostringlist(root): f.write(s) @@ -568,8 +569,8 @@ class Regrtest: # Run the tests in a context manager that temporarily changes the CWD to a # temporary and writable directory. If it's not possible to create or # change the CWD, the original CWD will be used. The original CWD is - # available from support.SAVEDCWD. - with support.temp_cwd(test_cwd, quiet=True): + # available from os_helper.SAVEDCWD. + with os_helper.temp_cwd(test_cwd, quiet=True): self._main(tests, kwargs) def getloadavg(self): diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index 822c90523..03747f7f7 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -61,7 +61,7 @@ def dash_R(ns, test_name, test_func): return int_pool.setdefault(value, value) nwarmup, ntracked, fname = ns.huntrleaks - fname = os.path.join(support.SAVEDCWD, fname) + fname = os.path.join(os_helper.SAVEDCWD, fname) repcount = nwarmup + ntracked # Pre-allocate to ensure that the loop doesn't allocate anything new diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 7bba46492..b2d3e54a6 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -11,6 +11,7 @@ import traceback import unittest from test import support +from test.support import os_helper from test.libregrtest.refleak import dash_R, clear_caches from test.libregrtest.save_env import saved_test_environment from test.libregrtest.utils import print_warning @@ -298,7 +299,7 @@ def cleanup_test_droppings(test_name, verbose): # since if a test leaves a file open, it cannot be deleted by name (while # there's nothing we can do about that here either, we can display the # name of the offending test, which is a real help). - for name in (support.TESTFN, + for name in (os_helper.TESTFN, "db_home", ): if not os.path.exists(name): diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py index dbab6954d..745a4a7ce 100644 --- a/Lib/test/libregrtest/runtest_mp.py +++ b/Lib/test/libregrtest/runtest_mp.py @@ -46,7 +46,7 @@ def run_test_in_subprocess(testname, ns): stderr=subprocess.PIPE, universal_newlines=True, close_fds=(os.name != 'nt'), - cwd=support.SAVEDCWD) + cwd=os_helper.SAVEDCWD) def run_tests_worker(worker_args): diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 31931f219..b9a1c0b39 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -9,6 +9,7 @@ import sysconfig import threading import warnings from test import support +from test.support import os_helper from test.libregrtest.utils import print_warning try: import _multiprocessing, multiprocessing.process @@ -228,12 +229,12 @@ class saved_test_environment: return sorted(fn + ('/' if os.path.isdir(fn) else '') for fn in os.listdir()) def restore_files(self, saved_value): - fn = support.TESTFN + fn = os_helper.TESTFN if fn not in saved_value and (fn + '/') not in saved_value: if os.path.isfile(fn): - support.unlink(fn) + os_helper.unlink(fn) elif os.path.isdir(fn): - support.rmtree(fn) + os_helper.rmtree(fn) _lc = [getattr(locale, lc) for lc in dir(locale) if lc.startswith('LC_')] diff --git a/Lib/test/libregrtest/win_utils.py b/Lib/test/libregrtest/win_utils.py index adfe278ba..95db3def3 100644 --- a/Lib/test/libregrtest/win_utils.py +++ b/Lib/test/libregrtest/win_utils.py @@ -55,7 +55,7 @@ class WindowsLoadTracker(): # Spawn off the load monitor command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)] - self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD) + self.p = subprocess.Popen(command, stdout=command_stdout, cwd=os_helper.SAVEDCWD) # Close our copy of the write end of the pipe os.close(command_stdout) diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index d4eadd57d..e8c1049b4 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -8,6 +8,7 @@ import unittest from functools import cmp_to_key from test import support, seq_tests +from test.support import os_helper class CommonTest(seq_tests.CommonTest): @@ -73,12 +74,12 @@ class CommonTest(seq_tests.CommonTest): d.append(d) d.append(400) try: - with open(support.TESTFN, "w") as fo: + with open(os_helper.TESTFN, "w") as fo: fo.write(str(d)) - with open(support.TESTFN, "r") as fo: + with open(os_helper.TESTFN, "r") as fo: self.assertEqual(fo.read(), repr(d)) finally: - os.remove(support.TESTFN) + os.remove(os_helper.TESTFN) def test_set_subscript(self): a = self.type2test(range(20)) diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index ac7ab85a2..512d861a4 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -9,6 +9,7 @@ import sys import unittest import test.support +from test.support import os_helper import _osx_support @@ -19,7 +20,7 @@ class Test_OSXSupport(unittest.TestCase): self.maxDiff = None self.prog_name = 'bogus_program_xxxx' self.temp_path_dir = os.path.abspath(os.getcwd()) - self.env = test.support.EnvironmentVarGuard() + self.env = os_helper.EnvironmentVarGuard() self.addCleanup(self.env.__exit__) for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 22cae626c..e98c15b11 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -13,6 +13,7 @@ import argparse from io import StringIO from test import support +from test.support import os_helper from unittest import mock class StdIOBuffer(StringIO): pass @@ -23,7 +24,7 @@ class TestCase(unittest.TestCase): # The tests assume that line wrapping occurs at 80 columns, but this # behaviour can be overridden by setting the COLUMNS environment # variable. To ensure that this width is used, set COLUMNS to 80. - env = support.EnvironmentVarGuard() + env = os_helper.EnvironmentVarGuard() env['COLUMNS'] = '80' self.addCleanup(env.__exit__) @@ -3244,7 +3245,7 @@ class TestShortColumns(HelpTestCase): but we don't want any exceptions thrown in such cases. Only ugly representation. ''' def setUp(self): - env = support.EnvironmentVarGuard() + env = os_helper.EnvironmentVarGuard() env.set("COLUMNS", '15') self.addCleanup(env.__exit__) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 6e6f4795e..f7405098b 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -4,7 +4,7 @@ import unittest from test import support -from test.support import _2G +from test.support import _2G, os_helper import weakref import pickle import operator @@ -371,13 +371,13 @@ class BaseTest: def test_tofromfile(self): a = array.array(self.typecode, 2*self.example) self.assertRaises(TypeError, a.tofile) - support.unlink(support.TESTFN) - f = open(support.TESTFN, 'wb') + os_helper.unlink(os_helper.TESTFN) + f = open(os_helper.TESTFN, 'wb') try: a.tofile(f) f.close() b = array.array(self.typecode) - f = open(support.TESTFN, 'rb') + f = open(os_helper.TESTFN, 'rb') self.assertRaises(TypeError, b.fromfile) b.fromfile(f, len(self.example)) self.assertEqual(b, array.array(self.typecode, self.example)) @@ -388,27 +388,27 @@ class BaseTest: finally: if not f.closed: f.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_fromfile_ioerror(self): # Issue #5395: Check if fromfile raises a proper OSError # instead of EOFError. a = array.array(self.typecode) - f = open(support.TESTFN, 'wb') + f = open(os_helper.TESTFN, 'wb') try: self.assertRaises(OSError, a.fromfile, f, len(self.example)) finally: f.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_filewrite(self): a = array.array(self.typecode, 2*self.example) - f = open(support.TESTFN, 'wb') + f = open(os_helper.TESTFN, 'wb') try: f.write(a) f.close() b = array.array(self.typecode) - f = open(support.TESTFN, 'rb') + f = open(os_helper.TESTFN, 'rb') b.fromfile(f, len(self.example)) self.assertEqual(b, array.array(self.typecode, self.example)) self.assertNotEqual(a, b) @@ -418,7 +418,7 @@ class BaseTest: finally: if not f.closed: f.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_tofromlist(self): a = array.array(self.typecode, 2*self.example) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 3fcedb58e..d6d9a10d4 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -10,6 +10,7 @@ import struct import threading from test import support +from test.support import os_helper from io import BytesIO if support.PGO: @@ -91,7 +92,7 @@ def bind_af_aware(sock, addr): """Helper function to bind a socket according to its family.""" if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. - support.unlink(addr) + os_helper.unlink(addr) support.bind_unix_socket(sock, addr) else: sock.bind(addr) @@ -368,14 +369,14 @@ class DispatcherWithSendTests(unittest.TestCase): class FileWrapperTest(unittest.TestCase): def setUp(self): self.d = b"It's not dead, it's sleeping!" - with open(support.TESTFN, 'wb') as file: + with open(os_helper.TESTFN, 'wb') as file: file.write(self.d) def tearDown(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_recv(self): - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) w = asyncore.file_wrapper(fd) os.close(fd) @@ -389,20 +390,20 @@ class FileWrapperTest(unittest.TestCase): def test_send(self): d1 = b"Come again?" d2 = b"I want to buy some cheese." - fd = os.open(support.TESTFN, os.O_WRONLY | os.O_APPEND) + fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_APPEND) w = asyncore.file_wrapper(fd) os.close(fd) w.write(d1) w.send(d2) w.close() - with open(support.TESTFN, 'rb') as file: + with open(os_helper.TESTFN, 'rb') as file: self.assertEqual(file.read(), self.d + d1 + d2) @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), 'asyncore.file_dispatcher required') def test_dispatcher(self): - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) data = [] class FileDispatcher(asyncore.file_dispatcher): def handle_read(self): @@ -414,7 +415,7 @@ class FileWrapperTest(unittest.TestCase): def test_resource_warning(self): # Issue #11453 - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) @@ -423,7 +424,7 @@ class FileWrapperTest(unittest.TestCase): support.gc_collect() def test_close_twice(self): - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) @@ -803,10 +804,10 @@ class TestAPI_UseIPv6Sockets(BaseTestAPI): class TestAPI_UseUnixSockets(BaseTestAPI): if HAS_UNIX_SOCKETS: family = socket.AF_UNIX - addr = support.TESTFN + addr = os_helper.TESTFN def tearDown(self): - support.unlink(self.addr) + os_helper.unlink(self.addr) BaseTestAPI.tearDown(self) class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase): diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index c2a38b44d..34ba5f9d4 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -4,7 +4,7 @@ import base64 import binascii import os from array import array -from test.support import script_helper +from test.support import script_helper, os_helper class LegacyBase64TestCase(unittest.TestCase): @@ -683,8 +683,8 @@ class BaseXYTestCase(unittest.TestCase): class TestMain(unittest.TestCase): def tearDown(self): - if os.path.exists(support.TESTFN): - os.unlink(support.TESTFN) + if os.path.exists(os_helper.TESTFN): + os.unlink(os_helper.TESTFN) def get_output(self, *args): return script_helper.assert_python_ok('-m', 'base64', *args).out @@ -698,9 +698,9 @@ class TestMain(unittest.TestCase): )) def test_encode_file(self): - with open(support.TESTFN, 'wb') as fp: + with open(os_helper.TESTFN, 'wb') as fp: fp.write(b'a\xffb\n') - output = self.get_output('-e', support.TESTFN) + output = self.get_output('-e', os_helper.TESTFN) self.assertEqual(output.rstrip(), b'Yf9iCg==') def test_encode_from_stdin(self): @@ -710,9 +710,9 @@ class TestMain(unittest.TestCase): self.assertIsNone(err) def test_decode(self): - with open(support.TESTFN, 'wb') as fp: + with open(os_helper.TESTFN, 'wb') as fp: fp.write(b'Yf9iCg==') - output = self.get_output('-d', support.TESTFN) + output = self.get_output('-d', os_helper.TESTFN) self.assertEqual(output.rstrip(), b'a\xffb') if __name__ == '__main__': diff --git a/Lib/test/test_binhex.py b/Lib/test/test_binhex.py index 492e25d97..8630f4237 100644 --- a/Lib/test/test_binhex.py +++ b/Lib/test/test_binhex.py @@ -6,19 +6,20 @@ import binhex import unittest from test import support +from test.support import os_helper class BinHexTestCase(unittest.TestCase): def setUp(self): - self.fname1 = support.TESTFN + "1" - self.fname2 = support.TESTFN + "2" - self.fname3 = support.TESTFN + "very_long_filename__very_long_filename__very_long_filename__very_long_filename__" + self.fname1 = os_helper.TESTFN + "1" + self.fname2 = os_helper.TESTFN + "2" + self.fname3 = os_helper.TESTFN + "very_long_filename__very_long_filename__very_long_filename__very_long_filename__" def tearDown(self): - support.unlink(self.fname1) - support.unlink(self.fname2) - support.unlink(self.fname3) + os_helper.unlink(self.fname1) + os_helper.unlink(self.fname2) + os_helper.unlink(self.fname3) DATA = b'Jack is my hero' diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 726ccd03a..a60aa6713 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -2,6 +2,7 @@ import unittest from test import support +from test.support import os_helper import os @@ -22,12 +23,12 @@ class BoolTest(unittest.TestCase): def test_print(self): try: - with open(support.TESTFN, "w") as fo: + with open(os_helper.TESTFN, "w") as fo: print(False, True, file=fo) - with open(support.TESTFN, "r") as fi: + with open(os_helper.TESTFN, "r") as fi: self.assertEqual(fi.read(), 'False True\n') finally: - os.remove(support.TESTFN) + os.remove(os_helper.TESTFN) def test_repr(self): self.assertEqual(repr(False), 'False') @@ -245,11 +246,11 @@ class BoolTest(unittest.TestCase): def test_fileclosed(self): try: - with open(support.TESTFN, "w") as f: + with open(os_helper.TESTFN, "w") as f: self.assertIs(f.closed, False) self.assertIs(f.closed, True) finally: - os.remove(support.TESTFN) + os.remove(os_helper.TESTFN) def test_types(self): # types are always true. diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 79425453d..43a7f404f 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -16,6 +16,7 @@ import contextlib import unittest from test import support +from test.support import os_helper from itertools import permutations, product from random import randrange, sample, choice import warnings @@ -39,7 +40,7 @@ except ImportError: ctypes = None try: - with support.EnvironmentVarGuard() as os.environ, \ + with os_helper.EnvironmentVarGuard() as os.environ, \ warnings.catch_warnings(): from numpy import ndarray as numpy_array except ImportError: diff --git a/Lib/test/test_bufio.py b/Lib/test/test_bufio.py index be4c3153b..3471351c4 100644 --- a/Lib/test/test_bufio.py +++ b/Lib/test/test_bufio.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper import io # C implementation. import _pyio as pyio # Python implementation. @@ -17,18 +18,18 @@ class BufferSizeTest: # .readline()s deliver what we wrote. # Ensure we can open TESTFN for writing. - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) # Since C doesn't guarantee we can write/read arbitrary bytes in text # files, use binary mode. - f = self.open(support.TESTFN, "wb") + f = self.open(os_helper.TESTFN, "wb") try: # write once with \n and once without f.write(s) f.write(b"\n") f.write(s) f.close() - f = open(support.TESTFN, "rb") + f = open(os_helper.TESTFN, "rb") line = f.readline() self.assertEqual(line, s + b"\n") line = f.readline() @@ -37,7 +38,7 @@ class BufferSizeTest: self.assertFalse(line) # Must be at EOF f.close() finally: - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def drive_one(self, pattern): for length in lengths: diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 341acc70c..70221eb8d 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -20,9 +20,9 @@ import warnings from functools import partial from contextlib import ExitStack from operator import neg -from test.support import ( - EnvironmentVarGuard, TESTFN, check_warnings, swap_attr, unlink) +from test.support import check_warnings, swap_attr from test.support.script_helper import assert_python_ok +from test.support.os_helper import EnvironmentVarGuard, TESTFN, unlink from unittest.mock import MagicMock, patch try: import pty, signal diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index fd39c4093..091ab4a4d 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -912,12 +912,12 @@ class CommandLineTestCase(unittest.TestCase): class MiscTestCase(unittest.TestCase): def test__all__(self): - blacklist = {'mdays', 'January', 'February', 'EPOCH', + not_exported = {'mdays', 'January', 'February', 'EPOCH', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY', 'different_locale', 'c', 'prweek', 'week', 'format', 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth'} - support.check__all__(self, calendar, blacklist=blacklist) + support.check__all__(self, calendar, not_exported=not_exported) class TestSubClassingCase(unittest.TestCase): diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index e65553af6..cc736572b 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -578,9 +578,9 @@ this is the content of the fake file ("form-data", {"name": "files", "filename": 'fo"o;bar'})) def test_all(self): - blacklist = {"logfile", "logfp", "initlog", "dolog", "nolog", + not_exported = {"logfile", "logfp", "initlog", "dolog", "nolog", "closelog", "log", "maxlen", "valid_boundary"} - support.check__all__(self, cgi, blacklist=blacklist) + support.check__all__(self, cgi, not_exported=not_exported) BOUNDARY = "---------------------------721837373350705526688164684" diff --git a/Lib/test/test_cgitb.py b/Lib/test/test_cgitb.py index 040d64aa7..6b7c58a19 100644 --- a/Lib/test/test_cgitb.py +++ b/Lib/test/test_cgitb.py @@ -1,4 +1,4 @@ -from test.support import temp_dir +from test.support.os_helper import temp_dir from test.support.script_helper import assert_python_failure import unittest import sys diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index ef3f172fa..cfb786c2d 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -12,6 +12,7 @@ from test.support.script_helper import ( spawn_python, kill_python, assert_python_ok, assert_python_failure, interpreter_requires_environment ) +from test.support import os_helper # Debug build? @@ -156,11 +157,11 @@ class CmdLineTest(unittest.TestCase): # All good if execution is successful assert_python_ok('-c', 'pass') - @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') + @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') def test_non_ascii(self): # Test handling of non-ascii data command = ("assert(ord(%r) == %s)" - % (support.FS_NONASCII, ord(support.FS_NONASCII))) + % (os_helper.FS_NONASCII, ord(os_helper.FS_NONASCII))) assert_python_ok('-c', command) # On Windows, pass bytes to subprocess doesn't test how Python decodes the @@ -526,8 +527,8 @@ class CmdLineTest(unittest.TestCase): # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a # borrowed reference to the dict of __main__ module and later modify # the dict whereas the module was destroyed - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) with open(filename, "w") as script: print("import sys", file=script) print("del sys.modules['__main__']", file=script) @@ -566,7 +567,7 @@ class CmdLineTest(unittest.TestCase): # dummyvar to prevent extraneous -E dummyvar="") self.assertEqual(out.strip(), b'1 1 1') - with support.temp_cwd() as tmpdir: + with os_helper.temp_cwd() as tmpdir: fake = os.path.join(tmpdir, "uuid.py") main = os.path.join(tmpdir, "main.py") with open(fake, "w") as f: @@ -632,7 +633,7 @@ class CmdLineTest(unittest.TestCase): elif opt is not None: args[:0] = ['-X', f'pycache_prefix={opt}'] with self.subTest(envval=envval, opt=opt): - with support.temp_cwd(): + with os_helper.temp_cwd(): assert_python_ok(*args, **env) def run_xdev(self, *args, check_exitcode=True, xdev=True): diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index e332a1d6a..59bbfee60 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -17,6 +17,7 @@ from test import support from test.support.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, assert_python_ok, assert_python_failure, spawn_python, kill_python) +from test.support import os_helper verbose = support.verbose @@ -224,7 +225,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_basic_script(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') self._check_script(script_name, script_name, script_name, script_dir, None, @@ -233,7 +234,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_script_compiled(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') py_compile.compile(script_name, doraise=True) os.remove(script_name) @@ -243,14 +244,14 @@ class CmdLineTest(unittest.TestCase): importlib.machinery.SourcelessFileLoader) def test_directory(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') self._check_script(script_dir, script_name, script_dir, script_dir, '', importlib.machinery.SourceFileLoader) def test_directory_compiled(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') py_compile.compile(script_name, doraise=True) os.remove(script_name) @@ -260,19 +261,19 @@ class CmdLineTest(unittest.TestCase): importlib.machinery.SourcelessFileLoader) def test_directory_error(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: msg = "can't find '__main__' module in %r" % script_dir self._check_import_error(script_dir, msg) def test_zipfile(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name) self._check_script(zip_name, run_name, zip_name, zip_name, '', zipimport.zipimporter) def test_zipfile_compiled_timestamp(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -284,7 +285,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_zipfile_compiled_checked_hash(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -296,7 +297,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_zipfile_compiled_unchecked_hash(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -306,14 +307,14 @@ class CmdLineTest(unittest.TestCase): zipimport.zipimporter) def test_zipfile_error(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'not_main') zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name) msg = "can't find '__main__' module in %r" % zip_name self._check_import_error(zip_name, msg) def test_module_in_package(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, 'script') @@ -323,14 +324,14 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_module_in_package_in_zipfile(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script') self._check_script(["-m", "test_pkg.script"], run_name, run_name, script_dir, 'test_pkg', zipimport.zipimporter, PYTHONPATH=zip_name, cwd=script_dir) def test_module_in_subpackage_in_zipfile(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script', depth=2) self._check_script(["-m", "test_pkg.test_pkg.script"], run_name, run_name, script_dir, 'test_pkg.test_pkg', @@ -338,7 +339,7 @@ class CmdLineTest(unittest.TestCase): PYTHONPATH=zip_name, cwd=script_dir) def test_package(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__') @@ -348,7 +349,7 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_package_compiled(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__') @@ -361,7 +362,7 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_package_error(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) msg = ("'test_pkg' is a package and cannot " @@ -369,7 +370,7 @@ class CmdLineTest(unittest.TestCase): self._check_import_error(["-m", "test_pkg"], msg, cwd=script_dir) def test_package_recursion(self): - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) main_dir = os.path.join(pkg_dir, '__main__') @@ -384,8 +385,8 @@ class CmdLineTest(unittest.TestCase): def test_issue8202(self): # Make sure package __init__ modules see "-m" in sys.argv0 while # searching for the module to execute - with support.temp_dir() as script_dir: - with support.change_cwd(path=script_dir): + with os_helper.temp_dir() as script_dir: + with os_helper.change_cwd(path=script_dir): pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir, "import sys; print('init_argv0==%r' % sys.argv[0])") script_name = _make_test_script(pkg_dir, 'script') @@ -403,8 +404,8 @@ class CmdLineTest(unittest.TestCase): def test_issue8202_dash_c_file_ignored(self): # Make sure a "-c" file in the current directory # does not alter the value of sys.path[0] - with support.temp_dir() as script_dir: - with support.change_cwd(path=script_dir): + with os_helper.temp_dir() as script_dir: + with os_helper.change_cwd(path=script_dir): with open("-c", "w") as f: f.write("data") rc, out, err = assert_python_ok('-c', @@ -418,9 +419,9 @@ class CmdLineTest(unittest.TestCase): def test_issue8202_dash_m_file_ignored(self): # Make sure a "-m" file in the current directory # does not alter the value of sys.path[0] - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'other') - with support.change_cwd(path=script_dir): + with os_helper.change_cwd(path=script_dir): with open("-m", "w") as f: f.write("data") rc, out, err = assert_python_ok('-m', 'other', *example_args, @@ -432,7 +433,7 @@ class CmdLineTest(unittest.TestCase): def test_issue20884(self): # On Windows, script with encoding cookie and LF line ending # will be failed. - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = os.path.join(script_dir, "issue20884.py") with open(script_name, "w", newline='\n') as f: f.write("#coding: iso-8859-1\n") @@ -441,15 +442,15 @@ class CmdLineTest(unittest.TestCase): f.write('x'*80 + '\n') f.write('"""\n') - with support.change_cwd(path=script_dir): + with os_helper.change_cwd(path=script_dir): rc, out, err = assert_python_ok(script_name) self.assertEqual(b"", out) self.assertEqual(b"", err) @contextlib.contextmanager def setup_test_pkg(self, *args): - with support.temp_dir() as script_dir, \ - support.change_cwd(path=script_dir): + with os_helper.temp_dir() as script_dir, \ + os_helper.change_cwd(path=script_dir): pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir, *args) yield pkg_dir @@ -493,8 +494,8 @@ class CmdLineTest(unittest.TestCase): self.assertNotIn(b'Traceback', err) def test_dash_m_bad_pyc(self): - with support.temp_dir() as script_dir, \ - support.change_cwd(path=script_dir): + with os_helper.temp_dir() as script_dir, \ + os_helper.change_cwd(path=script_dir): os.mkdir('test_pkg') # Create invalid *.pyc as empty file with open('test_pkg/__init__.pyc', 'wb'): @@ -541,7 +542,7 @@ class CmdLineTest(unittest.TestCase): except: raise NameError from None """) - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = stderr.decode('ascii').split('\n') @@ -554,18 +555,18 @@ class CmdLineTest(unittest.TestCase): # Mac OS X denies the creation of a file with an invalid UTF-8 name. # Windows allows creating a name with an arbitrary bytes name, but # Python cannot a undecodable bytes argument to a subprocess. - if (support.TESTFN_UNDECODABLE + if (os_helper.TESTFN_UNDECODABLE and sys.platform not in ('win32', 'darwin')): - name = os.fsdecode(support.TESTFN_UNDECODABLE) - elif support.TESTFN_NONASCII: - name = support.TESTFN_NONASCII + name = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + elif os_helper.TESTFN_NONASCII: + name = os_helper.TESTFN_NONASCII else: - self.skipTest("need support.TESTFN_NONASCII") + self.skipTest("need os_helper.TESTFN_NONASCII") # Issue #16218 source = 'print(ascii(__file__))\n' script_name = _make_test_script(os.curdir, name, source) - self.addCleanup(support.unlink, script_name) + self.addCleanup(os_helper.unlink, script_name) rc, stdout, stderr = assert_python_ok(script_name) self.assertEqual( ascii(script_name), @@ -591,7 +592,7 @@ class CmdLineTest(unittest.TestCase): if error: sys.exit(error) """) - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = stderr.decode('ascii') @@ -601,7 +602,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_unindented_caret_position(self): script = "1 + 1 = 2\n" - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read() @@ -615,7 +616,7 @@ class CmdLineTest(unittest.TestCase): if True: 1 + 1 = 2 """) - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read() @@ -637,7 +638,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_multi_line_fstring(self): script = 'foo = f"""{}\nfoo"""\n' - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) self.assertEqual( @@ -653,7 +654,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_invalid_escape_sequence_multi_line(self): script = 'foo = """\\q\n"""\n' - with support.temp_dir() as script_dir: + with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure( '-Werror', script_name, @@ -683,7 +684,7 @@ class CmdLineTest(unittest.TestCase): """) # Always show full path diffs on errors self.maxDiff = None - with support.temp_dir() as work_dir, support.temp_dir() as script_dir: + with os_helper.temp_dir() as work_dir, os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', script) # Reference output comes from directly executing __main__.py # We omit PYTHONPATH and user site to align with isolated mode @@ -715,7 +716,7 @@ class CmdLineTest(unittest.TestCase): """) # Always show full path diffs on errors self.maxDiff = None - with support.temp_dir() as work_dir: + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) script_name = _make_test_script(script_dir, '__main__', script) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index cd60f774b..dd8a4b503 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -8,6 +8,7 @@ import encodings from unittest import mock from test import support +from test.support import os_helper try: import _testcapi @@ -839,11 +840,11 @@ class UTF16Test(ReadTest, unittest.TestCase): s1 = 'Hello\r\nworld\r\n' s = s1.encode(self.encoding) - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, 'wb') as fp: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, 'wb') as fp: fp.write(s) with support.check_warnings(('', DeprecationWarning)): - reader = codecs.open(support.TESTFN, 'U', encoding=self.encoding) + reader = codecs.open(os_helper.TESTFN, 'U', encoding=self.encoding) with reader: self.assertEqual(reader.read(), s1) @@ -1891,10 +1892,10 @@ class CodecsModuleTest(unittest.TestCase): getattr(codecs, api) def test_open(self): - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) for mode in ('w', 'r', 'r+', 'w+', 'a', 'a+'): with self.subTest(mode), \ - codecs.open(support.TESTFN, mode, 'ascii') as file: + codecs.open(os_helper.TESTFN, mode, 'ascii') as file: self.assertIsInstance(file, codecs.StreamReaderWriter) def test_undefined(self): @@ -1912,7 +1913,7 @@ class CodecsModuleTest(unittest.TestCase): mock_open = mock.mock_open() with mock.patch('builtins.open', mock_open) as file: with self.assertRaises(LookupError): - codecs.open(support.TESTFN, 'wt', 'invalid-encoding') + codecs.open(os_helper.TESTFN, 'wt', 'invalid-encoding') file().close.assert_called() @@ -2754,10 +2755,10 @@ class BomTest(unittest.TestCase): "utf-32", "utf-32-le", "utf-32-be") - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) for encoding in tests: # Check if the BOM is written only once - with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: f.write(data) f.write(data) f.seek(0) @@ -2766,7 +2767,7 @@ class BomTest(unittest.TestCase): self.assertEqual(f.read(), data * 2) # Check that the BOM is written after a seek(0) - with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: f.write(data[0]) self.assertNotEqual(f.tell(), 0) f.seek(0) @@ -2775,7 +2776,7 @@ class BomTest(unittest.TestCase): self.assertEqual(f.read(), data) # (StreamWriter) Check that the BOM is written after a seek(0) - with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: f.writer.write(data[0]) self.assertNotEqual(f.writer.tell(), 0) f.writer.seek(0) @@ -2785,7 +2786,7 @@ class BomTest(unittest.TestCase): # Check that the BOM is not written after a seek() at a position # different than the start - with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: f.write(data) f.seek(f.tell()) f.write(data) @@ -2794,7 +2795,7 @@ class BomTest(unittest.TestCase): # (StreamWriter) Check that the BOM is not written after a seek() # at a position different than the start - with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: f.writer.write(data) f.writer.seek(f.writer.tell()) f.writer.write(data) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 615a600ab..c10a21bb3 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -7,7 +7,8 @@ import _ast import tempfile import types from test import support -from test.support import script_helper, FakePath +from test.support import script_helper +from test.support.os_helper import FakePath class TestSpecifics(unittest.TestCase): diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index e0ed859c5..a6f754591 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper from test.test_grammar import (VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS) @@ -514,15 +515,15 @@ class ComplexTest(unittest.TestCase): fo = None try: - fo = open(support.TESTFN, "w") + fo = open(os_helper.TESTFN, "w") print(a, b, file=fo) fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), ("%s %s\n" % (a, b))) finally: if (fo is not None) and (not fo.closed): fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_getnewargs(self): self.assertEqual((1+2j).__getnewargs__(), (1.0, 2.0)) diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index ddf8de0f7..d2cfd08cc 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -8,6 +8,7 @@ import unittest import warnings from test import support +from test.support import os_helper class SortedDict(collections.UserDict): @@ -1073,17 +1074,17 @@ class MultilineValuesTestCase(BasicTestCase, unittest.TestCase): cf.add_section(s) for j in range(10): cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam) - with open(support.TESTFN, 'w') as f: + with open(os_helper.TESTFN, 'w') as f: cf.write(f) def tearDown(self): - os.unlink(support.TESTFN) + os.unlink(os_helper.TESTFN) def test_dominating_multiline_values(self): # We're reading from file because this is where the code changed # during performance updates in Python 3.2 cf_from_file = self.newconfig() - with open(support.TESTFN) as f: + with open(os_helper.TESTFN) as f: cf_from_file.read_file(f) self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'), self.wonderful_spam.replace('\t\n', '\n')) @@ -2161,8 +2162,8 @@ class BlatantOverrideConvertersTestCase(unittest.TestCase): class MiscTestCase(unittest.TestCase): def test__all__(self): - blacklist = {"Error"} - support.check__all__(self, configparser, blacklist=blacklist) + not_exported = {"Error"} + support.check__all__(self, configparser, not_exported=not_exported) if __name__ == '__main__': diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 90ea50105..f8e04698c 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -7,6 +7,7 @@ import threading import unittest from contextlib import * # Tests __all__ from test import support +from test.support import os_helper import weakref @@ -331,7 +332,7 @@ class FileContextTestCase(unittest.TestCase): 1 / 0 self.assertTrue(f.closed) finally: - support.unlink(tfn) + os_helper.unlink(tfn) class LockContextTestCase(unittest.TestCase): diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 571da973a..256e71683 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -3,6 +3,7 @@ import unittest import glob import test.support +from test.support import os_helper # Skip tests if dbm module doesn't exist. dbm = test.support.import_module('dbm') @@ -12,7 +13,7 @@ try: except ImportError: ndbm = None -_fname = test.support.TESTFN +_fname = os_helper.TESTFN # # Iterates over every database module supported by dbm currently available, @@ -34,7 +35,7 @@ def delete_files(): # we don't know the precise name the underlying database uses # so we use glob to locate all names for f in glob.glob(glob.escape(_fname) + "*"): - test.support.unlink(f) + os_helper.unlink(f) class AnyDBMTestCase: @@ -74,7 +75,7 @@ class AnyDBMTestCase: def test_anydbm_creation_n_file_exists_with_invalid_contents(self): # create an empty file - test.support.create_empty_file(_fname) + os_helper.create_empty_file(_fname) with dbm.open(_fname, 'n') as f: self.assertEqual(len(f), 0) @@ -169,7 +170,7 @@ class WhichDBTestCase(unittest.TestCase): # Issue 17198: check that ndbm which is referenced in whichdb is defined db_file = '{}_ndbm.db'.format(_fname) with open(db_file, 'w'): - self.addCleanup(test.support.unlink, db_file) + self.addCleanup(os_helper.unlink, db_file) self.assertIsNone(self.dbm.whichdb(db_file[:-3])) def tearDown(self): @@ -177,7 +178,7 @@ class WhichDBTestCase(unittest.TestCase): def setUp(self): delete_files() - self.filename = test.support.TESTFN + self.filename = os_helper.TESTFN self.d = dbm.open(self.filename, 'c') self.d.close() self.dbm = test.support.import_fresh_module('dbm') diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 0a6077820..0dc489362 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -10,9 +10,10 @@ import stat import unittest import dbm.dumb as dumbdbm from test import support +from test.support import os_helper from functools import partial -_fname = support.TESTFN +_fname = os_helper.TESTFN def _delete_files(): for ext in [".dir", ".dat", ".bak"]: @@ -264,7 +265,7 @@ class DumbDBMTestCase(unittest.TestCase): dumbdbm.open(_fname, flag) def test_readonly_files(self): - with support.temp_dir() as dir: + with os_helper.temp_dir() as dir: fname = os.path.join(dir, 'db') with dumbdbm.open(fname, 'n') as f: self.assertEqual(list(f.keys()), []) @@ -277,12 +278,12 @@ class DumbDBMTestCase(unittest.TestCase): self.assertEqual(sorted(f.keys()), sorted(self._dict)) f.close() # don't write - @unittest.skipUnless(support.TESTFN_NONASCII, + @unittest.skipUnless(os_helper.TESTFN_NONASCII, 'requires OS support of non-ASCII encodings') def test_nonascii_filename(self): - filename = support.TESTFN_NONASCII + filename = os_helper.TESTFN_NONASCII for suffix in ['.dir', '.dat', '.bak']: - self.addCleanup(support.unlink, filename + suffix) + self.addCleanup(os_helper.unlink, filename + suffix) with dumbdbm.open(filename, 'c') as db: db[b'key'] = b'value' self.assertTrue(os.path.exists(filename + '.dat')) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 68d751b9f..ba6e6a383 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -1,6 +1,7 @@ from collections import deque import unittest from test import support, seq_tests +from test.support import os_helper import gc import weakref import copy @@ -66,28 +67,28 @@ class TestBasic(unittest.TestCase): self.assertEqual(list(d), [7, 8, 9]) d = deque(range(200), maxlen=10) d.append(d) - support.unlink(support.TESTFN) - fo = open(support.TESTFN, "w") + os_helper.unlink(os_helper.TESTFN) + fo = open(os_helper.TESTFN, "w") try: fo.write(str(d)) fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) d = deque(range(10), maxlen=None) self.assertEqual(repr(d), 'deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])') - fo = open(support.TESTFN, "w") + fo = open(os_helper.TESTFN, "w") try: fo.write(str(d)) fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_maxlen_zero(self): it = iter(range(100)) @@ -539,15 +540,15 @@ class TestBasic(unittest.TestCase): d = deque(range(200)) d.append(d) try: - support.unlink(support.TESTFN) - fo = open(support.TESTFN, "w") + os_helper.unlink(os_helper.TESTFN) + fo = open(os_helper.TESTFN, "w") print(d, file=fo, end='') fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_init(self): self.assertRaises(TypeError, deque, 'abc', 2, 3); diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index dab99f154..2ea96285f 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -8,11 +8,14 @@ import pickle import weakref import errno -from test.support import (TESTFN, captured_stderr, check_impl_detail, - check_warnings, cpython_only, gc_collect, run_unittest, - no_tracing, unlink, import_module, script_helper, - SuppressCrashReport) +from test.support import (captured_stderr, check_impl_detail, + cpython_only, gc_collect, + no_tracing, script_helper, + SuppressCrashReport, + import_module, check_warnings,) +from test.support.os_helper import TESTFN, unlink from test import support + class NaiveException(Exception): def __init__(self, x): self.x = x diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index 5fecec861..2b7ee3a6b 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -7,8 +7,9 @@ from weakref import proxy import io import _pyio as pyio -from test.support import TESTFN from test import support +from test.support import os_helper +from test.support.os_helper import TESTFN from collections import UserList class AutoFileTests: @@ -20,7 +21,7 @@ class AutoFileTests: def tearDown(self): if self.f: self.f.close() - support.unlink(TESTFN) + os_helper.unlink(TESTFN) def testWeakRefs(self): # verify weak references @@ -140,7 +141,7 @@ class PyAutoFileTests(AutoFileTests, unittest.TestCase): class OtherFileTests: def tearDown(self): - support.unlink(TESTFN) + os_helper.unlink(TESTFN) def testModeStrings(self): # check invalid mode strings diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 4fb921e04..a1e6de3b2 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -9,8 +9,9 @@ from array import array from weakref import proxy from functools import wraps -from test.support import (TESTFN, TESTFN_UNICODE, check_warnings, run_unittest, - make_bad_fd, cpython_only, swap_attr) +from test.support import (check_warnings, run_unittest, + cpython_only, swap_attr) +from test.support.os_helper import TESTFN, TESTFN_UNICODE, make_bad_fd from collections import UserList import _io # C implementation of io diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index c2c4fcd63..4e31ec989 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -1080,10 +1080,10 @@ class TestTimeouts(TestCase): class MiscTestCase(TestCase): def test__all__(self): - blacklist = {'MSG_OOB', 'FTP_PORT', 'MAXLINE', 'CRLF', 'B_CRLF', + not_exported = {'MSG_OOB', 'FTP_PORT', 'MAXLINE', 'CRLF', 'B_CRLF', 'Error', 'parse150', 'parse227', 'parse229', 'parse257', 'print_line', 'ftpcp', 'test'} - support.check__all__(self, ftplib, blacklist=blacklist) + support.check__all__(self, ftplib, not_exported=not_exported) def test_main(): diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 545fe6073..45054b619 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -8,8 +8,9 @@ import sys import unittest import warnings from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok -from test.support import FakePath +from test.support.os_helper import FakePath def create_file(filename, data=b'foo'): @@ -97,8 +98,8 @@ class GenericTest: self.assertNotEqual(s1[n:n+1], s2[n:n+1]) def test_getsize(self): - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) create_file(filename, b'Hello') self.assertEqual(self.pathmodule.getsize(filename), 5) @@ -108,8 +109,8 @@ class GenericTest: self.assertEqual(self.pathmodule.getsize(filename), 12) def test_filetime(self): - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) create_file(filename, b'foo') @@ -126,9 +127,9 @@ class GenericTest: ) def test_exists(self): - filename = support.TESTFN + filename = os_helper.TESTFN bfilename = os.fsencode(filename) - self.addCleanup(support.unlink, filename) + self.addCleanup(os_helper.unlink, filename) self.assertIs(self.pathmodule.exists(filename), False) self.assertIs(self.pathmodule.exists(bfilename), False) @@ -163,7 +164,7 @@ class GenericTest: self.assertFalse(self.pathmodule.exists(r)) def test_isdir(self): - filename = support.TESTFN + filename = os_helper.TESTFN bfilename = os.fsencode(filename) self.assertIs(self.pathmodule.isdir(filename), False) self.assertIs(self.pathmodule.isdir(bfilename), False) @@ -178,7 +179,7 @@ class GenericTest: self.assertIs(self.pathmodule.isdir(filename), False) self.assertIs(self.pathmodule.isdir(bfilename), False) finally: - support.unlink(filename) + os_helper.unlink(filename) try: os.mkdir(filename) @@ -188,7 +189,7 @@ class GenericTest: support.rmdir(filename) def test_isfile(self): - filename = support.TESTFN + filename = os_helper.TESTFN bfilename = os.fsencode(filename) self.assertIs(self.pathmodule.isfile(filename), False) self.assertIs(self.pathmodule.isfile(bfilename), False) @@ -203,7 +204,7 @@ class GenericTest: self.assertIs(self.pathmodule.isfile(filename), True) self.assertIs(self.pathmodule.isfile(bfilename), True) finally: - support.unlink(filename) + os_helper.unlink(filename) try: os.mkdir(filename) @@ -213,10 +214,10 @@ class GenericTest: support.rmdir(filename) def test_samefile(self): - file1 = support.TESTFN - file2 = support.TESTFN + "2" - self.addCleanup(support.unlink, file1) - self.addCleanup(support.unlink, file2) + file1 = os_helper.TESTFN + file2 = os_helper.TESTFN + "2" + self.addCleanup(os_helper.unlink, file1) + self.addCleanup(os_helper.unlink, file2) create_file(file1) self.assertTrue(self.pathmodule.samefile(file1, file1)) @@ -227,10 +228,10 @@ class GenericTest: self.assertRaises(TypeError, self.pathmodule.samefile) def _test_samefile_on_link_func(self, func): - test_fn1 = support.TESTFN - test_fn2 = support.TESTFN + "2" - self.addCleanup(support.unlink, test_fn1) - self.addCleanup(support.unlink, test_fn2) + test_fn1 = os_helper.TESTFN + test_fn2 = os_helper.TESTFN + "2" + self.addCleanup(os_helper.unlink, test_fn1) + self.addCleanup(os_helper.unlink, test_fn2) create_file(test_fn1) @@ -241,7 +242,7 @@ class GenericTest: create_file(test_fn2) self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_samefile_on_symlink(self): self._test_samefile_on_link_func(os.symlink) @@ -256,10 +257,10 @@ class GenericTest: self.skipTest('os.link(): %s' % e) def test_samestat(self): - test_fn1 = support.TESTFN - test_fn2 = support.TESTFN + "2" - self.addCleanup(support.unlink, test_fn1) - self.addCleanup(support.unlink, test_fn2) + test_fn1 = os_helper.TESTFN + test_fn2 = os_helper.TESTFN + "2" + self.addCleanup(os_helper.unlink, test_fn1) + self.addCleanup(os_helper.unlink, test_fn2) create_file(test_fn1) stat1 = os.stat(test_fn1) @@ -272,10 +273,10 @@ class GenericTest: self.assertRaises(TypeError, self.pathmodule.samestat) def _test_samestat_on_link_func(self, func): - test_fn1 = support.TESTFN + "1" - test_fn2 = support.TESTFN + "2" - self.addCleanup(support.unlink, test_fn1) - self.addCleanup(support.unlink, test_fn2) + test_fn1 = os_helper.TESTFN + "1" + test_fn2 = os_helper.TESTFN + "2" + self.addCleanup(os_helper.unlink, test_fn1) + self.addCleanup(os_helper.unlink, test_fn2) create_file(test_fn1) func(test_fn1, test_fn2) @@ -287,7 +288,7 @@ class GenericTest: self.assertFalse(self.pathmodule.samestat(os.stat(test_fn1), os.stat(test_fn2))) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_samestat_on_symlink(self): self._test_samestat_on_link_func(os.symlink) @@ -302,8 +303,8 @@ class GenericTest: self.skipTest('os.link(): %s' % e) def test_sameopenfile(self): - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) create_file(filename) with open(filename, "rb", 0) as fp1: @@ -406,7 +407,7 @@ class CommonTest(GenericTest): def test_expandvars(self): expandvars = self.pathmodule.expandvars - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.clear() env["foo"] = "bar" env["{foo"] = "baz1" @@ -435,14 +436,14 @@ class CommonTest(GenericTest): self.assertEqual(expandvars(b"$foo$foo"), b"barbar") self.assertEqual(expandvars(b"$bar$bar"), b"$bar$bar") - @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') + @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') def test_expandvars_nonascii(self): expandvars = self.pathmodule.expandvars def check(value, expected): self.assertEqual(expandvars(value), expected) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.clear() - nonascii = support.FS_NONASCII + nonascii = os_helper.FS_NONASCII env['spam'] = nonascii env[nonascii] = 'ham' + nonascii check(nonascii, nonascii) @@ -501,26 +502,26 @@ class CommonTest(GenericTest): # FS encoding is probably ASCII pass else: - with support.temp_cwd(unicwd): + with os_helper.temp_cwd(unicwd): for path in ('', 'fuu', 'f\xf9\xf9', '/fuu', 'U:\\'): self.assertIsInstance(abspath(path), str) def test_nonascii_abspath(self): - if (support.TESTFN_UNDECODABLE + if (os_helper.TESTFN_UNDECODABLE # Mac OS X denies the creation of a directory with an invalid # UTF-8 name. Windows allows creating a directory with an # arbitrary bytes name, but fails to enter this directory # (when the bytes name is used). and sys.platform not in ('win32', 'darwin')): - name = support.TESTFN_UNDECODABLE - elif support.TESTFN_NONASCII: - name = support.TESTFN_NONASCII + name = os_helper.TESTFN_UNDECODABLE + elif os_helper.TESTFN_NONASCII: + name = os_helper.TESTFN_NONASCII else: - self.skipTest("need support.TESTFN_NONASCII") + self.skipTest("need os_helper.TESTFN_NONASCII") with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - with support.temp_cwd(name): + with os_helper.temp_cwd(name): self.test_abspath() def test_join_errors(self): @@ -566,9 +567,9 @@ class CommonTest(GenericTest): class PathLikeTests(unittest.TestCase): def setUp(self): - self.file_name = support.TESTFN.lower() - self.file_path = FakePath(support.TESTFN) - self.addCleanup(support.unlink, self.file_name) + self.file_name = os_helper.TESTFN.lower() + self.file_path = FakePath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, self.file_name) create_file(self.file_name, b"test_genericpath.PathLikeTests") def assertPathEqual(self, func): diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 9275dc4c5..9261276eb 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -1,7 +1,8 @@ # test_getopt.py # David Goodger 2000-08-19 -from test.support import verbose, run_doctest, EnvironmentVarGuard +from test.support import verbose, run_doctest +from test.support.os_helper import EnvironmentVarGuard import unittest import getopt diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 767bd3764..13f7e4ac4 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,8 +4,8 @@ import shutil import sys import unittest -from test.support import (TESTFN, skip_unless_symlink, - can_symlink, create_empty_file, change_cwd) +from test.support.os_helper import (TESTFN, skip_unless_symlink, + can_symlink, create_empty_file, change_cwd) class GlobTests(unittest.TestCase): diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 5ac37fa45..f340d725c 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -11,7 +11,7 @@ import sys import unittest from subprocess import PIPE, Popen from test import support -from test.support import _4G, bigmemtest +from test.support import _4G, bigmemtest, os_helper from test.support.script_helper import assert_python_ok, assert_python_failure gzip = support.import_module('gzip') @@ -29,7 +29,7 @@ data2 = b"""/* zlibmodule.c -- gzip-compatible data compression */ """ -TEMPDIR = os.path.abspath(support.TESTFN) + '-gzdir' +TEMPDIR = os.path.abspath(os_helper.TESTFN) + '-gzdir' class UnseekableIO(io.BytesIO): @@ -44,13 +44,13 @@ class UnseekableIO(io.BytesIO): class BaseTest(unittest.TestCase): - filename = support.TESTFN + filename = os_helper.TESTFN def setUp(self): - support.unlink(self.filename) + os_helper.unlink(self.filename) def tearDown(self): - support.unlink(self.filename) + os_helper.unlink(self.filename) class TestGzip(BaseTest): @@ -288,7 +288,7 @@ class TestGzip(BaseTest): self.test_write() with gzip.GzipFile(self.filename, 'r') as f: self.assertEqual(f.myfileobj.mode, 'rb') - support.unlink(self.filename) + os_helper.unlink(self.filename) with gzip.GzipFile(self.filename, 'x') as f: self.assertEqual(f.myfileobj.mode, 'xb') @@ -489,7 +489,7 @@ class TestGzip(BaseTest): self.assertEqual(g.mode, gzip.READ) for mode in "wb", "ab", "xb": if "x" in mode: - support.unlink(self.filename) + os_helper.unlink(self.filename) with open(self.filename, mode) as f: with gzip.GzipFile(fileobj=f) as g: self.assertEqual(g.mode, gzip.WRITE) @@ -601,7 +601,7 @@ class TestOpen(BaseTest): with self.assertRaises(FileExistsError): gzip.open(self.filename, "xb") - support.unlink(self.filename) + os_helper.unlink(self.filename) with gzip.open(self.filename, "xb") as f: f.write(uncompressed) with open(self.filename, "rb") as f: @@ -638,7 +638,7 @@ class TestOpen(BaseTest): with self.assertRaises(FileExistsError): gzip.open(self.filename, "x") - support.unlink(self.filename) + os_helper.unlink(self.filename) with gzip.open(self.filename, "x") as f: f.write(uncompressed) with open(self.filename, "rb") as f: @@ -732,7 +732,7 @@ def create_and_remove_directory(directory): try: return function(*args, **kwargs) finally: - support.rmtree(directory) + os_helper.rmtree(directory) return wrapper return decorator diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py index 0363aef15..eb8459b71 100644 --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -3,6 +3,7 @@ import os import re import test.support +from test.support import os_helper import time import unittest import urllib.request @@ -330,14 +331,14 @@ def _interact(cookiejar, url, set_cookie_hdrs, hdr_name): class FileCookieJarTests(unittest.TestCase): def test_constructor_with_str(self): - filename = test.support.TESTFN + filename = os_helper.TESTFN c = LWPCookieJar(filename) self.assertEqual(c.filename, filename) # TODO: RUSTPYTHON @unittest.expectedFailure def test_constructor_with_path_like(self): - filename = pathlib.Path(test.support.TESTFN) + filename = pathlib.Path(os_helper.TESTFN) c = LWPCookieJar(filename) self.assertEqual(c.filename, os.fspath(filename)) @@ -359,7 +360,7 @@ class FileCookieJarTests(unittest.TestCase): def test_lwp_valueless_cookie(self): # cookies with no value should be saved and loaded consistently - filename = test.support.TESTFN + filename = os_helper.TESTFN c = LWPCookieJar() interact_netscape(c, "http://www.acme.com/", 'boo') self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None) @@ -374,7 +375,7 @@ class FileCookieJarTests(unittest.TestCase): def test_bad_magic(self): # OSErrors (eg. file doesn't exist) are allowed to propagate - filename = test.support.TESTFN + filename = os_helper.TESTFN for cookiejar_class in LWPCookieJar, MozillaCookieJar: c = cookiejar_class() try: @@ -483,7 +484,7 @@ class CookieTests(unittest.TestCase): def test_missing_value(self): # missing = sign in Cookie: header is regarded by Mozilla as a missing # name, and by http.cookiejar as a missing value - filename = test.support.TESTFN + filename = os_helper.TESTFN c = MozillaCookieJar(filename) interact_netscape(c, "http://www.acme.com/", 'eggs') interact_netscape(c, "http://www.acme.com/", '"spam"; path=/foo/') @@ -1731,7 +1732,7 @@ class LWPCookieTests(unittest.TestCase): self.assertEqual(len(c), 6) # save and restore - filename = test.support.TESTFN + filename = os_helper.TESTFN try: c.save(filename, ignore_discard=True) @@ -1771,7 +1772,7 @@ class LWPCookieTests(unittest.TestCase): # Save / load Mozilla/Netscape cookie file format. year_plus_one = time.localtime()[0] + 1 - filename = test.support.TESTFN + filename = os_helper.TESTFN c = MozillaCookieJar(filename, policy=DefaultCookiePolicy(rfc2965=True)) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 016bfb61b..6b13efe76 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -13,6 +13,7 @@ import unittest TestCase = unittest.TestCase from test import support +from test.support import os_helper here = os.path.dirname(__file__) # Self-signed cert file for 'localhost' @@ -1916,10 +1917,10 @@ class RequestBodyTest(TestCase): self.assertEqual(b'body\xc1', f.read()) def test_text_file_body(self): - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, "w") as f: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, "w") as f: f.write("body") - with open(support.TESTFN) as f: + with open(os_helper.TESTFN) as f: self.conn.request("PUT", "/url", f) message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) @@ -1931,10 +1932,10 @@ class RequestBodyTest(TestCase): self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read()) def test_binary_file_body(self): - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, "wb") as f: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, "wb") as f: f.write(b"body\xc1") - with open(support.TESTFN, "rb") as f: + with open(os_helper.TESTFN, "rb") as f: self.conn.request("PUT", "/url", f) message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 97692cd2b..2a4587b9d 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -29,6 +29,7 @@ from io import BytesIO import unittest from test import support +from test.support import os_helper class NoLogRequestHandler: @@ -64,7 +65,7 @@ class TestServerThread(threading.Thread): class BaseTestCase(unittest.TestCase): def setUp(self): self._threads = support.threading_setup() - os.environ = support.EnvironmentVarGuard() + os.environ = os_helper.EnvironmentVarGuard() self.server_started = threading.Event() self.thread = TestServerThread(self, self.request_handler) self.thread.start() @@ -391,13 +392,13 @@ class SimpleHTTPServerTestCase(BaseTestCase): 'undecodable name cannot always be decoded on macOS') @unittest.skipIf(sys.platform == 'win32', 'undecodable name cannot be decoded on win32') - @unittest.skipUnless(support.TESTFN_UNDECODABLE, - 'need support.TESTFN_UNDECODABLE') + @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE, + 'need os_helper.TESTFN_UNDECODABLE') def test_undecodable_filename(self): enc = sys.getfilesystemencoding() - filename = os.fsdecode(support.TESTFN_UNDECODABLE) + '.txt' + filename = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.txt' with open(os.path.join(self.tempdir, filename), 'wb') as f: - f.write(support.TESTFN_UNDECODABLE) + f.write(os_helper.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') if sys.platform == 'darwin': # On Mac OS the HFS+ filesystem replaces bytes that aren't valid @@ -414,7 +415,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): .encode(enc, 'surrogateescape'), body) response = self.request(self.base_url + '/' + quotedname) self.check_status_and_reason(response, HTTPStatus.OK, - data=support.TESTFN_UNDECODABLE) + data=os_helper.TESTFN_UNDECODABLE) def test_get(self): #constructs the path relative to the root directory of the HTTPServer @@ -616,7 +617,7 @@ class CGIHTTPServerTestCase(BaseTestCase): # The shebang line should be pure ASCII: use symlink if possible. # See issue #7668. self._pythonexe_symlink = None - if support.can_symlink(): + if os_helper.can_symlink(): self.pythonexe = os.path.join(self.parent_dir, 'python') self._pythonexe_symlink = support.PythonSymlink(self.pythonexe).__enter__() else: diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 29365d615..78a658dfd 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -5,7 +5,7 @@ import os.path import py_compile import sys from test import support -from test.support import script_helper +from test.support import script_helper, os_helper import unittest import warnings with warnings.catch_warnings(): @@ -114,8 +114,8 @@ class ImportTests(unittest.TestCase): self.assertEqual(file.encoding, 'cp1252') finally: del sys.path[0] - support.unlink(temp_mod_name + '.py') - support.unlink(temp_mod_name + '.pyc') + os_helper.unlink(temp_mod_name + '.py') + os_helper.unlink(temp_mod_name + '.pyc') def test_issue5604(self): # Test cannot cover imp.load_compiled function. @@ -199,10 +199,10 @@ class ImportTests(unittest.TestCase): finally: del sys.path[0] for ext in ('.py', '.pyc'): - support.unlink(temp_mod_name + ext) - support.unlink(init_file_name + ext) - support.rmtree(test_package_name) - support.rmtree('__pycache__') + os_helper.unlink(temp_mod_name + ext) + os_helper.unlink(init_file_name + ext) + os_helper.rmtree(test_package_name) + os_helper.rmtree('__pycache__') def test_issue9319(self): path = os.path.dirname(__file__) @@ -220,7 +220,7 @@ class ImportTests(unittest.TestCase): # workaround orig_path = os.path orig_getenv = os.getenv - with support.EnvironmentVarGuard(): + with os_helper.EnvironmentVarGuard(): x = imp.find_module("os") self.addCleanup(x[0].close) new_os = imp.load_module("os", *x) @@ -306,11 +306,11 @@ class ImportTests(unittest.TestCase): @unittest.skipIf(sys.dont_write_bytecode, "test meaningful only when writing bytecode") def test_bug7732(self): - with support.temp_cwd(): - source = support.TESTFN + '.py' + with os_helper.temp_cwd(): + source = os_helper.TESTFN + '.py' os.mkdir(source) self.assertRaisesRegex(ImportError, '^No module', - imp.find_module, support.TESTFN, ["."]) + imp.find_module, os_helper.TESTFN, ["."]) def test_multiple_calls_to_get_data(self): # Issue #18755: make sure multiple calls to get_data() can succeed. @@ -377,7 +377,7 @@ class ImportTests(unittest.TestCase): @unittest.expectedFailure def test_find_and_load_checked_pyc(self): # issue 34056 - with support.temp_cwd(): + with os_helper.temp_cwd(): with open('mymod.py', 'wb') as fp: fp.write(b'x = 42\n') py_compile.compile( @@ -403,7 +403,7 @@ class ReloadTests(unittest.TestCase): # state after reversion. Reinitialising the module contents # and just reverting os.environ to its previous state is an OK # workaround - with support.EnvironmentVarGuard(): + with os_helper.EnvironmentVarGuard(): import os imp.reload(os) @@ -456,10 +456,10 @@ class PEP3147Tests(unittest.TestCase): class NullImporterTests(unittest.TestCase): - @unittest.skipIf(support.TESTFN_UNENCODABLE is None, + @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, "Need an undecodeable filename") def test_unencodeable(self): - name = support.TESTFN_UNENCODABLE + name = os_helper.TESTFN_UNENCODABLE os.mkdir(name) try: self.assertRaises(ImportError, imp.NullImporter, name) diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py index 0dd9c8615..7f4b2b3d1 100644 --- a/Lib/test/test_importlib/extension/test_case_sensitivity.py +++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py @@ -1,5 +1,6 @@ from importlib import _bootstrap_external from test import support +from test.support import os_helper import unittest from .. import util @@ -22,14 +23,14 @@ class ExtensionModuleCaseSensitivityTest(util.CASEOKTestBase): return finder.find_module(bad_name) def test_case_sensitive(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.unset('PYTHONCASEOK') self.caseok_env_changed(should_exist=False) loader = self.find_module() self.assertIsNone(loader) def test_case_insensitivity(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.set('PYTHONCASEOK', '1') self.caseok_env_changed(should_exist=True) loader = self.find_module() diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index edb745c2c..9e022f925 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -7,6 +7,7 @@ machinery = test_util.import_importlib('importlib.machinery') import os.path import sys from test import support +from test.support import os_helper import types import unittest import warnings @@ -244,7 +245,7 @@ class ReloadTests: def test_reload_location_changed(self): name = 'spam' - with support.temp_cwd(None) as cwd: + with os_helper.temp_cwd(None) as cwd: with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a plain module. @@ -257,7 +258,7 @@ class ReloadTests: '__cached__': cached, '__doc__': None, } - support.create_empty_file(path) + os_helper.create_empty_file(path) module = self.init.import_module(name) ns = vars(module).copy() loader = ns.pop('__loader__') @@ -295,7 +296,7 @@ class ReloadTests: def test_reload_namespace_changed(self): name = 'spam' - with support.temp_cwd(None) as cwd: + with os_helper.temp_cwd(None) as cwd: with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a namespace package. diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index 196ea1c9d..c1c161331 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -11,6 +11,7 @@ import os import os.path from pathlib import Path, PurePath from test import support +from test.support import os_helper import unittest import sys import tempfile @@ -147,7 +148,7 @@ def uncache(*names): @contextlib.contextmanager def temp_module(name, content='', *, pkg=False): conflicts = [n for n in sys.modules if n.partition('.')[0] == name] - with support.temp_cwd(None) as cwd: + with os_helper.temp_cwd(None) as cwd: with uncache(name, *conflicts): with support.DirsOnSysPath(cwd): invalidate_caches() @@ -385,7 +386,7 @@ def create_modules(*names): state_manager.__exit__(None, None, None) if uncache_manager is not None: uncache_manager.__exit__(None, None, None) - support.rmtree(temp_dir) + os_helper.rmtree(temp_dir) def mock_path_hook(*entries, importer): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 02c5bb0fd..f72de9d65 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -37,8 +37,9 @@ import weakref from collections import deque, UserList from itertools import cycle, count from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok, run_python_until_end -from test.support import FakePath +from test.support.os_helper import FakePath import codecs import io # C implementation of io @@ -314,10 +315,10 @@ class PyMockNonBlockWriterIO(MockNonBlockWriterIO, pyio.RawIOBase): class IOTest(unittest.TestCase): def setUp(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def tearDown(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def write_ops(self, f): self.assertEqual(f.write(b"blah."), 5) @@ -402,19 +403,19 @@ class IOTest(unittest.TestCase): # Try writing on a file opened in read mode and vice-versa. exc = self.UnsupportedOperation for mode in ("w", "wb"): - with self.open(support.TESTFN, mode) as fp: + with self.open(os_helper.TESTFN, mode) as fp: self.assertRaises(exc, fp.read) self.assertRaises(exc, fp.readline) - with self.open(support.TESTFN, "wb", buffering=0) as fp: + with self.open(os_helper.TESTFN, "wb", buffering=0) as fp: self.assertRaises(exc, fp.read) self.assertRaises(exc, fp.readline) - with self.open(support.TESTFN, "rb", buffering=0) as fp: + with self.open(os_helper.TESTFN, "rb", buffering=0) as fp: self.assertRaises(exc, fp.write, b"blah") self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(support.TESTFN, "rb") as fp: + with self.open(os_helper.TESTFN, "rb") as fp: self.assertRaises(exc, fp.write, b"blah") self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(support.TESTFN, "r") as fp: + with self.open(os_helper.TESTFN, "r") as fp: self.assertRaises(exc, fp.write, "blah") self.assertRaises(exc, fp.writelines, ["blah\n"]) # Non-zero seeking from current or end pos @@ -535,33 +536,33 @@ class IOTest(unittest.TestCase): self.assertRaises(ValueError, self.open, bytes_fn, 'w') def test_raw_file_io(self): - with self.open(support.TESTFN, "wb", buffering=0) as f: + with self.open(os_helper.TESTFN, "wb", buffering=0) as f: self.assertEqual(f.readable(), False) self.assertEqual(f.writable(), True) self.assertEqual(f.seekable(), True) self.write_ops(f) - with self.open(support.TESTFN, "rb", buffering=0) as f: + with self.open(os_helper.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.readable(), True) self.assertEqual(f.writable(), False) self.assertEqual(f.seekable(), True) self.read_ops(f) def test_buffered_file_io(self): - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: self.assertEqual(f.readable(), False) self.assertEqual(f.writable(), True) self.assertEqual(f.seekable(), True) self.write_ops(f) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.readable(), True) self.assertEqual(f.writable(), False) self.assertEqual(f.seekable(), True) self.read_ops(f, True) def test_readline(self): - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(b"abc\ndef\nxyzzy\nfoo\x00bar\nanother line") - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.readline(), b"abc\n") self.assertEqual(f.readline(10), b"def\n") self.assertEqual(f.readline(2), b"xy") @@ -569,7 +570,7 @@ class IOTest(unittest.TestCase): self.assertEqual(f.readline(), b"foo\x00bar\n") self.assertEqual(f.readline(None), b"another line") self.assertRaises(TypeError, f.readline, 5.3) - with self.open(support.TESTFN, "r") as f: + with self.open(os_helper.TESTFN, "r") as f: self.assertRaises(TypeError, f.readline, 5.3) def test_readline_nonsizeable(self): @@ -604,20 +605,20 @@ class IOTest(unittest.TestCase): support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) - with self.open(support.TESTFN, "w+b", 0) as f: + with self.open(os_helper.TESTFN, "w+b", 0) as f: self.large_file_ops(f) - with self.open(support.TESTFN, "w+b") as f: + with self.open(os_helper.TESTFN, "w+b") as f: self.large_file_ops(f) def test_with_open(self): for bufsize in (0, 100): f = None - with self.open(support.TESTFN, "wb", bufsize) as f: + with self.open(os_helper.TESTFN, "wb", bufsize) as f: f.write(b"xxx") self.assertEqual(f.closed, True) f = None try: - with self.open(support.TESTFN, "wb", bufsize) as f: + with self.open(os_helper.TESTFN, "wb", bufsize) as f: 1/0 except ZeroDivisionError: self.assertEqual(f.closed, True) @@ -626,13 +627,13 @@ class IOTest(unittest.TestCase): # issue 5008 def test_append_mode_tell(self): - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(b"xxx") - with self.open(support.TESTFN, "ab", buffering=0) as f: + with self.open(os_helper.TESTFN, "ab", buffering=0) as f: self.assertEqual(f.tell(), 3) - with self.open(support.TESTFN, "ab") as f: + with self.open(os_helper.TESTFN, "ab") as f: self.assertEqual(f.tell(), 3) - with self.open(support.TESTFN, "a") as f: + with self.open(os_helper.TESTFN, "a") as f: self.assertGreater(f.tell(), 0) def test_destructor(self): @@ -653,12 +654,12 @@ class IOTest(unittest.TestCase): record.append(3) super().flush() with support.check_warnings(('', ResourceWarning)): - f = MyFileIO(support.TESTFN, "wb") + f = MyFileIO(os_helper.TESTFN, "wb") f.write(b"xxx") del f support.gc_collect() self.assertEqual(record, [1, 2, 3]) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"xxx") def _check_base_destructor(self, base): @@ -704,9 +705,9 @@ class IOTest(unittest.TestCase): self._check_base_destructor(self.TextIOBase) def test_close_flushes(self): - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(b"xxx") - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"xxx") def test_array_writes(self): @@ -717,25 +718,25 @@ class IOTest(unittest.TestCase): self.assertEqual(f.write(a), n) f.writelines((a,)) check(self.BytesIO()) - check(self.FileIO(support.TESTFN, "w")) + check(self.FileIO(os_helper.TESTFN, "w")) check(self.BufferedWriter(self.MockRawIO())) check(self.BufferedRandom(self.MockRawIO())) check(self.BufferedRWPair(self.MockRawIO(), self.MockRawIO())) def test_closefd(self): - self.assertRaises(ValueError, self.open, support.TESTFN, 'w', + self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'w', closefd=False) def test_read_closed(self): - with self.open(support.TESTFN, "w") as f: + with self.open(os_helper.TESTFN, "w") as f: f.write("egg\n") - with self.open(support.TESTFN, "r") as f: + with self.open(os_helper.TESTFN, "r") as f: file = self.open(f.fileno(), "r", closefd=False) self.assertEqual(file.read(), "egg\n") file.seek(0) file.close() self.assertRaises(ValueError, file.read) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: file = self.open(f.fileno(), "rb", closefd=False) self.assertEqual(file.read()[:3], b"egg") file.close() @@ -743,12 +744,12 @@ class IOTest(unittest.TestCase): def test_no_closefd_with_filename(self): # can't use closefd in combination with a file name - self.assertRaises(ValueError, self.open, support.TESTFN, "r", closefd=False) + self.assertRaises(ValueError, self.open, os_helper.TESTFN, "r", closefd=False) def test_closefd_attr(self): - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(b"egg\n") - with self.open(support.TESTFN, "r") as f: + with self.open(os_helper.TESTFN, "r") as f: self.assertEqual(f.buffer.raw.closefd, True) file = self.open(f.fileno(), "r", closefd=False) self.assertEqual(file.buffer.raw.closefd, False) @@ -759,14 +760,14 @@ class IOTest(unittest.TestCase): # FileIO objects are collected, and collecting them flushes # all data to disk. with support.check_warnings(('', ResourceWarning)): - f = self.FileIO(support.TESTFN, "wb") + f = self.FileIO(os_helper.TESTFN, "wb") f.write(b"abcxxx") f.f = f wr = weakref.ref(f) del f support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"abcxxx") def test_unbounded_file(self): @@ -803,29 +804,29 @@ class IOTest(unittest.TestCase): def test_flush_error_on_close(self): # raw file # Issue #5700: io.FileIO calls flush() after file closed - self.check_flush_error_on_close(support.TESTFN, 'wb', buffering=0) - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(os_helper.TESTFN, 'wb', buffering=0) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', buffering=0) - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False) os.close(fd) # buffered io - self.check_flush_error_on_close(support.TESTFN, 'wb') - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(os_helper.TESTFN, 'wb') + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb') - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', closefd=False) os.close(fd) # text io - self.check_flush_error_on_close(support.TESTFN, 'w') - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(os_helper.TESTFN, 'w') + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'w') - fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'w', closefd=False) os.close(fd) def test_multi_close(self): - f = self.open(support.TESTFN, "wb", buffering=0) + f = self.open(os_helper.TESTFN, "wb", buffering=0) f.close() f.close() f.close() @@ -856,9 +857,9 @@ class IOTest(unittest.TestCase): self.assertTrue(hasattr(obj, "__dict__")) def test_opener(self): - with self.open(support.TESTFN, "w") as f: + with self.open(os_helper.TESTFN, "w") as f: f.write("egg\n") - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) def opener(path, flags): return fd with self.open("non-existent", "r", opener=opener) as f: @@ -895,12 +896,12 @@ class IOTest(unittest.TestCase): def test_nonbuffered_textio(self): with support.check_no_resource_warning(self): with self.assertRaises(ValueError): - self.open(support.TESTFN, 'w', buffering=0) + self.open(os_helper.TESTFN, 'w', buffering=0) def test_invalid_newline(self): with support.check_no_resource_warning(self): with self.assertRaises(ValueError): - self.open(support.TESTFN, 'w', newline='invalid') + self.open(os_helper.TESTFN, 'w', newline='invalid') def test_buffered_readinto_mixin(self): # Test the implementation provided by BufferedIOBase @@ -923,10 +924,10 @@ class IOTest(unittest.TestCase): with self.open(path, "r") as f: self.assertEqual(f.read(), "egg\n") - check_path_succeeds(FakePath(support.TESTFN)) - check_path_succeeds(FakePath(support.TESTFN.encode('utf-8'))) + check_path_succeeds(FakePath(os_helper.TESTFN)) + check_path_succeeds(FakePath(os_helper.TESTFN.encode('utf-8'))) - with self.open(support.TESTFN, "w") as f: + with self.open(os_helper.TESTFN, "w") as f: bad_path = FakePath(f.fileno()) with self.assertRaises(TypeError): self.open(bad_path, 'w') @@ -941,7 +942,7 @@ class IOTest(unittest.TestCase): # ensure that refcounting is correct with some error conditions with self.assertRaisesRegex(ValueError, 'read/write/append mode'): - self.open(FakePath(support.TESTFN), 'rwxa') + self.open(FakePath(os_helper.TESTFN), 'rwxa') def test_RawIOBase_readall(self): # Exercise the default unlimited RawIOBase.read() and readall() @@ -1462,9 +1463,9 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): l = list(range(256)) * N random.shuffle(l) s = bytes(bytearray(l)) - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(s) - with self.open(support.TESTFN, self.read_mode, buffering=0) as raw: + with self.open(os_helper.TESTFN, self.read_mode, buffering=0) as raw: bufio = self.tp(raw, 8) errors = [] results = [] @@ -1490,7 +1491,7 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): c = bytes(bytearray([i])) self.assertEqual(s.count(c), N) finally: - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_unseekable(self): bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) @@ -1576,9 +1577,9 @@ class CBufferedReaderTest(BufferedReaderTest, SizeofTest): def test_garbage_collection(self): # C BufferedReader objects are collected. # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) with support.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(support.TESTFN, "w+b") + rawio = self.FileIO(os_helper.TESTFN, "w+b") f = self.tp(rawio) f.f = f wr = weakref.ref(f) @@ -1788,26 +1789,26 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): def test_truncate(self): # Truncate implicitly flushes the buffer. - self.addCleanup(support.unlink, support.TESTFN) - with self.open(support.TESTFN, self.write_mode, buffering=0) as raw: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: bufio = self.tp(raw, 8) bufio.write(b"abcdef") self.assertEqual(bufio.truncate(3), 3) self.assertEqual(bufio.tell(), 6) - with self.open(support.TESTFN, "rb", buffering=0) as f: + with self.open(os_helper.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.read(), b"abc") def test_truncate_after_write(self): # Ensure that truncate preserves the file position after # writes longer than the buffer size. # Issue: https://bugs.python.org/issue32228 - self.addCleanup(support.unlink, support.TESTFN) - with self.open(support.TESTFN, "wb") as f: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with self.open(os_helper.TESTFN, "wb") as f: # Fill with some buffer f.write(b'\x00' * 10000) buffer_sizes = [8192, 4096, 200] for buffer_size in buffer_sizes: - with self.open(support.TESTFN, "r+b", buffering=buffer_size) as f: + with self.open(os_helper.TESTFN, "r+b", buffering=buffer_size) as f: f.write(b'\x00' * (buffer_size + 1)) # After write write_pos and write_end are set to 0 f.read(1) @@ -1835,7 +1836,7 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): # writing the buffer to the raw streams. This is in addition # to concurrency issues due to switching threads in the middle # of Python code. - with self.open(support.TESTFN, self.write_mode, buffering=0) as raw: + with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: bufio = self.tp(raw, 8) errors = [] def f(): @@ -1855,12 +1856,12 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): self.assertFalse(errors, "the following exceptions were caught: %r" % errors) bufio.close() - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: s = f.read() for i in range(256): self.assertEqual(s.count(bytes([i])), N) finally: - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_misbehaved_io(self): rawio = self.MisbehavedRawIO() @@ -1931,9 +1932,9 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest): # C BufferedWriter objects are collected, and collecting them flushes # all data to disk. # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os_helper.unlink, os_helper.TESTFN) with support.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(support.TESTFN, "w+b") + rawio = self.FileIO(os_helper.TESTFN, "w+b") f = self.tp(rawio) f.write(b"123xxx") f.x = f @@ -1941,7 +1942,7 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest): del f support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"123xxx") # TODO: RUSTPYTHON @@ -2594,10 +2595,10 @@ class TextIOWrapperTest(unittest.TestCase): def setUp(self): self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def tearDown(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_constructor(self): r = self.BytesIO(b"\xc3\xa9\n\n") @@ -2935,11 +2936,11 @@ class TextIOWrapperTest(unittest.TestCase): def test_basic_io(self): for chunksize in (1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65): for enc in "ascii", "latin-1", "utf-8" :# , "utf-16-be", "utf-16-le": - f = self.open(support.TESTFN, "w+", encoding=enc) + f = self.open(os_helper.TESTFN, "w+", encoding=enc) f._CHUNK_SIZE = chunksize self.assertEqual(f.write("abc"), 3) f.close() - f = self.open(support.TESTFN, "r+", encoding=enc) + f = self.open(os_helper.TESTFN, "r+", encoding=enc) f._CHUNK_SIZE = chunksize self.assertEqual(f.tell(), 0) self.assertEqual(f.read(), "abc") @@ -2984,7 +2985,7 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(rlines, wlines) def test_telling(self): - f = self.open(support.TESTFN, "w+", encoding="utf-8") + f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") p0 = f.tell() f.write("\xff\n") p1 = f.tell() @@ -3012,9 +3013,9 @@ class TextIOWrapperTest(unittest.TestCase): u_suffix = "\u8888\n" suffix = bytes(u_suffix.encode("utf-8")) line = prefix + suffix - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(line*2) - with self.open(support.TESTFN, "r", encoding="utf-8") as f: + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: s = f.read(prefix_size) self.assertEqual(s, str(prefix, "ascii")) self.assertEqual(f.tell(), prefix_size) @@ -3023,9 +3024,9 @@ class TextIOWrapperTest(unittest.TestCase): def test_seeking_too(self): # Regression test for a specific bug data = b'\xe0\xbf\xbf\n' - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: f.write(data) - with self.open(support.TESTFN, "r", encoding="utf-8") as f: + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: f._CHUNK_SIZE # Just test that it exists f._CHUNK_SIZE = 2 f.readline() @@ -3039,17 +3040,17 @@ class TextIOWrapperTest(unittest.TestCase): def test_seek_and_tell_with_data(data, min_pos=0): """Tell/seek to various points within a data stream and ensure that the decoded data returned by read() is consistent.""" - f = self.open(support.TESTFN, 'wb') + f = self.open(os_helper.TESTFN, 'wb') f.write(data) f.close() - f = self.open(support.TESTFN, encoding='test_decoder') + f = self.open(os_helper.TESTFN, encoding='test_decoder') f._CHUNK_SIZE = CHUNK_SIZE decoded = f.read() f.close() for i in range(min_pos, len(decoded) + 1): # seek positions for j in [1, 5, len(decoded) - i]: # read lengths - f = self.open(support.TESTFN, encoding='test_decoder') + f = self.open(os_helper.TESTFN, encoding='test_decoder') self.assertEqual(f.read(i), decoded[:i]) cookie = f.tell() self.assertEqual(f.read(j), decoded[i:i + j]) @@ -3081,11 +3082,11 @@ class TextIOWrapperTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_multibyte_seek_and_tell(self): - f = self.open(support.TESTFN, "w", encoding="euc_jp") + f = self.open(os_helper.TESTFN, "w", encoding="euc_jp") f.write("AB\n\u3046\u3048\n") f.close() - f = self.open(support.TESTFN, "r", encoding="euc_jp") + f = self.open(os_helper.TESTFN, "r", encoding="euc_jp") self.assertEqual(f.readline(), "AB\n") p0 = f.tell() self.assertEqual(f.readline(), "\u3046\u3048\n") @@ -3098,7 +3099,7 @@ class TextIOWrapperTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_seek_with_encoder_state(self): - f = self.open(support.TESTFN, "w", encoding="euc_jis_2004") + f = self.open(os_helper.TESTFN, "w", encoding="euc_jis_2004") f.write("\u00e6\u0300") p0 = f.tell() f.write("\u00e6") @@ -3106,7 +3107,7 @@ class TextIOWrapperTest(unittest.TestCase): f.write("\u0300") f.close() - f = self.open(support.TESTFN, "r", encoding="euc_jis_2004") + f = self.open(os_helper.TESTFN, "r", encoding="euc_jis_2004") self.assertEqual(f.readline(), "\u00e6\u0300\u0300") f.close() @@ -3254,7 +3255,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_append_bom(self): # The BOM is not written again when appending to a non-empty file - filename = support.TESTFN + filename = os_helper.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3271,7 +3272,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_seek_bom(self): # Same test, but when seeking manually - filename = support.TESTFN + filename = os_helper.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3288,7 +3289,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_seek_append_bom(self): # Same test, but first seek to the start and then to the end - filename = support.TESTFN + filename = os_helper.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3300,16 +3301,16 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) def test_errors_property(self): - with self.open(support.TESTFN, "w") as f: + with self.open(os_helper.TESTFN, "w") as f: self.assertEqual(f.errors, "strict") - with self.open(support.TESTFN, "w", errors="replace") as f: + with self.open(os_helper.TESTFN, "w", errors="replace") as f: self.assertEqual(f.errors, "replace") @support.no_tracing def test_threads_write(self): # Issue6750: concurrent writes could duplicate data event = threading.Event() - with self.open(support.TESTFN, "w", buffering=1) as f: + with self.open(os_helper.TESTFN, "w", buffering=1) as f: def run(n): text = "Thread%03d\n" % n event.wait() @@ -3318,7 +3319,7 @@ class TextIOWrapperTest(unittest.TestCase): for x in range(20)] with support.start_threads(threads, event.set): time.sleep(0.02) - with self.open(support.TESTFN) as f: + with self.open(os_helper.TESTFN) as f: content = f.read() for n in range(20): self.assertEqual(content.count("Thread%03d\n" % n), 1) @@ -3892,7 +3893,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): # all data to disk. # The Python version has __del__, so it ends in gc.garbage instead. with support.check_warnings(('', ResourceWarning)): - rawio = io.FileIO(support.TESTFN, "wb") + rawio = io.FileIO(os_helper.TESTFN, "wb") b = self.BufferedWriter(rawio) t = self.TextIOWrapper(b, encoding="ascii") t.write("456def") @@ -3901,7 +3902,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): del t support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(support.TESTFN, "rb") as f: + with self.open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"456def") def test_rwpair_cleared_before_textio(self): @@ -4069,7 +4070,7 @@ class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest): class MiscIOTest(unittest.TestCase): def tearDown(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test___all__(self): for name in self.io.__all__: @@ -4083,23 +4084,23 @@ class MiscIOTest(unittest.TestCase): self.assertTrue(issubclass(obj, self.IOBase)) def test_attributes(self): - f = self.open(support.TESTFN, "wb", buffering=0) + f = self.open(os_helper.TESTFN, "wb", buffering=0) self.assertEqual(f.mode, "wb") f.close() # XXX RUSTPYTHON: universal mode is deprecated anyway, so I # feel fine about skipping it # with support.check_warnings(('', DeprecationWarning)): - # f = self.open(support.TESTFN, "U") - # self.assertEqual(f.name, support.TESTFN) - # self.assertEqual(f.buffer.name, support.TESTFN) - # self.assertEqual(f.buffer.raw.name, support.TESTFN) + # f = self.open(os_helper.TESTFN, "U") + # self.assertEqual(f.name, os_helper.TESTFN) + # self.assertEqual(f.buffer.name, os_helper.TESTFN) + # self.assertEqual(f.buffer.raw.name, os_helper.TESTFN) # self.assertEqual(f.mode, "U") # self.assertEqual(f.buffer.mode, "rb") # self.assertEqual(f.buffer.raw.mode, "rb") # f.close() - f = self.open(support.TESTFN, "w+") + f = self.open(os_helper.TESTFN, "w+") self.assertEqual(f.mode, "w+") self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? self.assertEqual(f.buffer.raw.mode, "rb+") @@ -4141,7 +4142,7 @@ class MiscIOTest(unittest.TestCase): {"mode": "w+", "buffering": 2}, {"mode": "w+b", "buffering": 0}, ]: - f = self.open(support.TESTFN, **kwargs) + f = self.open(os_helper.TESTFN, **kwargs) f.close() self.assertRaises(ValueError, f.flush) self.assertRaises(ValueError, f.fileno) @@ -4193,17 +4194,17 @@ class MiscIOTest(unittest.TestCase): self.assertIsInstance(self.TextIOBase, abc.ABCMeta) def _check_abc_inheritance(self, abcmodule): - with self.open(support.TESTFN, "wb", buffering=0) as f: + with self.open(os_helper.TESTFN, "wb", buffering=0) as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertIsInstance(f, abcmodule.RawIOBase) self.assertNotIsInstance(f, abcmodule.BufferedIOBase) self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(support.TESTFN, "wb") as f: + with self.open(os_helper.TESTFN, "wb") as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertNotIsInstance(f, abcmodule.RawIOBase) self.assertIsInstance(f, abcmodule.BufferedIOBase) self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(support.TESTFN, "w") as f: + with self.open(os_helper.TESTFN, "w") as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertNotIsInstance(f, abcmodule.RawIOBase) self.assertNotIsInstance(f, abcmodule.BufferedIOBase) @@ -4229,9 +4230,9 @@ class MiscIOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_warn_on_dealloc(self): - self._check_warn_on_dealloc(support.TESTFN, "wb", buffering=0) - self._check_warn_on_dealloc(support.TESTFN, "wb") - self._check_warn_on_dealloc(support.TESTFN, "w") + self._check_warn_on_dealloc(os_helper.TESTFN, "wb", buffering=0) + self._check_warn_on_dealloc(os_helper.TESTFN, "wb") + self._check_warn_on_dealloc(os_helper.TESTFN, "w") def _check_warn_on_dealloc_fd(self, *args, **kwargs): fds = [] @@ -4274,7 +4275,7 @@ class MiscIOTest(unittest.TestCase): {"mode": "w+b", "buffering": 0}, ]: for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - with self.open(support.TESTFN, **kwargs) as f: + with self.open(os_helper.TESTFN, **kwargs) as f: self.assertRaises(TypeError, pickle.dumps, f, protocol) # TODO: RUSTPYTHON @@ -4341,20 +4342,20 @@ class MiscIOTest(unittest.TestCase): def test_create_fail(self): # 'x' mode fails if file is existing - with self.open(support.TESTFN, 'w'): + with self.open(os_helper.TESTFN, 'w'): pass - self.assertRaises(FileExistsError, self.open, support.TESTFN, 'x') + self.assertRaises(FileExistsError, self.open, os_helper.TESTFN, 'x') def test_create_writes(self): # 'x' mode opens for writing - with self.open(support.TESTFN, 'xb') as f: + with self.open(os_helper.TESTFN, 'xb') as f: f.write(b"spam") - with self.open(support.TESTFN, 'rb') as f: + with self.open(os_helper.TESTFN, 'rb') as f: self.assertEqual(b"spam", f.read()) def test_open_allargs(self): # there used to be a buffer overflow in the parser for rawmode - self.assertRaises(ValueError, self.open, support.TESTFN, 'rwax+') + self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'rwax+') class CMiscIOTest(MiscIOTest): diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index 748ad5e93..065cedc85 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -2,8 +2,9 @@ import sys import unittest -from test.support import run_unittest, TESTFN, unlink, cpython_only +from test.support import run_unittest, cpython_only # from test.support import check_free_after_iterating +from test.support.os_helper import TESTFN, unlink import pickle import collections.abc diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index f362f1b13..9f2329ad2 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -4,6 +4,7 @@ import textwrap import unittest from subprocess import Popen, PIPE from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok @@ -90,7 +91,7 @@ class TestTool(unittest.TestCase): self.assertEqual(err, b'') def _create_infile(self, data=None): - infile = support.TESTFN + infile = os_helper.TESTFN with open(infile, "w", encoding="utf-8") as fp: self.addCleanup(os.remove, infile) fp.write(data or self.data) @@ -120,7 +121,7 @@ class TestTool(unittest.TestCase): def test_infile_outfile(self): infile = self._create_infile() - outfile = support.TESTFN + '.out' + outfile = os_helper.TESTFN + '.out' rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile) self.addCleanup(os.remove, outfile) with open(outfile, "r") as fp: diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index 8b344ff1d..9232e8a8e 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -6,6 +6,7 @@ import os.path import tempfile import tokenize from test import support +from test.support import os_helper FILENAME = linecache.__file__ @@ -44,7 +45,7 @@ class TempFile: with tempfile.NamedTemporaryFile(delete=False) as fp: self.file_name = fp.name fp.write(self.file_byte_string) - self.addCleanup(support.unlink, self.file_name) + self.addCleanup(os_helper.unlink, self.file_name) class GetLineTestsGoodData(TempFile): @@ -124,10 +125,10 @@ class LineCacheTests(unittest.TestCase): self.assertEqual(empty, []) def test_no_ending_newline(self): - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, "w") as fp: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, "w") as fp: fp.write(SOURCE_3) - lines = linecache.getlines(support.TESTFN) + lines = linecache.getlines(os_helper.TESTFN) self.assertEqual(lines, ["\n", "def f():\n", " return 3\n"]) def test_clearcache(self): @@ -150,8 +151,8 @@ class LineCacheTests(unittest.TestCase): def test_checkcache(self): getline = linecache.getline # Create a source file and cache its contents - source_name = support.TESTFN + '.py' - self.addCleanup(support.unlink, source_name) + source_name = os_helper.TESTFN + '.py' + self.addCleanup(os_helper.unlink, source_name) with open(source_name, 'w') as source: source.write(SOURCE_1) getline(source_name, 1) diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index b625915ba..23092ffd0 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -62,7 +62,7 @@ class MimeTypesTestCase(unittest.TestCase): # Unreadable file returns None self.assertIsNone(mimetypes.read_mime_types("non-existent")) - with support.temp_dir() as directory: + with os_helper.temp_dir() as directory: data = "x-application/x-unittest pyunit\n" file = pathlib.Path(directory, "sample.mimetype") file.write_text(data) @@ -72,7 +72,7 @@ class MimeTypesTestCase(unittest.TestCase): # bpo-41048: read_mime_types should read the rule file with 'utf-8' encoding. # Not with locale encoding. _bootlocale has been imported because io.open(...) # uses it. - with support.temp_dir() as directory: + with os_helper.temp_dir() as directory: data = "application/no-mans-land Fran\u00E7ais" file = pathlib.Path(directory, "sample.mimetype") file.write_text(data, encoding='utf-8') diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index eadd69af9..27091c589 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -1,5 +1,6 @@ import netrc, os, unittest, sys, tempfile, textwrap from test import support +from test.support import os_helper class NetrcTestCase(unittest.TestCase): @@ -108,16 +109,16 @@ class NetrcTestCase(unittest.TestCase): def test_security(self): # This test is incomplete since we are normally not run as root and # therefore can't test the file ownership being wrong. - d = support.TESTFN + d = os_helper.TESTFN os.mkdir(d) - self.addCleanup(support.rmtree, d) + self.addCleanup(os_helper.rmtree, d) fn = os.path.join(d, '.netrc') with open(fn, 'wt') as f: f.write("""\ machine foo.domain.com login bar password pass default login foo password pass """) - with support.EnvironmentVarGuard() as environ: + with os_helper.EnvironmentVarGuard() as environ: environ.set('HOME', d) os.chmod(fn, 0o600) nrc = netrc.netrc() @@ -127,10 +128,10 @@ class NetrcTestCase(unittest.TestCase): self.assertRaises(netrc.NetrcParseError, netrc.netrc) def test_file_not_found_in_home(self): - d = support.TESTFN + d = os_helper.TESTFN os.mkdir(d) - self.addCleanup(support.rmtree, d) - with support.EnvironmentVarGuard() as environ: + self.addCleanup(os_helper.rmtree, d) + with os_helper.EnvironmentVarGuard() as environ: environ.set('HOME', d) self.assertRaises(FileNotFoundError, netrc.netrc) @@ -140,9 +141,9 @@ class NetrcTestCase(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, permission error from remnant directory") def test_home_not_set(self): - fake_home = support.TESTFN + fake_home = os_helper.TESTFN os.mkdir(fake_home) - self.addCleanup(support.rmtree, fake_home) + self.addCleanup(os_helper.rmtree, fake_home) fake_netrc_path = os.path.join(fake_home, '.netrc') with open(fake_netrc_path, 'w') as f: f.write('machine foo.domain.com login bar password pass') @@ -153,7 +154,7 @@ class NetrcTestCase(unittest.TestCase): def fake_expanduser(s): called.append(s) - with support.EnvironmentVarGuard() as environ: + with os_helper.EnvironmentVarGuard() as environ: environ.set('HOME', fake_home) environ.set('USERPROFILE', fake_home) result = orig_expanduser(s) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index ae3856f3e..d26354fbb 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -3,7 +3,9 @@ import os import sys import unittest import warnings -from test.support import TestFailed, FakePath +from test.support import TestFailed +from test.support import os_helper +from test.support.os_helper import FakePath from test import support, test_genericpath from tempfile import TemporaryFile @@ -254,40 +256,40 @@ class TestNtpath(NtpathTestCase): tester("ntpath.realpath('\\'.join(['..'] * 50))", ntpath.splitdrive(expected)[0] + '\\') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_basic(self): - ABSTFN = ntpath.abspath(support.TESTFN) + ABSTFN = ntpath.abspath(os_helper.TESTFN) open(ABSTFN, "wb").close() - self.addCleanup(support.unlink, ABSTFN) - self.addCleanup(support.unlink, ABSTFN + "1") + self.addCleanup(os_helper.unlink, ABSTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "1") os.symlink(ABSTFN, ABSTFN + "1") self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")), os.fsencode(ABSTFN)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_relative(self): - ABSTFN = ntpath.abspath(support.TESTFN) + ABSTFN = ntpath.abspath(os_helper.TESTFN) open(ABSTFN, "wb").close() - self.addCleanup(support.unlink, ABSTFN) - self.addCleanup(support.unlink, ABSTFN + "1") + self.addCleanup(os_helper.unlink, ABSTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "1") os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1")) self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) # TODO: RUSTPYTHON @unittest.expectedFailure - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_broken_symlinks(self): - ABSTFN = ntpath.abspath(support.TESTFN) + ABSTFN = ntpath.abspath(os_helper.TESTFN) os.mkdir(ABSTFN) - self.addCleanup(support.rmtree, ABSTFN) + self.addCleanup(os_helper.rmtree, ABSTFN) - with support.change_cwd(ABSTFN): + with os_helper.change_cwd(ABSTFN): os.mkdir("subdir") os.chdir("subdir") os.symlink(".", "recursive") @@ -338,18 +340,18 @@ class TestNtpath(NtpathTestCase): os.fsencode(ABSTFN + r"\missing")) @unittest.skip("TODO: RUSTPYTHON, leaves behind TESTFN") - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_loops(self): # Symlink loops are non-deterministic as to which path is returned, but # it will always be the fully resolved path of one member of the cycle - ABSTFN = ntpath.abspath(support.TESTFN) - self.addCleanup(support.unlink, ABSTFN) - self.addCleanup(support.unlink, ABSTFN + "1") - self.addCleanup(support.unlink, ABSTFN + "2") - self.addCleanup(support.unlink, ABSTFN + "y") - self.addCleanup(support.unlink, ABSTFN + "c") - self.addCleanup(support.unlink, ABSTFN + "a") + ABSTFN = ntpath.abspath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, ABSTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "1") + self.addCleanup(os_helper.unlink, ABSTFN + "2") + self.addCleanup(os_helper.unlink, ABSTFN + "y") + self.addCleanup(os_helper.unlink, ABSTFN + "c") + self.addCleanup(os_helper.unlink, ABSTFN + "a") os.symlink(ABSTFN, ABSTFN) self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN) @@ -384,14 +386,14 @@ class TestNtpath(NtpathTestCase): # Test using relative path as well. self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_prefix(self): - ABSTFN = ntpath.abspath(support.TESTFN) - self.addCleanup(support.unlink, ABSTFN + "3") - self.addCleanup(support.unlink, "\\\\?\\" + ABSTFN + "3.") - self.addCleanup(support.unlink, ABSTFN + "3link") - self.addCleanup(support.unlink, ABSTFN + "3.link") + ABSTFN = ntpath.abspath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "3") + self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.") + self.addCleanup(os_helper.unlink, ABSTFN + "3link") + self.addCleanup(os_helper.unlink, ABSTFN + "3.link") with open(ABSTFN + "3", "wb") as f: f.write(b'0') @@ -425,12 +427,12 @@ class TestNtpath(NtpathTestCase): @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname') def test_realpath_cwd(self): - ABSTFN = ntpath.abspath(support.TESTFN) + ABSTFN = ntpath.abspath(os_helper.TESTFN) - support.unlink(ABSTFN) - support.rmtree(ABSTFN) + os_helper.unlink(ABSTFN) + os_helper.rmtree(ABSTFN) os.mkdir(ABSTFN) - self.addCleanup(support.rmtree, ABSTFN) + self.addCleanup(os_helper.rmtree, ABSTFN) test_dir_long = ntpath.join(ABSTFN, "MyVeryLongDirectoryName") os.mkdir(test_dir_long) @@ -444,15 +446,15 @@ class TestNtpath(NtpathTestCase): self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short)) - with support.change_cwd(test_dir_long): + with os_helper.change_cwd(test_dir_long): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) - with support.change_cwd(test_dir_long.lower()): + with os_helper.change_cwd(test_dir_long.lower()): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) - with support.change_cwd(test_dir_short): + with os_helper.change_cwd(test_dir_short): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) def test_expandvars(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.clear() env["foo"] = "bar" env["{foo"] = "baz1" @@ -477,13 +479,13 @@ class TestNtpath(NtpathTestCase): tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar") tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%") - @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') + @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') def test_expandvars_nonascii(self): def check(value, expected): tester('ntpath.expandvars(%r)' % value, expected) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.clear() - nonascii = support.FS_NONASCII + nonascii = os_helper.FS_NONASCII env['spam'] = nonascii env[nonascii] = 'ham' + nonascii check('$spam bar', '%s bar' % nonascii) @@ -500,7 +502,7 @@ class TestNtpath(NtpathTestCase): def test_expanduser(self): tester('ntpath.expanduser("test")', 'test') - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.clear() tester('ntpath.expanduser("~test")', '~test') @@ -536,7 +538,7 @@ class TestNtpath(NtpathTestCase): @unittest.skipUnless(nt, "abspath requires 'nt' module") def test_abspath(self): tester('ntpath.abspath("C:\\")', "C:\\") - with support.temp_cwd(support.TESTFN) as cwd_dir: # bpo-31047 + with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: # bpo-31047 tester('ntpath.abspath("")', cwd_dir) tester('ntpath.abspath(" ")', cwd_dir + "\\ ") tester('ntpath.abspath("?")', cwd_dir + "\\?") @@ -548,7 +550,7 @@ class TestNtpath(NtpathTestCase): tester('ntpath.relpath(ntpath.abspath("a"))', 'a') tester('ntpath.relpath("a/b")', 'a\\b') tester('ntpath.relpath("../a/b")', '..\\a\\b') - with support.temp_cwd(support.TESTFN) as cwd_dir: + with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: currentdir = ntpath.basename(cwd_dir) tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a') tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b') @@ -668,7 +670,7 @@ class TestNtpath(NtpathTestCase): self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\")) self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\")) - with support.temp_dir() as d: + with os_helper.temp_dir() as d: self.assertFalse(ntpath.ismount(d)) if sys.platform == "win32": @@ -678,7 +680,7 @@ class TestNtpath(NtpathTestCase): # locations below cannot then refer to mount points # drive, path = ntpath.splitdrive(sys.executable) - with support.change_cwd(ntpath.dirname(sys.executable)): + with os_helper.change_cwd(ntpath.dirname(sys.executable)): self.assertFalse(ntpath.ismount(drive.lower())) self.assertFalse(ntpath.ismount(drive.upper())) @@ -765,9 +767,9 @@ class PathLikeTests(NtpathTestCase): path = ntpath def setUp(self): - self.file_name = support.TESTFN.lower() - self.file_path = FakePath(support.TESTFN) - self.addCleanup(support.unlink, self.file_name) + self.file_name = os_helper.TESTFN.lower() + self.file_path = FakePath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, self.file_name) with open(self.file_name, 'xb', 0) as file: file.write(b"test_ntpath.PathLikeTests") diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 437fdd2be..1ed6bf9f9 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -14,6 +14,7 @@ import unittest from io import StringIO from test import support +from test.support import os_helper import optparse @@ -1021,10 +1022,10 @@ class TestExtendAddTypes(BaseTest): self.parser.add_option("-f", "--file", type="file", dest="file") def tearDown(self): - if os.path.isdir(support.TESTFN): - os.rmdir(support.TESTFN) - elif os.path.isfile(support.TESTFN): - os.unlink(support.TESTFN) + if os.path.isdir(os_helper.TESTFN): + os.rmdir(os_helper.TESTFN) + elif os.path.isfile(os_helper.TESTFN): + os.unlink(os_helper.TESTFN) class MyOption (Option): def check_file(option, opt, value): @@ -1039,21 +1040,21 @@ class TestExtendAddTypes(BaseTest): TYPE_CHECKER["file"] = check_file def test_filetype_ok(self): - support.create_empty_file(support.TESTFN) - self.assertParseOK(["--file", support.TESTFN, "-afoo"], - {'file': support.TESTFN, 'a': 'foo'}, + os_helper.create_empty_file(os_helper.TESTFN) + self.assertParseOK(["--file", os_helper.TESTFN, "-afoo"], + {'file': os_helper.TESTFN, 'a': 'foo'}, []) def test_filetype_noexist(self): - self.assertParseFail(["--file", support.TESTFN, "-afoo"], + self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"], "%s: file does not exist" % - support.TESTFN) + os_helper.TESTFN) def test_filetype_notfile(self): - os.mkdir(support.TESTFN) - self.assertParseFail(["--file", support.TESTFN, "-afoo"], + os.mkdir(os_helper.TESTFN) + self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"], "%s: not a regular file" % - support.TESTFN) + os_helper.TESTFN) class TestExtendAddActions(BaseTest): @@ -1497,7 +1498,7 @@ class TestHelp(BaseTest): # we must restore its original value -- otherwise, this test # screws things up for other tests when it's part of the Python # test suite. - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['COLUMNS'] = str(columns) return InterceptingOptionParser(option_list=options) @@ -1522,7 +1523,7 @@ class TestHelp(BaseTest): self.assertHelpEquals(_expected_help_long_opts_first) def test_help_title_formatter(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env["COLUMNS"] = "80" self.parser.formatter = TitledHelpFormatter() self.assertHelpEquals(_expected_help_title_formatter) @@ -1651,8 +1652,8 @@ class TestParseNumber(BaseTest): class MiscTestCase(unittest.TestCase): def test__all__(self): - blacklist = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} - support.check__all__(self, optparse, blacklist=blacklist) + not_exported = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} + support.check__all__(self, optparse, not_exported=not_exported) def test_main(): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index c66045f4b..2018ef179 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3551,13 +3551,13 @@ def supports_extended_attributes(): return False try: - with open(support.TESTFN, "xb", 0) as fp: + with open(os_helper.TESTFN, "xb", 0) as fp: try: os.setxattr(fp.fileno(), b"user.test", b"") except OSError: return False finally: - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) return True diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 14192e137..ea723c8be 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -12,7 +12,8 @@ import unittest from unittest import mock from test import support -from test.support import TESTFN, FakePath +from test.support import os_helper +from test.support.os_helper import TESTFN, FakePath try: import grp, pwd @@ -1227,7 +1228,7 @@ class _BasePathTest(object): def setUp(self): def cleanup(): os.chmod(join('dirE'), 0o777) - support.rmtree(BASE) + os_helper.rmtree(BASE) self.addCleanup(cleanup) os.mkdir(BASE) os.mkdir(join('dirA')) @@ -1244,7 +1245,7 @@ class _BasePathTest(object): with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: f.write(b"this is file D\n") os.chmod(join('dirE'), 0) - if support.can_symlink(): + if os_helper.can_symlink(): # Relative symlinks. os.symlink('fileA', join('linkA')) os.symlink('non-existing', join('brokenLink')) @@ -1338,7 +1339,7 @@ class _BasePathTest(object): self.assertIs(True, (p / 'dirA').exists()) self.assertIs(True, (p / 'fileA').exists()) self.assertIs(False, (p / 'fileA' / 'bah').exists()) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertIs(True, (p / 'linkA').exists()) self.assertIs(True, (p / 'linkB').exists()) self.assertIs(True, (p / 'linkB' / 'fileB').exists()) @@ -1383,11 +1384,11 @@ class _BasePathTest(object): it = p.iterdir() paths = set(it) expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] - if support.can_symlink(): + if os_helper.can_symlink(): expected += ['linkA', 'linkB', 'brokenLink'] self.assertEqual(paths, { P(BASE, q) for q in expected }) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_iterdir_symlink(self): # __iter__ on a symlink to a directory. P = self.cls @@ -1416,16 +1417,16 @@ class _BasePathTest(object): _check(it, ["fileA"]) _check(p.glob("fileB"), []) _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) - if not support.can_symlink(): + if not os_helper.can_symlink(): _check(p.glob("*A"), ['dirA', 'fileA']) else: _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) - if not support.can_symlink(): + if not os_helper.can_symlink(): _check(p.glob("*B/*"), ['dirB/fileB']) else: _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 'linkB/fileB', 'linkB/linkD']) - if not support.can_symlink(): + if not os_helper.can_symlink(): _check(p.glob("*/fileB"), ['dirB/fileB']) else: _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) @@ -1440,7 +1441,7 @@ class _BasePathTest(object): _check(it, ["fileA"]) _check(p.rglob("fileB"), ["dirB/fileB"]) _check(p.rglob("*/fileA"), []) - if not support.can_symlink(): + if not os_helper.can_symlink(): _check(p.rglob("*/fileB"), ["dirB/fileB"]) else: _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", @@ -1451,7 +1452,7 @@ class _BasePathTest(object): _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_rglob_symlink_loop(self): # Don't get fooled by symlink loops (Issue #26012). P = self.cls @@ -1484,7 +1485,7 @@ class _BasePathTest(object): # This can be used to check both relative and absolute resolutions. _check_resolve_relative = _check_resolve_absolute = _check_resolve - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_resolve_common(self): P = self.cls p = P(BASE, 'foo') @@ -1525,7 +1526,7 @@ class _BasePathTest(object): self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks. d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) - self.addCleanup(support.rmtree, d) + self.addCleanup(os_helper.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') @@ -1544,7 +1545,7 @@ class _BasePathTest(object): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_resolve_dot(self): # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks p = self.cls(BASE) @@ -1596,7 +1597,7 @@ class _BasePathTest(object): self.addCleanup(p.chmod, st.st_mode) self.assertNotEqual(p.stat(), st) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_lstat(self): p = self.cls(BASE)/ 'linkA' st = p.stat() @@ -1857,7 +1858,7 @@ class _BasePathTest(object): self.assertNotIn(str(p12), concurrently_created) self.assertTrue(p.exists()) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_symlink_to(self): P = self.cls(BASE) target = P / 'fileA' @@ -1887,7 +1888,7 @@ class _BasePathTest(object): self.assertFalse((P / 'fileA').is_dir()) self.assertFalse((P / 'non-existing').is_dir()) self.assertFalse((P / 'fileA' / 'bah').is_dir()) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertFalse((P / 'linkA').is_dir()) self.assertTrue((P / 'linkB').is_dir()) self.assertFalse((P/ 'brokenLink').is_dir(), False) @@ -1900,7 +1901,7 @@ class _BasePathTest(object): self.assertFalse((P / 'dirA').is_file()) self.assertFalse((P / 'non-existing').is_file()) self.assertFalse((P / 'fileA' / 'bah').is_file()) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertTrue((P / 'linkA').is_file()) self.assertFalse((P / 'linkB').is_file()) self.assertFalse((P/ 'brokenLink').is_file()) @@ -1916,7 +1917,7 @@ class _BasePathTest(object): self.assertFalse((P / 'non-existing').is_mount()) self.assertFalse((P / 'fileA' / 'bah').is_mount()) self.assertTrue(R.is_mount()) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertFalse((P / 'linkA').is_mount()) self.assertIs(self.cls('/\udfff').is_mount(), False) self.assertIs(self.cls('/\x00').is_mount(), False) @@ -1927,13 +1928,13 @@ class _BasePathTest(object): self.assertFalse((P / 'dirA').is_symlink()) self.assertFalse((P / 'non-existing').is_symlink()) self.assertFalse((P / 'fileA' / 'bah').is_symlink()) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertTrue((P / 'linkA').is_symlink()) self.assertTrue((P / 'linkB').is_symlink()) self.assertTrue((P/ 'brokenLink').is_symlink()) self.assertIs((P / 'fileA\udfff').is_file(), False) self.assertIs((P / 'fileA\x00').is_file(), False) - if support.can_symlink(): + if os_helper.can_symlink(): self.assertIs((P / 'linkA\udfff').is_file(), False) self.assertIs((P / 'linkA\x00').is_file(), False) @@ -2071,15 +2072,15 @@ class _BasePathTest(object): finally: os.chdir(old_path) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_complex_symlinks_absolute(self): self._check_complex_symlinks(BASE) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_complex_symlinks_relative(self): self._check_complex_symlinks('.') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_complex_symlinks_relative_dot_dot(self): self._check_complex_symlinks(os.path.join('dirA', '..')) @@ -2142,7 +2143,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): st = os.stat(join('masked_new_file')) self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_resolve_loop(self): # Loops with relative symlinks. os.symlink('linkX/inside', join('linkX')) @@ -2167,7 +2168,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): P = self.cls p = P(BASE) given = set(p.glob("FILEa")) - expect = set() if not support.fs_is_case_insensitive(BASE) else given + expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given self.assertEqual(given, expect) self.assertEqual(set(p.glob("FILEa*")), set()) @@ -2175,7 +2176,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): P = self.cls p = P(BASE, "dirC") given = set(p.rglob("FILEd")) - expect = set() if not support.fs_is_case_insensitive(BASE) else given + expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given self.assertEqual(given, expect) self.assertEqual(set(p.rglob("FILEd*")), set()) @@ -2206,7 +2207,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): p6 = P('') p7 = P('~fakeuser/Documents') - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.pop('HOME', None) self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') @@ -2268,7 +2269,7 @@ class WindowsPathTest(_BasePathTest, unittest.TestCase): def test_expanduser(self): P = self.cls - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.pop('HOME', None) env.pop('USERPROFILE', None) env.pop('HOMEPATH', None) diff --git a/Lib/test/test_pkgimport.py b/Lib/test/test_pkgimport.py index 5d9a45111..ee3b28578 100644 --- a/Lib/test/test_pkgimport.py +++ b/Lib/test/test_pkgimport.py @@ -7,7 +7,7 @@ import tempfile import unittest from importlib.util import cache_from_source -from test.support import create_empty_file +from test.support.os_helper import create_empty_file class TestImport(unittest.TestCase): diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 7a5146216..fe3b7dd71 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -8,6 +8,7 @@ import unittest from unittest import mock from test import support +from test.support import os_helper class PlatformTest(unittest.TestCase): def clear_caches(self): @@ -18,7 +19,7 @@ class PlatformTest(unittest.TestCase): def test_architecture(self): res = platform.architecture() - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_architecture_via_symlink(self): # issue3762 # On Windows, the EXE needs to know where pythonXY.dll and *.pyd is at # so we add the directory to the path, PYTHONHOME and PYTHONPATH. @@ -45,7 +46,7 @@ class PlatformTest(unittest.TestCase): return r real = os.path.realpath(sys.executable) - link = os.path.abspath(support.TESTFN) + link = os.path.abspath(os_helper.TESTFN) os.symlink(real, link) try: self.assertEqual(get(real), get(link, env=env)) @@ -198,7 +199,7 @@ class PlatformTest(unittest.TestCase): # using it, per # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx try: - with support.EnvironmentVarGuard() as environ: + with os_helper.EnvironmentVarGuard() as environ: if 'PROCESSOR_ARCHITEW6432' in environ: del environ['PROCESSOR_ARCHITEW6432'] environ['PROCESSOR_ARCHITECTURE'] = 'foo' @@ -285,8 +286,8 @@ class PlatformTest(unittest.TestCase): executable = sys.executable platform.libc_ver(executable) - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) with mock.patch('os.confstr', create=True, return_value='mock 1.0'): # test os.confstr() code path diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index e13fe4304..b8fb4ceb7 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1,6 +1,7 @@ "Test posix functions" from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok # Skip these tests if there is no posix module. @@ -20,7 +21,7 @@ import warnings import textwrap _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), - support.TESTFN + '-dummy-symlink') + os_helper.TESTFN + '-dummy-symlink') requires_32b = unittest.skipUnless(sys.maxsize < 2**32, 'test is only meaningful on 32-bit builds') @@ -42,9 +43,9 @@ class PosixTester(unittest.TestCase): def setUp(self): # create empty file - fp = open(support.TESTFN, 'w+') + fp = open(os_helper.TESTFN, 'w+') fp.close() - self.teardown_files = [ support.TESTFN ] + self.teardown_files = [ os_helper.TESTFN ] self._warnings_manager = support.check_warnings() self._warnings_manager.__enter__() warnings.filterwarnings('ignore', '.* potential security risk .*', @@ -52,7 +53,7 @@ class PosixTester(unittest.TestCase): def tearDown(self): for teardown_file in self.teardown_files: - support.unlink(teardown_file) + os_helper.unlink(teardown_file) self._warnings_manager.__exit__(None, None, None) def testNoArgFunctions(self): @@ -153,7 +154,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'fstatvfs'), 'test needs posix.fstatvfs()') def test_fstatvfs(self): - fp = open(support.TESTFN) + fp = open(os_helper.TESTFN) try: self.assertTrue(posix.fstatvfs(fp.fileno())) self.assertTrue(posix.statvfs(fp.fileno())) @@ -163,7 +164,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'ftruncate'), 'test needs posix.ftruncate()') def test_ftruncate(self): - fp = open(support.TESTFN, 'w+') + fp = open(os_helper.TESTFN, 'w+') try: # we need to have some data to truncate fp.write('test') @@ -174,10 +175,10 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'truncate'), "test needs posix.truncate()") def test_truncate(self): - with open(support.TESTFN, 'w') as fp: + with open(os_helper.TESTFN, 'w') as fp: fp.write('test') fp.flush() - posix.truncate(support.TESTFN, 0) + posix.truncate(os_helper.TESTFN, 0) @unittest.skipUnless(getattr(os, 'execve', None) in os.supports_fd, "test needs execve() to support the fd parameter") @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") @@ -268,7 +269,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'lockf'), "test needs posix.lockf()") def test_lockf(self): - fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -280,7 +281,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pread'), "test needs posix.pread()") def test_pread(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -292,7 +293,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") def test_preadv(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3t5t6t6t8') buf = [bytearray(i) for i in [5, 3, 2]] @@ -304,7 +305,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") @unittest.skipUnless(hasattr(posix, 'RWF_HIPRI'), "test needs posix.RWF_HIPRI") def test_preadv_flags(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3t5t6t6t8') buf = [bytearray(i) for i in [5, 3, 2]] @@ -326,7 +327,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") @requires_32b def test_preadv_overflow_32bits(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: buf = [bytearray(2**16)] * 2**15 with self.assertRaises(OSError) as cm: @@ -338,7 +339,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()") def test_pwrite(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -349,7 +350,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") def test_pwritev(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b"xx") os.lseek(fd, 0, os.SEEK_SET) @@ -364,7 +365,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") @unittest.skipUnless(hasattr(posix, 'os.RWF_SYNC'), "test needs os.RWF_SYNC") def test_pwritev_flags(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd,b"xx") os.lseek(fd, 0, os.SEEK_SET) @@ -379,7 +380,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") @requires_32b def test_pwritev_overflow_32bits(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: with self.assertRaises(OSError) as cm: os.pwritev(fd, [b"x" * 2**16] * 2**15, 0) @@ -390,7 +391,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'posix_fallocate'), "test needs posix.posix_fallocate()") def test_posix_fallocate(self): - fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_CREAT) try: posix.posix_fallocate(fd, 0, 10) except OSError as inst: @@ -419,7 +420,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'posix_fadvise'), "test needs posix.posix_fadvise()") def test_posix_fadvise(self): - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) try: posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) finally: @@ -437,7 +438,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.utime in os.supports_fd, "test needs fd support in os.utime") def test_utime_with_fd(self): now = time.time() - fd = os.open(support.TESTFN, os.O_RDONLY) + fd = os.open(os_helper.TESTFN, os.O_RDONLY) try: posix.utime(fd) posix.utime(fd, None) @@ -458,17 +459,17 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.utime in os.supports_follow_symlinks, "test needs follow_symlinks support in os.utime") def test_utime_nofollow_symlinks(self): now = time.time() - posix.utime(support.TESTFN, None, follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), follow_symlinks=False) - posix.utime(support.TESTFN, (int(now), int(now)), follow_symlinks=False) - posix.utime(support.TESTFN, (now, now), follow_symlinks=False) - posix.utime(support.TESTFN, follow_symlinks=False) + posix.utime(os_helper.TESTFN, None, follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now), follow_symlinks=False) + posix.utime(os_helper.TESTFN, (int(now), int(now)), follow_symlinks=False) + posix.utime(os_helper.TESTFN, (now, now), follow_symlinks=False) + posix.utime(os_helper.TESTFN, follow_symlinks=False) @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") def test_writev(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: n = os.writev(fd, (b'test1', b'tt2', b't3')) self.assertEqual(n, 10) @@ -491,7 +492,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") @requires_32b def test_writev_overflow_32bits(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: with self.assertRaises(OSError) as cm: os.writev(fd, [b"x" * 2**16] * 2**15) @@ -501,7 +502,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") def test_readv(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3') os.lseek(fd, 0, os.SEEK_SET) @@ -524,7 +525,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") @requires_32b def test_readv_overflow_32bits(self): - fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) try: buf = [bytearray(2**16)] * 2**15 with self.assertRaises(OSError) as cm: @@ -537,7 +538,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'dup'), 'test needs posix.dup()') def test_dup(self): - fp = open(support.TESTFN) + fp = open(os_helper.TESTFN) try: fd = posix.dup(fp.fileno()) self.assertIsInstance(fd, int) @@ -554,8 +555,8 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'dup2'), 'test needs posix.dup2()') def test_dup2(self): - fp1 = open(support.TESTFN) - fp2 = open(support.TESTFN) + fp1 = open(os_helper.TESTFN) + fp2 = open(os_helper.TESTFN) try: posix.dup2(fp1.fileno(), fp2.fileno()) finally: @@ -565,40 +566,40 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'O_CLOEXEC'), "needs os.O_CLOEXEC") @support.requires_linux_version(2, 6, 23) def test_oscloexec(self): - fd = os.open(support.TESTFN, os.O_RDONLY|os.O_CLOEXEC) + fd = os.open(os_helper.TESTFN, os.O_RDONLY|os.O_CLOEXEC) self.addCleanup(os.close, fd) self.assertFalse(os.get_inheritable(fd)) @unittest.skipUnless(hasattr(posix, 'O_EXLOCK'), 'test needs posix.O_EXLOCK') def test_osexlock(self): - fd = os.open(support.TESTFN, + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, support.TESTFN, + self.assertRaises(OSError, os.open, os_helper.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) if hasattr(posix, "O_SHLOCK"): - fd = os.open(support.TESTFN, + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, support.TESTFN, + self.assertRaises(OSError, os.open, os_helper.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) @unittest.skipUnless(hasattr(posix, 'O_SHLOCK'), 'test needs posix.O_SHLOCK') def test_osshlock(self): - fd1 = os.open(support.TESTFN, + fd1 = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - fd2 = os.open(support.TESTFN, + fd2 = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) os.close(fd2) os.close(fd1) if hasattr(posix, "O_EXLOCK"): - fd = os.open(support.TESTFN, + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, support.TESTFN, + self.assertRaises(OSError, os.open, os_helper.TESTFN, os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) @@ -607,7 +608,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'fstat'), 'test needs posix.fstat()') def test_fstat(self): - fp = open(support.TESTFN) + fp = open(os_helper.TESTFN) try: self.assertTrue(posix.fstat(fp.fileno())) self.assertTrue(posix.stat(fp.fileno())) @@ -621,58 +622,58 @@ class PosixTester(unittest.TestCase): # TODO: RUSTPYTHON: AssertionError: DeprecationWarning not triggered by stat @unittest.expectedFailure def test_stat(self): - self.assertTrue(posix.stat(support.TESTFN)) - self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) + self.assertTrue(posix.stat(os_helper.TESTFN)) + self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN))) self.assertWarnsRegex(DeprecationWarning, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, bytearray(os.fsencode(support.TESTFN))) + posix.stat, bytearray(os.fsencode(os_helper.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', posix.stat, None) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(support.TESTFN)) + posix.stat, list(os_helper.TESTFN)) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os.fsencode(support.TESTFN))) + posix.stat, list(os.fsencode(os_helper.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") def test_mkfifo(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) try: - posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR) + posix.mkfifo(os_helper.TESTFN, stat.S_IRUSR | stat.S_IWUSR) except PermissionError as e: self.skipTest('posix.mkfifo(): %s' % e) - self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) @unittest.skipUnless(hasattr(posix, 'mknod') and hasattr(stat, 'S_IFIFO'), "don't have mknod()/S_IFIFO") def test_mknod(self): # Test using mknod() to create a FIFO (the only use specified # by POSIX). - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR try: - posix.mknod(support.TESTFN, mode, 0) + posix.mknod(os_helper.TESTFN, mode, 0) except OSError as e: # Some old systems don't allow unprivileged users to use # mknod(), or only support creating device nodes. self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) else: - self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) # Keyword arguments are also supported - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) try: - posix.mknod(path=support.TESTFN, mode=mode, device=0, + posix.mknod(path=os_helper.TESTFN, mode=mode, device=0, dir_fd=None) except OSError as e: self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) @unittest.skipUnless(hasattr(posix, 'makedev'), 'test needs posix.makedev()') def test_makedev(self): - st = posix.stat(support.TESTFN) + st = posix.stat(os_helper.TESTFN) dev = st.st_dev self.assertIsInstance(dev, int) self.assertGreaterEqual(dev, 0) @@ -761,19 +762,19 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()") def test_chown(self): # raise an OSError if the file does not exist - os.unlink(support.TESTFN) - self.assertRaises(OSError, posix.chown, support.TESTFN, -1, -1) + os.unlink(os_helper.TESTFN) + self.assertRaises(OSError, posix.chown, os_helper.TESTFN, -1, -1) # re-create the file - support.create_empty_file(support.TESTFN) - self._test_all_chown_common(posix.chown, support.TESTFN, posix.stat) + os_helper.create_empty_file(os_helper.TESTFN) + self._test_all_chown_common(posix.chown, os_helper.TESTFN, posix.stat) @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()") def test_fchown(self): - os.unlink(support.TESTFN) + os.unlink(os_helper.TESTFN) # re-create the file - test_file = open(support.TESTFN, 'w') + test_file = open(os_helper.TESTFN, 'w') try: fd = test_file.fileno() self._test_all_chown_common(posix.fchown, fd, @@ -783,29 +784,29 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'lchown'), "test needs os.lchown()") def test_lchown(self): - os.unlink(support.TESTFN) + os.unlink(os_helper.TESTFN) # create a symlink - os.symlink(_DUMMY_SYMLINK, support.TESTFN) - self._test_all_chown_common(posix.lchown, support.TESTFN, + os.symlink(_DUMMY_SYMLINK, os_helper.TESTFN) + self._test_all_chown_common(posix.lchown, os_helper.TESTFN, getattr(posix, 'lstat', None)) @unittest.skipUnless(hasattr(posix, 'chdir'), 'test needs posix.chdir()') def test_chdir(self): posix.chdir(os.curdir) - self.assertRaises(OSError, posix.chdir, support.TESTFN) + self.assertRaises(OSError, posix.chdir, os_helper.TESTFN) def test_listdir(self): - self.assertIn(support.TESTFN, posix.listdir(os.curdir)) + self.assertIn(os_helper.TESTFN, posix.listdir(os.curdir)) def test_listdir_default(self): # When listdir is called without argument, # it's the same as listdir(os.curdir). - self.assertIn(support.TESTFN, posix.listdir()) + self.assertIn(os_helper.TESTFN, posix.listdir()) def test_listdir_bytes(self): # When listdir is called with a bytes object, # the returned strings are of type bytes. - self.assertIn(os.fsencode(support.TESTFN), posix.listdir(b'.')) + self.assertIn(os.fsencode(os_helper.TESTFN), posix.listdir(b'.')) # TODO: RUSTPYTHON: AssertionError: DeprecationWarning not triggered @unittest.expectedFailure @@ -813,7 +814,7 @@ class PosixTester(unittest.TestCase): for cls in bytearray, memoryview: with self.assertWarns(DeprecationWarning): names = posix.listdir(cls(b'.')) - self.assertIn(os.fsencode(support.TESTFN), names) + self.assertIn(os.fsencode(os_helper.TESTFN), names) for name in names: self.assertIs(type(name), bytes) @@ -834,7 +835,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'access'), 'test needs posix.access()') def test_access(self): - self.assertTrue(posix.access(support.TESTFN, os.R_OK)) + self.assertTrue(posix.access(os_helper.TESTFN, os.R_OK)) @unittest.skipUnless(hasattr(posix, 'umask'), 'test needs posix.umask()') def test_umask(self): @@ -893,12 +894,12 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'utime'), 'test needs posix.utime()') def test_utime(self): now = time.time() - posix.utime(support.TESTFN, None) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None)) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None)) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now)) - posix.utime(support.TESTFN, (int(now), int(now))) - posix.utime(support.TESTFN, (now, now)) + posix.utime(os_helper.TESTFN, None) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None)) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None)) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now)) + posix.utime(os_helper.TESTFN, (int(now), int(now))) + posix.utime(os_helper.TESTFN, (now, now)) def _test_chflags_regular_file(self, chflags_func, target_file, **kwargs): st = os.stat(target_file) @@ -926,20 +927,20 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'chflags'), 'test needs os.chflags()') def test_chflags(self): - self._test_chflags_regular_file(posix.chflags, support.TESTFN) + self._test_chflags_regular_file(posix.chflags, os_helper.TESTFN) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_regular_file(self): - self._test_chflags_regular_file(posix.lchflags, support.TESTFN) - self._test_chflags_regular_file(posix.chflags, support.TESTFN, follow_symlinks=False) + self._test_chflags_regular_file(posix.lchflags, os_helper.TESTFN) + self._test_chflags_regular_file(posix.chflags, os_helper.TESTFN, follow_symlinks=False) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_symlink(self): - testfn_st = os.stat(support.TESTFN) + testfn_st = os.stat(os_helper.TESTFN) self.assertTrue(hasattr(testfn_st, 'st_flags')) - os.symlink(support.TESTFN, _DUMMY_SYMLINK) + os.symlink(os_helper.TESTFN, _DUMMY_SYMLINK) self.teardown_files.append(_DUMMY_SYMLINK) dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) @@ -957,7 +958,7 @@ class PosixTester(unittest.TestCase): msg = 'chflag UF_IMMUTABLE not supported by underlying fs' self.skipTest(msg) try: - new_testfn_st = os.stat(support.TESTFN) + new_testfn_st = os.stat(os_helper.TESTFN) new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) self.assertEqual(testfn_st.st_flags, new_testfn_st.st_flags) @@ -993,7 +994,7 @@ class PosixTester(unittest.TestCase): def test_getcwd_long_pathnames(self): dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef' curdir = os.getcwd() - base_path = os.path.abspath(support.TESTFN) + '.getcwd' + base_path = os.path.abspath(os_helper.TESTFN) + '.getcwd' try: os.mkdir(base_path) @@ -1023,7 +1024,7 @@ class PosixTester(unittest.TestCase): finally: os.chdir(curdir) - support.rmtree(base_path) + os_helper.rmtree(base_path) @unittest.skipUnless(hasattr(posix, 'getgrouplist'), "test needs posix.getgrouplist()") @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") @@ -1067,53 +1068,53 @@ class PosixTester(unittest.TestCase): def test_access_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - self.assertTrue(posix.access(support.TESTFN, os.R_OK, dir_fd=f)) + self.assertTrue(posix.access(os_helper.TESTFN, os.R_OK, dir_fd=f)) finally: posix.close(f) @unittest.skipUnless(os.chmod in os.supports_dir_fd, "test needs dir_fd support in os.chmod()") def test_chmod_dir_fd(self): - os.chmod(support.TESTFN, stat.S_IRUSR) + os.chmod(os_helper.TESTFN, stat.S_IRUSR) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.chmod(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) + posix.chmod(os_helper.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) - s = posix.stat(support.TESTFN) + s = posix.stat(os_helper.TESTFN) self.assertEqual(s[0] & stat.S_IRWXU, stat.S_IRUSR | stat.S_IWUSR) finally: posix.close(f) @unittest.skipUnless(os.chown in os.supports_dir_fd, "test needs dir_fd support in os.chown()") def test_chown_dir_fd(self): - support.unlink(support.TESTFN) - support.create_empty_file(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) + os_helper.create_empty_file(os_helper.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.chown(support.TESTFN, os.getuid(), os.getgid(), dir_fd=f) + posix.chown(os_helper.TESTFN, os.getuid(), os.getgid(), dir_fd=f) finally: posix.close(f) @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") def test_stat_dir_fd(self): - support.unlink(support.TESTFN) - with open(support.TESTFN, 'w') as outfile: + os_helper.unlink(os_helper.TESTFN) + with open(os_helper.TESTFN, 'w') as outfile: outfile.write("testline\n") f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - s1 = posix.stat(support.TESTFN) - s2 = posix.stat(support.TESTFN, dir_fd=f) + s1 = posix.stat(os_helper.TESTFN) + s2 = posix.stat(os_helper.TESTFN, dir_fd=f) self.assertEqual(s1, s2) - s2 = posix.stat(support.TESTFN, dir_fd=None) + s2 = posix.stat(os_helper.TESTFN, dir_fd=None) self.assertEqual(s1, s2) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, support.TESTFN, dir_fd=posix.getcwd()) + posix.stat, os_helper.TESTFN, dir_fd=posix.getcwd()) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, support.TESTFN, dir_fd=float(f)) + posix.stat, os_helper.TESTFN, dir_fd=float(f)) self.assertRaises(OverflowError, - posix.stat, support.TESTFN, dir_fd=10**20) + posix.stat, os_helper.TESTFN, dir_fd=10**20) finally: posix.close(f) @@ -1122,24 +1123,24 @@ class PosixTester(unittest.TestCase): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: now = time.time() - posix.utime(support.TESTFN, None, dir_fd=f) - posix.utime(support.TESTFN, dir_fd=f) - self.assertRaises(TypeError, posix.utime, support.TESTFN, now, dir_fd=f) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), dir_fd=f) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), dir_fd=f) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), dir_fd=f) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, "x"), dir_fd=f) - posix.utime(support.TESTFN, (int(now), int(now)), dir_fd=f) - posix.utime(support.TESTFN, (now, now), dir_fd=f) - posix.utime(support.TESTFN, + posix.utime(os_helper.TESTFN, None, dir_fd=f) + posix.utime(os_helper.TESTFN, dir_fd=f) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, now, dir_fd=f) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now), dir_fd=f) + self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, "x"), dir_fd=f) + posix.utime(os_helper.TESTFN, (int(now), int(now)), dir_fd=f) + posix.utime(os_helper.TESTFN, (now, now), dir_fd=f) + posix.utime(os_helper.TESTFN, (int(now), int((now - int(now)) * 1e9)), dir_fd=f) - posix.utime(support.TESTFN, dir_fd=f, + posix.utime(os_helper.TESTFN, dir_fd=f, times=(int(now), int((now - int(now)) * 1e9))) # try dir_fd and follow_symlinks together if os.utime in os.supports_follow_symlinks: try: - posix.utime(support.TESTFN, follow_symlinks=False, dir_fd=f) + posix.utime(os_helper.TESTFN, follow_symlinks=False, dir_fd=f) except ValueError: # whoops! using both together not supported on this platform. pass @@ -1151,53 +1152,53 @@ class PosixTester(unittest.TestCase): def test_link_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.link(support.TESTFN, support.TESTFN + 'link', src_dir_fd=f, dst_dir_fd=f) + posix.link(os_helper.TESTFN, os_helper.TESTFN + 'link', src_dir_fd=f, dst_dir_fd=f) except PermissionError as e: self.skipTest('posix.link(): %s' % e) else: # should have same inodes - self.assertEqual(posix.stat(support.TESTFN)[1], - posix.stat(support.TESTFN + 'link')[1]) + self.assertEqual(posix.stat(os_helper.TESTFN)[1], + posix.stat(os_helper.TESTFN + 'link')[1]) finally: posix.close(f) - support.unlink(support.TESTFN + 'link') + os_helper.unlink(os_helper.TESTFN + 'link') @unittest.skipUnless(os.mkdir in os.supports_dir_fd, "test needs dir_fd support in os.mkdir()") def test_mkdir_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mkdir(support.TESTFN + 'dir', dir_fd=f) - posix.stat(support.TESTFN + 'dir') # should not raise exception + posix.mkdir(os_helper.TESTFN + 'dir', dir_fd=f) + posix.stat(os_helper.TESTFN + 'dir') # should not raise exception finally: posix.close(f) - support.rmtree(support.TESTFN + 'dir') + os_helper.rmtree(os_helper.TESTFN + 'dir') @unittest.skipUnless((os.mknod in os.supports_dir_fd) and hasattr(stat, 'S_IFIFO'), "test requires both stat.S_IFIFO and dir_fd support for os.mknod()") def test_mknod_dir_fd(self): # Test using mknodat() to create a FIFO (the only use specified # by POSIX). - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mknod(support.TESTFN, mode, 0, dir_fd=f) + posix.mknod(os_helper.TESTFN, mode, 0, dir_fd=f) except OSError as e: # Some old systems don't allow unprivileged users to use # mknod(), or only support creating device nodes. self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) else: - self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) finally: posix.close(f) @unittest.skipUnless(os.open in os.supports_dir_fd, "test needs dir_fd support in os.open()") def test_open_dir_fd(self): - support.unlink(support.TESTFN) - with open(support.TESTFN, 'w') as outfile: + os_helper.unlink(os_helper.TESTFN) + with open(os_helper.TESTFN, 'w') as outfile: outfile.write("testline\n") a = posix.open(posix.getcwd(), posix.O_RDONLY) - b = posix.open(support.TESTFN, posix.O_RDONLY, dir_fd=a) + b = posix.open(os_helper.TESTFN, posix.O_RDONLY, dir_fd=a) try: res = posix.read(b, 9).decode(encoding="utf-8") self.assertEqual("testline\n", res) @@ -1207,27 +1208,27 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.readlink in os.supports_dir_fd, "test needs dir_fd support in os.readlink()") def test_readlink_dir_fd(self): - os.symlink(support.TESTFN, support.TESTFN + 'link') + os.symlink(os_helper.TESTFN, os_helper.TESTFN + 'link') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - self.assertEqual(posix.readlink(support.TESTFN + 'link'), - posix.readlink(support.TESTFN + 'link', dir_fd=f)) + self.assertEqual(posix.readlink(os_helper.TESTFN + 'link'), + posix.readlink(os_helper.TESTFN + 'link', dir_fd=f)) finally: - support.unlink(support.TESTFN + 'link') + os_helper.unlink(os_helper.TESTFN + 'link') posix.close(f) @unittest.skipUnless(os.rename in os.supports_dir_fd, "test needs dir_fd support in os.rename()") def test_rename_dir_fd(self): - support.unlink(support.TESTFN) - support.create_empty_file(support.TESTFN + 'ren') + os_helper.unlink(os_helper.TESTFN) + os_helper.create_empty_file(os_helper.TESTFN + 'ren') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.rename(support.TESTFN + 'ren', support.TESTFN, src_dir_fd=f, dst_dir_fd=f) + posix.rename(os_helper.TESTFN + 'ren', os_helper.TESTFN, src_dir_fd=f, dst_dir_fd=f) except: - posix.rename(support.TESTFN + 'ren', support.TESTFN) + posix.rename(os_helper.TESTFN + 'ren', os_helper.TESTFN) raise else: - posix.stat(support.TESTFN) # should not raise exception + posix.stat(os_helper.TESTFN) # should not raise exception finally: posix.close(f) @@ -1245,24 +1246,24 @@ class PosixTester(unittest.TestCase): def test_symlink_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.symlink(support.TESTFN, support.TESTFN + 'link', dir_fd=f) - self.assertEqual(posix.readlink(support.TESTFN + 'link'), support.TESTFN) + posix.symlink(os_helper.TESTFN, os_helper.TESTFN + 'link', dir_fd=f) + self.assertEqual(posix.readlink(os_helper.TESTFN + 'link'), os_helper.TESTFN) finally: posix.close(f) - support.unlink(support.TESTFN + 'link') + os_helper.unlink(os_helper.TESTFN + 'link') @unittest.skipUnless(os.unlink in os.supports_dir_fd, "test needs dir_fd support in os.unlink()") def test_unlink_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) - support.create_empty_file(support.TESTFN + 'del') - posix.stat(support.TESTFN + 'del') # should not raise exception + os_helper.create_empty_file(os_helper.TESTFN + 'del') + posix.stat(os_helper.TESTFN + 'del') # should not raise exception try: - posix.unlink(support.TESTFN + 'del', dir_fd=f) + posix.unlink(os_helper.TESTFN + 'del', dir_fd=f) except: - support.unlink(support.TESTFN + 'del') + os_helper.unlink(os_helper.TESTFN + 'del') raise else: - self.assertRaises(OSError, posix.stat, support.TESTFN + 'link') + self.assertRaises(OSError, posix.stat, os_helper.TESTFN + 'link') finally: posix.close(f) @@ -1271,15 +1272,15 @@ class PosixTester(unittest.TestCase): # @unittest.skipUnless(os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()") @unittest.expectedFailure def test_mkfifo_dir_fd(self): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: try: - posix.mkfifo(support.TESTFN, + posix.mkfifo(os_helper.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) except PermissionError as e: self.skipTest('posix.mkfifo(): %s' % e) - self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) finally: posix.close(f) @@ -1406,7 +1407,7 @@ class PosixTester(unittest.TestCase): # behaviour: # os.SEEK_DATA = current position # os.SEEK_HOLE = end of file position - with open(support.TESTFN, 'r+b') as fp: + with open(os_helper.TESTFN, 'r+b') as fp: fp.write(b"hello") fp.flush() size = fp.tell() @@ -1433,7 +1434,7 @@ class PosixTester(unittest.TestCase): if function is None: continue - for dst in ("noodly2", support.TESTFN): + for dst in ("noodly2", os_helper.TESTFN): try: function('doesnotexistfilename', dst) except OSError as e: @@ -1443,10 +1444,10 @@ class PosixTester(unittest.TestCase): self.fail("No valid path_error2() test for os." + name) def test_path_with_null_character(self): - fn = support.TESTFN + fn = os_helper.TESTFN fn_with_NUL = fn + '\0' - self.addCleanup(support.unlink, fn) - support.unlink(fn) + self.addCleanup(os_helper.unlink, fn) + os_helper.unlink(fn) fd = None try: with self.assertRaises(ValueError): @@ -1461,10 +1462,10 @@ class PosixTester(unittest.TestCase): self.assertRaises(ValueError, os.stat, fn_with_NUL) def test_path_with_null_byte(self): - fn = os.fsencode(support.TESTFN) + fn = os.fsencode(os_helper.TESTFN) fn_with_NUL = fn + b'\0' - self.addCleanup(support.unlink, fn) - support.unlink(fn) + self.addCleanup(os_helper.unlink, fn) + os_helper.unlink(fn) fd = None try: with self.assertRaises(ValueError): @@ -1539,8 +1540,8 @@ class _PosixSpawnMixin: return (sys.executable, '-I', '-S', *args) def test_returns_pid(self): - pidfile = support.TESTFN - self.addCleanup(support.unlink, pidfile) + pidfile = os_helper.TESTFN + self.addCleanup(os_helper.unlink, pidfile) script = f"""if 1: import os with open({pidfile!r}, "w") as pidfile: @@ -1570,8 +1571,8 @@ class _PosixSpawnMixin: # TODO: RUSTPYTHON: TypeError: '_Environ' object is not a mapping @unittest.expectedFailure def test_specify_environment(self): - envfile = support.TESTFN - self.addCleanup(support.unlink, envfile) + envfile = os_helper.TESTFN + self.addCleanup(os_helper.unlink, envfile) script = f"""if 1: import os with open({envfile!r}, "w") as envfile: @@ -1831,8 +1832,8 @@ class _PosixSpawnMixin: os.O_RDONLY, 0)]) def test_open_file(self): - outfile = support.TESTFN - self.addCleanup(support.unlink, outfile) + outfile = os_helper.TESTFN + self.addCleanup(os_helper.unlink, outfile) script = """if 1: import sys sys.stdout.write("hello") @@ -1853,8 +1854,8 @@ class _PosixSpawnMixin: # TODO: RUSTPYTHON: FileNotFoundError: [Errno 2] No such file or directory (os error 2): '@test_55144_tmp' -> 'None' @unittest.expectedFailure def test_close_file(self): - closefile = support.TESTFN - self.addCleanup(support.unlink, closefile) + closefile = os_helper.TESTFN + self.addCleanup(os_helper.unlink, closefile) script = f"""if 1: import os try: @@ -1872,8 +1873,8 @@ class _PosixSpawnMixin: self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) def test_dup2(self): - dupfile = support.TESTFN - self.addCleanup(support.unlink, dupfile) + dupfile = os_helper.TESTFN + self.addCleanup(os_helper.unlink, dupfile) script = """if 1: import sys sys.stdout.write("hello") @@ -1899,11 +1900,11 @@ class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): spawn_func = getattr(posix, 'posix_spawnp', None) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_posix_spawnp(self): # Use a symlink to create a program in its own temporary directory temp_dir = tempfile.mkdtemp() - self.addCleanup(support.rmtree, temp_dir) + self.addCleanup(os_helper.rmtree, temp_dir) program = 'posix_spawnp_test_program.exe' program_fullpath = os.path.join(temp_dir, program) @@ -2032,7 +2033,7 @@ class TestPosixWeaklinking(unittest.TestCase): os.link("source", "target", src_dir_fd=0, dst_dir_fd=0) # issue 41355: !HAVE_LINKAT code path ignores the follow_symlinks flag - with support.temp_dir() as base_path: + with os_helper.temp_dir() as base_path: link_path = os.path.join(base_path, "link") target_path = os.path.join(base_path, "target") source_path = os.path.join(base_path, "source") diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 1d69fb4f4..6b40fc09d 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -3,7 +3,8 @@ import posixpath import unittest from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath -from test.support import FakePath +from test.support import os_helper +from test.support.os_helper import FakePath from unittest import mock try: @@ -15,7 +16,7 @@ except ImportError: # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. -ABSTFN = abspath(support.TESTFN) +ABSTFN = abspath(os_helper.TESTFN) def skip_if_ABSTFN_contains_backslash(test): """ @@ -40,8 +41,8 @@ class PosixPathTest(unittest.TestCase): def tearDown(self): for suffix in ["", "1", "2"]: - support.unlink(support.TESTFN + suffix) - safe_rmdir(support.TESTFN + suffix) + os_helper.unlink(os_helper.TESTFN + suffix) + safe_rmdir(os_helper.TESTFN + suffix) def test_join(self): self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), @@ -152,25 +153,25 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo") def test_islink(self): - self.assertIs(posixpath.islink(support.TESTFN + "1"), False) - self.assertIs(posixpath.lexists(support.TESTFN + "2"), False) + self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) + self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False) - with open(support.TESTFN + "1", "wb") as f: + with open(os_helper.TESTFN + "1", "wb") as f: f.write(b"foo") - self.assertIs(posixpath.islink(support.TESTFN + "1"), False) + self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) - if support.can_symlink(): - os.symlink(support.TESTFN + "1", support.TESTFN + "2") - self.assertIs(posixpath.islink(support.TESTFN + "2"), True) - os.remove(support.TESTFN + "1") - self.assertIs(posixpath.islink(support.TESTFN + "2"), True) - self.assertIs(posixpath.exists(support.TESTFN + "2"), False) - self.assertIs(posixpath.lexists(support.TESTFN + "2"), True) + if os_helper.can_symlink(): + os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2") + self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) + os.remove(os_helper.TESTFN + "1") + self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) + self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False) + self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True) - self.assertIs(posixpath.islink(support.TESTFN + "\udfff"), False) - self.assertIs(posixpath.islink(os.fsencode(support.TESTFN) + b"\xff"), False) - self.assertIs(posixpath.islink(support.TESTFN + "\x00"), False) - self.assertIs(posixpath.islink(os.fsencode(support.TESTFN) + b"\x00"), False) + self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False) + self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False) + self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False) + self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False) # TODO: RUSTPYTHON if os.name == "nt": @@ -194,7 +195,7 @@ class PosixPathTest(unittest.TestCase): self.assertIs(posixpath.ismount('/\x00'), False) self.assertIs(posixpath.ismount(b'/\x00'), False) - @unittest.skipUnless(support.can_symlink(), + @unittest.skipUnless(os_helper.can_symlink(), "Test requires symlink support") def test_ismount_symlinks(self): # Symlinks are never mountpoints. @@ -253,7 +254,7 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(posixpath.expanduser(b"foo"), b"foo") def test_expanduser_home_envvar(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['HOME'] = '/home/victor' self.assertEqual(posixpath.expanduser("~"), "/home/victor") @@ -289,7 +290,7 @@ class PosixPathTest(unittest.TestCase): self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: # expanduser should fall back to using the password database del env['HOME'] @@ -356,7 +357,7 @@ class PosixPathTest(unittest.TestCase): os.symlink(ABSTFN+"1", ABSTFN) self.assertEqual(realpath(ABSTFN), ABSTFN+"1") finally: - support.unlink(ABSTFN) + os_helper.unlink(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -366,7 +367,7 @@ class PosixPathTest(unittest.TestCase): os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) self.assertEqual(realpath(ABSTFN), ABSTFN+"1") finally: - support.unlink(ABSTFN) + os_helper.unlink(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -400,15 +401,15 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c") # Test using relative path as well. - with support.change_cwd(dirname(ABSTFN)): + with os_helper.change_cwd(dirname(ABSTFN)): self.assertEqual(realpath(basename(ABSTFN)), ABSTFN) finally: - support.unlink(ABSTFN) - support.unlink(ABSTFN+"1") - support.unlink(ABSTFN+"2") - support.unlink(ABSTFN+"y") - support.unlink(ABSTFN+"c") - support.unlink(ABSTFN+"a") + os_helper.unlink(ABSTFN) + os_helper.unlink(ABSTFN+"1") + os_helper.unlink(ABSTFN+"2") + os_helper.unlink(ABSTFN+"y") + os_helper.unlink(ABSTFN+"c") + os_helper.unlink(ABSTFN+"a") @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -421,8 +422,8 @@ class PosixPathTest(unittest.TestCase): os.symlink('self/self/self', ABSTFN + '/link') self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN) finally: - support.unlink(ABSTFN + '/self') - support.unlink(ABSTFN + '/link') + os_helper.unlink(ABSTFN + '/self') + os_helper.unlink(ABSTFN + '/link') safe_rmdir(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), @@ -438,11 +439,11 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN) # Test using relative path as well. - with support.change_cwd(ABSTFN): + with os_helper.change_cwd(ABSTFN): self.assertEqual(realpath('%d' % depth), ABSTFN) finally: for i in range(depth + 1): - support.unlink(ABSTFN + '/%d' % i) + os_helper.unlink(ABSTFN + '/%d' % i) safe_rmdir(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), @@ -458,10 +459,10 @@ class PosixPathTest(unittest.TestCase): os.mkdir(ABSTFN + "/y") os.symlink(ABSTFN + "/y", ABSTFN + "/k") - with support.change_cwd(ABSTFN + "/k"): + with os_helper.change_cwd(ABSTFN + "/k"): self.assertEqual(realpath("a"), ABSTFN + "/y/a") finally: - support.unlink(ABSTFN + "/k") + os_helper.unlink(ABSTFN + "/k") safe_rmdir(ABSTFN + "/y") safe_rmdir(ABSTFN) @@ -485,11 +486,11 @@ class PosixPathTest(unittest.TestCase): # Absolute path. self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k") # Relative path. - with support.change_cwd(dirname(ABSTFN)): + with os_helper.change_cwd(dirname(ABSTFN)): self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."), ABSTFN + "/k") finally: - support.unlink(ABSTFN + "/link-y") + os_helper.unlink(ABSTFN + "/link-y") safe_rmdir(ABSTFN + "/k/y") safe_rmdir(ABSTFN + "/k") safe_rmdir(ABSTFN) @@ -505,12 +506,12 @@ class PosixPathTest(unittest.TestCase): os.mkdir(ABSTFN) os.mkdir(ABSTFN + "/k") os.symlink(ABSTFN, ABSTFN + "link") - with support.change_cwd(dirname(ABSTFN)): + with os_helper.change_cwd(dirname(ABSTFN)): base = basename(ABSTFN) self.assertEqual(realpath(base + "link"), ABSTFN) self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k") finally: - support.unlink(ABSTFN + "link") + os_helper.unlink(ABSTFN + "link") safe_rmdir(ABSTFN + "/k") safe_rmdir(ABSTFN) @@ -679,9 +680,9 @@ class PathLikeTests(unittest.TestCase): path = posixpath def setUp(self): - self.file_name = support.TESTFN.lower() - self.file_path = FakePath(support.TESTFN) - self.addCleanup(support.unlink, self.file_name) + self.file_name = os_helper.TESTFN.lower() + self.file_path = FakePath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, self.file_name) with open(self.file_name, 'xb', 0) as file: file.write(b"test_posixpath.PathLikeTests") diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index 4b726f51f..d7de6808d 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -9,13 +9,14 @@ import tempfile import unittest from test import support +from test.support import os_helper def without_source_date_epoch(fxn): """Runs function with SOURCE_DATE_EPOCH unset.""" @functools.wraps(fxn) def wrapper(*args, **kwargs): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.unset('SOURCE_DATE_EPOCH') return fxn(*args, **kwargs) return wrapper @@ -25,7 +26,7 @@ def with_source_date_epoch(fxn): """Runs function with SOURCE_DATE_EPOCH set.""" @functools.wraps(fxn) def wrapper(*args, **kwargs): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['SOURCE_DATE_EPOCH'] = '123456789' return fxn(*args, **kwargs) return wrapper @@ -101,7 +102,7 @@ class PyCompileTestsBase: self.assertTrue(os.path.exists(self.cache_path)) def test_cwd(self): - with support.change_cwd(self.directory): + with os_helper.change_cwd(self.directory): py_compile.compile(os.path.basename(self.source_path), os.path.basename(self.pyc_path)) self.assertTrue(os.path.exists(self.pyc_path)) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 4258946aa..fc56ec4af 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -19,6 +19,7 @@ import textwrap import unittest from test import libregrtest from test import support +from test.support import os_helper from test.libregrtest import utils @@ -166,12 +167,12 @@ class ParseArgsTestCase(unittest.TestCase): self.assertEqual(ns.ignore_tests, ['pattern']) self.checkError([opt], 'expected one argument') - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, "w") as fp: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, "w") as fp: print('matchfile1', file=fp) print('matchfile2', file=fp) - filename = os.path.abspath(support.TESTFN) + filename = os.path.abspath(os_helper.TESTFN) ns = libregrtest._parse_args(['-m', 'match', '--ignorefile', filename]) self.assertEqual(ns.ignore_tests, @@ -188,12 +189,12 @@ class ParseArgsTestCase(unittest.TestCase): '-m', 'pattern2']) self.assertEqual(ns.match_tests, ['pattern1', 'pattern2']) - self.addCleanup(support.unlink, support.TESTFN) - with open(support.TESTFN, "w") as fp: + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with open(os_helper.TESTFN, "w") as fp: print('matchfile1', file=fp) print('matchfile2', file=fp) - filename = os.path.abspath(support.TESTFN) + filename = os.path.abspath(os_helper.TESTFN) ns = libregrtest._parse_args(['-m', 'match', '--matchfile', filename]) self.assertEqual(ns.match_tests, @@ -242,7 +243,7 @@ class ParseArgsTestCase(unittest.TestCase): def test_testdir(self): ns = libregrtest._parse_args(['--testdir', 'foo']) - self.assertEqual(ns.testdir, os.path.join(support.SAVEDCWD, 'foo')) + self.assertEqual(ns.testdir, os.path.join(os_helper.SAVEDCWD, 'foo')) self.checkError(['--testdir'], 'expected one argument') def test_runleaks(self): @@ -289,7 +290,7 @@ class ParseArgsTestCase(unittest.TestCase): with self.subTest(opt=opt): ns = libregrtest._parse_args([opt, 'foo']) self.assertEqual(ns.coverdir, - os.path.join(support.SAVEDCWD, 'foo')) + os.path.join(os_helper.SAVEDCWD, 'foo')) self.checkError([opt], 'expected one argument') def test_nocoverdir(self): @@ -368,7 +369,7 @@ class BaseTestCase(unittest.TestCase): self.testdir = os.path.realpath(os.path.dirname(__file__)) self.tmptestdir = tempfile.mkdtemp() - self.addCleanup(support.rmtree, self.tmptestdir) + self.addCleanup(os_helper.rmtree, self.tmptestdir) def create_test(self, name=None, code=None): if not name: @@ -389,7 +390,7 @@ class BaseTestCase(unittest.TestCase): name = self.TESTNAME_PREFIX + name path = os.path.join(self.tmptestdir, name + '.py') - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) # Use 'x' mode to ensure that we do not override existing tests try: with open(path, 'x', encoding='utf-8') as fp: @@ -778,8 +779,8 @@ class ArgsTestCase(BaseTestCase): # Write the list of files using a format similar to regrtest output: # [1/2] test_1 # [2/2] test_2 - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) # test format '0:00:00 [2/7] test_opcodes -- test_grammar took 0 sec' with open(filename, "w") as fp: @@ -900,7 +901,7 @@ class ArgsTestCase(BaseTestCase): test = self.create_test('huntrleaks', code=code) filename = 'reflog.txt' - self.addCleanup(support.unlink, filename) + self.addCleanup(os_helper.unlink, filename) output = self.run_tests('--huntrleaks', '3:3:', test, exitcode=2, stderr=subprocess.STDOUT) @@ -1013,8 +1014,8 @@ class ArgsTestCase(BaseTestCase): testname = self.create_test(code=code) # only run a subset - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) subset = [ # only ignore the method name @@ -1054,8 +1055,8 @@ class ArgsTestCase(BaseTestCase): self.assertEqual(methods, all_methods) # only run a subset - filename = support.TESTFN - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) subset = [ # only match the method name diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 73f28390f..a4ab868e7 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -10,7 +10,8 @@ import importlib import importlib.util import unittest -from test.support import create_empty_file, verbose +from test.support import verbose +from test.support.os_helper import create_empty_file from reprlib import repr as r # Don't shadow builtin repr from reprlib import Repr from reprlib import recursive_repr diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index 176e2cd0b..b3d549ff4 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -2,6 +2,7 @@ import contextlib import sys import unittest from test import support +from test.support import os_helper import time resource = support.import_module('resource') @@ -53,7 +54,7 @@ class ResourceTest(unittest.TestCase): limit_set = True except ValueError: limit_set = False - f = open(support.TESTFN, "wb") + f = open(os_helper.TESTFN, "wb") try: f.write(b"X" * 1024) try: @@ -79,7 +80,7 @@ class ResourceTest(unittest.TestCase): finally: if limit_set: resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_fsize_toobig(self): # Be sure that setrlimit is checking for really large values diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 4fb9ac762..85ee84218 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -13,8 +13,8 @@ import textwrap import unittest import warnings from test.support import ( - forget, make_legacy_pyc, unload, verbose, no_tracing, - create_empty_file, temp_dir) + forget, make_legacy_pyc, unload, verbose, no_tracing) +from test.support.os_helper import create_empty_file, temp_dir from test.support.script_helper import make_script, make_zip_script diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index beb20f43d..99bd58bb6 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper # import gc import weakref import operator @@ -321,15 +322,15 @@ class TestJointOps: w = ReprWrapper() s = self.thetype([w]) w.value = s - fo = open(support.TESTFN, "w") + fo = open(os_helper.TESTFN, "w") try: fo.write(str(s)) fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), repr(s)) finally: fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -828,14 +829,14 @@ class TestBasicOps: def test_print(self): try: - fo = open(support.TESTFN, "w") + fo = open(os_helper.TESTFN, "w") fo.write(str(self.set)) fo.close() - fo = open(support.TESTFN, "r") + fo = open(os_helper.TESTFN, "r") self.assertEqual(fo.read(), repr(self.set)) finally: fo.close() - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def test_length(self): self.assertEqual(len(self.set), self.length) diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index 9ffe2cbea..ac25eee2e 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -2,6 +2,7 @@ import unittest import shelve import glob from test import support +from test.support import os_helper from collections.abc import MutableMapping from test.test_dbm import dbm_iterator @@ -45,7 +46,7 @@ class TestCase(unittest.TestCase): def tearDown(self): for f in glob.glob(self.fn+"*"): - support.unlink(f) + os_helper.unlink(f) def test_close(self): d1 = {} @@ -186,7 +187,7 @@ class TestShelveBase(mapping_tests.BasicTestMappingProtocol): self._db = [] if not self._in_mem: for f in glob.glob(self.fn+"*"): - support.unlink(f) + os_helper.unlink(f) class TestAsciiFileShelve(TestShelveBase): _args={'protocol':0} diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index d6b9fc876..fba98972d 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -30,7 +30,8 @@ except ImportError: posix = None from test import support -from test.support import TESTFN, FakePath +from test.support import os_helper +from test.support.os_helper import TESTFN, FakePath TESTFN2 = TESTFN + "2" MACOS = sys.platform.startswith("darwin") @@ -140,9 +141,9 @@ def supports_file2file_sendfile(): return True finally: if srcname is not None: - support.unlink(srcname) + os_helper.unlink(srcname) if dstname is not None: - support.unlink(dstname) + os_helper.unlink(dstname) SUPPORTS_SENDFILE = supports_file2file_sendfile() @@ -193,7 +194,7 @@ class TestShutil(unittest.TestCase): self.assertIsInstance(victim, bytes) shutil.rmtree(victim) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_rmtree_fails_on_symlink(self): tmp = self.mkdtemp() dir_ = os.path.join(tmp, 'dir') @@ -212,7 +213,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(errors[0][1], link) self.assertIsInstance(errors[0][2][1], OSError) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_rmtree_works_on_symlinks(self): tmp = self.mkdtemp() dir1 = os.path.join(tmp, 'dir1') @@ -324,7 +325,7 @@ class TestShutil(unittest.TestCase): self.child_file_path = os.path.join(TESTFN, 'a') self.child_dir_path = os.path.join(TESTFN, 'b') - support.create_empty_file(self.child_file_path) + os_helper.create_empty_file(self.child_file_path) os.mkdir(self.child_dir_path) old_dir_mode = os.stat(TESTFN).st_mode old_child_file_mode = os.stat(self.child_file_path).st_mode @@ -387,7 +388,7 @@ class TestShutil(unittest.TestCase): finally: os.lstat = orig_lstat - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copymode_follow_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -420,7 +421,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode) @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copymode_symlink_to_symlink(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -450,7 +451,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode) @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copymode_symlink_to_symlink_wo_lchmod(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -463,7 +464,7 @@ class TestShutil(unittest.TestCase): os.symlink(dst, dst_link) shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copystat_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -533,7 +534,7 @@ class TestShutil(unittest.TestCase): finally: os.chflags = old_chflags - @support.skip_unless_xattr + @os_helper.skip_unless_xattr def test_copyxattr(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -597,8 +598,8 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly') self.assertEqual(os.getxattr(dstro, 'user.the_value'), b'fiddly') - @support.skip_unless_symlink - @support.skip_unless_xattr + @os_helper.skip_unless_symlink + @os_helper.skip_unless_xattr @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0, 'root privileges required') def test_copyxattr_symlinks(self): @@ -622,7 +623,7 @@ class TestShutil(unittest.TestCase): shutil._copyxattr(src_link, dst, follow_symlinks=False) self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copy_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -645,7 +646,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.lstat(src_link).st_mode, os.lstat(dst).st_mode) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copy2_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -680,7 +681,7 @@ class TestShutil(unittest.TestCase): if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'): self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags) - @support.skip_unless_xattr + @os_helper.skip_unless_xattr def test_copy2_xattr(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -693,7 +694,7 @@ class TestShutil(unittest.TestCase): os.getxattr(dst, 'user.foo')) os.remove(dst) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copyfile_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'src') @@ -785,7 +786,7 @@ class TestShutil(unittest.TestCase): with self.assertRaises(FileExistsError): shutil.copytree(src_dir, dst_dir, dirs_exist_ok=False) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copytree_symlinks(self): tmp_dir = self.mkdtemp() src_dir = os.path.join(tmp_dir, 'src') @@ -975,9 +976,9 @@ class TestShutil(unittest.TestCase): flag = [] src = tempfile.mkdtemp() - self.addCleanup(support.rmtree, src) + self.addCleanup(os_helper.rmtree, src) dst = tempfile.mktemp() - self.addCleanup(support.rmtree, dst) + self.addCleanup(os_helper.rmtree, dst) with open(os.path.join(src, 'foo'), 'w') as f: f.close() shutil.copytree(src, dst, copy_function=custom_cpfun) @@ -1003,7 +1004,7 @@ class TestShutil(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_dont_copy_file_onto_symlink_to_itself(self): # bug 851123. os.mkdir(TESTFN) @@ -1023,7 +1024,7 @@ class TestShutil(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_rmtree_on_symlink(self): # bug 1669. os.mkdir(TESTFN) @@ -1067,7 +1068,7 @@ class TestShutil(unittest.TestCase): os.remove(TESTFN) @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copytree_named_pipe(self): os.mkdir(TESTFN) try: @@ -1106,7 +1107,7 @@ class TestShutil(unittest.TestCase): shutil.copytree(src_dir, dst_dir, copy_function=_copy) self.assertEqual(len(copied), 2) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copytree_dangling_symlinks(self): # a dangling symlink raises an error at the end @@ -1127,7 +1128,7 @@ class TestShutil(unittest.TestCase): shutil.copytree(src_dir, dst_dir, symlinks=True) self.assertIn('test.txt', os.listdir(dst_dir)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_copytree_symlink_dir(self): src_dir = self.mkdtemp() dst_dir = os.path.join(self.mkdtemp(), 'destination') @@ -1192,7 +1193,7 @@ class TestShutil(unittest.TestCase): work_dir = os.path.dirname(tmpdir2) rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - with support.change_cwd(work_dir): + with os_helper.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) tarball = make_archive(rel_base_name, 'gztar', root_dir, '.') @@ -1206,7 +1207,7 @@ class TestShutil(unittest.TestCase): './file1', './file2', './sub/file3']) # trying an uncompressed one - with support.change_cwd(work_dir): + with os_helper.change_cwd(work_dir): tarball = make_archive(rel_base_name, 'tar', root_dir, '.') self.assertEqual(tarball, base_name + '.tar') self.assertTrue(os.path.isfile(tarball)) @@ -1281,7 +1282,7 @@ class TestShutil(unittest.TestCase): work_dir = os.path.dirname(tmpdir2) rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - with support.change_cwd(work_dir): + with os_helper.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) res = make_archive(rel_base_name, 'zip', root_dir) @@ -1294,7 +1295,7 @@ class TestShutil(unittest.TestCase): 'dist/file1', 'dist/file2', 'dist/sub/file3', 'outer']) - with support.change_cwd(work_dir): + with os_helper.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) res = make_archive(rel_base_name, 'zip', root_dir, base_dir) @@ -1346,7 +1347,7 @@ class TestShutil(unittest.TestCase): # now check the ZIP file using `unzip -t` zip_cmd = ['unzip', '-t', archive] - with support.change_cwd(root_dir): + with os_helper.change_cwd(root_dir): try: subprocess.check_output(zip_cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: @@ -1396,7 +1397,7 @@ class TestShutil(unittest.TestCase): base_name = os.path.join(self.mkdtemp(), 'archive') group = grp.getgrgid(0)[0] owner = pwd.getpwuid(0)[0] - with support.change_cwd(root_dir): + with os_helper.change_cwd(root_dir): archive_name = make_archive(base_name, 'gztar', root_dir, 'dist', owner=owner, group=group) @@ -1430,7 +1431,7 @@ class TestShutil(unittest.TestCase): def test_make_tarfile_in_curdir(self): # Issue #21280 root_dir = self.mkdtemp() - with support.change_cwd(root_dir): + with os_helper.change_cwd(root_dir): self.assertEqual(make_archive('test', 'tar'), 'test.tar') self.assertTrue(os.path.isfile('test.tar')) @@ -1438,7 +1439,7 @@ class TestShutil(unittest.TestCase): def test_make_zipfile_in_curdir(self): # Issue #21280 root_dir = self.mkdtemp() - with support.change_cwd(root_dir): + with os_helper.change_cwd(root_dir): self.assertEqual(make_archive('test', 'zip'), 'test.zip') self.assertTrue(os.path.isfile('test.zip')) @@ -1695,18 +1696,18 @@ class TestWhich(unittest.TestCase): # that exists, it should be returned. base_dir, tail_dir = os.path.split(self.dir) relpath = os.path.join(tail_dir, self.file) - with support.change_cwd(path=base_dir): + with os_helper.change_cwd(path=base_dir): rv = shutil.which(relpath, path=self.temp_dir) self.assertEqual(rv, relpath) # But it shouldn't be searched in PATH directories (issue #16957). - with support.change_cwd(path=self.dir): + with os_helper.change_cwd(path=self.dir): rv = shutil.which(relpath, path=base_dir) self.assertIsNone(rv) def test_cwd(self): # Issue #16957 base_dir = os.path.dirname(self.dir) - with support.change_cwd(path=self.dir): + with os_helper.change_cwd(path=self.dir): rv = shutil.which(self.file, path=base_dir) if sys.platform == "win32": # Windows: current directory implicitly on PATH @@ -1727,7 +1728,7 @@ class TestWhich(unittest.TestCase): def test_relative_path(self): base_dir, tail_dir = os.path.split(self.dir) - with support.change_cwd(path=base_dir): + with os_helper.change_cwd(path=base_dir): rv = shutil.which(self.file, path=tail_dir) self.assertEqual(rv, os.path.join(tail_dir, self.file)) @@ -1745,19 +1746,19 @@ class TestWhich(unittest.TestCase): self.assertEqual(rv, self.temp_file.name[:-4] + self.ext) def test_environ_path(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATH'] = self.env_path rv = shutil.which(self.file) self.assertEqual(rv, self.temp_file.name) def test_environ_path_empty(self): # PATH='': no match - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATH'] = '' with unittest.mock.patch('os.confstr', return_value=self.dir, \ create=True), \ support.swap_attr(os, 'defpath', self.dir), \ - support.change_cwd(self.dir): + os_helper.change_cwd(self.dir): rv = shutil.which(self.file) self.assertIsNone(rv) @@ -1770,7 +1771,7 @@ class TestWhich(unittest.TestCase): expected_cwd = os.path.join(curdir, expected_cwd) # PATH=':': explicitly looks in the current directory - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATH'] = os.pathsep with unittest.mock.patch('os.confstr', return_value=self.dir, \ create=True), \ @@ -1779,12 +1780,12 @@ class TestWhich(unittest.TestCase): self.assertIsNone(rv) # look in current directory - with support.change_cwd(self.dir): + with os_helper.change_cwd(self.dir): rv = shutil.which(self.file) self.assertEqual(rv, expected_cwd) def test_environ_path_missing(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.pop('PATH', None) # without confstr @@ -1803,14 +1804,14 @@ class TestWhich(unittest.TestCase): def test_empty_path(self): base_dir = os.path.dirname(self.dir) - with support.change_cwd(path=self.dir), \ - support.EnvironmentVarGuard() as env: + with os_helper.change_cwd(path=self.dir), \ + os_helper.EnvironmentVarGuard() as env: env['PATH'] = self.env_path rv = shutil.which(self.file, path='') self.assertIsNone(rv) def test_empty_path_no_PATH(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.pop('PATH', None) rv = shutil.which(self.file) self.assertIsNone(rv) @@ -1827,7 +1828,7 @@ class TestWhich(unittest.TestCase): program = os.path.basename(temp_filexyz.name) program = os.path.splitext(program)[0] - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATHEXT'] = ext rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) @@ -1967,7 +1968,7 @@ class TestMove(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @mock_rename def test_move_file_symlink(self): dst = os.path.join(self.src_dir, 'bar') @@ -1976,7 +1977,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(self.dst_file)) self.assertTrue(os.path.samefile(self.src_file, self.dst_file)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @mock_rename def test_move_file_symlink_to_dir(self): filename = "bar" @@ -1987,7 +1988,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(final_link)) self.assertTrue(os.path.samefile(self.src_file, final_link)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @mock_rename def test_move_dangling_symlink(self): src = os.path.join(self.src_dir, 'baz') @@ -1998,7 +1999,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(dst_link)) self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link)) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink @mock_rename def test_move_dir_symlink(self): src = os.path.join(self.src_dir, 'baz') @@ -2032,8 +2033,8 @@ class TestMove(unittest.TestCase): moved = [] def _copy(src, dst): moved.append((src, dst)) - support.create_empty_file(os.path.join(self.src_dir, 'child')) - support.create_empty_file(os.path.join(self.src_dir, 'child1')) + os_helper.create_empty_file(os.path.join(self.src_dir, 'child')) + os_helper.create_empty_file(os.path.join(self.src_dir, 'child1')) shutil.move(self.src_dir, self.dst_dir, copy_function=_copy) self.assertEqual(len(moved), 3) @@ -2171,11 +2172,11 @@ class TestCopyFileObj(unittest.TestCase): @classmethod def tearDownClass(cls): - support.unlink(TESTFN) - support.unlink(TESTFN2) + os_helper.unlink(TESTFN) + os_helper.unlink(TESTFN2) def tearDown(self): - support.unlink(TESTFN2) + os_helper.unlink(TESTFN2) @contextlib.contextmanager def get_files(self): @@ -2220,7 +2221,7 @@ class TestCopyFileObj(unittest.TestCase): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(b'foo') fname = f.name - self.addCleanup(support.unlink, fname) + self.addCleanup(os_helper.unlink, fname) with unittest.mock.patch("shutil._copyfileobj_readinto") as m: shutil.copyfile(fname, TESTFN2) self.assertEqual(m.call_args[0][2], 3) @@ -2229,7 +2230,7 @@ class TestCopyFileObj(unittest.TestCase): with tempfile.NamedTemporaryFile(delete=False) as f: pass fname = f.name - self.addCleanup(support.unlink, fname) + self.addCleanup(os_helper.unlink, fname) with unittest.mock.patch("shutil._copyfileobj_readinto") as m: shutil.copyfile(fname, TESTFN2) assert not m.called @@ -2251,10 +2252,10 @@ class _ZeroCopyFileTest(object): @classmethod def tearDownClass(cls): - support.unlink(TESTFN) + os_helper.unlink(TESTFN) def tearDown(self): - support.unlink(TESTFN2) + os_helper.unlink(TESTFN2) @contextlib.contextmanager def get_files(self): @@ -2300,8 +2301,8 @@ class _ZeroCopyFileTest(object): def test_empty_file(self): srcname = TESTFN + 'src' dstname = TESTFN + 'dst' - self.addCleanup(lambda: support.unlink(srcname)) - self.addCleanup(lambda: support.unlink(dstname)) + self.addCleanup(lambda: os_helper.unlink(srcname)) + self.addCleanup(lambda: os_helper.unlink(dstname)) with open(srcname, "wb"): pass @@ -2427,9 +2428,9 @@ class TestZeroCopySendfile(_ZeroCopyFileTest, unittest.TestCase): # sendfile() are the same. self.assertEqual(blocksize, os.path.getsize(TESTFN)) # ...unless we're dealing with a small file. - support.unlink(TESTFN2) + os_helper.unlink(TESTFN2) write_file(TESTFN2, b"hello", binary=True) - self.addCleanup(support.unlink, TESTFN2 + '3') + self.addCleanup(os_helper.unlink, TESTFN2 + '3') self.assertRaises(ZeroDivisionError, shutil.copyfile, TESTFN2, TESTFN2 + '3') blocksize = m.call_args[0][3] @@ -2479,20 +2480,20 @@ class TermsizeTests(unittest.TestCase): def test_os_environ_first(self): "Check if environment variables have precedence" - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['COLUMNS'] = '777' del env['LINES'] size = shutil.get_terminal_size() self.assertEqual(size.columns, 777) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: del env['COLUMNS'] env['LINES'] = '888' size = shutil.get_terminal_size() self.assertEqual(size.lines, 888) def test_bad_environ(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['COLUMNS'] = 'xxx' env['LINES'] = 'yyy' size = shutil.get_terminal_size() @@ -2516,7 +2517,7 @@ class TermsizeTests(unittest.TestCase): self.skipTest("stty invocation failed") expected = (int(size[1]), int(size[0])) # reversed order - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: del env['LINES'] del env['COLUMNS'] actual = shutil.get_terminal_size() @@ -2524,7 +2525,7 @@ class TermsizeTests(unittest.TestCase): self.assertEqual(expected, actual) def test_fallback(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: del env['LINES'] del env['COLUMNS'] diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 3f61915a3..8d59a5153 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -10,6 +10,7 @@ import threading import time import unittest from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok, spawn_python try: import _testcapi @@ -171,7 +172,7 @@ class WakeupFDTests(unittest.TestCase): signal.set_wakeup_fd(signal.SIGINT, False) def test_invalid_fd(self): - fd = support.make_bad_fd() + fd = os_helper.make_bad_fd() self.assertRaises((ValueError, OSError), signal.set_wakeup_fd, fd) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 921daf2a1..94592d907 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper import errno import io @@ -645,7 +646,7 @@ class UnixSocketTestBase(SocketTestBase): def bindSock(self, sock): path = tempfile.mktemp(dir=self.dir_path) support.bind_unix_socket(sock, path) - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) class UnixStreamBase(UnixSocketTestBase): """Base class for Unix-domain SOCK_STREAM tests.""" @@ -1835,14 +1836,14 @@ class GeneralModuleTests(unittest.TestCase): def test_socket_fileno_requires_valid_fd(self): WSAENOTSOCK = 10038 with self.assertRaises(OSError) as cm: - socket.socket(fileno=support.make_bad_fd()) + socket.socket(fileno=os_helper.make_bad_fd()) self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) with self.assertRaises(OSError) as cm: socket.socket( socket.AF_INET, socket.SOCK_STREAM, - fileno=support.make_bad_fd()) + fileno=os_helper.make_bad_fd()) self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) def test_socket_fileno_requires_socket_fd(self): @@ -5136,16 +5137,16 @@ class TestUnixDomain(unittest.TestCase): def testStrAddr(self): # Test binding to and retrieving a normal string pathname. - path = os.path.abspath(support.TESTFN) + path = os.path.abspath(os_helper.TESTFN) self.bind(self.sock, path) - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) self.assertEqual(self.sock.getsockname(), path) def testBytesAddr(self): # Test binding to a bytes pathname. - path = os.path.abspath(support.TESTFN) + path = os.path.abspath(os_helper.TESTFN) self.bind(self.sock, self.encoded(path)) - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) self.assertEqual(self.sock.getsockname(), path) # TODO: RUSTPYTHON, surrogateescape @@ -5156,17 +5157,17 @@ class TestUnixDomain(unittest.TestCase): path = os.path.abspath(support.TESTFN_UNICODE) b = self.encoded(path) self.bind(self.sock, b.decode("ascii", "surrogateescape")) - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) self.assertEqual(self.sock.getsockname(), path) def testUnencodableAddr(self): # Test binding to a pathname that cannot be encoded in the # file system encoding. - if support.TESTFN_UNENCODABLE is None: + if os_helper.TESTFN_UNENCODABLE is None: self.skipTest("No unencodable filename available") - path = os.path.abspath(support.TESTFN_UNENCODABLE) + path = os.path.abspath(os_helper.TESTFN_UNENCODABLE) self.bind(self.sock, path) - self.addCleanup(support.unlink, path) + self.addCleanup(os_helper.unlink, path) self.assertEqual(self.sock.getsockname(), path) @@ -5641,16 +5642,16 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): chunk = b"".join([random.choice(string.ascii_letters).encode() for i in range(cls.BUFSIZE)]) - with open(support.TESTFN, 'wb') as f: + with open(os_helper.TESTFN, 'wb') as f: for csize in chunks(cls.FILESIZE, cls.BUFSIZE): f.write(chunk) - with open(support.TESTFN, 'rb') as f: + with open(os_helper.TESTFN, 'rb') as f: cls.FILEDATA = f.read() assert len(cls.FILEDATA) == cls.FILESIZE @classmethod def tearDownClass(cls): - support.unlink(support.TESTFN) + os_helper.unlink(os_helper.TESTFN) def accept_conn(self): self.serv.settimeout(MAIN_TIMEOUT) @@ -5677,7 +5678,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testRegularFile(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file) @@ -5712,9 +5713,9 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testEmptyFileSend(self): address = self.serv.getsockname() - filename = support.TESTFN + "2" + filename = os_helper.TESTFN + "2" with open(filename, 'wb'): - self.addCleanup(support.unlink, filename) + self.addCleanup(os_helper.unlink, filename) file = open(filename, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) @@ -5731,7 +5732,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testOffset(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file, offset=5000) @@ -5748,7 +5749,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCount(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 5000007 meth = self.meth_from_sock(sock) @@ -5768,7 +5769,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCountSmall(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 1 meth = self.meth_from_sock(sock) @@ -5787,7 +5788,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCountWithOffset(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 100007 meth = self.meth_from_sock(sock) @@ -5806,7 +5807,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testNonBlocking(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: sock.setblocking(False) meth = self.meth_from_sock(sock) @@ -5822,7 +5823,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testWithTimeout(self): address = self.serv.getsockname() - file = open(support.TESTFN, 'rb') + file = open(os_helper.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file) @@ -5839,7 +5840,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testWithTimeoutTriggeredSend(self): address = self.serv.getsockname() - with open(support.TESTFN, 'rb') as file: + with open(os_helper.TESTFN, 'rb') as file: with socket.create_connection(address) as sock: sock.settimeout(0.01) meth = self.meth_from_sock(sock) @@ -5855,17 +5856,17 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): pass def test_errors(self): - with open(support.TESTFN, 'rb') as file: + with open(os_helper.TESTFN, 'rb') as file: with socket.socket(type=socket.SOCK_DGRAM) as s: meth = self.meth_from_sock(s) self.assertRaisesRegex( ValueError, "SOCK_STREAM", meth, file) - with open(support.TESTFN, 'rt') as file: + with open(os_helper.TESTFN, 'rt') as file: with socket.socket() as s: meth = self.meth_from_sock(s) self.assertRaisesRegex( ValueError, "binary mode", meth, file) - with open(support.TESTFN, 'rb') as file: + with open(os_helper.TESTFN, 'rb') as file: with socket.socket() as s: meth = self.meth_from_sock(s) self.assertRaisesRegex(TypeError, "positive integer", diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 63fc7c597..ea77da357 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -14,7 +14,7 @@ import unittest import socketserver import test.support -from test.support import reap_children, reap_threads, verbose +from test.support import reap_children, reap_threads, verbose, os_helper test.support.requires("network") @@ -306,7 +306,7 @@ class ErrorHandlerTest(unittest.TestCase): KeyboardInterrupt are not passed.""" def tearDown(self): - test.support.unlink(test.support.TESTFN) + test.os_helper.unlink(test.os_helper.TESTFN) def test_sync_handled(self): BaseErrorTestServer(ValueError) @@ -336,7 +336,7 @@ class ErrorHandlerTest(unittest.TestCase): self.check_result(handled=False) def check_result(self, handled): - with open(test.support.TESTFN) as log: + with open(test.os_helper.TESTFN) as log: expected = 'Handler called\n' + 'Error handled\n' * handled self.assertEqual(log.read(), expected) @@ -354,7 +354,7 @@ class BaseErrorTestServer(socketserver.TCPServer): self.wait_done() def handle_error(self, request, client_address): - with open(test.support.TESTFN, 'a') as log: + with open(test.os_helper.TESTFN, 'a') as log: log.write('Error handled\n') def wait_done(self): @@ -363,7 +363,7 @@ class BaseErrorTestServer(socketserver.TCPServer): class BadHandler(socketserver.BaseRequestHandler): def handle(self): - with open(test.support.TESTFN, 'a') as log: + with open(test.os_helper.TESTFN, 'a') as log: log.write('Handler called\n') raise self.server.exception('Test error') diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index 10fe68849..395f60a04 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -2,8 +2,9 @@ import unittest import os import socket import sys -from test.support import (TESTFN, import_fresh_module, +from test.support import (import_fresh_module, skip_unless_bind_unix_socket) +from test.support.os_helper import TESTFN c_stat = import_fresh_module('stat', fresh=['_stat']) py_stat = import_fresh_module('stat', blocked=['_stat']) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 07c4bee78..a125f8f04 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -18,7 +18,8 @@ import shutil import threading import gc import textwrap -from test.support import FakePath +from test.support import os_helper +from test.support.os_helper import FakePath try: import _testcapi @@ -382,7 +383,7 @@ class ProcessTestCase(BaseTestCase): # Normalize an expected cwd (for Tru64 support). # We can't use os.path.realpath since it doesn't expand Tru64 {memb} # strings. See bug #1063571. - with support.change_cwd(cwd): + with os_helper.change_cwd(cwd): return os.getcwd() # For use in the test_cwd* tests below. @@ -433,7 +434,7 @@ class ProcessTestCase(BaseTestCase): # is relative. python_dir, python_base = self._split_python_path() rel_python = os.path.join(os.curdir, python_base) - with support.temp_cwd() as wrong_dir: + with os_helper.temp_cwd() as wrong_dir: # Before calling with the correct cwd, confirm that the call fails # without cwd and with the wrong cwd. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -450,7 +451,7 @@ class ProcessTestCase(BaseTestCase): python_dir, python_base = self._split_python_path() rel_python = os.path.join(os.curdir, python_base) doesntexist = "somethingyoudonthave" - with support.temp_cwd() as wrong_dir: + with os_helper.temp_cwd() as wrong_dir: # Before calling with the correct cwd, confirm that the call fails # without cwd and with the wrong cwd. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -468,7 +469,7 @@ class ProcessTestCase(BaseTestCase): python_dir, python_base = self._split_python_path() abs_python = os.path.join(python_dir, python_base) rel_python = os.path.join(os.curdir, python_base) - with support.temp_dir() as wrong_dir: + with os_helper.temp_dir() as wrong_dir: # Before calling with an absolute path, confirm that using a # relative path fails. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -1102,7 +1103,7 @@ class ProcessTestCase(BaseTestCase): try: for i in range(max_handles): try: - tmpfile = os.path.join(tmpdir, support.TESTFN) + tmpfile = os.path.join(tmpdir, os_helper.TESTFN) handles.append(os.open(tmpfile, os.O_WRONLY|os.O_CREAT)) except OSError as e: if e.errno != errno.EMFILE: diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index dcf5ae5d0..445b29e5f 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -13,9 +13,9 @@ import textwrap import time import unittest from test import support -from test.support import script_helper +from test.support import script_helper, os_helper -TESTFN = support.TESTFN +TESTFN = os_helper.TESTFN class TestSupport(unittest.TestCase): @@ -45,32 +45,32 @@ class TestSupport(unittest.TestCase): def test_unlink(self): with open(TESTFN, "w") as f: pass - support.unlink(TESTFN) + os_helper.unlink(TESTFN) self.assertFalse(os.path.exists(TESTFN)) - support.unlink(TESTFN) + os_helper.unlink(TESTFN) def test_rmtree(self): - dirpath = support.TESTFN + 'd' + dirpath = os_helper.TESTFN + 'd' subdirpath = os.path.join(dirpath, 'subdir') os.mkdir(dirpath) os.mkdir(subdirpath) - support.rmtree(dirpath) + os_helper.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) with support.swap_attr(support, 'verbose', 0): - support.rmtree(dirpath) + os_helper.rmtree(dirpath) os.mkdir(dirpath) os.mkdir(subdirpath) os.chmod(dirpath, stat.S_IRUSR|stat.S_IXUSR) with support.swap_attr(support, 'verbose', 0): - support.rmtree(dirpath) + os_helper.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) os.mkdir(dirpath) os.mkdir(subdirpath) os.chmod(dirpath, 0) with support.swap_attr(support, 'verbose', 0): - support.rmtree(dirpath) + os_helper.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) def test_forget(self): @@ -87,8 +87,8 @@ class TestSupport(unittest.TestCase): self.assertNotIn(TESTFN, sys.modules) finally: del sys.path[0] - support.unlink(mod_filename) - support.rmtree('__pycache__') + os_helper.unlink(mod_filename) + os_helper.rmtree('__pycache__') def test_HOST(self): s = socket.create_server((support.HOST, 0)) @@ -115,23 +115,23 @@ class TestSupport(unittest.TestCase): try: path = os.path.join(parent_dir, 'temp') self.assertFalse(os.path.isdir(path)) - with support.temp_dir(path) as temp_path: + with os_helper.temp_dir(path) as temp_path: self.assertEqual(temp_path, path) self.assertTrue(os.path.isdir(path)) self.assertFalse(os.path.isdir(path)) finally: - support.rmtree(parent_dir) + os_helper.rmtree(parent_dir) def test_temp_dir__path_none(self): """Test passing no path.""" - with support.temp_dir() as temp_path: + with os_helper.temp_dir() as temp_path: self.assertTrue(os.path.isdir(temp_path)) self.assertFalse(os.path.isdir(temp_path)) def test_temp_dir__existing_dir__quiet_default(self): """Test passing a directory that already exists.""" def call_temp_dir(path): - with support.temp_dir(path) as temp_path: + with os_helper.temp_dir(path) as temp_path: raise Exception("should not get here") path = tempfile.mkdtemp() @@ -151,7 +151,7 @@ class TestSupport(unittest.TestCase): try: with support.check_warnings() as recorder: - with support.temp_dir(path, quiet=True) as temp_path: + with os_helper.temp_dir(path, quiet=True) as temp_path: self.assertEqual(path, temp_path) warnings = [str(w.message) for w in recorder.warnings] # Make sure temp_dir did not delete the original directory. @@ -173,7 +173,7 @@ class TestSupport(unittest.TestCase): script_helper.assert_python_ok("-c", textwrap.dedent(""" import os from test import support - with support.temp_cwd() as temp_path: + with os_helper.temp_cwd() as temp_path: pid = os.fork() if pid != 0: # parent process (child has pid == 0) @@ -197,8 +197,8 @@ class TestSupport(unittest.TestCase): def test_change_cwd(self): original_cwd = os.getcwd() - with support.temp_dir() as temp_path: - with support.change_cwd(temp_path) as new_cwd: + with os_helper.temp_dir() as temp_path: + with os_helper.change_cwd(temp_path) as new_cwd: self.assertEqual(new_cwd, temp_path) self.assertEqual(os.getcwd(), new_cwd) @@ -209,10 +209,10 @@ class TestSupport(unittest.TestCase): original_cwd = os.getcwd() def call_change_cwd(path): - with support.change_cwd(path) as new_cwd: + with os_helper.change_cwd(path) as new_cwd: raise Exception("should not get here") - with support.temp_dir() as parent_dir: + with os_helper.temp_dir() as parent_dir: non_existent_dir = os.path.join(parent_dir, 'does_not_exist') self.assertRaises(FileNotFoundError, call_change_cwd, non_existent_dir) @@ -223,10 +223,10 @@ class TestSupport(unittest.TestCase): """Test passing a non-existent directory with quiet=True.""" original_cwd = os.getcwd() - with support.temp_dir() as parent_dir: + with os_helper.temp_dir() as parent_dir: bad_dir = os.path.join(parent_dir, 'does_not_exist') with support.check_warnings() as recorder: - with support.change_cwd(bad_dir, quiet=True) as new_cwd: + with os_helper.change_cwd(bad_dir, quiet=True) as new_cwd: self.assertEqual(new_cwd, original_cwd) self.assertEqual(os.getcwd(), new_cwd) warnings = [str(w.message) for w in recorder.warnings] @@ -244,7 +244,7 @@ class TestSupport(unittest.TestCase): """Check the warning message when os.chdir() fails.""" path = TESTFN + '_does_not_exist' with support.check_warnings() as recorder: - with support.change_cwd(path=path, quiet=True): + with os_helper.change_cwd(path=path, quiet=True): pass messages = [str(w.message) for w in recorder.warnings] @@ -259,7 +259,7 @@ class TestSupport(unittest.TestCase): def test_temp_cwd(self): here = os.getcwd() - with support.temp_cwd(name=TESTFN): + with os_helper.temp_cwd(name=TESTFN): self.assertEqual(os.path.basename(os.getcwd()), TESTFN) self.assertFalse(os.path.exists(TESTFN)) self.assertEqual(os.getcwd(), here) @@ -268,7 +268,7 @@ class TestSupport(unittest.TestCase): def test_temp_cwd__name_none(self): """Test passing None to temp_cwd().""" original_cwd = os.getcwd() - with support.temp_cwd(name=None) as new_cwd: + with os_helper.temp_cwd(name=None) as new_cwd: self.assertNotEqual(new_cwd, original_cwd) self.assertTrue(os.path.isdir(new_cwd)) self.assertEqual(os.getcwd(), new_cwd) @@ -279,7 +279,7 @@ class TestSupport(unittest.TestCase): @unittest.skipIf(sys.platform.startswith("win"), "TODO: RUSTPYTHON; actual c fds on windows") def test_make_bad_fd(self): - fd = support.make_bad_fd() + fd = os_helper.make_bad_fd() with self.assertRaises(OSError) as cm: os.write(fd, b"foo") self.assertEqual(cm.exception.errno, errno.EBADF) @@ -390,14 +390,14 @@ class TestSupport(unittest.TestCase): def test_check__all__(self): extra = {'tempdir'} - blacklist = {'template'} + not_exported = {'template'} support.check__all__(self, tempfile, extra=extra, - blacklist=blacklist) + not_exported=not_exported) extra = {'TextTestResult', 'installHandler'} - blacklist = {'load_tests', "TestProgram", "BaseTestSuite"} + not_exported = {'load_tests', "TestProgram", "BaseTestSuite"} support.check__all__(self, unittest, @@ -406,7 +406,7 @@ class TestSupport(unittest.TestCase): "unittest.main", "unittest.runner", "unittest.signals", "unittest.async_case"), extra=extra, - blacklist=blacklist) + not_exported=not_exported) self.assertRaises(AssertionError, support.check__all__, self, unittest) @@ -632,10 +632,10 @@ class TestSupport(unittest.TestCase): # We cannot test the absolute value of fd_count(): on old Linux # kernel or glibc versions, os.urandom() keeps a FD open on # /dev/urandom device and Python has 4 FD opens instead of 3. - start = support.fd_count() + start = os_helper.fd_count() fd = os.open(__file__, os.O_RDONLY) try: - more = support.fd_count() + more = os_helper.fd_count() finally: os.close(fd) self.assertEqual(more - start, 1) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index fb387f836..666f6a7cd 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,4 +1,5 @@ from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok, assert_python_failure import builtins import codecs @@ -651,7 +652,7 @@ class SysModuleTest(unittest.TestCase): out = p.communicate()[0].strip() self.assertEqual(out, b'\xbd') - @unittest.skipUnless(test.support.FS_NONASCII, + @unittest.skipUnless(os_helper.FS_NONASCII, 'requires OS support of non-ASCII encodings') @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), 'requires FS encoding to match locale') @@ -660,10 +661,10 @@ class SysModuleTest(unittest.TestCase): env["PYTHONIOENCODING"] = "" p = subprocess.Popen([sys.executable, "-c", - 'print(%a)' % test.support.FS_NONASCII], + 'print(%a)' % os_helper.FS_NONASCII], stdout=subprocess.PIPE, env=env) out = p.communicate()[0].strip() - self.assertEqual(out, os.fsencode(test.support.FS_NONASCII)) + self.assertEqual(out, os.fsencode(os_helper.FS_NONASCII)) @unittest.skipIf(sys.base_prefix != sys.prefix, 'Test is not venv-compatible') diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 40b399935..b2e722b16 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -5,9 +5,10 @@ import subprocess import shutil from copy import copy -from test.support import (import_module, TESTFN, unlink, check_warnings, - captured_stdout, skip_unless_symlink, change_cwd, - PythonSymlink) +from test.support import (import_module, check_warnings, + captured_stdout, PythonSymlink) +from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, + change_cwd) import sysconfig from sysconfig import (get_paths, get_platform, get_config_vars, diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 77a021855..f58284434 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -11,7 +11,7 @@ import unittest.mock import tarfile from test import support -from test.support import script_helper, requires_hashdigest +from test.support import script_helper, requires_hashdigest, os_helper # Check for our compression modules. try: @@ -30,7 +30,7 @@ except ImportError: def sha256sum(data): return sha256(data).hexdigest() -TEMPDIR = os.path.abspath(support.TESTFN) + "-tardir" +TEMPDIR = os.path.abspath(os_helper.TESTFN) + "-tardir" tarextdir = TEMPDIR + '-extract-test' tarname = support.findfile("testtar.tar") gzipname = os.path.join(TEMPDIR, "testtar.tar.gz") @@ -552,21 +552,21 @@ class MiscReadTestBase(CommonReadTest): @unittest.skipUnless(hasattr(os, "link"), "Missing hardlink implementation") - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_extract_hardlink(self): # Test hardlink extraction (e.g. bug #857297). with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar: tar.extract("ustar/regtype", TEMPDIR) - self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype")) + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/regtype")) tar.extract("ustar/lnktype", TEMPDIR) - self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f: data = f.read() self.assertEqual(sha256sum(data), sha256_regtype) tar.extract("ustar/symtype", TEMPDIR) - self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype")) + self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/symtype")) with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f: data = f.read() self.assertEqual(sha256sum(data), sha256_regtype) @@ -599,7 +599,7 @@ class MiscReadTestBase(CommonReadTest): self.assertEqual(tarinfo.mtime, file_mtime, errmsg) finally: tar.close() - support.rmtree(DIR) + os_helper.rmtree(DIR) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -618,7 +618,7 @@ class MiscReadTestBase(CommonReadTest): if sys.platform != "win32": self.assertEqual(os.stat(extracted).st_mode & 0o777, 0o755) finally: - support.rmtree(DIR) + os_helper.rmtree(DIR) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -626,7 +626,7 @@ class MiscReadTestBase(CommonReadTest): def test_extractall_pathlike_name(self): DIR = pathlib.Path(TEMPDIR) / "extractall" - with support.temp_dir(DIR), \ + with os_helper.temp_dir(DIR), \ tarfile.open(tarname, encoding="iso8859-1") as tar: directories = [t for t in tar if t.isdir()] tar.extractall(DIR, directories) @@ -641,7 +641,7 @@ class MiscReadTestBase(CommonReadTest): def test_extract_pathlike_name(self): dirtype = "ustar/dirtype" DIR = pathlib.Path(TEMPDIR) / "extractall" - with support.temp_dir(DIR), \ + with os_helper.temp_dir(DIR), \ tarfile.open(tarname, encoding="iso8859-1") as tar: tarinfo = tar.getmember(dirtype) tar.extract(tarinfo, path=DIR) @@ -669,7 +669,7 @@ class MiscReadTestBase(CommonReadTest): else: self.fail("ReadError not raised") finally: - support.unlink(empty) + os_helper.unlink(empty) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -1037,7 +1037,7 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase): fobj.write(b'x' * 4096) fobj.truncate() s = os.stat(name) - support.unlink(name) + os_helper.unlink(name) return (s.st_blocks * 512 < s.st_size) else: return False @@ -1204,8 +1204,8 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - support.unlink(os.path.join(path, "1")) - support.unlink(os.path.join(path, "2")) + os_helper.unlink(os.path.join(path, "1")) + os_helper.unlink(os.path.join(path, "2")) support.rmdir(path) def test_gettarinfo_pathlike_name(self): @@ -1240,14 +1240,14 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - support.unlink(target) - support.unlink(link) + os_helper.unlink(target) + os_helper.unlink(link) # TODO: RUSTPYTHON if sys.platform == "win32": test_link_size = unittest.expectedFailure(test_link_size) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_symlink_size(self): path = os.path.join(TEMPDIR, "symlink") os.symlink("link_target", path) @@ -1259,7 +1259,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - support.unlink(path) + os_helper.unlink(path) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -1276,7 +1276,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): self.assertEqual(tar.getnames(), [], "added the archive to itself") - with support.change_cwd(TEMPDIR): + with os_helper.change_cwd(TEMPDIR): tar.add(dstname) self.assertEqual(tar.getnames(), [], "added the archive to itself") @@ -1289,7 +1289,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): try: for name in ("foo", "bar", "baz"): name = os.path.join(tempdir, name) - support.create_empty_file(name) + os_helper.create_empty_file(name) def filter(tarinfo): if os.path.basename(tarinfo.name) == "bar": @@ -1317,7 +1317,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - support.rmtree(tempdir) + os_helper.rmtree(tempdir) # Guarantee that stored pathnames are not modified. Don't # remove ./ or ../ or double slashes. Still make absolute @@ -1328,7 +1328,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): # and compare the stored name with the original. foo = os.path.join(TEMPDIR, "foo") if not dir: - support.create_empty_file(foo) + os_helper.create_empty_file(foo) else: os.mkdir(foo) @@ -1345,14 +1345,14 @@ class WriteTest(WriteTestBase, unittest.TestCase): tar.close() if not dir: - support.unlink(foo) + os_helper.unlink(foo) else: support.rmdir(foo) self.assertEqual(t.name, cmp_path or path.replace(os.sep, "/")) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_extractall_symlinks(self): # Test if extractall works properly when tarfile contains symlinks tempdir = os.path.join(TEMPDIR, "testsymlinks") @@ -1375,8 +1375,8 @@ class WriteTest(WriteTestBase, unittest.TestCase): except OSError: self.fail("extractall failed with symlinked files") finally: - support.unlink(temparchive) - support.rmtree(tempdir) + os_helper.unlink(temparchive) + os_helper.rmtree(tempdir) # TODO: RUSTPYTHON if sys.platform != "win32": @@ -1408,7 +1408,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): def test_cwd(self): # Test adding the current working directory. - with support.change_cwd(TEMPDIR): + with os_helper.change_cwd(TEMPDIR): tar = tarfile.open(tmpname, self.mode) try: tar.add(".") @@ -1499,7 +1499,7 @@ class StreamWriteTest(WriteTestBase, unittest.TestCase): # Test for issue #8464: Create files with correct # permissions. if os.path.exists(tmpname): - support.unlink(tmpname) + os_helper.unlink(tmpname) original_umask = os.umask(0o022) try: @@ -1620,7 +1620,7 @@ class CreateTest(WriteTestBase, unittest.TestCase): file_path = os.path.join(TEMPDIR, "spameggs42") def setUp(self): - support.unlink(tmpname) + os_helper.unlink(tmpname) @classmethod def setUpClass(cls): @@ -1629,7 +1629,7 @@ class CreateTest(WriteTestBase, unittest.TestCase): @classmethod def tearDownClass(cls): - support.unlink(cls.file_path) + os_helper.unlink(cls.file_path) def test_create(self): with tarfile.open(tmpname, self.mode) as tobj: @@ -1744,8 +1744,8 @@ class HardlinkTest(unittest.TestCase): def tearDown(self): self.tar.close() - support.unlink(self.foo) - support.unlink(self.bar) + os_helper.unlink(self.foo) + os_helper.unlink(self.bar) def test_add_twice(self): # The same name will be added as a REGTYPE every @@ -2074,7 +2074,7 @@ class AppendTestBase: def setUp(self): self.tarname = tmpname if os.path.exists(self.tarname): - support.unlink(self.tarname) + os_helper.unlink(self.tarname) def _create_testtar(self, mode="w:"): with tarfile.open(tarname, encoding="iso8859-1") as src: @@ -2287,7 +2287,7 @@ class MiscTest(unittest.TestCase): tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT) def test__all__(self): - blacklist = {'version', 'grp', 'pwd', 'symlink_exception', + not_exported = {'version', 'grp', 'pwd', 'symlink_exception', 'NUL', 'BLOCKSIZE', 'RECORDSIZE', 'GNU_MAGIC', 'POSIX_MAGIC', 'LENGTH_NAME', 'LENGTH_LINK', 'LENGTH_PREFIX', 'REGTYPE', 'AREGTYPE', 'LNKTYPE', @@ -2302,7 +2302,7 @@ class MiscTest(unittest.TestCase): 'EOFHeaderError', 'InvalidHeaderError', 'SubsequentHeaderError', 'ExFileObject', 'main'} - support.check__all__(self, tarfile, blacklist=blacklist) + support.check__all__(self, tarfile, not_exported=not_exported) class CommandLineTest(unittest.TestCase): @@ -2319,7 +2319,7 @@ class CommandLineTest(unittest.TestCase): files = [support.findfile('tokenize_tests.txt'), support.findfile('tokenize_tests-no-coding-cookie-' 'and-utf8-bom-sig-only.txt')] - self.addCleanup(support.unlink, tar_name) + self.addCleanup(os_helper.unlink, tar_name) with tarfile.open(tar_name, 'w') as tf: for tardata in files: tf.add(tardata, arcname=os.path.basename(tardata)) @@ -2368,7 +2368,7 @@ class CommandLineTest(unittest.TestCase): self.assertEqual(out, b'') self.assertEqual(rc, 1) finally: - support.unlink(tmpname) + os_helper.unlink(tmpname) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -2414,7 +2414,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tmpname) as tar: tar.getmembers() finally: - support.unlink(tmpname) + os_helper.unlink(tmpname) def test_create_command_verbose(self): files = [support.findfile('tokenize_tests.txt'), @@ -2427,7 +2427,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tmpname) as tar: tar.getmembers() finally: - support.unlink(tmpname) + os_helper.unlink(tmpname) def test_create_command_dotless_filename(self): files = [support.findfile('tokenize_tests.txt')] @@ -2437,7 +2437,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(dotlessname) as tar: tar.getmembers() finally: - support.unlink(dotlessname) + os_helper.unlink(dotlessname) def test_create_command_dot_started_filename(self): tar_name = os.path.join(TEMPDIR, ".testtar") @@ -2448,7 +2448,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tar_name) as tar: tar.getmembers() finally: - support.unlink(tar_name) + os_helper.unlink(tar_name) def test_create_command_compressed(self): files = [support.findfile('tokenize_tests.txt'), @@ -2463,40 +2463,40 @@ class CommandLineTest(unittest.TestCase): with filetype.taropen(tar_name) as tar: tar.getmembers() finally: - support.unlink(tar_name) + os_helper.unlink(tar_name) def test_extract_command(self): self.make_simple_tarfile(tmpname) for opt in '-e', '--extract': try: - with support.temp_cwd(tarextdir): + with os_helper.temp_cwd(tarextdir): out = self.tarfilecmd(opt, tmpname) self.assertEqual(out, b'') finally: - support.rmtree(tarextdir) + os_helper.rmtree(tarextdir) def test_extract_command_verbose(self): self.make_simple_tarfile(tmpname) for opt in '-v', '--verbose': try: - with support.temp_cwd(tarextdir): + with os_helper.temp_cwd(tarextdir): out = self.tarfilecmd(opt, '-e', tmpname) self.assertIn(b' file is extracted.', out) finally: - support.rmtree(tarextdir) + os_helper.rmtree(tarextdir) def test_extract_command_different_directory(self): self.make_simple_tarfile(tmpname) try: - with support.temp_cwd(tarextdir): + with os_helper.temp_cwd(tarextdir): out = self.tarfilecmd('-e', tmpname, 'spamdir') self.assertEqual(out, b'') finally: - support.rmtree(tarextdir) + os_helper.rmtree(tarextdir) def test_extract_command_invalid_file(self): zipname = support.findfile('zipdir.zip') - with support.temp_cwd(tarextdir): + with os_helper.temp_cwd(tarextdir): rc, out, err = self.tarfilecmd_failure('-e', zipname) self.assertIn(b' is not a tar archive.', err) self.assertEqual(out, b'') @@ -2759,7 +2759,7 @@ class NumericOwnerTest(unittest.TestCase): def setUpModule(): - support.unlink(TEMPDIR) + os_helper.unlink(TEMPDIR) os.makedirs(TEMPDIR) global testtarnames @@ -2770,14 +2770,14 @@ def setUpModule(): # Create compressed tarfiles. for c in GzipTest, Bz2Test, LzmaTest: if c.open: - support.unlink(c.tarname) + os_helper.unlink(c.tarname) testtarnames.append(c.tarname) with c.open(c.tarname, "wb") as tar: tar.write(data) def tearDownModule(): if os.path.exists(TEMPDIR): - support.rmtree(TEMPDIR) + os_helper.rmtree(TEMPDIR) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 3f8a6f0fd..d79e9abf8 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -15,7 +15,7 @@ from unittest import mock import unittest from test import support -from test.support import script_helper +from test.support import script_helper, os_helper has_textmode = (tempfile._text_openflags != tempfile._bin_openflags) @@ -232,7 +232,7 @@ class TestCandidateTempdirList(BaseTestCase): # _candidate_tempdir_list contains the expected directories # Make sure the interesting environment variables are all set. - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: for envname in 'TMPDIR', 'TEMP', 'TMP': dirname = os.getenv(envname) if not dirname: @@ -318,7 +318,7 @@ def _inside_empty_temp_dir(): with support.swap_attr(tempfile, 'tempdir', dir): yield finally: - support.rmtree(dir) + os_helper.rmtree(dir) def _mock_candidate_names(*names): @@ -602,7 +602,7 @@ class TestGetTempDir(BaseTestCase): case_sensitive_tempdir = tempfile.mkdtemp("-Temp") _tempdir, tempfile.tempdir = tempfile.tempdir, None try: - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: # Fake the first env var which is checked as a candidate env["TMPDIR"] = case_sensitive_tempdir self.assertEqual(tempfile.gettempdir(), case_sensitive_tempdir) @@ -958,7 +958,7 @@ class TestNamedTemporaryFile(BaseTestCase): def test_bad_mode(self): dir = tempfile.mkdtemp() - self.addCleanup(support.rmtree, dir) + self.addCleanup(os_helper.rmtree, dir) with self.assertRaises(ValueError): tempfile.NamedTemporaryFile(mode='wr', dir=dir) with self.assertRaises(TypeError): @@ -1364,7 +1364,7 @@ class TestTemporaryDirectory(BaseTestCase): finally: os.rmdir(dir) - @support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_cleanup_with_symlink_to_a_directory(self): # cleanup() should not follow symlinks to directories (issue #12464) d1 = self.do_create() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 91ef35241..5f99b7936 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1376,9 +1376,9 @@ class MiscTestCase(unittest.TestCase): @unittest.expectedFailure def test__all__(self): extra = {"ThreadError"} - blacklist = {'currentThread', 'activeCount'} + not_exported = {'currentThread', 'activeCount'} support.check__all__(self, threading, ('threading', '_thread'), - extra=extra, blacklist=blacklist) + extra=extra, not_exported=not_exported) class InterruptMainTests(unittest.TestCase): diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 9c185c2e6..f7b05705d 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1,4 +1,5 @@ from test import support +from test.support import os_helper from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, open as tokenize_open, Untokenizer, generate_tokens, @@ -1306,8 +1307,8 @@ class TestDetectEncoding(TestCase): self.assertEqual(consumed_lines, [b'print("#coding=fake")']) def test_open(self): - filename = support.TESTFN + '.py' - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + '.py' + self.addCleanup(os_helper.unlink, filename) # test coding cookie for encoding in ('iso-8859-15', 'utf-8'): diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 6cf886942..e5183b90e 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -1,6 +1,7 @@ import os import sys -from test.support import TESTFN, TESTFN_UNICODE, FS_NONASCII, rmtree, unlink, captured_stdout +from test.support import captured_stdout +from test.support.os_helper import TESTFN, TESTFN_UNICODE, FS_NONASCII, rmtree, unlink from test.support.script_helper import assert_python_ok, assert_python_failure import textwrap import unittest diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 0864e373d..351c7723b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -7,7 +7,8 @@ import sys import unittest import re from test import support -from test.support import TESTFN, Error, captured_output, unlink, cpython_only +from test.support import Error, captured_output, cpython_only +from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok import textwrap diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py index e8feb42c6..d73cb9cf0 100644 --- a/Lib/test/test_unicode_file.py +++ b/Lib/test/test_unicode_file.py @@ -5,8 +5,9 @@ import os, glob, time, shutil import unicodedata import unittest -from test.support import (run_unittest, rmtree, change_cwd, - TESTFN_ENCODING, TESTFN_UNICODE, TESTFN_UNENCODABLE, create_empty_file) +from test.support import run_unittest +from test.support.os_helper import (rmtree, change_cwd, TESTFN_UNICODE, + TESTFN_UNENCODABLE, create_empty_file) if not os.path.supports_unicode_filenames: try: diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py index a9986469c..c4bb3e186 100644 --- a/Lib/test/test_unicode_file_functions.py +++ b/Lib/test/test_unicode_file_functions.py @@ -6,6 +6,7 @@ import unittest import warnings from unicodedata import normalize from test import support +from test.support import os_helper filenames = [ '1_abc', @@ -62,14 +63,14 @@ class UnicodeFileTests(unittest.TestCase): def setUp(self): try: - os.mkdir(support.TESTFN) + os.mkdir(os_helper.TESTFN) except FileExistsError: pass - self.addCleanup(support.rmtree, support.TESTFN) + self.addCleanup(os_helper.rmtree, os_helper.TESTFN) files = set() for name in self.files: - name = os.path.join(support.TESTFN, self.norm(name)) + name = os.path.join(os_helper.TESTFN, self.norm(name)) with open(name, 'wb') as f: f.write((name+'\n').encode("utf-8")) os.stat(name) @@ -150,9 +151,9 @@ class UnicodeFileTests(unittest.TestCase): sf0 = set(self.files) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - f1 = os.listdir(support.TESTFN.encode(sys.getfilesystemencoding())) - f2 = os.listdir(support.TESTFN) - sf2 = set(os.path.join(support.TESTFN, f) for f in f2) + f1 = os.listdir(os_helper.TESTFN.encode(sys.getfilesystemencoding())) + f2 = os.listdir(os_helper.TESTFN) + sf2 = set(os.path.join(os_helper.TESTFN, f) for f in f2) self.assertEqual(sf0, sf2, "%a != %a" % (sf0, sf2)) self.assertEqual(len(f1), len(f2)) @@ -162,9 +163,9 @@ class UnicodeFileTests(unittest.TestCase): os.rename("tmp", name) def test_directory(self): - dirname = os.path.join(support.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') + dirname = os.path.join(os_helper.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') filename = '\xdf-\u66e8\u66e9\u66eb' - with support.temp_cwd(dirname): + with os_helper.temp_cwd(dirname): with open(filename, 'wb') as f: f.write((filename + '\n').encode("utf-8")) os.access(filename,os.R_OK) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index e29ae3697..9626ef3e6 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -9,6 +9,7 @@ import io import unittest from unittest.mock import patch from test import support +from test.support import os_helper import os try: import ssl @@ -145,18 +146,18 @@ class urlopen_FileTests(unittest.TestCase): # Create a temp file to use for testing self.text = bytes("test_urllib: %s\n" % self.__class__.__name__, "ascii") - f = open(support.TESTFN, 'wb') + f = open(os_helper.TESTFN, 'wb') try: f.write(self.text) finally: f.close() - self.pathname = support.TESTFN + self.pathname = os_helper.TESTFN self.returned_obj = urlopen("file:%s" % self.pathname) def tearDown(self): """Shut down the open object""" self.returned_obj.close() - os.remove(support.TESTFN) + os.remove(os_helper.TESTFN) def test_interface(self): # Make sure object returned by urlopen() has the specified methods @@ -220,7 +221,7 @@ class ProxyTests(unittest.TestCase): def setUp(self): # Records changes to env vars - self.env = support.EnvironmentVarGuard() + self.env = os_helper.EnvironmentVarGuard() # Delete all proxy related env vars for k in list(os.environ): if 'proxy' in k.lower(): @@ -692,10 +693,10 @@ class urlretrieve_FileTests(unittest.TestCase): self.tempFiles = [] # Create a temporary file. - self.registerFileForCleanUp(support.TESTFN) + self.registerFileForCleanUp(os_helper.TESTFN) self.text = b'testing urllib.urlretrieve' try: - FILE = open(support.TESTFN, 'wb') + FILE = open(os_helper.TESTFN, 'wb') FILE.write(self.text) FILE.close() finally: @@ -738,18 +739,18 @@ class urlretrieve_FileTests(unittest.TestCase): def test_basic(self): # Make sure that a local file just gets its own location returned and # a headers value is returned. - result = urllib.request.urlretrieve("file:%s" % support.TESTFN) - self.assertEqual(result[0], support.TESTFN) + result = urllib.request.urlretrieve("file:%s" % os_helper.TESTFN) + self.assertEqual(result[0], os_helper.TESTFN) self.assertIsInstance(result[1], email.message.Message, "did not get an email.message.Message instance " "as second returned value") def test_copy(self): # Test that setting the filename argument works. - second_temp = "%s.2" % support.TESTFN + second_temp = "%s.2" % os_helper.TESTFN self.registerFileForCleanUp(second_temp) result = urllib.request.urlretrieve(self.constructLocalFileUrl( - support.TESTFN), second_temp) + os_helper.TESTFN), second_temp) self.assertEqual(second_temp, result[0]) self.assertTrue(os.path.exists(second_temp), "copy of the file was not " "made") @@ -770,10 +771,10 @@ class urlretrieve_FileTests(unittest.TestCase): self.assertIsInstance(file_size, int) self.assertEqual(block_count, count_holder[0]) count_holder[0] = count_holder[0] + 1 - second_temp = "%s.2" % support.TESTFN + second_temp = "%s.2" % os_helper.TESTFN self.registerFileForCleanUp(second_temp) urllib.request.urlretrieve( - self.constructLocalFileUrl(support.TESTFN), + self.constructLocalFileUrl(os_helper.TESTFN), second_temp, hooktester) def test_reporthook_0_bytes(self): @@ -783,7 +784,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile() urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - support.TESTFN, hooktester) + os_helper.TESTFN, hooktester) self.assertEqual(len(report), 1) self.assertEqual(report[0][2], 0) @@ -796,7 +797,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile(b"x" * 5) urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - support.TESTFN, hooktester) + os_helper.TESTFN, hooktester) self.assertEqual(len(report), 2) self.assertEqual(report[0][2], 5) self.assertEqual(report[1][2], 5) @@ -810,7 +811,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile(b"x" * 8193) urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - support.TESTFN, hooktester) + os_helper.TESTFN, hooktester) self.assertEqual(len(report), 3) self.assertEqual(report[0][2], 8193) self.assertEqual(report[0][1], 8192) @@ -1543,7 +1544,7 @@ class URLopener_Tests(FakeHTTPMixin, unittest.TestCase): @support.ignore_warnings(category=DeprecationWarning) def test_urlopener_retrieve_file(self): - with support.temp_dir() as tmpdir: + with os_helper.temp_dir() as tmpdir: fd, tmpfile = tempfile.mkstemp(dir=tmpdir) os.close(fd) fileurl = "file:" + urllib.request.pathname2url(tmpfile) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 4ddf50683..50f97913f 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper from test import test_urllib import os @@ -772,7 +773,7 @@ class HandlerTests(unittest.TestCase): h = urllib.request.FileHandler() o = h.parent = MockOpener() - TESTFN = support.TESTFN + TESTFN = os_helper.TESTFN urlpath = sanepathname2url(os.path.abspath(TESTFN)) towrite = b"hello, world\n" urls = [ diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 0f43d71ea..c27e14810 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper from test.test_urllib2 import sanepathname2url import os @@ -112,7 +113,7 @@ class OtherNetworkTests(unittest.TestCase): self._test_urls(urls, self._extra_handlers()) def test_file(self): - TESTFN = support.TESTFN + TESTFN = os_helper.TESTFN f = open(TESTFN, 'w') try: f.write('hi there\n') diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index d394ceddd..975715e51 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import os_helper import contextlib import socket @@ -152,7 +153,7 @@ class urlretrieveNetworkTests(unittest.TestCase): try: yield file_location, info finally: - support.unlink(file_location) + os_helper.unlink(file_location) def test_basic(self): # Test basic functionality. @@ -166,8 +167,8 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_specified_path(self): # Make sure that specifying the location of the file to write to works. with self.urlretrieve(self.logo, - support.TESTFN) as (file_location, info): - self.assertEqual(file_location, support.TESTFN) + os_helper.TESTFN) as (file_location, info): + self.assertEqual(file_location, os_helper.TESTFN) self.assertTrue(os.path.exists(file_location)) with open(file_location, 'rb') as f: self.assertTrue(f.read(), "reading from temporary file failed") diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index f1288eb5a..8a137216e 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -5,6 +5,7 @@ Nick Mathewson import unittest from test import support +from test.support import os_helper import os import stat @@ -186,10 +187,10 @@ class UUStdIOTest(unittest.TestCase): class UUFileTest(unittest.TestCase): def setUp(self): - self.tmpin = support.TESTFN + "i" - self.tmpout = support.TESTFN + "o" - self.addCleanup(support.unlink, self.tmpin) - self.addCleanup(support.unlink, self.tmpout) + self.tmpin = os_helper.TESTFN + "i" + self.tmpout = os_helper.TESTFN + "o" + self.addCleanup(os_helper.unlink, self.tmpin) + self.addCleanup(os_helper.unlink, self.tmpout) # TODO: RUSTPYTHON @unittest.expectedFailure diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 669ca82ad..10c39e9ae 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -16,8 +16,8 @@ import subprocess import sys import tempfile from test.support import (captured_stdout, captured_stderr, requires_zlib, - can_symlink, EnvironmentVarGuard, rmtree, import_module) +from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import threading import unittest import venv diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index 7ebde0c37..b6cf1b25b 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py @@ -5,6 +5,7 @@ import sys import subprocess from unittest import mock from test import support +from test.support import os_helper URL = 'http://www.example.com' @@ -320,7 +321,7 @@ class ImportTest(unittest.TestCase): browser = webbrowser.get().name except (webbrowser.Error, AttributeError) as err: self.skipTest(str(err)) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env["BROWSER"] = browser webbrowser = support.import_fresh_module('webbrowser') webbrowser.get() @@ -333,12 +334,12 @@ class ImportTest(unittest.TestCase): except (webbrowser.Error, AttributeError, IndexError) as err: self.skipTest(str(err)) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env["BROWSER"] = least_preferred_browser webbrowser = support.import_fresh_module('webbrowser') self.assertEqual(webbrowser.get().name, least_preferred_browser) - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env["BROWSER"] = sys.executable webbrowser = support.import_fresh_module('webbrowser') self.assertEqual(webbrowser.get().name, sys.executable) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 6b2426d70..9bcbfd1d6 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -24,7 +24,8 @@ import weakref from functools import partial from itertools import product, islice from test import support -from test.support import TESTFN, findfile, import_fresh_module, gc_collect, swap_attr +from test.support import findfile, import_fresh_module, gc_collect, swap_attr, os_helper +from test.support.os_helper import TESTFN # pyET is the pure-Python implementation. # @@ -123,7 +124,7 @@ class ModuleTest(unittest.TestCase): def test_all(self): names = ("xml.etree.ElementTree", "_elementtree") - support.check__all__(self, ET, names, blacklist=("HTML_EMPTY",)) + support.check__all__(self, ET, names, not_exported=("HTML_EMPTY",)) def serialize(elem, to_string=True, encoding='unicode', **options): @@ -649,7 +650,7 @@ class ElementTreeTest(unittest.TestCase): self.assertEqual(str(cm.exception), 'junk after document element: line 1, column 12') - self.addCleanup(support.unlink, TESTFN) + self.addCleanup(os_helper.unlink, TESTFN) with open(TESTFN, "wb") as f: f.write(b"junk") it = iterparse(TESTFN) @@ -3644,7 +3645,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_filename(self): - self.addCleanup(support.unlink, TESTFN) + self.addCleanup(os_helper.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) tree.write(TESTFN) with open(TESTFN, 'rb') as f: @@ -3653,7 +3654,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_text_file(self): - self.addCleanup(support.unlink, TESTFN) + self.addCleanup(os_helper.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) with open(TESTFN, 'w', encoding='utf-8') as f: tree.write(f, encoding='unicode') @@ -3664,7 +3665,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_binary_file(self): - self.addCleanup(support.unlink, TESTFN) + self.addCleanup(os_helper.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) with open(TESTFN, 'wb') as f: tree.write(f) @@ -3675,7 +3676,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_binary_file_with_bom(self): - self.addCleanup(support.unlink, TESTFN) + self.addCleanup(os_helper.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) # test BOM writing to buffered file with open(TESTFN, 'wb') as f: diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 0d88c276a..877e2d2dc 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -15,6 +15,7 @@ import re import io import contextlib from test import support +from test.support import os_helper try: import gzip @@ -1459,7 +1460,7 @@ class CGIHandlerTestCase(unittest.TestCase): self.cgi = None def test_cgi_get(self): - with support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['REQUEST_METHOD'] = 'GET' # if the method is GET and no request_text is given, it runs handle_get # get sysout output @@ -1493,7 +1494,7 @@ class CGIHandlerTestCase(unittest.TestCase): """ - with support.EnvironmentVarGuard() as env, \ + with os_helper.EnvironmentVarGuard() as env, \ captured_stdout(encoding=self.cgi.encoding) as data_out, \ support.captured_stdin() as data_in: data_in.write(data) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 837250fc8..7a4a39774 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -19,9 +19,9 @@ from tempfile import TemporaryFile from random import randint, random, getrandbits from test.support import script_helper -from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir, temp_cwd, - requires_zlib, requires_bz2, requires_lzma, +from test.support import (findfile, requires_zlib, requires_bz2, requires_lzma, captured_stdout) +from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd TESTFN2 = TESTFN + "2" TESTFNDIR = TESTFN + "d" diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 231e6ab21..562827138 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -9,6 +9,7 @@ import unittest import unittest.mock from test import support +from test.support import os_helper from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED @@ -92,7 +93,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): def makeTree(self, files, dirName=TEMP_DIR): # Create a filesystem based set of modules/packages # defined by files under the directory dirName. - self.addCleanup(support.rmtree, dirName) + self.addCleanup(os_helper.rmtree, dirName) for name, (mtime, data) in files.items(): path = os.path.join(dirName, name) @@ -110,7 +111,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): # Create a zip archive based set of modules/packages # defined by files in the zip file zipName. If the # key 'stuff' exists in kw it is prepended to the archive. - self.addCleanup(support.unlink, zipName) + self.addCleanup(os_helper.unlink, zipName) with ZipFile(zipName, "w") as z: for name, (mtime, data) in files.items(): @@ -442,7 +443,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc), "spam" + pyc_ext: (NOW, test_pyc)} - self.addCleanup(support.unlink, TEMP_ZIP) + self.addCleanup(os_helper.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: for name, (mtime, data) in files.items(): zinfo = ZipInfo(name, time.localtime(mtime)) @@ -496,7 +497,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): files = {packdir2 + "__init__" + pyc_ext: (NOW, test_pyc), packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} - self.addCleanup(support.unlink, TEMP_ZIP) + self.addCleanup(os_helper.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: for name, (mtime, data) in files.items(): zinfo = ZipInfo(name, time.localtime(mtime)) @@ -540,7 +541,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): self.assertEqual(loader.get_filename(mod_name), mod.__file__) def testGetData(self): - self.addCleanup(support.unlink, TEMP_ZIP) + self.addCleanup(os_helper.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: z.compression = self.compression name = "testdata.dat" @@ -648,11 +649,11 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): files = {TESTMOD + ".py": (NOW, raise_src)} self.doTest(None, files, TESTMOD, call=self.doTraceback) - @unittest.skipIf(support.TESTFN_UNENCODABLE is None, + @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, "need an unencodable filename") def testUnencodable(self): - filename = support.TESTFN_UNENCODABLE + ".zip" - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(os_helper.unlink, filename) with ZipFile(filename, "w") as z: zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) zinfo.compress_type = self.compression @@ -660,8 +661,8 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): zipimport.zipimporter(filename).load_module(TESTMOD) def testBytesPath(self): - filename = support.TESTFN + ".zip" - self.addCleanup(support.unlink, filename) + filename = os_helper.TESTFN + ".zip" + self.addCleanup(os_helper.unlink, filename) with ZipFile(filename, "w") as z: zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) zinfo.compress_type = self.compression @@ -713,12 +714,12 @@ class BadFileZipImportTestCase(unittest.TestCase): self.assertZipFailure('A' * 33000) def testEmptyFile(self): - support.unlink(TESTMOD) - support.create_empty_file(TESTMOD) + os_helper.unlink(TESTMOD) + os_helper.create_empty_file(TESTMOD) self.assertZipFailure(TESTMOD) def testFileUnreadable(self): - support.unlink(TESTMOD) + os_helper.unlink(TESTMOD) fd = os.open(TESTMOD, os.O_CREAT, 000) try: os.close(fd) @@ -729,10 +730,10 @@ class BadFileZipImportTestCase(unittest.TestCase): # If we leave "the read-only bit" set on Windows, nothing can # delete TESTMOD, and later tests suffer bogus failures. os.chmod(TESTMOD, 0o666) - support.unlink(TESTMOD) + os_helper.unlink(TESTMOD) def testNotZipFile(self): - support.unlink(TESTMOD) + os_helper.unlink(TESTMOD) fp = open(TESTMOD, 'w+') fp.write('a' * 22) fp.close() @@ -740,7 +741,7 @@ class BadFileZipImportTestCase(unittest.TestCase): # XXX: disabled until this works on Big-endian machines def _testBogusZipFile(self): - support.unlink(TESTMOD) + os_helper.unlink(TESTMOD) fp = open(TESTMOD, 'w+') fp.write(struct.pack('=I', 0x06054B50)) fp.write('a' * 18) @@ -775,7 +776,7 @@ def test_main(): BadFileZipImportTestCase, ) finally: - support.unlink(TESTMOD) + os_helper.unlink(TESTMOD) if __name__ == "__main__": test_main() From bd14debebea77a8e3772c0042dec01a2a585c30c Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 13 Nov 2021 03:36:25 +0000 Subject: [PATCH 10/40] test: update test_support --- Lib/test/test_support.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 445b29e5f..6a872d95d 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -642,14 +642,8 @@ class TestSupport(unittest.TestCase): def check_print_warning(self, msg, expected): stderr = io.StringIO() - - old_stderr = sys.__stderr__ - try: - sys.__stderr__ = stderr + with support.swap_attr(support.print_warning, 'orig_stderr', stderr): support.print_warning(msg) - finally: - sys.__stderr__ = old_stderr - self.assertEqual(stderr.getvalue(), expected) def test_print_warning(self): From 2f7e695c4a9ffdc8edce2f094f364f1fd5667ae8 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 13 Nov 2021 03:44:54 +0000 Subject: [PATCH 11/40] test: update test_uu --- Lib/test/test_uu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index 8a137216e..6b0b2f24f 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -4,7 +4,6 @@ Nick Mathewson """ import unittest -from test import support from test.support import os_helper import os @@ -187,8 +186,9 @@ class UUStdIOTest(unittest.TestCase): class UUFileTest(unittest.TestCase): def setUp(self): - self.tmpin = os_helper.TESTFN + "i" - self.tmpout = os_helper.TESTFN + "o" + # uu.encode() supports only ASCII file names + self.tmpin = os_helper.TESTFN_ASCII + "i" + self.tmpout = os_helper.TESTFN_ASCII + "o" self.addCleanup(os_helper.unlink, self.tmpin) self.addCleanup(os_helper.unlink, self.tmpout) From 323cf447880e4054ee0c99f822023ba38c0af340 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 13 Nov 2021 03:51:06 +0000 Subject: [PATCH 12/40] test: update test_urllib --- Lib/test/test_urllib.py | 150 +++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 87 deletions(-) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 9626ef3e6..39f10b6de 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -152,7 +152,8 @@ class urlopen_FileTests(unittest.TestCase): finally: f.close() self.pathname = os_helper.TESTFN - self.returned_obj = urlopen("file:%s" % self.pathname) + self.quoted_pathname = urllib.parse.quote(self.pathname) + self.returned_obj = urlopen("file:%s" % self.quoted_pathname) def tearDown(self): """Shut down the open object""" @@ -195,11 +196,21 @@ class urlopen_FileTests(unittest.TestCase): # by the tearDown() method for the test self.returned_obj.close() + def test_headers(self): + self.assertIsInstance(self.returned_obj.headers, email.message.Message) + + def test_url(self): + self.assertEqual(self.returned_obj.url, self.quoted_pathname) + + @unittest.skip("TODO: RUSTPYTHON (AttributeError: 'BufferedReader' object has no attribute 'status')") + def test_status(self): + self.assertIsNone(self.returned_obj.status) + def test_info(self): self.assertIsInstance(self.returned_obj.info(), email.message.Message) def test_geturl(self): - self.assertEqual(self.returned_obj.geturl(), self.pathname) + self.assertEqual(self.returned_obj.geturl(), self.quoted_pathname) def test_getcode(self): self.assertIsNone(self.returned_obj.getcode()) @@ -609,6 +620,9 @@ class urlopen_DataTests(unittest.TestCase): """Test urlopen() opening a data URL.""" def setUp(self): + # clear _opener global variable + self.addCleanup(urllib.request.urlcleanup) + # text containing URL special- and unicode-characters self.text = "test data URLs :;,%=& \u00f6 \u00c4 " # 2x1 pixel RGB PNG image with one black and one white pixel @@ -683,6 +697,9 @@ class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" def setUp(self): + # clear _opener global variable + self.addCleanup(urllib.request.urlcleanup) + # Create a list of temporary files. Each item in the list is a file # name (absolute path or relative to the current working directory). # All files in this list will be deleted in the tearDown method. Note, @@ -823,6 +840,8 @@ class urlretrieve_HttpTests(unittest.TestCase, FakeHTTPMixin): """Test urllib.urlretrieve() using fake http connections""" def test_short_content_raises_ContentTooShortError(self): + self.addCleanup(urllib.request.urlcleanup) + self.fakehttp(b'''HTTP/1.1 200 OK Date: Wed, 02 Jan 2008 03:03:54 GMT Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e @@ -844,6 +863,8 @@ FF self.unfakehttp() def test_short_content_raises_ContentTooShortError_without_reporthook(self): + self.addCleanup(urllib.request.urlcleanup) + self.fakehttp(b'''HTTP/1.1 200 OK Date: Wed, 02 Jan 2008 03:03:54 GMT Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e @@ -1094,8 +1115,6 @@ class UnquotingTests(unittest.TestCase): "%s" % result) self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, None) self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, ()) - with support.check_warnings(('', BytesWarning), quiet=True): - self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, b'') def test_unquoting_badpercent(self): # Test unquoting on bad percent-escapes @@ -1255,11 +1274,29 @@ class UnquotingTests(unittest.TestCase): self.assertEqual(expect, result, "using unquote(): %r != %r" % (expect, result)) + @unittest.skip("TODO: RUSTPYTHON (TypeError: Expected str, got bytes)") def test_unquoting_with_bytes_input(self): - # Bytes not supported yet - with self.assertRaisesRegex(TypeError, 'Expected str, got bytes'): - given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' - urllib.parse.unquote(given) + # ASCII characters decoded to a string + given = b'blueberryjam' + expect = 'blueberryjam' + result = urllib.parse.unquote(given) + self.assertEqual(expect, result, + "using unquote(): %r != %r" % (expect, result)) + + # A mix of non-ASCII hex-encoded characters and ASCII characters + given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' + expect = 'bl\u00e5b\u00e6rsyltet\u00f8y' + result = urllib.parse.unquote(given) + self.assertEqual(expect, result, + "using unquote(): %r != %r" % (expect, result)) + + # A mix of non-ASCII percent-encoded characters and ASCII characters + given = b'bl%c3%a5b%c3%a6rsyltet%c3%b8j' + expect = 'bl\u00e5b\u00e6rsyltet\u00f8j' + result = urllib.parse.unquote(given) + self.assertEqual(expect, result, + "using unquote(): %r != %r" % (expect, result)) + class urlencode_Tests(unittest.TestCase): """Tests for urlencode()""" @@ -1499,6 +1536,24 @@ class Pathname_Tests(unittest.TestCase): "url2pathname() failed; %s != %s" % (expect, result)) + @unittest.skipUnless(sys.platform == 'win32', + 'test specific to the nturl2path functions.') + def test_prefixes(self): + # Test special prefixes are correctly handled in pathname2url() + given = '\\\\?\\C:\\dir' + expect = '///C:/dir' + result = urllib.request.pathname2url(given) + self.assertEqual(expect, result, + "pathname2url() failed; %s != %s" % + (expect, result)) + given = '\\\\?\\unc\\server\\share\\dir' + expect = '/server/share/dir' + result = urllib.request.pathname2url(given) + self.assertEqual(expect, result, + "pathname2url() failed; %s != %s" % + (expect, result)) + + @unittest.skipUnless(sys.platform == 'win32', 'test specific to the urllib.url2path function.') def test_ntpath(self): @@ -1574,85 +1629,6 @@ class URLopener_Tests(FakeHTTPMixin, unittest.TestCase): self.assertRaises(OSError, DummyURLopener().retrieve, url) -# Just commented them out. -# Can't really tell why keep failing in windows and sparc. -# Everywhere else they work ok, but on those machines, sometimes -# fail in one of the tests, sometimes in other. I have a linux, and -# the tests go ok. -# If anybody has one of the problematic environments, please help! -# . Facundo -# -# def server(evt): -# import socket, time -# serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -# serv.settimeout(3) -# serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -# serv.bind(("", 9093)) -# serv.listen() -# try: -# conn, addr = serv.accept() -# conn.send("1 Hola mundo\n") -# cantdata = 0 -# while cantdata < 13: -# data = conn.recv(13-cantdata) -# cantdata += len(data) -# time.sleep(.3) -# conn.send("2 No more lines\n") -# conn.close() -# except socket.timeout: -# pass -# finally: -# serv.close() -# evt.set() -# -# class FTPWrapperTests(unittest.TestCase): -# -# def setUp(self): -# import ftplib, time, threading -# ftplib.FTP.port = 9093 -# self.evt = threading.Event() -# threading.Thread(target=server, args=(self.evt,)).start() -# time.sleep(.1) -# -# def tearDown(self): -# self.evt.wait() -# -# def testBasic(self): -# # connects -# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) -# ftp.close() -# -# def testTimeoutNone(self): -# # global default timeout is ignored -# import socket -# self.assertIsNone(socket.getdefaulttimeout()) -# socket.setdefaulttimeout(30) -# try: -# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) -# finally: -# socket.setdefaulttimeout(None) -# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) -# ftp.close() -# -# def testTimeoutDefault(self): -# # global default timeout is used -# import socket -# self.assertIsNone(socket.getdefaulttimeout()) -# socket.setdefaulttimeout(30) -# try: -# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) -# finally: -# socket.setdefaulttimeout(None) -# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) -# ftp.close() -# -# def testTimeoutValue(self): -# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [], -# timeout=30) -# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) -# ftp.close() - - class RequestTests(unittest.TestCase): """Unit tests for urllib.request.Request.""" From c15971f18a2f5ae1b97d3d889bb58d13933cd3e6 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 13 Nov 2021 04:24:06 +0000 Subject: [PATCH 13/40] test: update test_unicode_file --- Lib/test/test_unicode_file.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py index d73cb9cf0..80c22c6cd 100644 --- a/Lib/test/test_unicode_file.py +++ b/Lib/test/test_unicode_file.py @@ -2,16 +2,17 @@ # We don't test many operations on files other than # that their names can be used with Unicode characters. import os, glob, time, shutil +import sys import unicodedata import unittest -from test.support import run_unittest from test.support.os_helper import (rmtree, change_cwd, TESTFN_UNICODE, TESTFN_UNENCODABLE, create_empty_file) + if not os.path.supports_unicode_filenames: try: - TESTFN_UNICODE.encode(TESTFN_ENCODING) + TESTFN_UNICODE.encode(sys.getfilesystemencoding()) except (UnicodeError, TypeError): # Either the file system encoding is None, or the file name # cannot be encoded in the file system encoding. @@ -134,8 +135,6 @@ class TestUnicodeFiles(unittest.TestCase): self._do_directory(TESTFN_UNENCODABLE+ext, TESTFN_UNENCODABLE+ext) -def test_main(): - run_unittest(__name__) if __name__ == "__main__": - test_main() + unittest.main() From a1202f56026b7705a1ab9e75b8112cff7c8c3226 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 13 Nov 2021 14:21:59 +0800 Subject: [PATCH 14/40] test: update importlib --- .../test_importlib/source/test_case_sensitivity.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py index 12ce0cb93..f7e277390 100644 --- a/Lib/test/test_importlib/source/test_case_sensitivity.py +++ b/Lib/test/test_importlib/source/test_case_sensitivity.py @@ -1,12 +1,15 @@ """Test case-sensitivity (PEP 235).""" +import sys + from .. import util importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') import os -from test import support as test_support +from test.support import os_helper import unittest +import warnings @util.case_insensitive_tests @@ -38,8 +41,9 @@ class CaseSensitivityTest(util.CASEOKTestBase): insensitive_finder = self.finder(insensitive_path) return self.find(sensitive_finder), self.find(insensitive_finder) + @unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set') def test_sensitive(self): - with test_support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.unset('PYTHONCASEOK') self.caseok_env_changed(should_exist=False) sensitive, insensitive = self.sensitivity_test() @@ -47,8 +51,9 @@ class CaseSensitivityTest(util.CASEOKTestBase): self.assertIn(self.name, sensitive.get_filename(self.name)) self.assertIsNone(insensitive) + @unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set') def test_insensitive(self): - with test_support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.set('PYTHONCASEOK', '1') self.caseok_env_changed(should_exist=True) sensitive, insensitive = self.sensitivity_test() @@ -60,6 +65,8 @@ class CaseSensitivityTest(util.CASEOKTestBase): class CaseSensitivityTestPEP302(CaseSensitivityTest): def find(self, finder): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) return finder.find_module(self.name) From 93a7d59bbb08729f2deb32ea6e4efd72fdc59c2c Mon Sep 17 00:00:00 2001 From: Jeong Yunwon Date: Mon, 15 Nov 2021 18:44:14 +0900 Subject: [PATCH 15/40] relocate wrongly placed sys test in snippets/stdlib_os.py --- extra_tests/snippets/stdlib_os.py | 23 ---------------------- extra_tests/snippets/stdlib_sys.py | 31 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/extra_tests/snippets/stdlib_os.py b/extra_tests/snippets/stdlib_os.py index def45bfaa..0b75f78cc 100644 --- a/extra_tests/snippets/stdlib_os.py +++ b/extra_tests/snippets/stdlib_os.py @@ -505,26 +505,3 @@ if "win" not in sys.platform: for arg in [None, 1, 1.0, TabError]: assert_raises(TypeError, os.system, arg) - -if sys.platform.startswith("win"): - winver = sys.getwindowsversion() - - # the biggest value of wSuiteMask (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa#members). - all_masks = 0x00000004 | 0x00000400 | 0x00004000 | 0x00000080 | 0x00000002 | 0x00000040 | 0x00000200 | \ - 0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000 - - # We really can't test if the results are correct, so it just checks for meaningful value - assert winver.major > 0 - assert winver.minor >= 0 - assert winver.build > 0 - assert winver.platform == 2 - assert isinstance(winver.service_pack, str) - assert 0 <= winver.suite_mask <= all_masks - assert 1 <= winver.product_type <= 3 - - # XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest - assert winver.major == winver.platform_version[0] - assert winver.minor == winver.platform_version[1] - assert winver.build == winver.platform_version[2] - - diff --git a/extra_tests/snippets/stdlib_sys.py b/extra_tests/snippets/stdlib_sys.py index 53df9bf60..25e3428c5 100644 --- a/extra_tests/snippets/stdlib_sys.py +++ b/extra_tests/snippets/stdlib_sys.py @@ -51,11 +51,11 @@ assert ("demo", "call", None) in events assert sys.exc_info() == (None, None, None) try: - 1/0 + 1/0 except ZeroDivisionError as exc: - exc_info = sys.exc_info() - assert exc_info[0] == type(exc) == ZeroDivisionError - assert exc_info[1] == exc + exc_info = sys.exc_info() + assert exc_info[0] == type(exc) == ZeroDivisionError + assert exc_info[1] == exc # Recursion: @@ -69,3 +69,26 @@ assert sys.getrecursionlimit() == 200 with assert_raises(RecursionError): recursive_call(300) + +if sys.platform.startswith("win"): + winver = sys.getwindowsversion() + print(f'winver: {winver} {winver.platform_version}') + + # the biggest value of wSuiteMask (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa#members). + all_masks = 0x00000004 | 0x00000400 | 0x00004000 | 0x00000080 | 0x00000002 | 0x00000040 | 0x00000200 | \ + 0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000 + + # We really can't test if the results are correct, so it just checks for meaningful value + assert winver.major > 0 + assert winver.minor >= 0 + assert winver.build > 0 + assert winver.platform == 2 + assert isinstance(winver.service_pack, str) + assert 0 <= winver.suite_mask <= all_masks + assert 1 <= winver.product_type <= 3 + + # XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest + # these fields can mismatch in CPython + # assert winver.major == winver.platform_version[0] + # assert winver.minor == winver.platform_version[1] + # assert winver.build == winver.platform_version[2] From e42a3ca48ed5b4f81355aef80ac717c84f8861c1 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Wed, 27 Oct 2021 20:13:02 -0500 Subject: [PATCH 16/40] Big overhaul part 1 - replace PyRc with manual RefCount + WeakRefList --- Cargo.lock | 1 + common/src/hash.rs | 2 +- common/src/lib.rs | 2 + common/src/linked_list.rs | 377 +++++++++++++++++++++ common/src/refcount.rs | 52 +++ derive/src/pyvalue.rs | 3 +- src/shell/helper.rs | 3 +- stdlib/src/ssl.rs | 17 +- vm/Cargo.toml | 1 + vm/src/builtins/function.rs | 8 +- vm/src/builtins/module.rs | 131 ++++---- vm/src/builtins/namespace.rs | 37 ++- vm/src/builtins/object.rs | 12 +- vm/src/builtins/pystr.rs | 11 +- vm/src/builtins/pytype.rs | 20 +- vm/src/builtins/weakproxy.rs | 12 +- vm/src/builtins/weakref.rs | 61 ++-- vm/src/pyobject.rs | 1 + vm/src/pyobjectrc.rs | 615 +++++++++++++++++++++++++++-------- vm/src/scope.rs | 2 +- vm/src/stdlib/builtins.rs | 2 +- vm/src/stdlib/itertools.rs | 2 +- vm/src/stdlib/weakref.rs | 2 +- vm/src/types/slot.rs | 2 + vm/src/types/zoo.rs | 6 +- vm/src/vm.rs | 57 ++-- wasm/lib/src/convert.rs | 2 +- wasm/lib/src/js_module.rs | 12 +- wasm/lib/src/vm_class.rs | 10 +- 29 files changed, 1125 insertions(+), 338 deletions(-) create mode 100644 common/src/linked_list.rs create mode 100644 common/src/refcount.rs diff --git a/Cargo.lock b/Cargo.lock index 762db6df6..1977f5085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1954,6 +1954,7 @@ dependencies = [ "libc", "log", "memchr", + "memoffset", "nix 0.23.0", "num-bigint", "num-complex", diff --git a/common/src/hash.rs b/common/src/hash.rs index 20dabf127..e3ed7d1b8 100644 --- a/common/src/hash.rs +++ b/common/src/hash.rs @@ -169,7 +169,7 @@ pub fn hash_bigint(value: &BigInt) -> PyHash { } #[inline(always)] -fn fix_sentinel(x: PyHash) -> PyHash { +pub fn fix_sentinel(x: PyHash) -> PyHash { if x == SENTINEL { -2 } else { diff --git a/common/src/lib.rs b/common/src/lib.rs index 0bd2b2c36..348733b3e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -9,8 +9,10 @@ pub mod cmp; pub mod encodings; pub mod float_ops; pub mod hash; +pub mod linked_list; pub mod lock; pub mod rc; +pub mod refcount; pub mod static_cell; pub mod str; diff --git a/common/src/linked_list.rs b/common/src/linked_list.rs new file mode 100644 index 000000000..0f4013755 --- /dev/null +++ b/common/src/linked_list.rs @@ -0,0 +1,377 @@ +//! This module is modified from tokio::util::linked_list: https://github.com/tokio-rs/tokio/blob/master/tokio/src/util/linked_list.rs +//! Tokio is licensed under the MIT license: +//! +//! Copyright (c) 2021 Tokio Contributors +//! +//! Permission is hereby granted, free of charge, to any +//! person obtaining a copy of this software and associated +//! documentation files (the "Software"), to deal in the +//! Software without restriction, including without +//! limitation the rights to use, copy, modify, merge, +//! publish, distribute, sublicense, and/or sell copies of +//! the Software, and to permit persons to whom the Software +//! is furnished to do so, subject to the following +//! conditions: +//! +//! The above copyright notice and this permission notice +//! shall be included in all copies or substantial portions +//! of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +//! DEALINGS IN THE SOFTWARE. +//! +//! Original header: +//! +//! An intrusive double linked list of data. +//! +//! The data structure supports tracking pinned nodes. Most of the data +//! structure's APIs are `unsafe` as they require the caller to ensure the +//! specified node is actually contained by the list. + +#![allow(clippy::new_without_default, clippy::missing_safety_doc)] + +use core::cell::UnsafeCell; +use core::fmt; +use core::marker::{PhantomData, PhantomPinned}; +use core::mem::ManuallyDrop; +use core::ptr::{self, NonNull}; + +/// An intrusive linked list. +/// +/// Currently, the list is not emptied on drop. It is the caller's +/// responsibility to ensure the list is empty before dropping it. +pub struct LinkedList { + /// Linked list head + head: Option>, + + /// Linked list tail + tail: Option>, + + /// Node type marker. + _marker: PhantomData<*const L>, +} + +unsafe impl Send for LinkedList where L::Target: Send {} +unsafe impl Sync for LinkedList where L::Target: Sync {} + +/// Defines how a type is tracked within a linked list. +/// +/// In order to support storing a single type within multiple lists, accessing +/// the list pointers is decoupled from the entry type. +/// +/// # Safety +/// +/// Implementations must guarantee that `Target` types are pinned in memory. In +/// other words, when a node is inserted, the value will not be moved as long as +/// it is stored in the list. +pub unsafe trait Link { + /// Handle to the list entry. + /// + /// This is usually a pointer-ish type. + type Handle; + + /// Node type. + type Target; + + /// Convert the handle to a raw pointer without consuming the handle. + #[allow(clippy::wrong_self_convention)] + fn as_raw(handle: &Self::Handle) -> NonNull; + + /// Convert the raw pointer to a handle + unsafe fn from_raw(ptr: NonNull) -> Self::Handle; + + /// Return the pointers for a node + unsafe fn pointers(target: NonNull) -> NonNull>; +} + +/// Previous / next pointers. +pub struct Pointers { + inner: UnsafeCell>, +} +/// We do not want the compiler to put the `noalias` attribute on mutable +/// references to this type, so the type has been made `!Unpin` with a +/// `PhantomPinned` field. +/// +/// Additionally, we never access the `prev` or `next` fields directly, as any +/// such access would implicitly involve the creation of a reference to the +/// field, which we want to avoid since the fields are not `!Unpin`, and would +/// hence be given the `noalias` attribute if we were to do such an access. +/// As an alternative to accessing the fields directly, the `Pointers` type +/// provides getters and setters for the two fields, and those are implemented +/// using raw pointer casts and offsets, which is valid since the struct is +/// #[repr(C)]. +/// +/// See this link for more information: +/// +#[repr(C)] +struct PointersInner { + /// The previous node in the list. null if there is no previous node. + /// + /// This field is accessed through pointer manipulation, so it is not dead code. + #[allow(dead_code)] + prev: Option>, + + /// The next node in the list. null if there is no previous node. + /// + /// This field is accessed through pointer manipulation, so it is not dead code. + #[allow(dead_code)] + next: Option>, + + /// This type is !Unpin due to the heuristic from: + /// + _pin: PhantomPinned, +} + +unsafe impl Send for Pointers {} +unsafe impl Sync for Pointers {} + +// ===== impl LinkedList ===== + +impl LinkedList { + /// Creates an empty linked list. + pub const fn new() -> LinkedList { + LinkedList { + head: None, + tail: None, + _marker: PhantomData, + } + } +} + +impl LinkedList { + /// Adds an element first in the list. + pub fn push_front(&mut self, val: L::Handle) { + // The value should not be dropped, it is being inserted into the list + let val = ManuallyDrop::new(val); + let ptr = L::as_raw(&*val); + assert_ne!(self.head, Some(ptr)); + unsafe { + L::pointers(ptr).as_mut().set_next(self.head); + L::pointers(ptr).as_mut().set_prev(None); + + if let Some(head) = self.head { + L::pointers(head).as_mut().set_prev(Some(ptr)); + } + + self.head = Some(ptr); + + if self.tail.is_none() { + self.tail = Some(ptr); + } + } + } + + /// Removes the last element from a list and returns it, or None if it is + /// empty. + pub fn pop_back(&mut self) -> Option { + unsafe { + let last = self.tail?; + self.tail = L::pointers(last).as_ref().get_prev(); + + if let Some(prev) = L::pointers(last).as_ref().get_prev() { + L::pointers(prev).as_mut().set_next(None); + } else { + self.head = None + } + + L::pointers(last).as_mut().set_prev(None); + L::pointers(last).as_mut().set_next(None); + + Some(L::from_raw(last)) + } + } + + /// Returns whether the linked list does not contain any node + pub fn is_empty(&self) -> bool { + if self.head.is_some() { + return false; + } + + assert!(self.tail.is_none()); + true + } + + /// Removes the specified node from the list + /// + /// # Safety + /// + /// The caller **must** ensure that `node` is currently contained by + /// `self` or not contained by any other list. + pub unsafe fn remove(&mut self, node: NonNull) -> Option { + if let Some(prev) = L::pointers(node).as_ref().get_prev() { + debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node)); + L::pointers(prev) + .as_mut() + .set_next(L::pointers(node).as_ref().get_next()); + } else { + if self.head != Some(node) { + return None; + } + + self.head = L::pointers(node).as_ref().get_next(); + } + + if let Some(next) = L::pointers(node).as_ref().get_next() { + debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node)); + L::pointers(next) + .as_mut() + .set_prev(L::pointers(node).as_ref().get_prev()); + } else { + // This might be the last item in the list + if self.tail != Some(node) { + return None; + } + + self.tail = L::pointers(node).as_ref().get_prev(); + } + + L::pointers(node).as_mut().set_next(None); + L::pointers(node).as_mut().set_prev(None); + + Some(L::from_raw(node)) + } + + pub fn last(&self) -> Option<&L::Target> { + let tail = self.tail.as_ref()?; + unsafe { Some(&*tail.as_ptr()) } + } + + pub fn count(&self) -> usize { + std::iter::successors(self.head, |node| unsafe { + L::pointers(*node).as_ref().get_next() + }) + .count() + } +} + +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("LinkedList") + .field("head", &self.head) + .field("tail", &self.tail) + .finish() + } +} + +impl Default for LinkedList { + fn default() -> Self { + Self::new() + } +} + +// ===== impl DrainFilter ===== + +pub struct DrainFilter<'a, T: Link, F> { + list: &'a mut LinkedList, + filter: F, + curr: Option>, +} + +impl LinkedList { + pub fn drain_filter(&mut self, filter: F) -> DrainFilter<'_, T, F> + where + F: FnMut(&mut T::Target) -> bool, + { + let curr = self.head; + DrainFilter { + curr, + filter, + list: self, + } + } +} + +impl<'a, T, F> Iterator for DrainFilter<'a, T, F> +where + T: Link, + F: FnMut(&mut T::Target) -> bool, +{ + type Item = T::Handle; + + fn next(&mut self) -> Option { + while let Some(curr) = self.curr { + // safety: the pointer references data contained by the list + self.curr = unsafe { T::pointers(curr).as_ref() }.get_next(); + + // safety: the value is still owned by the linked list. + if (self.filter)(unsafe { &mut *curr.as_ptr() }) { + return unsafe { self.list.remove(curr) }; + } + } + + None + } +} + +// ===== impl Pointers ===== + +impl Pointers { + /// Create a new set of empty pointers + pub fn new() -> Pointers { + Pointers { + inner: UnsafeCell::new(PointersInner { + prev: None, + next: None, + _pin: PhantomPinned, + }), + } + } + + pub fn is_null(&self) -> bool { + self.get_prev().is_none() && self.get_next().is_none() + } + + fn get_prev(&self) -> Option> { + // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. + unsafe { + let inner = self.inner.get(); + let prev = inner as *const Option>; + ptr::read(prev) + } + } + fn get_next(&self) -> Option> { + // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. + unsafe { + let inner = self.inner.get(); + let prev = inner as *const Option>; + let next = prev.add(1); + ptr::read(next) + } + } + + fn set_prev(&mut self, value: Option>) { + // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. + unsafe { + let inner = self.inner.get(); + let prev = inner as *mut Option>; + ptr::write(prev, value); + } + } + fn set_next(&mut self, value: Option>) { + // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. + unsafe { + let inner = self.inner.get(); + let prev = inner as *mut Option>; + let next = prev.add(1); + ptr::write(next, value); + } + } +} + +impl fmt::Debug for Pointers { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let prev = self.get_prev(); + let next = self.get_next(); + f.debug_struct("Pointers") + .field("prev", &prev) + .field("next", &next) + .finish() + } +} diff --git a/common/src/refcount.rs b/common/src/refcount.rs new file mode 100644 index 000000000..bf6705b13 --- /dev/null +++ b/common/src/refcount.rs @@ -0,0 +1,52 @@ +use crate::atomic::{Ordering::*, PyAtomic, Radium}; + +/// from alloc::sync +/// A soft limit on the amount of references that may be made to an `Arc`. +/// +/// Going above this limit will abort your program (although not +/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. +const MAX_REFCOUNT: usize = (isize::MAX) as usize; + +pub struct RefCount { + strong: PyAtomic, +} + +impl Default for RefCount { + fn default() -> Self { + Self::new() + } +} + +impl RefCount { + pub fn new() -> Self { + RefCount { + strong: Radium::new(1), + } + } + + #[inline] + pub fn get(&self) -> usize { + self.strong.load(SeqCst) + } + + #[inline] + pub fn incref(&self) { + let old_size = self.strong.fetch_add(1, Relaxed); + + if old_size > MAX_REFCOUNT { + std::process::abort(); + } + } + + /// Decrement the reference count. Returns true when the refcount drops to 0. + #[inline] + pub fn decref(&self) -> bool { + if self.strong.fetch_sub(1, Release) != 1 { + return false; + } + + PyAtomic::::fence(Acquire); + + true + } +} diff --git a/derive/src/pyvalue.rs b/derive/src/pyvalue.rs index 64878f071..23636e511 100644 --- a/derive/src/pyvalue.rs +++ b/derive/src/pyvalue.rs @@ -8,8 +8,7 @@ pub(crate) fn impl_pyvalue(input: DeriveInput) -> Result { let ret = quote! { impl ::rustpython_vm::PyValue for #ty { fn class(_vm: &::rustpython_vm::VirtualMachine) -> &rustpython_vm::builtins::PyTypeRef { - use ::rustpython_vm::StaticType; - Self::static_type() + ::static_type() } } }; diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 941e9e204..360ae6057 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -90,7 +90,8 @@ impl<'vm> ShellHelper<'vm> { // we need to get a variable based off of globals/builtins let globals = str_iter_method(self.globals.as_object().to_owned(), "keys").ok()?; - let builtins = str_iter_method(self.vm.builtins.clone(), "__dir__").ok()?; + let builtins = + str_iter_method(self.vm.builtins.as_object().to_owned(), "__dir__").ok()?; (first, globals, Some(builtins)) }; Some((word_start, iter1.chain(iter2.into_iter().flatten()))) diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index 5230ba865..ad5578b1d 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -30,7 +30,7 @@ mod _ssl { }, socket::{self, PySocket}, vm::{ - builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef, PyWeak}, + builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef}, exceptions, function::{ ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, IntoPyException, @@ -39,7 +39,7 @@ mod _ssl { stdlib::os::PyPathLike, types::Constructor, utils::{Either, ToCString}, - ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, + ItemProtocol, PyObjectRef, PyObjectWeak, PyRef, PyResult, PyValue, VirtualMachine, }, }; use crossbeam_utils::atomic::AtomicCell; @@ -773,7 +773,7 @@ mod _ssl { stream: PyRwLock::new(stream), socket_type, server_hostname: args.server_hostname, - owner: PyRwLock::new(args.owner.as_ref().map(|o| PyWeak::downgrade(o))), + owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?), }) } } @@ -890,7 +890,7 @@ mod _ssl { stream: PyRwLock>, socket_type: SslServerOrClient, server_hostname: Option, - owner: PyRwLock>, + owner: PyRwLock>, } impl fmt::Debug for PySslSocket { @@ -903,11 +903,14 @@ mod _ssl { impl PySslSocket { #[pyproperty] fn owner(&self) -> Option { - self.owner.read().as_ref().and_then(PyWeak::upgrade) + self.owner.read().as_ref().and_then(|weak| weak.upgrade()) } #[pyproperty(setter)] - fn set_owner(&self, owner: PyObjectRef) { - *self.owner.write() = Some(PyWeak::downgrade(&owner)) + fn set_owner(&self, owner: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let mut lock = self.owner.write(); + lock.take(); + *lock = Some(owner.downgrade(None, vm)?); + Ok(()) } #[pyproperty] fn server_side(&self) -> bool { diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 914baf292..30c3d79bb 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -68,6 +68,7 @@ memchr = "2" adler32 = "1.0.3" flate2 = "1.0.20" once_cell = "1" +memoffset = "0.6" # RustPython crates implementing functionality based on CPython sre-engine = "0.1.2" diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index 766bf5b36..e579b7f69 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -5,9 +5,9 @@ use super::{ tuple::PyTupleTyped, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStrRef, PyTupleRef, PyTypeRef, }; +use crate::common::lock::PyMutex; use crate::{ bytecode, - common::lock::PyMutex, frame::Frame, function::{FuncArgs, OptionalArg}, protocol::PyMapping, @@ -136,10 +136,10 @@ impl PyFunction { ); } *slot = Some(value); - } else if argpos(0..code.posonlyarg_count, &name).is_some() { - posonly_passed_as_kwarg.push(name); } else if let Some(kwargs) = kwargs.as_ref() { kwargs.set_item(name, value, vm)?; + } else if argpos(0..code.posonlyarg_count, &name).is_some() { + posonly_passed_as_kwarg.push(name); } else { return Err( vm.new_type_error(format!("got an unexpected keyword argument '{}'", name)) @@ -299,7 +299,7 @@ impl PyFunction { let frame = Frame::new( code.clone(), Scope::new(Some(locals), self.globals.clone()), - vm.builtins.dict().unwrap(), + vm.builtins.dict(), self.closure.as_ref().map_or(&[], |c| c.as_slice()), vm, ) diff --git a/vm/src/builtins/module.rs b/vm/src/builtins/module.rs index a2300b271..79a9399e3 100644 --- a/vm/src/builtins/module.rs +++ b/vm/src/builtins/module.rs @@ -1,13 +1,15 @@ +use super::pystr::IntoPyStrRef; use super::{PyDictRef, PyStr, PyStrRef, PyTypeRef}; use crate::{ function::{FuncArgs, IntoPyObject}, types::GetAttr, - ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, + ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, + VirtualMachine, }; #[pyclass(module = false, name = "module")] #[derive(Debug)] -pub struct PyModule; +pub struct PyModule {} impl PyValue for PyModule { fn class(vm: &VirtualMachine) -> &PyTypeRef { @@ -15,29 +17,6 @@ impl PyValue for PyModule { } } -pub fn init_module_dict( - vm: &VirtualMachine, - module_dict: &PyDictRef, - name: PyObjectRef, - doc: PyObjectRef, -) { - module_dict - .set_item("__name__", name, vm) - .expect("Failed to set __name__ on module"); - module_dict - .set_item("__doc__", doc, vm) - .expect("Failed to set __doc__ on module"); - module_dict - .set_item("__package__", vm.ctx.none(), vm) - .expect("Failed to set __package__ on module"); - module_dict - .set_item("__loader__", vm.ctx.none(), vm) - .expect("Failed to set __loader__ on module"); - module_dict - .set_item("__spec__", vm.ctx.none(), vm) - .expect("Failed to set __spec__ on module"); -} - #[derive(FromArgs)] struct ModuleInitArgs { name: PyStrRef, @@ -47,6 +26,15 @@ struct ModuleInitArgs { #[pyimpl(with(GetAttr), flags(BASETYPE, HAS_DICT))] impl PyModule { + // pub(crate) fn new(d: PyDictRef) -> Self { + // PyModule { dict: d.into() } + // } + + // #[inline] + // pub fn dict(&self) -> PyDictRef { + // self.dict.get() + // } + #[pyslot] fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { PyModule {}.into_pyresult_with_type(vm, cls) @@ -58,22 +46,30 @@ impl PyModule { .slots .flags .has_feature(crate::types::PyTypeFlags::HAS_DICT)); - init_module_dict( - vm, - &zelf.as_object().dict().unwrap(), - args.name.into(), - args.doc.into_pyobject(vm), - ); + zelf.init_module_dict(args.name.into(), args.doc.into_pyobject(vm), vm); } - fn name(zelf: PyRef, vm: &VirtualMachine) -> Option { - vm.generic_getattribute_opt( - zelf.as_object().to_owned(), - PyStr::from("__name__").into_ref(vm), - None, - ) - .unwrap_or(None) - .and_then(|obj| obj.payload::().map(|s| s.as_str().to_owned())) + fn getattr_inner(zelf: &PyObjectView, name: PyStrRef, vm: &VirtualMachine) -> PyResult { + if let Some(attr) = + vm.generic_getattribute_opt(zelf.as_object().to_owned(), name.clone(), None)? + { + return Ok(attr); + } + if let Ok(getattr) = zelf.dict().get_item("__getattr__", vm) { + return vm.invoke(&getattr, (name,)); + } + let module_name = if let Some(name) = Self::name(zelf.to_owned(), vm) { + format!(" '{}'", name) + } else { + "".to_owned() + }; + Err(vm.new_attribute_error(format!("module{} has no attribute '{}'", module_name, name))) + } + + fn name(zelf: PyRef, vm: &VirtualMachine) -> Option { + vm.generic_getattribute_opt(zelf.into(), PyStr::from("__name__").into_ref(vm), None) + .unwrap_or(None) + .and_then(|obj| obj.downcast::().ok()) } #[pymethod(magic)] @@ -94,26 +90,47 @@ impl PyModule { } } +impl PyObjectView { + // TODO: to be replaced by the commented-out dict method above once dictoffsets land + pub fn dict(&self) -> PyDictRef { + self.as_object().dict().unwrap() + } + // TODO: should be on PyModule, not PyObjectView + pub(crate) fn init_module_dict( + &self, + name: PyObjectRef, + doc: PyObjectRef, + vm: &VirtualMachine, + ) { + let dict = self.dict(); + dict.set_item("__name__", name, vm) + .expect("Failed to set __name__ on module"); + dict.set_item("__doc__", doc, vm) + .expect("Failed to set __doc__ on module"); + dict.set_item("__package__", vm.ctx.none(), vm) + .expect("Failed to set __package__ on module"); + dict.set_item("__loader__", vm.ctx.none(), vm) + .expect("Failed to set __loader__ on module"); + dict.set_item("__spec__", vm.ctx.none(), vm) + .expect("Failed to set __spec__ on module"); + } + + pub fn get_attr(&self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult { + PyModule::getattr_inner(self, attr_name.into_pystr_ref(vm), vm) + } + pub fn set_attr( + &self, + attr_name: impl IntoPyStrRef, + attr_value: impl Into, + vm: &VirtualMachine, + ) -> PyResult<()> { + self.as_object().set_attr(attr_name, attr_value, vm) + } +} + impl GetAttr for PyModule { fn getattro(zelf: PyRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult { - if let Some(attr) = - vm.generic_getattribute_opt(zelf.as_object().to_owned(), name.clone(), None)? - { - return Ok(attr); - } - if let Some(getattr) = zelf - .as_object() - .dict() - .and_then(|d| d.get_item("__getattr__", vm).ok()) - { - return vm.invoke(&getattr, (name,)); - } - let module_name = if let Some(name) = Self::name(zelf, vm) { - format!(" '{}'", name) - } else { - "".to_owned() - }; - Err(vm.new_attribute_error(format!("module{} has no attribute '{}'", module_name, name))) + Self::getattr_inner(&zelf, name, vm) } } diff --git a/vm/src/builtins/namespace.rs b/vm/src/builtins/namespace.rs index 77a5e4c07..8e7443e5d 100644 --- a/vm/src/builtins/namespace.rs +++ b/vm/src/builtins/namespace.rs @@ -13,7 +13,7 @@ use crate::{ /// SimpleNamespace(**kwargs) #[pyclass(module = false, name = "SimpleNamespace")] #[derive(Debug)] -pub struct PyNamespace; +pub struct PyNamespace {} impl PyValue for PyNamespace { fn class(vm: &VirtualMachine) -> &PyTypeRef { @@ -31,7 +31,11 @@ impl Constructor for PyNamespace { impl PyNamespace { pub fn new_ref(ctx: &PyContext) -> PyRef { - PyRef::new_ref(Self, ctx.types.namespace_type.clone(), Some(ctx.new_dict())) + PyRef::new_ref( + Self {}, + ctx.types.namespace_type.clone(), + Some(ctx.new_dict()), + ) } } @@ -58,18 +62,14 @@ impl PyNamespace { }; let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let parts = if let Some(dict) = zelf.as_object().dict() { - let mut parts = Vec::with_capacity(dict.len()); - for (key, value) in dict { - let k = &key.repr(vm)?; - let key_str = k.as_str(); - let value_repr = value.repr(vm)?; - parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr)); - } - parts - } else { - vec![] - }; + let dict = zelf.as_object().dict().unwrap(); + let mut parts = Vec::with_capacity(dict.len()); + for (key, value) in dict { + let k = &key.repr(vm)?; + let key_str = k.as_str(); + let value_repr = value.repr(vm)?; + parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr)); + } format!("{}({})", name, parts.join(", ")) } else { format!("{}(...)", name) @@ -86,10 +86,11 @@ impl Comparable for PyNamespace { vm: &VirtualMachine, ) -> PyResult { let other = class_or_notimplemented!(Self, other); - match (zelf.as_object().dict(), other.as_object().dict()) { - (Some(d1), Some(d2)) => PyDict::cmp(&d1, d2.as_object(), op, vm), - _ => Ok(PyComparisonValue::NotImplemented), - } + let (d1, d2) = ( + zelf.as_object().dict().unwrap(), + other.as_object().dict().unwrap(), + ); + PyDict::cmp(&d1, d2.as_object(), op, vm) } } diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 260e40c89..8467bcd10 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -148,13 +148,13 @@ impl PyBaseObject { value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - setattr(&obj, name, Some(value), vm) + generic_setattr(&obj, name, Some(value), vm) } /// Implement delattr(self, name). #[pymethod] fn __delattr__(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { - setattr(&obj, name, None, vm) + generic_setattr(&obj, name, None, vm) } #[pyslot] @@ -164,7 +164,7 @@ impl PyBaseObject { value: Option, vm: &VirtualMachine, ) -> PyResult<()> { - setattr(&*obj, attr_name, value, vm) + generic_setattr(&*obj, attr_name, value, vm) } /// Return str(self). @@ -316,8 +316,12 @@ pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) - .map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned())) } +pub fn generic_getattr(obj: PyObjectRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult { + vm.generic_getattribute(obj, attr_name) +} + #[cfg_attr(feature = "flame-it", flame)] -pub(crate) fn setattr( +pub fn generic_setattr( obj: &PyObject, attr_name: PyStrRef, value: Option, diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index 9f0d1d436..e100a22b9 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -89,13 +89,22 @@ impl PyStrKindData { /// encoding defaults to sys.getdefaultencoding(). /// errors defaults to 'strict'." #[pyclass(module = false, name = "str")] -#[derive(Debug)] pub struct PyStr { bytes: Box<[u8]>, kind: PyStrKindData, hash: PyAtomic, } +impl fmt::Debug for PyStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PyStr") + .field("value", &self.as_str()) + .field("kind", &self.kind) + .field("hash", &self.hash) + .finish() + } +} + impl AsRef for PyStr { fn as_ref(&self) -> &str { self.as_str() diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index 867a6c587..b6e249595 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -1,6 +1,6 @@ use super::{ mappingproxy::PyMappingProxy, object, PyClassMethod, PyDictRef, PyList, PyStaticMethod, PyStr, - PyStrRef, PyTuple, PyTupleRef, PyWeak, + PyStrRef, PyTuple, PyTupleRef, }; use crate::common::{ ascii, @@ -10,8 +10,8 @@ use crate::common::{ use crate::{ function::{FuncArgs, KwArgs, OptionalArg}, types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr}, - IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyRef, PyResult, - PyValue, StaticType, TypeProtocol, VirtualMachine, + IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyObjectWeak, PyRef, + PyResult, PyValue, StaticType, TypeProtocol, VirtualMachine, }; use itertools::Itertools; use std::collections::HashSet; @@ -26,7 +26,7 @@ pub struct PyType { pub base: Option, pub bases: Vec, pub mro: Vec, - pub subclasses: PyRwLock>, + pub subclasses: PyRwLock>, pub attributes: PyRwLock, pub slots: PyTypeSlots, } @@ -96,7 +96,7 @@ impl PyType { slots.flags |= PyTypeFlags::HAS_DICT } - *slots.name.write() = Some(String::from(name)); + *slots.name.get_mut() = Some(String::from(name)); let new_type = PyRef::new_ref( PyType { @@ -116,10 +116,14 @@ impl PyType { new_type.update_slot(attr_name, true); } } + let weakref_type = super::PyWeak::static_type(); for base in &new_type.bases { - base.subclasses - .write() - .push(PyWeak::downgrade(new_type.as_object())); + base.subclasses.write().push( + new_type + .as_object() + .downgrade_with_typ_opt(None, weakref_type.clone()) + .unwrap(), + ); } Ok(new_type) diff --git a/vm/src/builtins/weakproxy.rs b/vm/src/builtins/weakproxy.rs index e7446ad65..f658a3893 100644 --- a/vm/src/builtins/weakproxy.rs +++ b/vm/src/builtins/weakproxy.rs @@ -1,14 +1,14 @@ -use super::{PyStrRef, PyTypeRef, PyWeak}; +use super::{PyStrRef, PyTypeRef}; use crate::{ function::OptionalArg, types::{Constructor, SetAttr}, - PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, VirtualMachine, + PyClassImpl, PyContext, PyObjectRef, PyObjectWeak, PyResult, PyValue, VirtualMachine, }; #[pyclass(module = false, name = "weakproxy")] #[derive(Debug)] pub struct PyWeakProxy { - weak: PyWeak, + weak: PyObjectWeak, } impl PyValue for PyWeakProxy { @@ -33,11 +33,9 @@ impl Constructor for PyWeakProxy { Self::Args { referent, callback }: Self::Args, vm: &VirtualMachine, ) -> PyResult { - if callback.is_present() { - panic!("Passed a callback to weakproxy, but weakproxy does not yet support proxies."); - } + // TODO: PyWeakProxy should use the same payload as PyWeak PyWeakProxy { - weak: PyWeak::downgrade(&referent), + weak: referent.downgrade(callback.into_option(), vm)?, } .into_pyresult_with_type(vm, cls) } diff --git a/vm/src/builtins/weakref.rs b/vm/src/builtins/weakref.rs index 5b80f301c..df600ce64 100644 --- a/vm/src/builtins/weakref.rs +++ b/vm/src/builtins/weakref.rs @@ -1,40 +1,21 @@ use super::{PyGenericAlias, PyTypeRef}; -use crate::common::hash::PyHash; +use crate::common::atomic::{Ordering, Radium}; +use crate::common::hash::{self, PyHash}; use crate::{ function::OptionalArg, types::{Callable, Comparable, Constructor, Hashable, PyComparisonOp}, - IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyObjectWeak, PyRef, PyResult, - PyValue, TypeProtocol, VirtualMachine, + IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, VirtualMachine, }; -use crossbeam_utils::atomic::AtomicCell; - -#[pyclass(module = false, name = "weakref")] -#[derive(Debug)] -pub struct PyWeak { - referent: PyObjectWeak, - hash: AtomicCell>, -} - -impl PyWeak { - pub fn downgrade(obj: &PyObject) -> PyWeak { - PyWeak { - referent: obj.downgrade(), - hash: AtomicCell::new(None), - } - } - - pub fn upgrade(&self) -> Option { - self.referent.upgrade() - } -} +pub use crate::pyobjectrc::PyWeak; #[derive(FromArgs)] pub struct WeakNewArgs { #[pyarg(positional)] referent: PyObjectRef, #[pyarg(positional, optional)] - _callback: OptionalArg, + callback: OptionalArg, } impl PyValue for PyWeak { @@ -54,16 +35,13 @@ impl Callable for PyWeak { impl Constructor for PyWeak { type Args = WeakNewArgs; - // TODO callbacks fn py_new( cls: PyTypeRef, - Self::Args { - referent, - _callback, - }: Self::Args, + Self::Args { referent, callback }: Self::Args, vm: &VirtualMachine, ) -> PyResult { - PyWeak::downgrade(&referent).into_pyresult_with_type(vm, cls) + let weak = referent.downgrade_with_typ(callback.into_option(), cls, vm)?; + Ok(weak.into_object()) } } @@ -92,17 +70,26 @@ impl PyWeak { impl Hashable for PyWeak { fn hash(zelf: &crate::PyObjectView, vm: &VirtualMachine) -> PyResult { - match zelf.hash.load() { - Some(hash) => Ok(hash), - None => { + let hash = match zelf.hash.load(Ordering::Relaxed) { + hash::SENTINEL => { let obj = zelf .upgrade() .ok_or_else(|| vm.new_type_error("weak object has gone away".to_owned()))?; let hash = obj.hash(vm)?; - zelf.hash.store(Some(hash)); - Ok(hash) + match Radium::compare_exchange( + &zelf.hash, + hash::SENTINEL, + hash::fix_sentinel(hash), + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => hash, + Err(prev_stored) => prev_stored, + } } - } + hash => hash, + }; + Ok(hash) } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 0d78ba471..82d29f862 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -94,6 +94,7 @@ impl PyContext { let types = TypeZoo::init(); let exceptions = exceptions::ExceptionZoo::init(); + #[inline] fn create_object(payload: T, cls: &PyTypeRef) -> PyRef { PyRef::new_ref(payload, cls.clone(), None) } diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 0e7832983..890dd5a8a 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -1,16 +1,19 @@ -use crate::common::{ - lock::PyRwLock, - rc::{PyRc, PyWeak}, -}; +use crate::common::atomic::{Ordering, PyAtomic, Radium}; +use crate::common::linked_list::{Link, LinkedList, Pointers}; +use crate::common::lock::{PyMutex, PyMutexGuard, PyRwLock}; +use crate::common::refcount::RefCount; use crate::{ builtins::{PyBaseExceptionRef, PyDictRef, PyTypeRef}, - IdProtocol, PyObjectPayload, TypeProtocol, VirtualMachine, + IdProtocol, PyObjectPayload, PyResult, TypeProtocol, VirtualMachine, }; use std::any::TypeId; use std::borrow::Borrow; +use std::cell::UnsafeCell; use std::fmt; +use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ops::Deref; +use std::ptr::{self, NonNull}; // so, PyObjectRef is basically equivalent to `PyRc>`, except it's // only one pointer in width rather than 2. We do that by manually creating a vtable, and putting @@ -49,36 +52,46 @@ use std::ops::Deref; struct Erased; struct PyObjVTable { - drop: unsafe fn(*mut PyInner), - debug: unsafe fn(*const PyInner, &mut fmt::Formatter) -> fmt::Result, + drop_dealloc: unsafe fn(&mut PyObject), + debug: unsafe fn(&PyObject, &mut fmt::Formatter) -> fmt::Result, } -unsafe fn drop_obj(x: *mut PyInner) { - std::ptr::drop_in_place(x as *mut PyInner) +unsafe fn drop_dealloc_obj(x: &mut PyObject) { + Box::from_raw(x as *mut PyObject as *mut PyInner); } -unsafe fn debug_obj( - x: *const PyInner, - f: &mut fmt::Formatter, -) -> fmt::Result { - let x = &*x.cast::>(); +unsafe fn debug_obj(x: &PyObject, f: &mut fmt::Formatter) -> fmt::Result { + let x = &*(x as *const PyObject as *const PyInner); fmt::Debug::fmt(x, f) } + impl PyObjVTable { pub fn of() -> &'static Self { - &PyObjVTable { - drop: drop_obj::, - debug: debug_obj::, + struct Helper(PhantomData); + trait VtableHelper { + const VTABLE: PyObjVTable; } + impl VtableHelper for Helper { + const VTABLE: PyObjVTable = PyObjVTable { + drop_dealloc: drop_dealloc_obj::, + debug: debug_obj::, + }; + } + &Helper::::VTABLE } } +/// This is an actual python object. It consists of a `typ` which is the +/// python class, and carries some rust payload optionally. This rust +/// payload can be a rust float or rust int in case of float and int objects. #[repr(C)] struct PyInner { + refcount: RefCount, // TODO: move typeid into vtable once TypeId::of is const typeid: TypeId, vtable: &'static PyObjVTable, - typ: PyRwLock, // __class__ member - dict: Option>, // __dict__ member + typ: PyRwLock, // __class__ member + dict: Option, + weaklist: WeakRefList, payload: T, } @@ -89,36 +102,256 @@ impl fmt::Debug for PyInner { } } +struct WeakRefList { + weak_lock: PyMutex, +} + +impl fmt::Debug for WeakRefList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("WeakRefList").finish_non_exhaustive() + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + unsafe impl Send for WeakRefList {} + unsafe impl Sync for WeakRefList {} + } +} + +struct WeakListInner { + list: LinkedList>, + obj: Option>, +} + +impl WeakRefList { + pub fn new() -> Self { + WeakRefList { + weak_lock: PyMutex::new(WeakListInner { + list: LinkedList::new(), + obj: None, + }), + } + } + + fn add( + &self, + obj: &PyObject, + cls: PyTypeRef, + callback: Option, + dict: Option, + ) -> PyObjectWeak { + let mut inner = self.weak_lock.lock(); + if inner.obj.is_none() { + inner.obj = Some(NonNull::from(obj)); + } + let obj = PyWeak { + pointers: Pointers::new(), + parent: Radium::new(self as *const WeakRefList as *mut WeakRefList), + callback: UnsafeCell::new(callback), + hash: Radium::new(-1), + }; + let weak = PyRef::new_ref(obj, cls, dict); + // SAFETY: we don't actually own the PyObjectWeaks inside `list`, and every time we take + // one out of the list we immediately wrap it in ManuallyDrop + inner.list.push_front(unsafe { ptr::read(&weak) }); + PyObjectWeak { weak } + } + + fn clear(&self) { + let mut inner = self.weak_lock.lock(); + inner.obj = None; + // TODO: can be an arrayvec + let mut v = Vec::with_capacity(16); + loop { + let iter = inner + .list + .drain_filter(|_| true) + .filter_map(|wr| { + // we don't have actual ownership of the reference counts in the list. + // but, now we do want ownership (and so incref these *while the lock + // is held*) to avoid weird things if PyWeakObj::drop happens after + // this but before we reach the loop body below + let wr = ManuallyDrop::new(wr); + + wr.parent.store(ptr::null_mut(), Ordering::SeqCst); + + // if strong_count == 0, drop_slow has been called but lock_parent is blocking + // in PyWeak::Drop OR there's some reentrancy going on. either way we don't + // want to call the callback + (wr.as_object().strong_count() > 0).then(|| (*wr).clone()) + }) + .take(16); + v.extend(iter); + if v.is_empty() { + break; + } + PyMutexGuard::unlocked(&mut inner, || { + for wr in v.drain(..) { + let cb = unsafe { wr.callback.get().replace(None) }; + if let Some(cb) = cb { + crate::vm::thread::with_vm(&cb, |vm| { + // TODO: handle unraisable exception + let _ = vm.invoke(&cb, (wr.clone(),)); + }); + } + } + }) + } + } + + fn count(&self) -> usize { + self.weak_lock.lock().list.count() + } +} + +impl Default for WeakRefList { + fn default() -> Self { + Self::new() + } +} + +struct WeakLink; +unsafe impl Link for WeakLink { + type Handle = PyRef; + + type Target = PyObjectView; + + fn as_raw(handle: &PyRef) -> NonNull { + NonNull::from(&**handle) + } + + unsafe fn from_raw(ptr: NonNull) -> Self::Handle { + PyRef::from_raw(ptr.as_ptr()) + } + + unsafe fn pointers(target: NonNull) -> NonNull> { + NonNull::new_unchecked(ptr::addr_of_mut!((*target.as_ptr()).0.payload.pointers)) + } +} + +#[pyclass(name = "weakref", module = false)] +#[derive(Debug)] +pub struct PyWeak { + pointers: Pointers>, + parent: PyAtomic<*mut WeakRefList>, + callback: UnsafeCell>, + pub(crate) hash: PyAtomic, +} + +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + unsafe impl Send for PyWeak {} + unsafe impl Sync for PyWeak {} + } +} + +impl PyWeak { + pub(crate) fn upgrade(&self) -> Option { + // TODO: figure out orderings for here, in drop, and the store in WeakRefList::clear + let guard = unsafe { + self.parent + .load(Ordering::SeqCst) + .as_ref()? + .weak_lock + .lock() + }; + let obj_ptr = guard.obj?; + unsafe { + obj_ptr.as_ref().0.refcount.incref(); + Some(PyObjectRef::from_raw(obj_ptr.as_ptr())) + } + } + + #[inline(always)] + fn drop_inner(&self) { + let parent_ptr = self.parent.load(Ordering::SeqCst); + let mut guard = match unsafe { parent_ptr.as_ref() } { + Some(parent) => parent.weak_lock.lock(), + // the object is dead - we don't have to worry about removing ourselves from the list + None => return, + }; + if self.parent.load(Ordering::SeqCst).is_null() { + // another thread is calling WeakRefList::clear(), and between the previous load and + // the lock() it reached our weakref in the .drain_filter() iterator. because of that, + // we know we've now been removed from the list, so we don't have to do anything + return; + } + let offset = memoffset::offset_of!(PyInner, payload); + let pyinner = (self as *const Self as usize - offset) as *const PyInner; + let node_ptr = unsafe { NonNull::new_unchecked(pyinner as *mut PyObjectView) }; + // the list doesn't have ownership over its PyRef! we're being dropped + // right now so that should be obvious!! + std::mem::forget(unsafe { guard.list.remove(node_ptr) }); + } +} + +impl Drop for PyWeak { + fn drop(&mut self) { + // we do NOT have actual exclusive access! + // no clue if doing this actually reduces chance of UB + let me: &Self = self; + me.drop_inner(); + } +} + +#[derive(Debug)] +struct InstanceDict { + d: PyRwLock, +} + +impl From for InstanceDict { + #[inline] + fn from(d: PyDictRef) -> Self { + Self::new(d) + } +} + +impl InstanceDict { + #[inline] + pub fn new(d: PyDictRef) -> Self { + Self { + d: PyRwLock::new(d), + } + } + + #[inline] + pub fn get(&self) -> PyDictRef { + self.d.read().clone() + } + + #[inline] + pub fn set(&self, d: PyDictRef) { + self.replace(d); + } + + #[inline] + pub fn replace(&self, d: PyDictRef) -> PyDictRef { + std::mem::replace(&mut self.d.write(), d) + } +} + /// This is an actual python object. It consists of a `typ` which is the /// python class, and carries some rust payload optionally. This rust /// payload can be a rust float or rust int in case of float and int objects. #[repr(transparent)] pub struct PyGenericObject { - inner: ManuallyDrop>, + inner: PyInner, } impl PyGenericObject { #[allow(clippy::new_ret_no_self)] pub fn new(payload: T, typ: PyTypeRef, dict: Option) -> PyObjectRef { let inner = PyInner { + refcount: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), typ: PyRwLock::new(typ), - dict: dict.map(PyRwLock::new), + dict: dict.map(InstanceDict::new), + weaklist: WeakRefList::new(), payload, }; - PyObjectRef::new(PyGenericObject { - inner: ManuallyDrop::new(inner), - }) - } -} - -impl Drop for PyGenericObject { - fn drop(&mut self) { - let erased = &mut *self.inner as *mut _ as *mut PyInner; - // SAFETY: the vtable contains functions that accept payload types that always match up - // with the payload of the object - unsafe { (self.inner.vtable.drop)(erased) } + PyObjectRef::new(PyGenericObject { inner }) } } @@ -127,34 +360,50 @@ impl Drop for PyGenericObject { /// this reference counting is accounted for by this type. Use the `.clone()` /// method to create a new reference and increment the amount of references /// to the python object by 1. -#[derive(Clone)] #[repr(transparent)] pub struct PyObjectRef { - rc: PyRc, + ptr: NonNull, +} + +impl Clone for PyObjectRef { + #[inline(always)] + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + unsafe impl Send for PyObjectRef {} + unsafe impl Sync for PyObjectRef {} + } } #[derive(Clone)] #[repr(transparent)] pub struct PyObjectWeak { - weak: PyWeak, + weak: PyRef, } #[repr(transparent)] -pub struct PyObject(PyGenericObject); +pub struct PyObject(PyInner); impl Deref for PyObjectRef { type Target = PyObject; fn deref(&self) -> &PyObject { - &self.rc + unsafe { self.ptr.as_ref() } } } impl ToOwned for PyObject { type Owned = PyObjectRef; - #[inline] + #[inline(always)] fn to_owned(&self) -> Self::Owned { - self.with_pyobjectref(Clone::clone) + self.0.refcount.incref(); + PyObjectRef { + ptr: NonNull::from(self), + } } } @@ -184,14 +433,15 @@ impl PyObjectRef { /// too many times. pub unsafe fn from_raw(ptr: *const PyObject) -> Self { Self { - rc: PyRc::from_raw(ptr.cast()), + ptr: NonNull::new_unchecked(ptr as *mut PyObject), } } fn new(value: PyGenericObject) -> Self { - let inner: *const PyGenericObject = PyRc::into_raw(PyRc::new(value)); - let rc = unsafe { PyRc::from_raw(inner as *const PyObject) }; - Self { rc } + let inner: *const PyInner = Box::into_raw(Box::new(value.inner)); + Self { + ptr: unsafe { NonNull::new_unchecked(inner as *mut PyObject) }, + } } /// Attempt to downcast this reference to a subclass. @@ -255,29 +505,61 @@ impl PyObjectRef { impl PyObject { #[inline] - fn with_pyobjectref(&self, f: impl FnOnce(&PyObjectRef) -> R) -> R { - // SAFETY: we don't use obj after the real lifetime (self) goes out of scope, we wrap it - // in a ManuallyDrop to prevent double free, and we only pass it by reference - let obj = unsafe { ManuallyDrop::new(PyObjectRef::from_raw(self)) }; - f(&obj) + fn weakreflist(&self) -> Option<&WeakRefList> { + Some(&self.0.weaklist) } - pub fn downgrade(&self) -> PyObjectWeak { - PyObjectWeak { - weak: self.with_pyobjectref(|obj| PyRc::downgrade(&obj.rc)), - } + pub(crate) fn downgrade_with_typ_opt( + &self, + callback: Option, + typ: PyTypeRef, + ) -> Option { + self.weakreflist() + .map(|wr| wr.add(self, typ, callback, None)) + } + + pub(crate) fn downgrade_with_typ( + &self, + callback: Option, + typ: PyTypeRef, + vm: &VirtualMachine, + ) -> PyResult { + let dict = if typ + .slots + .flags + .has_feature(crate::types::PyTypeFlags::HAS_DICT) + { + Some(vm.ctx.new_dict()) + } else { + None + }; + self.weakreflist() + .map(|wr| wr.add(self, typ, callback, dict)) + .ok_or_else(|| { + vm.new_type_error(format!( + "cannot create weak reference to '{}' object", + self.class().name() + )) + }) + } + + pub fn downgrade( + &self, + callback: Option, + vm: &VirtualMachine, + ) -> PyResult { + self.downgrade_with_typ(callback, vm.ctx.types.weakref_type.clone(), vm) } pub fn payload_is(&self) -> bool { - self.0.inner.typeid == TypeId::of::() + self.0.typeid == TypeId::of::() } pub fn payload(&self) -> Option<&T> { if self.payload_is::() { // we cast to a PyInner first because we don't know T's exact offset because of // varying alignment, but once we get a PyInner the compiler can get it for us - let inner = - unsafe { &*(&*self.0.inner as *const PyInner as *const PyInner) }; + let inner = unsafe { &*(&self.0 as *const PyInner as *const PyInner) }; Some(&inner.payload) } else { None @@ -285,7 +567,7 @@ impl PyObject { } pub(crate) fn class_lock(&self) -> &PyRwLock { - &self.0.inner.typ + &self.0.typ } #[inline] @@ -300,15 +582,20 @@ impl PyObject { } } + #[inline] + fn instance_dict(&self) -> Option<&InstanceDict> { + self.0.dict.as_ref() + } + pub fn dict(&self) -> Option { - self.0.inner.dict.as_ref().map(|mu| mu.read().clone()) + self.instance_dict().map(|d| d.get()) } /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field /// in the first place. pub fn set_dict(&self, dict: PyDictRef) -> Result<(), PyDictRef> { - match self.0.inner.dict { - Some(ref mu) => { - *mu.write() = dict; + match self.instance_dict() { + Some(d) => { + d.set(dict); Ok(()) } None => Err(dict), @@ -343,12 +630,12 @@ impl PyObject { #[inline] pub fn strong_count(&self) -> usize { - self.with_pyobjectref(|obj| PyRc::strong_count(&obj.rc)) + self.0.refcount.get() } #[inline] - pub fn weak_count(&self) -> usize { - self.with_pyobjectref(|obj| PyRc::weak_count(&obj.rc)) + pub fn weak_count(&self) -> Option { + self.weakreflist().map(|wrl| wrl.count()) } #[inline] @@ -357,6 +644,38 @@ impl PyObject { } } +impl PyObjectRef { + #[inline(never)] + #[cold] + fn drop_slow(&mut self) { + // CPython-compatible drop implementation + if let Some(slot_del) = self.class().mro_find_map(|cls| cls.slots.del.load()) { + let ret = crate::vm::thread::with_vm(self, |vm| { + self.0.refcount.incref(); + if let Err(e) = slot_del(self, vm) { + print_del_error(e, self, vm); + } + self.0.refcount.decref() + }); + match ret { + // the decref right above set refcount back to 0 + Some(true) => {} + // we've been resurrected by __del__ + Some(false) => return, + None => { + warn!("couldn't run __del__ method for object") + } + } + } + if let Some(wrl) = self.weakreflist() { + wrl.clear(); + } + + let drop_dealloc = self.0.vtable.drop_dealloc; + unsafe { drop_dealloc(self.ptr.as_mut()) } + } +} + impl Borrow for PyObjectRef { fn borrow(&self) -> &PyObject { self @@ -377,7 +696,7 @@ impl AsRef for PyObject { impl IdProtocol for PyObjectRef { fn get_id(&self) -> usize { - self.rc.get_id() + self.ptr.as_ptr() as usize } } @@ -403,33 +722,21 @@ where } impl PyObjectWeak { + #[inline] pub fn upgrade(&self) -> Option { - self.weak.upgrade().map(|rc| PyObjectRef { rc }) + self.weak.upgrade() + } + + pub fn into_object(self) -> PyObjectRef { + self.weak.into_object() } } impl Drop for PyObjectRef { fn drop(&mut self) { - // PyObjectRef will drop the value when its count goes to 0 - if PyRc::strong_count(&self.rc) != 1 { - return; + if self.0.refcount.decref() { + self.drop_slow() } - - // CPython-compatible drop implementation - let zelf = self.clone(); - if let Some(slot_del) = self.class().mro_find_map(|cls| cls.slots.del.load()) { - let ret = crate::vm::thread::with_vm(&zelf, |vm| { - if let Err(e) = slot_del(&zelf, vm) { - print_del_error(e, &zelf, vm); - } - }); - if ret.is_none() { - warn!("couldn't run __del__ method for object") - } - } - - // __del__ might have resurrected the object at this point, but that's fine, - // inner.strong_count would be >1 now and it'll maybe get dropped the next time } } @@ -457,8 +764,7 @@ impl fmt::Debug for PyObjectRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // SAFETY: the vtable contains functions that accept payload types that always match up // with the payload of the object - let inner = &*self.rc.0.inner; - unsafe { (inner.vtable.debug)(inner, f) } + unsafe { ((*self).0.vtable.debug)(self, f) } } } @@ -477,19 +783,14 @@ impl PyObjectView { unsafe { &*(&self.0 as *const PyInner as *const PyObject) } } - #[inline] - fn with_pyref(&self, f: impl FnOnce(&PyRef) -> R) -> R { - unsafe { - // SAFETY: we don't use obj after the real lifetime (self) goes out of scope, we wrap it - // in a ManuallyDrop to prevent double free, and we only pass it by reference - let obj = ManuallyDrop::new(PyRef::from_raw(self)); - f(&obj) - } - } - - pub fn downgrade(&self) -> PyWeakRef { - self.with_pyref(|r| PyWeakRef { - weak: PyRc::downgrade(&r.obj), + pub fn downgrade( + &self, + callback: Option, + vm: &VirtualMachine, + ) -> PyResult> { + Ok(PyWeakRef { + weak: self.as_object().downgrade(callback, vm)?, + _marker: PhantomData, }) } } @@ -497,8 +798,12 @@ impl PyObjectView { impl ToOwned for PyObjectView { type Owned = PyRef; + #[inline(always)] fn to_owned(&self) -> Self::Owned { - self.with_pyref(Clone::clone) + PyRef { + obj: self.as_object().to_owned(), + _marker: PhantomData, + } } } @@ -537,43 +842,42 @@ impl fmt::Debug for PyObjectView { #[repr(transparent)] pub struct PyRef { // invariant: this obj must always have payload of type T - obj: PyRc>, + obj: PyObjectRef, + _marker: PhantomData, } impl fmt::Debug for PyRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.obj.fmt(f) + (**self).fmt(f) } } impl Clone for PyRef { + #[inline(always)] fn clone(&self) -> Self { - Self { - obj: self.obj.clone(), - } + (**self).to_owned() } } impl PyRef { unsafe fn from_raw(raw: *const PyObjectView) -> Self { Self { - obj: PyRc::from_raw(raw), + obj: PyObjectRef::from_raw(raw as _), + _marker: PhantomData, } } /// Safety: payload type of `obj` must be `T` unsafe fn from_obj_unchecked(obj: PyObjectRef) -> Self { debug_assert!(obj.payload_is::()); - let obj = PyRc::from_raw(obj.into_raw() as *const PyObjectView); - Self { obj } + Self { + obj, + _marker: PhantomData, + } } // ideally we'd be able to define this in pyobject.rs, but method visibility rules are weird - pub fn new_ref( - payload: T, - typ: crate::builtins::PyTypeRef, - dict: Option, - ) -> Self { + pub fn new_ref(payload: T, typ: crate::builtins::PyTypeRef, dict: Option) -> Self { let obj = PyGenericObject::new(payload, typ, dict); // SAFETY: we just created the object from a payload of type T unsafe { Self::from_obj_unchecked(obj) } @@ -585,7 +889,7 @@ where T: PyObjectPayload, { fn into_object(self) -> PyObjectRef { - unsafe { PyObjectRef::from_raw(PyRc::into_raw(self.obj) as *const PyObject) } + self.obj } } @@ -594,7 +898,7 @@ where T: PyObjectPayload, { fn as_ref(&self) -> &PyObject { - (**self).as_object() + &self.obj } } @@ -614,18 +918,22 @@ where type Target = PyObjectView; fn deref(&self) -> &PyObjectView { - &self.obj + unsafe { &*(&*self.obj as *const PyObject as *const PyObjectView) } } } #[repr(transparent)] pub struct PyWeakRef { - weak: PyWeak>, + weak: PyObjectWeak, + _marker: PhantomData, } impl PyWeakRef { pub fn upgrade(&self) -> Option> { - self.weak.upgrade().map(|obj| PyRef { obj }) + self.weak.upgrade().map(|obj| PyRef { + obj, + _marker: PhantomData, + }) } } @@ -653,18 +961,17 @@ macro_rules! partially_init { }}; } -pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef) { - use crate::builtins::{object, PyType, PyWeak}; +pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { + use crate::builtins::{object, PyType}; use crate::{PyAttributes, PyClassImpl}; use std::mem::MaybeUninit; - use std::ptr; // `type` inherits from `object` // and both `type` and `object are instances of `type`. // to produce this circular dependency, we need an unsafe block. // (and yes, this will never get dropped. TODO?) let (type_type, object_type) = { - type UninitRef = PyRwLock>>>; + type UninitRef = PyRwLock>>; // We cast between these 2 types, so make sure (at compile time) that there's no change in // layout when we wrap PyInner in MaybeUninit<> @@ -687,59 +994,83 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef) { attributes: PyRwLock::new(PyAttributes::default()), slots: object::PyBaseObject::make_slots(), }; - let type_type = PyRc::new(partially_init!( + let type_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { + refcount: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None, + weaklist: WeakRefList::new(), payload: type_payload, }, Uninit { typ } - )); - let object_type = PyRc::new(partially_init!( + ))); + let object_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { + refcount: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None, + weaklist: WeakRefList::new(), payload: object_payload, }, Uninit { typ }, - )); + ))); - let object_type_ptr = PyRc::into_raw(object_type) as *mut MaybeUninit> - as *mut PyInner; - let type_type_ptr = PyRc::into_raw(type_type.clone()) as *mut MaybeUninit> - as *mut PyInner; + let object_type_ptr = + object_type_ptr as *mut MaybeUninit> as *mut PyInner; + let type_type_ptr = + type_type_ptr as *mut MaybeUninit> as *mut PyInner; unsafe { + (*type_type_ptr).refcount.incref(); ptr::write( &mut (*object_type_ptr).typ as *mut PyRwLock as *mut UninitRef, - PyRwLock::new(type_type.clone()), + PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), ); + (*type_type_ptr).refcount.incref(); ptr::write( &mut (*type_type_ptr).typ as *mut PyRwLock as *mut UninitRef, - PyRwLock::new(type_type), + PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), ); - let type_type = - PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(type_type_ptr.cast())); - let object_type = - PyTypeRef::from_obj_unchecked(PyObjectRef::from_raw(object_type_ptr.cast())); + let object_type = PyTypeRef::from_raw(object_type_ptr.cast()); (*type_type_ptr).payload.mro = vec![object_type.clone()]; (*type_type_ptr).payload.bases = vec![object_type.clone()]; (*type_type_ptr).payload.base = Some(object_type.clone()); + let type_type = PyTypeRef::from_raw(type_type_ptr.cast()); + (type_type, object_type) } }; - object_type - .subclasses - .write() - .push(PyWeak::downgrade(type_type.as_object())); + let weakref_type = PyType { + base: Some(object_type.clone()), + bases: vec![object_type.clone()], + mro: vec![object_type.clone()], + subclasses: PyRwLock::default(), + attributes: PyRwLock::default(), + slots: PyWeak::make_slots(), + }; + let weakref_type = PyRef::new_ref(weakref_type, type_type.clone(), None); - (type_type, object_type) + object_type.subclasses.write().push( + type_type + .as_object() + .downgrade_with_typ_opt(None, weakref_type.clone()) + .unwrap(), + ); + + object_type.subclasses.write().push( + weakref_type + .as_object() + .downgrade_with_typ_opt(None, weakref_type.clone()) + .unwrap(), + ); + + (type_type, object_type, weakref_type) } #[cfg(test)] diff --git a/vm/src/scope.rs b/vm/src/scope.rs index 0ab2587d3..30d833e4a 100644 --- a/vm/src/scope.rs +++ b/vm/src/scope.rs @@ -33,7 +33,7 @@ impl Scope { ) -> Scope { if !globals.contains_key("__builtins__", vm) { globals - .set_item("__builtins__", vm.builtins.clone(), vm) + .set_item("__builtins__", vm.builtins.clone().into(), vm) .unwrap(); } Scope::new(locals, globals) diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index 4dace44c4..630aaa772 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -214,7 +214,7 @@ mod builtins { let (globals, locals) = match self.globals { Some(globals) => { if !globals.contains_key("__builtins__", vm) { - let builtins_dict = vm.builtins.dict().unwrap().into(); + let builtins_dict = vm.builtins.dict().into(); globals.set_item("__builtins__", builtins_dict, vm)?; } ( diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index e58112743..8b611540a 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -638,7 +638,7 @@ mod decl { } .into_ref(vm); - state.grouper = Some(grouper.downgrade()); + state.grouper = Some(grouper.downgrade(None, vm).unwrap()); Ok(PyIterReturn::Return( (state.current_key.as_ref().unwrap().clone(), grouper).into_pyobject(vm), )) diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index e13aadfb3..c943d511a 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -33,7 +33,7 @@ mod _weakref { #[pyfunction] fn getweakrefcount(obj: PyObjectRef) -> usize { - obj.weak_count() + obj.weak_count().unwrap_or(0) } #[pyfunction] diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 1a9c21e63..d5fb06800 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -150,6 +150,8 @@ pub(crate) type DescrSetFunc = pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; +pub use crate::builtins::object::{generic_getattr, generic_setattr}; + fn as_mapping_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods { macro_rules! then_some_closure { ($cond:expr, $closure:expr) => { diff --git a/vm/src/types/zoo.rs b/vm/src/types/zoo.rs index f780343f4..df3e048a8 100644 --- a/vm/src/types/zoo.rs +++ b/vm/src/types/zoo.rs @@ -85,11 +85,12 @@ pub struct TypeZoo { impl TypeZoo { pub(crate) fn init() -> Self { - let (type_type, object_type) = crate::pyobjectrc::init_type_hierarchy(); + let (type_type, object_type, weakref_type) = crate::pyobjectrc::init_type_hierarchy(); Self { - // the order matters for type, object and int + // the order matters for type, object, weakref, and int type_type: pytype::PyType::init_manually(type_type).clone(), object_type: object::PyBaseObject::init_manually(object_type).clone(), + weakref_type: weakref::PyWeak::init_manually(weakref_type).clone(), int_type: int::PyInt::init_bare_type().clone(), // types exposed as builtins @@ -162,7 +163,6 @@ impl TypeZoo { traceback_type: traceback::PyTraceback::init_bare_type().clone(), tuple_iterator_type: tuple::PyTupleIterator::init_bare_type().clone(), weakproxy_type: weakproxy::PyWeakProxy::init_bare_type().clone(), - weakref_type: weakref::PyWeak::init_bare_type().clone(), method_descriptor_type: builtinfunc::PyBuiltinMethod::init_bare_type().clone(), none_type: singletons::PyNone::init_bare_type().clone(), not_implemented_type: singletons::PyNotImplemented::init_bare_type().clone(), diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 5c4d27ad1..b097fed30 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -9,7 +9,7 @@ use crate::compile::{self, CompileError, CompileErrorType, CompileOpts}; use crate::{ builtins::{ code::{self, PyCode}, - module, object, + object, pystr::IntoPyStrRef, tuple::{IntoPyTuple, PyTuple, PyTupleRef, PyTupleTyped}, PyBaseException, PyBaseExceptionRef, PyDictRef, PyInt, PyIntRef, PyList, PyModule, PyStr, @@ -26,9 +26,8 @@ use crate::{ scope::Scope, signal, stdlib, types::PyComparisonOp, - IdProtocol, ItemProtocol, PyArithmeticValue, PyContext, PyGenericObject, PyLease, PyMethod, - PyObject, PyObjectRef, PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, - TypeProtocol, + IdProtocol, ItemProtocol, PyArithmeticValue, PyContext, PyLease, PyMethod, PyObject, + PyObjectRef, PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crossbeam_utils::atomic::AtomicCell; use num_traits::{Signed, ToPrimitive}; @@ -46,8 +45,8 @@ use std::fmt; /// /// To construct this, please refer to the [`Interpreter`](Interpreter) pub struct VirtualMachine { - pub builtins: PyObjectRef, - pub sys_module: PyObjectRef, + pub builtins: PyRef, + pub sys_module: PyRef, pub ctx: PyRc, pub frames: RefCell>, pub wasm_id: Option, @@ -248,14 +247,17 @@ impl VirtualMachine { // make a new module without access to the vm; doesn't // set __spec__, __loader__, etc. attributes - let new_module = - |dict| PyGenericObject::new(PyModule {}, ctx.types.module_type.clone(), Some(dict)); + let new_module = || { + PyRef::new_ref( + PyModule {}, + ctx.types.module_type.clone(), + Some(ctx.new_dict()), + ) + }; // Hard-core modules: - let builtins_dict = ctx.new_dict(); - let builtins = new_module(builtins_dict.clone()); - let sysmod_dict = ctx.new_dict(); - let sysmod = new_module(sysmod_dict.clone()); + let builtins = new_module(); + let sys_module = new_module(); let import_func = ctx.none(); let profile_func = RefCell::new(ctx.none()); @@ -279,7 +281,7 @@ impl VirtualMachine { let mut vm = VirtualMachine { builtins, - sys_module: sysmod, + sys_module, ctx: PyRc::new(ctx), frames: RefCell::new(vec![]), wasm_id: None, @@ -309,18 +311,14 @@ impl VirtualMachine { let frozen = frozen::map_frozen(&vm, frozen::get_module_inits()).collect(); PyRc::get_mut(&mut vm.state).unwrap().frozen = frozen; - module::init_module_dict( - &vm, - &builtins_dict, + vm.builtins.init_module_dict( vm.ctx.new_str(ascii!("builtins")).into(), vm.ctx.none(), - ); - module::init_module_dict( &vm, - &sysmod_dict, - vm.ctx.new_str(ascii!("sys")).into(), - vm.ctx.none(), ); + vm.sys_module + .init_module_dict(vm.ctx.new_str(ascii!("sys")).into(), vm.ctx.none(), &vm); + vm } @@ -331,8 +329,8 @@ impl VirtualMachine { panic!("Double Initialize Error"); } - stdlib::builtins::make_module(self, self.builtins.clone()); - stdlib::sys::init_module(self, &self.sys_module, &self.builtins); + stdlib::builtins::make_module(self, self.builtins.clone().into()); + stdlib::sys::init_module(self, self.sys_module.as_ref(), self.builtins.as_ref()); let mut inner_init = || -> PyResult<()> { #[cfg(not(target_arch = "wasm32"))] @@ -511,8 +509,7 @@ impl VirtualMachine { } pub fn run_code_obj(&self, code: PyRef, scope: Scope) -> PyResult { - let frame = - Frame::new(code, scope, self.builtins.dict().unwrap(), &[], self).into_ref(self); + let frame = Frame::new(code, scope, self.builtins.dict(), &[], self).into_ref(self); self.run_frame_full(frame) } @@ -624,14 +621,14 @@ impl VirtualMachine { } pub fn new_module(&self, name: &str, dict: PyDictRef, doc: Option<&str>) -> PyObjectRef { - module::init_module_dict( - self, - &dict, + let module = PyRef::new_ref(PyModule {}, self.ctx.types.module_type.clone(), Some(dict)); + module.init_module_dict( self.new_pyobj(name.to_owned()), doc.map(|doc| self.new_pyobj(doc.to_owned())) .unwrap_or_else(|| self.ctx.none()), + self, ); - PyGenericObject::new(PyModule {}, self.ctx.types.module_type.clone(), Some(dict)) + module.into_object() } /// Instantiate an exception with arguments. @@ -1898,7 +1895,7 @@ impl VirtualMachine { attr_value: impl Into, ) -> PyResult<()> { let val = attr_value.into(); - object::setattr(module, attr_name.into_pystr_ref(self), Some(val), self) + object::generic_setattr(module, attr_name.into_pystr_ref(self), Some(val), self) } } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index b90276901..c71c4e1ec 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -84,7 +84,7 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { let wasm_vm = WASMVirtualMachine { id: wasm_id.clone(), }; - let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap(); + let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap().unwrap(); let closure = move |args: Option>, kwargs: Option| diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index 8617e6a90..a3273c4f1 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -123,12 +123,12 @@ mod _js { } #[pymethod] - fn new_closure(&self, obj: PyObjectRef, vm: &VirtualMachine) -> JsClosure { + fn new_closure(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { JsClosure::new(obj, false, vm) } #[pymethod] - fn new_closure_once(&self, obj: PyObjectRef, vm: &VirtualMachine) -> JsClosure { + fn new_closure_once(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { JsClosure::new(obj, true, vm) } @@ -306,11 +306,11 @@ mod _js { #[pyimpl] impl JsClosure { - fn new(obj: PyObjectRef, once: bool, vm: &VirtualMachine) -> Self { + fn new(obj: PyObjectRef, once: bool, vm: &VirtualMachine) -> PyResult { let wasm_vm = WASMVirtualMachine { id: vm.wasm_id.clone().unwrap(), }; - let weak_py_obj = wasm_vm.push_held_rc(obj).unwrap(); + let weak_py_obj = wasm_vm.push_held_rc(obj).unwrap()?; let f = move |this: JsValue, args: Box<[JsValue]>| { let py_obj = match wasm_vm.assert_valid() { Ok(_) => weak_py_obj @@ -337,11 +337,11 @@ mod _js { Closure::once(Box::new(f)) }; let wrapped = PyJsValue::new(wrap_closure(closure.as_ref())).into_ref(vm); - JsClosure { + Ok(JsClosure { closure: Some((closure, wrapped)).into(), destroyed: false.into(), detached: false.into(), - } + }) } #[pyproperty] diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 0dc45fa9f..67b0d7582 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -8,7 +8,7 @@ use wasm_bindgen::prelude::*; use rustpython_vm::compile::{self, Mode}; use rustpython_vm::scope::Scope; use rustpython_vm::{InitParameter, Interpreter, PySettings, VirtualMachine}; -use rustpython_vm::{ItemProtocol, PyObjectRef, PyObjectWeak, PyValue}; +use rustpython_vm::{ItemProtocol, PyObjectRef, PyObjectWeak, PyResult, PyValue}; use crate::browser_module::setup_browser_module; use crate::convert::{self, PyResultExt}; @@ -195,11 +195,11 @@ impl WASMVirtualMachine { STORED_VMS.with(|cell| cell.borrow().contains_key(&self.id)) } - pub(crate) fn push_held_rc(&self, obj: PyObjectRef) -> Result { - self.with(|stored_vm| { - let weak = obj.downgrade(); + pub(crate) fn push_held_rc(&self, obj: PyObjectRef) -> Result, JsValue> { + self.with_vm(|vm, stored_vm| { + let weak = obj.downgrade(None, vm)?; stored_vm.held_objects.borrow_mut().push(obj); - weak + Ok(weak) }) } From baf581c11b5f4a7b1134488a5685d09097588a3b Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Thu, 28 Oct 2021 12:10:28 -0500 Subject: [PATCH 17/40] Update weakref.py and _weakrefset.py to Python 3.9 --- Lib/_weakrefset.py | 10 ++- Lib/weakref.py | 151 +++++++++++++++++++++++++++++---------------- 2 files changed, 104 insertions(+), 57 deletions(-) diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py index b267780f0..2a2768432 100644 --- a/Lib/_weakrefset.py +++ b/Lib/_weakrefset.py @@ -51,10 +51,14 @@ class WeakSet: self.update(data) def _commit_removals(self): - l = self._pending_removals + pop = self._pending_removals.pop discard = self.data.discard - while l: - discard(l.pop()) + while True: + try: + item = pop() + except IndexError: + return + discard(item) def __iter__(self): with _IterationGuard(self): diff --git a/Lib/weakref.py b/Lib/weakref.py index 99de2eab7..994ea8aa3 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -2,7 +2,7 @@ This module is an implementation of PEP 205: -http://www.python.org/dev/peps/pep-0205/ +https://www.python.org/dev/peps/pep-0205/ """ # Naming convention: Variables named "wr" are weak reference objects; @@ -33,6 +33,9 @@ __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", "WeakSet", "WeakMethod", "finalize"] +_collections_abc.Set.register(WeakSet) +_collections_abc.MutableSet.register(WeakSet) + class WeakMethod(ref): """ A custom `weakref.ref` subclass which simulates a weak reference to @@ -75,14 +78,14 @@ class WeakMethod(ref): if not self._alive or not other._alive: return self is other return ref.__eq__(self, other) and self._func_ref == other._func_ref - return False + return NotImplemented def __ne__(self, other): if isinstance(other, WeakMethod): if not self._alive or not other._alive: return self is not other return ref.__ne__(self, other) or self._func_ref != other._func_ref - return True + return NotImplemented __hash__ = ref.__hash__ @@ -99,13 +102,7 @@ class WeakValueDictionary(_collections_abc.MutableMapping): # objects are unwrapped on the way out, and we always wrap on the # way in). - def __init__(*args, **kw): - if not args: - raise TypeError("descriptor '__init__' of 'WeakValueDictionary' " - "object needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) + def __init__(self, other=(), /, **kw): def remove(wr, selfref=ref(self), _atomic_removal=_remove_dead_weakref): self = selfref() if self is not None: @@ -114,22 +111,25 @@ class WeakValueDictionary(_collections_abc.MutableMapping): else: # Atomic removal is necessary since this function # can be called asynchronously by the GC - _atomic_removal(d, wr.key) + _atomic_removal(self.data, wr.key) self._remove = remove # A list of keys to be removed self._pending_removals = [] self._iterating = set() - self.data = d = {} - self.update(*args, **kw) + self.data = {} + self.update(other, **kw) - def _commit_removals(self): - l = self._pending_removals + def _commit_removals(self, _atomic_removal=_remove_dead_weakref): + pop = self._pending_removals.pop d = self.data # We shouldn't encounter any KeyError, because this method should # always be called *before* mutating the dict. - while l: - key = l.pop() - _remove_dead_weakref(d, key) + while True: + try: + key = pop() + except IndexError: + return + _atomic_removal(d, key) def __getitem__(self, key): if self._pending_removals: @@ -171,10 +171,11 @@ class WeakValueDictionary(_collections_abc.MutableMapping): if self._pending_removals: self._commit_removals() new = WeakValueDictionary() - for key, wr in self.data.items(): - o = wr() - if o is not None: - new[key] = o + with _IterationGuard(self): + for key, wr in self.data.items(): + o = wr() + if o is not None: + new[key] = o return new __copy__ = copy @@ -184,10 +185,11 @@ class WeakValueDictionary(_collections_abc.MutableMapping): if self._pending_removals: self._commit_removals() new = self.__class__() - for key, wr in self.data.items(): - o = wr() - if o is not None: - new[deepcopy(key, memo)] = o + with _IterationGuard(self): + for key, wr in self.data.items(): + o = wr() + if o is not None: + new[deepcopy(key, memo)] = o return new def get(self, key, default=None): @@ -285,24 +287,17 @@ class WeakValueDictionary(_collections_abc.MutableMapping): else: return o - def update(*args, **kwargs): - if not args: - raise TypeError("descriptor 'update' of 'WeakValueDictionary' " - "object needs an argument") - self, *args = args - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - dict = args[0] if args else None + def update(self, other=None, /, **kwargs): if self._pending_removals: self._commit_removals() d = self.data - if dict is not None: - if not hasattr(dict, "items"): - dict = type({})(dict) - for key, o in dict.items(): + if other is not None: + if not hasattr(other, "items"): + other = dict(other) + for key, o in other.items(): d[key] = KeyedRef(o, self._remove, key) - if len(kwargs): - self.update(kwargs) + for key, o in kwargs.items(): + d[key] = KeyedRef(o, self._remove, key) def valuerefs(self): """Return a list of weak references to the values. @@ -318,6 +313,25 @@ class WeakValueDictionary(_collections_abc.MutableMapping): self._commit_removals() return list(self.data.values()) + def __ior__(self, other): + self.update(other) + return self + + def __or__(self, other): + if isinstance(other, _collections_abc.Mapping): + c = self.copy() + c.update(other) + return c + return NotImplemented + + def __ror__(self, other): + if isinstance(other, _collections_abc.Mapping): + c = self.__class__() + c.update(other) + c.update(self) + return c + return NotImplemented + class KeyedRef(ref): """Specialized reference that includes a key corresponding to the value. @@ -359,7 +373,10 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): if self._iterating: self._pending_removals.append(k) else: - del self.data[k] + try: + del self.data[k] + except KeyError: + pass self._remove = remove # A list of dead weakrefs (keys to be removed) self._pending_removals = [] @@ -373,11 +390,16 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): # because a dead weakref never compares equal to a live weakref, # even if they happened to refer to equal objects. # However, it means keys may already have been removed. - l = self._pending_removals + pop = self._pending_removals.pop d = self.data - while l: + while True: try: - del d[l.pop()] + key = pop() + except IndexError: + return + + try: + del d[key] except KeyError: pass @@ -408,10 +430,11 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): def copy(self): new = WeakKeyDictionary() - for key, value in self.data.items(): - o = key() - if o is not None: - new[o] = value + with _IterationGuard(self): + for key, value in self.data.items(): + o = key() + if o is not None: + new[o] = value return new __copy__ = copy @@ -419,10 +442,11 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): def __deepcopy__(self, memo): from copy import deepcopy new = self.__class__() - for key, value in self.data.items(): - o = key() - if o is not None: - new[o] = deepcopy(value, memo) + with _IterationGuard(self): + for key, value in self.data.items(): + o = key() + if o is not None: + new[o] = deepcopy(value, memo) return new def get(self, key, default=None): @@ -484,7 +508,7 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): def setdefault(self, key, default=None): return self.data.setdefault(ref(key, self._remove),default) - def update(self, dict=None, **kwargs): + def update(self, dict=None, /, **kwargs): d = self.data if dict is not None: if not hasattr(dict, "items"): @@ -494,6 +518,25 @@ class WeakKeyDictionary(_collections_abc.MutableMapping): if len(kwargs): self.update(kwargs) + def __ior__(self, other): + self.update(other) + return self + + def __or__(self, other): + if isinstance(other, _collections_abc.Mapping): + c = self.copy() + c.update(other) + return c + return NotImplemented + + def __ror__(self, other): + if isinstance(other, _collections_abc.Mapping): + c = self.__class__() + c.update(other) + c.update(self) + return c + return NotImplemented + class finalize: """Class for finalization of weakrefable objects @@ -523,7 +566,7 @@ class finalize: class _Info: __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") - def __init__(self, obj, func, *args, **kwargs): + def __init__(self, obj, func, /, *args, **kwargs): if not self._registered_with_atexit: # We may register the exit function more than once because # of a thread race, but that is harmless From e51cb82ff0c1f1233e1caf5a222d47a7162bcd14 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Thu, 28 Oct 2021 12:10:36 -0500 Subject: [PATCH 18/40] Unskip tests --- Lib/test/test_copy.py | 4 ---- Lib/test/test_functools.py | 2 -- Lib/test/test_memoryview.py | 2 -- Lib/test/test_positional_only_arg.py | 2 -- Lib/test/test_tempfile.py | 2 -- Lib/test/test_typing.py | 2 -- Lib/test/test_weakref.py | 31 ++++------------------------ Lib/test/test_weakset.py | 14 ------------- Lib/test/test_xml_etree.py | 2 -- 9 files changed, 4 insertions(+), 57 deletions(-) diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 87fe6044a..5c28a1eeb 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -826,8 +826,6 @@ class TestCopy(unittest.TestCase): v[x] = y self.assertNotIn(x, u) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_copy_weakkeydict(self): self._check_copy_weakdict(weakref.WeakKeyDictionary) @@ -836,8 +834,6 @@ class TestCopy(unittest.TestCase): def test_copy_weakvaluedict(self): self._check_copy_weakdict(weakref.WeakValueDictionary) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_deepcopy_weakkeydict(self): class C(object): def __init__(self, i): diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index df6894947..18609f4e5 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -500,8 +500,6 @@ class TestPartialMethod(unittest.TestCase): a = A() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_arg_combinations(self): self.assertEqual(self.a.nothing(), ((self.a,), {})) self.assertEqual(self.a.nothing(5), ((self.a, 5), {})) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 68d0bfa0f..9ddddbb8e 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -343,8 +343,6 @@ class AbstractMemoryTests: m = self._view(b) self.assertRaises(ValueError, hash, m) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weakref(self): # Check memoryviews are weakrefable for tp in self._types: diff --git a/Lib/test/test_positional_only_arg.py b/Lib/test/test_positional_only_arg.py index ba1810c24..690009fde 100644 --- a/Lib/test/test_positional_only_arg.py +++ b/Lib/test/test_positional_only_arg.py @@ -318,8 +318,6 @@ class PositionalOnlyTestCase(unittest.TestCase): with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"): f(1,2)(3,4,5) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_same_keyword_as_positional_with_kwargs(self): def f(something,/,**kwargs): return (something, kwargs) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index d79e9abf8..59d00c0b1 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1454,8 +1454,6 @@ class TestTemporaryDirectory(BaseTestCase): self.assertNotIn("Exception ", err) self.assertIn("ResourceWarning: Implicitly cleaning up", err) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_warnings_on_cleanup(self): # ResourceWarning will be triggered by __del__ with self.do_create() as dir: diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index f2f651214..9f53edc04 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3739,8 +3739,6 @@ class TypedDictTests(BaseTestCase): self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) self.assertEqual(Emp.__total__, True) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_typeddict_special_keyword_names(self): TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, fields=list, _fields=dict) self.assertEqual(TD.__name__, 'TD') diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 97efd9e74..2ab8ffb61 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -113,8 +113,6 @@ class ReferencesTestCase(TestBase): del o repr(wr) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_basic_callback(self): self.check_basic_callback(C) self.check_basic_callback(create_function) @@ -132,8 +130,6 @@ class ReferencesTestCase(TestBase): self.check_basic_ref(create_cfunction) self.check_basic_callback(create_cfunction) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_multiple_callbacks(self): o = C() ref1 = weakref.ref(o, self.callback) @@ -166,7 +162,8 @@ class ReferencesTestCase(TestBase): c = C() self.assertRaises(TypeError, weakref.ref, c, callback=None) - @unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_proxy_ref(self): o = C() o.bar = 1 @@ -332,7 +329,8 @@ class ReferencesTestCase(TestBase): def test_shared_ref_without_callback(self): self.check_shared_without_callback(weakref.ref) - @unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'") + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_shared_proxy_without_callback(self): self.check_shared_without_callback(weakref.proxy) @@ -439,7 +437,6 @@ class ReferencesTestCase(TestBase): # can be killed in the middle of the call "blech" in p - @unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'") def test_getweakrefcount(self): o = C() ref1 = weakref.ref(o) @@ -828,8 +825,6 @@ class ReferencesTestCase(TestBase): # No exception should be raised here gc.collect() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_classes(self): # Check that classes are weakrefable. class A(object): @@ -1107,8 +1102,6 @@ class WeakMethodTestCase(unittest.TestCase): gc.collect() self.assertIs(r(), None) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_callback_when_object_dead(self): # Test callback behaviour when object dies first. C = self._subclass() @@ -1125,8 +1118,6 @@ class WeakMethodTestCase(unittest.TestCase): gc.collect() self.assertEqual(calls, [r]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_callback_when_method_dead(self): # Test callback behaviour when method dies first. C = self._subclass() @@ -1154,8 +1145,6 @@ class WeakMethodTestCase(unittest.TestCase): del r self.assertIs(wr(), None) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_equality(self): def _eq(a, b): self.assertTrue(a == b) @@ -1313,8 +1302,6 @@ class MappingTestCase(TestBase): dict[2] = C() self.assertRaises(KeyError, dict.__getitem__, 2) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_keys(self): # # This exercises d.copy(), d.items(), d[] = v, d[], del d[], @@ -1492,8 +1479,6 @@ class MappingTestCase(TestBase): self.assertEqual(len(dict), 0) self.assertEqual(len(dict), 0) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_keys_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed dict, objects = self.make_weak_keyed_dict() @@ -1724,8 +1709,6 @@ class MappingTestCase(TestBase): self.assertRaises(TypeError, d.__getitem__, 13) self.assertRaises(TypeError, d.__setitem__, 13, 13) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_keyed_cascading_deletes(self): # SF bug 742860. For some reason, before 2.3 __delitem__ iterated # over the keys via self.data.iterkeys(). If things vanished from @@ -1921,8 +1904,6 @@ class FinalizeTestCase(unittest.TestCase): if sys.implementation.name != 'cpython': support.gc_collect() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_finalize(self): def add(x,y,z): res.append(x + y + z) @@ -1995,8 +1976,6 @@ class FinalizeTestCase(unittest.TestCase): self.assertRaises(TypeError, weakref.finalize, a) self.assertRaises(TypeError, weakref.finalize) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_order(self): a = self.A() res = [] @@ -2036,8 +2015,6 @@ class FinalizeTestCase(unittest.TestCase): expected = ['A', 'f3', 'B', 'C', 'f4', 'f2', 'f1', 'D'] self.assertEqual(res, expected) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_all_freed(self): # we want a weakrefable subclass of weakref.finalize class MyFinalizer(weakref.finalize): diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py index 40a3bee49..307203850 100644 --- a/Lib/test/test_weakset.py +++ b/Lib/test/test_weakset.py @@ -43,8 +43,6 @@ class TestWeakSet(unittest.TestCase): def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) @@ -60,8 +58,6 @@ class TestWeakSet(unittest.TestCase): del self.obj self.assertNotIn(ustr('F'), self.fs) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_union(self): u = self.s.union(self.items2) for c in self.letters: @@ -84,8 +80,6 @@ class TestWeakSet(unittest.TestCase): self.assertEqual(self.s | set(self.items2), i) self.assertEqual(self.s | frozenset(self.items2), i) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_intersection(self): s = WeakSet(self.letters) i = s.intersection(self.items2) @@ -123,8 +117,6 @@ class TestWeakSet(unittest.TestCase): self.assertEqual(self.s - set(self.items2), i) self.assertEqual(self.s - frozenset(self.items2), i) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_symmetric_difference(self): i = self.s.symmetric_difference(self.items2) for c in self.letters: @@ -215,8 +207,6 @@ class TestWeakSet(unittest.TestCase): self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_add(self): x = ustr('Q') self.s.add(x) @@ -352,8 +342,6 @@ class TestWeakSet(unittest.TestCase): s2 = WeakSet() self.assertFalse(s1 != s2) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference @@ -370,8 +358,6 @@ class TestWeakSet(unittest.TestCase): # The removal has been committed self.assertEqual(len(s), len(items)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_destroy_and_mutate_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed items = [ustr(c) for c in string.ascii_letters] diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 9bcbfd1d6..8ead53407 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2345,8 +2345,6 @@ class BasicElementTest(ElementTestCase, unittest.TestCase): gc_collect() self.assertIsNone(wref()) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weakref(self): flag = False def wref_cb(w): From 65cde9a0d6e0f405c09876d58f5d00d24a9ee335 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Thu, 28 Oct 2021 16:02:15 -0500 Subject: [PATCH 19/40] Optimize size of WeakRefList The outer struct is only 1 word now, and inside the box it's a byte for the mutex (which gets padded to a word) + a word for the linked list (we don't use the tail) + a word for the pointer to the object. --- common/src/linked_list.rs | 76 +++++++++++++++++++-------------------- vm/src/pyobjectrc.rs | 57 +++++++++++++++++------------ 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/common/src/linked_list.rs b/common/src/linked_list.rs index 0f4013755..e31fe3c23 100644 --- a/common/src/linked_list.rs +++ b/common/src/linked_list.rs @@ -51,9 +51,8 @@ pub struct LinkedList { /// Linked list head head: Option>, - /// Linked list tail - tail: Option>, - + // /// Linked list tail + // tail: Option>, /// Node type marker. _marker: PhantomData<*const L>, } @@ -139,7 +138,7 @@ impl LinkedList { pub const fn new() -> LinkedList { LinkedList { head: None, - tail: None, + // tail: None, _marker: PhantomData, } } @@ -162,40 +161,41 @@ impl LinkedList { self.head = Some(ptr); - if self.tail.is_none() { - self.tail = Some(ptr); - } + // if self.tail.is_none() { + // self.tail = Some(ptr); + // } } } - /// Removes the last element from a list and returns it, or None if it is - /// empty. - pub fn pop_back(&mut self) -> Option { - unsafe { - let last = self.tail?; - self.tail = L::pointers(last).as_ref().get_prev(); + // /// Removes the last element from a list and returns it, or None if it is + // /// empty. + // pub fn pop_back(&mut self) -> Option { + // unsafe { + // let last = self.tail?; + // self.tail = L::pointers(last).as_ref().get_prev(); - if let Some(prev) = L::pointers(last).as_ref().get_prev() { - L::pointers(prev).as_mut().set_next(None); - } else { - self.head = None - } + // if let Some(prev) = L::pointers(last).as_ref().get_prev() { + // L::pointers(prev).as_mut().set_next(None); + // } else { + // self.head = None + // } - L::pointers(last).as_mut().set_prev(None); - L::pointers(last).as_mut().set_next(None); + // L::pointers(last).as_mut().set_prev(None); + // L::pointers(last).as_mut().set_next(None); - Some(L::from_raw(last)) - } - } + // Some(L::from_raw(last)) + // } + // } /// Returns whether the linked list does not contain any node pub fn is_empty(&self) -> bool { - if self.head.is_some() { - return false; - } + self.head.is_none() + // if self.head.is_some() { + // return false; + // } - assert!(self.tail.is_none()); - true + // assert!(self.tail.is_none()); + // true } /// Removes the specified node from the list @@ -224,12 +224,12 @@ impl LinkedList { .as_mut() .set_prev(L::pointers(node).as_ref().get_prev()); } else { - // This might be the last item in the list - if self.tail != Some(node) { - return None; - } + // // This might be the last item in the list + // if self.tail != Some(node) { + // return None; + // } - self.tail = L::pointers(node).as_ref().get_prev(); + // self.tail = L::pointers(node).as_ref().get_prev(); } L::pointers(node).as_mut().set_next(None); @@ -238,10 +238,10 @@ impl LinkedList { Some(L::from_raw(node)) } - pub fn last(&self) -> Option<&L::Target> { - let tail = self.tail.as_ref()?; - unsafe { Some(&*tail.as_ptr()) } - } + // pub fn last(&self) -> Option<&L::Target> { + // let tail = self.tail.as_ref()?; + // unsafe { Some(&*tail.as_ptr()) } + // } pub fn count(&self) -> usize { std::iter::successors(self.head, |node| unsafe { @@ -255,7 +255,7 @@ impl fmt::Debug for LinkedList { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LinkedList") .field("head", &self.head) - .field("tail", &self.tail) + // .field("tail", &self.tail) .finish() } } diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 890dd5a8a..02999e510 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -15,6 +15,11 @@ use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::{self, NonNull}; +#[cfg(feature = "threading")] +use once_cell::race::OnceBox; +#[cfg(not(feature = "threading"))] +type OnceBox = once_cell::unsync::OnceCell>; + // so, PyObjectRef is basically equivalent to `PyRc>`, except it's // only one pointer in width rather than 2. We do that by manually creating a vtable, and putting // a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do. @@ -103,7 +108,7 @@ impl fmt::Debug for PyInner { } struct WeakRefList { - weak_lock: PyMutex, + inner: OnceBox>, } impl fmt::Debug for WeakRefList { @@ -112,28 +117,37 @@ impl fmt::Debug for WeakRefList { } } -cfg_if::cfg_if! { - if #[cfg(feature = "threading")] { - unsafe impl Send for WeakRefList {} - unsafe impl Sync for WeakRefList {} - } -} - +#[derive(Default)] struct WeakListInner { list: LinkedList>, obj: Option>, } +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + unsafe impl Send for WeakListInner {} + unsafe impl Sync for WeakListInner {} + } +} + impl WeakRefList { pub fn new() -> Self { WeakRefList { - weak_lock: PyMutex::new(WeakListInner { - list: LinkedList::new(), - obj: None, - }), + inner: OnceBox::new(), } } + /// returns None if there have never been any weakrefs in this list + fn try_lock(&self) -> Option> { + self.inner.get().map(|mu| mu.lock()) + } + fn lock(&self) -> PyMutexGuard<'_, WeakListInner> { + self.inner + .get() + .expect("weakref should be initialized") + .lock() + } + fn add( &self, obj: &PyObject, @@ -141,7 +155,7 @@ impl WeakRefList { callback: Option, dict: Option, ) -> PyObjectWeak { - let mut inner = self.weak_lock.lock(); + let mut inner = self.inner.get_or_init(Default::default).lock(); if inner.obj.is_none() { inner.obj = Some(NonNull::from(obj)); } @@ -159,7 +173,10 @@ impl WeakRefList { } fn clear(&self) { - let mut inner = self.weak_lock.lock(); + let mut inner = match self.try_lock() { + Some(inner) => inner, + None => return, + }; inner.obj = None; // TODO: can be an arrayvec let mut v = Vec::with_capacity(16); @@ -201,7 +218,7 @@ impl WeakRefList { } fn count(&self) -> usize { - self.weak_lock.lock().list.count() + self.try_lock().map_or(0, |inner| inner.list.count()) } } @@ -249,13 +266,7 @@ cfg_if::cfg_if! { impl PyWeak { pub(crate) fn upgrade(&self) -> Option { // TODO: figure out orderings for here, in drop, and the store in WeakRefList::clear - let guard = unsafe { - self.parent - .load(Ordering::SeqCst) - .as_ref()? - .weak_lock - .lock() - }; + let guard = unsafe { self.parent.load(Ordering::SeqCst).as_ref()?.lock() }; let obj_ptr = guard.obj?; unsafe { obj_ptr.as_ref().0.refcount.incref(); @@ -267,7 +278,7 @@ impl PyWeak { fn drop_inner(&self) { let parent_ptr = self.parent.load(Ordering::SeqCst); let mut guard = match unsafe { parent_ptr.as_ref() } { - Some(parent) => parent.weak_lock.lock(), + Some(parent) => parent.lock(), // the object is dead - we don't have to worry about removing ourselves from the list None => return, }; From 86edc72c7efb3efbf43e3704ad56717a02253925 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Wed, 3 Nov 2021 14:02:01 -0500 Subject: [PATCH 20/40] Fix miri UB --- vm/src/pyobjectrc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 02999e510..cfff137c4 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -57,11 +57,11 @@ type OnceBox = once_cell::unsync::OnceCell>; struct Erased; struct PyObjVTable { - drop_dealloc: unsafe fn(&mut PyObject), + drop_dealloc: unsafe fn(*mut PyObject), debug: unsafe fn(&PyObject, &mut fmt::Formatter) -> fmt::Result, } -unsafe fn drop_dealloc_obj(x: &mut PyObject) { - Box::from_raw(x as *mut PyObject as *mut PyInner); +unsafe fn drop_dealloc_obj(x: *mut PyObject) { + Box::from_raw(x as *mut PyInner); } unsafe fn debug_obj(x: &PyObject, f: &mut fmt::Formatter) -> fmt::Result { let x = &*(x as *const PyObject as *const PyInner); @@ -683,7 +683,7 @@ impl PyObjectRef { } let drop_dealloc = self.0.vtable.drop_dealloc; - unsafe { drop_dealloc(self.ptr.as_mut()) } + unsafe { drop_dealloc(self.ptr.as_ptr()) } } } From 0b7c0fab61e44dbc6b6004fdc0c1df0ce75a4f5c Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Wed, 3 Nov 2021 15:10:21 -0500 Subject: [PATCH 21/40] Add miri test for dropping objects --- vm/src/pyobjectrc.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index cfff137c4..13284858c 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -1091,4 +1091,11 @@ mod tests { fn miri_test_type_initialization() { let _ = init_type_hierarchy(); } + + #[test] + fn miri_test_drop() { + let ctx = crate::PyContext::new(); + let obj = ctx.new_bytes(b"dfghjkl".to_vec()); + drop(obj); + } } From c6871166d113bec819a6ca5dacdf66efbd88ea70 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:12:48 -0500 Subject: [PATCH 22/40] Make WeakListInner a sidetable --- common/src/atomic.rs | 69 ++++++++++++++++ common/src/linked_list.rs | 10 +-- vm/src/pyobjectrc.rs | 166 ++++++++++++++++++++------------------ 3 files changed, 162 insertions(+), 83 deletions(-) diff --git a/common/src/atomic.rs b/common/src/atomic.rs index 5b4d77a74..b27f2d3e5 100644 --- a/common/src/atomic.rs +++ b/common/src/atomic.rs @@ -1,3 +1,4 @@ +use core::ptr::{self, NonNull}; pub use core::sync::atomic::*; pub use radium::Radium; @@ -50,3 +51,71 @@ impl sealed::Sealed for *mut T {} impl PyAtomicScalar for *mut T { type Radium = atomic_ty!(*mut T, AtomicPtr); } + +pub struct OncePtr { + inner: PyAtomic<*mut T>, +} + +impl OncePtr { + #[inline] + pub fn new() -> Self { + OncePtr { + inner: Radium::new(ptr::null_mut()), + } + } + + pub fn get(&self) -> Option> { + NonNull::new(self.inner.load(Ordering::Acquire)) + } + + pub fn set(&self, value: NonNull) -> Result<(), NonNull> { + let exchange = self.inner.compare_exchange( + ptr::null_mut(), + value.as_ptr(), + Ordering::AcqRel, + Ordering::Acquire, + ); + match exchange { + Ok(_) => Ok(()), + Err(_) => Err(value), + } + } + + pub fn get_or_init(&self, f: F) -> NonNull + where + F: FnOnce() -> Box, + { + enum Void {} + match self.get_or_try_init(|| Ok::<_, Void>(f())) { + Ok(val) => val, + Err(void) => match void {}, + } + } + + pub fn get_or_try_init(&self, f: F) -> Result, E> + where + F: FnOnce() -> Result, E>, + { + if let Some(val) = self.get() { + return Ok(val); + } + + Ok(self.initialize(f()?)) + } + + #[cold] + fn initialize(&self, val: Box) -> NonNull { + let ptr = Box::into_raw(val); + let exchange = + self.inner + .compare_exchange(ptr::null_mut(), ptr, Ordering::AcqRel, Ordering::Acquire); + let ptr = match exchange { + Ok(_) => ptr, + Err(winner) => { + drop(unsafe { Box::from_raw(ptr) }); + winner + } + }; + unsafe { NonNull::new_unchecked(ptr) } + } +} diff --git a/common/src/linked_list.rs b/common/src/linked_list.rs index e31fe3c23..0d508b94a 100644 --- a/common/src/linked_list.rs +++ b/common/src/linked_list.rs @@ -243,11 +243,13 @@ impl LinkedList { // unsafe { Some(&*tail.as_ptr()) } // } - pub fn count(&self) -> usize { + // === rustpython additions === + + pub fn iter<'a>(&'a self) -> impl Iterator { std::iter::successors(self.head, |node| unsafe { L::pointers(*node).as_ref().get_next() }) - .count() + .map(|ptr| unsafe { ptr.as_ref() }) } } @@ -324,10 +326,6 @@ impl Pointers { } } - pub fn is_null(&self) -> bool { - self.get_prev().is_none() && self.get_next().is_none() - } - fn get_prev(&self) -> Option> { // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. unsafe { diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 13284858c..49687487e 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -1,4 +1,4 @@ -use crate::common::atomic::{Ordering, PyAtomic, Radium}; +use crate::common::atomic::{OncePtr, PyAtomic, Radium}; use crate::common::linked_list::{Link, LinkedList, Pointers}; use crate::common::lock::{PyMutex, PyMutexGuard, PyRwLock}; use crate::common::refcount::RefCount; @@ -15,11 +15,6 @@ use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::{self, NonNull}; -#[cfg(feature = "threading")] -use once_cell::race::OnceBox; -#[cfg(not(feature = "threading"))] -type OnceBox = once_cell::unsync::OnceCell>; - // so, PyObjectRef is basically equivalent to `PyRc>`, except it's // only one pointer in width rather than 2. We do that by manually creating a vtable, and putting // a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do. @@ -108,7 +103,7 @@ impl fmt::Debug for PyInner { } struct WeakRefList { - inner: OnceBox>, + inner: OncePtr>, } impl fmt::Debug for WeakRefList { @@ -117,10 +112,21 @@ impl fmt::Debug for WeakRefList { } } -#[derive(Default)] struct WeakListInner { list: LinkedList>, obj: Option>, + // one for each live PyWeak with a reference to this, + 1 for the referent object if it's not dead + ref_count: usize, +} + +impl Default for WeakListInner { + fn default() -> Self { + Self { + list: LinkedList::default(), + obj: None, + ref_count: 1, + } + } } cfg_if::cfg_if! { @@ -133,19 +139,13 @@ cfg_if::cfg_if! { impl WeakRefList { pub fn new() -> Self { WeakRefList { - inner: OnceBox::new(), + inner: OncePtr::new(), } } /// returns None if there have never been any weakrefs in this list fn try_lock(&self) -> Option> { - self.inner.get().map(|mu| mu.lock()) - } - fn lock(&self) -> PyMutexGuard<'_, WeakListInner> { - self.inner - .get() - .expect("weakref should be initialized") - .lock() + self.inner.get().map(|mu| unsafe { mu.as_ref().lock() }) } fn add( @@ -155,70 +155,85 @@ impl WeakRefList { callback: Option, dict: Option, ) -> PyObjectWeak { - let mut inner = self.inner.get_or_init(Default::default).lock(); + let inner_ptr = self.inner.get_or_init(Default::default); + let mut inner = unsafe { inner_ptr.as_ref().lock() }; if inner.obj.is_none() { inner.obj = Some(NonNull::from(obj)); } let obj = PyWeak { pointers: Pointers::new(), - parent: Radium::new(self as *const WeakRefList as *mut WeakRefList), + parent: inner_ptr, callback: UnsafeCell::new(callback), hash: Radium::new(-1), }; let weak = PyRef::new_ref(obj, cls, dict); // SAFETY: we don't actually own the PyObjectWeaks inside `list`, and every time we take - // one out of the list we immediately wrap it in ManuallyDrop + // one out of the list we immediately wrap it in ManuallyDrop or forget it inner.list.push_front(unsafe { ptr::read(&weak) }); + inner.ref_count += 1; PyObjectWeak { weak } } fn clear(&self) { - let mut inner = match self.try_lock() { - Some(inner) => inner, - None => return, - }; - inner.obj = None; - // TODO: can be an arrayvec - let mut v = Vec::with_capacity(16); - loop { - let iter = inner - .list - .drain_filter(|_| true) - .filter_map(|wr| { - // we don't have actual ownership of the reference counts in the list. - // but, now we do want ownership (and so incref these *while the lock - // is held*) to avoid weird things if PyWeakObj::drop happens after - // this but before we reach the loop body below - let wr = ManuallyDrop::new(wr); + let to_dealloc = { + let ptr = match self.inner.get() { + Some(ptr) => ptr, + None => return, + }; + let mut inner = unsafe { ptr.as_ref().lock() }; + inner.obj = None; + // TODO: can be an arrayvec + let mut v = Vec::with_capacity(16); + loop { + let iter = inner + .list + .drain_filter(|_| true) + .filter_map(|wr| { + // we don't have actual ownership of the reference counts in the list. + // but, now we do want ownership (and so incref these *while the lock + // is held*) to avoid weird things if PyWeakObj::drop happens after + // this but before we reach the loop body below + let wr = ManuallyDrop::new(wr); - wr.parent.store(ptr::null_mut(), Ordering::SeqCst); - - // if strong_count == 0, drop_slow has been called but lock_parent is blocking - // in PyWeak::Drop OR there's some reentrancy going on. either way we don't - // want to call the callback - (wr.as_object().strong_count() > 0).then(|| (*wr).clone()) - }) - .take(16); - v.extend(iter); - if v.is_empty() { - break; - } - PyMutexGuard::unlocked(&mut inner, || { - for wr in v.drain(..) { - let cb = unsafe { wr.callback.get().replace(None) }; - if let Some(cb) = cb { - crate::vm::thread::with_vm(&cb, |vm| { - // TODO: handle unraisable exception - let _ = vm.invoke(&cb, (wr.clone(),)); - }); - } + // if strong_count == 0 there's some reentrancy going on. we don't + // want to call the callback + (wr.as_object().strong_count() > 0).then(|| (*wr).clone()) + }) + .take(16); + v.extend(iter); + if v.is_empty() { + break; } - }) + PyMutexGuard::unlocked(&mut inner, || { + for wr in v.drain(..) { + let cb = unsafe { wr.callback.get().replace(None) }; + if let Some(cb) = cb { + crate::vm::thread::with_vm(&cb, |vm| { + // TODO: handle unraisable exception + let _ = vm.invoke(&cb, (wr.clone(),)); + }); + } + } + }) + } + inner.ref_count -= 1; + (inner.ref_count == 0).then(|| ptr) + }; + if let Some(ptr) = to_dealloc { + unsafe { WeakRefList::dealloc(ptr) } } } fn count(&self) -> usize { - self.try_lock().map_or(0, |inner| inner.list.count()) + self.try_lock() + // we assume the object is still alive (and this is only + // called from PyObject::weak_count so it should be) + .map(|inner| inner.ref_count - 1) + .unwrap_or(0) + } + + unsafe fn dealloc(ptr: NonNull>) { + Box::from_raw(ptr.as_ptr()); } } @@ -251,7 +266,8 @@ unsafe impl Link for WeakLink { #[derive(Debug)] pub struct PyWeak { pointers: Pointers>, - parent: PyAtomic<*mut WeakRefList>, + parent: NonNull>, + // this is treated as part of parent's mutex - you must hold that lock to access it callback: UnsafeCell>, pub(crate) hash: PyAtomic, } @@ -266,7 +282,7 @@ cfg_if::cfg_if! { impl PyWeak { pub(crate) fn upgrade(&self) -> Option { // TODO: figure out orderings for here, in drop, and the store in WeakRefList::clear - let guard = unsafe { self.parent.load(Ordering::SeqCst).as_ref()?.lock() }; + let guard = unsafe { self.parent.as_ref().lock() }; let obj_ptr = guard.obj?; unsafe { obj_ptr.as_ref().0.refcount.incref(); @@ -276,24 +292,20 @@ impl PyWeak { #[inline(always)] fn drop_inner(&self) { - let parent_ptr = self.parent.load(Ordering::SeqCst); - let mut guard = match unsafe { parent_ptr.as_ref() } { - Some(parent) => parent.lock(), - // the object is dead - we don't have to worry about removing ourselves from the list - None => return, + let dealloc = { + let mut guard = unsafe { self.parent.as_ref().lock() }; + let offset = memoffset::offset_of!(PyInner, payload); + let pyinner = (self as *const Self as usize - offset) as *const PyInner; + let node_ptr = unsafe { NonNull::new_unchecked(pyinner as *mut PyObjectView) }; + // the list doesn't have ownership over its PyRef! we're being dropped + // right now so that should be obvious!! + std::mem::forget(unsafe { guard.list.remove(node_ptr) }); + guard.ref_count -= 1; + guard.ref_count == 0 }; - if self.parent.load(Ordering::SeqCst).is_null() { - // another thread is calling WeakRefList::clear(), and between the previous load and - // the lock() it reached our weakref in the .drain_filter() iterator. because of that, - // we know we've now been removed from the list, so we don't have to do anything - return; + if dealloc { + unsafe { WeakRefList::dealloc(self.parent) } } - let offset = memoffset::offset_of!(PyInner, payload); - let pyinner = (self as *const Self as usize - offset) as *const PyInner; - let node_ptr = unsafe { NonNull::new_unchecked(pyinner as *mut PyObjectView) }; - // the list doesn't have ownership over its PyRef! we're being dropped - // right now so that should be obvious!! - std::mem::forget(unsafe { guard.list.remove(node_ptr) }); } } From ec0a658d20f162fb6f1ddc92565b6b236639e9b6 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:20:35 -0500 Subject: [PATCH 23/40] Implement weakref.getweakrefs --- Lib/test/test_weakref.py | 4 ---- vm/src/pyobjectrc.rs | 12 ++++++++++++ vm/src/stdlib/weakref.rs | 8 +++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 2ab8ffb61..f91fc9a1d 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -458,8 +458,6 @@ class ReferencesTestCase(TestBase): self.assertEqual(weakref.getweakrefcount(1), 0, "got wrong number of weak reference objects for int") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_getweakrefs(self): o = C() ref1 = weakref.ref(o, self.callback) @@ -998,8 +996,6 @@ class SubclassableWeakrefTestCase(TestBase): self.assertIn(r1, refs[1:]) self.assertIn(r3, refs[1:]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_subclass_refs_dont_conflate_callbacks(self): class MyRef(weakref.ref): pass diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 49687487e..96cfc243f 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -574,6 +574,18 @@ impl PyObject { self.downgrade_with_typ(callback, vm.ctx.types.weakref_type.clone(), vm) } + pub fn get_weak_references(&self) -> Option> { + self.weakreflist().map(|wrl| { + wrl.try_lock() + .iter() + .flat_map(|inner| inner.list.iter()) + .map(|wr| PyObjectWeak { + weak: wr.to_owned(), + }) + .collect() + }) + } + pub fn payload_is(&self) -> bool { self.0.typeid == TypeId::of::() } diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index c943d511a..e4a1e9cac 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -37,9 +37,11 @@ mod _weakref { } #[pyfunction] - fn getweakrefs(_obj: PyObjectRef) -> Vec { - // TODO: implement this, may require a different gc - Vec::new() + fn getweakrefs(obj: PyObjectRef) -> Vec { + match obj.get_weak_references() { + Some(v) => v.into_iter().map(|weak| weak.into_object()).collect(), + None => vec![], + } } #[pyfunction] From f9d9a1e15e5304cc3d3d2e007ccf73bee2e77afd Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Fri, 5 Nov 2021 19:38:59 -0500 Subject: [PATCH 24/40] Implement weakref._remove_dead_weakref --- Lib/test/test_copy.py | 4 --- Lib/test/test_weakref.py | 4 --- vm/src/builtins/dict.rs | 6 ++++ vm/src/dictdatatype.rs | 73 ++++++++++++++++++++++++++-------------- vm/src/pyobjectrc.rs | 5 +++ vm/src/stdlib/weakref.rs | 18 ++++++++-- 6 files changed, 74 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 5c28a1eeb..c0988f953 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -829,8 +829,6 @@ class TestCopy(unittest.TestCase): def test_copy_weakkeydict(self): self._check_copy_weakdict(weakref.WeakKeyDictionary) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_copy_weakvaluedict(self): self._check_copy_weakdict(weakref.WeakValueDictionary) @@ -853,8 +851,6 @@ class TestCopy(unittest.TestCase): del c self.assertEqual(len(v), 1) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_deepcopy_weakvaluedict(self): class C(object): def __init__(self, i): diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index f91fc9a1d..67a17d341 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1267,8 +1267,6 @@ class MappingTestCase(TestBase): def test_weak_valued_len_race(self): self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_values(self): # # This exercises d.copy(), d.items(), d[], del d[], len(d). @@ -1501,8 +1499,6 @@ class MappingTestCase(TestBase): dict, objects = self.make_weak_keyed_dict() self.check_weak_del_and_len_while_iterating(dict, testcontext) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weak_values_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed dict, objects = self.make_weak_valued_dict() diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 0ee51973d..ba5ad5319 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -63,6 +63,12 @@ impl PyDict { #[allow(clippy::len_without_is_empty)] #[pyimpl(with(AsMapping, Hashable, Comparable, Iterable), flags(BASETYPE))] impl PyDict { + /// escape hatch to access the underlying data structure directly. prefer adding a method on + /// PyDict instead of using this + pub(crate) fn _as_dict_inner(&self) -> &DictContentType { + &self.entries + } + #[pyslot] fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { PyDict::default().into_pyresult_with_type(vm, cls) diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index dbbcfddde..eed94e622 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -13,6 +13,7 @@ use crate::{ }; use std::fmt; use std::mem::size_of; +use std::ops::ControlFlow; // HashIndex is intended to be same size with hash::PyHash // but it doesn't mean the values are compatible with actual pyhash value @@ -223,6 +224,8 @@ impl DictInner { } } +type PopInnerResult = ControlFlow>>; + impl Dict { fn read(&self) -> PyRwLockReadGuard<'_, DictInner> { self.inner.read() @@ -348,18 +351,23 @@ impl Dict { pub fn delete_if_exists(&self, vm: &VirtualMachine, key: &K) -> PyResult where K: DictKey, + { + self.delete_if(vm, key, |_| Ok(true)) + } + + /// pred should be VERY CAREFUL about what it does as it is called while + /// the dict's internal mutex is held + pub(crate) fn delete_if(&self, vm: &VirtualMachine, key: &K, pred: F) -> PyResult + where + K: DictKey, + F: Fn(&T) -> PyResult, { let hash = key.key_hash(vm)?; let deleted = loop { let lookup = self.lookup(vm, key, hash, None)?; - if let IndexEntry::Index(_) = lookup.0 { - if let Ok(Some(entry)) = self.pop_inner(lookup) { - break Some(entry); - } else { - // The dict was changed since we did lookup. Let's try again. - } - } else { - break None; + match self.pop_inner_if(lookup, &pred)? { + ControlFlow::Break(entry) => break entry, + ControlFlow::Continue(()) => continue, } }; Ok(deleted.is_some()) @@ -371,10 +379,9 @@ impl Dict { let lookup = self.lookup(vm, key, hash, None)?; let (entry, index_index) = lookup; if let IndexEntry::Index(_) = entry { - if let Ok(Some(entry)) = self.pop_inner(lookup) { - break Some(entry); - } else { - // The dict was changed since we did lookup. Let's try again. + match self.pop_inner(lookup) { + ControlFlow::Break(Some(entry)) => break Some(entry), + _ => continue, } } else { let mut inner = self.write(); @@ -573,25 +580,42 @@ impl Dict { } // returns Err(()) if changed since lookup - fn pop_inner(&self, lookup: LookupResult) -> Result>, ()> { + fn pop_inner(&self, lookup: LookupResult) -> PopInnerResult { + self.pop_inner_if(lookup, |_| Ok::<_, std::convert::Infallible>(true)) + .unwrap_or_else(|x| match x {}) + } + + fn pop_inner_if( + &self, + lookup: LookupResult, + pred: impl Fn(&T) -> Result, + ) -> Result, E> { let (entry_index, index_index) = lookup; let entry_index = if let IndexEntry::Index(entry_index) = entry_index { entry_index } else { - return Ok(None); + return Ok(ControlFlow::Break(None)); }; - let mut inner = self.write(); - if matches!(inner.entries.get(entry_index), Some(Some(entry)) if entry.index == index_index) - { - // all good + let inner = &mut *self.write(); + let slot = if let Some(slot) = inner.entries.get_mut(entry_index) { + slot } else { // The dict was changed since we did lookup. Let's try again. - return Err(()); + return Ok(ControlFlow::Continue(())); }; + match slot { + Some(entry) if entry.index == index_index => { + if pred(&entry.value)? == false { + return Ok(ControlFlow::Break(None)); + } + } + // The dict was changed since we did lookup. Let's try again. + _ => return Ok(ControlFlow::Continue(())), + } inner.indices[index_index] = IndexEntry::DUMMY; inner.used -= 1; - let removed = std::mem::take(&mut inner.entries[entry_index]); - Ok(removed) + let removed = slot.take(); + Ok(ControlFlow::Break(removed)) } /// Retrieve and delete a key @@ -599,10 +623,9 @@ impl Dict { let hash_value = key.key_hash(vm)?; let removed = loop { let lookup = self.lookup(vm, key, hash_value, None)?; - if let Ok(ret) = self.pop_inner(lookup) { - break ret.map(|e| e.value); - } else { - // changed since lookup, loop again + match self.pop_inner(lookup) { + ControlFlow::Break(entry) => break entry.map(|e| e.value), + ControlFlow::Continue(()) => continue, } }; Ok(removed) diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 96cfc243f..750651c75 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -290,6 +290,11 @@ impl PyWeak { } } + pub(crate) fn is_dead(&self) -> bool { + let guard = unsafe { self.parent.as_ref().lock() }; + guard.obj.is_none() + } + #[inline(always)] fn drop_inner(&self) { let dealloc = { diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index e4a1e9cac..ffeb03628 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -8,7 +8,8 @@ pub(crate) use _weakref::make_module; #[pymodule] mod _weakref { - use crate::{builtins::PyTypeRef, PyObjectRef, VirtualMachine}; + use crate::builtins::{PyDictRef, PyTypeRef, PyWeak}; + use crate::{PyObjectRef, PyResult, VirtualMachine}; #[pyattr(name = "ref")] fn ref_(vm: &VirtualMachine) -> PyTypeRef { @@ -45,7 +46,18 @@ mod _weakref { } #[pyfunction] - fn _remove_dead_weakref(_obj: PyObjectRef, _key: PyObjectRef) { - // TODO + fn _remove_dead_weakref( + dict: PyDictRef, + key: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + dict._as_dict_inner() + .delete_if(vm, &key, |wr| { + let wr = wr + .payload::() + .ok_or_else(|| vm.new_type_error("not a weakref".to_owned()))?; + Ok(wr.is_dead()) + }) + .map(drop) } } From 2a87b893fbdfa648e401a9ae0704461ed4e41fbe Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:41:03 -0600 Subject: [PATCH 25/40] Reuse standard no-cb weakref --- Lib/test/test_weakref.py | 2 - vm/src/builtins/pytype.rs | 2 +- vm/src/builtins/weakproxy.rs | 16 ++++++- vm/src/builtins/weakref.rs | 2 +- vm/src/pyobjectrc.rs | 86 ++++++++++++++++++++++++------------ 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 67a17d341..3b783c456 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -198,8 +198,6 @@ class ReferencesTestCase(TestBase): self.assertIsNone(ref(), "ref2 should be dead after deleting object reference") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_ref_reuse(self): o = C() ref1 = weakref.ref(o) diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index b6e249595..3ed6f77bc 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -121,7 +121,7 @@ impl PyType { base.subclasses.write().push( new_type .as_object() - .downgrade_with_typ_opt(None, weakref_type.clone()) + .downgrade_with_weakref_typ_opt(None, weakref_type.clone()) .unwrap(), ); } diff --git a/vm/src/builtins/weakproxy.rs b/vm/src/builtins/weakproxy.rs index f658a3893..8cf4ab404 100644 --- a/vm/src/builtins/weakproxy.rs +++ b/vm/src/builtins/weakproxy.rs @@ -33,14 +33,28 @@ impl Constructor for PyWeakProxy { Self::Args { referent, callback }: Self::Args, vm: &VirtualMachine, ) -> PyResult { + // using an internal subclass as the class prevents us from getting the generic weakref, + // which would mess up the weakref count + let weak_cls = WEAK_SUBCLASS.get_or_init(|| { + vm.ctx.new_class( + None, + "__weakproxy", + &vm.ctx.types.weakref_type, + super::PyWeak::make_slots(), + ) + }); // TODO: PyWeakProxy should use the same payload as PyWeak PyWeakProxy { - weak: referent.downgrade(callback.into_option(), vm)?, + weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?, } .into_pyresult_with_type(vm, cls) } } +crate::common::static_cell! { + static WEAK_SUBCLASS: PyTypeRef; +} + #[pyimpl(with(SetAttr, Constructor))] impl PyWeakProxy { // TODO: callbacks diff --git a/vm/src/builtins/weakref.rs b/vm/src/builtins/weakref.rs index df600ce64..841681444 100644 --- a/vm/src/builtins/weakref.rs +++ b/vm/src/builtins/weakref.rs @@ -105,7 +105,7 @@ impl Comparable for PyWeak { let both = zelf.upgrade().and_then(|s| other.upgrade().map(|o| (s, o))); let eq = match both { Some((a, b)) => vm.bool_eq(&a, &b)?, - None => false, + None => zelf.is(other), }; Ok(eq.into()) }) diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 750651c75..5e844fad6 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -114,21 +114,12 @@ impl fmt::Debug for WeakRefList { struct WeakListInner { list: LinkedList>, + generic_weakref: Option>>, obj: Option>, // one for each live PyWeak with a reference to this, + 1 for the referent object if it's not dead ref_count: usize, } -impl Default for WeakListInner { - fn default() -> Self { - Self { - list: LinkedList::default(), - obj: None, - ref_count: 1, - } - } -} - cfg_if::cfg_if! { if #[cfg(feature = "threading")] { unsafe impl Send for WeakListInner {} @@ -152,13 +143,29 @@ impl WeakRefList { &self, obj: &PyObject, cls: PyTypeRef, + cls_is_weakref: bool, callback: Option, dict: Option, ) -> PyObjectWeak { - let inner_ptr = self.inner.get_or_init(Default::default); + let is_generic = cls_is_weakref && callback.is_none(); + let inner_ptr = self.inner.get_or_init(|| { + Box::new(PyMutex::new(WeakListInner { + list: LinkedList::default(), + generic_weakref: None, + obj: Some(NonNull::from(obj)), + ref_count: 1, + })) + }); let mut inner = unsafe { inner_ptr.as_ref().lock() }; - if inner.obj.is_none() { - inner.obj = Some(NonNull::from(obj)); + if is_generic { + if let Some(generic_weakref) = inner.generic_weakref { + let generic_weakref = unsafe { generic_weakref.as_ref() }; + if generic_weakref.0.refcount.get() != 0 { + return PyObjectWeak { + weak: generic_weakref.to_owned(), + }; + } + } } let obj = PyWeak { pointers: Pointers::new(), @@ -171,6 +178,9 @@ impl WeakRefList { // one out of the list we immediately wrap it in ManuallyDrop or forget it inner.list.push_front(unsafe { ptr::read(&weak) }); inner.ref_count += 1; + if is_generic { + inner.generic_weakref = Some(NonNull::from(&*weak)); + } PyObjectWeak { weak } } @@ -185,7 +195,8 @@ impl WeakRefList { // TODO: can be an arrayvec let mut v = Vec::with_capacity(16); loop { - let iter = inner + let inner2 = &mut *inner; + let iter = inner2 .list .drain_filter(|_| true) .filter_map(|wr| { @@ -195,6 +206,10 @@ impl WeakRefList { // this but before we reach the loop body below let wr = ManuallyDrop::new(wr); + if Some(NonNull::from(&**wr)) == inner2.generic_weakref { + inner2.generic_weakref = None + } + // if strong_count == 0 there's some reentrancy going on. we don't // want to call the callback (wr.as_object().strong_count() > 0).then(|| (*wr).clone()) @@ -235,6 +250,24 @@ impl WeakRefList { unsafe fn dealloc(ptr: NonNull>) { Box::from_raw(ptr.as_ptr()); } + + fn get_weak_references(&self) -> Vec { + let inner = match self.try_lock() { + Some(inner) => inner, + None => return vec![], + }; + let mut v = Vec::with_capacity(inner.ref_count - 1); + v.extend(inner.iter().map(|wr| PyObjectWeak { + weak: wr.to_owned(), + })); + v + } +} + +impl WeakListInner { + fn iter<'a>(&'a self) -> impl Iterator> { + self.list.iter().filter(|wr| wr.0.refcount.get() > 0) + } } impl Default for WeakRefList { @@ -306,6 +339,9 @@ impl PyWeak { // right now so that should be obvious!! std::mem::forget(unsafe { guard.list.remove(node_ptr) }); guard.ref_count -= 1; + if Some(node_ptr) == guard.generic_weakref { + guard.generic_weakref = None; + } guard.ref_count == 0 }; if dealloc { @@ -537,13 +573,14 @@ impl PyObject { Some(&self.0.weaklist) } - pub(crate) fn downgrade_with_typ_opt( + pub(crate) fn downgrade_with_weakref_typ_opt( &self, callback: Option, + // a reference to weakref_type **specifically** typ: PyTypeRef, ) -> Option { self.weakreflist() - .map(|wr| wr.add(self, typ, callback, None)) + .map(|wr| wr.add(self, typ, true, callback, None)) } pub(crate) fn downgrade_with_typ( @@ -561,8 +598,9 @@ impl PyObject { } else { None }; + let cls_is_weakref = typ.is(&vm.ctx.types.weakref_type); self.weakreflist() - .map(|wr| wr.add(self, typ, callback, dict)) + .map(|wr| wr.add(self, typ, cls_is_weakref, callback, dict)) .ok_or_else(|| { vm.new_type_error(format!( "cannot create weak reference to '{}' object", @@ -580,15 +618,7 @@ impl PyObject { } pub fn get_weak_references(&self) -> Option> { - self.weakreflist().map(|wrl| { - wrl.try_lock() - .iter() - .flat_map(|inner| inner.list.iter()) - .map(|wr| PyObjectWeak { - weak: wr.to_owned(), - }) - .collect() - }) + self.weakreflist().map(|wrl| wrl.get_weak_references()) } pub fn payload_is(&self) -> bool { @@ -1099,14 +1129,14 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { object_type.subclasses.write().push( type_type .as_object() - .downgrade_with_typ_opt(None, weakref_type.clone()) + .downgrade_with_weakref_typ_opt(None, weakref_type.clone()) .unwrap(), ); object_type.subclasses.write().push( weakref_type .as_object() - .downgrade_with_typ_opt(None, weakref_type.clone()) + .downgrade_with_weakref_typ_opt(None, weakref_type.clone()) .unwrap(), ); From 1da239ac0809463bde41c0e18f24e4ef5528f953 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:44:02 -0600 Subject: [PATCH 26/40] Fix clippy --- common/src/atomic.rs | 6 ++++++ common/src/linked_list.rs | 2 +- vm/src/dictdatatype.rs | 2 +- vm/src/pyobjectrc.rs | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/common/src/atomic.rs b/common/src/atomic.rs index b27f2d3e5..afe4afb44 100644 --- a/common/src/atomic.rs +++ b/common/src/atomic.rs @@ -56,6 +56,12 @@ pub struct OncePtr { inner: PyAtomic<*mut T>, } +impl Default for OncePtr { + fn default() -> Self { + Self::new() + } +} + impl OncePtr { #[inline] pub fn new() -> Self { diff --git a/common/src/linked_list.rs b/common/src/linked_list.rs index 0d508b94a..5834a1443 100644 --- a/common/src/linked_list.rs +++ b/common/src/linked_list.rs @@ -245,7 +245,7 @@ impl LinkedList { // === rustpython additions === - pub fn iter<'a>(&'a self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { std::iter::successors(self.head, |node| unsafe { L::pointers(*node).as_ref().get_next() }) diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index eed94e622..4fe19d014 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -605,7 +605,7 @@ impl Dict { }; match slot { Some(entry) if entry.index == index_index => { - if pred(&entry.value)? == false { + if !pred(&entry.value)? { return Ok(ControlFlow::Break(None)); } } diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 5e844fad6..7a031de8a 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -265,7 +265,7 @@ impl WeakRefList { } impl WeakListInner { - fn iter<'a>(&'a self) -> impl Iterator> { + fn iter(&self) -> impl Iterator> { self.list.iter().filter(|wr| wr.0.refcount.get() > 0) } } From 8c7def00ec9d6b33d86c96928635526ff1b91d8d Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:46:20 -0600 Subject: [PATCH 27/40] Address review --- common/src/refcount.rs | 4 ++-- vm/src/pyobjectrc.rs | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/common/src/refcount.rs b/common/src/refcount.rs index bf6705b13..1003d8536 100644 --- a/common/src/refcount.rs +++ b/common/src/refcount.rs @@ -30,7 +30,7 @@ impl RefCount { } #[inline] - pub fn incref(&self) { + pub fn inc(&self) { let old_size = self.strong.fetch_add(1, Relaxed); if old_size > MAX_REFCOUNT { @@ -40,7 +40,7 @@ impl RefCount { /// Decrement the reference count. Returns true when the refcount drops to 0. #[inline] - pub fn decref(&self) -> bool { + pub fn dec(&self) -> bool { if self.strong.fetch_sub(1, Release) != 1 { return false; } diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 7a031de8a..bdaa0bf67 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -318,7 +318,7 @@ impl PyWeak { let guard = unsafe { self.parent.as_ref().lock() }; let obj_ptr = guard.obj?; unsafe { - obj_ptr.as_ref().0.refcount.incref(); + obj_ptr.as_ref().0.refcount.inc(); Some(PyObjectRef::from_raw(obj_ptr.as_ptr())) } } @@ -464,7 +464,7 @@ impl ToOwned for PyObject { #[inline(always)] fn to_owned(&self) -> Self::Owned { - self.0.refcount.incref(); + self.0.refcount.inc(); PyObjectRef { ptr: NonNull::from(self), } @@ -569,7 +569,7 @@ impl PyObjectRef { impl PyObject { #[inline] - fn weakreflist(&self) -> Option<&WeakRefList> { + fn weak_ref_list(&self) -> Option<&WeakRefList> { Some(&self.0.weaklist) } @@ -579,8 +579,8 @@ impl PyObject { // a reference to weakref_type **specifically** typ: PyTypeRef, ) -> Option { - self.weakreflist() - .map(|wr| wr.add(self, typ, true, callback, None)) + self.weak_ref_list() + .map(|wrl| wrl.add(self, typ, true, callback, None)) } pub(crate) fn downgrade_with_typ( @@ -599,8 +599,8 @@ impl PyObject { None }; let cls_is_weakref = typ.is(&vm.ctx.types.weakref_type); - self.weakreflist() - .map(|wr| wr.add(self, typ, cls_is_weakref, callback, dict)) + self.weak_ref_list() + .map(|wrl| wrl.add(self, typ, cls_is_weakref, callback, dict)) .ok_or_else(|| { vm.new_type_error(format!( "cannot create weak reference to '{}' object", @@ -618,7 +618,7 @@ impl PyObject { } pub fn get_weak_references(&self) -> Option> { - self.weakreflist().map(|wrl| wrl.get_weak_references()) + self.weak_ref_list().map(|wrl| wrl.get_weak_references()) } pub fn payload_is(&self) -> bool { @@ -705,7 +705,7 @@ impl PyObject { #[inline] pub fn weak_count(&self) -> Option { - self.weakreflist().map(|wrl| wrl.count()) + self.weak_ref_list().map(|wrl| wrl.count()) } #[inline] @@ -721,11 +721,11 @@ impl PyObjectRef { // CPython-compatible drop implementation if let Some(slot_del) = self.class().mro_find_map(|cls| cls.slots.del.load()) { let ret = crate::vm::thread::with_vm(self, |vm| { - self.0.refcount.incref(); + self.0.refcount.inc(); if let Err(e) = slot_del(self, vm) { print_del_error(e, self, vm); } - self.0.refcount.decref() + self.0.refcount.dec() }); match ret { // the decref right above set refcount back to 0 @@ -737,7 +737,7 @@ impl PyObjectRef { } } } - if let Some(wrl) = self.weakreflist() { + if let Some(wrl) = self.weak_ref_list() { wrl.clear(); } @@ -804,7 +804,7 @@ impl PyObjectWeak { impl Drop for PyObjectRef { fn drop(&mut self) { - if self.0.refcount.decref() { + if self.0.refcount.dec() { self.drop_slow() } } @@ -1093,12 +1093,12 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { type_type_ptr as *mut MaybeUninit> as *mut PyInner; unsafe { - (*type_type_ptr).refcount.incref(); + (*type_type_ptr).refcount.inc(); ptr::write( &mut (*object_type_ptr).typ as *mut PyRwLock as *mut UninitRef, PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), ); - (*type_type_ptr).refcount.incref(); + (*type_type_ptr).refcount.inc(); ptr::write( &mut (*type_type_ptr).typ as *mut PyRwLock as *mut UninitRef, PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), From 229a9c8578f88be9f249cd27f20f2a959b7561ac Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Tue, 9 Nov 2021 16:04:02 -0600 Subject: [PATCH 28/40] Make PyRef wrap a NonNull> This makes debuggers (or rust-gdb, at least) more pleasant to use, since you don't have to manually cast `PyRef.obj.ptr as `*const PyObjectView` Also get rid of PyGenericObject, since it's vestigial --- derive/src/pyclass.rs | 2 +- vm/src/builtins/object.rs | 6 +- vm/src/pyobject.rs | 5 +- vm/src/pyobjectrc.rs | 119 ++++++++++++++++++--------------- vm/src/vm.rs | 2 +- wasm/lib/src/browser_module.rs | 6 +- 6 files changed, 75 insertions(+), 65 deletions(-) diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 81a7a6aff..b148e951a 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -667,7 +667,7 @@ impl ToTokens for GetSetNursery { #( #cfgs )* class.set_str_attr( #name, - ::rustpython_vm::PyGenericObject::new( + ::rustpython_vm::PyRef::new_ref( ::rustpython_vm::builtins::PyGetSet::new(#name.into(), class.clone()) .with_get(&Self::#getter) #setter #deleter, diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 8467bcd10..bbd64e54e 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -2,8 +2,8 @@ use super::{PyDict, PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::common::hash::PyHash; use crate::{ function::FuncArgs, types::PyComparisonOp, utils::Either, IdProtocol, ItemProtocol, - PyArithmeticValue, PyAttributes, PyClassImpl, PyComparisonValue, PyContext, PyGenericObject, - PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, VirtualMachine, + PyArithmeticValue, PyAttributes, PyClassImpl, PyComparisonValue, PyContext, PyObject, + PyObjectRef, PyResult, PyValue, TypeProtocol, VirtualMachine, }; /// object() @@ -34,7 +34,7 @@ impl PyBaseObject { } else { Some(vm.ctx.new_dict()) }; - Ok(PyGenericObject::new(PyBaseObject, cls, dict)) + Ok(crate::PyRef::new_ref(PyBaseObject, cls, dict).into()) } #[pyslot] diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 82d29f862..e6f0abd5e 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -4,8 +4,7 @@ use crate::common::{ static_cell, }; pub use crate::pyobjectrc::{ - PyGenericObject, PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyObjectWrap, PyRef, - PyWeakRef, + PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyObjectWrap, PyRef, PyWeakRef, }; use crate::{ builtins::{ @@ -334,7 +333,7 @@ impl PyContext { class.slots.flags.contains(PyTypeFlags::HAS_DICT), dict.is_some() ); - PyGenericObject::new(object::PyBaseObject, class, dict) + PyRef::new_ref(object::PyBaseObject, class, dict).into() } } diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index bdaa0bf67..91271af9f 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -15,7 +15,7 @@ use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::{self, NonNull}; -// so, PyObjectRef is basically equivalent to `PyRc>`, except it's +// so, PyObjectRef is basically equivalent to `PyRc>`, except it's // only one pointer in width rather than 2. We do that by manually creating a vtable, and putting // a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do. // This can lead to faster code since there's just less data to pass around, as well as because of @@ -32,18 +32,18 @@ use std::ptr::{self, NonNull}; // There has to be padding in the space between the 2 fields. But, if that field is a trait object // (like `dyn PyObjectPayload`) we don't *know* how much padding there is between the `payload` // field and the previous field. So, Rust has to consult the vtable to know the exact offset of -// `payload` in `PyGenericObject`, which has a huge performance impact when *every +// `payload` in `PyInner`, which has a huge performance impact when *every // single payload access* requires a vtable lookup. Thankfully, we're able to avoid that because of // the way we use PyObjectRef, in that whenever we want to access the payload we (almost) always // access it from a generic function. So, rather than doing // // - check vtable for payload offset -// - get offset in PyGenericObject struct +// - get offset in PyInner struct // - call as_any() method of PyObjectPayload // - call downcast_ref() method of Any // we can just do // - check vtable that typeid matches -// - pointer cast directly to *const PyGenericObject +// - pointer cast directly to *const PyInner // // and at that point the compiler can know the offset of `payload` for us because **we've given it a // concrete type to work with before we ever access the `payload` field** @@ -395,18 +395,9 @@ impl InstanceDict { } } -/// This is an actual python object. It consists of a `typ` which is the -/// python class, and carries some rust payload optionally. This rust -/// payload can be a rust float or rust int in case of float and int objects. -#[repr(transparent)] -pub struct PyGenericObject { - inner: PyInner, -} - -impl PyGenericObject { - #[allow(clippy::new_ret_no_self)] - pub fn new(payload: T, typ: PyTypeRef, dict: Option) -> PyObjectRef { - let inner = PyInner { +impl PyInner { + fn new(payload: T, typ: PyTypeRef, dict: Option) -> Box { + Box::new(PyInner { refcount: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), @@ -414,8 +405,7 @@ impl PyGenericObject { dict: dict.map(InstanceDict::new), weaklist: WeakRefList::new(), payload, - }; - PyObjectRef::new(PyGenericObject { inner }) + }) } } @@ -501,13 +491,6 @@ impl PyObjectRef { } } - fn new(value: PyGenericObject) -> Self { - let inner: *const PyInner = Box::into_raw(Box::new(value.inner)); - Self { - ptr: unsafe { NonNull::new_unchecked(inner as *mut PyObject) }, - } - } - /// Attempt to downcast this reference to a subclass. /// /// If the downcast fails, the original ref is returned in as `Err` so @@ -712,12 +695,9 @@ impl PyObject { pub fn as_raw(&self) -> *const PyObject { self } -} -impl PyObjectRef { - #[inline(never)] - #[cold] - fn drop_slow(&mut self) { + #[inline] + fn drop_slow_inner(&self) -> Result<(), ()> { // CPython-compatible drop implementation if let Some(slot_del) = self.class().mro_find_map(|cls| cls.slots.del.load()) { let ret = crate::vm::thread::with_vm(self, |vm| { @@ -731,7 +711,7 @@ impl PyObjectRef { // the decref right above set refcount back to 0 Some(true) => {} // we've been resurrected by __del__ - Some(false) => return, + Some(false) => return Err(()), None => { warn!("couldn't run __del__ method for object") } @@ -741,8 +721,20 @@ impl PyObjectRef { wrl.clear(); } - let drop_dealloc = self.0.vtable.drop_dealloc; - unsafe { drop_dealloc(self.ptr.as_ptr()) } + Ok(()) + } + + /// Can only be called when refcount has dropped to zero. `ptr` must be valid + #[inline(never)] + #[cold] + unsafe fn drop_slow(ptr: NonNull) { + if let Err(()) = ptr.as_ref().drop_slow_inner() { + // abort drop for whatever reason + return; + } + let drop_dealloc = ptr.as_ref().0.vtable.drop_dealloc; + // call drop only when there are no references in scope - stacked borrows stuff + drop_dealloc(ptr.as_ptr()) } } @@ -805,7 +797,7 @@ impl PyObjectWeak { impl Drop for PyObjectRef { fn drop(&mut self) { if self.0.refcount.dec() { - self.drop_slow() + unsafe { PyObject::drop_slow(self.ptr) } } } } @@ -870,9 +862,9 @@ impl ToOwned for PyObjectView { #[inline(always)] fn to_owned(&self) -> Self::Owned { + self.0.refcount.inc(); PyRef { - obj: self.as_object().to_owned(), - _marker: PhantomData, + ptr: NonNull::from(self), } } } @@ -911,9 +903,14 @@ impl fmt::Debug for PyObjectView { /// where a reference to the same object must be returned. #[repr(transparent)] pub struct PyRef { - // invariant: this obj must always have payload of type T - obj: PyObjectRef, - _marker: PhantomData, + ptr: NonNull>, +} + +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + unsafe impl Send for PyRef {} + unsafe impl Sync for PyRef {} + } } impl fmt::Debug for PyRef { @@ -922,6 +919,15 @@ impl fmt::Debug for PyRef { } } +impl Drop for PyRef { + #[inline] + fn drop(&mut self) { + if self.0.refcount.dec() { + unsafe { PyObject::drop_slow(self.ptr.cast::()) } + } + } +} + impl Clone for PyRef { #[inline(always)] fn clone(&self) -> Self { @@ -932,25 +938,26 @@ impl Clone for PyRef { impl PyRef { unsafe fn from_raw(raw: *const PyObjectView) -> Self { Self { - obj: PyObjectRef::from_raw(raw as _), - _marker: PhantomData, + ptr: NonNull::new_unchecked(raw as *mut _), } } /// Safety: payload type of `obj` must be `T` + #[inline] unsafe fn from_obj_unchecked(obj: PyObjectRef) -> Self { debug_assert!(obj.payload_is::()); + let obj = ManuallyDrop::new(obj); Self { - obj, - _marker: PhantomData, + ptr: obj.ptr.cast(), } } - // ideally we'd be able to define this in pyobject.rs, but method visibility rules are weird + #[inline] pub fn new_ref(payload: T, typ: crate::builtins::PyTypeRef, dict: Option) -> Self { - let obj = PyGenericObject::new(payload, typ, dict); - // SAFETY: we just created the object from a payload of type T - unsafe { Self::from_obj_unchecked(obj) } + let inner = Box::into_raw(PyInner::new(payload, typ, dict)); + Self { + ptr: unsafe { NonNull::new_unchecked(inner.cast::>()) }, + } } } @@ -958,8 +965,10 @@ impl PyObjectWrap for PyRef where T: PyObjectPayload, { + #[inline] fn into_object(self) -> PyObjectRef { - self.obj + let me = ManuallyDrop::new(self); + PyObjectRef { ptr: me.ptr.cast() } } } @@ -967,8 +976,9 @@ impl AsRef for PyRef where T: PyObjectPayload, { + #[inline(always)] fn as_ref(&self) -> &PyObject { - &self.obj + (**self).as_object() } } @@ -987,8 +997,9 @@ where { type Target = PyObjectView; + #[inline(always)] fn deref(&self) -> &PyObjectView { - unsafe { &*(&*self.obj as *const PyObject as *const PyObjectView) } + unsafe { self.ptr.as_ref() } } } @@ -1000,10 +1011,10 @@ pub struct PyWeakRef { impl PyWeakRef { pub fn upgrade(&self) -> Option> { - self.weak.upgrade().map(|obj| PyRef { - obj, - _marker: PhantomData, - }) + self.weak + .upgrade() + // SAFETY: PyWeakRef was always created from a PyRef, so the object is T + .map(|obj| unsafe { PyRef::from_obj_unchecked(obj) }) } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index b097fed30..49b2583f7 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1936,7 +1936,7 @@ impl<'vm> ReprGuard<'vm> { let mut guards = vm.repr_guards.borrow_mut(); // Should this be a flag on the obj itself? putting it in a global variable for now until it - // decided the form of the PyGenericObject. https://github.com/RustPython/RustPython/issues/371 + // decided the form of PyObject. https://github.com/RustPython/RustPython/issues/371 let id = obj.get_id(); if guards.contains(&id) { return None; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 49996bd0f..2a4f1ae62 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -10,7 +10,7 @@ mod _browser { builtins::{PyDictRef, PyStrRef}, function::{ArgCallable, IntoPyObject, OptionalArg}, import::import_file, - PyClassImpl, PyGenericObject, PyObjectRef, PyResult, PyValue, VirtualMachine, + PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, }; use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen_futures::JsFuture; @@ -179,8 +179,8 @@ mod _browser { } #[pyattr] - fn document(vm: &VirtualMachine) -> PyObjectRef { - PyGenericObject::new( + fn document(vm: &VirtualMachine) -> PyRef { + PyRef::new_ref( Document { doc: window().document().expect("Document missing from window"), }, From af7ec416f90b23f7403ae2b979a0884cd30e9e75 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Tue, 9 Nov 2021 22:52:46 -0600 Subject: [PATCH 29/40] Prevent edge case race condition, hopefully --- Cargo.lock | 4 ++-- common/Cargo.toml | 2 +- common/src/refcount.rs | 8 ++++++++ vm/src/pyobjectrc.rs | 5 +++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1977f5085..a216107f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1524,9 +1524,9 @@ dependencies = [ [[package]] name = "radium" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "radix_trie" diff --git a/common/Cargo.toml b/common/Cargo.toml index e74de0855..4b845c737 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -20,7 +20,7 @@ once_cell = "1.4.1" siphasher = "0.3" rand = "0.8" volatile = "0.3" -radium = "0.6" +radium = "0.7" libc = "0.2.101" ascii = "1.0" unic-ucd-category = "0.9" diff --git a/common/src/refcount.rs b/common/src/refcount.rs index 1003d8536..6db81e5ab 100644 --- a/common/src/refcount.rs +++ b/common/src/refcount.rs @@ -38,6 +38,14 @@ impl RefCount { } } + /// Returns true if successful + #[inline] + pub fn safe_inc(&self) -> bool { + self.strong + .fetch_update(AcqRel, Acquire, |prev| (prev != 0).then(|| prev + 1)) + .is_ok() + } + /// Decrement the reference count. Returns true when the refcount drops to 0. #[inline] pub fn dec(&self) -> bool { diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index 91271af9f..da0b3c1a0 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -314,11 +314,12 @@ cfg_if::cfg_if! { impl PyWeak { pub(crate) fn upgrade(&self) -> Option { - // TODO: figure out orderings for here, in drop, and the store in WeakRefList::clear let guard = unsafe { self.parent.as_ref().lock() }; let obj_ptr = guard.obj?; unsafe { - obj_ptr.as_ref().0.refcount.inc(); + if !obj_ptr.as_ref().0.refcount.safe_inc() { + return None; + } Some(PyObjectRef::from_raw(obj_ptr.as_ptr())) } } From 913b78ca44f59f0e956ea61489478f712e9d2249 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 17 Nov 2021 17:06:51 +0900 Subject: [PATCH 30/40] Revert "Merge pull request #3433 from deantvv/test-update" This reverts commit 9fa5c5ac66b4e29c5a00d77d38dc25b824109f8f, reversing changes made to e7fa32c687e2da83ec69cbc53a718aa69555b1ba. --- Lib/test/libregrtest/cmdline.py | 3 +- Lib/test/libregrtest/main.py | 9 +- Lib/test/libregrtest/refleak.py | 2 +- Lib/test/libregrtest/runtest.py | 3 +- Lib/test/libregrtest/runtest_mp.py | 2 +- Lib/test/libregrtest/save_env.py | 7 +- Lib/test/libregrtest/win_utils.py | 2 +- Lib/test/list_tests.py | 7 +- Lib/test/support/__init__.py | 841 ++++++++---- Lib/test/support/os_helper.py | 617 --------- Lib/test/test__osx_support.py | 3 +- Lib/test/test_argparse.py | 5 +- Lib/test/test_array.py | 20 +- Lib/test/test_asyncore.py | 23 +- Lib/test/test_base64.py | 14 +- Lib/test/test_binhex.py | 13 +- Lib/test/test_bool.py | 11 +- Lib/test/test_buffer.py | 3 +- Lib/test/test_bufio.py | 9 +- Lib/test/test_builtin.py | 4 +- Lib/test/test_calendar.py | 4 +- Lib/test/test_cgi.py | 4 +- Lib/test/test_cgitb.py | 2 +- Lib/test/test_cmd_line.py | 13 +- Lib/test/test_cmd_line_script.py | 87 +- Lib/test/test_codecs.py | 25 +- Lib/test/test_compile.py | 3 +- Lib/test/test_complex.py | 7 +- Lib/test/test_configparser.py | 11 +- Lib/test/test_contextlib.py | 3 +- Lib/test/test_dbm.py | 11 +- Lib/test/test_dbm_dumb.py | 11 +- Lib/test/test_deque.py | 23 +- Lib/test/test_exceptions.py | 11 +- Lib/test/test_file.py | 7 +- Lib/test/test_fileio.py | 5 +- Lib/test/test_ftplib.py | 4 +- Lib/test/test_genericpath.py | 91 +- Lib/test/test_getopt.py | 3 +- Lib/test/test_glob.py | 4 +- Lib/test/test_gzip.py | 20 +- Lib/test/test_http_cookiejar.py | 15 +- Lib/test/test_httplib.py | 13 +- Lib/test/test_httpservers.py | 15 +- Lib/test/test_imp.py | 30 +- .../extension/test_case_sensitivity.py | 5 +- .../source/test_case_sensitivity.py | 13 +- Lib/test/test_importlib/test_api.py | 7 +- Lib/test/test_importlib/util.py | 5 +- Lib/test/test_io.py | 235 ++-- Lib/test/test_iter.py | 3 +- Lib/test/test_json/test_tool.py | 5 +- Lib/test/test_linecache.py | 13 +- Lib/test/test_mimetypes.py | 4 +- Lib/test/test_netrc.py | 19 +- Lib/test/test_ntpath.py | 94 +- Lib/test/test_optparse.py | 33 +- Lib/test/test_os.py | 1167 +++++------------ Lib/test/test_pathlib.py | 59 +- Lib/test/test_pkgimport.py | 2 +- Lib/test/test_platform.py | 11 +- Lib/test/test_posix.py | 337 +++-- Lib/test/test_posixpath.py | 89 +- Lib/test/test_py_compile.py | 7 +- Lib/test/test_regrtest.py | 35 +- Lib/test/test_reprlib.py | 3 +- Lib/test/test_resource.py | 5 +- Lib/test/test_runpy.py | 4 +- Lib/test/test_set.py | 13 +- Lib/test/test_shelve.py | 5 +- Lib/test/test_shutil.py | 139 +- Lib/test/test_signal.py | 3 +- Lib/test/test_socket.py | 55 +- Lib/test/test_socketserver.py | 10 +- Lib/test/test_stat.py | 3 +- Lib/test/test_subprocess.py | 13 +- Lib/test/test_support.py | 74 +- Lib/test/test_sys.py | 7 +- Lib/test/test_sysconfig.py | 7 +- Lib/test/test_tarfile.py | 104 +- Lib/test/test_tempfile.py | 12 +- Lib/test/test_threading.py | 4 +- Lib/test/test_tokenize.py | 5 +- Lib/test/test_trace.py | 3 +- Lib/test/test_traceback.py | 3 +- Lib/test/test_unicode_file.py | 12 +- Lib/test/test_unicode_file_functions.py | 17 +- Lib/test/test_urllib.py | 183 +-- Lib/test/test_urllib2.py | 3 +- Lib/test/test_urllib2net.py | 3 +- Lib/test/test_urllibnet.py | 7 +- Lib/test/test_uu.py | 11 +- Lib/test/test_venv.py | 2 +- Lib/test/test_webbrowser.py | 7 +- Lib/test/test_xml_etree.py | 15 +- Lib/test/test_xmlrpc.py | 5 +- Lib/test/test_zipfile.py | 4 +- Lib/test/test_zipimport.py | 35 +- 98 files changed, 2036 insertions(+), 2903 deletions(-) delete mode 100644 Lib/test/support/os_helper.py diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 0a97c8c19..cb09ee0e0 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -2,7 +2,6 @@ import argparse import os import sys from test import support -from test.support import os_helper USAGE = """\ @@ -281,7 +280,7 @@ def _create_parser(): def relative_filename(string): # CWD is replaced with a temporary dir before calling main(), so we # join it with the saved CWD so it ends up where the user expects. - return os.path.join(os_helper.SAVEDCWD, string) + return os.path.join(support.SAVEDCWD, string) def huntrleaks(string): diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 0f76a53de..98559e38a 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -20,7 +20,6 @@ from test.libregrtest.runtest import ( from test.libregrtest.setup import setup_tests from test.libregrtest.utils import removepy, count, format_duration, printlist from test import support -from test.support import os_helper # When tests are run from the Python build directory, it is best practice @@ -200,7 +199,7 @@ class Regrtest: # regex to match 'test_builtin' in line: # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec' regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b') - with open(os.path.join(os_helper.SAVEDCWD, self.ns.fromfile)) as fp: + with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp: for line in fp: line = line.split('#', 1)[0] line = line.strip() @@ -543,7 +542,7 @@ class Regrtest: for k, v in totals.items(): root.set(k, str(v)) - xmlpath = os.path.join(os_helper.SAVEDCWD, self.ns.xmlpath) + xmlpath = os.path.join(support.SAVEDCWD, self.ns.xmlpath) with open(xmlpath, 'wb') as f: for s in ET.tostringlist(root): f.write(s) @@ -569,8 +568,8 @@ class Regrtest: # Run the tests in a context manager that temporarily changes the CWD to a # temporary and writable directory. If it's not possible to create or # change the CWD, the original CWD will be used. The original CWD is - # available from os_helper.SAVEDCWD. - with os_helper.temp_cwd(test_cwd, quiet=True): + # available from support.SAVEDCWD. + with support.temp_cwd(test_cwd, quiet=True): self._main(tests, kwargs) def getloadavg(self): diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index 03747f7f7..822c90523 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -61,7 +61,7 @@ def dash_R(ns, test_name, test_func): return int_pool.setdefault(value, value) nwarmup, ntracked, fname = ns.huntrleaks - fname = os.path.join(os_helper.SAVEDCWD, fname) + fname = os.path.join(support.SAVEDCWD, fname) repcount = nwarmup + ntracked # Pre-allocate to ensure that the loop doesn't allocate anything new diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index b2d3e54a6..7bba46492 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -11,7 +11,6 @@ import traceback import unittest from test import support -from test.support import os_helper from test.libregrtest.refleak import dash_R, clear_caches from test.libregrtest.save_env import saved_test_environment from test.libregrtest.utils import print_warning @@ -299,7 +298,7 @@ def cleanup_test_droppings(test_name, verbose): # since if a test leaves a file open, it cannot be deleted by name (while # there's nothing we can do about that here either, we can display the # name of the offending test, which is a real help). - for name in (os_helper.TESTFN, + for name in (support.TESTFN, "db_home", ): if not os.path.exists(name): diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py index 745a4a7ce..dbab6954d 100644 --- a/Lib/test/libregrtest/runtest_mp.py +++ b/Lib/test/libregrtest/runtest_mp.py @@ -46,7 +46,7 @@ def run_test_in_subprocess(testname, ns): stderr=subprocess.PIPE, universal_newlines=True, close_fds=(os.name != 'nt'), - cwd=os_helper.SAVEDCWD) + cwd=support.SAVEDCWD) def run_tests_worker(worker_args): diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index b9a1c0b39..31931f219 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -9,7 +9,6 @@ import sysconfig import threading import warnings from test import support -from test.support import os_helper from test.libregrtest.utils import print_warning try: import _multiprocessing, multiprocessing.process @@ -229,12 +228,12 @@ class saved_test_environment: return sorted(fn + ('/' if os.path.isdir(fn) else '') for fn in os.listdir()) def restore_files(self, saved_value): - fn = os_helper.TESTFN + fn = support.TESTFN if fn not in saved_value and (fn + '/') not in saved_value: if os.path.isfile(fn): - os_helper.unlink(fn) + support.unlink(fn) elif os.path.isdir(fn): - os_helper.rmtree(fn) + support.rmtree(fn) _lc = [getattr(locale, lc) for lc in dir(locale) if lc.startswith('LC_')] diff --git a/Lib/test/libregrtest/win_utils.py b/Lib/test/libregrtest/win_utils.py index 95db3def3..adfe278ba 100644 --- a/Lib/test/libregrtest/win_utils.py +++ b/Lib/test/libregrtest/win_utils.py @@ -55,7 +55,7 @@ class WindowsLoadTracker(): # Spawn off the load monitor command = ['typeperf', COUNTER_NAME, '-si', str(SAMPLING_INTERVAL)] - self.p = subprocess.Popen(command, stdout=command_stdout, cwd=os_helper.SAVEDCWD) + self.p = subprocess.Popen(command, stdout=command_stdout, cwd=support.SAVEDCWD) # Close our copy of the write end of the pipe os.close(command_stdout) diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index e8c1049b4..d4eadd57d 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -8,7 +8,6 @@ import unittest from functools import cmp_to_key from test import support, seq_tests -from test.support import os_helper class CommonTest(seq_tests.CommonTest): @@ -74,12 +73,12 @@ class CommonTest(seq_tests.CommonTest): d.append(d) d.append(400) try: - with open(os_helper.TESTFN, "w") as fo: + with open(support.TESTFN, "w") as fo: fo.write(str(d)) - with open(os_helper.TESTFN, "r") as fo: + with open(support.TESTFN, "r") as fo: self.assertEqual(fo.read(), repr(d)) finally: - os.remove(os_helper.TESTFN) + os.remove(support.TESTFN) def test_set_subscript(self): a = self.type2test(range(20)) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index af8936155..1027e93df 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -4,6 +4,7 @@ if __name__ != 'test.support': raise ImportError('support must be imported from the test package') import asyncio.events +import collections.abc import contextlib import errno import faulthandler @@ -162,13 +163,6 @@ SHORT_TIMEOUT = 30.0 # option. LONG_TIMEOUT = 5 * 60.0 -# TEST_HOME_DIR refers to the top level directory of the "test" package -# that contains Python's regression test suite -TEST_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__)) -TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR) -STDLIB_DIR = os.path.dirname(TEST_HOME_DIR) -REPO_ROOT = os.path.dirname(STDLIB_DIR) - class Error(Exception): """Base class for regression test exceptions.""" @@ -176,17 +170,6 @@ class Error(Exception): class TestFailed(Error): """Test failed.""" -class TestFailedWithDetails(TestFailed): - """Test failed.""" - def __init__(self, msg, errors, failures): - self.msg = msg - self.errors = errors - self.failures = failures - super().__init__(msg, errors, failures) - - def __str__(self): - return self.msg - class TestDidNotRun(Error): """Test did not run any subtests.""" @@ -296,7 +279,9 @@ def load_package_tests(pkg_dir, loader, standard_tests, pattern): """ if pattern is None: pattern = "test*" - top_dir = STDLIB_DIR + top_dir = os.path.dirname( # Lib + os.path.dirname( # test + os.path.dirname(__file__))) # support package_tests = loader.discover(start_dir=pkg_dir, top_level_dir=top_dir, pattern=pattern) @@ -457,7 +442,7 @@ if sys.platform.startswith("win"): try: mode = os.lstat(fullname).st_mode except OSError as exc: - print("os_helper.rmtree(): os.lstat(%r) failed with %s" % (fullname, exc), + print("support.rmtree(): os.lstat(%r) failed with %s" % (fullname, exc), file=sys.__stderr__) mode = 0 if stat.S_ISDIR(mode): @@ -560,11 +545,8 @@ def forget(modname): def _is_gui_available(): if hasattr(_is_gui_available, 'result'): return _is_gui_available.result - import platform reason = None - if sys.platform.startswith('win') and platform.win32_is_iot(): - reason = "gui is not available on Windows IoT Core" - elif sys.platform.startswith('win'): + if sys.platform.startswith('win'): # if Python is running as a service (such as the buildbot service), # gui interaction may be disallowed import ctypes @@ -658,25 +640,25 @@ def _requires_unix_version(sysname, min_version): For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if the FreeBSD version is less than 7.2. """ - import platform - min_version_txt = '.'.join(map(str, min_version)) - version_txt = platform.release().split('-', 1)[0] - if platform.system() == sysname: - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - skip = False - else: - skip = version < min_version - else: - skip = False - - return unittest.skipIf( - skip, - f"{sysname} version {min_version_txt} or higher required, not " - f"{version_txt}" - ) - + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kw): + if platform.system() == sysname: + version_txt = platform.release().split('-', 1)[0] + try: + version = tuple(map(int, version_txt.split('.'))) + except ValueError: + pass + else: + if version < min_version: + min_version_txt = '.'.join(map(str, min_version)) + raise unittest.SkipTest( + "%s version %s or higher required, not %s" + % (sysname, min_version_txt, version_txt)) + return func(*args, **kw) + wrapper.min_version = min_version + return wrapper + return decorator def requires_freebsd_version(*min_version): """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is @@ -707,7 +689,6 @@ def requires_mac_ver(*min_version): @functools.wraps(func) def wrapper(*args, **kw): if sys.platform == 'darwin': - import platform version_txt = platform.mac_ver()[0] try: version = tuple(map(int, version_txt.split('.'))) @@ -725,16 +706,6 @@ def requires_mac_ver(*min_version): return decorator -def skip_if_buildbot(reason=None): - """Decorator raising SkipTest if running on a buildbot.""" - if not reason: - reason = 'not suitable for buildbots' - if sys.platform == 'win32': - isbuildbot = os.environ.get('USERNAME') == 'Buildbot' - else: - isbuildbot = os.environ.get('USER') == 'buildbot' - return unittest.skipIf(isbuildbot, reason) - def requires_hashdigest(digestname, openssl=None): """Decorator raising SkipTest if a hashing algorithm is not available @@ -917,8 +888,8 @@ PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 # A constant likely larger than the underlying OS socket buffer size, to make # writes blocking. # The socket buffer sizes can usually be tuned system-wide (e.g. through sysctl -# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 -# for a discussion of this number. +# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 +# for a discussion of this number). SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1 # # decorator for skipping tests on non-IEEE 754 platforms @@ -939,15 +910,156 @@ is_jython = sys.platform.startswith('java') is_android = hasattr(sys, 'getandroidapilevel') -if sys.platform not in ('win32', 'vxworks'): +if sys.platform != 'win32': unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None +# Filename used for testing +if os.name == 'java': + # Jython disallows @ in module names + TESTFN = '$test' +else: + TESTFN = '@test' + +# Disambiguate TESTFN for parallel testing, while letting it remain a valid +# module name. +TESTFN = "{}_{}_tmp".format(TESTFN, os.getpid()) + # Define the URL of a dedicated HTTP server for the network tests. # The URL must use clear-text HTTP: no redirection to encrypted HTTPS. TEST_HTTP_URL = "http://www.pythontest.net" +# FS_NONASCII: non-ASCII character encodable by os.fsencode(), +# or None if there is no such character. +FS_NONASCII = None +for character in ( + # First try printable and common characters to have a readable filename. + # For each character, the encoding list are just example of encodings able + # to encode the character (the list is not exhaustive). + + # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 + '\u00E6', + # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 + '\u0130', + # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 + '\u0141', + # U+03C6 (Greek Small Letter Phi): cp1253 + '\u03C6', + # U+041A (Cyrillic Capital Letter Ka): cp1251 + '\u041A', + # U+05D0 (Hebrew Letter Alef): Encodable to cp424 + '\u05D0', + # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic + '\u060C', + # U+062A (Arabic Letter Teh): cp720 + '\u062A', + # U+0E01 (Thai Character Ko Kai): cp874 + '\u0E01', + + # Then try more "special" characters. "special" because they may be + # interpreted or displayed differently depending on the exact locale + # encoding and the font. + + # U+00A0 (No-Break Space) + '\u00A0', + # U+20AC (Euro Sign) + '\u20AC', +): + try: + # If Python is set up to use the legacy 'mbcs' in Windows, + # 'replace' error mode is used, and encode() returns b'?' + # for characters missing in the ANSI codepage + if os.fsdecode(os.fsencode(character)) != character: + raise UnicodeError + except UnicodeError: + pass + else: + FS_NONASCII = character + break + +# TESTFN_UNICODE is a non-ascii filename +TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f" +if sys.platform == 'darwin': + # In Mac OS X's VFS API file names are, by definition, canonically + # decomposed Unicode, encoded using UTF-8. See QA1173: + # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html + import unicodedata + TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) +TESTFN_ENCODING = sys.getfilesystemencoding() + +# TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be +# encoded by the filesystem encoding (in strict mode). It can be None if we +# cannot generate such filename. +TESTFN_UNENCODABLE = None +if os.name == 'nt': + # skip win32s (0) or Windows 9x/ME (1) + if sys.getwindowsversion().platform >= 2: + # Different kinds of characters from various languages to minimize the + # probability that the whole name is encodable to MBCS (issue #9819) + TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" + try: + TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) + except UnicodeEncodeError: + pass + else: + print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). ' + 'Unicode filename tests may not be effective' + % (TESTFN_UNENCODABLE, TESTFN_ENCODING), + file=sys.__stderr__) + TESTFN_UNENCODABLE = None +# # Mac OS X denies unencodable filenames (invalid utf-8) +# elif sys.platform != 'darwin': +# try: +# # ascii and utf-8 cannot encode the byte 0xff +# b'\xff'.decode(TESTFN_ENCODING) +# except UnicodeDecodeError: +# # 0xff will be encoded using the surrogate character u+DCFF +# TESTFN_UNENCODABLE = TESTFN \ +# + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape') +# else: +# # File system encoding (eg. ISO-8859-* encodings) can encode +# # the byte 0xff. Skip some unicode filename tests. +# pass + +# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be +# decoded from the filesystem encoding (in strict mode). It can be None if we +# cannot generate such filename (ex: the latin1 encoding can decode any byte +# sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks +# to the surrogateescape error handler (PEP 383), but not from the filesystem +# encoding in strict mode. +TESTFN_UNDECODABLE = None +for name in ( + # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows + # accepts it to create a file or a directory, or don't accept to enter to + # such directory (when the bytes name is used). So test b'\xe7' first: it is + # not decodable from cp932. + b'\xe7w\xf0', + # undecodable from ASCII, UTF-8 + b'\xff', + # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 + # and cp857 + b'\xae\xd5' + # undecodable from UTF-8 (UNIX and Mac OS X) + b'\xed\xb2\x80', b'\xed\xb4\x80', + # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, + # cp1253, cp1254, cp1255, cp1257, cp1258 + b'\x81\x98', +): + try: + name.decode(TESTFN_ENCODING) + except UnicodeDecodeError: + TESTFN_UNDECODABLE = os.fsencode(TESTFN) + name + break + +if FS_NONASCII: + TESTFN_NONASCII = TESTFN + '-' + FS_NONASCII +else: + TESTFN_NONASCII = None + +# Save the initial cwd +SAVEDCWD = os.getcwd() + # Set by libregrtest/main.py so we can skip tests that are not # useful for PGO PGO = False @@ -956,28 +1068,121 @@ PGO = False # PGO task. If this is True, PGO is also True. PGO_EXTENDED = False +@contextlib.contextmanager +def temp_dir(path=None, quiet=False): + """Return a context manager that creates a temporary directory. + + Arguments: + + path: the directory to create temporarily. If omitted or None, + defaults to creating a temporary directory using tempfile.mkdtemp. + + quiet: if False (the default), the context manager raises an exception + on error. Otherwise, if the path is specified and cannot be + created, only a warning is issued. + + """ + dir_created = False + if path is None: + path = tempfile.mkdtemp() + dir_created = True + path = os.path.realpath(path) + else: + try: + os.mkdir(path) + dir_created = True + except OSError as exc: + if not quiet: + raise + warnings.warn(f'tests may fail, unable to create ' + f'temporary directory {path!r}: {exc}', + RuntimeWarning, stacklevel=3) + if dir_created: + pid = os.getpid() + try: + yield path + finally: + # In case the process forks, let only the parent remove the + # directory. The child has a different process id. (bpo-30028) + if dir_created and pid == os.getpid(): + try: + rmtree(path) + except OSError as exc: + # XXX RUSTPYTHON: something something async file removal? + # also part of the thing with rmtree() + # throwing PermissionError, I think + if os.path.exists(path): + if not quiet: + raise + warnings.warn(f'unable to remove temporary' + f'directory {path!r}: {exc}', + RuntimeWarning, stacklevel=3) + +@contextlib.contextmanager +def change_cwd(path, quiet=False): + """Return a context manager that changes the current working directory. + + Arguments: + + path: the directory to use as the temporary current working directory. + + quiet: if False (the default), the context manager raises an exception + on error. Otherwise, it issues only a warning and keeps the current + working directory the same. + + """ + saved_dir = os.getcwd() + try: + os.chdir(os.path.realpath(path)) + except OSError as exc: + if not quiet: + raise + warnings.warn(f'tests may fail, unable to change the current working ' + f'directory to {path!r}: {exc}', + RuntimeWarning, stacklevel=3) + try: + yield os.getcwd() + finally: + os.chdir(saved_dir) + + +@contextlib.contextmanager +def temp_cwd(name='tempcwd', quiet=False): + """ + Context manager that temporarily creates and changes the CWD. + + The function temporarily changes the current working directory + after creating a temporary directory in the current directory with + name *name*. If *name* is None, the temporary directory is + created using tempfile.mkdtemp. + + If *quiet* is False (default) and it is not possible to + create or change the CWD, an error is raised. If *quiet* is True, + only a warning is raised and the original CWD is used. + + """ + with temp_dir(path=name, quiet=quiet) as temp_path: + with change_cwd(temp_path, quiet=quiet) as cwd_dir: + yield cwd_dir + +if hasattr(os, "umask"): + @contextlib.contextmanager + def temp_umask(umask): + """Context manager that temporarily sets the process umask.""" + oldmask = os.umask(umask) + try: + yield + finally: + os.umask(oldmask) + +# TEST_HOME_DIR refers to the top level directory of the "test" package +# that contains Python's regression test suite +TEST_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR) + # TEST_DATA_DIR is used as a target download location for remote resources TEST_DATA_DIR = os.path.join(TEST_HOME_DIR, "data") - -def darwin_malloc_err_warning(test_name): - """Assure user that loud errors generated by macOS libc's malloc are - expected.""" - if sys.platform != 'darwin': - return - - import shutil - msg = ' NOTICE ' - detail = (f'{test_name} may generate "malloc can\'t allocate region"\n' - 'warnings on macOS systems. This behavior is known. Do not\n' - 'report a bug unless tests are also failing. See bpo-40928.') - - padding, _ = shutil.get_terminal_size() - print(msg.center(padding, '-')) - print(detail) - print('-' * padding) - - def findfile(filename, subdir=None): """Try to find a file on sys.path or in the test directory. If it is not found the argument passed to the function is returned (this does not @@ -996,6 +1201,10 @@ def findfile(filename, subdir=None): if os.path.exists(fn): return fn return filename +def create_empty_file(filename): + """Create an empty file. If the file already exists, truncate it.""" + fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) + os.close(fd) def sortdict(dict): "Like repr(dict), but in sorted order." @@ -1004,6 +1213,19 @@ def sortdict(dict): withcommas = ", ".join(reprpairs) return "{%s}" % withcommas +def make_bad_fd(): + """ + Create an invalid file descriptor by opening and closing a file and return + its fd. + """ + file = open(TESTFN, "wb") + try: + return file.fileno() + finally: + file.close() + unlink(TESTFN) + + def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=None): with testcase.assertRaisesRegex(SyntaxError, errtext) as cm: compile(statement, '', 'exec') @@ -1044,11 +1266,6 @@ def check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=No def open_urlresource(url, *args, **kw): import urllib.request, urllib.parse - from .os_helper import unlink - try: - import gzip - except ImportError: - gzip = None check = kw.pop('check', None) @@ -1079,7 +1296,7 @@ def open_urlresource(url, *args, **kw): opener = urllib.request.build_opener() if gzip: opener.addheaders.append(('Accept-Encoding', 'gzip')) - f = opener.open(url, timeout=INTERNET_TIMEOUT) + f = opener.open(url, timeout=15) if gzip and f.headers.get('Content-Encoding') == 'gzip': f = gzip.GzipFile(fileobj=f) try: @@ -1257,6 +1474,59 @@ class CleanImport(object): sys.modules.update(self.original_modules) +class EnvironmentVarGuard(collections.abc.MutableMapping): + + """Class to help protect the environment variable properly. Can be used as + a context manager.""" + + def __init__(self): + self._environ = os.environ + self._changed = {} + + def __getitem__(self, envvar): + return self._environ[envvar] + + def __setitem__(self, envvar, value): + # Remember the initial value on the first access + if envvar not in self._changed: + self._changed[envvar] = self._environ.get(envvar) + self._environ[envvar] = value + + def __delitem__(self, envvar): + # Remember the initial value on the first access + if envvar not in self._changed: + self._changed[envvar] = self._environ.get(envvar) + if envvar in self._environ: + del self._environ[envvar] + + def keys(self): + return self._environ.keys() + + def __iter__(self): + return iter(self._environ) + + def __len__(self): + return len(self._environ) + + def set(self, envvar, value): + self[envvar] = value + + def unset(self, envvar): + del self[envvar] + + def __enter__(self): + return self + + def __exit__(self, *ignore_exc): + for (k, v) in self._changed.items(): + if v is None: + if k in self._environ: + del self._environ[k] + else: + self._environ[k] = v + os.environ = self._environ + + class DirsOnSysPath(object): """Context manager to temporarily add directories to sys.path. @@ -1467,7 +1737,6 @@ def gc_collect(): longer than expected. This function tries its best to force all garbage objects to disappear. """ - # TODO: RUSTPYTHON (comment out before) # gc.collect() # if is_jython: # time.sleep(0.1) @@ -1477,7 +1746,6 @@ def gc_collect(): @contextlib.contextmanager def disable_gc(): - # TODO: RUSTPYTHON (comment out before) # have_gc = gc.isenabled() # gc.disable() # try: @@ -1516,15 +1784,12 @@ _TPFLAGS_HAVE_GC = 1<<14 _TPFLAGS_HEAPTYPE = 1<<9 def check_sizeof(test, o, size): - try: - import _testinternalcapi - except ImportError: - raise unittest.SkipTest("_testinternalcapi required") + import _testcapi result = sys.getsizeof(o) # add GC header size if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))): - size += _testinternalcapi.SIZEOF_PYGC_HEAD + size += _testcapi.SIZEOF_PYGC_HEAD msg = 'wrong size for %s: got %d, expected %d' \ % (type(o), result, size) test.assertEqual(result, size, msg) @@ -1533,8 +1798,9 @@ def check_sizeof(test, o, size): # Decorator for running a function in a different locale, correctly resetting # it afterwards. -@contextlib.contextmanager def run_with_locale(catstr, *locales): + def decorator(func): + def inner(*args, **kwds): try: import locale category = getattr(locale, catstr) @@ -1553,11 +1819,16 @@ def run_with_locale(catstr, *locales): except: pass + # now run the function, resetting the locale on exceptions try: - yield + return func(*args, **kwds) finally: if locale and orig_locale: locale.setlocale(category, orig_locale) + inner.__name__ = func.__name__ + inner.__doc__ = func.__doc__ + return inner + return decorator #======================================================================= # Decorator for running a function in a specific timezone, correctly @@ -1636,7 +1907,6 @@ class _MemoryWatchdog: self.started = False def start(self): - import warnings try: f = open(self.procfile, 'r') except OSError as e: @@ -1645,7 +1915,6 @@ class _MemoryWatchdog: sys.stderr.flush() return - import subprocess with f: watchdog_script = findfile("memory_watchdog.py") self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], @@ -1779,7 +2048,7 @@ def check_impl_detail(**guards): if check_impl_detail(cpython=False): # everywhere except on CPython """ guards, default = _parse_guards(guards) - return guards.get(sys.implementation.name, default) + return guards.get(platform.python_implementation().lower(), default) def no_tracing(func): @@ -1842,9 +2111,7 @@ def _run_suite(suite): else: err = "multiple errors occurred" if not verbose: err += "; run in verbose mode for details" - errors = [(str(tc), exc_str) for tc, exc_str in result.errors] - failures = [(str(tc), exc_str) for tc, exc_str in result.failures] - raise TestFailedWithDetails(err, errors, failures) + raise TestFailed(err) # By default, don't filter tests @@ -1915,7 +2182,6 @@ def _compile_match_function(patterns): # The test.bisect_cmd utility only uses such full test identifiers. func = set(patterns).__contains__ else: - import fnmatch regex = '|'.join(map(fnmatch.translate, patterns)) # The search *is* case sensitive on purpose: # don't use flags=re.IGNORECASE @@ -1940,18 +2206,17 @@ def _compile_match_function(patterns): def run_unittest(*classes): """Run tests from unittest.TestCase-derived classes.""" valid_types = (unittest.TestSuite, unittest.TestCase) - loader = unittest.TestLoader() suite = unittest.TestSuite() for cls in classes: if isinstance(cls, str): if cls in sys.modules: - suite.addTest(loader.loadTestsFromModule(sys.modules[cls])) + suite.addTest(unittest.findTestCases(sys.modules[cls])) else: raise ValueError("str arguments must be keys in sys.modules") elif isinstance(cls, valid_types): suite.addTest(cls) else: - suite.addTest(loader.loadTestsFromTestCase(cls)) + suite.addTest(unittest.makeSuite(cls)) _filter_suite(suite, match_test) _run_suite(suite) @@ -2005,24 +2270,11 @@ def run_doctest(module, verbosity=None, optionflags=0): #======================================================================= # Support for saving and restoring the imported modules. -def flush_std_streams(): - if sys.stdout is not None: - sys.stdout.flush() - if sys.stderr is not None: - sys.stderr.flush() - - def print_warning(msg): - # bpo-45410: Explicitly flush stdout to keep logs in order - flush_std_streams() - stream = print_warning.orig_stderr + # bpo-39983: Print into sys.__stderr__ to display the warning even + # when sys.stderr is captured temporarily by a test for line in msg.splitlines(): - print(f"Warning -- {line}", file=stream) - stream.flush() - -# bpo-39983: Store the original sys.stderr at Python startup to be able to -# log warnings even if sys.stderr is captured temporarily by a test. -print_warning.orig_stderr = sys.stderr + print(f"Warning -- {line}", file=sys.__stderr__, flush=True) def modules_setup(): return sys.modules.copy(), @@ -2050,7 +2302,7 @@ def modules_cleanup(oldmodules): # to check if a test modified the environment. The flag should be set to False # before running a new test. # -# For example, threading_helper.threading_cleanup() sets the flag is the function fails +# For example, threading_cleanup() sets the flag is the function fails # to cleanup threads. environment_altered = False @@ -2296,13 +2548,11 @@ requires_type_collecting = unittest.skipIf(hasattr(sys, 'getcounts'), def args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current settings in sys.flags and sys.warnoptions.""" - import subprocess return subprocess._args_from_interpreter_flags() def optim_args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current optimization settings in sys.flags.""" - import subprocess return subprocess._optim_args_from_interpreter_flags() #============================================================ @@ -2371,6 +2621,28 @@ class Matcher(object): return result +_can_symlink = None +def can_symlink(): + global _can_symlink + if _can_symlink is not None: + return _can_symlink + symlink_path = TESTFN + "can_symlink" + try: + os.symlink(TESTFN, symlink_path) + can = True + except (OSError, NotImplementedError, AttributeError): + can = False + else: + os.remove(symlink_path) + _can_symlink = can + return can + +def skip_unless_symlink(test): + """Skip decorator for tests that require functional symlink""" + ok = can_symlink() + msg = "Requires functional symlink implementation" + return test if ok else unittest.skip(msg)(test) + _buggy_ucrt = None def skip_if_buggy_ucrt_strfptime(test): """ @@ -2382,7 +2654,6 @@ def skip_if_buggy_ucrt_strfptime(test): See bpo-37552 [Windows] strptime/strftime return invalid results with UCRT version 17763.615 """ - import locale global _buggy_ucrt if _buggy_ucrt is None: if(sys.platform == 'win32' and @@ -2396,8 +2667,6 @@ def skip_if_buggy_ucrt_strfptime(test): class PythonSymlink: """Creates a symlink for the current Python executable""" def __init__(self, link=None): - from .os_helper import TESTFN - self.link = link or os.path.abspath(TESTFN) self._linked = [] self.real = os.path.realpath(sys.executable) @@ -2412,7 +2681,6 @@ class PythonSymlink: if sys.platform == "win32": def _platform_specific(self): - import glob import _winapi if os.path.lexists(self.real) and not os.path.exists(self.real): @@ -2427,7 +2695,7 @@ class PythonSymlink: dll, os.path.join(dest_dir, os.path.basename(dll)) )) - for runtime in glob.glob(os.path.join(glob.escape(src_dir), "vcruntime*.dll")): + for runtime in glob.glob(os.path.join(src_dir, "vcruntime*.dll")): self._also_link.append(( runtime, os.path.join(dest_dir, os.path.basename(runtime)) @@ -2436,7 +2704,7 @@ class PythonSymlink: self._env = {k.upper(): os.getenv(k) for k in os.environ} self._env["PYTHONHOME"] = os.path.dirname(self.real) if sysconfig.is_python_build(True): - self._env["PYTHONPATH"] = STDLIB_DIR + self._env["PYTHONPATH"] = os.path.dirname(os.__file__) def __enter__(self): os.symlink(self.real, self.link) @@ -2455,7 +2723,6 @@ class PythonSymlink: print("failed to clean up {}: {}".format(link, ex)) def _call(self, python, args, env, returncode): - import subprocess cmd = [python, *args] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) @@ -2475,6 +2742,43 @@ class PythonSymlink: return self._call(self.link, args, self._env, returncode) +_can_xattr = None +def can_xattr(): + global _can_xattr + if _can_xattr is not None: + return _can_xattr + if not hasattr(os, "setxattr"): + can = False + else: + tmp_dir = tempfile.mkdtemp() + tmp_fp, tmp_name = tempfile.mkstemp(dir=tmp_dir) + try: + with open(TESTFN, "wb") as fp: + try: + # TESTFN & tempfile may use different file systems with + # different capabilities + os.setxattr(tmp_fp, b"user.test", b"") + os.setxattr(tmp_name, b"trusted.foo", b"42") + os.setxattr(fp.fileno(), b"user.test", b"") + # Kernels < 2.6.39 don't respect setxattr flags. + kernel_version = platform.release() + m = re.match(r"2.6.(\d{1,2})", kernel_version) + can = m is None or int(m.group(1)) >= 39 + except OSError: + can = False + finally: + unlink(TESTFN) + unlink(tmp_name) + rmdir(tmp_dir) + _can_xattr = can + return can + +def skip_unless_xattr(test): + """Skip decorator for tests that require functional extended attributes""" + ok = can_xattr() + msg = "no non-broken extended attribute support" + return test if ok else unittest.skip(msg)(test) + def skip_if_pgo_task(test): """Skip decorator for tests not run in (non-extended) PGO task""" ok = not PGO or PGO_EXTENDED @@ -2484,7 +2788,6 @@ def skip_if_pgo_task(test): _bind_nix_socket_error = None def skip_unless_bind_unix_socket(test): """Decorator for tests requiring a functional bind() for unix sockets.""" - from .os_helper import TESTFN if not hasattr(socket, 'AF_UNIX'): return unittest.skip('No UNIX Sockets')(test) global _bind_nix_socket_error @@ -2505,6 +2808,18 @@ def skip_unless_bind_unix_socket(test): return test +def fs_is_case_insensitive(directory): + """Detects if the file system for the specified directory is case-insensitive.""" + with tempfile.NamedTemporaryFile(dir=directory) as base: + base_path = base.name + case_path = base_path.upper() + if case_path == base_path: + case_path = base_path.lower() + try: + return os.path.samefile(base_path, case_path) + except FileNotFoundError: + return False + def detect_api_mismatch(ref_api, other_api, *, ignore=()): """Returns the set of items in ref_api not in other_api, except for a @@ -2522,7 +2837,7 @@ def detect_api_mismatch(ref_api, other_api, *, ignore=()): def check__all__(test_case, module, name_of_module=None, extra=(), - not_exported=()): + blacklist=()): """Assert that the __all__ variable of 'module' contains all public names. The module's public names (its API) are detected automatically based on @@ -2539,7 +2854,7 @@ def check__all__(test_case, module, name_of_module=None, extra=(), '__module__' attribute. If provided, it will be added to the automatically detected ones. - The 'not_exported' argument can be a set of names that must not be treated + The 'blacklist' argument can be a set of names that must not be treated as part of the public API even though their names indicate otherwise. Usage: @@ -2555,10 +2870,10 @@ def check__all__(test_case, module, name_of_module=None, extra=(), class OtherTestCase(unittest.TestCase): def test__all__(self): extra = {'BAR_CONST', 'FOO_CONST'} - not_exported = {'baz'} # Undocumented name. + blacklist = {'baz'} # Undocumented name. # bar imports part of its API from _bar. support.check__all__(self, bar, ('bar', '_bar'), - extra=extra, not_exported=not_exported) + extra=extra, blacklist=blacklist) """ @@ -2570,7 +2885,7 @@ def check__all__(test_case, module, name_of_module=None, extra=(), expected = set(extra) for name in dir(module): - if name.startswith('_') or name in not_exported: + if name.startswith('_') or name in blacklist: continue obj = getattr(module, name) if (getattr(obj, '__module__', None) in name_of_module or @@ -2580,27 +2895,6 @@ def check__all__(test_case, module, name_of_module=None, extra=(), test_case.assertCountEqual(module.__all__, expected) -def suppress_msvcrt_asserts(verbose=False): - try: - import msvcrt - except ImportError: - return - - msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS - | msvcrt.SEM_NOALIGNMENTFAULTEXCEPT - | msvcrt.SEM_NOGPFAULTERRORBOX - | msvcrt.SEM_NOOPENFILEERRORBOX) - - # CrtSetReportMode() is only available in debug build - if hasattr(msvcrt, 'CrtSetReportMode'): - for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: - if verbose: - msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) - msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) - else: - msvcrt.CrtSetReportMode(m, 0) - - class SuppressCrashReport: """Try to prevent a crash report from popping up. @@ -2612,25 +2906,30 @@ class SuppressCrashReport: def __enter__(self): """On Windows, disable Windows Error Reporting dialogs using - SetErrorMode() and CrtSetReportMode(). + SetErrorMode. On UNIX, try to save the previous core file size limit, then set soft limit to 0. """ if sys.platform.startswith('win'): # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx + # GetErrorMode is not available on Windows XP and Windows Server 2003, + # but SetErrorMode returns the previous value, so we can use that + import ctypes + self._k32 = ctypes.windll.kernel32 + SEM_NOGPFAULTERRORBOX = 0x02 + self.old_value = self._k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) + self._k32.SetErrorMode(self.old_value | SEM_NOGPFAULTERRORBOX) + + # Suppress assert dialogs in debug builds + # (see http://bugs.python.org/issue23314) try: import msvcrt - except ImportError: - return - - self.old_value = msvcrt.GetErrorMode() - - msvcrt.SetErrorMode(self.old_value | msvcrt.SEM_NOGPFAULTERRORBOX) - - # bpo-23314: Suppress assert dialogs in debug builds. - # CrtSetReportMode() is only available in debug build. - if hasattr(msvcrt, 'CrtSetReportMode'): + msvcrt.CrtSetReportMode + except (AttributeError, ImportError): + # no msvcrt or a release build + pass + else: self.old_modes = {} for report_type in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, @@ -2642,21 +2941,15 @@ class SuppressCrashReport: self.old_modes[report_type] = old_mode, old_file else: - try: - import resource - self.resource = resource - except ImportError: - self.resource = None - if self.resource is not None: + if resource is not None: try: - self.old_value = self.resource.getrlimit(self.resource.RLIMIT_CORE) - self.resource.setrlimit(self.resource.RLIMIT_CORE, + self.old_value = resource.getrlimit(resource.RLIMIT_CORE) + resource.setrlimit(resource.RLIMIT_CORE, (0, self.old_value[1])) except (ValueError, OSError): pass if sys.platform == 'darwin': - import subprocess # Check if the 'Crash Reporter' on OSX was configured # in 'Developer' mode and warn that it will get triggered # when it is. @@ -2682,17 +2975,17 @@ class SuppressCrashReport: return if sys.platform.startswith('win'): - import msvcrt - msvcrt.SetErrorMode(self.old_value) + self._k32.SetErrorMode(self.old_value) if self.old_modes: + import msvcrt for report_type, (old_mode, old_file) in self.old_modes.items(): msvcrt.CrtSetReportMode(report_type, old_mode) msvcrt.CrtSetReportFile(report_type, old_file) else: - if self.resource is not None: + if resource is not None: try: - self.resource.setrlimit(self.resource.RLIMIT_CORE, self.old_value) + resource.setrlimit(resource.RLIMIT_CORE, self.old_value) except (ValueError, OSError): pass @@ -2751,7 +3044,6 @@ def run_in_subinterp(code): return _testcapi.run_in_subinterp(code) -# TODO: RUSTPYTHON (comment out before) # def check_free_after_iterating(test, iter, cls, args=()): # class A(cls): # def __del__(self): @@ -2780,16 +3072,9 @@ def missing_compiler_executable(cmd_names=[]): missing. """ - # TODO (PEP 632): alternate check without using distutils - from distutils import ccompiler, sysconfig, spawn, errors + from distutils import ccompiler, sysconfig, spawn compiler = ccompiler.new_compiler() sysconfig.customize_compiler(compiler) - if compiler.compiler_type == "msvc": - # MSVC has no executables, so check whether initialization succeeds - try: - compiler.initialize() - except errors.DistutilsPlatformError: - return "msvc" for name in compiler.executables: if cmd_names and name not in cmd_names: continue @@ -2811,7 +3096,6 @@ def setswitchinterval(interval): if is_android and interval < minimum_interval: global _is_android_emulator if _is_android_emulator is None: - import subprocess _is_android_emulator = (subprocess.check_output( ['getprop', 'ro.kernel.qemu']).strip() == b'1') if _is_android_emulator: @@ -2821,8 +3105,6 @@ def setswitchinterval(interval): @contextlib.contextmanager def disable_faulthandler(): - import faulthandler - # use sys.__stderr__ instead of sys.stderr, since regrtest replaces # sys.stderr with a StringIO which has no file descriptor when a test # is run with -W/--verbose3. @@ -2837,6 +3119,65 @@ def disable_faulthandler(): faulthandler.enable(file=fd, all_threads=True) +def fd_count(): + """Count the number of open file descriptors. + """ + if sys.platform.startswith(('linux', 'freebsd')): + try: + names = os.listdir("/proc/self/fd") + # Subtract one because listdir() internally opens a file + # descriptor to list the content of the /proc/self/fd/ directory. + return len(names) - 1 + except FileNotFoundError: + pass + + MAXFD = 256 + if hasattr(os, 'sysconf'): + try: + MAXFD = os.sysconf("SC_OPEN_MAX") + except OSError: + pass + + old_modes = None + if sys.platform == 'win32': + # bpo-25306, bpo-31009: Call CrtSetReportMode() to not kill the process + # on invalid file descriptor if Python is compiled in debug mode + try: + import msvcrt + msvcrt.CrtSetReportMode + except (AttributeError, ImportError): + # no msvcrt or a release build + pass + else: + old_modes = {} + for report_type in (msvcrt.CRT_WARN, + msvcrt.CRT_ERROR, + msvcrt.CRT_ASSERT): + old_modes[report_type] = msvcrt.CrtSetReportMode(report_type, 0) + + try: + count = 0 + for fd in range(MAXFD): + try: + # Prefer dup() over fstat(). fstat() can require input/output + # whereas dup() doesn't. + fd2 = os.dup(fd) + except OSError as e: + if e.errno != errno.EBADF: + raise + else: + os.close(fd2) + count += 1 + finally: + if old_modes is not None: + for report_type in (msvcrt.CRT_WARN, + msvcrt.CRT_ERROR, + msvcrt.CRT_ASSERT): + msvcrt.CrtSetReportMode(report_type, old_modes[report_type]) + + return count + + class SaveSignals: """ Save and restore signal handlers. @@ -2881,6 +3222,24 @@ def with_pymalloc(): return _testcapi.WITH_PYMALLOC +class FakePath: + """Simple implementing of the path protocol. + """ + def __init__(self, path): + self.path = path + + def __repr__(self): + return f'' + + def __fspath__(self): + if (isinstance(self.path, BaseException) or + isinstance(self.path, type) and + issubclass(self.path, BaseException)): + raise self.path + else: + return self.path + + class _ALWAYS_EQ: """ Object that is equal to anything. @@ -2892,19 +3251,6 @@ class _ALWAYS_EQ: ALWAYS_EQ = _ALWAYS_EQ() -class _NEVER_EQ: - """ - Object that is not equal to anything. - """ - def __eq__(self, other): - return False - def __ne__(self, other): - return True - def __hash__(self): - return 1 - -NEVER_EQ = _NEVER_EQ() - @functools.total_ordering class _LARGEST: """ @@ -2931,7 +3277,6 @@ SMALLEST = _SMALLEST() def maybe_get_event_loop_policy(): """Return the global event loop policy if one is set, else return None.""" - import asyncio.events return asyncio.events._event_loop_policy # Helpers for testing hashing. @@ -3081,9 +3426,7 @@ def wait_process(pid, *, exitcode, timeout=None): """ Wait until process pid completes and check that the process exit code is exitcode. - Raise an AssertionError if the process exit code is not equal to exitcode. - If the process runs longer than timeout seconds (SHORT_TIMEOUT by default), kill the process (if signal.SIGKILL is available) and raise an AssertionError. The timeout feature is not available on Windows. @@ -3127,88 +3470,4 @@ def wait_process(pid, *, exitcode, timeout=None): # sanity check: it should not fail in practice if pid2 != pid: - raise AssertionError(f"pid {pid2} != pid {pid}") - -def skip_if_broken_multiprocessing_synchronize(): - """ - Skip tests if the multiprocessing.synchronize module is missing, if there - is no available semaphore implementation, or if creating a lock raises an - OSError (on Linux only). - """ - from .import_helper import import_module - - # Skip tests if the _multiprocessing extension is missing. - import_module('_multiprocessing') - - # Skip tests if there is no available semaphore implementation: - # multiprocessing.synchronize requires _multiprocessing.SemLock. - synchronize = import_module('multiprocessing.synchronize') - - if sys.platform == "linux": - try: - # bpo-38377: On Linux, creating a semaphore fails with OSError - # if the current user does not have the permission to create - # a file in /dev/shm/ directory. - synchronize.Lock(ctx=None) - except OSError as exc: - raise unittest.SkipTest(f"broken multiprocessing SemLock: {exc!r}") - - -def check_disallow_instantiation(testcase, tp, *args, **kwds): - """ - Check that given type cannot be instantiated using *args and **kwds. - - See bpo-43916: Add Py_TPFLAGS_DISALLOW_INSTANTIATION type flag. - """ - mod = tp.__module__ - name = tp.__name__ - if mod != 'builtins': - qualname = f"{mod}.{name}" - else: - qualname = f"{name}" - msg = f"cannot create '{re.escape(qualname)}' instances" - testcase.assertRaisesRegex(TypeError, msg, tp, *args, **kwds) - -@contextlib.contextmanager -def infinite_recursion(max_depth=75): - """Set a lower limit for tests that interact with infinite recursions - (e.g test_ast.ASTHelpers_Test.test_recursion_direct) since on some - debug windows builds, due to not enough functions being inlined the - stack size might not handle the default recursion limit (1000). See - bpo-11105 for details.""" - - original_depth = sys.getrecursionlimit() - try: - sys.setrecursionlimit(max_depth) - yield - finally: - sys.setrecursionlimit(original_depth) - -def ignore_deprecations_from(module: str, *, like: str) -> object: - token = object() - warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - module=module, - message=like + fr"(?#support{id(token)})", - ) - return token - -def clear_ignored_deprecations(*tokens: object) -> None: - if not tokens: - raise ValueError("Provide token or tokens returned by ignore_deprecations_from") - - new_filters = [] - endswith = tuple(rf"(?#support{id(token)})" for token in tokens) - for action, message, category, module, lineno in warnings.filters: - if action == "ignore" and category is DeprecationWarning: - if isinstance(message, re.Pattern): - msg = message.pattern - else: - msg = message or "" - if msg.endswith(endswith): - continue - new_filters.append((action, message, category, module, lineno)) - if warnings.filters != new_filters: - warnings.filters[:] = new_filters - warnings._filters_mutated() + raise AssertionError(f"pid {pid2} != pid {pid}") \ No newline at end of file diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py deleted file mode 100644 index 75f9b6c1d..000000000 --- a/Lib/test/support/os_helper.py +++ /dev/null @@ -1,617 +0,0 @@ -import collections.abc -import contextlib -import errno -import os -import re -import stat -import sys -import time -import unittest -import warnings - - -# Filename used for testing -if os.name == 'java': - # Jython disallows @ in module names - TESTFN_ASCII = '$test' -else: - TESTFN_ASCII = '@test' - -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN_ASCII = "{}_{}_tmp".format(TESTFN_ASCII, os.getpid()) - -# TESTFN_UNICODE is a non-ascii filename -TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" -if sys.platform == 'darwin': - # In Mac OS X's VFS API file names are, by definition, canonically - # decomposed Unicode, encoded using UTF-8. See QA1173: - # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html - import unicodedata - TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) - -# TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be -# encoded by the filesystem encoding (in strict mode). It can be None if we -# cannot generate such filename. -TESTFN_UNENCODABLE = None -if os.name == 'nt': - # skip win32s (0) or Windows 9x/ME (1) - if sys.getwindowsversion().platform >= 2: - # Different kinds of characters from various languages to minimize the - # probability that the whole name is encodable to MBCS (issue #9819) - TESTFN_UNENCODABLE = TESTFN_ASCII + "-\u5171\u0141\u2661\u0363\uDC80" - try: - TESTFN_UNENCODABLE.encode(sys.getfilesystemencoding()) - except UnicodeEncodeError: - pass - else: - print('WARNING: The filename %r CAN be encoded by the filesystem ' - 'encoding (%s). Unicode filename tests may not be effective' - % (TESTFN_UNENCODABLE, sys.getfilesystemencoding())) - TESTFN_UNENCODABLE = None -# TODO: RUSTPYTHON comment out before -# # Mac OS X denies unencodable filenames (invalid utf-8) -# elif sys.platform != 'darwin': -# try: -# # ascii and utf-8 cannot encode the byte 0xff -# b'\xff'.decode(sys.getfilesystemencoding()) -# except UnicodeDecodeError: -# # 0xff will be encoded using the surrogate character u+DCFF -# TESTFN_UNENCODABLE = TESTFN_ASCII \ -# + b'-\xff'.decode(sys.getfilesystemencoding(), 'surrogateescape') -# else: -# # File system encoding (eg. ISO-8859-* encodings) can encode -# # the byte 0xff. Skip some unicode filename tests. -# pass - -# FS_NONASCII: non-ASCII character encodable by os.fsencode(), -# or an empty string if there is no such character. -FS_NONASCII = '' -for character in ( - # First try printable and common characters to have a readable filename. - # For each character, the encoding list are just example of encodings able - # to encode the character (the list is not exhaustive). - - # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 - '\u00E6', - # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 - '\u0130', - # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 - '\u0141', - # U+03C6 (Greek Small Letter Phi): cp1253 - '\u03C6', - # U+041A (Cyrillic Capital Letter Ka): cp1251 - '\u041A', - # U+05D0 (Hebrew Letter Alef): Encodable to cp424 - '\u05D0', - # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic - '\u060C', - # U+062A (Arabic Letter Teh): cp720 - '\u062A', - # U+0E01 (Thai Character Ko Kai): cp874 - '\u0E01', - - # Then try more "special" characters. "special" because they may be - # interpreted or displayed differently depending on the exact locale - # encoding and the font. - - # U+00A0 (No-Break Space) - '\u00A0', - # U+20AC (Euro Sign) - '\u20AC', -): - try: - # If Python is set up to use the legacy 'mbcs' in Windows, - # 'replace' error mode is used, and encode() returns b'?' - # for characters missing in the ANSI codepage - if os.fsdecode(os.fsencode(character)) != character: - raise UnicodeError - except UnicodeError: - pass - else: - FS_NONASCII = character - break - -# Save the initial cwd -SAVEDCWD = os.getcwd() - -# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be -# decoded from the filesystem encoding (in strict mode). It can be None if we -# cannot generate such filename (ex: the latin1 encoding can decode any byte -# sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks -# to the surrogateescape error handler (PEP 383), but not from the filesystem -# encoding in strict mode. -TESTFN_UNDECODABLE = None -for name in ( - # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows - # accepts it to create a file or a directory, or don't accept to enter to - # such directory (when the bytes name is used). So test b'\xe7' first: - # it is not decodable from cp932. - b'\xe7w\xf0', - # undecodable from ASCII, UTF-8 - b'\xff', - # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 - # and cp857 - b'\xae\xd5' - # undecodable from UTF-8 (UNIX and Mac OS X) - b'\xed\xb2\x80', b'\xed\xb4\x80', - # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, - # cp1253, cp1254, cp1255, cp1257, cp1258 - b'\x81\x98', -): - try: - name.decode(sys.getfilesystemencoding()) - except UnicodeDecodeError: - TESTFN_UNDECODABLE = os.fsencode(TESTFN_ASCII) + name - break - -if FS_NONASCII: - TESTFN_NONASCII = TESTFN_ASCII + FS_NONASCII -else: - TESTFN_NONASCII = None -TESTFN = TESTFN_NONASCII or TESTFN_ASCII - - -def make_bad_fd(): - """ - Create an invalid file descriptor by opening and closing a file and return - its fd. - """ - file = open(TESTFN, "wb") - try: - return file.fileno() - finally: - file.close() - unlink(TESTFN) - - -_can_symlink = None - - -def can_symlink(): - global _can_symlink - if _can_symlink is not None: - return _can_symlink - symlink_path = TESTFN + "can_symlink" - try: - os.symlink(TESTFN, symlink_path) - can = True - except (OSError, NotImplementedError, AttributeError): - can = False - else: - os.remove(symlink_path) - _can_symlink = can - return can - - -def skip_unless_symlink(test): - """Skip decorator for tests that require functional symlink""" - ok = can_symlink() - msg = "Requires functional symlink implementation" - return test if ok else unittest.skip(msg)(test) - - -_can_xattr = None - - -def can_xattr(): - import tempfile - global _can_xattr - if _can_xattr is not None: - return _can_xattr - if not hasattr(os, "setxattr"): - can = False - else: - import platform - tmp_dir = tempfile.mkdtemp() - tmp_fp, tmp_name = tempfile.mkstemp(dir=tmp_dir) - try: - with open(TESTFN, "wb") as fp: - try: - # TESTFN & tempfile may use different file systems with - # different capabilities - os.setxattr(tmp_fp, b"user.test", b"") - os.setxattr(tmp_name, b"trusted.foo", b"42") - os.setxattr(fp.fileno(), b"user.test", b"") - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match(r"2.6.(\d{1,2})", kernel_version) - can = m is None or int(m.group(1)) >= 39 - except OSError: - can = False - finally: - unlink(TESTFN) - unlink(tmp_name) - rmdir(tmp_dir) - _can_xattr = can - return can - - -def skip_unless_xattr(test): - """Skip decorator for tests that require functional extended attributes""" - ok = can_xattr() - msg = "no non-broken extended attribute support" - return test if ok else unittest.skip(msg)(test) - - -def unlink(filename): - try: - _unlink(filename) - except (FileNotFoundError, NotADirectoryError): - pass - - -if sys.platform.startswith("win"): - def _waitfor(func, pathname, waitall=False): - # Perform the operation - func(pathname) - # Now setup the wait loop - if waitall: - dirname = pathname - else: - dirname, name = os.path.split(pathname) - dirname = dirname or '.' - # Check for `pathname` to be removed from the filesystem. - # The exponential backoff of the timeout amounts to a total - # of ~1 second after which the deletion is probably an error - # anyway. - # Testing on an i7@4.3GHz shows that usually only 1 iteration is - # required when contention occurs. - timeout = 0.001 - while timeout < 1.0: - # Note we are only testing for the existence of the file(s) in - # the contents of the directory regardless of any security or - # access rights. If we have made it this far, we have sufficient - # permissions to do that much using Python's equivalent of the - # Windows API FindFirstFile. - # Other Windows APIs can fail or give incorrect results when - # dealing with files that are pending deletion. - L = os.listdir(dirname) - if not (L if waitall else name in L): - return - # Increase the timeout and try again - time.sleep(timeout) - timeout *= 2 - warnings.warn('tests may fail, delete still pending for ' + pathname, - RuntimeWarning, stacklevel=4) - - def _unlink(filename): - _waitfor(os.unlink, filename) - - def _rmdir(dirname): - _waitfor(os.rmdir, dirname) - - def _rmtree(path): - from test.support import _force_run - - def _rmtree_inner(path): - for name in _force_run(path, os.listdir, path): - fullname = os.path.join(path, name) - try: - mode = os.lstat(fullname).st_mode - except OSError as exc: - print("os_helper.rmtree(): os.lstat(%r) failed with %s" - % (fullname, exc), - file=sys.__stderr__) - mode = 0 - if stat.S_ISDIR(mode): - _waitfor(_rmtree_inner, fullname, waitall=True) - _force_run(fullname, os.rmdir, fullname) - else: - _force_run(fullname, os.unlink, fullname) - _waitfor(_rmtree_inner, path, waitall=True) - _waitfor(lambda p: _force_run(p, os.rmdir, p), path) - - def _longpath(path): - try: - import ctypes - except ImportError: - # No ctypes means we can't expands paths. - pass - else: - buffer = ctypes.create_unicode_buffer(len(path) * 2) - length = ctypes.windll.kernel32.GetLongPathNameW(path, buffer, - len(buffer)) - if length: - return buffer[:length] - return path -else: - _unlink = os.unlink - _rmdir = os.rmdir - - def _rmtree(path): - import shutil - try: - shutil.rmtree(path) - return - except OSError: - pass - - def _rmtree_inner(path): - from test.support import _force_run - for name in _force_run(path, os.listdir, path): - fullname = os.path.join(path, name) - try: - mode = os.lstat(fullname).st_mode - except OSError: - mode = 0 - if stat.S_ISDIR(mode): - _rmtree_inner(fullname) - _force_run(path, os.rmdir, fullname) - else: - _force_run(path, os.unlink, fullname) - _rmtree_inner(path) - os.rmdir(path) - - def _longpath(path): - return path - - -def rmdir(dirname): - try: - _rmdir(dirname) - except FileNotFoundError: - pass - - -def rmtree(path): - try: - _rmtree(path) - except FileNotFoundError: - pass - - -@contextlib.contextmanager -def temp_dir(path=None, quiet=False): - """Return a context manager that creates a temporary directory. - - Arguments: - - path: the directory to create temporarily. If omitted or None, - defaults to creating a temporary directory using tempfile.mkdtemp. - - quiet: if False (the default), the context manager raises an exception - on error. Otherwise, if the path is specified and cannot be - created, only a warning is issued. - - """ - import tempfile - dir_created = False - if path is None: - path = tempfile.mkdtemp() - dir_created = True - path = os.path.realpath(path) - else: - try: - os.mkdir(path) - dir_created = True - except OSError as exc: - if not quiet: - raise - warnings.warn(f'tests may fail, unable to create ' - f'temporary directory {path!r}: {exc}', - RuntimeWarning, stacklevel=3) - if dir_created: - pid = os.getpid() - try: - yield path - finally: - # In case the process forks, let only the parent remove the - # directory. The child has a different process id. (bpo-30028) - if dir_created and pid == os.getpid(): - rmtree(path) - - -@contextlib.contextmanager -def change_cwd(path, quiet=False): - """Return a context manager that changes the current working directory. - - Arguments: - - path: the directory to use as the temporary current working directory. - - quiet: if False (the default), the context manager raises an exception - on error. Otherwise, it issues only a warning and keeps the current - working directory the same. - - """ - saved_dir = os.getcwd() - try: - os.chdir(os.path.realpath(path)) - except OSError as exc: - if not quiet: - raise - warnings.warn(f'tests may fail, unable to change the current working ' - f'directory to {path!r}: {exc}', - RuntimeWarning, stacklevel=3) - try: - yield os.getcwd() - finally: - os.chdir(saved_dir) - - -@contextlib.contextmanager -def temp_cwd(name='tempcwd', quiet=False): - """ - Context manager that temporarily creates and changes the CWD. - - The function temporarily changes the current working directory - after creating a temporary directory in the current directory with - name *name*. If *name* is None, the temporary directory is - created using tempfile.mkdtemp. - - If *quiet* is False (default) and it is not possible to - create or change the CWD, an error is raised. If *quiet* is True, - only a warning is raised and the original CWD is used. - - """ - with temp_dir(path=name, quiet=quiet) as temp_path: - with change_cwd(temp_path, quiet=quiet) as cwd_dir: - yield cwd_dir - - -def create_empty_file(filename): - """Create an empty file. If the file already exists, truncate it.""" - fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) - os.close(fd) - - -def fs_is_case_insensitive(directory): - """Detects if the file system for the specified directory - is case-insensitive.""" - import tempfile - with tempfile.NamedTemporaryFile(dir=directory) as base: - base_path = base.name - case_path = base_path.upper() - if case_path == base_path: - case_path = base_path.lower() - try: - return os.path.samefile(base_path, case_path) - except FileNotFoundError: - return False - - -class FakePath: - """Simple implementing of the path protocol. - """ - def __init__(self, path): - self.path = path - - def __repr__(self): - return f'' - - def __fspath__(self): - if (isinstance(self.path, BaseException) or - isinstance(self.path, type) and - issubclass(self.path, BaseException)): - raise self.path - else: - return self.path - - -def fd_count(): - """Count the number of open file descriptors. - """ - if sys.platform.startswith(('linux', 'freebsd')): - try: - names = os.listdir("/proc/self/fd") - # Subtract one because listdir() internally opens a file - # descriptor to list the content of the /proc/self/fd/ directory. - return len(names) - 1 - except FileNotFoundError: - pass - - MAXFD = 256 - if hasattr(os, 'sysconf'): - try: - MAXFD = os.sysconf("SC_OPEN_MAX") - except OSError: - pass - - old_modes = None - if sys.platform == 'win32': - # bpo-25306, bpo-31009: Call CrtSetReportMode() to not kill the process - # on invalid file descriptor if Python is compiled in debug mode - try: - import msvcrt - msvcrt.CrtSetReportMode - except (AttributeError, ImportError): - # no msvcrt or a release build - pass - else: - old_modes = {} - for report_type in (msvcrt.CRT_WARN, - msvcrt.CRT_ERROR, - msvcrt.CRT_ASSERT): - old_modes[report_type] = msvcrt.CrtSetReportMode(report_type, - 0) - - try: - count = 0 - for fd in range(MAXFD): - try: - # Prefer dup() over fstat(). fstat() can require input/output - # whereas dup() doesn't. - fd2 = os.dup(fd) - except OSError as e: - if e.errno != errno.EBADF: - raise - else: - os.close(fd2) - count += 1 - finally: - if old_modes is not None: - for report_type in (msvcrt.CRT_WARN, - msvcrt.CRT_ERROR, - msvcrt.CRT_ASSERT): - msvcrt.CrtSetReportMode(report_type, old_modes[report_type]) - - return count - - -if hasattr(os, "umask"): - @contextlib.contextmanager - def temp_umask(umask): - """Context manager that temporarily sets the process umask.""" - oldmask = os.umask(umask) - try: - yield - finally: - os.umask(oldmask) - - -class EnvironmentVarGuard(collections.abc.MutableMapping): - - """Class to help protect the environment variable properly. Can be used as - a context manager.""" - - def __init__(self): - self._environ = os.environ - self._changed = {} - - def __getitem__(self, envvar): - return self._environ[envvar] - - def __setitem__(self, envvar, value): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - self._environ[envvar] = value - - def __delitem__(self, envvar): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - if envvar in self._environ: - del self._environ[envvar] - - def keys(self): - return self._environ.keys() - - def __iter__(self): - return iter(self._environ) - - def __len__(self): - return len(self._environ) - - def set(self, envvar, value): - self[envvar] = value - - def unset(self, envvar): - del self[envvar] - - def copy(self): - # We do what os.environ.copy() does. - return dict(self) - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - for (k, v) in self._changed.items(): - if v is None: - if k in self._environ: - del self._environ[k] - else: - self._environ[k] = v - os.environ = self._environ diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index 512d861a4..ac7ab85a2 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -9,7 +9,6 @@ import sys import unittest import test.support -from test.support import os_helper import _osx_support @@ -20,7 +19,7 @@ class Test_OSXSupport(unittest.TestCase): self.maxDiff = None self.prog_name = 'bogus_program_xxxx' self.temp_path_dir = os.path.abspath(os.getcwd()) - self.env = os_helper.EnvironmentVarGuard() + self.env = test.support.EnvironmentVarGuard() self.addCleanup(self.env.__exit__) for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index e98c15b11..22cae626c 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -13,7 +13,6 @@ import argparse from io import StringIO from test import support -from test.support import os_helper from unittest import mock class StdIOBuffer(StringIO): pass @@ -24,7 +23,7 @@ class TestCase(unittest.TestCase): # The tests assume that line wrapping occurs at 80 columns, but this # behaviour can be overridden by setting the COLUMNS environment # variable. To ensure that this width is used, set COLUMNS to 80. - env = os_helper.EnvironmentVarGuard() + env = support.EnvironmentVarGuard() env['COLUMNS'] = '80' self.addCleanup(env.__exit__) @@ -3245,7 +3244,7 @@ class TestShortColumns(HelpTestCase): but we don't want any exceptions thrown in such cases. Only ugly representation. ''' def setUp(self): - env = os_helper.EnvironmentVarGuard() + env = support.EnvironmentVarGuard() env.set("COLUMNS", '15') self.addCleanup(env.__exit__) diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index f7405098b..6e6f4795e 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -4,7 +4,7 @@ import unittest from test import support -from test.support import _2G, os_helper +from test.support import _2G import weakref import pickle import operator @@ -371,13 +371,13 @@ class BaseTest: def test_tofromfile(self): a = array.array(self.typecode, 2*self.example) self.assertRaises(TypeError, a.tofile) - os_helper.unlink(os_helper.TESTFN) - f = open(os_helper.TESTFN, 'wb') + support.unlink(support.TESTFN) + f = open(support.TESTFN, 'wb') try: a.tofile(f) f.close() b = array.array(self.typecode) - f = open(os_helper.TESTFN, 'rb') + f = open(support.TESTFN, 'rb') self.assertRaises(TypeError, b.fromfile) b.fromfile(f, len(self.example)) self.assertEqual(b, array.array(self.typecode, self.example)) @@ -388,27 +388,27 @@ class BaseTest: finally: if not f.closed: f.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_fromfile_ioerror(self): # Issue #5395: Check if fromfile raises a proper OSError # instead of EOFError. a = array.array(self.typecode) - f = open(os_helper.TESTFN, 'wb') + f = open(support.TESTFN, 'wb') try: self.assertRaises(OSError, a.fromfile, f, len(self.example)) finally: f.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_filewrite(self): a = array.array(self.typecode, 2*self.example) - f = open(os_helper.TESTFN, 'wb') + f = open(support.TESTFN, 'wb') try: f.write(a) f.close() b = array.array(self.typecode) - f = open(os_helper.TESTFN, 'rb') + f = open(support.TESTFN, 'rb') b.fromfile(f, len(self.example)) self.assertEqual(b, array.array(self.typecode, self.example)) self.assertNotEqual(a, b) @@ -418,7 +418,7 @@ class BaseTest: finally: if not f.closed: f.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_tofromlist(self): a = array.array(self.typecode, 2*self.example) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index d6d9a10d4..3fcedb58e 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -10,7 +10,6 @@ import struct import threading from test import support -from test.support import os_helper from io import BytesIO if support.PGO: @@ -92,7 +91,7 @@ def bind_af_aware(sock, addr): """Helper function to bind a socket according to its family.""" if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. - os_helper.unlink(addr) + support.unlink(addr) support.bind_unix_socket(sock, addr) else: sock.bind(addr) @@ -369,14 +368,14 @@ class DispatcherWithSendTests(unittest.TestCase): class FileWrapperTest(unittest.TestCase): def setUp(self): self.d = b"It's not dead, it's sleeping!" - with open(os_helper.TESTFN, 'wb') as file: + with open(support.TESTFN, 'wb') as file: file.write(self.d) def tearDown(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_recv(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) w = asyncore.file_wrapper(fd) os.close(fd) @@ -390,20 +389,20 @@ class FileWrapperTest(unittest.TestCase): def test_send(self): d1 = b"Come again?" d2 = b"I want to buy some cheese." - fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_APPEND) + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_APPEND) w = asyncore.file_wrapper(fd) os.close(fd) w.write(d1) w.send(d2) w.close() - with open(os_helper.TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: self.assertEqual(file.read(), self.d + d1 + d2) @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), 'asyncore.file_dispatcher required') def test_dispatcher(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) data = [] class FileDispatcher(asyncore.file_dispatcher): def handle_read(self): @@ -415,7 +414,7 @@ class FileWrapperTest(unittest.TestCase): def test_resource_warning(self): # Issue #11453 - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) @@ -424,7 +423,7 @@ class FileWrapperTest(unittest.TestCase): support.gc_collect() def test_close_twice(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) @@ -804,10 +803,10 @@ class TestAPI_UseIPv6Sockets(BaseTestAPI): class TestAPI_UseUnixSockets(BaseTestAPI): if HAS_UNIX_SOCKETS: family = socket.AF_UNIX - addr = os_helper.TESTFN + addr = support.TESTFN def tearDown(self): - os_helper.unlink(self.addr) + support.unlink(self.addr) BaseTestAPI.tearDown(self) class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase): diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index 34ba5f9d4..c2a38b44d 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -4,7 +4,7 @@ import base64 import binascii import os from array import array -from test.support import script_helper, os_helper +from test.support import script_helper class LegacyBase64TestCase(unittest.TestCase): @@ -683,8 +683,8 @@ class BaseXYTestCase(unittest.TestCase): class TestMain(unittest.TestCase): def tearDown(self): - if os.path.exists(os_helper.TESTFN): - os.unlink(os_helper.TESTFN) + if os.path.exists(support.TESTFN): + os.unlink(support.TESTFN) def get_output(self, *args): return script_helper.assert_python_ok('-m', 'base64', *args).out @@ -698,9 +698,9 @@ class TestMain(unittest.TestCase): )) def test_encode_file(self): - with open(os_helper.TESTFN, 'wb') as fp: + with open(support.TESTFN, 'wb') as fp: fp.write(b'a\xffb\n') - output = self.get_output('-e', os_helper.TESTFN) + output = self.get_output('-e', support.TESTFN) self.assertEqual(output.rstrip(), b'Yf9iCg==') def test_encode_from_stdin(self): @@ -710,9 +710,9 @@ class TestMain(unittest.TestCase): self.assertIsNone(err) def test_decode(self): - with open(os_helper.TESTFN, 'wb') as fp: + with open(support.TESTFN, 'wb') as fp: fp.write(b'Yf9iCg==') - output = self.get_output('-d', os_helper.TESTFN) + output = self.get_output('-d', support.TESTFN) self.assertEqual(output.rstrip(), b'a\xffb') if __name__ == '__main__': diff --git a/Lib/test/test_binhex.py b/Lib/test/test_binhex.py index 8630f4237..492e25d97 100644 --- a/Lib/test/test_binhex.py +++ b/Lib/test/test_binhex.py @@ -6,20 +6,19 @@ import binhex import unittest from test import support -from test.support import os_helper class BinHexTestCase(unittest.TestCase): def setUp(self): - self.fname1 = os_helper.TESTFN + "1" - self.fname2 = os_helper.TESTFN + "2" - self.fname3 = os_helper.TESTFN + "very_long_filename__very_long_filename__very_long_filename__very_long_filename__" + self.fname1 = support.TESTFN + "1" + self.fname2 = support.TESTFN + "2" + self.fname3 = support.TESTFN + "very_long_filename__very_long_filename__very_long_filename__very_long_filename__" def tearDown(self): - os_helper.unlink(self.fname1) - os_helper.unlink(self.fname2) - os_helper.unlink(self.fname3) + support.unlink(self.fname1) + support.unlink(self.fname2) + support.unlink(self.fname3) DATA = b'Jack is my hero' diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index a60aa6713..726ccd03a 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -2,7 +2,6 @@ import unittest from test import support -from test.support import os_helper import os @@ -23,12 +22,12 @@ class BoolTest(unittest.TestCase): def test_print(self): try: - with open(os_helper.TESTFN, "w") as fo: + with open(support.TESTFN, "w") as fo: print(False, True, file=fo) - with open(os_helper.TESTFN, "r") as fi: + with open(support.TESTFN, "r") as fi: self.assertEqual(fi.read(), 'False True\n') finally: - os.remove(os_helper.TESTFN) + os.remove(support.TESTFN) def test_repr(self): self.assertEqual(repr(False), 'False') @@ -246,11 +245,11 @@ class BoolTest(unittest.TestCase): def test_fileclosed(self): try: - with open(os_helper.TESTFN, "w") as f: + with open(support.TESTFN, "w") as f: self.assertIs(f.closed, False) self.assertIs(f.closed, True) finally: - os.remove(os_helper.TESTFN) + os.remove(support.TESTFN) def test_types(self): # types are always true. diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 43a7f404f..79425453d 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -16,7 +16,6 @@ import contextlib import unittest from test import support -from test.support import os_helper from itertools import permutations, product from random import randrange, sample, choice import warnings @@ -40,7 +39,7 @@ except ImportError: ctypes = None try: - with os_helper.EnvironmentVarGuard() as os.environ, \ + with support.EnvironmentVarGuard() as os.environ, \ warnings.catch_warnings(): from numpy import ndarray as numpy_array except ImportError: diff --git a/Lib/test/test_bufio.py b/Lib/test/test_bufio.py index 3471351c4..be4c3153b 100644 --- a/Lib/test/test_bufio.py +++ b/Lib/test/test_bufio.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper import io # C implementation. import _pyio as pyio # Python implementation. @@ -18,18 +17,18 @@ class BufferSizeTest: # .readline()s deliver what we wrote. # Ensure we can open TESTFN for writing. - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) # Since C doesn't guarantee we can write/read arbitrary bytes in text # files, use binary mode. - f = self.open(os_helper.TESTFN, "wb") + f = self.open(support.TESTFN, "wb") try: # write once with \n and once without f.write(s) f.write(b"\n") f.write(s) f.close() - f = open(os_helper.TESTFN, "rb") + f = open(support.TESTFN, "rb") line = f.readline() self.assertEqual(line, s + b"\n") line = f.readline() @@ -38,7 +37,7 @@ class BufferSizeTest: self.assertFalse(line) # Must be at EOF f.close() finally: - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def drive_one(self, pattern): for length in lengths: diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 70221eb8d..341acc70c 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -20,9 +20,9 @@ import warnings from functools import partial from contextlib import ExitStack from operator import neg -from test.support import check_warnings, swap_attr +from test.support import ( + EnvironmentVarGuard, TESTFN, check_warnings, swap_attr, unlink) from test.support.script_helper import assert_python_ok -from test.support.os_helper import EnvironmentVarGuard, TESTFN, unlink from unittest.mock import MagicMock, patch try: import pty, signal diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 091ab4a4d..fd39c4093 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -912,12 +912,12 @@ class CommandLineTestCase(unittest.TestCase): class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {'mdays', 'January', 'February', 'EPOCH', + blacklist = {'mdays', 'January', 'February', 'EPOCH', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY', 'different_locale', 'c', 'prweek', 'week', 'format', 'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth'} - support.check__all__(self, calendar, not_exported=not_exported) + support.check__all__(self, calendar, blacklist=blacklist) class TestSubClassingCase(unittest.TestCase): diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index cc736572b..e65553af6 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -578,9 +578,9 @@ this is the content of the fake file ("form-data", {"name": "files", "filename": 'fo"o;bar'})) def test_all(self): - not_exported = {"logfile", "logfp", "initlog", "dolog", "nolog", + blacklist = {"logfile", "logfp", "initlog", "dolog", "nolog", "closelog", "log", "maxlen", "valid_boundary"} - support.check__all__(self, cgi, not_exported=not_exported) + support.check__all__(self, cgi, blacklist=blacklist) BOUNDARY = "---------------------------721837373350705526688164684" diff --git a/Lib/test/test_cgitb.py b/Lib/test/test_cgitb.py index 6b7c58a19..040d64aa7 100644 --- a/Lib/test/test_cgitb.py +++ b/Lib/test/test_cgitb.py @@ -1,4 +1,4 @@ -from test.support.os_helper import temp_dir +from test.support import temp_dir from test.support.script_helper import assert_python_failure import unittest import sys diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index cfb786c2d..ef3f172fa 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -12,7 +12,6 @@ from test.support.script_helper import ( spawn_python, kill_python, assert_python_ok, assert_python_failure, interpreter_requires_environment ) -from test.support import os_helper # Debug build? @@ -157,11 +156,11 @@ class CmdLineTest(unittest.TestCase): # All good if execution is successful assert_python_ok('-c', 'pass') - @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') + @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') def test_non_ascii(self): # Test handling of non-ascii data command = ("assert(ord(%r) == %s)" - % (os_helper.FS_NONASCII, ord(os_helper.FS_NONASCII))) + % (support.FS_NONASCII, ord(support.FS_NONASCII))) assert_python_ok('-c', command) # On Windows, pass bytes to subprocess doesn't test how Python decodes the @@ -527,8 +526,8 @@ class CmdLineTest(unittest.TestCase): # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a # borrowed reference to the dict of __main__ module and later modify # the dict whereas the module was destroyed - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) with open(filename, "w") as script: print("import sys", file=script) print("del sys.modules['__main__']", file=script) @@ -567,7 +566,7 @@ class CmdLineTest(unittest.TestCase): # dummyvar to prevent extraneous -E dummyvar="") self.assertEqual(out.strip(), b'1 1 1') - with os_helper.temp_cwd() as tmpdir: + with support.temp_cwd() as tmpdir: fake = os.path.join(tmpdir, "uuid.py") main = os.path.join(tmpdir, "main.py") with open(fake, "w") as f: @@ -633,7 +632,7 @@ class CmdLineTest(unittest.TestCase): elif opt is not None: args[:0] = ['-X', f'pycache_prefix={opt}'] with self.subTest(envval=envval, opt=opt): - with os_helper.temp_cwd(): + with support.temp_cwd(): assert_python_ok(*args, **env) def run_xdev(self, *args, check_exitcode=True, xdev=True): diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 59bbfee60..e332a1d6a 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -17,7 +17,6 @@ from test import support from test.support.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, assert_python_ok, assert_python_failure, spawn_python, kill_python) -from test.support import os_helper verbose = support.verbose @@ -225,7 +224,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_basic_script(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') self._check_script(script_name, script_name, script_name, script_dir, None, @@ -234,7 +233,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_script_compiled(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') py_compile.compile(script_name, doraise=True) os.remove(script_name) @@ -244,14 +243,14 @@ class CmdLineTest(unittest.TestCase): importlib.machinery.SourcelessFileLoader) def test_directory(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') self._check_script(script_dir, script_name, script_dir, script_dir, '', importlib.machinery.SourceFileLoader) def test_directory_compiled(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') py_compile.compile(script_name, doraise=True) os.remove(script_name) @@ -261,19 +260,19 @@ class CmdLineTest(unittest.TestCase): importlib.machinery.SourcelessFileLoader) def test_directory_error(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: msg = "can't find '__main__' module in %r" % script_dir self._check_import_error(script_dir, msg) def test_zipfile(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name) self._check_script(zip_name, run_name, zip_name, zip_name, '', zipimport.zipimporter) def test_zipfile_compiled_timestamp(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -285,7 +284,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_zipfile_compiled_checked_hash(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -297,7 +296,7 @@ class CmdLineTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_zipfile_compiled_unchecked_hash(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') compiled_name = py_compile.compile( script_name, doraise=True, @@ -307,14 +306,14 @@ class CmdLineTest(unittest.TestCase): zipimport.zipimporter) def test_zipfile_error(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'not_main') zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name) msg = "can't find '__main__' module in %r" % zip_name self._check_import_error(zip_name, msg) def test_module_in_package(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, 'script') @@ -324,14 +323,14 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_module_in_package_in_zipfile(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script') self._check_script(["-m", "test_pkg.script"], run_name, run_name, script_dir, 'test_pkg', zipimport.zipimporter, PYTHONPATH=zip_name, cwd=script_dir) def test_module_in_subpackage_in_zipfile(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script', depth=2) self._check_script(["-m", "test_pkg.test_pkg.script"], run_name, run_name, script_dir, 'test_pkg.test_pkg', @@ -339,7 +338,7 @@ class CmdLineTest(unittest.TestCase): PYTHONPATH=zip_name, cwd=script_dir) def test_package(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__') @@ -349,7 +348,7 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_package_compiled(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__') @@ -362,7 +361,7 @@ class CmdLineTest(unittest.TestCase): cwd=script_dir) def test_package_error(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) msg = ("'test_pkg' is a package and cannot " @@ -370,7 +369,7 @@ class CmdLineTest(unittest.TestCase): self._check_import_error(["-m", "test_pkg"], msg, cwd=script_dir) def test_package_recursion(self): - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) main_dir = os.path.join(pkg_dir, '__main__') @@ -385,8 +384,8 @@ class CmdLineTest(unittest.TestCase): def test_issue8202(self): # Make sure package __init__ modules see "-m" in sys.argv0 while # searching for the module to execute - with os_helper.temp_dir() as script_dir: - with os_helper.change_cwd(path=script_dir): + with support.temp_dir() as script_dir: + with support.change_cwd(path=script_dir): pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir, "import sys; print('init_argv0==%r' % sys.argv[0])") script_name = _make_test_script(pkg_dir, 'script') @@ -404,8 +403,8 @@ class CmdLineTest(unittest.TestCase): def test_issue8202_dash_c_file_ignored(self): # Make sure a "-c" file in the current directory # does not alter the value of sys.path[0] - with os_helper.temp_dir() as script_dir: - with os_helper.change_cwd(path=script_dir): + with support.temp_dir() as script_dir: + with support.change_cwd(path=script_dir): with open("-c", "w") as f: f.write("data") rc, out, err = assert_python_ok('-c', @@ -419,9 +418,9 @@ class CmdLineTest(unittest.TestCase): def test_issue8202_dash_m_file_ignored(self): # Make sure a "-m" file in the current directory # does not alter the value of sys.path[0] - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'other') - with os_helper.change_cwd(path=script_dir): + with support.change_cwd(path=script_dir): with open("-m", "w") as f: f.write("data") rc, out, err = assert_python_ok('-m', 'other', *example_args, @@ -433,7 +432,7 @@ class CmdLineTest(unittest.TestCase): def test_issue20884(self): # On Windows, script with encoding cookie and LF line ending # will be failed. - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = os.path.join(script_dir, "issue20884.py") with open(script_name, "w", newline='\n') as f: f.write("#coding: iso-8859-1\n") @@ -442,15 +441,15 @@ class CmdLineTest(unittest.TestCase): f.write('x'*80 + '\n') f.write('"""\n') - with os_helper.change_cwd(path=script_dir): + with support.change_cwd(path=script_dir): rc, out, err = assert_python_ok(script_name) self.assertEqual(b"", out) self.assertEqual(b"", err) @contextlib.contextmanager def setup_test_pkg(self, *args): - with os_helper.temp_dir() as script_dir, \ - os_helper.change_cwd(path=script_dir): + with support.temp_dir() as script_dir, \ + support.change_cwd(path=script_dir): pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir, *args) yield pkg_dir @@ -494,8 +493,8 @@ class CmdLineTest(unittest.TestCase): self.assertNotIn(b'Traceback', err) def test_dash_m_bad_pyc(self): - with os_helper.temp_dir() as script_dir, \ - os_helper.change_cwd(path=script_dir): + with support.temp_dir() as script_dir, \ + support.change_cwd(path=script_dir): os.mkdir('test_pkg') # Create invalid *.pyc as empty file with open('test_pkg/__init__.pyc', 'wb'): @@ -542,7 +541,7 @@ class CmdLineTest(unittest.TestCase): except: raise NameError from None """) - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = stderr.decode('ascii').split('\n') @@ -555,18 +554,18 @@ class CmdLineTest(unittest.TestCase): # Mac OS X denies the creation of a file with an invalid UTF-8 name. # Windows allows creating a name with an arbitrary bytes name, but # Python cannot a undecodable bytes argument to a subprocess. - if (os_helper.TESTFN_UNDECODABLE + if (support.TESTFN_UNDECODABLE and sys.platform not in ('win32', 'darwin')): - name = os.fsdecode(os_helper.TESTFN_UNDECODABLE) - elif os_helper.TESTFN_NONASCII: - name = os_helper.TESTFN_NONASCII + name = os.fsdecode(support.TESTFN_UNDECODABLE) + elif support.TESTFN_NONASCII: + name = support.TESTFN_NONASCII else: - self.skipTest("need os_helper.TESTFN_NONASCII") + self.skipTest("need support.TESTFN_NONASCII") # Issue #16218 source = 'print(ascii(__file__))\n' script_name = _make_test_script(os.curdir, name, source) - self.addCleanup(os_helper.unlink, script_name) + self.addCleanup(support.unlink, script_name) rc, stdout, stderr = assert_python_ok(script_name) self.assertEqual( ascii(script_name), @@ -592,7 +591,7 @@ class CmdLineTest(unittest.TestCase): if error: sys.exit(error) """) - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = stderr.decode('ascii') @@ -602,7 +601,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_unindented_caret_position(self): script = "1 + 1 = 2\n" - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read() @@ -616,7 +615,7 @@ class CmdLineTest(unittest.TestCase): if True: 1 + 1 = 2 """) - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read() @@ -638,7 +637,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_multi_line_fstring(self): script = 'foo = f"""{}\nfoo"""\n' - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure(script_name) self.assertEqual( @@ -654,7 +653,7 @@ class CmdLineTest(unittest.TestCase): @unittest.expectedFailure def test_syntaxerror_invalid_escape_sequence_multi_line(self): script = 'foo = """\\q\n"""\n' - with os_helper.temp_dir() as script_dir: + with support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script', script) exitcode, stdout, stderr = assert_python_failure( '-Werror', script_name, @@ -684,7 +683,7 @@ class CmdLineTest(unittest.TestCase): """) # Always show full path diffs on errors self.maxDiff = None - with os_helper.temp_dir() as work_dir, os_helper.temp_dir() as script_dir: + with support.temp_dir() as work_dir, support.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', script) # Reference output comes from directly executing __main__.py # We omit PYTHONPATH and user site to align with isolated mode @@ -716,7 +715,7 @@ class CmdLineTest(unittest.TestCase): """) # Always show full path diffs on errors self.maxDiff = None - with os_helper.temp_dir() as work_dir: + with support.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) script_name = _make_test_script(script_dir, '__main__', script) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index dd8a4b503..cd60f774b 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -8,7 +8,6 @@ import encodings from unittest import mock from test import support -from test.support import os_helper try: import _testcapi @@ -840,11 +839,11 @@ class UTF16Test(ReadTest, unittest.TestCase): s1 = 'Hello\r\nworld\r\n' s = s1.encode(self.encoding) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, 'wb') as fp: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, 'wb') as fp: fp.write(s) with support.check_warnings(('', DeprecationWarning)): - reader = codecs.open(os_helper.TESTFN, 'U', encoding=self.encoding) + reader = codecs.open(support.TESTFN, 'U', encoding=self.encoding) with reader: self.assertEqual(reader.read(), s1) @@ -1892,10 +1891,10 @@ class CodecsModuleTest(unittest.TestCase): getattr(codecs, api) def test_open(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) for mode in ('w', 'r', 'r+', 'w+', 'a', 'a+'): with self.subTest(mode), \ - codecs.open(os_helper.TESTFN, mode, 'ascii') as file: + codecs.open(support.TESTFN, mode, 'ascii') as file: self.assertIsInstance(file, codecs.StreamReaderWriter) def test_undefined(self): @@ -1913,7 +1912,7 @@ class CodecsModuleTest(unittest.TestCase): mock_open = mock.mock_open() with mock.patch('builtins.open', mock_open) as file: with self.assertRaises(LookupError): - codecs.open(os_helper.TESTFN, 'wt', 'invalid-encoding') + codecs.open(support.TESTFN, 'wt', 'invalid-encoding') file().close.assert_called() @@ -2755,10 +2754,10 @@ class BomTest(unittest.TestCase): "utf-32", "utf-32-le", "utf-32-be") - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) for encoding in tests: # Check if the BOM is written only once - with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: f.write(data) f.write(data) f.seek(0) @@ -2767,7 +2766,7 @@ class BomTest(unittest.TestCase): self.assertEqual(f.read(), data * 2) # Check that the BOM is written after a seek(0) - with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: f.write(data[0]) self.assertNotEqual(f.tell(), 0) f.seek(0) @@ -2776,7 +2775,7 @@ class BomTest(unittest.TestCase): self.assertEqual(f.read(), data) # (StreamWriter) Check that the BOM is written after a seek(0) - with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: f.writer.write(data[0]) self.assertNotEqual(f.writer.tell(), 0) f.writer.seek(0) @@ -2786,7 +2785,7 @@ class BomTest(unittest.TestCase): # Check that the BOM is not written after a seek() at a position # different than the start - with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: f.write(data) f.seek(f.tell()) f.write(data) @@ -2795,7 +2794,7 @@ class BomTest(unittest.TestCase): # (StreamWriter) Check that the BOM is not written after a seek() # at a position different than the start - with codecs.open(os_helper.TESTFN, 'w+', encoding=encoding) as f: + with codecs.open(support.TESTFN, 'w+', encoding=encoding) as f: f.writer.write(data) f.writer.seek(f.writer.tell()) f.writer.write(data) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index c10a21bb3..615a600ab 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -7,8 +7,7 @@ import _ast import tempfile import types from test import support -from test.support import script_helper -from test.support.os_helper import FakePath +from test.support import script_helper, FakePath class TestSpecifics(unittest.TestCase): diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index a6f754591..e0ed859c5 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper from test.test_grammar import (VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS) @@ -515,15 +514,15 @@ class ComplexTest(unittest.TestCase): fo = None try: - fo = open(os_helper.TESTFN, "w") + fo = open(support.TESTFN, "w") print(a, b, file=fo) fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), ("%s %s\n" % (a, b))) finally: if (fo is not None) and (not fo.closed): fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_getnewargs(self): self.assertEqual((1+2j).__getnewargs__(), (1.0, 2.0)) diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index d2cfd08cc..ddf8de0f7 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -8,7 +8,6 @@ import unittest import warnings from test import support -from test.support import os_helper class SortedDict(collections.UserDict): @@ -1074,17 +1073,17 @@ class MultilineValuesTestCase(BasicTestCase, unittest.TestCase): cf.add_section(s) for j in range(10): cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam) - with open(os_helper.TESTFN, 'w') as f: + with open(support.TESTFN, 'w') as f: cf.write(f) def tearDown(self): - os.unlink(os_helper.TESTFN) + os.unlink(support.TESTFN) def test_dominating_multiline_values(self): # We're reading from file because this is where the code changed # during performance updates in Python 3.2 cf_from_file = self.newconfig() - with open(os_helper.TESTFN) as f: + with open(support.TESTFN) as f: cf_from_file.read_file(f) self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'), self.wonderful_spam.replace('\t\n', '\n')) @@ -2162,8 +2161,8 @@ class BlatantOverrideConvertersTestCase(unittest.TestCase): class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {"Error"} - support.check__all__(self, configparser, not_exported=not_exported) + blacklist = {"Error"} + support.check__all__(self, configparser, blacklist=blacklist) if __name__ == '__main__': diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index f8e04698c..90ea50105 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -7,7 +7,6 @@ import threading import unittest from contextlib import * # Tests __all__ from test import support -from test.support import os_helper import weakref @@ -332,7 +331,7 @@ class FileContextTestCase(unittest.TestCase): 1 / 0 self.assertTrue(f.closed) finally: - os_helper.unlink(tfn) + support.unlink(tfn) class LockContextTestCase(unittest.TestCase): diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 256e71683..571da973a 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -3,7 +3,6 @@ import unittest import glob import test.support -from test.support import os_helper # Skip tests if dbm module doesn't exist. dbm = test.support.import_module('dbm') @@ -13,7 +12,7 @@ try: except ImportError: ndbm = None -_fname = os_helper.TESTFN +_fname = test.support.TESTFN # # Iterates over every database module supported by dbm currently available, @@ -35,7 +34,7 @@ def delete_files(): # we don't know the precise name the underlying database uses # so we use glob to locate all names for f in glob.glob(glob.escape(_fname) + "*"): - os_helper.unlink(f) + test.support.unlink(f) class AnyDBMTestCase: @@ -75,7 +74,7 @@ class AnyDBMTestCase: def test_anydbm_creation_n_file_exists_with_invalid_contents(self): # create an empty file - os_helper.create_empty_file(_fname) + test.support.create_empty_file(_fname) with dbm.open(_fname, 'n') as f: self.assertEqual(len(f), 0) @@ -170,7 +169,7 @@ class WhichDBTestCase(unittest.TestCase): # Issue 17198: check that ndbm which is referenced in whichdb is defined db_file = '{}_ndbm.db'.format(_fname) with open(db_file, 'w'): - self.addCleanup(os_helper.unlink, db_file) + self.addCleanup(test.support.unlink, db_file) self.assertIsNone(self.dbm.whichdb(db_file[:-3])) def tearDown(self): @@ -178,7 +177,7 @@ class WhichDBTestCase(unittest.TestCase): def setUp(self): delete_files() - self.filename = os_helper.TESTFN + self.filename = test.support.TESTFN self.d = dbm.open(self.filename, 'c') self.d.close() self.dbm = test.support.import_fresh_module('dbm') diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 0dc489362..0a6077820 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -10,10 +10,9 @@ import stat import unittest import dbm.dumb as dumbdbm from test import support -from test.support import os_helper from functools import partial -_fname = os_helper.TESTFN +_fname = support.TESTFN def _delete_files(): for ext in [".dir", ".dat", ".bak"]: @@ -265,7 +264,7 @@ class DumbDBMTestCase(unittest.TestCase): dumbdbm.open(_fname, flag) def test_readonly_files(self): - with os_helper.temp_dir() as dir: + with support.temp_dir() as dir: fname = os.path.join(dir, 'db') with dumbdbm.open(fname, 'n') as f: self.assertEqual(list(f.keys()), []) @@ -278,12 +277,12 @@ class DumbDBMTestCase(unittest.TestCase): self.assertEqual(sorted(f.keys()), sorted(self._dict)) f.close() # don't write - @unittest.skipUnless(os_helper.TESTFN_NONASCII, + @unittest.skipUnless(support.TESTFN_NONASCII, 'requires OS support of non-ASCII encodings') def test_nonascii_filename(self): - filename = os_helper.TESTFN_NONASCII + filename = support.TESTFN_NONASCII for suffix in ['.dir', '.dat', '.bak']: - self.addCleanup(os_helper.unlink, filename + suffix) + self.addCleanup(support.unlink, filename + suffix) with dumbdbm.open(filename, 'c') as db: db[b'key'] = b'value' self.assertTrue(os.path.exists(filename + '.dat')) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index ba6e6a383..68d751b9f 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -1,7 +1,6 @@ from collections import deque import unittest from test import support, seq_tests -from test.support import os_helper import gc import weakref import copy @@ -67,28 +66,28 @@ class TestBasic(unittest.TestCase): self.assertEqual(list(d), [7, 8, 9]) d = deque(range(200), maxlen=10) d.append(d) - os_helper.unlink(os_helper.TESTFN) - fo = open(os_helper.TESTFN, "w") + support.unlink(support.TESTFN) + fo = open(support.TESTFN, "w") try: fo.write(str(d)) fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) d = deque(range(10), maxlen=None) self.assertEqual(repr(d), 'deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])') - fo = open(os_helper.TESTFN, "w") + fo = open(support.TESTFN, "w") try: fo.write(str(d)) fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_maxlen_zero(self): it = iter(range(100)) @@ -540,15 +539,15 @@ class TestBasic(unittest.TestCase): d = deque(range(200)) d.append(d) try: - os_helper.unlink(os_helper.TESTFN) - fo = open(os_helper.TESTFN, "w") + support.unlink(support.TESTFN) + fo = open(support.TESTFN, "w") print(d, file=fo, end='') fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), repr(d)) finally: fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_init(self): self.assertRaises(TypeError, deque, 'abc', 2, 3); diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 2ea96285f..dab99f154 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -8,14 +8,11 @@ import pickle import weakref import errno -from test.support import (captured_stderr, check_impl_detail, - cpython_only, gc_collect, - no_tracing, script_helper, - SuppressCrashReport, - import_module, check_warnings,) -from test.support.os_helper import TESTFN, unlink +from test.support import (TESTFN, captured_stderr, check_impl_detail, + check_warnings, cpython_only, gc_collect, run_unittest, + no_tracing, unlink, import_module, script_helper, + SuppressCrashReport) from test import support - class NaiveException(Exception): def __init__(self, x): self.x = x diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index 2b7ee3a6b..5fecec861 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -7,9 +7,8 @@ from weakref import proxy import io import _pyio as pyio +from test.support import TESTFN from test import support -from test.support import os_helper -from test.support.os_helper import TESTFN from collections import UserList class AutoFileTests: @@ -21,7 +20,7 @@ class AutoFileTests: def tearDown(self): if self.f: self.f.close() - os_helper.unlink(TESTFN) + support.unlink(TESTFN) def testWeakRefs(self): # verify weak references @@ -141,7 +140,7 @@ class PyAutoFileTests(AutoFileTests, unittest.TestCase): class OtherFileTests: def tearDown(self): - os_helper.unlink(TESTFN) + support.unlink(TESTFN) def testModeStrings(self): # check invalid mode strings diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index a1e6de3b2..4fb921e04 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -9,9 +9,8 @@ from array import array from weakref import proxy from functools import wraps -from test.support import (check_warnings, run_unittest, - cpython_only, swap_attr) -from test.support.os_helper import TESTFN, TESTFN_UNICODE, make_bad_fd +from test.support import (TESTFN, TESTFN_UNICODE, check_warnings, run_unittest, + make_bad_fd, cpython_only, swap_attr) from collections import UserList import _io # C implementation of io diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 4e31ec989..c2c4fcd63 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -1080,10 +1080,10 @@ class TestTimeouts(TestCase): class MiscTestCase(TestCase): def test__all__(self): - not_exported = {'MSG_OOB', 'FTP_PORT', 'MAXLINE', 'CRLF', 'B_CRLF', + blacklist = {'MSG_OOB', 'FTP_PORT', 'MAXLINE', 'CRLF', 'B_CRLF', 'Error', 'parse150', 'parse227', 'parse229', 'parse257', 'print_line', 'ftpcp', 'test'} - support.check__all__(self, ftplib, not_exported=not_exported) + support.check__all__(self, ftplib, blacklist=blacklist) def test_main(): diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 45054b619..545fe6073 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -8,9 +8,8 @@ import sys import unittest import warnings from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok -from test.support.os_helper import FakePath +from test.support import FakePath def create_file(filename, data=b'foo'): @@ -98,8 +97,8 @@ class GenericTest: self.assertNotEqual(s1[n:n+1], s2[n:n+1]) def test_getsize(self): - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) create_file(filename, b'Hello') self.assertEqual(self.pathmodule.getsize(filename), 5) @@ -109,8 +108,8 @@ class GenericTest: self.assertEqual(self.pathmodule.getsize(filename), 12) def test_filetime(self): - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) create_file(filename, b'foo') @@ -127,9 +126,9 @@ class GenericTest: ) def test_exists(self): - filename = os_helper.TESTFN + filename = support.TESTFN bfilename = os.fsencode(filename) - self.addCleanup(os_helper.unlink, filename) + self.addCleanup(support.unlink, filename) self.assertIs(self.pathmodule.exists(filename), False) self.assertIs(self.pathmodule.exists(bfilename), False) @@ -164,7 +163,7 @@ class GenericTest: self.assertFalse(self.pathmodule.exists(r)) def test_isdir(self): - filename = os_helper.TESTFN + filename = support.TESTFN bfilename = os.fsencode(filename) self.assertIs(self.pathmodule.isdir(filename), False) self.assertIs(self.pathmodule.isdir(bfilename), False) @@ -179,7 +178,7 @@ class GenericTest: self.assertIs(self.pathmodule.isdir(filename), False) self.assertIs(self.pathmodule.isdir(bfilename), False) finally: - os_helper.unlink(filename) + support.unlink(filename) try: os.mkdir(filename) @@ -189,7 +188,7 @@ class GenericTest: support.rmdir(filename) def test_isfile(self): - filename = os_helper.TESTFN + filename = support.TESTFN bfilename = os.fsencode(filename) self.assertIs(self.pathmodule.isfile(filename), False) self.assertIs(self.pathmodule.isfile(bfilename), False) @@ -204,7 +203,7 @@ class GenericTest: self.assertIs(self.pathmodule.isfile(filename), True) self.assertIs(self.pathmodule.isfile(bfilename), True) finally: - os_helper.unlink(filename) + support.unlink(filename) try: os.mkdir(filename) @@ -214,10 +213,10 @@ class GenericTest: support.rmdir(filename) def test_samefile(self): - file1 = os_helper.TESTFN - file2 = os_helper.TESTFN + "2" - self.addCleanup(os_helper.unlink, file1) - self.addCleanup(os_helper.unlink, file2) + file1 = support.TESTFN + file2 = support.TESTFN + "2" + self.addCleanup(support.unlink, file1) + self.addCleanup(support.unlink, file2) create_file(file1) self.assertTrue(self.pathmodule.samefile(file1, file1)) @@ -228,10 +227,10 @@ class GenericTest: self.assertRaises(TypeError, self.pathmodule.samefile) def _test_samefile_on_link_func(self, func): - test_fn1 = os_helper.TESTFN - test_fn2 = os_helper.TESTFN + "2" - self.addCleanup(os_helper.unlink, test_fn1) - self.addCleanup(os_helper.unlink, test_fn2) + test_fn1 = support.TESTFN + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) create_file(test_fn1) @@ -242,7 +241,7 @@ class GenericTest: create_file(test_fn2) self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_samefile_on_symlink(self): self._test_samefile_on_link_func(os.symlink) @@ -257,10 +256,10 @@ class GenericTest: self.skipTest('os.link(): %s' % e) def test_samestat(self): - test_fn1 = os_helper.TESTFN - test_fn2 = os_helper.TESTFN + "2" - self.addCleanup(os_helper.unlink, test_fn1) - self.addCleanup(os_helper.unlink, test_fn2) + test_fn1 = support.TESTFN + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) create_file(test_fn1) stat1 = os.stat(test_fn1) @@ -273,10 +272,10 @@ class GenericTest: self.assertRaises(TypeError, self.pathmodule.samestat) def _test_samestat_on_link_func(self, func): - test_fn1 = os_helper.TESTFN + "1" - test_fn2 = os_helper.TESTFN + "2" - self.addCleanup(os_helper.unlink, test_fn1) - self.addCleanup(os_helper.unlink, test_fn2) + test_fn1 = support.TESTFN + "1" + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) create_file(test_fn1) func(test_fn1, test_fn2) @@ -288,7 +287,7 @@ class GenericTest: self.assertFalse(self.pathmodule.samestat(os.stat(test_fn1), os.stat(test_fn2))) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_samestat_on_symlink(self): self._test_samestat_on_link_func(os.symlink) @@ -303,8 +302,8 @@ class GenericTest: self.skipTest('os.link(): %s' % e) def test_sameopenfile(self): - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) create_file(filename) with open(filename, "rb", 0) as fp1: @@ -407,7 +406,7 @@ class CommonTest(GenericTest): def test_expandvars(self): expandvars = self.pathmodule.expandvars - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.clear() env["foo"] = "bar" env["{foo"] = "baz1" @@ -436,14 +435,14 @@ class CommonTest(GenericTest): self.assertEqual(expandvars(b"$foo$foo"), b"barbar") self.assertEqual(expandvars(b"$bar$bar"), b"$bar$bar") - @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') + @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') def test_expandvars_nonascii(self): expandvars = self.pathmodule.expandvars def check(value, expected): self.assertEqual(expandvars(value), expected) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.clear() - nonascii = os_helper.FS_NONASCII + nonascii = support.FS_NONASCII env['spam'] = nonascii env[nonascii] = 'ham' + nonascii check(nonascii, nonascii) @@ -502,26 +501,26 @@ class CommonTest(GenericTest): # FS encoding is probably ASCII pass else: - with os_helper.temp_cwd(unicwd): + with support.temp_cwd(unicwd): for path in ('', 'fuu', 'f\xf9\xf9', '/fuu', 'U:\\'): self.assertIsInstance(abspath(path), str) def test_nonascii_abspath(self): - if (os_helper.TESTFN_UNDECODABLE + if (support.TESTFN_UNDECODABLE # Mac OS X denies the creation of a directory with an invalid # UTF-8 name. Windows allows creating a directory with an # arbitrary bytes name, but fails to enter this directory # (when the bytes name is used). and sys.platform not in ('win32', 'darwin')): - name = os_helper.TESTFN_UNDECODABLE - elif os_helper.TESTFN_NONASCII: - name = os_helper.TESTFN_NONASCII + name = support.TESTFN_UNDECODABLE + elif support.TESTFN_NONASCII: + name = support.TESTFN_NONASCII else: - self.skipTest("need os_helper.TESTFN_NONASCII") + self.skipTest("need support.TESTFN_NONASCII") with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - with os_helper.temp_cwd(name): + with support.temp_cwd(name): self.test_abspath() def test_join_errors(self): @@ -567,9 +566,9 @@ class CommonTest(GenericTest): class PathLikeTests(unittest.TestCase): def setUp(self): - self.file_name = os_helper.TESTFN.lower() - self.file_path = FakePath(os_helper.TESTFN) - self.addCleanup(os_helper.unlink, self.file_name) + self.file_name = support.TESTFN.lower() + self.file_path = FakePath(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) create_file(self.file_name, b"test_genericpath.PathLikeTests") def assertPathEqual(self, func): diff --git a/Lib/test/test_getopt.py b/Lib/test/test_getopt.py index 9261276eb..9275dc4c5 100644 --- a/Lib/test/test_getopt.py +++ b/Lib/test/test_getopt.py @@ -1,8 +1,7 @@ # test_getopt.py # David Goodger 2000-08-19 -from test.support import verbose, run_doctest -from test.support.os_helper import EnvironmentVarGuard +from test.support import verbose, run_doctest, EnvironmentVarGuard import unittest import getopt diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 13f7e4ac4..767bd3764 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,8 +4,8 @@ import shutil import sys import unittest -from test.support.os_helper import (TESTFN, skip_unless_symlink, - can_symlink, create_empty_file, change_cwd) +from test.support import (TESTFN, skip_unless_symlink, + can_symlink, create_empty_file, change_cwd) class GlobTests(unittest.TestCase): diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index de6f3180a..0b5113c20 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -11,7 +11,7 @@ import sys import unittest from subprocess import PIPE, Popen from test import support -from test.support import _4G, bigmemtest, os_helper +from test.support import _4G, bigmemtest from test.support.script_helper import assert_python_ok, assert_python_failure gzip = support.import_module('gzip') @@ -29,7 +29,7 @@ data2 = b"""/* zlibmodule.c -- gzip-compatible data compression */ """ -TEMPDIR = os.path.abspath(os_helper.TESTFN) + '-gzdir' +TEMPDIR = os.path.abspath(support.TESTFN) + '-gzdir' class UnseekableIO(io.BytesIO): @@ -44,13 +44,13 @@ class UnseekableIO(io.BytesIO): class BaseTest(unittest.TestCase): - filename = os_helper.TESTFN + filename = support.TESTFN def setUp(self): - os_helper.unlink(self.filename) + support.unlink(self.filename) def tearDown(self): - os_helper.unlink(self.filename) + support.unlink(self.filename) class TestGzip(BaseTest): @@ -286,7 +286,7 @@ class TestGzip(BaseTest): self.test_write() with gzip.GzipFile(self.filename, 'r') as f: self.assertEqual(f.myfileobj.mode, 'rb') - os_helper.unlink(self.filename) + support.unlink(self.filename) with gzip.GzipFile(self.filename, 'x') as f: self.assertEqual(f.myfileobj.mode, 'xb') @@ -487,7 +487,7 @@ class TestGzip(BaseTest): self.assertEqual(g.mode, gzip.READ) for mode in "wb", "ab", "xb": if "x" in mode: - os_helper.unlink(self.filename) + support.unlink(self.filename) with open(self.filename, mode) as f: with gzip.GzipFile(fileobj=f) as g: self.assertEqual(g.mode, gzip.WRITE) @@ -599,7 +599,7 @@ class TestOpen(BaseTest): with self.assertRaises(FileExistsError): gzip.open(self.filename, "xb") - os_helper.unlink(self.filename) + support.unlink(self.filename) with gzip.open(self.filename, "xb") as f: f.write(uncompressed) with open(self.filename, "rb") as f: @@ -636,7 +636,7 @@ class TestOpen(BaseTest): with self.assertRaises(FileExistsError): gzip.open(self.filename, "x") - os_helper.unlink(self.filename) + support.unlink(self.filename) with gzip.open(self.filename, "x") as f: f.write(uncompressed) with open(self.filename, "rb") as f: @@ -730,7 +730,7 @@ def create_and_remove_directory(directory): try: return function(*args, **kwargs) finally: - os_helper.rmtree(directory) + support.rmtree(directory) return wrapper return decorator diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py index eb8459b71..0363aef15 100644 --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -3,7 +3,6 @@ import os import re import test.support -from test.support import os_helper import time import unittest import urllib.request @@ -331,14 +330,14 @@ def _interact(cookiejar, url, set_cookie_hdrs, hdr_name): class FileCookieJarTests(unittest.TestCase): def test_constructor_with_str(self): - filename = os_helper.TESTFN + filename = test.support.TESTFN c = LWPCookieJar(filename) self.assertEqual(c.filename, filename) # TODO: RUSTPYTHON @unittest.expectedFailure def test_constructor_with_path_like(self): - filename = pathlib.Path(os_helper.TESTFN) + filename = pathlib.Path(test.support.TESTFN) c = LWPCookieJar(filename) self.assertEqual(c.filename, os.fspath(filename)) @@ -360,7 +359,7 @@ class FileCookieJarTests(unittest.TestCase): def test_lwp_valueless_cookie(self): # cookies with no value should be saved and loaded consistently - filename = os_helper.TESTFN + filename = test.support.TESTFN c = LWPCookieJar() interact_netscape(c, "http://www.acme.com/", 'boo') self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None) @@ -375,7 +374,7 @@ class FileCookieJarTests(unittest.TestCase): def test_bad_magic(self): # OSErrors (eg. file doesn't exist) are allowed to propagate - filename = os_helper.TESTFN + filename = test.support.TESTFN for cookiejar_class in LWPCookieJar, MozillaCookieJar: c = cookiejar_class() try: @@ -484,7 +483,7 @@ class CookieTests(unittest.TestCase): def test_missing_value(self): # missing = sign in Cookie: header is regarded by Mozilla as a missing # name, and by http.cookiejar as a missing value - filename = os_helper.TESTFN + filename = test.support.TESTFN c = MozillaCookieJar(filename) interact_netscape(c, "http://www.acme.com/", 'eggs') interact_netscape(c, "http://www.acme.com/", '"spam"; path=/foo/') @@ -1732,7 +1731,7 @@ class LWPCookieTests(unittest.TestCase): self.assertEqual(len(c), 6) # save and restore - filename = os_helper.TESTFN + filename = test.support.TESTFN try: c.save(filename, ignore_discard=True) @@ -1772,7 +1771,7 @@ class LWPCookieTests(unittest.TestCase): # Save / load Mozilla/Netscape cookie file format. year_plus_one = time.localtime()[0] + 1 - filename = os_helper.TESTFN + filename = test.support.TESTFN c = MozillaCookieJar(filename, policy=DefaultCookiePolicy(rfc2965=True)) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 6b13efe76..016bfb61b 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -13,7 +13,6 @@ import unittest TestCase = unittest.TestCase from test import support -from test.support import os_helper here = os.path.dirname(__file__) # Self-signed cert file for 'localhost' @@ -1917,10 +1916,10 @@ class RequestBodyTest(TestCase): self.assertEqual(b'body\xc1', f.read()) def test_text_file_body(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "w") as f: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, "w") as f: f.write("body") - with open(os_helper.TESTFN) as f: + with open(support.TESTFN) as f: self.conn.request("PUT", "/url", f) message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) @@ -1932,10 +1931,10 @@ class RequestBodyTest(TestCase): self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read()) def test_binary_file_body(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "wb") as f: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, "wb") as f: f.write(b"body\xc1") - with open(os_helper.TESTFN, "rb") as f: + with open(support.TESTFN, "rb") as f: self.conn.request("PUT", "/url", f) message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2a4587b9d..97692cd2b 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -29,7 +29,6 @@ from io import BytesIO import unittest from test import support -from test.support import os_helper class NoLogRequestHandler: @@ -65,7 +64,7 @@ class TestServerThread(threading.Thread): class BaseTestCase(unittest.TestCase): def setUp(self): self._threads = support.threading_setup() - os.environ = os_helper.EnvironmentVarGuard() + os.environ = support.EnvironmentVarGuard() self.server_started = threading.Event() self.thread = TestServerThread(self, self.request_handler) self.thread.start() @@ -392,13 +391,13 @@ class SimpleHTTPServerTestCase(BaseTestCase): 'undecodable name cannot always be decoded on macOS') @unittest.skipIf(sys.platform == 'win32', 'undecodable name cannot be decoded on win32') - @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE, - 'need os_helper.TESTFN_UNDECODABLE') + @unittest.skipUnless(support.TESTFN_UNDECODABLE, + 'need support.TESTFN_UNDECODABLE') def test_undecodable_filename(self): enc = sys.getfilesystemencoding() - filename = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.txt' + filename = os.fsdecode(support.TESTFN_UNDECODABLE) + '.txt' with open(os.path.join(self.tempdir, filename), 'wb') as f: - f.write(os_helper.TESTFN_UNDECODABLE) + f.write(support.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') if sys.platform == 'darwin': # On Mac OS the HFS+ filesystem replaces bytes that aren't valid @@ -415,7 +414,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): .encode(enc, 'surrogateescape'), body) response = self.request(self.base_url + '/' + quotedname) self.check_status_and_reason(response, HTTPStatus.OK, - data=os_helper.TESTFN_UNDECODABLE) + data=support.TESTFN_UNDECODABLE) def test_get(self): #constructs the path relative to the root directory of the HTTPServer @@ -617,7 +616,7 @@ class CGIHTTPServerTestCase(BaseTestCase): # The shebang line should be pure ASCII: use symlink if possible. # See issue #7668. self._pythonexe_symlink = None - if os_helper.can_symlink(): + if support.can_symlink(): self.pythonexe = os.path.join(self.parent_dir, 'python') self._pythonexe_symlink = support.PythonSymlink(self.pythonexe).__enter__() else: diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 78a658dfd..29365d615 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -5,7 +5,7 @@ import os.path import py_compile import sys from test import support -from test.support import script_helper, os_helper +from test.support import script_helper import unittest import warnings with warnings.catch_warnings(): @@ -114,8 +114,8 @@ class ImportTests(unittest.TestCase): self.assertEqual(file.encoding, 'cp1252') finally: del sys.path[0] - os_helper.unlink(temp_mod_name + '.py') - os_helper.unlink(temp_mod_name + '.pyc') + support.unlink(temp_mod_name + '.py') + support.unlink(temp_mod_name + '.pyc') def test_issue5604(self): # Test cannot cover imp.load_compiled function. @@ -199,10 +199,10 @@ class ImportTests(unittest.TestCase): finally: del sys.path[0] for ext in ('.py', '.pyc'): - os_helper.unlink(temp_mod_name + ext) - os_helper.unlink(init_file_name + ext) - os_helper.rmtree(test_package_name) - os_helper.rmtree('__pycache__') + support.unlink(temp_mod_name + ext) + support.unlink(init_file_name + ext) + support.rmtree(test_package_name) + support.rmtree('__pycache__') def test_issue9319(self): path = os.path.dirname(__file__) @@ -220,7 +220,7 @@ class ImportTests(unittest.TestCase): # workaround orig_path = os.path orig_getenv = os.getenv - with os_helper.EnvironmentVarGuard(): + with support.EnvironmentVarGuard(): x = imp.find_module("os") self.addCleanup(x[0].close) new_os = imp.load_module("os", *x) @@ -306,11 +306,11 @@ class ImportTests(unittest.TestCase): @unittest.skipIf(sys.dont_write_bytecode, "test meaningful only when writing bytecode") def test_bug7732(self): - with os_helper.temp_cwd(): - source = os_helper.TESTFN + '.py' + with support.temp_cwd(): + source = support.TESTFN + '.py' os.mkdir(source) self.assertRaisesRegex(ImportError, '^No module', - imp.find_module, os_helper.TESTFN, ["."]) + imp.find_module, support.TESTFN, ["."]) def test_multiple_calls_to_get_data(self): # Issue #18755: make sure multiple calls to get_data() can succeed. @@ -377,7 +377,7 @@ class ImportTests(unittest.TestCase): @unittest.expectedFailure def test_find_and_load_checked_pyc(self): # issue 34056 - with os_helper.temp_cwd(): + with support.temp_cwd(): with open('mymod.py', 'wb') as fp: fp.write(b'x = 42\n') py_compile.compile( @@ -403,7 +403,7 @@ class ReloadTests(unittest.TestCase): # state after reversion. Reinitialising the module contents # and just reverting os.environ to its previous state is an OK # workaround - with os_helper.EnvironmentVarGuard(): + with support.EnvironmentVarGuard(): import os imp.reload(os) @@ -456,10 +456,10 @@ class PEP3147Tests(unittest.TestCase): class NullImporterTests(unittest.TestCase): - @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, + @unittest.skipIf(support.TESTFN_UNENCODABLE is None, "Need an undecodeable filename") def test_unencodeable(self): - name = os_helper.TESTFN_UNENCODABLE + name = support.TESTFN_UNENCODABLE os.mkdir(name) try: self.assertRaises(ImportError, imp.NullImporter, name) diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py index 7f4b2b3d1..0dd9c8615 100644 --- a/Lib/test/test_importlib/extension/test_case_sensitivity.py +++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py @@ -1,6 +1,5 @@ from importlib import _bootstrap_external from test import support -from test.support import os_helper import unittest from .. import util @@ -23,14 +22,14 @@ class ExtensionModuleCaseSensitivityTest(util.CASEOKTestBase): return finder.find_module(bad_name) def test_case_sensitive(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.unset('PYTHONCASEOK') self.caseok_env_changed(should_exist=False) loader = self.find_module() self.assertIsNone(loader) def test_case_insensitivity(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.set('PYTHONCASEOK', '1') self.caseok_env_changed(should_exist=True) loader = self.find_module() diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py index f7e277390..12ce0cb93 100644 --- a/Lib/test/test_importlib/source/test_case_sensitivity.py +++ b/Lib/test/test_importlib/source/test_case_sensitivity.py @@ -1,15 +1,12 @@ """Test case-sensitivity (PEP 235).""" -import sys - from .. import util importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') import os -from test.support import os_helper +from test import support as test_support import unittest -import warnings @util.case_insensitive_tests @@ -41,9 +38,8 @@ class CaseSensitivityTest(util.CASEOKTestBase): insensitive_finder = self.finder(insensitive_path) return self.find(sensitive_finder), self.find(insensitive_finder) - @unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set') def test_sensitive(self): - with os_helper.EnvironmentVarGuard() as env: + with test_support.EnvironmentVarGuard() as env: env.unset('PYTHONCASEOK') self.caseok_env_changed(should_exist=False) sensitive, insensitive = self.sensitivity_test() @@ -51,9 +47,8 @@ class CaseSensitivityTest(util.CASEOKTestBase): self.assertIn(self.name, sensitive.get_filename(self.name)) self.assertIsNone(insensitive) - @unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set') def test_insensitive(self): - with os_helper.EnvironmentVarGuard() as env: + with test_support.EnvironmentVarGuard() as env: env.set('PYTHONCASEOK', '1') self.caseok_env_changed(should_exist=True) sensitive, insensitive = self.sensitivity_test() @@ -65,8 +60,6 @@ class CaseSensitivityTest(util.CASEOKTestBase): class CaseSensitivityTestPEP302(CaseSensitivityTest): def find(self, finder): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) return finder.find_module(self.name) diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 9e022f925..edb745c2c 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -7,7 +7,6 @@ machinery = test_util.import_importlib('importlib.machinery') import os.path import sys from test import support -from test.support import os_helper import types import unittest import warnings @@ -245,7 +244,7 @@ class ReloadTests: def test_reload_location_changed(self): name = 'spam' - with os_helper.temp_cwd(None) as cwd: + with support.temp_cwd(None) as cwd: with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a plain module. @@ -258,7 +257,7 @@ class ReloadTests: '__cached__': cached, '__doc__': None, } - os_helper.create_empty_file(path) + support.create_empty_file(path) module = self.init.import_module(name) ns = vars(module).copy() loader = ns.pop('__loader__') @@ -296,7 +295,7 @@ class ReloadTests: def test_reload_namespace_changed(self): name = 'spam' - with os_helper.temp_cwd(None) as cwd: + with support.temp_cwd(None) as cwd: with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a namespace package. diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index c1c161331..196ea1c9d 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -11,7 +11,6 @@ import os import os.path from pathlib import Path, PurePath from test import support -from test.support import os_helper import unittest import sys import tempfile @@ -148,7 +147,7 @@ def uncache(*names): @contextlib.contextmanager def temp_module(name, content='', *, pkg=False): conflicts = [n for n in sys.modules if n.partition('.')[0] == name] - with os_helper.temp_cwd(None) as cwd: + with support.temp_cwd(None) as cwd: with uncache(name, *conflicts): with support.DirsOnSysPath(cwd): invalidate_caches() @@ -386,7 +385,7 @@ def create_modules(*names): state_manager.__exit__(None, None, None) if uncache_manager is not None: uncache_manager.__exit__(None, None, None) - os_helper.rmtree(temp_dir) + support.rmtree(temp_dir) def mock_path_hook(*entries, importer): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index f72de9d65..02c5bb0fd 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -37,9 +37,8 @@ import weakref from collections import deque, UserList from itertools import cycle, count from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok, run_python_until_end -from test.support.os_helper import FakePath +from test.support import FakePath import codecs import io # C implementation of io @@ -315,10 +314,10 @@ class PyMockNonBlockWriterIO(MockNonBlockWriterIO, pyio.RawIOBase): class IOTest(unittest.TestCase): def setUp(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def tearDown(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def write_ops(self, f): self.assertEqual(f.write(b"blah."), 5) @@ -403,19 +402,19 @@ class IOTest(unittest.TestCase): # Try writing on a file opened in read mode and vice-versa. exc = self.UnsupportedOperation for mode in ("w", "wb"): - with self.open(os_helper.TESTFN, mode) as fp: + with self.open(support.TESTFN, mode) as fp: self.assertRaises(exc, fp.read) self.assertRaises(exc, fp.readline) - with self.open(os_helper.TESTFN, "wb", buffering=0) as fp: + with self.open(support.TESTFN, "wb", buffering=0) as fp: self.assertRaises(exc, fp.read) self.assertRaises(exc, fp.readline) - with self.open(os_helper.TESTFN, "rb", buffering=0) as fp: + with self.open(support.TESTFN, "rb", buffering=0) as fp: self.assertRaises(exc, fp.write, b"blah") self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(os_helper.TESTFN, "rb") as fp: + with self.open(support.TESTFN, "rb") as fp: self.assertRaises(exc, fp.write, b"blah") self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(os_helper.TESTFN, "r") as fp: + with self.open(support.TESTFN, "r") as fp: self.assertRaises(exc, fp.write, "blah") self.assertRaises(exc, fp.writelines, ["blah\n"]) # Non-zero seeking from current or end pos @@ -536,33 +535,33 @@ class IOTest(unittest.TestCase): self.assertRaises(ValueError, self.open, bytes_fn, 'w') def test_raw_file_io(self): - with self.open(os_helper.TESTFN, "wb", buffering=0) as f: + with self.open(support.TESTFN, "wb", buffering=0) as f: self.assertEqual(f.readable(), False) self.assertEqual(f.writable(), True) self.assertEqual(f.seekable(), True) self.write_ops(f) - with self.open(os_helper.TESTFN, "rb", buffering=0) as f: + with self.open(support.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.readable(), True) self.assertEqual(f.writable(), False) self.assertEqual(f.seekable(), True) self.read_ops(f) def test_buffered_file_io(self): - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: self.assertEqual(f.readable(), False) self.assertEqual(f.writable(), True) self.assertEqual(f.seekable(), True) self.write_ops(f) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.readable(), True) self.assertEqual(f.writable(), False) self.assertEqual(f.seekable(), True) self.read_ops(f, True) def test_readline(self): - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(b"abc\ndef\nxyzzy\nfoo\x00bar\nanother line") - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.readline(), b"abc\n") self.assertEqual(f.readline(10), b"def\n") self.assertEqual(f.readline(2), b"xy") @@ -570,7 +569,7 @@ class IOTest(unittest.TestCase): self.assertEqual(f.readline(), b"foo\x00bar\n") self.assertEqual(f.readline(None), b"another line") self.assertRaises(TypeError, f.readline, 5.3) - with self.open(os_helper.TESTFN, "r") as f: + with self.open(support.TESTFN, "r") as f: self.assertRaises(TypeError, f.readline, 5.3) def test_readline_nonsizeable(self): @@ -605,20 +604,20 @@ class IOTest(unittest.TestCase): support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) - with self.open(os_helper.TESTFN, "w+b", 0) as f: + with self.open(support.TESTFN, "w+b", 0) as f: self.large_file_ops(f) - with self.open(os_helper.TESTFN, "w+b") as f: + with self.open(support.TESTFN, "w+b") as f: self.large_file_ops(f) def test_with_open(self): for bufsize in (0, 100): f = None - with self.open(os_helper.TESTFN, "wb", bufsize) as f: + with self.open(support.TESTFN, "wb", bufsize) as f: f.write(b"xxx") self.assertEqual(f.closed, True) f = None try: - with self.open(os_helper.TESTFN, "wb", bufsize) as f: + with self.open(support.TESTFN, "wb", bufsize) as f: 1/0 except ZeroDivisionError: self.assertEqual(f.closed, True) @@ -627,13 +626,13 @@ class IOTest(unittest.TestCase): # issue 5008 def test_append_mode_tell(self): - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(b"xxx") - with self.open(os_helper.TESTFN, "ab", buffering=0) as f: + with self.open(support.TESTFN, "ab", buffering=0) as f: self.assertEqual(f.tell(), 3) - with self.open(os_helper.TESTFN, "ab") as f: + with self.open(support.TESTFN, "ab") as f: self.assertEqual(f.tell(), 3) - with self.open(os_helper.TESTFN, "a") as f: + with self.open(support.TESTFN, "a") as f: self.assertGreater(f.tell(), 0) def test_destructor(self): @@ -654,12 +653,12 @@ class IOTest(unittest.TestCase): record.append(3) super().flush() with support.check_warnings(('', ResourceWarning)): - f = MyFileIO(os_helper.TESTFN, "wb") + f = MyFileIO(support.TESTFN, "wb") f.write(b"xxx") del f support.gc_collect() self.assertEqual(record, [1, 2, 3]) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.read(), b"xxx") def _check_base_destructor(self, base): @@ -705,9 +704,9 @@ class IOTest(unittest.TestCase): self._check_base_destructor(self.TextIOBase) def test_close_flushes(self): - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(b"xxx") - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.read(), b"xxx") def test_array_writes(self): @@ -718,25 +717,25 @@ class IOTest(unittest.TestCase): self.assertEqual(f.write(a), n) f.writelines((a,)) check(self.BytesIO()) - check(self.FileIO(os_helper.TESTFN, "w")) + check(self.FileIO(support.TESTFN, "w")) check(self.BufferedWriter(self.MockRawIO())) check(self.BufferedRandom(self.MockRawIO())) check(self.BufferedRWPair(self.MockRawIO(), self.MockRawIO())) def test_closefd(self): - self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'w', + self.assertRaises(ValueError, self.open, support.TESTFN, 'w', closefd=False) def test_read_closed(self): - with self.open(os_helper.TESTFN, "w") as f: + with self.open(support.TESTFN, "w") as f: f.write("egg\n") - with self.open(os_helper.TESTFN, "r") as f: + with self.open(support.TESTFN, "r") as f: file = self.open(f.fileno(), "r", closefd=False) self.assertEqual(file.read(), "egg\n") file.seek(0) file.close() self.assertRaises(ValueError, file.read) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: file = self.open(f.fileno(), "rb", closefd=False) self.assertEqual(file.read()[:3], b"egg") file.close() @@ -744,12 +743,12 @@ class IOTest(unittest.TestCase): def test_no_closefd_with_filename(self): # can't use closefd in combination with a file name - self.assertRaises(ValueError, self.open, os_helper.TESTFN, "r", closefd=False) + self.assertRaises(ValueError, self.open, support.TESTFN, "r", closefd=False) def test_closefd_attr(self): - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(b"egg\n") - with self.open(os_helper.TESTFN, "r") as f: + with self.open(support.TESTFN, "r") as f: self.assertEqual(f.buffer.raw.closefd, True) file = self.open(f.fileno(), "r", closefd=False) self.assertEqual(file.buffer.raw.closefd, False) @@ -760,14 +759,14 @@ class IOTest(unittest.TestCase): # FileIO objects are collected, and collecting them flushes # all data to disk. with support.check_warnings(('', ResourceWarning)): - f = self.FileIO(os_helper.TESTFN, "wb") + f = self.FileIO(support.TESTFN, "wb") f.write(b"abcxxx") f.f = f wr = weakref.ref(f) del f support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.read(), b"abcxxx") def test_unbounded_file(self): @@ -804,29 +803,29 @@ class IOTest(unittest.TestCase): def test_flush_error_on_close(self): # raw file # Issue #5700: io.FileIO calls flush() after file closed - self.check_flush_error_on_close(os_helper.TESTFN, 'wb', buffering=0) - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(support.TESTFN, 'wb', buffering=0) + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', buffering=0) - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False) os.close(fd) # buffered io - self.check_flush_error_on_close(os_helper.TESTFN, 'wb') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(support.TESTFN, 'wb') + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'wb', closefd=False) os.close(fd) # text io - self.check_flush_error_on_close(os_helper.TESTFN, 'w') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(support.TESTFN, 'w') + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'w') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT) self.check_flush_error_on_close(fd, 'w', closefd=False) os.close(fd) def test_multi_close(self): - f = self.open(os_helper.TESTFN, "wb", buffering=0) + f = self.open(support.TESTFN, "wb", buffering=0) f.close() f.close() f.close() @@ -857,9 +856,9 @@ class IOTest(unittest.TestCase): self.assertTrue(hasattr(obj, "__dict__")) def test_opener(self): - with self.open(os_helper.TESTFN, "w") as f: + with self.open(support.TESTFN, "w") as f: f.write("egg\n") - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) def opener(path, flags): return fd with self.open("non-existent", "r", opener=opener) as f: @@ -896,12 +895,12 @@ class IOTest(unittest.TestCase): def test_nonbuffered_textio(self): with support.check_no_resource_warning(self): with self.assertRaises(ValueError): - self.open(os_helper.TESTFN, 'w', buffering=0) + self.open(support.TESTFN, 'w', buffering=0) def test_invalid_newline(self): with support.check_no_resource_warning(self): with self.assertRaises(ValueError): - self.open(os_helper.TESTFN, 'w', newline='invalid') + self.open(support.TESTFN, 'w', newline='invalid') def test_buffered_readinto_mixin(self): # Test the implementation provided by BufferedIOBase @@ -924,10 +923,10 @@ class IOTest(unittest.TestCase): with self.open(path, "r") as f: self.assertEqual(f.read(), "egg\n") - check_path_succeeds(FakePath(os_helper.TESTFN)) - check_path_succeeds(FakePath(os_helper.TESTFN.encode('utf-8'))) + check_path_succeeds(FakePath(support.TESTFN)) + check_path_succeeds(FakePath(support.TESTFN.encode('utf-8'))) - with self.open(os_helper.TESTFN, "w") as f: + with self.open(support.TESTFN, "w") as f: bad_path = FakePath(f.fileno()) with self.assertRaises(TypeError): self.open(bad_path, 'w') @@ -942,7 +941,7 @@ class IOTest(unittest.TestCase): # ensure that refcounting is correct with some error conditions with self.assertRaisesRegex(ValueError, 'read/write/append mode'): - self.open(FakePath(os_helper.TESTFN), 'rwxa') + self.open(FakePath(support.TESTFN), 'rwxa') def test_RawIOBase_readall(self): # Exercise the default unlimited RawIOBase.read() and readall() @@ -1463,9 +1462,9 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): l = list(range(256)) * N random.shuffle(l) s = bytes(bytearray(l)) - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(s) - with self.open(os_helper.TESTFN, self.read_mode, buffering=0) as raw: + with self.open(support.TESTFN, self.read_mode, buffering=0) as raw: bufio = self.tp(raw, 8) errors = [] results = [] @@ -1491,7 +1490,7 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): c = bytes(bytearray([i])) self.assertEqual(s.count(c), N) finally: - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_unseekable(self): bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) @@ -1577,9 +1576,9 @@ class CBufferedReaderTest(BufferedReaderTest, SizeofTest): def test_garbage_collection(self): # C BufferedReader objects are collected. # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) with support.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(os_helper.TESTFN, "w+b") + rawio = self.FileIO(support.TESTFN, "w+b") f = self.tp(rawio) f.f = f wr = weakref.ref(f) @@ -1789,26 +1788,26 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): def test_truncate(self): # Truncate implicitly flushes the buffer. - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: + self.addCleanup(support.unlink, support.TESTFN) + with self.open(support.TESTFN, self.write_mode, buffering=0) as raw: bufio = self.tp(raw, 8) bufio.write(b"abcdef") self.assertEqual(bufio.truncate(3), 3) self.assertEqual(bufio.tell(), 6) - with self.open(os_helper.TESTFN, "rb", buffering=0) as f: + with self.open(support.TESTFN, "rb", buffering=0) as f: self.assertEqual(f.read(), b"abc") def test_truncate_after_write(self): # Ensure that truncate preserves the file position after # writes longer than the buffer size. # Issue: https://bugs.python.org/issue32228 - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with self.open(os_helper.TESTFN, "wb") as f: + self.addCleanup(support.unlink, support.TESTFN) + with self.open(support.TESTFN, "wb") as f: # Fill with some buffer f.write(b'\x00' * 10000) buffer_sizes = [8192, 4096, 200] for buffer_size in buffer_sizes: - with self.open(os_helper.TESTFN, "r+b", buffering=buffer_size) as f: + with self.open(support.TESTFN, "r+b", buffering=buffer_size) as f: f.write(b'\x00' * (buffer_size + 1)) # After write write_pos and write_end are set to 0 f.read(1) @@ -1836,7 +1835,7 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): # writing the buffer to the raw streams. This is in addition # to concurrency issues due to switching threads in the middle # of Python code. - with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: + with self.open(support.TESTFN, self.write_mode, buffering=0) as raw: bufio = self.tp(raw, 8) errors = [] def f(): @@ -1856,12 +1855,12 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): self.assertFalse(errors, "the following exceptions were caught: %r" % errors) bufio.close() - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: s = f.read() for i in range(256): self.assertEqual(s.count(bytes([i])), N) finally: - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_misbehaved_io(self): rawio = self.MisbehavedRawIO() @@ -1932,9 +1931,9 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest): # C BufferedWriter objects are collected, and collecting them flushes # all data to disk. # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) with support.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(os_helper.TESTFN, "w+b") + rawio = self.FileIO(support.TESTFN, "w+b") f = self.tp(rawio) f.write(b"123xxx") f.x = f @@ -1942,7 +1941,7 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest): del f support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.read(), b"123xxx") # TODO: RUSTPYTHON @@ -2595,10 +2594,10 @@ class TextIOWrapperTest(unittest.TestCase): def setUp(self): self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def tearDown(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_constructor(self): r = self.BytesIO(b"\xc3\xa9\n\n") @@ -2936,11 +2935,11 @@ class TextIOWrapperTest(unittest.TestCase): def test_basic_io(self): for chunksize in (1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65): for enc in "ascii", "latin-1", "utf-8" :# , "utf-16-be", "utf-16-le": - f = self.open(os_helper.TESTFN, "w+", encoding=enc) + f = self.open(support.TESTFN, "w+", encoding=enc) f._CHUNK_SIZE = chunksize self.assertEqual(f.write("abc"), 3) f.close() - f = self.open(os_helper.TESTFN, "r+", encoding=enc) + f = self.open(support.TESTFN, "r+", encoding=enc) f._CHUNK_SIZE = chunksize self.assertEqual(f.tell(), 0) self.assertEqual(f.read(), "abc") @@ -2985,7 +2984,7 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(rlines, wlines) def test_telling(self): - f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") + f = self.open(support.TESTFN, "w+", encoding="utf-8") p0 = f.tell() f.write("\xff\n") p1 = f.tell() @@ -3013,9 +3012,9 @@ class TextIOWrapperTest(unittest.TestCase): u_suffix = "\u8888\n" suffix = bytes(u_suffix.encode("utf-8")) line = prefix + suffix - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(line*2) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + with self.open(support.TESTFN, "r", encoding="utf-8") as f: s = f.read(prefix_size) self.assertEqual(s, str(prefix, "ascii")) self.assertEqual(f.tell(), prefix_size) @@ -3024,9 +3023,9 @@ class TextIOWrapperTest(unittest.TestCase): def test_seeking_too(self): # Regression test for a specific bug data = b'\xe0\xbf\xbf\n' - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: f.write(data) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + with self.open(support.TESTFN, "r", encoding="utf-8") as f: f._CHUNK_SIZE # Just test that it exists f._CHUNK_SIZE = 2 f.readline() @@ -3040,17 +3039,17 @@ class TextIOWrapperTest(unittest.TestCase): def test_seek_and_tell_with_data(data, min_pos=0): """Tell/seek to various points within a data stream and ensure that the decoded data returned by read() is consistent.""" - f = self.open(os_helper.TESTFN, 'wb') + f = self.open(support.TESTFN, 'wb') f.write(data) f.close() - f = self.open(os_helper.TESTFN, encoding='test_decoder') + f = self.open(support.TESTFN, encoding='test_decoder') f._CHUNK_SIZE = CHUNK_SIZE decoded = f.read() f.close() for i in range(min_pos, len(decoded) + 1): # seek positions for j in [1, 5, len(decoded) - i]: # read lengths - f = self.open(os_helper.TESTFN, encoding='test_decoder') + f = self.open(support.TESTFN, encoding='test_decoder') self.assertEqual(f.read(i), decoded[:i]) cookie = f.tell() self.assertEqual(f.read(j), decoded[i:i + j]) @@ -3082,11 +3081,11 @@ class TextIOWrapperTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_multibyte_seek_and_tell(self): - f = self.open(os_helper.TESTFN, "w", encoding="euc_jp") + f = self.open(support.TESTFN, "w", encoding="euc_jp") f.write("AB\n\u3046\u3048\n") f.close() - f = self.open(os_helper.TESTFN, "r", encoding="euc_jp") + f = self.open(support.TESTFN, "r", encoding="euc_jp") self.assertEqual(f.readline(), "AB\n") p0 = f.tell() self.assertEqual(f.readline(), "\u3046\u3048\n") @@ -3099,7 +3098,7 @@ class TextIOWrapperTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_seek_with_encoder_state(self): - f = self.open(os_helper.TESTFN, "w", encoding="euc_jis_2004") + f = self.open(support.TESTFN, "w", encoding="euc_jis_2004") f.write("\u00e6\u0300") p0 = f.tell() f.write("\u00e6") @@ -3107,7 +3106,7 @@ class TextIOWrapperTest(unittest.TestCase): f.write("\u0300") f.close() - f = self.open(os_helper.TESTFN, "r", encoding="euc_jis_2004") + f = self.open(support.TESTFN, "r", encoding="euc_jis_2004") self.assertEqual(f.readline(), "\u00e6\u0300\u0300") f.close() @@ -3255,7 +3254,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_append_bom(self): # The BOM is not written again when appending to a non-empty file - filename = os_helper.TESTFN + filename = support.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3272,7 +3271,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_seek_bom(self): # Same test, but when seeking manually - filename = os_helper.TESTFN + filename = support.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3289,7 +3288,7 @@ class TextIOWrapperTest(unittest.TestCase): @unittest.expectedFailure def test_seek_append_bom(self): # Same test, but first seek to the start and then to the end - filename = os_helper.TESTFN + filename = support.TESTFN for charset in ('utf-8-sig', 'utf-16', 'utf-32'): with self.open(filename, 'w', encoding=charset) as f: f.write('aaa') @@ -3301,16 +3300,16 @@ class TextIOWrapperTest(unittest.TestCase): self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) def test_errors_property(self): - with self.open(os_helper.TESTFN, "w") as f: + with self.open(support.TESTFN, "w") as f: self.assertEqual(f.errors, "strict") - with self.open(os_helper.TESTFN, "w", errors="replace") as f: + with self.open(support.TESTFN, "w", errors="replace") as f: self.assertEqual(f.errors, "replace") @support.no_tracing def test_threads_write(self): # Issue6750: concurrent writes could duplicate data event = threading.Event() - with self.open(os_helper.TESTFN, "w", buffering=1) as f: + with self.open(support.TESTFN, "w", buffering=1) as f: def run(n): text = "Thread%03d\n" % n event.wait() @@ -3319,7 +3318,7 @@ class TextIOWrapperTest(unittest.TestCase): for x in range(20)] with support.start_threads(threads, event.set): time.sleep(0.02) - with self.open(os_helper.TESTFN) as f: + with self.open(support.TESTFN) as f: content = f.read() for n in range(20): self.assertEqual(content.count("Thread%03d\n" % n), 1) @@ -3893,7 +3892,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): # all data to disk. # The Python version has __del__, so it ends in gc.garbage instead. with support.check_warnings(('', ResourceWarning)): - rawio = io.FileIO(os_helper.TESTFN, "wb") + rawio = io.FileIO(support.TESTFN, "wb") b = self.BufferedWriter(rawio) t = self.TextIOWrapper(b, encoding="ascii") t.write("456def") @@ -3902,7 +3901,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): del t support.gc_collect() self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: + with self.open(support.TESTFN, "rb") as f: self.assertEqual(f.read(), b"456def") def test_rwpair_cleared_before_textio(self): @@ -4070,7 +4069,7 @@ class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest): class MiscIOTest(unittest.TestCase): def tearDown(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test___all__(self): for name in self.io.__all__: @@ -4084,23 +4083,23 @@ class MiscIOTest(unittest.TestCase): self.assertTrue(issubclass(obj, self.IOBase)) def test_attributes(self): - f = self.open(os_helper.TESTFN, "wb", buffering=0) + f = self.open(support.TESTFN, "wb", buffering=0) self.assertEqual(f.mode, "wb") f.close() # XXX RUSTPYTHON: universal mode is deprecated anyway, so I # feel fine about skipping it # with support.check_warnings(('', DeprecationWarning)): - # f = self.open(os_helper.TESTFN, "U") - # self.assertEqual(f.name, os_helper.TESTFN) - # self.assertEqual(f.buffer.name, os_helper.TESTFN) - # self.assertEqual(f.buffer.raw.name, os_helper.TESTFN) + # f = self.open(support.TESTFN, "U") + # self.assertEqual(f.name, support.TESTFN) + # self.assertEqual(f.buffer.name, support.TESTFN) + # self.assertEqual(f.buffer.raw.name, support.TESTFN) # self.assertEqual(f.mode, "U") # self.assertEqual(f.buffer.mode, "rb") # self.assertEqual(f.buffer.raw.mode, "rb") # f.close() - f = self.open(os_helper.TESTFN, "w+") + f = self.open(support.TESTFN, "w+") self.assertEqual(f.mode, "w+") self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? self.assertEqual(f.buffer.raw.mode, "rb+") @@ -4142,7 +4141,7 @@ class MiscIOTest(unittest.TestCase): {"mode": "w+", "buffering": 2}, {"mode": "w+b", "buffering": 0}, ]: - f = self.open(os_helper.TESTFN, **kwargs) + f = self.open(support.TESTFN, **kwargs) f.close() self.assertRaises(ValueError, f.flush) self.assertRaises(ValueError, f.fileno) @@ -4194,17 +4193,17 @@ class MiscIOTest(unittest.TestCase): self.assertIsInstance(self.TextIOBase, abc.ABCMeta) def _check_abc_inheritance(self, abcmodule): - with self.open(os_helper.TESTFN, "wb", buffering=0) as f: + with self.open(support.TESTFN, "wb", buffering=0) as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertIsInstance(f, abcmodule.RawIOBase) self.assertNotIsInstance(f, abcmodule.BufferedIOBase) self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(os_helper.TESTFN, "wb") as f: + with self.open(support.TESTFN, "wb") as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertNotIsInstance(f, abcmodule.RawIOBase) self.assertIsInstance(f, abcmodule.BufferedIOBase) self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(os_helper.TESTFN, "w") as f: + with self.open(support.TESTFN, "w") as f: self.assertIsInstance(f, abcmodule.IOBase) self.assertNotIsInstance(f, abcmodule.RawIOBase) self.assertNotIsInstance(f, abcmodule.BufferedIOBase) @@ -4230,9 +4229,9 @@ class MiscIOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_warn_on_dealloc(self): - self._check_warn_on_dealloc(os_helper.TESTFN, "wb", buffering=0) - self._check_warn_on_dealloc(os_helper.TESTFN, "wb") - self._check_warn_on_dealloc(os_helper.TESTFN, "w") + self._check_warn_on_dealloc(support.TESTFN, "wb", buffering=0) + self._check_warn_on_dealloc(support.TESTFN, "wb") + self._check_warn_on_dealloc(support.TESTFN, "w") def _check_warn_on_dealloc_fd(self, *args, **kwargs): fds = [] @@ -4275,7 +4274,7 @@ class MiscIOTest(unittest.TestCase): {"mode": "w+b", "buffering": 0}, ]: for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - with self.open(os_helper.TESTFN, **kwargs) as f: + with self.open(support.TESTFN, **kwargs) as f: self.assertRaises(TypeError, pickle.dumps, f, protocol) # TODO: RUSTPYTHON @@ -4342,20 +4341,20 @@ class MiscIOTest(unittest.TestCase): def test_create_fail(self): # 'x' mode fails if file is existing - with self.open(os_helper.TESTFN, 'w'): + with self.open(support.TESTFN, 'w'): pass - self.assertRaises(FileExistsError, self.open, os_helper.TESTFN, 'x') + self.assertRaises(FileExistsError, self.open, support.TESTFN, 'x') def test_create_writes(self): # 'x' mode opens for writing - with self.open(os_helper.TESTFN, 'xb') as f: + with self.open(support.TESTFN, 'xb') as f: f.write(b"spam") - with self.open(os_helper.TESTFN, 'rb') as f: + with self.open(support.TESTFN, 'rb') as f: self.assertEqual(b"spam", f.read()) def test_open_allargs(self): # there used to be a buffer overflow in the parser for rawmode - self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'rwax+') + self.assertRaises(ValueError, self.open, support.TESTFN, 'rwax+') class CMiscIOTest(MiscIOTest): diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index 065cedc85..748ad5e93 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -2,9 +2,8 @@ import sys import unittest -from test.support import run_unittest, cpython_only +from test.support import run_unittest, TESTFN, unlink, cpython_only # from test.support import check_free_after_iterating -from test.support.os_helper import TESTFN, unlink import pickle import collections.abc diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 9f2329ad2..f362f1b13 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -4,7 +4,6 @@ import textwrap import unittest from subprocess import Popen, PIPE from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok @@ -91,7 +90,7 @@ class TestTool(unittest.TestCase): self.assertEqual(err, b'') def _create_infile(self, data=None): - infile = os_helper.TESTFN + infile = support.TESTFN with open(infile, "w", encoding="utf-8") as fp: self.addCleanup(os.remove, infile) fp.write(data or self.data) @@ -121,7 +120,7 @@ class TestTool(unittest.TestCase): def test_infile_outfile(self): infile = self._create_infile() - outfile = os_helper.TESTFN + '.out' + outfile = support.TESTFN + '.out' rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile) self.addCleanup(os.remove, outfile) with open(outfile, "r") as fp: diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index 9232e8a8e..8b344ff1d 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -6,7 +6,6 @@ import os.path import tempfile import tokenize from test import support -from test.support import os_helper FILENAME = linecache.__file__ @@ -45,7 +44,7 @@ class TempFile: with tempfile.NamedTemporaryFile(delete=False) as fp: self.file_name = fp.name fp.write(self.file_byte_string) - self.addCleanup(os_helper.unlink, self.file_name) + self.addCleanup(support.unlink, self.file_name) class GetLineTestsGoodData(TempFile): @@ -125,10 +124,10 @@ class LineCacheTests(unittest.TestCase): self.assertEqual(empty, []) def test_no_ending_newline(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "w") as fp: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, "w") as fp: fp.write(SOURCE_3) - lines = linecache.getlines(os_helper.TESTFN) + lines = linecache.getlines(support.TESTFN) self.assertEqual(lines, ["\n", "def f():\n", " return 3\n"]) def test_clearcache(self): @@ -151,8 +150,8 @@ class LineCacheTests(unittest.TestCase): def test_checkcache(self): getline = linecache.getline # Create a source file and cache its contents - source_name = os_helper.TESTFN + '.py' - self.addCleanup(os_helper.unlink, source_name) + source_name = support.TESTFN + '.py' + self.addCleanup(support.unlink, source_name) with open(source_name, 'w') as source: source.write(SOURCE_1) getline(source_name, 1) diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 23092ffd0..b625915ba 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -62,7 +62,7 @@ class MimeTypesTestCase(unittest.TestCase): # Unreadable file returns None self.assertIsNone(mimetypes.read_mime_types("non-existent")) - with os_helper.temp_dir() as directory: + with support.temp_dir() as directory: data = "x-application/x-unittest pyunit\n" file = pathlib.Path(directory, "sample.mimetype") file.write_text(data) @@ -72,7 +72,7 @@ class MimeTypesTestCase(unittest.TestCase): # bpo-41048: read_mime_types should read the rule file with 'utf-8' encoding. # Not with locale encoding. _bootlocale has been imported because io.open(...) # uses it. - with os_helper.temp_dir() as directory: + with support.temp_dir() as directory: data = "application/no-mans-land Fran\u00E7ais" file = pathlib.Path(directory, "sample.mimetype") file.write_text(data, encoding='utf-8') diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index 27091c589..eadd69af9 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -1,6 +1,5 @@ import netrc, os, unittest, sys, tempfile, textwrap from test import support -from test.support import os_helper class NetrcTestCase(unittest.TestCase): @@ -109,16 +108,16 @@ class NetrcTestCase(unittest.TestCase): def test_security(self): # This test is incomplete since we are normally not run as root and # therefore can't test the file ownership being wrong. - d = os_helper.TESTFN + d = support.TESTFN os.mkdir(d) - self.addCleanup(os_helper.rmtree, d) + self.addCleanup(support.rmtree, d) fn = os.path.join(d, '.netrc') with open(fn, 'wt') as f: f.write("""\ machine foo.domain.com login bar password pass default login foo password pass """) - with os_helper.EnvironmentVarGuard() as environ: + with support.EnvironmentVarGuard() as environ: environ.set('HOME', d) os.chmod(fn, 0o600) nrc = netrc.netrc() @@ -128,10 +127,10 @@ class NetrcTestCase(unittest.TestCase): self.assertRaises(netrc.NetrcParseError, netrc.netrc) def test_file_not_found_in_home(self): - d = os_helper.TESTFN + d = support.TESTFN os.mkdir(d) - self.addCleanup(os_helper.rmtree, d) - with os_helper.EnvironmentVarGuard() as environ: + self.addCleanup(support.rmtree, d) + with support.EnvironmentVarGuard() as environ: environ.set('HOME', d) self.assertRaises(FileNotFoundError, netrc.netrc) @@ -141,9 +140,9 @@ class NetrcTestCase(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, permission error from remnant directory") def test_home_not_set(self): - fake_home = os_helper.TESTFN + fake_home = support.TESTFN os.mkdir(fake_home) - self.addCleanup(os_helper.rmtree, fake_home) + self.addCleanup(support.rmtree, fake_home) fake_netrc_path = os.path.join(fake_home, '.netrc') with open(fake_netrc_path, 'w') as f: f.write('machine foo.domain.com login bar password pass') @@ -154,7 +153,7 @@ class NetrcTestCase(unittest.TestCase): def fake_expanduser(s): called.append(s) - with os_helper.EnvironmentVarGuard() as environ: + with support.EnvironmentVarGuard() as environ: environ.set('HOME', fake_home) environ.set('USERPROFILE', fake_home) result = orig_expanduser(s) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index d26354fbb..ae3856f3e 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -3,9 +3,7 @@ import os import sys import unittest import warnings -from test.support import TestFailed -from test.support import os_helper -from test.support.os_helper import FakePath +from test.support import TestFailed, FakePath from test import support, test_genericpath from tempfile import TemporaryFile @@ -256,40 +254,40 @@ class TestNtpath(NtpathTestCase): tester("ntpath.realpath('\\'.join(['..'] * 50))", ntpath.splitdrive(expected)[0] + '\\') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_basic(self): - ABSTFN = ntpath.abspath(os_helper.TESTFN) + ABSTFN = ntpath.abspath(support.TESTFN) open(ABSTFN, "wb").close() - self.addCleanup(os_helper.unlink, ABSTFN) - self.addCleanup(os_helper.unlink, ABSTFN + "1") + self.addCleanup(support.unlink, ABSTFN) + self.addCleanup(support.unlink, ABSTFN + "1") os.symlink(ABSTFN, ABSTFN + "1") self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")), os.fsencode(ABSTFN)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_relative(self): - ABSTFN = ntpath.abspath(os_helper.TESTFN) + ABSTFN = ntpath.abspath(support.TESTFN) open(ABSTFN, "wb").close() - self.addCleanup(os_helper.unlink, ABSTFN) - self.addCleanup(os_helper.unlink, ABSTFN + "1") + self.addCleanup(support.unlink, ABSTFN) + self.addCleanup(support.unlink, ABSTFN + "1") os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1")) self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) # TODO: RUSTPYTHON @unittest.expectedFailure - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_broken_symlinks(self): - ABSTFN = ntpath.abspath(os_helper.TESTFN) + ABSTFN = ntpath.abspath(support.TESTFN) os.mkdir(ABSTFN) - self.addCleanup(os_helper.rmtree, ABSTFN) + self.addCleanup(support.rmtree, ABSTFN) - with os_helper.change_cwd(ABSTFN): + with support.change_cwd(ABSTFN): os.mkdir("subdir") os.chdir("subdir") os.symlink(".", "recursive") @@ -340,18 +338,18 @@ class TestNtpath(NtpathTestCase): os.fsencode(ABSTFN + r"\missing")) @unittest.skip("TODO: RUSTPYTHON, leaves behind TESTFN") - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_loops(self): # Symlink loops are non-deterministic as to which path is returned, but # it will always be the fully resolved path of one member of the cycle - ABSTFN = ntpath.abspath(os_helper.TESTFN) - self.addCleanup(os_helper.unlink, ABSTFN) - self.addCleanup(os_helper.unlink, ABSTFN + "1") - self.addCleanup(os_helper.unlink, ABSTFN + "2") - self.addCleanup(os_helper.unlink, ABSTFN + "y") - self.addCleanup(os_helper.unlink, ABSTFN + "c") - self.addCleanup(os_helper.unlink, ABSTFN + "a") + ABSTFN = ntpath.abspath(support.TESTFN) + self.addCleanup(support.unlink, ABSTFN) + self.addCleanup(support.unlink, ABSTFN + "1") + self.addCleanup(support.unlink, ABSTFN + "2") + self.addCleanup(support.unlink, ABSTFN + "y") + self.addCleanup(support.unlink, ABSTFN + "c") + self.addCleanup(support.unlink, ABSTFN + "a") os.symlink(ABSTFN, ABSTFN) self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN) @@ -386,14 +384,14 @@ class TestNtpath(NtpathTestCase): # Test using relative path as well. self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_prefix(self): - ABSTFN = ntpath.abspath(os_helper.TESTFN) - self.addCleanup(os_helper.unlink, ABSTFN + "3") - self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.") - self.addCleanup(os_helper.unlink, ABSTFN + "3link") - self.addCleanup(os_helper.unlink, ABSTFN + "3.link") + ABSTFN = ntpath.abspath(support.TESTFN) + self.addCleanup(support.unlink, ABSTFN + "3") + self.addCleanup(support.unlink, "\\\\?\\" + ABSTFN + "3.") + self.addCleanup(support.unlink, ABSTFN + "3link") + self.addCleanup(support.unlink, ABSTFN + "3.link") with open(ABSTFN + "3", "wb") as f: f.write(b'0') @@ -427,12 +425,12 @@ class TestNtpath(NtpathTestCase): @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname') def test_realpath_cwd(self): - ABSTFN = ntpath.abspath(os_helper.TESTFN) + ABSTFN = ntpath.abspath(support.TESTFN) - os_helper.unlink(ABSTFN) - os_helper.rmtree(ABSTFN) + support.unlink(ABSTFN) + support.rmtree(ABSTFN) os.mkdir(ABSTFN) - self.addCleanup(os_helper.rmtree, ABSTFN) + self.addCleanup(support.rmtree, ABSTFN) test_dir_long = ntpath.join(ABSTFN, "MyVeryLongDirectoryName") os.mkdir(test_dir_long) @@ -446,15 +444,15 @@ class TestNtpath(NtpathTestCase): self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short)) - with os_helper.change_cwd(test_dir_long): + with support.change_cwd(test_dir_long): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) - with os_helper.change_cwd(test_dir_long.lower()): + with support.change_cwd(test_dir_long.lower()): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) - with os_helper.change_cwd(test_dir_short): + with support.change_cwd(test_dir_short): self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) def test_expandvars(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.clear() env["foo"] = "bar" env["{foo"] = "baz1" @@ -479,13 +477,13 @@ class TestNtpath(NtpathTestCase): tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar") tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%") - @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') + @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') def test_expandvars_nonascii(self): def check(value, expected): tester('ntpath.expandvars(%r)' % value, expected) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.clear() - nonascii = os_helper.FS_NONASCII + nonascii = support.FS_NONASCII env['spam'] = nonascii env[nonascii] = 'ham' + nonascii check('$spam bar', '%s bar' % nonascii) @@ -502,7 +500,7 @@ class TestNtpath(NtpathTestCase): def test_expanduser(self): tester('ntpath.expanduser("test")', 'test') - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.clear() tester('ntpath.expanduser("~test")', '~test') @@ -538,7 +536,7 @@ class TestNtpath(NtpathTestCase): @unittest.skipUnless(nt, "abspath requires 'nt' module") def test_abspath(self): tester('ntpath.abspath("C:\\")', "C:\\") - with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: # bpo-31047 + with support.temp_cwd(support.TESTFN) as cwd_dir: # bpo-31047 tester('ntpath.abspath("")', cwd_dir) tester('ntpath.abspath(" ")', cwd_dir + "\\ ") tester('ntpath.abspath("?")', cwd_dir + "\\?") @@ -550,7 +548,7 @@ class TestNtpath(NtpathTestCase): tester('ntpath.relpath(ntpath.abspath("a"))', 'a') tester('ntpath.relpath("a/b")', 'a\\b') tester('ntpath.relpath("../a/b")', '..\\a\\b') - with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: + with support.temp_cwd(support.TESTFN) as cwd_dir: currentdir = ntpath.basename(cwd_dir) tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a') tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b') @@ -670,7 +668,7 @@ class TestNtpath(NtpathTestCase): self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\")) self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\")) - with os_helper.temp_dir() as d: + with support.temp_dir() as d: self.assertFalse(ntpath.ismount(d)) if sys.platform == "win32": @@ -680,7 +678,7 @@ class TestNtpath(NtpathTestCase): # locations below cannot then refer to mount points # drive, path = ntpath.splitdrive(sys.executable) - with os_helper.change_cwd(ntpath.dirname(sys.executable)): + with support.change_cwd(ntpath.dirname(sys.executable)): self.assertFalse(ntpath.ismount(drive.lower())) self.assertFalse(ntpath.ismount(drive.upper())) @@ -767,9 +765,9 @@ class PathLikeTests(NtpathTestCase): path = ntpath def setUp(self): - self.file_name = os_helper.TESTFN.lower() - self.file_path = FakePath(os_helper.TESTFN) - self.addCleanup(os_helper.unlink, self.file_name) + self.file_name = support.TESTFN.lower() + self.file_path = FakePath(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) with open(self.file_name, 'xb', 0) as file: file.write(b"test_ntpath.PathLikeTests") diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 1ed6bf9f9..437fdd2be 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -14,7 +14,6 @@ import unittest from io import StringIO from test import support -from test.support import os_helper import optparse @@ -1022,10 +1021,10 @@ class TestExtendAddTypes(BaseTest): self.parser.add_option("-f", "--file", type="file", dest="file") def tearDown(self): - if os.path.isdir(os_helper.TESTFN): - os.rmdir(os_helper.TESTFN) - elif os.path.isfile(os_helper.TESTFN): - os.unlink(os_helper.TESTFN) + if os.path.isdir(support.TESTFN): + os.rmdir(support.TESTFN) + elif os.path.isfile(support.TESTFN): + os.unlink(support.TESTFN) class MyOption (Option): def check_file(option, opt, value): @@ -1040,21 +1039,21 @@ class TestExtendAddTypes(BaseTest): TYPE_CHECKER["file"] = check_file def test_filetype_ok(self): - os_helper.create_empty_file(os_helper.TESTFN) - self.assertParseOK(["--file", os_helper.TESTFN, "-afoo"], - {'file': os_helper.TESTFN, 'a': 'foo'}, + support.create_empty_file(support.TESTFN) + self.assertParseOK(["--file", support.TESTFN, "-afoo"], + {'file': support.TESTFN, 'a': 'foo'}, []) def test_filetype_noexist(self): - self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"], + self.assertParseFail(["--file", support.TESTFN, "-afoo"], "%s: file does not exist" % - os_helper.TESTFN) + support.TESTFN) def test_filetype_notfile(self): - os.mkdir(os_helper.TESTFN) - self.assertParseFail(["--file", os_helper.TESTFN, "-afoo"], + os.mkdir(support.TESTFN) + self.assertParseFail(["--file", support.TESTFN, "-afoo"], "%s: not a regular file" % - os_helper.TESTFN) + support.TESTFN) class TestExtendAddActions(BaseTest): @@ -1498,7 +1497,7 @@ class TestHelp(BaseTest): # we must restore its original value -- otherwise, this test # screws things up for other tests when it's part of the Python # test suite. - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['COLUMNS'] = str(columns) return InterceptingOptionParser(option_list=options) @@ -1523,7 +1522,7 @@ class TestHelp(BaseTest): self.assertHelpEquals(_expected_help_long_opts_first) def test_help_title_formatter(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env["COLUMNS"] = "80" self.parser.formatter = TitledHelpFormatter() self.assertHelpEquals(_expected_help_title_formatter) @@ -1652,8 +1651,8 @@ class TestParseNumber(BaseTest): class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} - support.check__all__(self, optparse, not_exported=not_exported) + blacklist = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} + support.check__all__(self, optparse, blacklist=blacklist) def test_main(): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 2018ef179..a58abef6e 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2,6 +2,8 @@ # does add tests for a few functions which have been determined to be more # portable than they had been thought to be. +import asynchat +import asyncore import codecs import contextlib import decimal @@ -10,41 +12,25 @@ import fnmatch import fractions import itertools import locale -# TODO: RUSTPYTHON # import mmap import os import pickle -import select import shutil import signal import socket import stat -import struct import subprocess import sys import sysconfig import tempfile import threading import time -import types import unittest import uuid import warnings from test import support -# TODO: RUSTPYTHON -# from test.support import import_helper -from test.support import os_helper -# TODO: RUSTPYTHON -# from test.support import socket_helper -# from test.support import threading_helper -# from test.support import warnings_helper from platform import win32_is_iot -with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - import asynchat - import asyncore - try: import resource except ImportError: @@ -67,10 +53,8 @@ try: except ImportError: INT_MAX = PY_SSIZE_T_MAX = sys.maxsize - from test.support.script_helper import assert_python_ok -from test.support import unix_shell -from test.support.os_helper import FakePath +from test.support import unix_shell, FakePath root_in_posix = False @@ -99,11 +83,6 @@ def create_file(filename, content=b'content'): fp.write(content) -# bpo-41625: On AIX, splice() only works with a socket, not with a pipe. -requires_splice_pipe = unittest.skipIf(sys.platform.startswith("aix"), - 'on AIX, splice() only accepts sockets') - - class MiscTests(unittest.TestCase): def test_getcwd(self): cwd = os.getcwd() @@ -122,16 +101,12 @@ class MiscTests(unittest.TestCase): # than MAX_PATH if long paths support is disabled: # see RtlAreLongPathsEnabled(). min_len = 2000 # characters - # On VxWorks, PATH_MAX is defined as 1024 bytes. Creating a path - # longer than PATH_MAX will fail. - if sys.platform == 'vxworks': - min_len = 1000 dirlen = 200 # characters dirname = 'python_test_dir_' dirname = dirname + ('a' * (dirlen - len(dirname))) with tempfile.TemporaryDirectory() as tmpdir: - with os_helper.change_cwd(tmpdir) as path: + with support.change_cwd(tmpdir) as path: expected = path while True: @@ -175,17 +150,17 @@ class MiscTests(unittest.TestCase): # Tests creating TESTFN class FileTests(unittest.TestCase): def setUp(self): - if os.path.lexists(os_helper.TESTFN): - os.unlink(os_helper.TESTFN) + if os.path.lexists(support.TESTFN): + os.unlink(support.TESTFN) tearDown = setUp def test_access(self): - f = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) + f = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) os.close(f) - self.assertTrue(os.access(os_helper.TESTFN, os.W_OK)) + self.assertTrue(os.access(support.TESTFN, os.W_OK)) def test_closerange(self): - first = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) + first = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) # We must allocate two consecutive file descriptors, otherwise # it will mess up other file descriptors (perhaps even the three # standard ones). @@ -207,14 +182,14 @@ class FileTests(unittest.TestCase): @support.cpython_only def test_rename(self): - path = os_helper.TESTFN + path = support.TESTFN old = sys.getrefcount(path) self.assertRaises(TypeError, os.rename, path, 0) new = sys.getrefcount(path) self.assertEqual(old, new) def test_read(self): - with open(os_helper.TESTFN, "w+b") as fobj: + with open(support.TESTFN, "w+b") as fobj: fobj.write(b"spam") fobj.flush() fd = fobj.fileno() @@ -230,12 +205,12 @@ class FileTests(unittest.TestCase): "needs INT_MAX < PY_SSIZE_T_MAX") @support.bigmemtest(size=INT_MAX + 10, memuse=1, dry_run=False) def test_large_read(self, size): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - create_file(os_helper.TESTFN, b'test') + self.addCleanup(support.unlink, support.TESTFN) + create_file(support.TESTFN, b'test') # Issue #21932: Make sure that os.read() does not raise an # OverflowError for size larger than INT_MAX - with open(os_helper.TESTFN, "rb") as fp: + with open(support.TESTFN, "rb") as fp: data = os.read(fp.fileno(), size) # The test does not try to read more than 2 GiB at once because the @@ -244,13 +219,13 @@ class FileTests(unittest.TestCase): def test_write(self): # os.write() accepts bytes- and buffer-like objects but not strings - fd = os.open(os_helper.TESTFN, os.O_CREAT | os.O_WRONLY) + fd = os.open(support.TESTFN, os.O_CREAT | os.O_WRONLY) self.assertRaises(TypeError, os.write, fd, "beans") os.write(fd, b"bacon\n") os.write(fd, bytearray(b"eggs\n")) os.write(fd, memoryview(b"spam\n")) os.close(fd) - with open(os_helper.TESTFN, "rb") as fobj: + with open(support.TESTFN, "rb") as fobj: self.assertEqual(fobj.read().splitlines(), [b"bacon", b"eggs", b"spam"]) @@ -274,12 +249,12 @@ class FileTests(unittest.TestCase): self.write_windows_console(sys.executable, "-u", "-c", code) def fdopen_helper(self, *args): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) - f = os.fdopen(fd, *args, encoding="utf-8") + fd = os.open(support.TESTFN, os.O_RDONLY) + f = os.fdopen(fd, *args) f.close() def test_fdopen(self): - fd = os.open(os_helper.TESTFN, os.O_CREAT|os.O_RDWR) + fd = os.open(support.TESTFN, os.O_CREAT|os.O_RDWR) os.close(fd) self.fdopen_helper() @@ -287,16 +262,16 @@ class FileTests(unittest.TestCase): self.fdopen_helper('r', 100) def test_replace(self): - TESTFN2 = os_helper.TESTFN + ".2" - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - self.addCleanup(os_helper.unlink, TESTFN2) + TESTFN2 = support.TESTFN + ".2" + self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(support.unlink, TESTFN2) - create_file(os_helper.TESTFN, b"1") + create_file(support.TESTFN, b"1") create_file(TESTFN2, b"2") - os.replace(os_helper.TESTFN, TESTFN2) - self.assertRaises(FileNotFoundError, os.stat, os_helper.TESTFN) - with open(TESTFN2, 'r', encoding='utf-8') as f: + os.replace(support.TESTFN, TESTFN2) + self.assertRaises(FileNotFoundError, os.stat, support.TESTFN) + with open(TESTFN2, 'r') as f: self.assertEqual(f.read(), "1") def test_open_keywords(self): @@ -307,7 +282,7 @@ class FileTests(unittest.TestCase): def test_symlink_keywords(self): symlink = support.get_attribute(os, "symlink") try: - symlink(src='target', dst=os_helper.TESTFN, + symlink(src='target', dst=support.TESTFN, target_is_directory=False, dir_fd=None) except (NotImplementedError, OSError): pass # No OS support or unprivileged user @@ -319,18 +294,18 @@ class FileTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'copy_file_range'), 'test needs os.copy_file_range()') def test_copy_file_range(self): - TESTFN2 = os_helper.TESTFN + ".3" + TESTFN2 = support.TESTFN + ".3" data = b'0123456789' - create_file(os_helper.TESTFN, data) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + create_file(support.TESTFN, data) + self.addCleanup(support.unlink, support.TESTFN) - in_file = open(os_helper.TESTFN, 'rb') + in_file = open(support.TESTFN, 'rb') self.addCleanup(in_file.close) in_fd = in_file.fileno() out_file = open(TESTFN2, 'w+b') - self.addCleanup(os_helper.unlink, TESTFN2) + self.addCleanup(support.unlink, TESTFN2) self.addCleanup(out_file.close) out_fd = out_file.fileno() @@ -353,21 +328,21 @@ class FileTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'copy_file_range'), 'test needs os.copy_file_range()') def test_copy_file_range_offset(self): - TESTFN4 = os_helper.TESTFN + ".4" + TESTFN4 = support.TESTFN + ".4" data = b'0123456789' bytes_to_copy = 6 in_skip = 3 out_seek = 5 - create_file(os_helper.TESTFN, data) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + create_file(support.TESTFN, data) + self.addCleanup(support.unlink, support.TESTFN) - in_file = open(os_helper.TESTFN, 'rb') + in_file = open(support.TESTFN, 'rb') self.addCleanup(in_file.close) in_fd = in_file.fileno() out_file = open(TESTFN4, 'w+b') - self.addCleanup(os_helper.unlink, TESTFN4) + self.addCleanup(support.unlink, TESTFN4) self.addCleanup(out_file.close) out_fd = out_file.fileno() @@ -396,131 +371,11 @@ class FileTests(unittest.TestCase): self.assertEqual(read[out_seek:], data[in_skip:in_skip+i]) - @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') - def test_splice_invalid_values(self): - with self.assertRaises(ValueError): - os.splice(0, 1, -10) - - @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') - @requires_splice_pipe - def test_splice(self): - TESTFN2 = os_helper.TESTFN + ".3" - data = b'0123456789' - - create_file(os_helper.TESTFN, data) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - - in_file = open(os_helper.TESTFN, 'rb') - self.addCleanup(in_file.close) - in_fd = in_file.fileno() - - read_fd, write_fd = os.pipe() - self.addCleanup(lambda: os.close(read_fd)) - self.addCleanup(lambda: os.close(write_fd)) - - try: - i = os.splice(in_fd, write_fd, 5) - except OSError as e: - # Handle the case in which Python was compiled - # in a system with the syscall but without support - # in the kernel. - if e.errno != errno.ENOSYS: - raise - self.skipTest(e) - else: - # The number of copied bytes can be less than - # the number of bytes originally requested. - self.assertIn(i, range(0, 6)); - - self.assertEqual(os.read(read_fd, 100), data[:i]) - - @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') - @requires_splice_pipe - def test_splice_offset_in(self): - TESTFN4 = os_helper.TESTFN + ".4" - data = b'0123456789' - bytes_to_copy = 6 - in_skip = 3 - - create_file(os_helper.TESTFN, data) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - - in_file = open(os_helper.TESTFN, 'rb') - self.addCleanup(in_file.close) - in_fd = in_file.fileno() - - read_fd, write_fd = os.pipe() - self.addCleanup(lambda: os.close(read_fd)) - self.addCleanup(lambda: os.close(write_fd)) - - try: - i = os.splice(in_fd, write_fd, bytes_to_copy, offset_src=in_skip) - except OSError as e: - # Handle the case in which Python was compiled - # in a system with the syscall but without support - # in the kernel. - if e.errno != errno.ENOSYS: - raise - self.skipTest(e) - else: - # The number of copied bytes can be less than - # the number of bytes originally requested. - self.assertIn(i, range(0, bytes_to_copy+1)); - - read = os.read(read_fd, 100) - # 012 are skipped (in_skip) - # 345678 are copied in the file (in_skip + bytes_to_copy) - self.assertEqual(read, data[in_skip:in_skip+i]) - - @unittest.skipUnless(hasattr(os, 'splice'), 'test needs os.splice()') - @requires_splice_pipe - def test_splice_offset_out(self): - TESTFN4 = os_helper.TESTFN + ".4" - data = b'0123456789' - bytes_to_copy = 6 - out_seek = 3 - - create_file(os_helper.TESTFN, data) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - - read_fd, write_fd = os.pipe() - self.addCleanup(lambda: os.close(read_fd)) - self.addCleanup(lambda: os.close(write_fd)) - os.write(write_fd, data) - - out_file = open(TESTFN4, 'w+b') - self.addCleanup(os_helper.unlink, TESTFN4) - self.addCleanup(out_file.close) - out_fd = out_file.fileno() - - try: - i = os.splice(read_fd, out_fd, bytes_to_copy, offset_dst=out_seek) - except OSError as e: - # Handle the case in which Python was compiled - # in a system with the syscall but without support - # in the kernel. - if e.errno != errno.ENOSYS: - raise - self.skipTest(e) - else: - # The number of copied bytes can be less than - # the number of bytes originally requested. - self.assertIn(i, range(0, bytes_to_copy+1)); - - with open(TESTFN4, 'rb') as in_file: - read = in_file.read() - # seeked bytes (5) are zero'ed - self.assertEqual(read[:out_seek], b'\x00'*out_seek) - # 012 are skipped (in_skip) - # 345678 are copied in the file (in_skip + bytes_to_copy) - self.assertEqual(read[out_seek:], data[:i]) - - # Test attributes on return values from os.*stat* family. class StatAttributeTests(unittest.TestCase): def setUp(self): - self.fname = os_helper.TESTFN - self.addCleanup(os_helper.unlink, self.fname) + self.fname = support.TESTFN + self.addCleanup(support.unlink, self.fname) create_file(self.fname, b"ABC") def check_stat_attributes(self, fname): @@ -705,7 +560,7 @@ class StatAttributeTests(unittest.TestCase): 0) # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) - dirname = os_helper.TESTFN + "dir" + dirname = support.TESTFN + "dir" os.mkdir(dirname) self.addCleanup(os.rmdir, dirname) @@ -722,7 +577,7 @@ class StatAttributeTests(unittest.TestCase): # os.environ['TEMP'] should be located on a volume that # supports file ACLs. fname = os.path.join(os.environ['TEMP'], self.fname) - self.addCleanup(os_helper.unlink, fname) + self.addCleanup(support.unlink, fname) create_file(fname, b'ABC') # Deny the right to [S]YNCHRONIZE on the file to # force CreateFile to fail with ERROR_ACCESS_DENIED. @@ -747,10 +602,10 @@ class StatAttributeTests(unittest.TestCase): class UtimeTests(unittest.TestCase): def setUp(self): - self.dirname = os_helper.TESTFN + self.dirname = support.TESTFN self.fname = os.path.join(self.dirname, "f1") - self.addCleanup(os_helper.rmtree, self.dirname) + self.addCleanup(support.rmtree, self.dirname) os.mkdir(self.dirname) create_file(self.fname) @@ -969,311 +824,185 @@ class UtimeTests(unittest.TestCase): from test import mapping_tests -class EnvironTests(mapping_tests.BasicTestMappingProtocol): - """check that os.environ object conform to mapping protocol""" - type2test = None +# TODO: RUSTPYTHON (KeyError: 'surrogateescape') +# class EnvironTests(mapping_tests.BasicTestMappingProtocol): +# """check that os.environ object conform to mapping protocol""" +# type2test = None - def setUp(self): - self.__save = dict(os.environ) - if os.supports_bytes_environ: - self.__saveb = dict(os.environb) - for key, value in self._reference().items(): - os.environ[key] = value +# def setUp(self): +# self.__save = dict(os.environ) +# if os.supports_bytes_environ: +# self.__saveb = dict(os.environb) +# for key, value in self._reference().items(): +# os.environ[key] = value - def tearDown(self): - os.environ.clear() - os.environ.update(self.__save) - if os.supports_bytes_environ: - os.environb.clear() - os.environb.update(self.__saveb) +# def tearDown(self): +# os.environ.clear() +# os.environ.update(self.__save) +# if os.supports_bytes_environ: +# os.environb.clear() +# os.environb.update(self.__saveb) - def _reference(self): - return {"KEY1":"VALUE1", "KEY2":"VALUE2", "KEY3":"VALUE3"} +# def _reference(self): +# return {"KEY1":"VALUE1", "KEY2":"VALUE2", "KEY3":"VALUE3"} - def _empty_mapping(self): - os.environ.clear() - return os.environ +# def _empty_mapping(self): +# os.environ.clear() +# return os.environ - # Bug 1110478 - @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), - 'requires a shell') - @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") - def test_update2(self): - os.environ.clear() - os.environ.update(HELLO="World") - with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: - value = popen.read().strip() - self.assertEqual(value, "World") +# # Bug 1110478 +# @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), +# 'requires a shell') +# def test_update2(self): +# os.environ.clear() +# os.environ.update(HELLO="World") +# with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: +# value = popen.read().strip() +# self.assertEqual(value, "World") - @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), - 'requires a shell') - @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") - def test_os_popen_iter(self): - with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" - % unix_shell) as popen: - it = iter(popen) - self.assertEqual(next(it), "line1\n") - self.assertEqual(next(it), "line2\n") - self.assertEqual(next(it), "line3\n") - self.assertRaises(StopIteration, next, it) +# @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), +# 'requires a shell') +# def test_os_popen_iter(self): +# with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" +# % unix_shell) as popen: +# it = iter(popen) +# self.assertEqual(next(it), "line1\n") +# self.assertEqual(next(it), "line2\n") +# self.assertEqual(next(it), "line3\n") +# self.assertRaises(StopIteration, next, it) - # Verify environ keys and values from the OS are of the - # correct str type. - def test_keyvalue_types(self): - for key, val in os.environ.items(): - self.assertEqual(type(key), str) - self.assertEqual(type(val), str) +# # Verify environ keys and values from the OS are of the +# # correct str type. +# def test_keyvalue_types(self): +# for key, val in os.environ.items(): +# self.assertEqual(type(key), str) +# self.assertEqual(type(val), str) - def test_items(self): - for key, value in self._reference().items(): - self.assertEqual(os.environ.get(key), value) +# def test_items(self): +# for key, value in self._reference().items(): +# self.assertEqual(os.environ.get(key), value) - # Issue 7310 - def test___repr__(self): - """Check that the repr() of os.environ looks like environ({...}).""" - env = os.environ - formatted_items = ", ".join( - f"{key!r}: {value!r}" - for key, value in env.items() - ) - self.assertEqual(repr(env), f"environ({{{formatted_items}}})") +# # Issue 7310 +# def test___repr__(self): +# """Check that the repr() of os.environ looks like environ({...}).""" +# env = os.environ +# self.assertEqual(repr(env), 'environ({{{}}})'.format(', '.join( +# '{!r}: {!r}'.format(key, value) +# for key, value in env.items()))) - def test_get_exec_path(self): - defpath_list = os.defpath.split(os.pathsep) - test_path = ['/monty', '/python', '', '/flying/circus'] - test_env = {'PATH': os.pathsep.join(test_path)} +# def test_get_exec_path(self): +# defpath_list = os.defpath.split(os.pathsep) +# test_path = ['/monty', '/python', '', '/flying/circus'] +# test_env = {'PATH': os.pathsep.join(test_path)} - saved_environ = os.environ - try: - os.environ = dict(test_env) - # Test that defaulting to os.environ works. - self.assertSequenceEqual(test_path, os.get_exec_path()) - self.assertSequenceEqual(test_path, os.get_exec_path(env=None)) - finally: - os.environ = saved_environ +# saved_environ = os.environ +# try: +# os.environ = dict(test_env) +# # Test that defaulting to os.environ works. +# self.assertSequenceEqual(test_path, os.get_exec_path()) +# self.assertSequenceEqual(test_path, os.get_exec_path(env=None)) +# finally: +# os.environ = saved_environ - # No PATH environment variable - self.assertSequenceEqual(defpath_list, os.get_exec_path({})) - # Empty PATH environment variable - self.assertSequenceEqual(('',), os.get_exec_path({'PATH':''})) - # Supplied PATH environment variable - self.assertSequenceEqual(test_path, os.get_exec_path(test_env)) +# # No PATH environment variable +# self.assertSequenceEqual(defpath_list, os.get_exec_path({})) +# # Empty PATH environment variable +# self.assertSequenceEqual(('',), os.get_exec_path({'PATH':''})) +# # Supplied PATH environment variable +# self.assertSequenceEqual(test_path, os.get_exec_path(test_env)) - if os.supports_bytes_environ: - # env cannot contain 'PATH' and b'PATH' keys - try: - # ignore BytesWarning warning - with warnings.catch_warnings(record=True): - mixed_env = {'PATH': '1', b'PATH': b'2'} - except BytesWarning: - # mixed_env cannot be created with python -bb - pass - else: - self.assertRaises(ValueError, os.get_exec_path, mixed_env) +# if os.supports_bytes_environ: +# # env cannot contain 'PATH' and b'PATH' keys +# try: +# # ignore BytesWarning warning +# with warnings.catch_warnings(record=True): +# mixed_env = {'PATH': '1', b'PATH': b'2'} +# except BytesWarning: +# # mixed_env cannot be created with python -bb +# pass +# else: +# self.assertRaises(ValueError, os.get_exec_path, mixed_env) - # bytes key and/or value - self.assertSequenceEqual(os.get_exec_path({b'PATH': b'abc'}), - ['abc']) - self.assertSequenceEqual(os.get_exec_path({b'PATH': 'abc'}), - ['abc']) - self.assertSequenceEqual(os.get_exec_path({'PATH': b'abc'}), - ['abc']) +# # bytes key and/or value +# self.assertSequenceEqual(os.get_exec_path({b'PATH': b'abc'}), +# ['abc']) +# self.assertSequenceEqual(os.get_exec_path({b'PATH': 'abc'}), +# ['abc']) +# self.assertSequenceEqual(os.get_exec_path({'PATH': b'abc'}), +# ['abc']) - @unittest.skipUnless(os.supports_bytes_environ, - "os.environb required for this test.") - # TODO: RUSTPYTHON (KeyError: 'surrogateescape') - @unittest.expectedFailure - def test_environb(self): - # os.environ -> os.environb - value = 'euro\u20ac' - try: - value_bytes = value.encode(sys.getfilesystemencoding(), - 'surrogateescape') - except UnicodeEncodeError: - msg = "U+20AC character is not encodable to %s" % ( - sys.getfilesystemencoding(),) - self.skipTest(msg) - os.environ['unicode'] = value - self.assertEqual(os.environ['unicode'], value) - self.assertEqual(os.environb[b'unicode'], value_bytes) +# @unittest.skipUnless(os.supports_bytes_environ, +# "os.environb required for this test.") +# def test_environb(self): +# # os.environ -> os.environb +# value = 'euro\u20ac' +# try: +# value_bytes = value.encode(sys.getfilesystemencoding(), +# 'surrogateescape') +# except UnicodeEncodeError: +# msg = "U+20AC character is not encodable to %s" % ( +# sys.getfilesystemencoding(),) +# self.skipTest(msg) +# os.environ['unicode'] = value +# self.assertEqual(os.environ['unicode'], value) +# self.assertEqual(os.environb[b'unicode'], value_bytes) - # os.environb -> os.environ - value = b'\xff' - os.environb[b'bytes'] = value - self.assertEqual(os.environb[b'bytes'], value) - value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape') - self.assertEqual(os.environ['bytes'], value_str) +# # os.environb -> os.environ +# value = b'\xff' +# os.environb[b'bytes'] = value +# self.assertEqual(os.environb[b'bytes'], value) +# value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape') +# self.assertEqual(os.environ['bytes'], value_str) - def test_putenv_unsetenv(self): - name = "PYTHONTESTVAR" - value = "testvalue" - code = f'import os; print(repr(os.environ.get({name!r})))' +# # On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415). +# @support.requires_mac_ver(10, 6) +# def test_unset_error(self): +# if sys.platform == "win32": +# # an environment variable is limited to 32,767 characters +# key = 'x' * 50000 +# self.assertRaises(ValueError, os.environ.__delitem__, key) +# else: +# # "=" is not allowed in a variable name +# key = 'key=' +# self.assertRaises(OSError, os.environ.__delitem__, key) - with os_helper.EnvironmentVarGuard() as env: - env.pop(name, None) +# def test_key_type(self): +# missing = 'missingkey' +# self.assertNotIn(missing, os.environ) - os.putenv(name, value) - proc = subprocess.run([sys.executable, '-c', code], check=True, - stdout=subprocess.PIPE, text=True) - self.assertEqual(proc.stdout.rstrip(), repr(value)) +# with self.assertRaises(KeyError) as cm: +# os.environ[missing] +# self.assertIs(cm.exception.args[0], missing) +# self.assertTrue(cm.exception.__suppress_context__) - os.unsetenv(name) - proc = subprocess.run([sys.executable, '-c', code], check=True, - stdout=subprocess.PIPE, text=True) - self.assertEqual(proc.stdout.rstrip(), repr(None)) +# with self.assertRaises(KeyError) as cm: +# del os.environ[missing] +# self.assertIs(cm.exception.args[0], missing) +# self.assertTrue(cm.exception.__suppress_context__) - # On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415). - @support.requires_mac_ver(10, 6) - def test_putenv_unsetenv_error(self): - # Empty variable name is invalid. - # "=" and null character are not allowed in a variable name. - for name in ('', '=name', 'na=me', 'name=', 'name\0', 'na\0me'): - self.assertRaises((OSError, ValueError), os.putenv, name, "value") - self.assertRaises((OSError, ValueError), os.unsetenv, name) +# def _test_environ_iteration(self, collection): +# iterator = iter(collection) +# new_key = "__new_key__" - if sys.platform == "win32": - # On Windows, an environment variable string ("name=value" string) - # is limited to 32,767 characters - longstr = 'x' * 32_768 - self.assertRaises(ValueError, os.putenv, longstr, "1") - self.assertRaises(ValueError, os.putenv, "X", longstr) - self.assertRaises(ValueError, os.unsetenv, longstr) +# next(iterator) # start iteration over os.environ.items - def test_key_type(self): - missing = 'missingkey' - self.assertNotIn(missing, os.environ) +# # add a new key in os.environ mapping +# os.environ[new_key] = "test_environ_iteration" - with self.assertRaises(KeyError) as cm: - os.environ[missing] - self.assertIs(cm.exception.args[0], missing) - self.assertTrue(cm.exception.__suppress_context__) +# try: +# next(iterator) # force iteration over modified mapping +# self.assertEqual(os.environ[new_key], "test_environ_iteration") +# finally: +# del os.environ[new_key] - with self.assertRaises(KeyError) as cm: - del os.environ[missing] - self.assertIs(cm.exception.args[0], missing) - self.assertTrue(cm.exception.__suppress_context__) +# def test_iter_error_when_changing_os_environ(self): +# self._test_environ_iteration(os.environ) - def _test_environ_iteration(self, collection): - iterator = iter(collection) - new_key = "__new_key__" +# def test_iter_error_when_changing_os_environ_items(self): +# self._test_environ_iteration(os.environ.items()) - next(iterator) # start iteration over os.environ.items - - # add a new key in os.environ mapping - os.environ[new_key] = "test_environ_iteration" - - try: - next(iterator) # force iteration over modified mapping - self.assertEqual(os.environ[new_key], "test_environ_iteration") - finally: - del os.environ[new_key] - - def test_iter_error_when_changing_os_environ(self): - self._test_environ_iteration(os.environ) - - def test_iter_error_when_changing_os_environ_items(self): - self._test_environ_iteration(os.environ.items()) - - def test_iter_error_when_changing_os_environ_values(self): - self._test_environ_iteration(os.environ.values()) - - def _test_underlying_process_env(self, var, expected): - if not (unix_shell and os.path.exists(unix_shell)): - return - - with os.popen(f"{unix_shell} -c 'echo ${var}'") as popen: - value = popen.read().strip() - - self.assertEqual(expected, value) - - # TODO: RUSTPYTHON (KeyError: 'surrogateescape') - @unittest.expectedFailure - def test_or_operator(self): - overridden_key = '_TEST_VAR_' - original_value = 'original_value' - os.environ[overridden_key] = original_value - - new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} - expected = dict(os.environ) - expected.update(new_vars_dict) - - actual = os.environ | new_vars_dict - self.assertDictEqual(expected, actual) - self.assertEqual('3', actual[overridden_key]) - - new_vars_items = new_vars_dict.items() - self.assertIs(NotImplemented, os.environ.__or__(new_vars_items)) - - self._test_underlying_process_env('_A_', '') - self._test_underlying_process_env(overridden_key, original_value) - - # TODO: RUSTPYTHON (KeyError: 'surrogateescape') - @unittest.expectedFailure - def test_ior_operator(self): - overridden_key = '_TEST_VAR_' - os.environ[overridden_key] = 'original_value' - - new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} - expected = dict(os.environ) - expected.update(new_vars_dict) - - os.environ |= new_vars_dict - self.assertEqual(expected, os.environ) - self.assertEqual('3', os.environ[overridden_key]) - - self._test_underlying_process_env('_A_', '1') - self._test_underlying_process_env(overridden_key, '3') - - def test_ior_operator_invalid_dicts(self): - os_environ_copy = os.environ.copy() - with self.assertRaises(TypeError): - dict_with_bad_key = {1: '_A_'} - os.environ |= dict_with_bad_key - - with self.assertRaises(TypeError): - dict_with_bad_val = {'_A_': 1} - os.environ |= dict_with_bad_val - - # Check nothing was added. - self.assertEqual(os_environ_copy, os.environ) - - # TODO: RUSTPYTHON (KeyError: 'surrogateescape') - @unittest.expectedFailure - def test_ior_operator_key_value_iterable(self): - overridden_key = '_TEST_VAR_' - os.environ[overridden_key] = 'original_value' - - new_vars_items = (('_A_', '1'), ('_B_', '2'), (overridden_key, '3')) - expected = dict(os.environ) - expected.update(new_vars_items) - - os.environ |= new_vars_items - self.assertEqual(expected, os.environ) - self.assertEqual('3', os.environ[overridden_key]) - - self._test_underlying_process_env('_A_', '1') - self._test_underlying_process_env(overridden_key, '3') - - # TODO: RUSTPYTHON (KeyError: 'surrogateescape') - @unittest.expectedFailure - def test_ror_operator(self): - overridden_key = '_TEST_VAR_' - original_value = 'original_value' - os.environ[overridden_key] = original_value - - new_vars_dict = {'_A_': '1', '_B_': '2', overridden_key: '3'} - expected = dict(new_vars_dict) - expected.update(os.environ) - - actual = new_vars_dict | os.environ - self.assertDictEqual(expected, actual) - self.assertEqual(original_value, actual[overridden_key]) - - new_vars_items = new_vars_dict.items() - self.assertIs(NotImplemented, os.environ.__ror__(new_vars_items)) - - self._test_underlying_process_env('_A_', '') - self._test_underlying_process_env(overridden_key, original_value) +# def test_iter_error_when_changing_os_environ_values(self): +# self._test_environ_iteration(os.environ.values()) class WalkTests(unittest.TestCase): @@ -1288,7 +1017,7 @@ class WalkTests(unittest.TestCase): def setUp(self): join = os.path.join - self.addCleanup(os_helper.rmtree, os_helper.TESTFN) + self.addCleanup(support.rmtree, support.TESTFN) # Build: # TESTFN/ @@ -1307,7 +1036,7 @@ class WalkTests(unittest.TestCase): # broken_link3 # TEST2/ # tmp4 a lone file - self.walk_path = join(os_helper.TESTFN, "TEST1") + self.walk_path = join(support.TESTFN, "TEST1") self.sub1_path = join(self.walk_path, "SUB1") self.sub11_path = join(self.sub1_path, "SUB11") sub2_path = join(self.walk_path, "SUB2") @@ -1317,8 +1046,8 @@ class WalkTests(unittest.TestCase): tmp3_path = join(sub2_path, "tmp3") tmp5_path = join(sub21_path, "tmp3") self.link_path = join(sub2_path, "link") - t2_path = join(os_helper.TESTFN, "TEST2") - tmp4_path = join(os_helper.TESTFN, "TEST2", "tmp4") + t2_path = join(support.TESTFN, "TEST2") + tmp4_path = join(support.TESTFN, "TEST2", "tmp4") broken_link_path = join(sub2_path, "broken_link") broken_link2_path = join(sub2_path, "broken_link2") broken_link3_path = join(sub2_path, "broken_link3") @@ -1330,10 +1059,10 @@ class WalkTests(unittest.TestCase): os.makedirs(t2_path) for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path, tmp5_path: - with open(path, "x", encoding='utf-8') as f: + with open(path, "x") as f: f.write("I'm " + path + " and proud of it. Blame test_os.\n") - if os_helper.can_symlink(): + if support.can_symlink(): os.symlink(os.path.abspath(t2_path), self.link_path) os.symlink('broken', broken_link_path, True) os.symlink(join('tmp3', 'broken'), broken_link2_path, True) @@ -1416,7 +1145,7 @@ class WalkTests(unittest.TestCase): self.sub2_tree) def test_walk_symlink(self): - if not os_helper.can_symlink(): + if not support.can_symlink(): self.skipTest("need symlink support") # Walk, following symlinks. @@ -1452,7 +1181,7 @@ class WalkTests(unittest.TestCase): def test_walk_many_open_files(self): depth = 30 - base = os.path.join(os_helper.TESTFN, 'deep') + base = os.path.join(support.TESTFN, 'deep') p = os.path.join(base, *(['d']*depth)) os.makedirs(p) @@ -1502,13 +1231,13 @@ class FwalkTests(WalkTests): self.assertEqual(expected[root], (set(dirs), set(files))) def test_compare_to_walk(self): - kwargs = {'top': os_helper.TESTFN} + kwargs = {'top': support.TESTFN} self._compare_to_walk(kwargs, kwargs) def test_dir_fd(self): try: fd = os.open(".", os.O_RDONLY) - walk_kwargs = {'top': os_helper.TESTFN} + walk_kwargs = {'top': support.TESTFN} fwalk_kwargs = walk_kwargs.copy() fwalk_kwargs['dir_fd'] = fd self._compare_to_walk(walk_kwargs, fwalk_kwargs) @@ -1518,7 +1247,7 @@ class FwalkTests(WalkTests): def test_yields_correct_dir_fd(self): # check returned file descriptors for topdown, follow_symlinks in itertools.product((True, False), repeat=2): - args = os_helper.TESTFN, topdown, None + args = support.TESTFN, topdown, None for root, dirs, files, rootfd in self.fwalk(*args, follow_symlinks=follow_symlinks): # check that the FD is valid os.fstat(rootfd) @@ -1534,7 +1263,7 @@ class FwalkTests(WalkTests): minfd = os.dup(1) os.close(minfd) for i in range(256): - for x in self.fwalk(os_helper.TESTFN): + for x in self.fwalk(support.TESTFN): pass newfd = os.dup(1) self.addCleanup(os.close, newfd) @@ -1592,10 +1321,10 @@ class BytesFwalkTests(FwalkTests): class MakedirTests(unittest.TestCase): def setUp(self): - os.mkdir(os_helper.TESTFN) + os.mkdir(support.TESTFN) def test_makedir(self): - base = os_helper.TESTFN + base = support.TESTFN path = os.path.join(base, 'dir1', 'dir2', 'dir3') os.makedirs(path) # Should work path = os.path.join(base, 'dir1', 'dir2', 'dir3', 'dir4') @@ -1610,8 +1339,8 @@ class MakedirTests(unittest.TestCase): os.makedirs(path) def test_mode(self): - with os_helper.temp_umask(0o002): - base = os_helper.TESTFN + with support.temp_umask(0o002): + base = support.TESTFN parent = os.path.join(base, 'dir1') path = os.path.join(parent, 'dir2') os.makedirs(path, 0o555) @@ -1622,7 +1351,7 @@ class MakedirTests(unittest.TestCase): self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775) def test_exist_ok_existing_directory(self): - path = os.path.join(os_helper.TESTFN, 'dir1') + path = os.path.join(support.TESTFN, 'dir1') mode = 0o777 old_mask = os.umask(0o022) os.makedirs(path, mode) @@ -1636,18 +1365,18 @@ class MakedirTests(unittest.TestCase): os.makedirs(os.path.abspath('/'), exist_ok=True) def test_exist_ok_s_isgid_directory(self): - path = os.path.join(os_helper.TESTFN, 'dir1') + path = os.path.join(support.TESTFN, 'dir1') S_ISGID = stat.S_ISGID mode = 0o777 old_mask = os.umask(0o022) try: existing_testfn_mode = stat.S_IMODE( - os.lstat(os_helper.TESTFN).st_mode) + os.lstat(support.TESTFN).st_mode) try: - os.chmod(os_helper.TESTFN, existing_testfn_mode | S_ISGID) + os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID) except PermissionError: raise unittest.SkipTest('Cannot set S_ISGID for dir.') - if (os.lstat(os_helper.TESTFN).st_mode & S_ISGID != S_ISGID): + if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID): raise unittest.SkipTest('No support for S_ISGID dir mode.') # The os should apply S_ISGID from the parent dir for us, but # this test need not depend on that behavior. Be explicit. @@ -1663,9 +1392,9 @@ class MakedirTests(unittest.TestCase): os.umask(old_mask) def test_exist_ok_existing_regular_file(self): - base = os_helper.TESTFN - path = os.path.join(os_helper.TESTFN, 'dir1') - with open(path, 'w', encoding='utf-8') as f: + base = support.TESTFN + path = os.path.join(support.TESTFN, 'dir1') + with open(path, 'w') as f: f.write('abc') self.assertRaises(OSError, os.makedirs, path) self.assertRaises(OSError, os.makedirs, path, exist_ok=False) @@ -1673,12 +1402,12 @@ class MakedirTests(unittest.TestCase): os.remove(path) def tearDown(self): - path = os.path.join(os_helper.TESTFN, 'dir1', 'dir2', 'dir3', + path = os.path.join(support.TESTFN, 'dir1', 'dir2', 'dir3', 'dir4', 'dir5', 'dir6') # If the tests failed, the bottom-most directory ('../dir6') # may not have been created, so we look for the outermost directory # that exists. - while not os.path.exists(path) and path != os_helper.TESTFN: + while not os.path.exists(path) and path != support.TESTFN: path = os.path.dirname(path) os.removedirs(path) @@ -1689,17 +1418,17 @@ class ChownFileTests(unittest.TestCase): @classmethod def setUpClass(cls): - os.mkdir(os_helper.TESTFN) + os.mkdir(support.TESTFN) def test_chown_uid_gid_arguments_must_be_index(self): - stat = os.stat(os_helper.TESTFN) + stat = os.stat(support.TESTFN) uid = stat.st_uid gid = stat.st_gid for value in (-1.0, -1j, decimal.Decimal(-1), fractions.Fraction(-2, 2)): - self.assertRaises(TypeError, os.chown, os_helper.TESTFN, value, gid) - self.assertRaises(TypeError, os.chown, os_helper.TESTFN, uid, value) - self.assertIsNone(os.chown(os_helper.TESTFN, uid, gid)) - self.assertIsNone(os.chown(os_helper.TESTFN, -1, -1)) + self.assertRaises(TypeError, os.chown, support.TESTFN, value, gid) + self.assertRaises(TypeError, os.chown, support.TESTFN, uid, value) + self.assertIsNone(os.chown(support.TESTFN, uid, gid)) + self.assertIsNone(os.chown(support.TESTFN, -1, -1)) @unittest.skipUnless(hasattr(os, 'getgroups'), 'need os.getgroups') def test_chown_gid(self): @@ -1708,61 +1437,61 @@ class ChownFileTests(unittest.TestCase): self.skipTest("test needs at least 2 groups") gid_1, gid_2 = groups[:2] - uid = os.stat(os_helper.TESTFN).st_uid + uid = os.stat(support.TESTFN).st_uid - os.chown(os_helper.TESTFN, uid, gid_1) - gid = os.stat(os_helper.TESTFN).st_gid + os.chown(support.TESTFN, uid, gid_1) + gid = os.stat(support.TESTFN).st_gid self.assertEqual(gid, gid_1) - os.chown(os_helper.TESTFN, uid, gid_2) - gid = os.stat(os_helper.TESTFN).st_gid + os.chown(support.TESTFN, uid, gid_2) + gid = os.stat(support.TESTFN).st_gid self.assertEqual(gid, gid_2) @unittest.skipUnless(root_in_posix and len(all_users) > 1, "test needs root privilege and more than one user") def test_chown_with_root(self): uid_1, uid_2 = all_users[:2] - gid = os.stat(os_helper.TESTFN).st_gid - os.chown(os_helper.TESTFN, uid_1, gid) - uid = os.stat(os_helper.TESTFN).st_uid + gid = os.stat(support.TESTFN).st_gid + os.chown(support.TESTFN, uid_1, gid) + uid = os.stat(support.TESTFN).st_uid self.assertEqual(uid, uid_1) - os.chown(os_helper.TESTFN, uid_2, gid) - uid = os.stat(os_helper.TESTFN).st_uid + os.chown(support.TESTFN, uid_2, gid) + uid = os.stat(support.TESTFN).st_uid self.assertEqual(uid, uid_2) @unittest.skipUnless(not root_in_posix and len(all_users) > 1, "test needs non-root account and more than one user") def test_chown_without_permission(self): uid_1, uid_2 = all_users[:2] - gid = os.stat(os_helper.TESTFN).st_gid + gid = os.stat(support.TESTFN).st_gid with self.assertRaises(PermissionError): - os.chown(os_helper.TESTFN, uid_1, gid) - os.chown(os_helper.TESTFN, uid_2, gid) + os.chown(support.TESTFN, uid_1, gid) + os.chown(support.TESTFN, uid_2, gid) @classmethod def tearDownClass(cls): - os.rmdir(os_helper.TESTFN) + os.rmdir(support.TESTFN) class RemoveDirsTests(unittest.TestCase): def setUp(self): - os.makedirs(os_helper.TESTFN) + os.makedirs(support.TESTFN) def tearDown(self): - os_helper.rmtree(os_helper.TESTFN) + support.rmtree(support.TESTFN) def test_remove_all(self): - dira = os.path.join(os_helper.TESTFN, 'dira') + dira = os.path.join(support.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) os.removedirs(dirb) self.assertFalse(os.path.exists(dirb)) self.assertFalse(os.path.exists(dira)) - self.assertFalse(os.path.exists(os_helper.TESTFN)) + self.assertFalse(os.path.exists(support.TESTFN)) def test_remove_partial(self): - dira = os.path.join(os_helper.TESTFN, 'dira') + dira = os.path.join(support.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) @@ -1770,10 +1499,10 @@ class RemoveDirsTests(unittest.TestCase): os.removedirs(dirb) self.assertFalse(os.path.exists(dirb)) self.assertTrue(os.path.exists(dira)) - self.assertTrue(os.path.exists(os_helper.TESTFN)) + self.assertTrue(os.path.exists(support.TESTFN)) def test_remove_nothing(self): - dira = os.path.join(os_helper.TESTFN, 'dira') + dira = os.path.join(support.TESTFN, 'dira') os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) @@ -1782,7 +1511,7 @@ class RemoveDirsTests(unittest.TestCase): os.removedirs(dirb) self.assertTrue(os.path.exists(dirb)) self.assertTrue(os.path.exists(dira)) - self.assertTrue(os.path.exists(os_helper.TESTFN)) + self.assertTrue(os.path.exists(support.TESTFN)) class DevNullTests(unittest.TestCase): @@ -1920,8 +1649,8 @@ class URandomFDTests(unittest.TestCase): def test_urandom_fd_reopened(self): # Issue #21207: urandom() should detect its fd to /dev/urandom # changed to something else, and reopen it. - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - create_file(os_helper.TESTFN, b"x" * 256) + self.addCleanup(support.unlink, support.TESTFN) + create_file(support.TESTFN, b"x" * 256) code = """if 1: import os @@ -1947,7 +1676,7 @@ class URandomFDTests(unittest.TestCase): os.dup2(new_fd, fd) sys.stdout.buffer.write(os.urandom(4)) sys.stdout.buffer.write(os.urandom(4)) - """.format(TESTFN=os_helper.TESTFN) + """.format(TESTFN=support.TESTFN) rc, out, err = assert_python_ok('-Sc', code) self.assertEqual(len(out), 8) self.assertNotEqual(out[0:4], out[4:8]) @@ -2099,40 +1828,40 @@ class ExecTests(unittest.TestCase): class Win32ErrorTests(unittest.TestCase): def setUp(self): try: - os.stat(os_helper.TESTFN) + os.stat(support.TESTFN) except FileNotFoundError: exists = False except OSError as exc: exists = True self.fail("file %s must not exist; os.stat failed with %s" - % (os_helper.TESTFN, exc)) + % (support.TESTFN, exc)) else: - self.fail("file %s must not exist" % os_helper.TESTFN) + self.fail("file %s must not exist" % support.TESTFN) def test_rename(self): - self.assertRaises(OSError, os.rename, os_helper.TESTFN, os_helper.TESTFN+".bak") + self.assertRaises(OSError, os.rename, support.TESTFN, support.TESTFN+".bak") def test_remove(self): - self.assertRaises(OSError, os.remove, os_helper.TESTFN) + self.assertRaises(OSError, os.remove, support.TESTFN) def test_chdir(self): - self.assertRaises(OSError, os.chdir, os_helper.TESTFN) + self.assertRaises(OSError, os.chdir, support.TESTFN) def test_mkdir(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) - with open(os_helper.TESTFN, "x") as f: - self.assertRaises(OSError, os.mkdir, os_helper.TESTFN) + with open(support.TESTFN, "x") as f: + self.assertRaises(OSError, os.mkdir, support.TESTFN) def test_utime(self): - self.assertRaises(OSError, os.utime, os_helper.TESTFN, None) + self.assertRaises(OSError, os.utime, support.TESTFN, None) def test_chmod(self): - self.assertRaises(OSError, os.chmod, os_helper.TESTFN, 0) + self.assertRaises(OSError, os.chmod, support.TESTFN, 0) class TestInvalidFD(unittest.TestCase): - singles = ["fchdir", "dup", "fdatasync", "fstat", + singles = ["fchdir", "dup", "fdopen", "fdatasync", "fstat", "fstatvfs", "fsync", "tcgetpgrp", "ttyname"] #singles.append("close") #We omit close because it doesn't raise an exception on some platforms @@ -2140,29 +1869,28 @@ class TestInvalidFD(unittest.TestCase): def helper(self): if hasattr(os, f): self.check(getattr(os, f)) + + return helper for f in singles: locals()["test_"+f] = get_single(f) - def check(self, f, *args, **kwargs): + def check(self, f, *args): try: - f(os_helper.make_bad_fd(), *args, **kwargs) + f(support.make_bad_fd(), *args) except OSError as e: self.assertEqual(e.errno, errno.EBADF) else: self.fail("%r didn't raise an OSError with a bad file descriptor" % f) - def test_fdopen(self): - self.check(os.fdopen, encoding="utf-8") - @unittest.skipUnless(hasattr(os, 'isatty'), 'test needs os.isatty()') def test_isatty(self): - self.assertEqual(os.isatty(os_helper.make_bad_fd()), False) + self.assertEqual(os.isatty(support.make_bad_fd()), False) @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') def test_closerange(self): - fd = os_helper.make_bad_fd() + fd = support.make_bad_fd() # Make sure none of the descriptors we are about to close are # currently valid (issue 6542). for i in range(10): @@ -2236,8 +1964,8 @@ class TestInvalidFD(unittest.TestCase): class LinkTests(unittest.TestCase): def setUp(self): - self.file1 = os_helper.TESTFN - self.file2 = os.path.join(os_helper.TESTFN + "2") + self.file1 = support.TESTFN + self.file2 = os.path.join(support.TESTFN + "2") def tearDown(self): for file in (self.file1, self.file2): @@ -2251,7 +1979,7 @@ class LinkTests(unittest.TestCase): os.link(file1, file2) except PermissionError as e: self.skipTest('os.link(): %s' % e) - with open(file1, "rb") as f1, open(file2, "rb") as f2: + with open(file1, "r") as f1, open(file2, "r") as f2: self.assertTrue(os.path.sameopenfile(f1.fileno(), f2.fileno())) def test_link(self): @@ -2342,12 +2070,12 @@ class PosixUidGidTests(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "Posix specific tests") class Pep383Tests(unittest.TestCase): def setUp(self): - if os_helper.TESTFN_UNENCODABLE: - self.dir = os_helper.TESTFN_UNENCODABLE - elif os_helper.TESTFN_NONASCII: - self.dir = os_helper.TESTFN_NONASCII + if support.TESTFN_UNENCODABLE: + self.dir = support.TESTFN_UNENCODABLE + elif support.TESTFN_NONASCII: + self.dir = support.TESTFN_NONASCII else: - self.dir = os_helper.TESTFN + self.dir = support.TESTFN self.bdir = os.fsencode(self.dir) bytesfn = [] @@ -2357,11 +2085,11 @@ class Pep383Tests(unittest.TestCase): except UnicodeEncodeError: return bytesfn.append(fn) - add_filename(os_helper.TESTFN_UNICODE) - if os_helper.TESTFN_UNENCODABLE: - add_filename(os_helper.TESTFN_UNENCODABLE) - if os_helper.TESTFN_NONASCII: - add_filename(os_helper.TESTFN_NONASCII) + add_filename(support.TESTFN_UNICODE) + if support.TESTFN_UNENCODABLE: + add_filename(support.TESTFN_UNENCODABLE) + if support.TESTFN_NONASCII: + add_filename(support.TESTFN_NONASCII) if not bytesfn: self.skipTest("couldn't create any non-ascii filename") @@ -2369,7 +2097,7 @@ class Pep383Tests(unittest.TestCase): os.mkdir(self.dir) try: for fn in bytesfn: - os_helper.create_empty_file(os.path.join(self.bdir, fn)) + support.create_empty_file(os.path.join(self.bdir, fn)) fn = os.fsdecode(fn) if fn in self.unicodefn: raise ValueError("duplicate filename") @@ -2537,41 +2265,41 @@ class Win32ListdirTests(unittest.TestCase): self.created_paths = [] for i in range(2): dir_name = 'SUB%d' % i - dir_path = os.path.join(os_helper.TESTFN, dir_name) + dir_path = os.path.join(support.TESTFN, dir_name) file_name = 'FILE%d' % i - file_path = os.path.join(os_helper.TESTFN, file_name) + file_path = os.path.join(support.TESTFN, file_name) os.makedirs(dir_path) - with open(file_path, 'w', encoding='utf-8') as f: + with open(file_path, 'w') as f: f.write("I'm %s and proud of it. Blame test_os.\n" % file_path) self.created_paths.extend([dir_name, file_name]) self.created_paths.sort() def tearDown(self): - shutil.rmtree(os_helper.TESTFN) + shutil.rmtree(support.TESTFN) def test_listdir_no_extended_path(self): """Test when the path is not an "extended" path.""" # unicode self.assertEqual( - sorted(os.listdir(os_helper.TESTFN)), + sorted(os.listdir(support.TESTFN)), self.created_paths) # bytes self.assertEqual( - sorted(os.listdir(os.fsencode(os_helper.TESTFN))), + sorted(os.listdir(os.fsencode(support.TESTFN))), [os.fsencode(path) for path in self.created_paths]) def test_listdir_extended_path(self): """Test when the path starts with '\\\\?\\'.""" # See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath # unicode - path = '\\\\?\\' + os.path.abspath(os_helper.TESTFN) + path = '\\\\?\\' + os.path.abspath(support.TESTFN) self.assertEqual( sorted(os.listdir(path)), self.created_paths) # bytes - path = b'\\\\?\\' + os.fsencode(os.path.abspath(os_helper.TESTFN)) + path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) self.assertEqual( sorted(os.listdir(path)), [os.fsencode(path) for path in self.created_paths]) @@ -2614,32 +2342,32 @@ class ReadlinkTests(unittest.TestCase): self.assertRaises(FileNotFoundError, os.readlink, FakePath('missing-link')) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_pathlike(self): os.symlink(self.filelink_target, self.filelink) - self.addCleanup(os_helper.unlink, self.filelink) + self.addCleanup(support.unlink, self.filelink) filelink = FakePath(self.filelink) self.assertPathEqual(os.readlink(filelink), self.filelink_target) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_pathlike_bytes(self): os.symlink(self.filelinkb_target, self.filelinkb) - self.addCleanup(os_helper.unlink, self.filelinkb) + self.addCleanup(support.unlink, self.filelinkb) path = os.readlink(FakePath(self.filelinkb)) self.assertPathEqual(path, self.filelinkb_target) self.assertIsInstance(path, bytes) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_bytes(self): os.symlink(self.filelinkb_target, self.filelinkb) - self.addCleanup(os_helper.unlink, self.filelinkb) + self.addCleanup(support.unlink, self.filelinkb) path = os.readlink(self.filelinkb) self.assertPathEqual(path, self.filelinkb_target) self.assertIsInstance(path, bytes) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -@os_helper.skip_unless_symlink +@support.skip_unless_symlink class Win32SymlinkTests(unittest.TestCase): filelink = 'filelinktest' filelink_target = os.path.abspath(__file__) @@ -2710,10 +2438,10 @@ class Win32SymlinkTests(unittest.TestCase): self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) def test_12084(self): - level1 = os.path.abspath(os_helper.TESTFN) + level1 = os.path.abspath(support.TESTFN) level2 = os.path.join(level1, "level2") level3 = os.path.join(level2, "level3") - self.addCleanup(os_helper.rmtree, level1) + self.addCleanup(support.rmtree, level1) os.mkdir(level1) os.mkdir(level2) @@ -2899,7 +2627,7 @@ class Win32NtTests(unittest.TestCase): self.assertEqual(0, handle_delta) -@os_helper.skip_unless_symlink +@support.skip_unless_symlink class NonLocalSymlinkTests(unittest.TestCase): def setUp(self): @@ -2948,7 +2676,6 @@ class FSEncodingTests(unittest.TestCase): class DeviceEncodingTests(unittest.TestCase): - # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'device_encoding') @unittest.expectedFailure def test_bad_fd(self): @@ -2974,7 +2701,7 @@ class PidTests(unittest.TestCase): # We are the parent of our subprocess self.assertEqual(int(stdout), os.getpid()) - def check_waitpid(self, code, exitcode, callback=None): + def check_waitpid(self, code, exitcode): if sys.platform == 'win32': # On Windows, os.spawnv() simply joins arguments with spaces: # arguments need to be quoted @@ -2983,13 +2710,12 @@ class PidTests(unittest.TestCase): args = [sys.executable, '-c', code] pid = os.spawnv(os.P_NOWAIT, sys.executable, args) - if callback is not None: - callback(pid) - - # don't use support.wait_process() to test directly os.waitpid() - # and os.waitstatus_to_exitcode() pid2, status = os.waitpid(pid, 0) - self.assertEqual(os.waitstatus_to_exitcode(status), exitcode) + if sys.platform == 'win32': + self.assertEqual(status, exitcode << 8) + else: + self.assertTrue(os.WIFEXITED(status), status) + self.assertEqual(os.WEXITSTATUS(status), exitcode) self.assertEqual(pid2, pid) # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') @@ -2999,55 +2725,25 @@ class PidTests(unittest.TestCase): # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') @unittest.expectedFailure - def test_waitstatus_to_exitcode(self): + def test_waitpid_exitcode(self): exitcode = 23 code = f'import sys; sys.exit({exitcode})' self.check_waitpid(code, exitcode=exitcode) - with self.assertRaises(TypeError): - os.waitstatus_to_exitcode(0.0) - @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') def test_waitpid_windows(self): - # bpo-40138: test os.waitpid() and os.waitstatus_to_exitcode() - # with exit code larger than INT_MAX. + # bpo-40138: test os.waitpid() with exit code larger than INT_MAX. STATUS_CONTROL_C_EXIT = 0xC000013A code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})' self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT) - @unittest.skipUnless(sys.platform == 'win32', 'win32-specific test') - def test_waitstatus_to_exitcode_windows(self): - max_exitcode = 2 ** 32 - 1 - for exitcode in (0, 1, 5, max_exitcode): - self.assertEqual(os.waitstatus_to_exitcode(exitcode << 8), - exitcode) - - # invalid values - with self.assertRaises(ValueError): - os.waitstatus_to_exitcode((max_exitcode + 1) << 8) - with self.assertRaises(OverflowError): - os.waitstatus_to_exitcode(-1) - - # TODO: RUSTPYTHON (AttributeError: module 'os' has no attribute 'spawnv') - @unittest.expectedFailure - # Skip the test on Windows - @unittest.skipUnless(hasattr(signal, 'SIGKILL'), 'need signal.SIGKILL') - def test_waitstatus_to_exitcode_kill(self): - code = f'import time; time.sleep({support.LONG_TIMEOUT})' - signum = signal.SIGKILL - - def kill_process(pid): - os.kill(pid, signum) - - self.check_waitpid(code, exitcode=-signum, callback=kill_process) - class SpawnTests(unittest.TestCase): def create_args(self, *, with_env=False, use_bytes=False): self.exitcode = 17 - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) if not with_env: code = 'import sys; sys.exit(%s)' % self.exitcode @@ -3060,7 +2756,7 @@ class SpawnTests(unittest.TestCase): code = ('import sys, os; magic = os.environ[%r]; sys.exit(%s)' % (self.key, self.exitcode)) - with open(filename, "w", encoding="utf-8") as fp: + with open(filename, "w") as fp: fp.write(code) args = [sys.executable, filename] @@ -3101,10 +2797,6 @@ class SpawnTests(unittest.TestCase): exitcode = os.spawnv(os.P_WAIT, args[0], args) self.assertEqual(exitcode, self.exitcode) - # Test for PyUnicode_FSConverter() - exitcode = os.spawnv(os.P_WAIT, FakePath(args[0]), args) - self.assertEqual(exitcode, self.exitcode) - @requires_os_func('spawnve') def test_spawnve(self): args = self.create_args(with_env=True) @@ -3127,7 +2819,14 @@ class SpawnTests(unittest.TestCase): def test_nowait(self): args = self.create_args() pid = os.spawnv(os.P_NOWAIT, args[0], args) - support.wait_process(pid, exitcode=self.exitcode) + result = os.waitpid(pid, 0) + self.assertEqual(result[0], pid) + status = result[1] + if hasattr(os, 'WIFEXITED'): + self.assertTrue(os.WIFEXITED(status)) + self.assertEqual(os.WEXITSTATUS(status), self.exitcode) + else: + self.assertEqual(status, self.exitcode << 8) @requires_os_func('spawnve') def test_spawnve_bytes(self): @@ -3198,9 +2897,9 @@ class SpawnTests(unittest.TestCase): self.assertEqual(exitcode, 127) # equal character in the environment variable value - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) - with open(filename, "w", encoding="utf-8") as fp: + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + with open(filename, "w") as fp: fp.write('import sys, os\n' 'if os.getenv("FRUIT") != "orange=lemon":\n' ' raise AssertionError') @@ -3354,12 +3053,12 @@ class TestSendfile(unittest.TestCase): @classmethod def setUpClass(cls): cls.key = support.threading_setup() - create_file(os_helper.TESTFN, cls.DATA) + create_file(support.TESTFN, cls.DATA) @classmethod def tearDownClass(cls): support.threading_cleanup(*cls.key) - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def setUp(self): self.server = SendfileTestServer((support.HOST, 0)) @@ -3370,7 +3069,7 @@ class TestSendfile(unittest.TestCase): # synchronize by waiting for "220 ready" response self.client.recv(1024) self.sockno = self.client.fileno() - self.file = open(os_helper.TESTFN, 'rb') + self.file = open(support.TESTFN, 'rb') self.fileno = self.file.fileno() def tearDown(self): @@ -3502,10 +3201,10 @@ class TestSendfile(unittest.TestCase): @requires_headers_trailers def test_trailers(self): - TESTFN2 = os_helper.TESTFN + "2" + TESTFN2 = support.TESTFN + "2" file_data = b"abcdef" - self.addCleanup(os_helper.unlink, TESTFN2) + self.addCleanup(support.unlink, TESTFN2) create_file(TESTFN2, file_data) with open(TESTFN2, 'rb') as f: @@ -3551,13 +3250,13 @@ def supports_extended_attributes(): return False try: - with open(os_helper.TESTFN, "xb", 0) as fp: + with open(support.TESTFN, "xb", 0) as fp: try: os.setxattr(fp.fileno(), b"user.test", b"") except OSError: return False finally: - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) return True @@ -3569,8 +3268,8 @@ def supports_extended_attributes(): class ExtendedAttributeTests(unittest.TestCase): def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwargs): - fn = os_helper.TESTFN - self.addCleanup(os_helper.unlink, fn) + fn = support.TESTFN + self.addCleanup(support.unlink, fn) create_file(fn) with self.assertRaises(OSError) as cm: @@ -3618,10 +3317,10 @@ class ExtendedAttributeTests(unittest.TestCase): def _check_xattrs(self, *args, **kwargs): self._check_xattrs_str(str, *args, **kwargs) - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) self._check_xattrs_str(os.fsencode, *args, **kwargs) - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_simple(self): self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, @@ -3675,11 +3374,7 @@ class TermsizeTests(unittest.TestCase): should work too. """ try: - size = ( - subprocess.check_output( - ["stty", "size"], stderr=subprocess.DEVNULL, text=True - ).split() - ) + size = subprocess.check_output(['stty', 'size']).decode().split() except (FileNotFoundError, subprocess.CalledProcessError, PermissionError): self.skipTest("stty invocation failed") @@ -3713,89 +3408,6 @@ class MemfdCreateTests(unittest.TestCase): self.assertFalse(os.get_inheritable(fd2)) -@unittest.skipUnless(hasattr(os, 'eventfd'), 'requires os.eventfd') -@support.requires_linux_version(2, 6, 30) -class EventfdTests(unittest.TestCase): - def test_eventfd_initval(self): - def pack(value): - """Pack as native uint64_t - """ - return struct.pack("@Q", value) - size = 8 # read/write 8 bytes - initval = 42 - fd = os.eventfd(initval) - self.assertNotEqual(fd, -1) - self.addCleanup(os.close, fd) - self.assertFalse(os.get_inheritable(fd)) - - # test with raw read/write - res = os.read(fd, size) - self.assertEqual(res, pack(initval)) - - os.write(fd, pack(23)) - res = os.read(fd, size) - self.assertEqual(res, pack(23)) - - os.write(fd, pack(40)) - os.write(fd, pack(2)) - res = os.read(fd, size) - self.assertEqual(res, pack(42)) - - # test with eventfd_read/eventfd_write - os.eventfd_write(fd, 20) - os.eventfd_write(fd, 3) - res = os.eventfd_read(fd) - self.assertEqual(res, 23) - - def test_eventfd_semaphore(self): - initval = 2 - flags = os.EFD_CLOEXEC | os.EFD_SEMAPHORE | os.EFD_NONBLOCK - fd = os.eventfd(initval, flags) - self.assertNotEqual(fd, -1) - self.addCleanup(os.close, fd) - - # semaphore starts has initval 2, two reads return '1' - res = os.eventfd_read(fd) - self.assertEqual(res, 1) - res = os.eventfd_read(fd) - self.assertEqual(res, 1) - # third read would block - with self.assertRaises(BlockingIOError): - os.eventfd_read(fd) - with self.assertRaises(BlockingIOError): - os.read(fd, 8) - - # increase semaphore counter, read one - os.eventfd_write(fd, 1) - res = os.eventfd_read(fd) - self.assertEqual(res, 1) - # next read would block, too - with self.assertRaises(BlockingIOError): - os.eventfd_read(fd) - - def test_eventfd_select(self): - flags = os.EFD_CLOEXEC | os.EFD_NONBLOCK - fd = os.eventfd(0, flags) - self.assertNotEqual(fd, -1) - self.addCleanup(os.close, fd) - - # counter is zero, only writeable - rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) - self.assertEqual((rfd, wfd, xfd), ([], [fd], [])) - - # counter is non-zero, read and writeable - os.eventfd_write(fd, 23) - rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) - self.assertEqual((rfd, wfd, xfd), ([fd], [fd], [])) - self.assertEqual(os.eventfd_read(fd), 23) - - # counter at max, only readable - os.eventfd_write(fd, (2**64) - 2) - rfd, wfd, xfd = select.select([fd], [fd], [fd], 0) - self.assertEqual((rfd, wfd, xfd), ([fd], [], [])) - os.eventfd_read(fd) - - class OSErrorTests(unittest.TestCase): def setUp(self): class Str(str): @@ -3803,16 +3415,16 @@ class OSErrorTests(unittest.TestCase): self.bytes_filenames = [] self.unicode_filenames = [] - if os_helper.TESTFN_UNENCODABLE is not None: - decoded = os_helper.TESTFN_UNENCODABLE + if support.TESTFN_UNENCODABLE is not None: + decoded = support.TESTFN_UNENCODABLE else: - decoded = os_helper.TESTFN + decoded = support.TESTFN self.unicode_filenames.append(decoded) self.unicode_filenames.append(Str(decoded)) - if os_helper.TESTFN_UNDECODABLE is not None: - encoded = os_helper.TESTFN_UNDECODABLE + if support.TESTFN_UNDECODABLE is not None: + encoded = support.TESTFN_UNDECODABLE else: - encoded = os.fsencode(os_helper.TESTFN) + encoded = os.fsencode(support.TESTFN) self.bytes_filenames.append(encoded) self.bytes_filenames.append(bytearray(encoded)) self.bytes_filenames.append(memoryview(encoded)) @@ -3934,33 +3546,6 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC, 0) - @unittest.skipUnless(hasattr(os, 'O_PATH'), "need os.O_PATH") - def test_get_set_inheritable_o_path(self): - fd = os.open(__file__, os.O_PATH) - self.addCleanup(os.close, fd) - self.assertEqual(os.get_inheritable(fd), False) - - os.set_inheritable(fd, True) - self.assertEqual(os.get_inheritable(fd), True) - - os.set_inheritable(fd, False) - self.assertEqual(os.get_inheritable(fd), False) - - def test_get_set_inheritable_badf(self): - fd = os_helper.make_bad_fd() - - with self.assertRaises(OSError) as ctx: - os.get_inheritable(fd) - self.assertEqual(ctx.exception.errno, errno.EBADF) - - with self.assertRaises(OSError) as ctx: - os.set_inheritable(fd, True) - self.assertEqual(ctx.exception.errno, errno.EBADF) - - with self.assertRaises(OSError) as ctx: - os.set_inheritable(fd, False) - self.assertEqual(ctx.exception.errno, errno.EBADF) - def test_open(self): fd = os.open(__file__, os.O_RDONLY) self.addCleanup(os.close, fd) @@ -4029,24 +3614,22 @@ class PathTConverterTests(unittest.TestCase): # function, cleanup function) functions = [ ('stat', True, (), None), - ('lstat', False, (), None), + ('lstat', True, (), None), ('access', False, (os.F_OK,), None), ('chflags', False, (0,), None), ('lchflags', False, (0,), None), ('open', False, (0,), getattr(os, 'close', None)), ] - # TODO: RUSTPYTHON (AssertionError: TypeError not raised) - @unittest.expectedFailure def test_path_t_converter(self): - str_filename = os_helper.TESTFN + str_filename = support.TESTFN if os.name == 'nt': bytes_fspath = bytes_filename = None else: - bytes_filename = os.fsencode(os_helper.TESTFN) + bytes_filename = support.TESTFN.encode('ascii') bytes_fspath = FakePath(bytes_filename) fd = os.open(FakePath(str_filename), os.O_WRONLY|os.O_CREAT) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) self.addCleanup(os.close, fd) int_fspath = FakePath(fd) @@ -4114,35 +3697,13 @@ class ExportsTests(unittest.TestCase): self.assertIn('walk', os.__all__) -class TestDirEntry(unittest.TestCase): - def setUp(self): - self.path = os.path.realpath(os_helper.TESTFN) - self.addCleanup(os_helper.rmtree, self.path) - os.mkdir(self.path) - - # TODO: RUSTPYTHON (AssertionError: TypeError not raised by DirEntry) - @unittest.expectedFailure - def test_uninstantiable(self): - self.assertRaises(TypeError, os.DirEntry) - - # TODO: RUSTPYTHON (pickle.PicklingError: Can't pickle : it's not found as _os.DirEntry) - @unittest.expectedFailure - def test_unpickable(self): - filename = create_file(os.path.join(self.path, "file.txt"), b'python') - entry = [entry for entry in os.scandir(self.path)].pop() - self.assertIsInstance(entry, os.DirEntry) - self.assertEqual(entry.name, "file.txt") - import pickle - self.assertRaises(TypeError, pickle.dumps, entry, filename) - - class TestScandir(unittest.TestCase): check_no_resource_warning = support.check_no_resource_warning def setUp(self): - self.path = os.path.realpath(os_helper.TESTFN) + self.path = os.path.realpath(support.TESTFN) self.bytes_path = os.fsencode(self.path) - self.addCleanup(os_helper.rmtree, self.path) + self.addCleanup(support.rmtree, self.path) os.mkdir(self.path) def create_file(self, name="file.txt"): @@ -4170,20 +3731,6 @@ class TestScandir(unittest.TestCase): else: self.assertEqual(stat1, stat2) - # TODO: RUSTPPYTHON (AssertionError: TypeError not raised by ScandirIter) - @unittest.expectedFailure - def test_uninstantiable(self): - scandir_iter = os.scandir(self.path) - self.assertRaises(TypeError, type(scandir_iter)) - scandir_iter.close() - - def test_unpickable(self): - filename = self.create_file("file.txt") - scandir_iter = os.scandir(self.path) - import pickle - self.assertRaises(TypeError, pickle.dumps, scandir_iter, filename) - scandir_iter.close() - def check_entry(self, entry, name, is_dir, is_file, is_symlink): self.assertIsInstance(entry, os.DirEntry) self.assertEqual(entry.name, name) @@ -4215,7 +3762,7 @@ class TestScandir(unittest.TestCase): @unittest.skipIf(sys.platform == "linux", "TODO: RUSTPYTHON, flaky test") def test_attributes(self): link = hasattr(os, 'link') - symlink = os_helper.can_symlink() + symlink = support.can_symlink() dirname = os.path.join(self.path, "dir") os.mkdir(dirname) @@ -4339,7 +3886,7 @@ class TestScandir(unittest.TestCase): self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False) def test_broken_symlink(self): - if not os_helper.can_symlink(): + if not support.can_symlink(): return self.skipTest('cannot create symbolic link') filename = self.create_file("file.txt") @@ -4389,15 +3936,14 @@ class TestScandir(unittest.TestCase): self.assertIs(type(entry.name), bytes) self.assertIs(type(entry.path), bytes) - # TODO: RUSTPYTHON - @unittest.expectedFailure - @unittest.skipUnless(os.listdir in os.supports_fd, - 'fd support for listdir required for this test.') + # TODO: RUSTPYTHON (scandir needs to have supports_fd) + @unittest.skipUnless(os.scandir in os.supports_fd, + 'fd support for scandir required for this test.') def test_fd(self): self.assertIn(os.scandir, os.supports_fd) self.create_file('file.txt') expected_names = ['file.txt'] - if os_helper.can_symlink(): + if support.can_symlink(): os.symlink('file.txt', os.path.join(self.path, 'link')) expected_names.append('link') @@ -4545,11 +4091,6 @@ class TestPEP519(unittest.TestCase): self.assertFalse(issubclass(FakePath, A)) self.assertTrue(issubclass(FakePath, os.PathLike)) - # TODO: RUSTPYTHON (TypeError: 'ABCMeta' object is not subscriptable) - @unittest.expectedFailure - def test_pathlike_class_getitem(self): - self.assertIsInstance(os.PathLike[bytes], types.GenericAlias) - class TimesTests(unittest.TestCase): def test_times(self): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index ea723c8be..14192e137 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -12,8 +12,7 @@ import unittest from unittest import mock from test import support -from test.support import os_helper -from test.support.os_helper import TESTFN, FakePath +from test.support import TESTFN, FakePath try: import grp, pwd @@ -1228,7 +1227,7 @@ class _BasePathTest(object): def setUp(self): def cleanup(): os.chmod(join('dirE'), 0o777) - os_helper.rmtree(BASE) + support.rmtree(BASE) self.addCleanup(cleanup) os.mkdir(BASE) os.mkdir(join('dirA')) @@ -1245,7 +1244,7 @@ class _BasePathTest(object): with open(join('dirC', 'dirD', 'fileD'), 'wb') as f: f.write(b"this is file D\n") os.chmod(join('dirE'), 0) - if os_helper.can_symlink(): + if support.can_symlink(): # Relative symlinks. os.symlink('fileA', join('linkA')) os.symlink('non-existing', join('brokenLink')) @@ -1339,7 +1338,7 @@ class _BasePathTest(object): self.assertIs(True, (p / 'dirA').exists()) self.assertIs(True, (p / 'fileA').exists()) self.assertIs(False, (p / 'fileA' / 'bah').exists()) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertIs(True, (p / 'linkA').exists()) self.assertIs(True, (p / 'linkB').exists()) self.assertIs(True, (p / 'linkB' / 'fileB').exists()) @@ -1384,11 +1383,11 @@ class _BasePathTest(object): it = p.iterdir() paths = set(it) expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA'] - if os_helper.can_symlink(): + if support.can_symlink(): expected += ['linkA', 'linkB', 'brokenLink'] self.assertEqual(paths, { P(BASE, q) for q in expected }) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_iterdir_symlink(self): # __iter__ on a symlink to a directory. P = self.cls @@ -1417,16 +1416,16 @@ class _BasePathTest(object): _check(it, ["fileA"]) _check(p.glob("fileB"), []) _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"]) - if not os_helper.can_symlink(): + if not support.can_symlink(): _check(p.glob("*A"), ['dirA', 'fileA']) else: _check(p.glob("*A"), ['dirA', 'fileA', 'linkA']) - if not os_helper.can_symlink(): + if not support.can_symlink(): _check(p.glob("*B/*"), ['dirB/fileB']) else: _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD', 'linkB/fileB', 'linkB/linkD']) - if not os_helper.can_symlink(): + if not support.can_symlink(): _check(p.glob("*/fileB"), ['dirB/fileB']) else: _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB']) @@ -1441,7 +1440,7 @@ class _BasePathTest(object): _check(it, ["fileA"]) _check(p.rglob("fileB"), ["dirB/fileB"]) _check(p.rglob("*/fileA"), []) - if not os_helper.can_symlink(): + if not support.can_symlink(): _check(p.rglob("*/fileB"), ["dirB/fileB"]) else: _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB", @@ -1452,7 +1451,7 @@ class _BasePathTest(object): _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"]) _check(p.rglob("*/*"), ["dirC/dirD/fileD"]) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_rglob_symlink_loop(self): # Don't get fooled by symlink loops (Issue #26012). P = self.cls @@ -1485,7 +1484,7 @@ class _BasePathTest(object): # This can be used to check both relative and absolute resolutions. _check_resolve_relative = _check_resolve_absolute = _check_resolve - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_resolve_common(self): P = self.cls p = P(BASE, 'foo') @@ -1526,7 +1525,7 @@ class _BasePathTest(object): self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) # Now create absolute symlinks. d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) - self.addCleanup(os_helper.rmtree, d) + self.addCleanup(support.rmtree, d) os.symlink(os.path.join(d), join('dirA', 'linkX')) os.symlink(join('dirB'), os.path.join(d, 'linkY')) p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') @@ -1545,7 +1544,7 @@ class _BasePathTest(object): # resolves to 'dirB/..' first before resolving to parent of dirB. self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_resolve_dot(self): # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks p = self.cls(BASE) @@ -1597,7 +1596,7 @@ class _BasePathTest(object): self.addCleanup(p.chmod, st.st_mode) self.assertNotEqual(p.stat(), st) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_lstat(self): p = self.cls(BASE)/ 'linkA' st = p.stat() @@ -1858,7 +1857,7 @@ class _BasePathTest(object): self.assertNotIn(str(p12), concurrently_created) self.assertTrue(p.exists()) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_symlink_to(self): P = self.cls(BASE) target = P / 'fileA' @@ -1888,7 +1887,7 @@ class _BasePathTest(object): self.assertFalse((P / 'fileA').is_dir()) self.assertFalse((P / 'non-existing').is_dir()) self.assertFalse((P / 'fileA' / 'bah').is_dir()) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertFalse((P / 'linkA').is_dir()) self.assertTrue((P / 'linkB').is_dir()) self.assertFalse((P/ 'brokenLink').is_dir(), False) @@ -1901,7 +1900,7 @@ class _BasePathTest(object): self.assertFalse((P / 'dirA').is_file()) self.assertFalse((P / 'non-existing').is_file()) self.assertFalse((P / 'fileA' / 'bah').is_file()) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertTrue((P / 'linkA').is_file()) self.assertFalse((P / 'linkB').is_file()) self.assertFalse((P/ 'brokenLink').is_file()) @@ -1917,7 +1916,7 @@ class _BasePathTest(object): self.assertFalse((P / 'non-existing').is_mount()) self.assertFalse((P / 'fileA' / 'bah').is_mount()) self.assertTrue(R.is_mount()) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertFalse((P / 'linkA').is_mount()) self.assertIs(self.cls('/\udfff').is_mount(), False) self.assertIs(self.cls('/\x00').is_mount(), False) @@ -1928,13 +1927,13 @@ class _BasePathTest(object): self.assertFalse((P / 'dirA').is_symlink()) self.assertFalse((P / 'non-existing').is_symlink()) self.assertFalse((P / 'fileA' / 'bah').is_symlink()) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertTrue((P / 'linkA').is_symlink()) self.assertTrue((P / 'linkB').is_symlink()) self.assertTrue((P/ 'brokenLink').is_symlink()) self.assertIs((P / 'fileA\udfff').is_file(), False) self.assertIs((P / 'fileA\x00').is_file(), False) - if os_helper.can_symlink(): + if support.can_symlink(): self.assertIs((P / 'linkA\udfff').is_file(), False) self.assertIs((P / 'linkA\x00').is_file(), False) @@ -2072,15 +2071,15 @@ class _BasePathTest(object): finally: os.chdir(old_path) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_complex_symlinks_absolute(self): self._check_complex_symlinks(BASE) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_complex_symlinks_relative(self): self._check_complex_symlinks('.') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_complex_symlinks_relative_dot_dot(self): self._check_complex_symlinks(os.path.join('dirA', '..')) @@ -2143,7 +2142,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): st = os.stat(join('masked_new_file')) self.assertEqual(stat.S_IMODE(st.st_mode), 0o750) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_resolve_loop(self): # Loops with relative symlinks. os.symlink('linkX/inside', join('linkX')) @@ -2168,7 +2167,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): P = self.cls p = P(BASE) given = set(p.glob("FILEa")) - expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given + expect = set() if not support.fs_is_case_insensitive(BASE) else given self.assertEqual(given, expect) self.assertEqual(set(p.glob("FILEa*")), set()) @@ -2176,7 +2175,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): P = self.cls p = P(BASE, "dirC") given = set(p.rglob("FILEd")) - expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given + expect = set() if not support.fs_is_case_insensitive(BASE) else given self.assertEqual(given, expect) self.assertEqual(set(p.rglob("FILEd*")), set()) @@ -2207,7 +2206,7 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): p6 = P('') p7 = P('~fakeuser/Documents') - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.pop('HOME', None) self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') @@ -2269,7 +2268,7 @@ class WindowsPathTest(_BasePathTest, unittest.TestCase): def test_expanduser(self): P = self.cls - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.pop('HOME', None) env.pop('USERPROFILE', None) env.pop('HOMEPATH', None) diff --git a/Lib/test/test_pkgimport.py b/Lib/test/test_pkgimport.py index ee3b28578..5d9a45111 100644 --- a/Lib/test/test_pkgimport.py +++ b/Lib/test/test_pkgimport.py @@ -7,7 +7,7 @@ import tempfile import unittest from importlib.util import cache_from_source -from test.support.os_helper import create_empty_file +from test.support import create_empty_file class TestImport(unittest.TestCase): diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index fe3b7dd71..7a5146216 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -8,7 +8,6 @@ import unittest from unittest import mock from test import support -from test.support import os_helper class PlatformTest(unittest.TestCase): def clear_caches(self): @@ -19,7 +18,7 @@ class PlatformTest(unittest.TestCase): def test_architecture(self): res = platform.architecture() - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_architecture_via_symlink(self): # issue3762 # On Windows, the EXE needs to know where pythonXY.dll and *.pyd is at # so we add the directory to the path, PYTHONHOME and PYTHONPATH. @@ -46,7 +45,7 @@ class PlatformTest(unittest.TestCase): return r real = os.path.realpath(sys.executable) - link = os.path.abspath(os_helper.TESTFN) + link = os.path.abspath(support.TESTFN) os.symlink(real, link) try: self.assertEqual(get(real), get(link, env=env)) @@ -199,7 +198,7 @@ class PlatformTest(unittest.TestCase): # using it, per # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx try: - with os_helper.EnvironmentVarGuard() as environ: + with support.EnvironmentVarGuard() as environ: if 'PROCESSOR_ARCHITEW6432' in environ: del environ['PROCESSOR_ARCHITEW6432'] environ['PROCESSOR_ARCHITECTURE'] = 'foo' @@ -286,8 +285,8 @@ class PlatformTest(unittest.TestCase): executable = sys.executable platform.libc_ver(executable) - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) with mock.patch('os.confstr', create=True, return_value='mock 1.0'): # test os.confstr() code path diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index b8fb4ceb7..e13fe4304 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1,7 +1,6 @@ "Test posix functions" from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok # Skip these tests if there is no posix module. @@ -21,7 +20,7 @@ import warnings import textwrap _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), - os_helper.TESTFN + '-dummy-symlink') + support.TESTFN + '-dummy-symlink') requires_32b = unittest.skipUnless(sys.maxsize < 2**32, 'test is only meaningful on 32-bit builds') @@ -43,9 +42,9 @@ class PosixTester(unittest.TestCase): def setUp(self): # create empty file - fp = open(os_helper.TESTFN, 'w+') + fp = open(support.TESTFN, 'w+') fp.close() - self.teardown_files = [ os_helper.TESTFN ] + self.teardown_files = [ support.TESTFN ] self._warnings_manager = support.check_warnings() self._warnings_manager.__enter__() warnings.filterwarnings('ignore', '.* potential security risk .*', @@ -53,7 +52,7 @@ class PosixTester(unittest.TestCase): def tearDown(self): for teardown_file in self.teardown_files: - os_helper.unlink(teardown_file) + support.unlink(teardown_file) self._warnings_manager.__exit__(None, None, None) def testNoArgFunctions(self): @@ -154,7 +153,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'fstatvfs'), 'test needs posix.fstatvfs()') def test_fstatvfs(self): - fp = open(os_helper.TESTFN) + fp = open(support.TESTFN) try: self.assertTrue(posix.fstatvfs(fp.fileno())) self.assertTrue(posix.statvfs(fp.fileno())) @@ -164,7 +163,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'ftruncate'), 'test needs posix.ftruncate()') def test_ftruncate(self): - fp = open(os_helper.TESTFN, 'w+') + fp = open(support.TESTFN, 'w+') try: # we need to have some data to truncate fp.write('test') @@ -175,10 +174,10 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'truncate'), "test needs posix.truncate()") def test_truncate(self): - with open(os_helper.TESTFN, 'w') as fp: + with open(support.TESTFN, 'w') as fp: fp.write('test') fp.flush() - posix.truncate(os_helper.TESTFN, 0) + posix.truncate(support.TESTFN, 0) @unittest.skipUnless(getattr(os, 'execve', None) in os.supports_fd, "test needs execve() to support the fd parameter") @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") @@ -269,7 +268,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'lockf'), "test needs posix.lockf()") def test_lockf(self): - fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -281,7 +280,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pread'), "test needs posix.pread()") def test_pread(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -293,7 +292,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") def test_preadv(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3t5t6t6t8') buf = [bytearray(i) for i in [5, 3, 2]] @@ -305,7 +304,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") @unittest.skipUnless(hasattr(posix, 'RWF_HIPRI'), "test needs posix.RWF_HIPRI") def test_preadv_flags(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3t5t6t6t8') buf = [bytearray(i) for i in [5, 3, 2]] @@ -327,7 +326,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") @requires_32b def test_preadv_overflow_32bits(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: buf = [bytearray(2**16)] * 2**15 with self.assertRaises(OSError) as cm: @@ -339,7 +338,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()") def test_pwrite(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test') os.lseek(fd, 0, os.SEEK_SET) @@ -350,7 +349,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") def test_pwritev(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b"xx") os.lseek(fd, 0, os.SEEK_SET) @@ -365,7 +364,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") @unittest.skipUnless(hasattr(posix, 'os.RWF_SYNC'), "test needs os.RWF_SYNC") def test_pwritev_flags(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd,b"xx") os.lseek(fd, 0, os.SEEK_SET) @@ -380,7 +379,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()") @requires_32b def test_pwritev_overflow_32bits(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: with self.assertRaises(OSError) as cm: os.pwritev(fd, [b"x" * 2**16] * 2**15, 0) @@ -391,7 +390,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'posix_fallocate'), "test needs posix.posix_fallocate()") def test_posix_fallocate(self): - fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) try: posix.posix_fallocate(fd, 0, 10) except OSError as inst: @@ -420,7 +419,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'posix_fadvise'), "test needs posix.posix_fadvise()") def test_posix_fadvise(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) try: posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) finally: @@ -438,7 +437,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.utime in os.supports_fd, "test needs fd support in os.utime") def test_utime_with_fd(self): now = time.time() - fd = os.open(os_helper.TESTFN, os.O_RDONLY) + fd = os.open(support.TESTFN, os.O_RDONLY) try: posix.utime(fd) posix.utime(fd, None) @@ -459,17 +458,17 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.utime in os.supports_follow_symlinks, "test needs follow_symlinks support in os.utime") def test_utime_nofollow_symlinks(self): now = time.time() - posix.utime(os_helper.TESTFN, None, follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None), follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None), follow_symlinks=False) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now), follow_symlinks=False) - posix.utime(os_helper.TESTFN, (int(now), int(now)), follow_symlinks=False) - posix.utime(os_helper.TESTFN, (now, now), follow_symlinks=False) - posix.utime(os_helper.TESTFN, follow_symlinks=False) + posix.utime(support.TESTFN, None, follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), follow_symlinks=False) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), follow_symlinks=False) + posix.utime(support.TESTFN, (int(now), int(now)), follow_symlinks=False) + posix.utime(support.TESTFN, (now, now), follow_symlinks=False) + posix.utime(support.TESTFN, follow_symlinks=False) @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") def test_writev(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: n = os.writev(fd, (b'test1', b'tt2', b't3')) self.assertEqual(n, 10) @@ -492,7 +491,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") @requires_32b def test_writev_overflow_32bits(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: with self.assertRaises(OSError) as cm: os.writev(fd, [b"x" * 2**16] * 2**15) @@ -502,7 +501,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") def test_readv(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: os.write(fd, b'test1tt2t3') os.lseek(fd, 0, os.SEEK_SET) @@ -525,7 +524,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") @requires_32b def test_readv_overflow_32bits(self): - fd = os.open(os_helper.TESTFN, os.O_RDWR | os.O_CREAT) + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) try: buf = [bytearray(2**16)] * 2**15 with self.assertRaises(OSError) as cm: @@ -538,7 +537,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'dup'), 'test needs posix.dup()') def test_dup(self): - fp = open(os_helper.TESTFN) + fp = open(support.TESTFN) try: fd = posix.dup(fp.fileno()) self.assertIsInstance(fd, int) @@ -555,8 +554,8 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'dup2'), 'test needs posix.dup2()') def test_dup2(self): - fp1 = open(os_helper.TESTFN) - fp2 = open(os_helper.TESTFN) + fp1 = open(support.TESTFN) + fp2 = open(support.TESTFN) try: posix.dup2(fp1.fileno(), fp2.fileno()) finally: @@ -566,40 +565,40 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'O_CLOEXEC'), "needs os.O_CLOEXEC") @support.requires_linux_version(2, 6, 23) def test_oscloexec(self): - fd = os.open(os_helper.TESTFN, os.O_RDONLY|os.O_CLOEXEC) + fd = os.open(support.TESTFN, os.O_RDONLY|os.O_CLOEXEC) self.addCleanup(os.close, fd) self.assertFalse(os.get_inheritable(fd)) @unittest.skipUnless(hasattr(posix, 'O_EXLOCK'), 'test needs posix.O_EXLOCK') def test_osexlock(self): - fd = os.open(os_helper.TESTFN, + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, os_helper.TESTFN, + self.assertRaises(OSError, os.open, support.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) if hasattr(posix, "O_SHLOCK"): - fd = os.open(os_helper.TESTFN, + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, os_helper.TESTFN, + self.assertRaises(OSError, os.open, support.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) @unittest.skipUnless(hasattr(posix, 'O_SHLOCK'), 'test needs posix.O_SHLOCK') def test_osshlock(self): - fd1 = os.open(os_helper.TESTFN, + fd1 = os.open(support.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - fd2 = os.open(os_helper.TESTFN, + fd2 = os.open(support.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) os.close(fd2) os.close(fd1) if hasattr(posix, "O_EXLOCK"): - fd = os.open(os_helper.TESTFN, + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, os_helper.TESTFN, + self.assertRaises(OSError, os.open, support.TESTFN, os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) @@ -608,7 +607,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'fstat'), 'test needs posix.fstat()') def test_fstat(self): - fp = open(os_helper.TESTFN) + fp = open(support.TESTFN) try: self.assertTrue(posix.fstat(fp.fileno())) self.assertTrue(posix.stat(fp.fileno())) @@ -622,58 +621,58 @@ class PosixTester(unittest.TestCase): # TODO: RUSTPYTHON: AssertionError: DeprecationWarning not triggered by stat @unittest.expectedFailure def test_stat(self): - self.assertTrue(posix.stat(os_helper.TESTFN)) - self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN))) + self.assertTrue(posix.stat(support.TESTFN)) + self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) self.assertWarnsRegex(DeprecationWarning, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, bytearray(os.fsencode(os_helper.TESTFN))) + posix.stat, bytearray(os.fsencode(support.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', posix.stat, None) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os_helper.TESTFN)) + posix.stat, list(support.TESTFN)) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os.fsencode(os_helper.TESTFN))) + posix.stat, list(os.fsencode(support.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") def test_mkfifo(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) try: - posix.mkfifo(os_helper.TESTFN, stat.S_IRUSR | stat.S_IWUSR) + posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR) except PermissionError as e: self.skipTest('posix.mkfifo(): %s' % e) - self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) @unittest.skipUnless(hasattr(posix, 'mknod') and hasattr(stat, 'S_IFIFO'), "don't have mknod()/S_IFIFO") def test_mknod(self): # Test using mknod() to create a FIFO (the only use specified # by POSIX). - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR try: - posix.mknod(os_helper.TESTFN, mode, 0) + posix.mknod(support.TESTFN, mode, 0) except OSError as e: # Some old systems don't allow unprivileged users to use # mknod(), or only support creating device nodes. self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) else: - self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) # Keyword arguments are also supported - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) try: - posix.mknod(path=os_helper.TESTFN, mode=mode, device=0, + posix.mknod(path=support.TESTFN, mode=mode, device=0, dir_fd=None) except OSError as e: self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) @unittest.skipUnless(hasattr(posix, 'makedev'), 'test needs posix.makedev()') def test_makedev(self): - st = posix.stat(os_helper.TESTFN) + st = posix.stat(support.TESTFN) dev = st.st_dev self.assertIsInstance(dev, int) self.assertGreaterEqual(dev, 0) @@ -762,19 +761,19 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()") def test_chown(self): # raise an OSError if the file does not exist - os.unlink(os_helper.TESTFN) - self.assertRaises(OSError, posix.chown, os_helper.TESTFN, -1, -1) + os.unlink(support.TESTFN) + self.assertRaises(OSError, posix.chown, support.TESTFN, -1, -1) # re-create the file - os_helper.create_empty_file(os_helper.TESTFN) - self._test_all_chown_common(posix.chown, os_helper.TESTFN, posix.stat) + support.create_empty_file(support.TESTFN) + self._test_all_chown_common(posix.chown, support.TESTFN, posix.stat) @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()") def test_fchown(self): - os.unlink(os_helper.TESTFN) + os.unlink(support.TESTFN) # re-create the file - test_file = open(os_helper.TESTFN, 'w') + test_file = open(support.TESTFN, 'w') try: fd = test_file.fileno() self._test_all_chown_common(posix.fchown, fd, @@ -784,29 +783,29 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'lchown'), "test needs os.lchown()") def test_lchown(self): - os.unlink(os_helper.TESTFN) + os.unlink(support.TESTFN) # create a symlink - os.symlink(_DUMMY_SYMLINK, os_helper.TESTFN) - self._test_all_chown_common(posix.lchown, os_helper.TESTFN, + os.symlink(_DUMMY_SYMLINK, support.TESTFN) + self._test_all_chown_common(posix.lchown, support.TESTFN, getattr(posix, 'lstat', None)) @unittest.skipUnless(hasattr(posix, 'chdir'), 'test needs posix.chdir()') def test_chdir(self): posix.chdir(os.curdir) - self.assertRaises(OSError, posix.chdir, os_helper.TESTFN) + self.assertRaises(OSError, posix.chdir, support.TESTFN) def test_listdir(self): - self.assertIn(os_helper.TESTFN, posix.listdir(os.curdir)) + self.assertIn(support.TESTFN, posix.listdir(os.curdir)) def test_listdir_default(self): # When listdir is called without argument, # it's the same as listdir(os.curdir). - self.assertIn(os_helper.TESTFN, posix.listdir()) + self.assertIn(support.TESTFN, posix.listdir()) def test_listdir_bytes(self): # When listdir is called with a bytes object, # the returned strings are of type bytes. - self.assertIn(os.fsencode(os_helper.TESTFN), posix.listdir(b'.')) + self.assertIn(os.fsencode(support.TESTFN), posix.listdir(b'.')) # TODO: RUSTPYTHON: AssertionError: DeprecationWarning not triggered @unittest.expectedFailure @@ -814,7 +813,7 @@ class PosixTester(unittest.TestCase): for cls in bytearray, memoryview: with self.assertWarns(DeprecationWarning): names = posix.listdir(cls(b'.')) - self.assertIn(os.fsencode(os_helper.TESTFN), names) + self.assertIn(os.fsencode(support.TESTFN), names) for name in names: self.assertIs(type(name), bytes) @@ -835,7 +834,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'access'), 'test needs posix.access()') def test_access(self): - self.assertTrue(posix.access(os_helper.TESTFN, os.R_OK)) + self.assertTrue(posix.access(support.TESTFN, os.R_OK)) @unittest.skipUnless(hasattr(posix, 'umask'), 'test needs posix.umask()') def test_umask(self): @@ -894,12 +893,12 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'utime'), 'test needs posix.utime()') def test_utime(self): now = time.time() - posix.utime(os_helper.TESTFN, None) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None)) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None)) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now)) - posix.utime(os_helper.TESTFN, (int(now), int(now))) - posix.utime(os_helper.TESTFN, (now, now)) + posix.utime(support.TESTFN, None) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None)) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None)) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now)) + posix.utime(support.TESTFN, (int(now), int(now))) + posix.utime(support.TESTFN, (now, now)) def _test_chflags_regular_file(self, chflags_func, target_file, **kwargs): st = os.stat(target_file) @@ -927,20 +926,20 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'chflags'), 'test needs os.chflags()') def test_chflags(self): - self._test_chflags_regular_file(posix.chflags, os_helper.TESTFN) + self._test_chflags_regular_file(posix.chflags, support.TESTFN) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_regular_file(self): - self._test_chflags_regular_file(posix.lchflags, os_helper.TESTFN) - self._test_chflags_regular_file(posix.chflags, os_helper.TESTFN, follow_symlinks=False) + self._test_chflags_regular_file(posix.lchflags, support.TESTFN) + self._test_chflags_regular_file(posix.chflags, support.TESTFN, follow_symlinks=False) @unittest.skipUnless(hasattr(posix, 'lchflags'), 'test needs os.lchflags()') def test_lchflags_symlink(self): - testfn_st = os.stat(os_helper.TESTFN) + testfn_st = os.stat(support.TESTFN) self.assertTrue(hasattr(testfn_st, 'st_flags')) - os.symlink(os_helper.TESTFN, _DUMMY_SYMLINK) + os.symlink(support.TESTFN, _DUMMY_SYMLINK) self.teardown_files.append(_DUMMY_SYMLINK) dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) @@ -958,7 +957,7 @@ class PosixTester(unittest.TestCase): msg = 'chflag UF_IMMUTABLE not supported by underlying fs' self.skipTest(msg) try: - new_testfn_st = os.stat(os_helper.TESTFN) + new_testfn_st = os.stat(support.TESTFN) new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK) self.assertEqual(testfn_st.st_flags, new_testfn_st.st_flags) @@ -994,7 +993,7 @@ class PosixTester(unittest.TestCase): def test_getcwd_long_pathnames(self): dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef' curdir = os.getcwd() - base_path = os.path.abspath(os_helper.TESTFN) + '.getcwd' + base_path = os.path.abspath(support.TESTFN) + '.getcwd' try: os.mkdir(base_path) @@ -1024,7 +1023,7 @@ class PosixTester(unittest.TestCase): finally: os.chdir(curdir) - os_helper.rmtree(base_path) + support.rmtree(base_path) @unittest.skipUnless(hasattr(posix, 'getgrouplist'), "test needs posix.getgrouplist()") @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") @@ -1068,53 +1067,53 @@ class PosixTester(unittest.TestCase): def test_access_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - self.assertTrue(posix.access(os_helper.TESTFN, os.R_OK, dir_fd=f)) + self.assertTrue(posix.access(support.TESTFN, os.R_OK, dir_fd=f)) finally: posix.close(f) @unittest.skipUnless(os.chmod in os.supports_dir_fd, "test needs dir_fd support in os.chmod()") def test_chmod_dir_fd(self): - os.chmod(os_helper.TESTFN, stat.S_IRUSR) + os.chmod(support.TESTFN, stat.S_IRUSR) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.chmod(os_helper.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) + posix.chmod(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) - s = posix.stat(os_helper.TESTFN) + s = posix.stat(support.TESTFN) self.assertEqual(s[0] & stat.S_IRWXU, stat.S_IRUSR | stat.S_IWUSR) finally: posix.close(f) @unittest.skipUnless(os.chown in os.supports_dir_fd, "test needs dir_fd support in os.chown()") def test_chown_dir_fd(self): - os_helper.unlink(os_helper.TESTFN) - os_helper.create_empty_file(os_helper.TESTFN) + support.unlink(support.TESTFN) + support.create_empty_file(support.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.chown(os_helper.TESTFN, os.getuid(), os.getgid(), dir_fd=f) + posix.chown(support.TESTFN, os.getuid(), os.getgid(), dir_fd=f) finally: posix.close(f) @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") def test_stat_dir_fd(self): - os_helper.unlink(os_helper.TESTFN) - with open(os_helper.TESTFN, 'w') as outfile: + support.unlink(support.TESTFN) + with open(support.TESTFN, 'w') as outfile: outfile.write("testline\n") f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - s1 = posix.stat(os_helper.TESTFN) - s2 = posix.stat(os_helper.TESTFN, dir_fd=f) + s1 = posix.stat(support.TESTFN) + s2 = posix.stat(support.TESTFN, dir_fd=f) self.assertEqual(s1, s2) - s2 = posix.stat(os_helper.TESTFN, dir_fd=None) + s2 = posix.stat(support.TESTFN, dir_fd=None) self.assertEqual(s1, s2) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, os_helper.TESTFN, dir_fd=posix.getcwd()) + posix.stat, support.TESTFN, dir_fd=posix.getcwd()) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, os_helper.TESTFN, dir_fd=float(f)) + posix.stat, support.TESTFN, dir_fd=float(f)) self.assertRaises(OverflowError, - posix.stat, os_helper.TESTFN, dir_fd=10**20) + posix.stat, support.TESTFN, dir_fd=10**20) finally: posix.close(f) @@ -1123,24 +1122,24 @@ class PosixTester(unittest.TestCase): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: now = time.time() - posix.utime(os_helper.TESTFN, None, dir_fd=f) - posix.utime(os_helper.TESTFN, dir_fd=f) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, now, dir_fd=f) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, None), dir_fd=f) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, None), dir_fd=f) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (None, now), dir_fd=f) - self.assertRaises(TypeError, posix.utime, os_helper.TESTFN, (now, "x"), dir_fd=f) - posix.utime(os_helper.TESTFN, (int(now), int(now)), dir_fd=f) - posix.utime(os_helper.TESTFN, (now, now), dir_fd=f) - posix.utime(os_helper.TESTFN, + posix.utime(support.TESTFN, None, dir_fd=f) + posix.utime(support.TESTFN, dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, now, dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now), dir_fd=f) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, "x"), dir_fd=f) + posix.utime(support.TESTFN, (int(now), int(now)), dir_fd=f) + posix.utime(support.TESTFN, (now, now), dir_fd=f) + posix.utime(support.TESTFN, (int(now), int((now - int(now)) * 1e9)), dir_fd=f) - posix.utime(os_helper.TESTFN, dir_fd=f, + posix.utime(support.TESTFN, dir_fd=f, times=(int(now), int((now - int(now)) * 1e9))) # try dir_fd and follow_symlinks together if os.utime in os.supports_follow_symlinks: try: - posix.utime(os_helper.TESTFN, follow_symlinks=False, dir_fd=f) + posix.utime(support.TESTFN, follow_symlinks=False, dir_fd=f) except ValueError: # whoops! using both together not supported on this platform. pass @@ -1152,53 +1151,53 @@ class PosixTester(unittest.TestCase): def test_link_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.link(os_helper.TESTFN, os_helper.TESTFN + 'link', src_dir_fd=f, dst_dir_fd=f) + posix.link(support.TESTFN, support.TESTFN + 'link', src_dir_fd=f, dst_dir_fd=f) except PermissionError as e: self.skipTest('posix.link(): %s' % e) else: # should have same inodes - self.assertEqual(posix.stat(os_helper.TESTFN)[1], - posix.stat(os_helper.TESTFN + 'link')[1]) + self.assertEqual(posix.stat(support.TESTFN)[1], + posix.stat(support.TESTFN + 'link')[1]) finally: posix.close(f) - os_helper.unlink(os_helper.TESTFN + 'link') + support.unlink(support.TESTFN + 'link') @unittest.skipUnless(os.mkdir in os.supports_dir_fd, "test needs dir_fd support in os.mkdir()") def test_mkdir_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mkdir(os_helper.TESTFN + 'dir', dir_fd=f) - posix.stat(os_helper.TESTFN + 'dir') # should not raise exception + posix.mkdir(support.TESTFN + 'dir', dir_fd=f) + posix.stat(support.TESTFN + 'dir') # should not raise exception finally: posix.close(f) - os_helper.rmtree(os_helper.TESTFN + 'dir') + support.rmtree(support.TESTFN + 'dir') @unittest.skipUnless((os.mknod in os.supports_dir_fd) and hasattr(stat, 'S_IFIFO'), "test requires both stat.S_IFIFO and dir_fd support for os.mknod()") def test_mknod_dir_fd(self): # Test using mknodat() to create a FIFO (the only use specified # by POSIX). - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) mode = stat.S_IFIFO | stat.S_IRUSR | stat.S_IWUSR f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.mknod(os_helper.TESTFN, mode, 0, dir_fd=f) + posix.mknod(support.TESTFN, mode, 0, dir_fd=f) except OSError as e: # Some old systems don't allow unprivileged users to use # mknod(), or only support creating device nodes. self.assertIn(e.errno, (errno.EPERM, errno.EINVAL, errno.EACCES)) else: - self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) finally: posix.close(f) @unittest.skipUnless(os.open in os.supports_dir_fd, "test needs dir_fd support in os.open()") def test_open_dir_fd(self): - os_helper.unlink(os_helper.TESTFN) - with open(os_helper.TESTFN, 'w') as outfile: + support.unlink(support.TESTFN) + with open(support.TESTFN, 'w') as outfile: outfile.write("testline\n") a = posix.open(posix.getcwd(), posix.O_RDONLY) - b = posix.open(os_helper.TESTFN, posix.O_RDONLY, dir_fd=a) + b = posix.open(support.TESTFN, posix.O_RDONLY, dir_fd=a) try: res = posix.read(b, 9).decode(encoding="utf-8") self.assertEqual("testline\n", res) @@ -1208,27 +1207,27 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(os.readlink in os.supports_dir_fd, "test needs dir_fd support in os.readlink()") def test_readlink_dir_fd(self): - os.symlink(os_helper.TESTFN, os_helper.TESTFN + 'link') + os.symlink(support.TESTFN, support.TESTFN + 'link') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - self.assertEqual(posix.readlink(os_helper.TESTFN + 'link'), - posix.readlink(os_helper.TESTFN + 'link', dir_fd=f)) + self.assertEqual(posix.readlink(support.TESTFN + 'link'), + posix.readlink(support.TESTFN + 'link', dir_fd=f)) finally: - os_helper.unlink(os_helper.TESTFN + 'link') + support.unlink(support.TESTFN + 'link') posix.close(f) @unittest.skipUnless(os.rename in os.supports_dir_fd, "test needs dir_fd support in os.rename()") def test_rename_dir_fd(self): - os_helper.unlink(os_helper.TESTFN) - os_helper.create_empty_file(os_helper.TESTFN + 'ren') + support.unlink(support.TESTFN) + support.create_empty_file(support.TESTFN + 'ren') f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.rename(os_helper.TESTFN + 'ren', os_helper.TESTFN, src_dir_fd=f, dst_dir_fd=f) + posix.rename(support.TESTFN + 'ren', support.TESTFN, src_dir_fd=f, dst_dir_fd=f) except: - posix.rename(os_helper.TESTFN + 'ren', os_helper.TESTFN) + posix.rename(support.TESTFN + 'ren', support.TESTFN) raise else: - posix.stat(os_helper.TESTFN) # should not raise exception + posix.stat(support.TESTFN) # should not raise exception finally: posix.close(f) @@ -1246,24 +1245,24 @@ class PosixTester(unittest.TestCase): def test_symlink_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: - posix.symlink(os_helper.TESTFN, os_helper.TESTFN + 'link', dir_fd=f) - self.assertEqual(posix.readlink(os_helper.TESTFN + 'link'), os_helper.TESTFN) + posix.symlink(support.TESTFN, support.TESTFN + 'link', dir_fd=f) + self.assertEqual(posix.readlink(support.TESTFN + 'link'), support.TESTFN) finally: posix.close(f) - os_helper.unlink(os_helper.TESTFN + 'link') + support.unlink(support.TESTFN + 'link') @unittest.skipUnless(os.unlink in os.supports_dir_fd, "test needs dir_fd support in os.unlink()") def test_unlink_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) - os_helper.create_empty_file(os_helper.TESTFN + 'del') - posix.stat(os_helper.TESTFN + 'del') # should not raise exception + support.create_empty_file(support.TESTFN + 'del') + posix.stat(support.TESTFN + 'del') # should not raise exception try: - posix.unlink(os_helper.TESTFN + 'del', dir_fd=f) + posix.unlink(support.TESTFN + 'del', dir_fd=f) except: - os_helper.unlink(os_helper.TESTFN + 'del') + support.unlink(support.TESTFN + 'del') raise else: - self.assertRaises(OSError, posix.stat, os_helper.TESTFN + 'link') + self.assertRaises(OSError, posix.stat, support.TESTFN + 'link') finally: posix.close(f) @@ -1272,15 +1271,15 @@ class PosixTester(unittest.TestCase): # @unittest.skipUnless(os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()") @unittest.expectedFailure def test_mkfifo_dir_fd(self): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) try: try: - posix.mkfifo(os_helper.TESTFN, + posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR, dir_fd=f) except PermissionError as e: self.skipTest('posix.mkfifo(): %s' % e) - self.assertTrue(stat.S_ISFIFO(posix.stat(os_helper.TESTFN).st_mode)) + self.assertTrue(stat.S_ISFIFO(posix.stat(support.TESTFN).st_mode)) finally: posix.close(f) @@ -1407,7 +1406,7 @@ class PosixTester(unittest.TestCase): # behaviour: # os.SEEK_DATA = current position # os.SEEK_HOLE = end of file position - with open(os_helper.TESTFN, 'r+b') as fp: + with open(support.TESTFN, 'r+b') as fp: fp.write(b"hello") fp.flush() size = fp.tell() @@ -1434,7 +1433,7 @@ class PosixTester(unittest.TestCase): if function is None: continue - for dst in ("noodly2", os_helper.TESTFN): + for dst in ("noodly2", support.TESTFN): try: function('doesnotexistfilename', dst) except OSError as e: @@ -1444,10 +1443,10 @@ class PosixTester(unittest.TestCase): self.fail("No valid path_error2() test for os." + name) def test_path_with_null_character(self): - fn = os_helper.TESTFN + fn = support.TESTFN fn_with_NUL = fn + '\0' - self.addCleanup(os_helper.unlink, fn) - os_helper.unlink(fn) + self.addCleanup(support.unlink, fn) + support.unlink(fn) fd = None try: with self.assertRaises(ValueError): @@ -1462,10 +1461,10 @@ class PosixTester(unittest.TestCase): self.assertRaises(ValueError, os.stat, fn_with_NUL) def test_path_with_null_byte(self): - fn = os.fsencode(os_helper.TESTFN) + fn = os.fsencode(support.TESTFN) fn_with_NUL = fn + b'\0' - self.addCleanup(os_helper.unlink, fn) - os_helper.unlink(fn) + self.addCleanup(support.unlink, fn) + support.unlink(fn) fd = None try: with self.assertRaises(ValueError): @@ -1540,8 +1539,8 @@ class _PosixSpawnMixin: return (sys.executable, '-I', '-S', *args) def test_returns_pid(self): - pidfile = os_helper.TESTFN - self.addCleanup(os_helper.unlink, pidfile) + pidfile = support.TESTFN + self.addCleanup(support.unlink, pidfile) script = f"""if 1: import os with open({pidfile!r}, "w") as pidfile: @@ -1571,8 +1570,8 @@ class _PosixSpawnMixin: # TODO: RUSTPYTHON: TypeError: '_Environ' object is not a mapping @unittest.expectedFailure def test_specify_environment(self): - envfile = os_helper.TESTFN - self.addCleanup(os_helper.unlink, envfile) + envfile = support.TESTFN + self.addCleanup(support.unlink, envfile) script = f"""if 1: import os with open({envfile!r}, "w") as envfile: @@ -1832,8 +1831,8 @@ class _PosixSpawnMixin: os.O_RDONLY, 0)]) def test_open_file(self): - outfile = os_helper.TESTFN - self.addCleanup(os_helper.unlink, outfile) + outfile = support.TESTFN + self.addCleanup(support.unlink, outfile) script = """if 1: import sys sys.stdout.write("hello") @@ -1854,8 +1853,8 @@ class _PosixSpawnMixin: # TODO: RUSTPYTHON: FileNotFoundError: [Errno 2] No such file or directory (os error 2): '@test_55144_tmp' -> 'None' @unittest.expectedFailure def test_close_file(self): - closefile = os_helper.TESTFN - self.addCleanup(os_helper.unlink, closefile) + closefile = support.TESTFN + self.addCleanup(support.unlink, closefile) script = f"""if 1: import os try: @@ -1873,8 +1872,8 @@ class _PosixSpawnMixin: self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) def test_dup2(self): - dupfile = os_helper.TESTFN - self.addCleanup(os_helper.unlink, dupfile) + dupfile = support.TESTFN + self.addCleanup(support.unlink, dupfile) script = """if 1: import sys sys.stdout.write("hello") @@ -1900,11 +1899,11 @@ class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): spawn_func = getattr(posix, 'posix_spawnp', None) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_posix_spawnp(self): # Use a symlink to create a program in its own temporary directory temp_dir = tempfile.mkdtemp() - self.addCleanup(os_helper.rmtree, temp_dir) + self.addCleanup(support.rmtree, temp_dir) program = 'posix_spawnp_test_program.exe' program_fullpath = os.path.join(temp_dir, program) @@ -2033,7 +2032,7 @@ class TestPosixWeaklinking(unittest.TestCase): os.link("source", "target", src_dir_fd=0, dst_dir_fd=0) # issue 41355: !HAVE_LINKAT code path ignores the follow_symlinks flag - with os_helper.temp_dir() as base_path: + with support.temp_dir() as base_path: link_path = os.path.join(base_path, "link") target_path = os.path.join(base_path, "target") source_path = os.path.join(base_path, "source") diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 6b40fc09d..1d69fb4f4 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -3,8 +3,7 @@ import posixpath import unittest from posixpath import realpath, abspath, dirname, basename from test import support, test_genericpath -from test.support import os_helper -from test.support.os_helper import FakePath +from test.support import FakePath from unittest import mock try: @@ -16,7 +15,7 @@ except ImportError: # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. -ABSTFN = abspath(os_helper.TESTFN) +ABSTFN = abspath(support.TESTFN) def skip_if_ABSTFN_contains_backslash(test): """ @@ -41,8 +40,8 @@ class PosixPathTest(unittest.TestCase): def tearDown(self): for suffix in ["", "1", "2"]: - os_helper.unlink(os_helper.TESTFN + suffix) - safe_rmdir(os_helper.TESTFN + suffix) + support.unlink(support.TESTFN + suffix) + safe_rmdir(support.TESTFN + suffix) def test_join(self): self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), @@ -153,25 +152,25 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo") def test_islink(self): - self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) - self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False) + self.assertIs(posixpath.islink(support.TESTFN + "1"), False) + self.assertIs(posixpath.lexists(support.TESTFN + "2"), False) - with open(os_helper.TESTFN + "1", "wb") as f: + with open(support.TESTFN + "1", "wb") as f: f.write(b"foo") - self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False) + self.assertIs(posixpath.islink(support.TESTFN + "1"), False) - if os_helper.can_symlink(): - os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2") - self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) - os.remove(os_helper.TESTFN + "1") - self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True) - self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False) - self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True) + if support.can_symlink(): + os.symlink(support.TESTFN + "1", support.TESTFN + "2") + self.assertIs(posixpath.islink(support.TESTFN + "2"), True) + os.remove(support.TESTFN + "1") + self.assertIs(posixpath.islink(support.TESTFN + "2"), True) + self.assertIs(posixpath.exists(support.TESTFN + "2"), False) + self.assertIs(posixpath.lexists(support.TESTFN + "2"), True) - self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False) - self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False) - self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False) - self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False) + self.assertIs(posixpath.islink(support.TESTFN + "\udfff"), False) + self.assertIs(posixpath.islink(os.fsencode(support.TESTFN) + b"\xff"), False) + self.assertIs(posixpath.islink(support.TESTFN + "\x00"), False) + self.assertIs(posixpath.islink(os.fsencode(support.TESTFN) + b"\x00"), False) # TODO: RUSTPYTHON if os.name == "nt": @@ -195,7 +194,7 @@ class PosixPathTest(unittest.TestCase): self.assertIs(posixpath.ismount('/\x00'), False) self.assertIs(posixpath.ismount(b'/\x00'), False) - @unittest.skipUnless(os_helper.can_symlink(), + @unittest.skipUnless(support.can_symlink(), "Test requires symlink support") def test_ismount_symlinks(self): # Symlinks are never mountpoints. @@ -254,7 +253,7 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(posixpath.expanduser(b"foo"), b"foo") def test_expanduser_home_envvar(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['HOME'] = '/home/victor' self.assertEqual(posixpath.expanduser("~"), "/home/victor") @@ -290,7 +289,7 @@ class PosixPathTest(unittest.TestCase): self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes) self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: # expanduser should fall back to using the password database del env['HOME'] @@ -357,7 +356,7 @@ class PosixPathTest(unittest.TestCase): os.symlink(ABSTFN+"1", ABSTFN) self.assertEqual(realpath(ABSTFN), ABSTFN+"1") finally: - os_helper.unlink(ABSTFN) + support.unlink(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -367,7 +366,7 @@ class PosixPathTest(unittest.TestCase): os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) self.assertEqual(realpath(ABSTFN), ABSTFN+"1") finally: - os_helper.unlink(ABSTFN) + support.unlink(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -401,15 +400,15 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c") # Test using relative path as well. - with os_helper.change_cwd(dirname(ABSTFN)): + with support.change_cwd(dirname(ABSTFN)): self.assertEqual(realpath(basename(ABSTFN)), ABSTFN) finally: - os_helper.unlink(ABSTFN) - os_helper.unlink(ABSTFN+"1") - os_helper.unlink(ABSTFN+"2") - os_helper.unlink(ABSTFN+"y") - os_helper.unlink(ABSTFN+"c") - os_helper.unlink(ABSTFN+"a") + support.unlink(ABSTFN) + support.unlink(ABSTFN+"1") + support.unlink(ABSTFN+"2") + support.unlink(ABSTFN+"y") + support.unlink(ABSTFN+"c") + support.unlink(ABSTFN+"a") @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @@ -422,8 +421,8 @@ class PosixPathTest(unittest.TestCase): os.symlink('self/self/self', ABSTFN + '/link') self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN) finally: - os_helper.unlink(ABSTFN + '/self') - os_helper.unlink(ABSTFN + '/link') + support.unlink(ABSTFN + '/self') + support.unlink(ABSTFN + '/link') safe_rmdir(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), @@ -439,11 +438,11 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN) # Test using relative path as well. - with os_helper.change_cwd(ABSTFN): + with support.change_cwd(ABSTFN): self.assertEqual(realpath('%d' % depth), ABSTFN) finally: for i in range(depth + 1): - os_helper.unlink(ABSTFN + '/%d' % i) + support.unlink(ABSTFN + '/%d' % i) safe_rmdir(ABSTFN) @unittest.skipUnless(hasattr(os, "symlink"), @@ -459,10 +458,10 @@ class PosixPathTest(unittest.TestCase): os.mkdir(ABSTFN + "/y") os.symlink(ABSTFN + "/y", ABSTFN + "/k") - with os_helper.change_cwd(ABSTFN + "/k"): + with support.change_cwd(ABSTFN + "/k"): self.assertEqual(realpath("a"), ABSTFN + "/y/a") finally: - os_helper.unlink(ABSTFN + "/k") + support.unlink(ABSTFN + "/k") safe_rmdir(ABSTFN + "/y") safe_rmdir(ABSTFN) @@ -486,11 +485,11 @@ class PosixPathTest(unittest.TestCase): # Absolute path. self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k") # Relative path. - with os_helper.change_cwd(dirname(ABSTFN)): + with support.change_cwd(dirname(ABSTFN)): self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."), ABSTFN + "/k") finally: - os_helper.unlink(ABSTFN + "/link-y") + support.unlink(ABSTFN + "/link-y") safe_rmdir(ABSTFN + "/k/y") safe_rmdir(ABSTFN + "/k") safe_rmdir(ABSTFN) @@ -506,12 +505,12 @@ class PosixPathTest(unittest.TestCase): os.mkdir(ABSTFN) os.mkdir(ABSTFN + "/k") os.symlink(ABSTFN, ABSTFN + "link") - with os_helper.change_cwd(dirname(ABSTFN)): + with support.change_cwd(dirname(ABSTFN)): base = basename(ABSTFN) self.assertEqual(realpath(base + "link"), ABSTFN) self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k") finally: - os_helper.unlink(ABSTFN + "link") + support.unlink(ABSTFN + "link") safe_rmdir(ABSTFN + "/k") safe_rmdir(ABSTFN) @@ -680,9 +679,9 @@ class PathLikeTests(unittest.TestCase): path = posixpath def setUp(self): - self.file_name = os_helper.TESTFN.lower() - self.file_path = FakePath(os_helper.TESTFN) - self.addCleanup(os_helper.unlink, self.file_name) + self.file_name = support.TESTFN.lower() + self.file_path = FakePath(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) with open(self.file_name, 'xb', 0) as file: file.write(b"test_posixpath.PathLikeTests") diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index d7de6808d..4b726f51f 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -9,14 +9,13 @@ import tempfile import unittest from test import support -from test.support import os_helper def without_source_date_epoch(fxn): """Runs function with SOURCE_DATE_EPOCH unset.""" @functools.wraps(fxn) def wrapper(*args, **kwargs): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.unset('SOURCE_DATE_EPOCH') return fxn(*args, **kwargs) return wrapper @@ -26,7 +25,7 @@ def with_source_date_epoch(fxn): """Runs function with SOURCE_DATE_EPOCH set.""" @functools.wraps(fxn) def wrapper(*args, **kwargs): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['SOURCE_DATE_EPOCH'] = '123456789' return fxn(*args, **kwargs) return wrapper @@ -102,7 +101,7 @@ class PyCompileTestsBase: self.assertTrue(os.path.exists(self.cache_path)) def test_cwd(self): - with os_helper.change_cwd(self.directory): + with support.change_cwd(self.directory): py_compile.compile(os.path.basename(self.source_path), os.path.basename(self.pyc_path)) self.assertTrue(os.path.exists(self.pyc_path)) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index fc56ec4af..4258946aa 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -19,7 +19,6 @@ import textwrap import unittest from test import libregrtest from test import support -from test.support import os_helper from test.libregrtest import utils @@ -167,12 +166,12 @@ class ParseArgsTestCase(unittest.TestCase): self.assertEqual(ns.ignore_tests, ['pattern']) self.checkError([opt], 'expected one argument') - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "w") as fp: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, "w") as fp: print('matchfile1', file=fp) print('matchfile2', file=fp) - filename = os.path.abspath(os_helper.TESTFN) + filename = os.path.abspath(support.TESTFN) ns = libregrtest._parse_args(['-m', 'match', '--ignorefile', filename]) self.assertEqual(ns.ignore_tests, @@ -189,12 +188,12 @@ class ParseArgsTestCase(unittest.TestCase): '-m', 'pattern2']) self.assertEqual(ns.match_tests, ['pattern1', 'pattern2']) - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with open(os_helper.TESTFN, "w") as fp: + self.addCleanup(support.unlink, support.TESTFN) + with open(support.TESTFN, "w") as fp: print('matchfile1', file=fp) print('matchfile2', file=fp) - filename = os.path.abspath(os_helper.TESTFN) + filename = os.path.abspath(support.TESTFN) ns = libregrtest._parse_args(['-m', 'match', '--matchfile', filename]) self.assertEqual(ns.match_tests, @@ -243,7 +242,7 @@ class ParseArgsTestCase(unittest.TestCase): def test_testdir(self): ns = libregrtest._parse_args(['--testdir', 'foo']) - self.assertEqual(ns.testdir, os.path.join(os_helper.SAVEDCWD, 'foo')) + self.assertEqual(ns.testdir, os.path.join(support.SAVEDCWD, 'foo')) self.checkError(['--testdir'], 'expected one argument') def test_runleaks(self): @@ -290,7 +289,7 @@ class ParseArgsTestCase(unittest.TestCase): with self.subTest(opt=opt): ns = libregrtest._parse_args([opt, 'foo']) self.assertEqual(ns.coverdir, - os.path.join(os_helper.SAVEDCWD, 'foo')) + os.path.join(support.SAVEDCWD, 'foo')) self.checkError([opt], 'expected one argument') def test_nocoverdir(self): @@ -369,7 +368,7 @@ class BaseTestCase(unittest.TestCase): self.testdir = os.path.realpath(os.path.dirname(__file__)) self.tmptestdir = tempfile.mkdtemp() - self.addCleanup(os_helper.rmtree, self.tmptestdir) + self.addCleanup(support.rmtree, self.tmptestdir) def create_test(self, name=None, code=None): if not name: @@ -390,7 +389,7 @@ class BaseTestCase(unittest.TestCase): name = self.TESTNAME_PREFIX + name path = os.path.join(self.tmptestdir, name + '.py') - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) # Use 'x' mode to ensure that we do not override existing tests try: with open(path, 'x', encoding='utf-8') as fp: @@ -779,8 +778,8 @@ class ArgsTestCase(BaseTestCase): # Write the list of files using a format similar to regrtest output: # [1/2] test_1 # [2/2] test_2 - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) # test format '0:00:00 [2/7] test_opcodes -- test_grammar took 0 sec' with open(filename, "w") as fp: @@ -901,7 +900,7 @@ class ArgsTestCase(BaseTestCase): test = self.create_test('huntrleaks', code=code) filename = 'reflog.txt' - self.addCleanup(os_helper.unlink, filename) + self.addCleanup(support.unlink, filename) output = self.run_tests('--huntrleaks', '3:3:', test, exitcode=2, stderr=subprocess.STDOUT) @@ -1014,8 +1013,8 @@ class ArgsTestCase(BaseTestCase): testname = self.create_test(code=code) # only run a subset - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) subset = [ # only ignore the method name @@ -1055,8 +1054,8 @@ class ArgsTestCase(BaseTestCase): self.assertEqual(methods, all_methods) # only run a subset - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) subset = [ # only match the method name diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index a4ab868e7..73f28390f 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -10,8 +10,7 @@ import importlib import importlib.util import unittest -from test.support import verbose -from test.support.os_helper import create_empty_file +from test.support import create_empty_file, verbose from reprlib import repr as r # Don't shadow builtin repr from reprlib import Repr from reprlib import recursive_repr diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index b3d549ff4..176e2cd0b 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -2,7 +2,6 @@ import contextlib import sys import unittest from test import support -from test.support import os_helper import time resource = support.import_module('resource') @@ -54,7 +53,7 @@ class ResourceTest(unittest.TestCase): limit_set = True except ValueError: limit_set = False - f = open(os_helper.TESTFN, "wb") + f = open(support.TESTFN, "wb") try: f.write(b"X" * 1024) try: @@ -80,7 +79,7 @@ class ResourceTest(unittest.TestCase): finally: if limit_set: resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_fsize_toobig(self): # Be sure that setrlimit is checking for really large values diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index 85ee84218..4fb9ac762 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -13,8 +13,8 @@ import textwrap import unittest import warnings from test.support import ( - forget, make_legacy_pyc, unload, verbose, no_tracing) -from test.support.os_helper import create_empty_file, temp_dir + forget, make_legacy_pyc, unload, verbose, no_tracing, + create_empty_file, temp_dir) from test.support.script_helper import make_script, make_zip_script diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 99bd58bb6..beb20f43d 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper # import gc import weakref import operator @@ -322,15 +321,15 @@ class TestJointOps: w = ReprWrapper() s = self.thetype([w]) w.value = s - fo = open(os_helper.TESTFN, "w") + fo = open(support.TESTFN, "w") try: fo.write(str(s)) fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), repr(s)) finally: fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -829,14 +828,14 @@ class TestBasicOps: def test_print(self): try: - fo = open(os_helper.TESTFN, "w") + fo = open(support.TESTFN, "w") fo.write(str(self.set)) fo.close() - fo = open(os_helper.TESTFN, "r") + fo = open(support.TESTFN, "r") self.assertEqual(fo.read(), repr(self.set)) finally: fo.close() - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def test_length(self): self.assertEqual(len(self.set), self.length) diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index ac25eee2e..9ffe2cbea 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -2,7 +2,6 @@ import unittest import shelve import glob from test import support -from test.support import os_helper from collections.abc import MutableMapping from test.test_dbm import dbm_iterator @@ -46,7 +45,7 @@ class TestCase(unittest.TestCase): def tearDown(self): for f in glob.glob(self.fn+"*"): - os_helper.unlink(f) + support.unlink(f) def test_close(self): d1 = {} @@ -187,7 +186,7 @@ class TestShelveBase(mapping_tests.BasicTestMappingProtocol): self._db = [] if not self._in_mem: for f in glob.glob(self.fn+"*"): - os_helper.unlink(f) + support.unlink(f) class TestAsciiFileShelve(TestShelveBase): _args={'protocol':0} diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index fba98972d..d6b9fc876 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -30,8 +30,7 @@ except ImportError: posix = None from test import support -from test.support import os_helper -from test.support.os_helper import TESTFN, FakePath +from test.support import TESTFN, FakePath TESTFN2 = TESTFN + "2" MACOS = sys.platform.startswith("darwin") @@ -141,9 +140,9 @@ def supports_file2file_sendfile(): return True finally: if srcname is not None: - os_helper.unlink(srcname) + support.unlink(srcname) if dstname is not None: - os_helper.unlink(dstname) + support.unlink(dstname) SUPPORTS_SENDFILE = supports_file2file_sendfile() @@ -194,7 +193,7 @@ class TestShutil(unittest.TestCase): self.assertIsInstance(victim, bytes) shutil.rmtree(victim) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_rmtree_fails_on_symlink(self): tmp = self.mkdtemp() dir_ = os.path.join(tmp, 'dir') @@ -213,7 +212,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(errors[0][1], link) self.assertIsInstance(errors[0][2][1], OSError) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_rmtree_works_on_symlinks(self): tmp = self.mkdtemp() dir1 = os.path.join(tmp, 'dir1') @@ -325,7 +324,7 @@ class TestShutil(unittest.TestCase): self.child_file_path = os.path.join(TESTFN, 'a') self.child_dir_path = os.path.join(TESTFN, 'b') - os_helper.create_empty_file(self.child_file_path) + support.create_empty_file(self.child_file_path) os.mkdir(self.child_dir_path) old_dir_mode = os.stat(TESTFN).st_mode old_child_file_mode = os.stat(self.child_file_path).st_mode @@ -388,7 +387,7 @@ class TestShutil(unittest.TestCase): finally: os.lstat = orig_lstat - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copymode_follow_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -421,7 +420,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode) @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copymode_symlink_to_symlink(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -451,7 +450,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode) @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copymode_symlink_to_symlink_wo_lchmod(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -464,7 +463,7 @@ class TestShutil(unittest.TestCase): os.symlink(dst, dst_link) shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copystat_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -534,7 +533,7 @@ class TestShutil(unittest.TestCase): finally: os.chflags = old_chflags - @os_helper.skip_unless_xattr + @support.skip_unless_xattr def test_copyxattr(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -598,8 +597,8 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly') self.assertEqual(os.getxattr(dstro, 'user.the_value'), b'fiddly') - @os_helper.skip_unless_symlink - @os_helper.skip_unless_xattr + @support.skip_unless_symlink + @support.skip_unless_xattr @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0, 'root privileges required') def test_copyxattr_symlinks(self): @@ -623,7 +622,7 @@ class TestShutil(unittest.TestCase): shutil._copyxattr(src_link, dst, follow_symlinks=False) self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copy_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -646,7 +645,7 @@ class TestShutil(unittest.TestCase): self.assertEqual(os.lstat(src_link).st_mode, os.lstat(dst).st_mode) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copy2_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -681,7 +680,7 @@ class TestShutil(unittest.TestCase): if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'): self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags) - @os_helper.skip_unless_xattr + @support.skip_unless_xattr def test_copy2_xattr(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'foo') @@ -694,7 +693,7 @@ class TestShutil(unittest.TestCase): os.getxattr(dst, 'user.foo')) os.remove(dst) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copyfile_symlinks(self): tmp_dir = self.mkdtemp() src = os.path.join(tmp_dir, 'src') @@ -786,7 +785,7 @@ class TestShutil(unittest.TestCase): with self.assertRaises(FileExistsError): shutil.copytree(src_dir, dst_dir, dirs_exist_ok=False) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copytree_symlinks(self): tmp_dir = self.mkdtemp() src_dir = os.path.join(tmp_dir, 'src') @@ -976,9 +975,9 @@ class TestShutil(unittest.TestCase): flag = [] src = tempfile.mkdtemp() - self.addCleanup(os_helper.rmtree, src) + self.addCleanup(support.rmtree, src) dst = tempfile.mktemp() - self.addCleanup(os_helper.rmtree, dst) + self.addCleanup(support.rmtree, dst) with open(os.path.join(src, 'foo'), 'w') as f: f.close() shutil.copytree(src, dst, copy_function=custom_cpfun) @@ -1004,7 +1003,7 @@ class TestShutil(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_dont_copy_file_onto_symlink_to_itself(self): # bug 851123. os.mkdir(TESTFN) @@ -1024,7 +1023,7 @@ class TestShutil(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_rmtree_on_symlink(self): # bug 1669. os.mkdir(TESTFN) @@ -1068,7 +1067,7 @@ class TestShutil(unittest.TestCase): os.remove(TESTFN) @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copytree_named_pipe(self): os.mkdir(TESTFN) try: @@ -1107,7 +1106,7 @@ class TestShutil(unittest.TestCase): shutil.copytree(src_dir, dst_dir, copy_function=_copy) self.assertEqual(len(copied), 2) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copytree_dangling_symlinks(self): # a dangling symlink raises an error at the end @@ -1128,7 +1127,7 @@ class TestShutil(unittest.TestCase): shutil.copytree(src_dir, dst_dir, symlinks=True) self.assertIn('test.txt', os.listdir(dst_dir)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_copytree_symlink_dir(self): src_dir = self.mkdtemp() dst_dir = os.path.join(self.mkdtemp(), 'destination') @@ -1193,7 +1192,7 @@ class TestShutil(unittest.TestCase): work_dir = os.path.dirname(tmpdir2) rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - with os_helper.change_cwd(work_dir): + with support.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) tarball = make_archive(rel_base_name, 'gztar', root_dir, '.') @@ -1207,7 +1206,7 @@ class TestShutil(unittest.TestCase): './file1', './file2', './sub/file3']) # trying an uncompressed one - with os_helper.change_cwd(work_dir): + with support.change_cwd(work_dir): tarball = make_archive(rel_base_name, 'tar', root_dir, '.') self.assertEqual(tarball, base_name + '.tar') self.assertTrue(os.path.isfile(tarball)) @@ -1282,7 +1281,7 @@ class TestShutil(unittest.TestCase): work_dir = os.path.dirname(tmpdir2) rel_base_name = os.path.join(os.path.basename(tmpdir2), 'archive') - with os_helper.change_cwd(work_dir): + with support.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) res = make_archive(rel_base_name, 'zip', root_dir) @@ -1295,7 +1294,7 @@ class TestShutil(unittest.TestCase): 'dist/file1', 'dist/file2', 'dist/sub/file3', 'outer']) - with os_helper.change_cwd(work_dir): + with support.change_cwd(work_dir): base_name = os.path.abspath(rel_base_name) res = make_archive(rel_base_name, 'zip', root_dir, base_dir) @@ -1347,7 +1346,7 @@ class TestShutil(unittest.TestCase): # now check the ZIP file using `unzip -t` zip_cmd = ['unzip', '-t', archive] - with os_helper.change_cwd(root_dir): + with support.change_cwd(root_dir): try: subprocess.check_output(zip_cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: @@ -1397,7 +1396,7 @@ class TestShutil(unittest.TestCase): base_name = os.path.join(self.mkdtemp(), 'archive') group = grp.getgrgid(0)[0] owner = pwd.getpwuid(0)[0] - with os_helper.change_cwd(root_dir): + with support.change_cwd(root_dir): archive_name = make_archive(base_name, 'gztar', root_dir, 'dist', owner=owner, group=group) @@ -1431,7 +1430,7 @@ class TestShutil(unittest.TestCase): def test_make_tarfile_in_curdir(self): # Issue #21280 root_dir = self.mkdtemp() - with os_helper.change_cwd(root_dir): + with support.change_cwd(root_dir): self.assertEqual(make_archive('test', 'tar'), 'test.tar') self.assertTrue(os.path.isfile('test.tar')) @@ -1439,7 +1438,7 @@ class TestShutil(unittest.TestCase): def test_make_zipfile_in_curdir(self): # Issue #21280 root_dir = self.mkdtemp() - with os_helper.change_cwd(root_dir): + with support.change_cwd(root_dir): self.assertEqual(make_archive('test', 'zip'), 'test.zip') self.assertTrue(os.path.isfile('test.zip')) @@ -1696,18 +1695,18 @@ class TestWhich(unittest.TestCase): # that exists, it should be returned. base_dir, tail_dir = os.path.split(self.dir) relpath = os.path.join(tail_dir, self.file) - with os_helper.change_cwd(path=base_dir): + with support.change_cwd(path=base_dir): rv = shutil.which(relpath, path=self.temp_dir) self.assertEqual(rv, relpath) # But it shouldn't be searched in PATH directories (issue #16957). - with os_helper.change_cwd(path=self.dir): + with support.change_cwd(path=self.dir): rv = shutil.which(relpath, path=base_dir) self.assertIsNone(rv) def test_cwd(self): # Issue #16957 base_dir = os.path.dirname(self.dir) - with os_helper.change_cwd(path=self.dir): + with support.change_cwd(path=self.dir): rv = shutil.which(self.file, path=base_dir) if sys.platform == "win32": # Windows: current directory implicitly on PATH @@ -1728,7 +1727,7 @@ class TestWhich(unittest.TestCase): def test_relative_path(self): base_dir, tail_dir = os.path.split(self.dir) - with os_helper.change_cwd(path=base_dir): + with support.change_cwd(path=base_dir): rv = shutil.which(self.file, path=tail_dir) self.assertEqual(rv, os.path.join(tail_dir, self.file)) @@ -1746,19 +1745,19 @@ class TestWhich(unittest.TestCase): self.assertEqual(rv, self.temp_file.name[:-4] + self.ext) def test_environ_path(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['PATH'] = self.env_path rv = shutil.which(self.file) self.assertEqual(rv, self.temp_file.name) def test_environ_path_empty(self): # PATH='': no match - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['PATH'] = '' with unittest.mock.patch('os.confstr', return_value=self.dir, \ create=True), \ support.swap_attr(os, 'defpath', self.dir), \ - os_helper.change_cwd(self.dir): + support.change_cwd(self.dir): rv = shutil.which(self.file) self.assertIsNone(rv) @@ -1771,7 +1770,7 @@ class TestWhich(unittest.TestCase): expected_cwd = os.path.join(curdir, expected_cwd) # PATH=':': explicitly looks in the current directory - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['PATH'] = os.pathsep with unittest.mock.patch('os.confstr', return_value=self.dir, \ create=True), \ @@ -1780,12 +1779,12 @@ class TestWhich(unittest.TestCase): self.assertIsNone(rv) # look in current directory - with os_helper.change_cwd(self.dir): + with support.change_cwd(self.dir): rv = shutil.which(self.file) self.assertEqual(rv, expected_cwd) def test_environ_path_missing(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.pop('PATH', None) # without confstr @@ -1804,14 +1803,14 @@ class TestWhich(unittest.TestCase): def test_empty_path(self): base_dir = os.path.dirname(self.dir) - with os_helper.change_cwd(path=self.dir), \ - os_helper.EnvironmentVarGuard() as env: + with support.change_cwd(path=self.dir), \ + support.EnvironmentVarGuard() as env: env['PATH'] = self.env_path rv = shutil.which(self.file, path='') self.assertIsNone(rv) def test_empty_path_no_PATH(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.pop('PATH', None) rv = shutil.which(self.file) self.assertIsNone(rv) @@ -1828,7 +1827,7 @@ class TestWhich(unittest.TestCase): program = os.path.basename(temp_filexyz.name) program = os.path.splitext(program)[0] - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['PATHEXT'] = ext rv = shutil.which(program, path=self.temp_dir) self.assertEqual(rv, temp_filexyz.name) @@ -1968,7 +1967,7 @@ class TestMove(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @mock_rename def test_move_file_symlink(self): dst = os.path.join(self.src_dir, 'bar') @@ -1977,7 +1976,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(self.dst_file)) self.assertTrue(os.path.samefile(self.src_file, self.dst_file)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @mock_rename def test_move_file_symlink_to_dir(self): filename = "bar" @@ -1988,7 +1987,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(final_link)) self.assertTrue(os.path.samefile(self.src_file, final_link)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @mock_rename def test_move_dangling_symlink(self): src = os.path.join(self.src_dir, 'baz') @@ -1999,7 +1998,7 @@ class TestMove(unittest.TestCase): self.assertTrue(os.path.islink(dst_link)) self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link)) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink @mock_rename def test_move_dir_symlink(self): src = os.path.join(self.src_dir, 'baz') @@ -2033,8 +2032,8 @@ class TestMove(unittest.TestCase): moved = [] def _copy(src, dst): moved.append((src, dst)) - os_helper.create_empty_file(os.path.join(self.src_dir, 'child')) - os_helper.create_empty_file(os.path.join(self.src_dir, 'child1')) + support.create_empty_file(os.path.join(self.src_dir, 'child')) + support.create_empty_file(os.path.join(self.src_dir, 'child1')) shutil.move(self.src_dir, self.dst_dir, copy_function=_copy) self.assertEqual(len(moved), 3) @@ -2172,11 +2171,11 @@ class TestCopyFileObj(unittest.TestCase): @classmethod def tearDownClass(cls): - os_helper.unlink(TESTFN) - os_helper.unlink(TESTFN2) + support.unlink(TESTFN) + support.unlink(TESTFN2) def tearDown(self): - os_helper.unlink(TESTFN2) + support.unlink(TESTFN2) @contextlib.contextmanager def get_files(self): @@ -2221,7 +2220,7 @@ class TestCopyFileObj(unittest.TestCase): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(b'foo') fname = f.name - self.addCleanup(os_helper.unlink, fname) + self.addCleanup(support.unlink, fname) with unittest.mock.patch("shutil._copyfileobj_readinto") as m: shutil.copyfile(fname, TESTFN2) self.assertEqual(m.call_args[0][2], 3) @@ -2230,7 +2229,7 @@ class TestCopyFileObj(unittest.TestCase): with tempfile.NamedTemporaryFile(delete=False) as f: pass fname = f.name - self.addCleanup(os_helper.unlink, fname) + self.addCleanup(support.unlink, fname) with unittest.mock.patch("shutil._copyfileobj_readinto") as m: shutil.copyfile(fname, TESTFN2) assert not m.called @@ -2252,10 +2251,10 @@ class _ZeroCopyFileTest(object): @classmethod def tearDownClass(cls): - os_helper.unlink(TESTFN) + support.unlink(TESTFN) def tearDown(self): - os_helper.unlink(TESTFN2) + support.unlink(TESTFN2) @contextlib.contextmanager def get_files(self): @@ -2301,8 +2300,8 @@ class _ZeroCopyFileTest(object): def test_empty_file(self): srcname = TESTFN + 'src' dstname = TESTFN + 'dst' - self.addCleanup(lambda: os_helper.unlink(srcname)) - self.addCleanup(lambda: os_helper.unlink(dstname)) + self.addCleanup(lambda: support.unlink(srcname)) + self.addCleanup(lambda: support.unlink(dstname)) with open(srcname, "wb"): pass @@ -2428,9 +2427,9 @@ class TestZeroCopySendfile(_ZeroCopyFileTest, unittest.TestCase): # sendfile() are the same. self.assertEqual(blocksize, os.path.getsize(TESTFN)) # ...unless we're dealing with a small file. - os_helper.unlink(TESTFN2) + support.unlink(TESTFN2) write_file(TESTFN2, b"hello", binary=True) - self.addCleanup(os_helper.unlink, TESTFN2 + '3') + self.addCleanup(support.unlink, TESTFN2 + '3') self.assertRaises(ZeroDivisionError, shutil.copyfile, TESTFN2, TESTFN2 + '3') blocksize = m.call_args[0][3] @@ -2480,20 +2479,20 @@ class TermsizeTests(unittest.TestCase): def test_os_environ_first(self): "Check if environment variables have precedence" - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['COLUMNS'] = '777' del env['LINES'] size = shutil.get_terminal_size() self.assertEqual(size.columns, 777) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: del env['COLUMNS'] env['LINES'] = '888' size = shutil.get_terminal_size() self.assertEqual(size.lines, 888) def test_bad_environ(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['COLUMNS'] = 'xxx' env['LINES'] = 'yyy' size = shutil.get_terminal_size() @@ -2517,7 +2516,7 @@ class TermsizeTests(unittest.TestCase): self.skipTest("stty invocation failed") expected = (int(size[1]), int(size[0])) # reversed order - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: del env['LINES'] del env['COLUMNS'] actual = shutil.get_terminal_size() @@ -2525,7 +2524,7 @@ class TermsizeTests(unittest.TestCase): self.assertEqual(expected, actual) def test_fallback(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: del env['LINES'] del env['COLUMNS'] diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 8d59a5153..3f61915a3 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -10,7 +10,6 @@ import threading import time import unittest from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok, spawn_python try: import _testcapi @@ -172,7 +171,7 @@ class WakeupFDTests(unittest.TestCase): signal.set_wakeup_fd(signal.SIGINT, False) def test_invalid_fd(self): - fd = os_helper.make_bad_fd() + fd = support.make_bad_fd() self.assertRaises((ValueError, OSError), signal.set_wakeup_fd, fd) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 94592d907..921daf2a1 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper import errno import io @@ -646,7 +645,7 @@ class UnixSocketTestBase(SocketTestBase): def bindSock(self, sock): path = tempfile.mktemp(dir=self.dir_path) support.bind_unix_socket(sock, path) - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) class UnixStreamBase(UnixSocketTestBase): """Base class for Unix-domain SOCK_STREAM tests.""" @@ -1836,14 +1835,14 @@ class GeneralModuleTests(unittest.TestCase): def test_socket_fileno_requires_valid_fd(self): WSAENOTSOCK = 10038 with self.assertRaises(OSError) as cm: - socket.socket(fileno=os_helper.make_bad_fd()) + socket.socket(fileno=support.make_bad_fd()) self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) with self.assertRaises(OSError) as cm: socket.socket( socket.AF_INET, socket.SOCK_STREAM, - fileno=os_helper.make_bad_fd()) + fileno=support.make_bad_fd()) self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK)) def test_socket_fileno_requires_socket_fd(self): @@ -5137,16 +5136,16 @@ class TestUnixDomain(unittest.TestCase): def testStrAddr(self): # Test binding to and retrieving a normal string pathname. - path = os.path.abspath(os_helper.TESTFN) + path = os.path.abspath(support.TESTFN) self.bind(self.sock, path) - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) self.assertEqual(self.sock.getsockname(), path) def testBytesAddr(self): # Test binding to a bytes pathname. - path = os.path.abspath(os_helper.TESTFN) + path = os.path.abspath(support.TESTFN) self.bind(self.sock, self.encoded(path)) - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) self.assertEqual(self.sock.getsockname(), path) # TODO: RUSTPYTHON, surrogateescape @@ -5157,17 +5156,17 @@ class TestUnixDomain(unittest.TestCase): path = os.path.abspath(support.TESTFN_UNICODE) b = self.encoded(path) self.bind(self.sock, b.decode("ascii", "surrogateescape")) - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) self.assertEqual(self.sock.getsockname(), path) def testUnencodableAddr(self): # Test binding to a pathname that cannot be encoded in the # file system encoding. - if os_helper.TESTFN_UNENCODABLE is None: + if support.TESTFN_UNENCODABLE is None: self.skipTest("No unencodable filename available") - path = os.path.abspath(os_helper.TESTFN_UNENCODABLE) + path = os.path.abspath(support.TESTFN_UNENCODABLE) self.bind(self.sock, path) - self.addCleanup(os_helper.unlink, path) + self.addCleanup(support.unlink, path) self.assertEqual(self.sock.getsockname(), path) @@ -5642,16 +5641,16 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): chunk = b"".join([random.choice(string.ascii_letters).encode() for i in range(cls.BUFSIZE)]) - with open(os_helper.TESTFN, 'wb') as f: + with open(support.TESTFN, 'wb') as f: for csize in chunks(cls.FILESIZE, cls.BUFSIZE): f.write(chunk) - with open(os_helper.TESTFN, 'rb') as f: + with open(support.TESTFN, 'rb') as f: cls.FILEDATA = f.read() assert len(cls.FILEDATA) == cls.FILESIZE @classmethod def tearDownClass(cls): - os_helper.unlink(os_helper.TESTFN) + support.unlink(support.TESTFN) def accept_conn(self): self.serv.settimeout(MAIN_TIMEOUT) @@ -5678,7 +5677,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testRegularFile(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file) @@ -5713,9 +5712,9 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testEmptyFileSend(self): address = self.serv.getsockname() - filename = os_helper.TESTFN + "2" + filename = support.TESTFN + "2" with open(filename, 'wb'): - self.addCleanup(os_helper.unlink, filename) + self.addCleanup(support.unlink, filename) file = open(filename, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) @@ -5732,7 +5731,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testOffset(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file, offset=5000) @@ -5749,7 +5748,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCount(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 5000007 meth = self.meth_from_sock(sock) @@ -5769,7 +5768,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCountSmall(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 1 meth = self.meth_from_sock(sock) @@ -5788,7 +5787,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testCountWithOffset(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: count = 100007 meth = self.meth_from_sock(sock) @@ -5807,7 +5806,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testNonBlocking(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address) as sock, file as file: sock.setblocking(False) meth = self.meth_from_sock(sock) @@ -5823,7 +5822,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testWithTimeout(self): address = self.serv.getsockname() - file = open(os_helper.TESTFN, 'rb') + file = open(support.TESTFN, 'rb') with socket.create_connection(address, timeout=2) as sock, file as file: meth = self.meth_from_sock(sock) sent = meth(file) @@ -5840,7 +5839,7 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): def _testWithTimeoutTriggeredSend(self): address = self.serv.getsockname() - with open(os_helper.TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: with socket.create_connection(address) as sock: sock.settimeout(0.01) meth = self.meth_from_sock(sock) @@ -5856,17 +5855,17 @@ class SendfileUsingSendTest(ThreadedTCPSocketTest): pass def test_errors(self): - with open(os_helper.TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: with socket.socket(type=socket.SOCK_DGRAM) as s: meth = self.meth_from_sock(s) self.assertRaisesRegex( ValueError, "SOCK_STREAM", meth, file) - with open(os_helper.TESTFN, 'rt') as file: + with open(support.TESTFN, 'rt') as file: with socket.socket() as s: meth = self.meth_from_sock(s) self.assertRaisesRegex( ValueError, "binary mode", meth, file) - with open(os_helper.TESTFN, 'rb') as file: + with open(support.TESTFN, 'rb') as file: with socket.socket() as s: meth = self.meth_from_sock(s) self.assertRaisesRegex(TypeError, "positive integer", diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index ea77da357..63fc7c597 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -14,7 +14,7 @@ import unittest import socketserver import test.support -from test.support import reap_children, reap_threads, verbose, os_helper +from test.support import reap_children, reap_threads, verbose test.support.requires("network") @@ -306,7 +306,7 @@ class ErrorHandlerTest(unittest.TestCase): KeyboardInterrupt are not passed.""" def tearDown(self): - test.os_helper.unlink(test.os_helper.TESTFN) + test.support.unlink(test.support.TESTFN) def test_sync_handled(self): BaseErrorTestServer(ValueError) @@ -336,7 +336,7 @@ class ErrorHandlerTest(unittest.TestCase): self.check_result(handled=False) def check_result(self, handled): - with open(test.os_helper.TESTFN) as log: + with open(test.support.TESTFN) as log: expected = 'Handler called\n' + 'Error handled\n' * handled self.assertEqual(log.read(), expected) @@ -354,7 +354,7 @@ class BaseErrorTestServer(socketserver.TCPServer): self.wait_done() def handle_error(self, request, client_address): - with open(test.os_helper.TESTFN, 'a') as log: + with open(test.support.TESTFN, 'a') as log: log.write('Error handled\n') def wait_done(self): @@ -363,7 +363,7 @@ class BaseErrorTestServer(socketserver.TCPServer): class BadHandler(socketserver.BaseRequestHandler): def handle(self): - with open(test.os_helper.TESTFN, 'a') as log: + with open(test.support.TESTFN, 'a') as log: log.write('Handler called\n') raise self.server.exception('Test error') diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index 395f60a04..10fe68849 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -2,9 +2,8 @@ import unittest import os import socket import sys -from test.support import (import_fresh_module, +from test.support import (TESTFN, import_fresh_module, skip_unless_bind_unix_socket) -from test.support.os_helper import TESTFN c_stat = import_fresh_module('stat', fresh=['_stat']) py_stat = import_fresh_module('stat', blocked=['_stat']) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index a125f8f04..07c4bee78 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -18,8 +18,7 @@ import shutil import threading import gc import textwrap -from test.support import os_helper -from test.support.os_helper import FakePath +from test.support import FakePath try: import _testcapi @@ -383,7 +382,7 @@ class ProcessTestCase(BaseTestCase): # Normalize an expected cwd (for Tru64 support). # We can't use os.path.realpath since it doesn't expand Tru64 {memb} # strings. See bug #1063571. - with os_helper.change_cwd(cwd): + with support.change_cwd(cwd): return os.getcwd() # For use in the test_cwd* tests below. @@ -434,7 +433,7 @@ class ProcessTestCase(BaseTestCase): # is relative. python_dir, python_base = self._split_python_path() rel_python = os.path.join(os.curdir, python_base) - with os_helper.temp_cwd() as wrong_dir: + with support.temp_cwd() as wrong_dir: # Before calling with the correct cwd, confirm that the call fails # without cwd and with the wrong cwd. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -451,7 +450,7 @@ class ProcessTestCase(BaseTestCase): python_dir, python_base = self._split_python_path() rel_python = os.path.join(os.curdir, python_base) doesntexist = "somethingyoudonthave" - with os_helper.temp_cwd() as wrong_dir: + with support.temp_cwd() as wrong_dir: # Before calling with the correct cwd, confirm that the call fails # without cwd and with the wrong cwd. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -469,7 +468,7 @@ class ProcessTestCase(BaseTestCase): python_dir, python_base = self._split_python_path() abs_python = os.path.join(python_dir, python_base) rel_python = os.path.join(os.curdir, python_base) - with os_helper.temp_dir() as wrong_dir: + with support.temp_dir() as wrong_dir: # Before calling with an absolute path, confirm that using a # relative path fails. self.assertRaises(FileNotFoundError, subprocess.Popen, @@ -1103,7 +1102,7 @@ class ProcessTestCase(BaseTestCase): try: for i in range(max_handles): try: - tmpfile = os.path.join(tmpdir, os_helper.TESTFN) + tmpfile = os.path.join(tmpdir, support.TESTFN) handles.append(os.open(tmpfile, os.O_WRONLY|os.O_CREAT)) except OSError as e: if e.errno != errno.EMFILE: diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 6a872d95d..dcf5ae5d0 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -13,9 +13,9 @@ import textwrap import time import unittest from test import support -from test.support import script_helper, os_helper +from test.support import script_helper -TESTFN = os_helper.TESTFN +TESTFN = support.TESTFN class TestSupport(unittest.TestCase): @@ -45,32 +45,32 @@ class TestSupport(unittest.TestCase): def test_unlink(self): with open(TESTFN, "w") as f: pass - os_helper.unlink(TESTFN) + support.unlink(TESTFN) self.assertFalse(os.path.exists(TESTFN)) - os_helper.unlink(TESTFN) + support.unlink(TESTFN) def test_rmtree(self): - dirpath = os_helper.TESTFN + 'd' + dirpath = support.TESTFN + 'd' subdirpath = os.path.join(dirpath, 'subdir') os.mkdir(dirpath) os.mkdir(subdirpath) - os_helper.rmtree(dirpath) + support.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) with support.swap_attr(support, 'verbose', 0): - os_helper.rmtree(dirpath) + support.rmtree(dirpath) os.mkdir(dirpath) os.mkdir(subdirpath) os.chmod(dirpath, stat.S_IRUSR|stat.S_IXUSR) with support.swap_attr(support, 'verbose', 0): - os_helper.rmtree(dirpath) + support.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) os.mkdir(dirpath) os.mkdir(subdirpath) os.chmod(dirpath, 0) with support.swap_attr(support, 'verbose', 0): - os_helper.rmtree(dirpath) + support.rmtree(dirpath) self.assertFalse(os.path.exists(dirpath)) def test_forget(self): @@ -87,8 +87,8 @@ class TestSupport(unittest.TestCase): self.assertNotIn(TESTFN, sys.modules) finally: del sys.path[0] - os_helper.unlink(mod_filename) - os_helper.rmtree('__pycache__') + support.unlink(mod_filename) + support.rmtree('__pycache__') def test_HOST(self): s = socket.create_server((support.HOST, 0)) @@ -115,23 +115,23 @@ class TestSupport(unittest.TestCase): try: path = os.path.join(parent_dir, 'temp') self.assertFalse(os.path.isdir(path)) - with os_helper.temp_dir(path) as temp_path: + with support.temp_dir(path) as temp_path: self.assertEqual(temp_path, path) self.assertTrue(os.path.isdir(path)) self.assertFalse(os.path.isdir(path)) finally: - os_helper.rmtree(parent_dir) + support.rmtree(parent_dir) def test_temp_dir__path_none(self): """Test passing no path.""" - with os_helper.temp_dir() as temp_path: + with support.temp_dir() as temp_path: self.assertTrue(os.path.isdir(temp_path)) self.assertFalse(os.path.isdir(temp_path)) def test_temp_dir__existing_dir__quiet_default(self): """Test passing a directory that already exists.""" def call_temp_dir(path): - with os_helper.temp_dir(path) as temp_path: + with support.temp_dir(path) as temp_path: raise Exception("should not get here") path = tempfile.mkdtemp() @@ -151,7 +151,7 @@ class TestSupport(unittest.TestCase): try: with support.check_warnings() as recorder: - with os_helper.temp_dir(path, quiet=True) as temp_path: + with support.temp_dir(path, quiet=True) as temp_path: self.assertEqual(path, temp_path) warnings = [str(w.message) for w in recorder.warnings] # Make sure temp_dir did not delete the original directory. @@ -173,7 +173,7 @@ class TestSupport(unittest.TestCase): script_helper.assert_python_ok("-c", textwrap.dedent(""" import os from test import support - with os_helper.temp_cwd() as temp_path: + with support.temp_cwd() as temp_path: pid = os.fork() if pid != 0: # parent process (child has pid == 0) @@ -197,8 +197,8 @@ class TestSupport(unittest.TestCase): def test_change_cwd(self): original_cwd = os.getcwd() - with os_helper.temp_dir() as temp_path: - with os_helper.change_cwd(temp_path) as new_cwd: + with support.temp_dir() as temp_path: + with support.change_cwd(temp_path) as new_cwd: self.assertEqual(new_cwd, temp_path) self.assertEqual(os.getcwd(), new_cwd) @@ -209,10 +209,10 @@ class TestSupport(unittest.TestCase): original_cwd = os.getcwd() def call_change_cwd(path): - with os_helper.change_cwd(path) as new_cwd: + with support.change_cwd(path) as new_cwd: raise Exception("should not get here") - with os_helper.temp_dir() as parent_dir: + with support.temp_dir() as parent_dir: non_existent_dir = os.path.join(parent_dir, 'does_not_exist') self.assertRaises(FileNotFoundError, call_change_cwd, non_existent_dir) @@ -223,10 +223,10 @@ class TestSupport(unittest.TestCase): """Test passing a non-existent directory with quiet=True.""" original_cwd = os.getcwd() - with os_helper.temp_dir() as parent_dir: + with support.temp_dir() as parent_dir: bad_dir = os.path.join(parent_dir, 'does_not_exist') with support.check_warnings() as recorder: - with os_helper.change_cwd(bad_dir, quiet=True) as new_cwd: + with support.change_cwd(bad_dir, quiet=True) as new_cwd: self.assertEqual(new_cwd, original_cwd) self.assertEqual(os.getcwd(), new_cwd) warnings = [str(w.message) for w in recorder.warnings] @@ -244,7 +244,7 @@ class TestSupport(unittest.TestCase): """Check the warning message when os.chdir() fails.""" path = TESTFN + '_does_not_exist' with support.check_warnings() as recorder: - with os_helper.change_cwd(path=path, quiet=True): + with support.change_cwd(path=path, quiet=True): pass messages = [str(w.message) for w in recorder.warnings] @@ -259,7 +259,7 @@ class TestSupport(unittest.TestCase): def test_temp_cwd(self): here = os.getcwd() - with os_helper.temp_cwd(name=TESTFN): + with support.temp_cwd(name=TESTFN): self.assertEqual(os.path.basename(os.getcwd()), TESTFN) self.assertFalse(os.path.exists(TESTFN)) self.assertEqual(os.getcwd(), here) @@ -268,7 +268,7 @@ class TestSupport(unittest.TestCase): def test_temp_cwd__name_none(self): """Test passing None to temp_cwd().""" original_cwd = os.getcwd() - with os_helper.temp_cwd(name=None) as new_cwd: + with support.temp_cwd(name=None) as new_cwd: self.assertNotEqual(new_cwd, original_cwd) self.assertTrue(os.path.isdir(new_cwd)) self.assertEqual(os.getcwd(), new_cwd) @@ -279,7 +279,7 @@ class TestSupport(unittest.TestCase): @unittest.skipIf(sys.platform.startswith("win"), "TODO: RUSTPYTHON; actual c fds on windows") def test_make_bad_fd(self): - fd = os_helper.make_bad_fd() + fd = support.make_bad_fd() with self.assertRaises(OSError) as cm: os.write(fd, b"foo") self.assertEqual(cm.exception.errno, errno.EBADF) @@ -390,14 +390,14 @@ class TestSupport(unittest.TestCase): def test_check__all__(self): extra = {'tempdir'} - not_exported = {'template'} + blacklist = {'template'} support.check__all__(self, tempfile, extra=extra, - not_exported=not_exported) + blacklist=blacklist) extra = {'TextTestResult', 'installHandler'} - not_exported = {'load_tests', "TestProgram", "BaseTestSuite"} + blacklist = {'load_tests', "TestProgram", "BaseTestSuite"} support.check__all__(self, unittest, @@ -406,7 +406,7 @@ class TestSupport(unittest.TestCase): "unittest.main", "unittest.runner", "unittest.signals", "unittest.async_case"), extra=extra, - not_exported=not_exported) + blacklist=blacklist) self.assertRaises(AssertionError, support.check__all__, self, unittest) @@ -632,18 +632,24 @@ class TestSupport(unittest.TestCase): # We cannot test the absolute value of fd_count(): on old Linux # kernel or glibc versions, os.urandom() keeps a FD open on # /dev/urandom device and Python has 4 FD opens instead of 3. - start = os_helper.fd_count() + start = support.fd_count() fd = os.open(__file__, os.O_RDONLY) try: - more = os_helper.fd_count() + more = support.fd_count() finally: os.close(fd) self.assertEqual(more - start, 1) def check_print_warning(self, msg, expected): stderr = io.StringIO() - with support.swap_attr(support.print_warning, 'orig_stderr', stderr): + + old_stderr = sys.__stderr__ + try: + sys.__stderr__ = stderr support.print_warning(msg) + finally: + sys.__stderr__ = old_stderr + self.assertEqual(stderr.getvalue(), expected) def test_print_warning(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 666f6a7cd..fb387f836 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,5 +1,4 @@ from test import support -from test.support import os_helper from test.support.script_helper import assert_python_ok, assert_python_failure import builtins import codecs @@ -652,7 +651,7 @@ class SysModuleTest(unittest.TestCase): out = p.communicate()[0].strip() self.assertEqual(out, b'\xbd') - @unittest.skipUnless(os_helper.FS_NONASCII, + @unittest.skipUnless(test.support.FS_NONASCII, 'requires OS support of non-ASCII encodings') @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), 'requires FS encoding to match locale') @@ -661,10 +660,10 @@ class SysModuleTest(unittest.TestCase): env["PYTHONIOENCODING"] = "" p = subprocess.Popen([sys.executable, "-c", - 'print(%a)' % os_helper.FS_NONASCII], + 'print(%a)' % test.support.FS_NONASCII], stdout=subprocess.PIPE, env=env) out = p.communicate()[0].strip() - self.assertEqual(out, os.fsencode(os_helper.FS_NONASCII)) + self.assertEqual(out, os.fsencode(test.support.FS_NONASCII)) @unittest.skipIf(sys.base_prefix != sys.prefix, 'Test is not venv-compatible') diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index b2e722b16..40b399935 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -5,10 +5,9 @@ import subprocess import shutil from copy import copy -from test.support import (import_module, check_warnings, - captured_stdout, PythonSymlink) -from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, - change_cwd) +from test.support import (import_module, TESTFN, unlink, check_warnings, + captured_stdout, skip_unless_symlink, change_cwd, + PythonSymlink) import sysconfig from sysconfig import (get_paths, get_platform, get_config_vars, diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index f58284434..77a021855 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -11,7 +11,7 @@ import unittest.mock import tarfile from test import support -from test.support import script_helper, requires_hashdigest, os_helper +from test.support import script_helper, requires_hashdigest # Check for our compression modules. try: @@ -30,7 +30,7 @@ except ImportError: def sha256sum(data): return sha256(data).hexdigest() -TEMPDIR = os.path.abspath(os_helper.TESTFN) + "-tardir" +TEMPDIR = os.path.abspath(support.TESTFN) + "-tardir" tarextdir = TEMPDIR + '-extract-test' tarname = support.findfile("testtar.tar") gzipname = os.path.join(TEMPDIR, "testtar.tar.gz") @@ -552,21 +552,21 @@ class MiscReadTestBase(CommonReadTest): @unittest.skipUnless(hasattr(os, "link"), "Missing hardlink implementation") - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_extract_hardlink(self): # Test hardlink extraction (e.g. bug #857297). with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar: tar.extract("ustar/regtype", TEMPDIR) - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/regtype")) + self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype")) tar.extract("ustar/lnktype", TEMPDIR) - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) + self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype")) with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f: data = f.read() self.assertEqual(sha256sum(data), sha256_regtype) tar.extract("ustar/symtype", TEMPDIR) - self.addCleanup(os_helper.unlink, os.path.join(TEMPDIR, "ustar/symtype")) + self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype")) with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f: data = f.read() self.assertEqual(sha256sum(data), sha256_regtype) @@ -599,7 +599,7 @@ class MiscReadTestBase(CommonReadTest): self.assertEqual(tarinfo.mtime, file_mtime, errmsg) finally: tar.close() - os_helper.rmtree(DIR) + support.rmtree(DIR) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -618,7 +618,7 @@ class MiscReadTestBase(CommonReadTest): if sys.platform != "win32": self.assertEqual(os.stat(extracted).st_mode & 0o777, 0o755) finally: - os_helper.rmtree(DIR) + support.rmtree(DIR) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -626,7 +626,7 @@ class MiscReadTestBase(CommonReadTest): def test_extractall_pathlike_name(self): DIR = pathlib.Path(TEMPDIR) / "extractall" - with os_helper.temp_dir(DIR), \ + with support.temp_dir(DIR), \ tarfile.open(tarname, encoding="iso8859-1") as tar: directories = [t for t in tar if t.isdir()] tar.extractall(DIR, directories) @@ -641,7 +641,7 @@ class MiscReadTestBase(CommonReadTest): def test_extract_pathlike_name(self): dirtype = "ustar/dirtype" DIR = pathlib.Path(TEMPDIR) / "extractall" - with os_helper.temp_dir(DIR), \ + with support.temp_dir(DIR), \ tarfile.open(tarname, encoding="iso8859-1") as tar: tarinfo = tar.getmember(dirtype) tar.extract(tarinfo, path=DIR) @@ -669,7 +669,7 @@ class MiscReadTestBase(CommonReadTest): else: self.fail("ReadError not raised") finally: - os_helper.unlink(empty) + support.unlink(empty) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -1037,7 +1037,7 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase): fobj.write(b'x' * 4096) fobj.truncate() s = os.stat(name) - os_helper.unlink(name) + support.unlink(name) return (s.st_blocks * 512 < s.st_size) else: return False @@ -1204,8 +1204,8 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - os_helper.unlink(os.path.join(path, "1")) - os_helper.unlink(os.path.join(path, "2")) + support.unlink(os.path.join(path, "1")) + support.unlink(os.path.join(path, "2")) support.rmdir(path) def test_gettarinfo_pathlike_name(self): @@ -1240,14 +1240,14 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - os_helper.unlink(target) - os_helper.unlink(link) + support.unlink(target) + support.unlink(link) # TODO: RUSTPYTHON if sys.platform == "win32": test_link_size = unittest.expectedFailure(test_link_size) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_symlink_size(self): path = os.path.join(TEMPDIR, "symlink") os.symlink("link_target", path) @@ -1259,7 +1259,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - os_helper.unlink(path) + support.unlink(path) # TODO: RUSTPYTHON if sys.platform == "win32": @@ -1276,7 +1276,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): self.assertEqual(tar.getnames(), [], "added the archive to itself") - with os_helper.change_cwd(TEMPDIR): + with support.change_cwd(TEMPDIR): tar.add(dstname) self.assertEqual(tar.getnames(), [], "added the archive to itself") @@ -1289,7 +1289,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): try: for name in ("foo", "bar", "baz"): name = os.path.join(tempdir, name) - os_helper.create_empty_file(name) + support.create_empty_file(name) def filter(tarinfo): if os.path.basename(tarinfo.name) == "bar": @@ -1317,7 +1317,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): finally: tar.close() finally: - os_helper.rmtree(tempdir) + support.rmtree(tempdir) # Guarantee that stored pathnames are not modified. Don't # remove ./ or ../ or double slashes. Still make absolute @@ -1328,7 +1328,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): # and compare the stored name with the original. foo = os.path.join(TEMPDIR, "foo") if not dir: - os_helper.create_empty_file(foo) + support.create_empty_file(foo) else: os.mkdir(foo) @@ -1345,14 +1345,14 @@ class WriteTest(WriteTestBase, unittest.TestCase): tar.close() if not dir: - os_helper.unlink(foo) + support.unlink(foo) else: support.rmdir(foo) self.assertEqual(t.name, cmp_path or path.replace(os.sep, "/")) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_extractall_symlinks(self): # Test if extractall works properly when tarfile contains symlinks tempdir = os.path.join(TEMPDIR, "testsymlinks") @@ -1375,8 +1375,8 @@ class WriteTest(WriteTestBase, unittest.TestCase): except OSError: self.fail("extractall failed with symlinked files") finally: - os_helper.unlink(temparchive) - os_helper.rmtree(tempdir) + support.unlink(temparchive) + support.rmtree(tempdir) # TODO: RUSTPYTHON if sys.platform != "win32": @@ -1408,7 +1408,7 @@ class WriteTest(WriteTestBase, unittest.TestCase): def test_cwd(self): # Test adding the current working directory. - with os_helper.change_cwd(TEMPDIR): + with support.change_cwd(TEMPDIR): tar = tarfile.open(tmpname, self.mode) try: tar.add(".") @@ -1499,7 +1499,7 @@ class StreamWriteTest(WriteTestBase, unittest.TestCase): # Test for issue #8464: Create files with correct # permissions. if os.path.exists(tmpname): - os_helper.unlink(tmpname) + support.unlink(tmpname) original_umask = os.umask(0o022) try: @@ -1620,7 +1620,7 @@ class CreateTest(WriteTestBase, unittest.TestCase): file_path = os.path.join(TEMPDIR, "spameggs42") def setUp(self): - os_helper.unlink(tmpname) + support.unlink(tmpname) @classmethod def setUpClass(cls): @@ -1629,7 +1629,7 @@ class CreateTest(WriteTestBase, unittest.TestCase): @classmethod def tearDownClass(cls): - os_helper.unlink(cls.file_path) + support.unlink(cls.file_path) def test_create(self): with tarfile.open(tmpname, self.mode) as tobj: @@ -1744,8 +1744,8 @@ class HardlinkTest(unittest.TestCase): def tearDown(self): self.tar.close() - os_helper.unlink(self.foo) - os_helper.unlink(self.bar) + support.unlink(self.foo) + support.unlink(self.bar) def test_add_twice(self): # The same name will be added as a REGTYPE every @@ -2074,7 +2074,7 @@ class AppendTestBase: def setUp(self): self.tarname = tmpname if os.path.exists(self.tarname): - os_helper.unlink(self.tarname) + support.unlink(self.tarname) def _create_testtar(self, mode="w:"): with tarfile.open(tarname, encoding="iso8859-1") as src: @@ -2287,7 +2287,7 @@ class MiscTest(unittest.TestCase): tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT) def test__all__(self): - not_exported = {'version', 'grp', 'pwd', 'symlink_exception', + blacklist = {'version', 'grp', 'pwd', 'symlink_exception', 'NUL', 'BLOCKSIZE', 'RECORDSIZE', 'GNU_MAGIC', 'POSIX_MAGIC', 'LENGTH_NAME', 'LENGTH_LINK', 'LENGTH_PREFIX', 'REGTYPE', 'AREGTYPE', 'LNKTYPE', @@ -2302,7 +2302,7 @@ class MiscTest(unittest.TestCase): 'EOFHeaderError', 'InvalidHeaderError', 'SubsequentHeaderError', 'ExFileObject', 'main'} - support.check__all__(self, tarfile, not_exported=not_exported) + support.check__all__(self, tarfile, blacklist=blacklist) class CommandLineTest(unittest.TestCase): @@ -2319,7 +2319,7 @@ class CommandLineTest(unittest.TestCase): files = [support.findfile('tokenize_tests.txt'), support.findfile('tokenize_tests-no-coding-cookie-' 'and-utf8-bom-sig-only.txt')] - self.addCleanup(os_helper.unlink, tar_name) + self.addCleanup(support.unlink, tar_name) with tarfile.open(tar_name, 'w') as tf: for tardata in files: tf.add(tardata, arcname=os.path.basename(tardata)) @@ -2368,7 +2368,7 @@ class CommandLineTest(unittest.TestCase): self.assertEqual(out, b'') self.assertEqual(rc, 1) finally: - os_helper.unlink(tmpname) + support.unlink(tmpname) # TODO: RUSTPYTHON @unittest.expectedFailure @@ -2414,7 +2414,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tmpname) as tar: tar.getmembers() finally: - os_helper.unlink(tmpname) + support.unlink(tmpname) def test_create_command_verbose(self): files = [support.findfile('tokenize_tests.txt'), @@ -2427,7 +2427,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tmpname) as tar: tar.getmembers() finally: - os_helper.unlink(tmpname) + support.unlink(tmpname) def test_create_command_dotless_filename(self): files = [support.findfile('tokenize_tests.txt')] @@ -2437,7 +2437,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(dotlessname) as tar: tar.getmembers() finally: - os_helper.unlink(dotlessname) + support.unlink(dotlessname) def test_create_command_dot_started_filename(self): tar_name = os.path.join(TEMPDIR, ".testtar") @@ -2448,7 +2448,7 @@ class CommandLineTest(unittest.TestCase): with tarfile.open(tar_name) as tar: tar.getmembers() finally: - os_helper.unlink(tar_name) + support.unlink(tar_name) def test_create_command_compressed(self): files = [support.findfile('tokenize_tests.txt'), @@ -2463,40 +2463,40 @@ class CommandLineTest(unittest.TestCase): with filetype.taropen(tar_name) as tar: tar.getmembers() finally: - os_helper.unlink(tar_name) + support.unlink(tar_name) def test_extract_command(self): self.make_simple_tarfile(tmpname) for opt in '-e', '--extract': try: - with os_helper.temp_cwd(tarextdir): + with support.temp_cwd(tarextdir): out = self.tarfilecmd(opt, tmpname) self.assertEqual(out, b'') finally: - os_helper.rmtree(tarextdir) + support.rmtree(tarextdir) def test_extract_command_verbose(self): self.make_simple_tarfile(tmpname) for opt in '-v', '--verbose': try: - with os_helper.temp_cwd(tarextdir): + with support.temp_cwd(tarextdir): out = self.tarfilecmd(opt, '-e', tmpname) self.assertIn(b' file is extracted.', out) finally: - os_helper.rmtree(tarextdir) + support.rmtree(tarextdir) def test_extract_command_different_directory(self): self.make_simple_tarfile(tmpname) try: - with os_helper.temp_cwd(tarextdir): + with support.temp_cwd(tarextdir): out = self.tarfilecmd('-e', tmpname, 'spamdir') self.assertEqual(out, b'') finally: - os_helper.rmtree(tarextdir) + support.rmtree(tarextdir) def test_extract_command_invalid_file(self): zipname = support.findfile('zipdir.zip') - with os_helper.temp_cwd(tarextdir): + with support.temp_cwd(tarextdir): rc, out, err = self.tarfilecmd_failure('-e', zipname) self.assertIn(b' is not a tar archive.', err) self.assertEqual(out, b'') @@ -2759,7 +2759,7 @@ class NumericOwnerTest(unittest.TestCase): def setUpModule(): - os_helper.unlink(TEMPDIR) + support.unlink(TEMPDIR) os.makedirs(TEMPDIR) global testtarnames @@ -2770,14 +2770,14 @@ def setUpModule(): # Create compressed tarfiles. for c in GzipTest, Bz2Test, LzmaTest: if c.open: - os_helper.unlink(c.tarname) + support.unlink(c.tarname) testtarnames.append(c.tarname) with c.open(c.tarname, "wb") as tar: tar.write(data) def tearDownModule(): if os.path.exists(TEMPDIR): - os_helper.rmtree(TEMPDIR) + support.rmtree(TEMPDIR) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index d79e9abf8..3f8a6f0fd 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -15,7 +15,7 @@ from unittest import mock import unittest from test import support -from test.support import script_helper, os_helper +from test.support import script_helper has_textmode = (tempfile._text_openflags != tempfile._bin_openflags) @@ -232,7 +232,7 @@ class TestCandidateTempdirList(BaseTestCase): # _candidate_tempdir_list contains the expected directories # Make sure the interesting environment variables are all set. - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: for envname in 'TMPDIR', 'TEMP', 'TMP': dirname = os.getenv(envname) if not dirname: @@ -318,7 +318,7 @@ def _inside_empty_temp_dir(): with support.swap_attr(tempfile, 'tempdir', dir): yield finally: - os_helper.rmtree(dir) + support.rmtree(dir) def _mock_candidate_names(*names): @@ -602,7 +602,7 @@ class TestGetTempDir(BaseTestCase): case_sensitive_tempdir = tempfile.mkdtemp("-Temp") _tempdir, tempfile.tempdir = tempfile.tempdir, None try: - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: # Fake the first env var which is checked as a candidate env["TMPDIR"] = case_sensitive_tempdir self.assertEqual(tempfile.gettempdir(), case_sensitive_tempdir) @@ -958,7 +958,7 @@ class TestNamedTemporaryFile(BaseTestCase): def test_bad_mode(self): dir = tempfile.mkdtemp() - self.addCleanup(os_helper.rmtree, dir) + self.addCleanup(support.rmtree, dir) with self.assertRaises(ValueError): tempfile.NamedTemporaryFile(mode='wr', dir=dir) with self.assertRaises(TypeError): @@ -1364,7 +1364,7 @@ class TestTemporaryDirectory(BaseTestCase): finally: os.rmdir(dir) - @os_helper.skip_unless_symlink + @support.skip_unless_symlink def test_cleanup_with_symlink_to_a_directory(self): # cleanup() should not follow symlinks to directories (issue #12464) d1 = self.do_create() diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 5f99b7936..91ef35241 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -1376,9 +1376,9 @@ class MiscTestCase(unittest.TestCase): @unittest.expectedFailure def test__all__(self): extra = {"ThreadError"} - not_exported = {'currentThread', 'activeCount'} + blacklist = {'currentThread', 'activeCount'} support.check__all__(self, threading, ('threading', '_thread'), - extra=extra, not_exported=not_exported) + extra=extra, blacklist=blacklist) class InterruptMainTests(unittest.TestCase): diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index f7b05705d..9c185c2e6 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1,5 +1,4 @@ from test import support -from test.support import os_helper from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, open as tokenize_open, Untokenizer, generate_tokens, @@ -1307,8 +1306,8 @@ class TestDetectEncoding(TestCase): self.assertEqual(consumed_lines, [b'print("#coding=fake")']) def test_open(self): - filename = os_helper.TESTFN + '.py' - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + '.py' + self.addCleanup(support.unlink, filename) # test coding cookie for encoding in ('iso-8859-15', 'utf-8'): diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index e5183b90e..6cf886942 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -1,7 +1,6 @@ import os import sys -from test.support import captured_stdout -from test.support.os_helper import TESTFN, TESTFN_UNICODE, FS_NONASCII, rmtree, unlink +from test.support import TESTFN, TESTFN_UNICODE, FS_NONASCII, rmtree, unlink, captured_stdout from test.support.script_helper import assert_python_ok, assert_python_failure import textwrap import unittest diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 351c7723b..0864e373d 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -7,8 +7,7 @@ import sys import unittest import re from test import support -from test.support import Error, captured_output, cpython_only -from test.support.os_helper import TESTFN, unlink +from test.support import TESTFN, Error, captured_output, unlink, cpython_only from test.support.script_helper import assert_python_ok import textwrap diff --git a/Lib/test/test_unicode_file.py b/Lib/test/test_unicode_file.py index 80c22c6cd..e8feb42c6 100644 --- a/Lib/test/test_unicode_file.py +++ b/Lib/test/test_unicode_file.py @@ -2,17 +2,15 @@ # We don't test many operations on files other than # that their names can be used with Unicode characters. import os, glob, time, shutil -import sys import unicodedata import unittest -from test.support.os_helper import (rmtree, change_cwd, TESTFN_UNICODE, - TESTFN_UNENCODABLE, create_empty_file) - +from test.support import (run_unittest, rmtree, change_cwd, + TESTFN_ENCODING, TESTFN_UNICODE, TESTFN_UNENCODABLE, create_empty_file) if not os.path.supports_unicode_filenames: try: - TESTFN_UNICODE.encode(sys.getfilesystemencoding()) + TESTFN_UNICODE.encode(TESTFN_ENCODING) except (UnicodeError, TypeError): # Either the file system encoding is None, or the file name # cannot be encoded in the file system encoding. @@ -135,6 +133,8 @@ class TestUnicodeFiles(unittest.TestCase): self._do_directory(TESTFN_UNENCODABLE+ext, TESTFN_UNENCODABLE+ext) +def test_main(): + run_unittest(__name__) if __name__ == "__main__": - unittest.main() + test_main() diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py index c4bb3e186..a9986469c 100644 --- a/Lib/test/test_unicode_file_functions.py +++ b/Lib/test/test_unicode_file_functions.py @@ -6,7 +6,6 @@ import unittest import warnings from unicodedata import normalize from test import support -from test.support import os_helper filenames = [ '1_abc', @@ -63,14 +62,14 @@ class UnicodeFileTests(unittest.TestCase): def setUp(self): try: - os.mkdir(os_helper.TESTFN) + os.mkdir(support.TESTFN) except FileExistsError: pass - self.addCleanup(os_helper.rmtree, os_helper.TESTFN) + self.addCleanup(support.rmtree, support.TESTFN) files = set() for name in self.files: - name = os.path.join(os_helper.TESTFN, self.norm(name)) + name = os.path.join(support.TESTFN, self.norm(name)) with open(name, 'wb') as f: f.write((name+'\n').encode("utf-8")) os.stat(name) @@ -151,9 +150,9 @@ class UnicodeFileTests(unittest.TestCase): sf0 = set(self.files) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - f1 = os.listdir(os_helper.TESTFN.encode(sys.getfilesystemencoding())) - f2 = os.listdir(os_helper.TESTFN) - sf2 = set(os.path.join(os_helper.TESTFN, f) for f in f2) + f1 = os.listdir(support.TESTFN.encode(sys.getfilesystemencoding())) + f2 = os.listdir(support.TESTFN) + sf2 = set(os.path.join(support.TESTFN, f) for f in f2) self.assertEqual(sf0, sf2, "%a != %a" % (sf0, sf2)) self.assertEqual(len(f1), len(f2)) @@ -163,9 +162,9 @@ class UnicodeFileTests(unittest.TestCase): os.rename("tmp", name) def test_directory(self): - dirname = os.path.join(os_helper.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') + dirname = os.path.join(support.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') filename = '\xdf-\u66e8\u66e9\u66eb' - with os_helper.temp_cwd(dirname): + with support.temp_cwd(dirname): with open(filename, 'wb') as f: f.write((filename + '\n').encode("utf-8")) os.access(filename,os.R_OK) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 39f10b6de..e29ae3697 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -9,7 +9,6 @@ import io import unittest from unittest.mock import patch from test import support -from test.support import os_helper import os try: import ssl @@ -146,19 +145,18 @@ class urlopen_FileTests(unittest.TestCase): # Create a temp file to use for testing self.text = bytes("test_urllib: %s\n" % self.__class__.__name__, "ascii") - f = open(os_helper.TESTFN, 'wb') + f = open(support.TESTFN, 'wb') try: f.write(self.text) finally: f.close() - self.pathname = os_helper.TESTFN - self.quoted_pathname = urllib.parse.quote(self.pathname) - self.returned_obj = urlopen("file:%s" % self.quoted_pathname) + self.pathname = support.TESTFN + self.returned_obj = urlopen("file:%s" % self.pathname) def tearDown(self): """Shut down the open object""" self.returned_obj.close() - os.remove(os_helper.TESTFN) + os.remove(support.TESTFN) def test_interface(self): # Make sure object returned by urlopen() has the specified methods @@ -196,21 +194,11 @@ class urlopen_FileTests(unittest.TestCase): # by the tearDown() method for the test self.returned_obj.close() - def test_headers(self): - self.assertIsInstance(self.returned_obj.headers, email.message.Message) - - def test_url(self): - self.assertEqual(self.returned_obj.url, self.quoted_pathname) - - @unittest.skip("TODO: RUSTPYTHON (AttributeError: 'BufferedReader' object has no attribute 'status')") - def test_status(self): - self.assertIsNone(self.returned_obj.status) - def test_info(self): self.assertIsInstance(self.returned_obj.info(), email.message.Message) def test_geturl(self): - self.assertEqual(self.returned_obj.geturl(), self.quoted_pathname) + self.assertEqual(self.returned_obj.geturl(), self.pathname) def test_getcode(self): self.assertIsNone(self.returned_obj.getcode()) @@ -232,7 +220,7 @@ class ProxyTests(unittest.TestCase): def setUp(self): # Records changes to env vars - self.env = os_helper.EnvironmentVarGuard() + self.env = support.EnvironmentVarGuard() # Delete all proxy related env vars for k in list(os.environ): if 'proxy' in k.lower(): @@ -620,9 +608,6 @@ class urlopen_DataTests(unittest.TestCase): """Test urlopen() opening a data URL.""" def setUp(self): - # clear _opener global variable - self.addCleanup(urllib.request.urlcleanup) - # text containing URL special- and unicode-characters self.text = "test data URLs :;,%=& \u00f6 \u00c4 " # 2x1 pixel RGB PNG image with one black and one white pixel @@ -697,9 +682,6 @@ class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" def setUp(self): - # clear _opener global variable - self.addCleanup(urllib.request.urlcleanup) - # Create a list of temporary files. Each item in the list is a file # name (absolute path or relative to the current working directory). # All files in this list will be deleted in the tearDown method. Note, @@ -710,10 +692,10 @@ class urlretrieve_FileTests(unittest.TestCase): self.tempFiles = [] # Create a temporary file. - self.registerFileForCleanUp(os_helper.TESTFN) + self.registerFileForCleanUp(support.TESTFN) self.text = b'testing urllib.urlretrieve' try: - FILE = open(os_helper.TESTFN, 'wb') + FILE = open(support.TESTFN, 'wb') FILE.write(self.text) FILE.close() finally: @@ -756,18 +738,18 @@ class urlretrieve_FileTests(unittest.TestCase): def test_basic(self): # Make sure that a local file just gets its own location returned and # a headers value is returned. - result = urllib.request.urlretrieve("file:%s" % os_helper.TESTFN) - self.assertEqual(result[0], os_helper.TESTFN) + result = urllib.request.urlretrieve("file:%s" % support.TESTFN) + self.assertEqual(result[0], support.TESTFN) self.assertIsInstance(result[1], email.message.Message, "did not get an email.message.Message instance " "as second returned value") def test_copy(self): # Test that setting the filename argument works. - second_temp = "%s.2" % os_helper.TESTFN + second_temp = "%s.2" % support.TESTFN self.registerFileForCleanUp(second_temp) result = urllib.request.urlretrieve(self.constructLocalFileUrl( - os_helper.TESTFN), second_temp) + support.TESTFN), second_temp) self.assertEqual(second_temp, result[0]) self.assertTrue(os.path.exists(second_temp), "copy of the file was not " "made") @@ -788,10 +770,10 @@ class urlretrieve_FileTests(unittest.TestCase): self.assertIsInstance(file_size, int) self.assertEqual(block_count, count_holder[0]) count_holder[0] = count_holder[0] + 1 - second_temp = "%s.2" % os_helper.TESTFN + second_temp = "%s.2" % support.TESTFN self.registerFileForCleanUp(second_temp) urllib.request.urlretrieve( - self.constructLocalFileUrl(os_helper.TESTFN), + self.constructLocalFileUrl(support.TESTFN), second_temp, hooktester) def test_reporthook_0_bytes(self): @@ -801,7 +783,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile() urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - os_helper.TESTFN, hooktester) + support.TESTFN, hooktester) self.assertEqual(len(report), 1) self.assertEqual(report[0][2], 0) @@ -814,7 +796,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile(b"x" * 5) urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - os_helper.TESTFN, hooktester) + support.TESTFN, hooktester) self.assertEqual(len(report), 2) self.assertEqual(report[0][2], 5) self.assertEqual(report[1][2], 5) @@ -828,7 +810,7 @@ class urlretrieve_FileTests(unittest.TestCase): _report.append((block_count, block_read_size, file_size)) srcFileName = self.createNewTempFile(b"x" * 8193) urllib.request.urlretrieve(self.constructLocalFileUrl(srcFileName), - os_helper.TESTFN, hooktester) + support.TESTFN, hooktester) self.assertEqual(len(report), 3) self.assertEqual(report[0][2], 8193) self.assertEqual(report[0][1], 8192) @@ -840,8 +822,6 @@ class urlretrieve_HttpTests(unittest.TestCase, FakeHTTPMixin): """Test urllib.urlretrieve() using fake http connections""" def test_short_content_raises_ContentTooShortError(self): - self.addCleanup(urllib.request.urlcleanup) - self.fakehttp(b'''HTTP/1.1 200 OK Date: Wed, 02 Jan 2008 03:03:54 GMT Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e @@ -863,8 +843,6 @@ FF self.unfakehttp() def test_short_content_raises_ContentTooShortError_without_reporthook(self): - self.addCleanup(urllib.request.urlcleanup) - self.fakehttp(b'''HTTP/1.1 200 OK Date: Wed, 02 Jan 2008 03:03:54 GMT Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7e @@ -1115,6 +1093,8 @@ class UnquotingTests(unittest.TestCase): "%s" % result) self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, None) self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, ()) + with support.check_warnings(('', BytesWarning), quiet=True): + self.assertRaises((TypeError, AttributeError), urllib.parse.unquote, b'') def test_unquoting_badpercent(self): # Test unquoting on bad percent-escapes @@ -1274,29 +1254,11 @@ class UnquotingTests(unittest.TestCase): self.assertEqual(expect, result, "using unquote(): %r != %r" % (expect, result)) - @unittest.skip("TODO: RUSTPYTHON (TypeError: Expected str, got bytes)") def test_unquoting_with_bytes_input(self): - # ASCII characters decoded to a string - given = b'blueberryjam' - expect = 'blueberryjam' - result = urllib.parse.unquote(given) - self.assertEqual(expect, result, - "using unquote(): %r != %r" % (expect, result)) - - # A mix of non-ASCII hex-encoded characters and ASCII characters - given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' - expect = 'bl\u00e5b\u00e6rsyltet\u00f8y' - result = urllib.parse.unquote(given) - self.assertEqual(expect, result, - "using unquote(): %r != %r" % (expect, result)) - - # A mix of non-ASCII percent-encoded characters and ASCII characters - given = b'bl%c3%a5b%c3%a6rsyltet%c3%b8j' - expect = 'bl\u00e5b\u00e6rsyltet\u00f8j' - result = urllib.parse.unquote(given) - self.assertEqual(expect, result, - "using unquote(): %r != %r" % (expect, result)) - + # Bytes not supported yet + with self.assertRaisesRegex(TypeError, 'Expected str, got bytes'): + given = b'bl\xc3\xa5b\xc3\xa6rsyltet\xc3\xb8y' + urllib.parse.unquote(given) class urlencode_Tests(unittest.TestCase): """Tests for urlencode()""" @@ -1536,24 +1498,6 @@ class Pathname_Tests(unittest.TestCase): "url2pathname() failed; %s != %s" % (expect, result)) - @unittest.skipUnless(sys.platform == 'win32', - 'test specific to the nturl2path functions.') - def test_prefixes(self): - # Test special prefixes are correctly handled in pathname2url() - given = '\\\\?\\C:\\dir' - expect = '///C:/dir' - result = urllib.request.pathname2url(given) - self.assertEqual(expect, result, - "pathname2url() failed; %s != %s" % - (expect, result)) - given = '\\\\?\\unc\\server\\share\\dir' - expect = '/server/share/dir' - result = urllib.request.pathname2url(given) - self.assertEqual(expect, result, - "pathname2url() failed; %s != %s" % - (expect, result)) - - @unittest.skipUnless(sys.platform == 'win32', 'test specific to the urllib.url2path function.') def test_ntpath(self): @@ -1599,7 +1543,7 @@ class URLopener_Tests(FakeHTTPMixin, unittest.TestCase): @support.ignore_warnings(category=DeprecationWarning) def test_urlopener_retrieve_file(self): - with os_helper.temp_dir() as tmpdir: + with support.temp_dir() as tmpdir: fd, tmpfile = tempfile.mkstemp(dir=tmpdir) os.close(fd) fileurl = "file:" + urllib.request.pathname2url(tmpfile) @@ -1629,6 +1573,85 @@ class URLopener_Tests(FakeHTTPMixin, unittest.TestCase): self.assertRaises(OSError, DummyURLopener().retrieve, url) +# Just commented them out. +# Can't really tell why keep failing in windows and sparc. +# Everywhere else they work ok, but on those machines, sometimes +# fail in one of the tests, sometimes in other. I have a linux, and +# the tests go ok. +# If anybody has one of the problematic environments, please help! +# . Facundo +# +# def server(evt): +# import socket, time +# serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +# serv.settimeout(3) +# serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +# serv.bind(("", 9093)) +# serv.listen() +# try: +# conn, addr = serv.accept() +# conn.send("1 Hola mundo\n") +# cantdata = 0 +# while cantdata < 13: +# data = conn.recv(13-cantdata) +# cantdata += len(data) +# time.sleep(.3) +# conn.send("2 No more lines\n") +# conn.close() +# except socket.timeout: +# pass +# finally: +# serv.close() +# evt.set() +# +# class FTPWrapperTests(unittest.TestCase): +# +# def setUp(self): +# import ftplib, time, threading +# ftplib.FTP.port = 9093 +# self.evt = threading.Event() +# threading.Thread(target=server, args=(self.evt,)).start() +# time.sleep(.1) +# +# def tearDown(self): +# self.evt.wait() +# +# def testBasic(self): +# # connects +# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) +# ftp.close() +# +# def testTimeoutNone(self): +# # global default timeout is ignored +# import socket +# self.assertIsNone(socket.getdefaulttimeout()) +# socket.setdefaulttimeout(30) +# try: +# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) +# finally: +# socket.setdefaulttimeout(None) +# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) +# ftp.close() +# +# def testTimeoutDefault(self): +# # global default timeout is used +# import socket +# self.assertIsNone(socket.getdefaulttimeout()) +# socket.setdefaulttimeout(30) +# try: +# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, []) +# finally: +# socket.setdefaulttimeout(None) +# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) +# ftp.close() +# +# def testTimeoutValue(self): +# ftp = urllib.ftpwrapper("myuser", "mypass", "localhost", 9093, [], +# timeout=30) +# self.assertEqual(ftp.ftp.sock.gettimeout(), 30) +# ftp.close() + + class RequestTests(unittest.TestCase): """Unit tests for urllib.request.Request.""" diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 50f97913f..4ddf50683 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper from test import test_urllib import os @@ -773,7 +772,7 @@ class HandlerTests(unittest.TestCase): h = urllib.request.FileHandler() o = h.parent = MockOpener() - TESTFN = os_helper.TESTFN + TESTFN = support.TESTFN urlpath = sanepathname2url(os.path.abspath(TESTFN)) towrite = b"hello, world\n" urls = [ diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index c27e14810..0f43d71ea 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper from test.test_urllib2 import sanepathname2url import os @@ -113,7 +112,7 @@ class OtherNetworkTests(unittest.TestCase): self._test_urls(urls, self._extra_handlers()) def test_file(self): - TESTFN = os_helper.TESTFN + TESTFN = support.TESTFN f = open(TESTFN, 'w') try: f.write('hi there\n') diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 975715e51..d394ceddd 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -1,6 +1,5 @@ import unittest from test import support -from test.support import os_helper import contextlib import socket @@ -153,7 +152,7 @@ class urlretrieveNetworkTests(unittest.TestCase): try: yield file_location, info finally: - os_helper.unlink(file_location) + support.unlink(file_location) def test_basic(self): # Test basic functionality. @@ -167,8 +166,8 @@ class urlretrieveNetworkTests(unittest.TestCase): def test_specified_path(self): # Make sure that specifying the location of the file to write to works. with self.urlretrieve(self.logo, - os_helper.TESTFN) as (file_location, info): - self.assertEqual(file_location, os_helper.TESTFN) + support.TESTFN) as (file_location, info): + self.assertEqual(file_location, support.TESTFN) self.assertTrue(os.path.exists(file_location)) with open(file_location, 'rb') as f: self.assertTrue(f.read(), "reading from temporary file failed") diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index 6b0b2f24f..f1288eb5a 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -4,7 +4,7 @@ Nick Mathewson """ import unittest -from test.support import os_helper +from test import support import os import stat @@ -186,11 +186,10 @@ class UUStdIOTest(unittest.TestCase): class UUFileTest(unittest.TestCase): def setUp(self): - # uu.encode() supports only ASCII file names - self.tmpin = os_helper.TESTFN_ASCII + "i" - self.tmpout = os_helper.TESTFN_ASCII + "o" - self.addCleanup(os_helper.unlink, self.tmpin) - self.addCleanup(os_helper.unlink, self.tmpout) + self.tmpin = support.TESTFN + "i" + self.tmpout = support.TESTFN + "o" + self.addCleanup(support.unlink, self.tmpin) + self.addCleanup(support.unlink, self.tmpout) # TODO: RUSTPYTHON @unittest.expectedFailure diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 10c39e9ae..669ca82ad 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -16,8 +16,8 @@ import subprocess import sys import tempfile from test.support import (captured_stdout, captured_stderr, requires_zlib, + can_symlink, EnvironmentVarGuard, rmtree, import_module) -from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import threading import unittest import venv diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index b6cf1b25b..7ebde0c37 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py @@ -5,7 +5,6 @@ import sys import subprocess from unittest import mock from test import support -from test.support import os_helper URL = 'http://www.example.com' @@ -321,7 +320,7 @@ class ImportTest(unittest.TestCase): browser = webbrowser.get().name except (webbrowser.Error, AttributeError) as err: self.skipTest(str(err)) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env["BROWSER"] = browser webbrowser = support.import_fresh_module('webbrowser') webbrowser.get() @@ -334,12 +333,12 @@ class ImportTest(unittest.TestCase): except (webbrowser.Error, AttributeError, IndexError) as err: self.skipTest(str(err)) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env["BROWSER"] = least_preferred_browser webbrowser = support.import_fresh_module('webbrowser') self.assertEqual(webbrowser.get().name, least_preferred_browser) - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env["BROWSER"] = sys.executable webbrowser = support.import_fresh_module('webbrowser') self.assertEqual(webbrowser.get().name, sys.executable) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 9bcbfd1d6..6b2426d70 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -24,8 +24,7 @@ import weakref from functools import partial from itertools import product, islice from test import support -from test.support import findfile, import_fresh_module, gc_collect, swap_attr, os_helper -from test.support.os_helper import TESTFN +from test.support import TESTFN, findfile, import_fresh_module, gc_collect, swap_attr # pyET is the pure-Python implementation. # @@ -124,7 +123,7 @@ class ModuleTest(unittest.TestCase): def test_all(self): names = ("xml.etree.ElementTree", "_elementtree") - support.check__all__(self, ET, names, not_exported=("HTML_EMPTY",)) + support.check__all__(self, ET, names, blacklist=("HTML_EMPTY",)) def serialize(elem, to_string=True, encoding='unicode', **options): @@ -650,7 +649,7 @@ class ElementTreeTest(unittest.TestCase): self.assertEqual(str(cm.exception), 'junk after document element: line 1, column 12') - self.addCleanup(os_helper.unlink, TESTFN) + self.addCleanup(support.unlink, TESTFN) with open(TESTFN, "wb") as f: f.write(b"junk") it = iterparse(TESTFN) @@ -3645,7 +3644,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_filename(self): - self.addCleanup(os_helper.unlink, TESTFN) + self.addCleanup(support.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) tree.write(TESTFN) with open(TESTFN, 'rb') as f: @@ -3654,7 +3653,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_text_file(self): - self.addCleanup(os_helper.unlink, TESTFN) + self.addCleanup(support.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) with open(TESTFN, 'w', encoding='utf-8') as f: tree.write(f, encoding='unicode') @@ -3665,7 +3664,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_binary_file(self): - self.addCleanup(os_helper.unlink, TESTFN) + self.addCleanup(support.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) with open(TESTFN, 'wb') as f: tree.write(f) @@ -3676,7 +3675,7 @@ class IOTest(unittest.TestCase): # TODO: RUSTPYTHON @unittest.expectedFailure def test_write_to_binary_file_with_bom(self): - self.addCleanup(os_helper.unlink, TESTFN) + self.addCleanup(support.unlink, TESTFN) tree = ET.ElementTree(ET.XML('''''')) # test BOM writing to buffered file with open(TESTFN, 'wb') as f: diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 877e2d2dc..0d88c276a 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -15,7 +15,6 @@ import re import io import contextlib from test import support -from test.support import os_helper try: import gzip @@ -1460,7 +1459,7 @@ class CGIHandlerTestCase(unittest.TestCase): self.cgi = None def test_cgi_get(self): - with os_helper.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env['REQUEST_METHOD'] = 'GET' # if the method is GET and no request_text is given, it runs handle_get # get sysout output @@ -1494,7 +1493,7 @@ class CGIHandlerTestCase(unittest.TestCase): """ - with os_helper.EnvironmentVarGuard() as env, \ + with support.EnvironmentVarGuard() as env, \ captured_stdout(encoding=self.cgi.encoding) as data_out, \ support.captured_stdin() as data_in: data_in.write(data) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 7a4a39774..837250fc8 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -19,9 +19,9 @@ from tempfile import TemporaryFile from random import randint, random, getrandbits from test.support import script_helper -from test.support import (findfile, requires_zlib, requires_bz2, requires_lzma, +from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir, temp_cwd, + requires_zlib, requires_bz2, requires_lzma, captured_stdout) -from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd TESTFN2 = TESTFN + "2" TESTFNDIR = TESTFN + "d" diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 562827138..231e6ab21 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -9,7 +9,6 @@ import unittest import unittest.mock from test import support -from test.support import os_helper from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED @@ -93,7 +92,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): def makeTree(self, files, dirName=TEMP_DIR): # Create a filesystem based set of modules/packages # defined by files under the directory dirName. - self.addCleanup(os_helper.rmtree, dirName) + self.addCleanup(support.rmtree, dirName) for name, (mtime, data) in files.items(): path = os.path.join(dirName, name) @@ -111,7 +110,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): # Create a zip archive based set of modules/packages # defined by files in the zip file zipName. If the # key 'stuff' exists in kw it is prepended to the archive. - self.addCleanup(os_helper.unlink, zipName) + self.addCleanup(support.unlink, zipName) with ZipFile(zipName, "w") as z: for name, (mtime, data) in files.items(): @@ -443,7 +442,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc), "spam" + pyc_ext: (NOW, test_pyc)} - self.addCleanup(os_helper.unlink, TEMP_ZIP) + self.addCleanup(support.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: for name, (mtime, data) in files.items(): zinfo = ZipInfo(name, time.localtime(mtime)) @@ -497,7 +496,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): files = {packdir2 + "__init__" + pyc_ext: (NOW, test_pyc), packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} - self.addCleanup(os_helper.unlink, TEMP_ZIP) + self.addCleanup(support.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: for name, (mtime, data) in files.items(): zinfo = ZipInfo(name, time.localtime(mtime)) @@ -541,7 +540,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): self.assertEqual(loader.get_filename(mod_name), mod.__file__) def testGetData(self): - self.addCleanup(os_helper.unlink, TEMP_ZIP) + self.addCleanup(support.unlink, TEMP_ZIP) with ZipFile(TEMP_ZIP, "w") as z: z.compression = self.compression name = "testdata.dat" @@ -649,11 +648,11 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): files = {TESTMOD + ".py": (NOW, raise_src)} self.doTest(None, files, TESTMOD, call=self.doTraceback) - @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None, + @unittest.skipIf(support.TESTFN_UNENCODABLE is None, "need an unencodable filename") def testUnencodable(self): - filename = os_helper.TESTFN_UNENCODABLE + ".zip" - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN_UNENCODABLE + ".zip" + self.addCleanup(support.unlink, filename) with ZipFile(filename, "w") as z: zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) zinfo.compress_type = self.compression @@ -661,8 +660,8 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): zipimport.zipimporter(filename).load_module(TESTMOD) def testBytesPath(self): - filename = os_helper.TESTFN + ".zip" - self.addCleanup(os_helper.unlink, filename) + filename = support.TESTFN + ".zip" + self.addCleanup(support.unlink, filename) with ZipFile(filename, "w") as z: zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW)) zinfo.compress_type = self.compression @@ -714,12 +713,12 @@ class BadFileZipImportTestCase(unittest.TestCase): self.assertZipFailure('A' * 33000) def testEmptyFile(self): - os_helper.unlink(TESTMOD) - os_helper.create_empty_file(TESTMOD) + support.unlink(TESTMOD) + support.create_empty_file(TESTMOD) self.assertZipFailure(TESTMOD) def testFileUnreadable(self): - os_helper.unlink(TESTMOD) + support.unlink(TESTMOD) fd = os.open(TESTMOD, os.O_CREAT, 000) try: os.close(fd) @@ -730,10 +729,10 @@ class BadFileZipImportTestCase(unittest.TestCase): # If we leave "the read-only bit" set on Windows, nothing can # delete TESTMOD, and later tests suffer bogus failures. os.chmod(TESTMOD, 0o666) - os_helper.unlink(TESTMOD) + support.unlink(TESTMOD) def testNotZipFile(self): - os_helper.unlink(TESTMOD) + support.unlink(TESTMOD) fp = open(TESTMOD, 'w+') fp.write('a' * 22) fp.close() @@ -741,7 +740,7 @@ class BadFileZipImportTestCase(unittest.TestCase): # XXX: disabled until this works on Big-endian machines def _testBogusZipFile(self): - os_helper.unlink(TESTMOD) + support.unlink(TESTMOD) fp = open(TESTMOD, 'w+') fp.write(struct.pack('=I', 0x06054B50)) fp.write('a' * 18) @@ -776,7 +775,7 @@ def test_main(): BadFileZipImportTestCase, ) finally: - os_helper.unlink(TESTMOD) + support.unlink(TESTMOD) if __name__ == "__main__": test_main() From 790090df62fea0875924a9c1c69efb2d80298e05 Mon Sep 17 00:00:00 2001 From: Noa <33094578+coolreader18@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:10:37 -0600 Subject: [PATCH 31/40] Apply review comments --- vm/src/pyobjectrc.rs | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index da0b3c1a0..d6ebc3ccd 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -84,14 +84,14 @@ impl PyObjVTable { /// payload can be a rust float or rust int in case of float and int objects. #[repr(C)] struct PyInner { - refcount: RefCount, + ref_count: RefCount, // TODO: move typeid into vtable once TypeId::of is const typeid: TypeId, vtable: &'static PyObjVTable, typ: PyRwLock, // __class__ member dict: Option, - weaklist: WeakRefList, + weak_list: WeakRefList, payload: T, } @@ -160,7 +160,7 @@ impl WeakRefList { if is_generic { if let Some(generic_weakref) = inner.generic_weakref { let generic_weakref = unsafe { generic_weakref.as_ref() }; - if generic_weakref.0.refcount.get() != 0 { + if generic_weakref.0.ref_count.get() != 0 { return PyObjectWeak { weak: generic_weakref.to_owned(), }; @@ -171,7 +171,7 @@ impl WeakRefList { pointers: Pointers::new(), parent: inner_ptr, callback: UnsafeCell::new(callback), - hash: Radium::new(-1), + hash: Radium::new(crate::common::hash::SENTINEL), }; let weak = PyRef::new_ref(obj, cls, dict); // SAFETY: we don't actually own the PyObjectWeaks inside `list`, and every time we take @@ -266,7 +266,7 @@ impl WeakRefList { impl WeakListInner { fn iter(&self) -> impl Iterator> { - self.list.iter().filter(|wr| wr.0.refcount.get() > 0) + self.list.iter().filter(|wr| wr.0.ref_count.get() > 0) } } @@ -317,7 +317,7 @@ impl PyWeak { let guard = unsafe { self.parent.as_ref().lock() }; let obj_ptr = guard.obj?; unsafe { - if !obj_ptr.as_ref().0.refcount.safe_inc() { + if !obj_ptr.as_ref().0.ref_count.safe_inc() { return None; } Some(PyObjectRef::from_raw(obj_ptr.as_ptr())) @@ -399,12 +399,12 @@ impl InstanceDict { impl PyInner { fn new(payload: T, typ: PyTypeRef, dict: Option) -> Box { Box::new(PyInner { - refcount: RefCount::new(), + ref_count: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), typ: PyRwLock::new(typ), dict: dict.map(InstanceDict::new), - weaklist: WeakRefList::new(), + weak_list: WeakRefList::new(), payload, }) } @@ -455,7 +455,7 @@ impl ToOwned for PyObject { #[inline(always)] fn to_owned(&self) -> Self::Owned { - self.0.refcount.inc(); + self.0.ref_count.inc(); PyObjectRef { ptr: NonNull::from(self), } @@ -554,7 +554,7 @@ impl PyObjectRef { impl PyObject { #[inline] fn weak_ref_list(&self) -> Option<&WeakRefList> { - Some(&self.0.weaklist) + Some(&self.0.weak_list) } pub(crate) fn downgrade_with_weakref_typ_opt( @@ -684,7 +684,7 @@ impl PyObject { #[inline] pub fn strong_count(&self) -> usize { - self.0.refcount.get() + self.0.ref_count.get() } #[inline] @@ -702,14 +702,14 @@ impl PyObject { // CPython-compatible drop implementation if let Some(slot_del) = self.class().mro_find_map(|cls| cls.slots.del.load()) { let ret = crate::vm::thread::with_vm(self, |vm| { - self.0.refcount.inc(); + self.0.ref_count.inc(); if let Err(e) = slot_del(self, vm) { print_del_error(e, self, vm); } - self.0.refcount.dec() + self.0.ref_count.dec() }); match ret { - // the decref right above set refcount back to 0 + // the decref right above set ref_count back to 0 Some(true) => {} // we've been resurrected by __del__ Some(false) => return Err(()), @@ -725,7 +725,7 @@ impl PyObject { Ok(()) } - /// Can only be called when refcount has dropped to zero. `ptr` must be valid + /// Can only be called when ref_count has dropped to zero. `ptr` must be valid #[inline(never)] #[cold] unsafe fn drop_slow(ptr: NonNull) { @@ -797,7 +797,7 @@ impl PyObjectWeak { impl Drop for PyObjectRef { fn drop(&mut self) { - if self.0.refcount.dec() { + if self.0.ref_count.dec() { unsafe { PyObject::drop_slow(self.ptr) } } } @@ -863,7 +863,7 @@ impl ToOwned for PyObjectView { #[inline(always)] fn to_owned(&self) -> Self::Owned { - self.0.refcount.inc(); + self.0.ref_count.inc(); PyRef { ptr: NonNull::from(self), } @@ -923,7 +923,7 @@ impl fmt::Debug for PyRef { impl Drop for PyRef { #[inline] fn drop(&mut self) { - if self.0.refcount.dec() { + if self.0.ref_count.dec() { unsafe { PyObject::drop_slow(self.ptr.cast::()) } } } @@ -1078,22 +1078,22 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { }; let type_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { - refcount: RefCount::new(), + ref_count: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None, - weaklist: WeakRefList::new(), + weak_list: WeakRefList::new(), payload: type_payload, }, Uninit { typ } ))); let object_type_ptr = Box::into_raw(Box::new(partially_init!( PyInner:: { - refcount: RefCount::new(), + ref_count: RefCount::new(), typeid: TypeId::of::(), vtable: PyObjVTable::of::(), dict: None, - weaklist: WeakRefList::new(), + weak_list: WeakRefList::new(), payload: object_payload, }, Uninit { typ }, @@ -1105,12 +1105,12 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) { type_type_ptr as *mut MaybeUninit> as *mut PyInner; unsafe { - (*type_type_ptr).refcount.inc(); + (*type_type_ptr).ref_count.inc(); ptr::write( &mut (*object_type_ptr).typ as *mut PyRwLock as *mut UninitRef, PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), ); - (*type_type_ptr).refcount.inc(); + (*type_type_ptr).ref_count.inc(); ptr::write( &mut (*type_type_ptr).typ as *mut PyRwLock as *mut UninitRef, PyRwLock::new(NonNull::new_unchecked(type_type_ptr)), From 0fe8a06d654a5ea2a42c1d72d798040853149629 Mon Sep 17 00:00:00 2001 From: rvsampson94 Date: Fri, 19 Nov 2021 22:30:22 -0500 Subject: [PATCH 32/40] Dict keys isdisjoint --- Lib/test/test_dictviews.py | 4 ---- extra_tests/snippets/builtin_dict.py | 16 +++++++++++++++- vm/src/builtins/dict.rs | 8 ++++++++ vm/src/builtins/set.rs | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index 22142358a..8455d0bfb 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -90,8 +90,6 @@ class DictSetTest(unittest.TestCase): self.assertTrue(r == "dict_values(['ABC', 10])" or r == "dict_values([10, 'ABC'])") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_keys_set_operations(self): d1 = {'a': 1, 'b': 2} d2 = {'b': 3, 'c': 2} @@ -148,8 +146,6 @@ class DictSetTest(unittest.TestCase): self.assertTrue(de.keys().isdisjoint(de.keys())) self.assertTrue(de.keys().isdisjoint([1])) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_items_set_operations(self): d1 = {'a': 1, 'b': 2} d2 = {'a': 2, 'b': 2} diff --git a/extra_tests/snippets/builtin_dict.py b/extra_tests/snippets/builtin_dict.py index d041a6fcb..ca29a956c 100644 --- a/extra_tests/snippets/builtin_dict.py +++ b/extra_tests/snippets/builtin_dict.py @@ -47,4 +47,18 @@ assert () not in d.keys() assert 10 not in d.keys() assert (1, 10) not in d.keys() assert "abc" not in d.keys() -assert ((3,4),5) not in d.keys() \ No newline at end of file +assert ((3,4),5) not in d.keys() + +d1 = {"a": 1, "b": 2} +d2 = {"c": 3, "d": 4} +assert d1.items().isdisjoint(d2.items()) +assert d1.keys().isdisjoint(d2.keys()) +d2 = {"b": 3, "d": 4} +assert d1.items().isdisjoint(d2.items()) +assert not d1.keys().isdisjoint(d2.keys()) +d2 = {"c": 2, "d": 4} +assert d1.items().isdisjoint(d2.items()) +assert d1.keys().isdisjoint(d2.keys()) +d2 = {"b": 2, "d": 4} +assert not d1.items().isdisjoint(d2.items()) +assert not d1.keys().isdisjoint(d2.keys()) \ No newline at end of file diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 0ee51973d..f069f5a30 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -987,6 +987,14 @@ trait ViewSetOps: DictView { } }) } + + #[pymethod] + fn isdisjoint(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { + // TODO: to_set is an expensive operation. After merging #3316 rewrite implementation using PySequence_Contains. + let zelf = Self::to_set(zelf, vm)?; + let result = zelf.isdisjoint(other, vm)?; + Ok(result) + } } impl ViewSetOps for PyDictKeys {} diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index cc238b8c3..e07145412 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -195,7 +195,7 @@ impl PySetInner { self.compare(&other_set, PyComparisonOp::Le, vm) } - fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult { + pub(super) fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult { for item in other.iter(vm)? { if self.contains(&*item?, vm)? { return Ok(false); From 7b79c09c3c74bb324f27371c9ac945acb89d3929 Mon Sep 17 00:00:00 2001 From: BongYang <33821003+BongYang@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:08:27 +0900 Subject: [PATCH 33/40] pystr.rs isdecimal() checks not only ASCII but also Unicode decimals --- Lib/test/test_unicode.py | 2 -- vm/src/builtins/pystr.rs | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 05eeb846d..bad28bba0 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -661,8 +661,6 @@ class UnicodeTest(string_tests.CommonTest, self.assertFalse("\u20ac".isascii()) self.assertFalse("\U0010ffff".isascii()) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_isdecimal(self): self.checkequalnofix(False, '', 'isdecimal') self.checkequalnofix(False, 'a', 'isdecimal') diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index 9f0d1d436..2d7bc4cf0 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -709,7 +709,8 @@ impl PyStr { #[pymethod] fn isdecimal(&self) -> bool { - !self.bytes.is_empty() && self.char_all(|c| c.is_ascii_digit()) + !self.bytes.is_empty() + && self.char_all(|c| GeneralCategory::of(c) == GeneralCategory::DecimalNumber) } #[pymethod(name = "__mod__")] From 3070b19b3672a68eba16ee919adedb43d8951597 Mon Sep 17 00:00:00 2001 From: yyun-d Date: Sat, 20 Nov 2021 15:23:07 +0900 Subject: [PATCH 34/40] Update test_math.py to cpython 3.10 --- Lib/test/test_math.py | 120 ++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index c90c063cd..91d071f7b 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1,4 +1,3 @@ - # Python test set -- math module # XXXX Should not do tests around zero only @@ -19,7 +18,6 @@ eps = 1E-05 NAN = float('nan') INF = float('inf') NINF = float('-inf') - FLOAT_MAX = sys.float_info.max FLOAT_MIN = sys.float_info.min @@ -43,8 +41,10 @@ def to_ulps(x): adjacent floats are converted to adjacent integers. Then abs(ulps(x) - ulps(y)) gives the difference in ulps between two floats. + The results from this function will only make sense on platforms where native doubles are represented in IEEE 754 binary64 format. + Note: 0.0 and -0.0 are converted to 0 and -1, respectively. """ n = struct.unpack('> 1 if not numfactors: @@ -94,6 +95,7 @@ def partial_product(start, stop): def py_factorial(n): """Factorial of nonnegative integer n, via "Binary Split Factorial Formula" described at http://www.luschny.de/math/factorial/binarysplitfact.html + """ inner = outer = 1 for i in reversed(range(n.bit_length())): @@ -105,6 +107,7 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol): """Given finite floats `expected` and `got`, check that they're approximately equal to within the given number of ulps or the given absolute tolerance, whichever is bigger. + Returns None on success and an error message on failure. """ ulp_error = abs(to_ulps(expected) - to_ulps(got)) @@ -120,12 +123,14 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol): def parse_mtestfile(fname): """Parse a file with test values + -- starts a comment blank lines, or lines containing only a comment, are ignored other lines are expected to have the form id fn arg -> expected [flag]* + """ - with open(fname) as fp: + with open(fname, encoding="utf-8") as fp: for line in fp: # strip comments, and skip blank lines if '--' in line: @@ -144,10 +149,11 @@ def parse_mtestfile(fname): def parse_testfile(fname): """Parse a file with test values + Empty lines or lines starting with -- are ignored yields id, fn, arg_real, arg_imag, exp_real, exp_imag """ - with open(fname) as fp: + with open(fname, encoding="utf-8") as fp: for line in fp: # skip comment lines and blank lines if line.startswith('--') or not line.strip(): @@ -170,9 +176,11 @@ def result_check(expected, got, ulp_tol=5, abs_tol=0.0): """Compare arguments expected and got, as floats, if either is a float, using a tolerance expressed in multiples of ulp(expected) or absolutely (if given and greater). + As a convenience, when neither argument is a float, and for non-finite floats, exact equality is demanded. Also, nan==nan as far as this function is concerned. + Returns None on success and an error message on failure. """ @@ -232,6 +240,7 @@ class MathTests(unittest.TestCase): """Compare arguments expected and got, as floats, if either is a float, using a tolerance expressed in multiples of ulp(expected) or absolutely, whichever is greater. + As a convenience, when neither argument is a float, and for non-finite floats, exact equality is demanded. Also, nan==nan in this function. @@ -492,17 +501,11 @@ class MathTests(unittest.TestCase): self.assertRaises(ValueError, math.factorial, -1) self.assertRaises(ValueError, math.factorial, -10**100) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testFactorialNonIntegers(self): - with self.assertWarns(DeprecationWarning): - self.assertEqual(math.factorial(5.0), 120) - with self.assertWarns(DeprecationWarning): - self.assertRaises(ValueError, math.factorial, 5.2) - with self.assertWarns(DeprecationWarning): - self.assertRaises(ValueError, math.factorial, -1.0) - with self.assertWarns(DeprecationWarning): - self.assertRaises(ValueError, math.factorial, -1e100) + self.assertRaises(TypeError, math.factorial, 5.0) + self.assertRaises(TypeError, math.factorial, 5.2) + self.assertRaises(TypeError, math.factorial, -1.0) + self.assertRaises(TypeError, math.factorial, -1e100) self.assertRaises(TypeError, math.factorial, decimal.Decimal('5')) self.assertRaises(TypeError, math.factorial, decimal.Decimal('5.2')) self.assertRaises(TypeError, math.factorial, "5") @@ -513,8 +516,7 @@ class MathTests(unittest.TestCase): # Currently raises OverflowError for inputs that are too large # to fit into a C long. self.assertRaises(OverflowError, math.factorial, 10**100) - with self.assertWarns(DeprecationWarning): - self.assertRaises(OverflowError, math.factorial, 1e100) + self.assertRaises(TypeError, math.factorial, 1e100) def testFloor(self): self.assertRaises(TypeError, math.floor) @@ -587,7 +589,6 @@ class MathTests(unittest.TestCase): self.assertEqual(math.frexp(NINF)[0], NINF) self.assertTrue(math.isnan(math.frexp(NAN)[0])) - @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "fsum is not exact on machines with double rounding") @@ -611,6 +612,7 @@ class MathTests(unittest.TestCase): """Full precision summation. Compute sum(iterable) without any intermediate accumulation of error. Based on the 'lsum' function at http://code.activestate.com/recipes/393090/ + """ tmant, texp = 0, 0 for x in iterable: @@ -684,8 +686,6 @@ class MathTests(unittest.TestCase): s = msum(vals) self.assertEqual(msum(vals), math.fsum(vals)) - - # Python 3.9 def testGcd(self): gcd = math.gcd self.assertEqual(gcd(0, 0), 0) @@ -726,7 +726,7 @@ class MathTests(unittest.TestCase): self.assertRaises(TypeError, gcd, 120.0, 84) self.assertRaises(TypeError, gcd, 120, 84.0) self.assertRaises(TypeError, gcd, 120, 1, 84.0) - #self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) # TODO: RUSTPYTHON + self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) def testHypot(self): from decimal import Decimal @@ -795,13 +795,79 @@ class MathTests(unittest.TestCase): # Verify scaling for extremely large values fourthmax = FLOAT_MAX / 4.0 for n in range(32): - self.assertEqual(hypot(*([fourthmax]*n)), fourthmax * math.sqrt(n)) + self.assertTrue(math.isclose(hypot(*([fourthmax]*n)), + fourthmax * math.sqrt(n))) # Verify scaling for extremely small values for exp in range(32): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) + @requires_IEEE_754 + @unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "hypot() loses accuracy on machines with double rounding") + # TODO: RUSTPYTHON + @unittest.expectedFailure + def testHypotAccuracy(self): + # Verify improved accuracy in cases that were known to be inaccurate. + # + # The new algorithm's accuracy depends on IEEE 754 arithmetic + # guarantees, on having the usual ROUND HALF EVEN rounding mode, on + # the system not having double rounding due to extended precision, + # and on the compiler maintaining the specified order of operations. + # + # This test is known to succeed on most of our builds. If it fails + # some build, we either need to add another skipIf if the cause is + # identifiable; otherwise, we can remove this test entirely. + + hypot = math.hypot + Decimal = decimal.Decimal + high_precision = decimal.Context(prec=500) + + for hx, hy in [ + # Cases with a 1 ulp error in Python 3.7 compiled with Clang + ('0x1.10e89518dca48p+29', '0x1.1970f7565b7efp+30'), + ('0x1.10106eb4b44a2p+29', '0x1.ef0596cdc97f8p+29'), + ('0x1.459c058e20bb7p+30', '0x1.993ca009b9178p+29'), + ('0x1.378371ae67c0cp+30', '0x1.fbe6619854b4cp+29'), + ('0x1.f4cd0574fb97ap+29', '0x1.50fe31669340ep+30'), + ('0x1.494b2cdd3d446p+29', '0x1.212a5367b4c7cp+29'), + ('0x1.f84e649f1e46dp+29', '0x1.1fa56bef8eec4p+30'), + ('0x1.2e817edd3d6fap+30', '0x1.eb0814f1e9602p+29'), + ('0x1.0d3a6e3d04245p+29', '0x1.32a62fea52352p+30'), + ('0x1.888e19611bfc5p+29', '0x1.52b8e70b24353p+29'), + + # Cases with 2 ulp error in Python 3.8 + ('0x1.538816d48a13fp+29', '0x1.7967c5ca43e16p+29'), + ('0x1.57b47b7234530p+29', '0x1.74e2c7040e772p+29'), + ('0x1.821b685e9b168p+30', '0x1.677dc1c1e3dc6p+29'), + ('0x1.9e8247f67097bp+29', '0x1.24bd2dc4f4baep+29'), + ('0x1.b73b59e0cb5f9p+29', '0x1.da899ab784a97p+28'), + ('0x1.94a8d2842a7cfp+30', '0x1.326a51d4d8d8ap+30'), + ('0x1.e930b9cd99035p+29', '0x1.5a1030e18dff9p+30'), + ('0x1.1592bbb0e4690p+29', '0x1.a9c337b33fb9ap+29'), + ('0x1.1243a50751fd4p+29', '0x1.a5a10175622d9p+29'), + ('0x1.57a8596e74722p+30', '0x1.42d1af9d04da9p+30'), + + # Cases with 1 ulp error in version fff3c28052e6b0 + ('0x1.ee7dbd9565899p+29', '0x1.7ab4d6fc6e4b4p+29'), + ('0x1.5c6bfbec5c4dcp+30', '0x1.02511184b4970p+30'), + ('0x1.59dcebba995cap+30', '0x1.50ca7e7c38854p+29'), + ('0x1.768cdd94cf5aap+29', '0x1.9cfdc5571d38ep+29'), + ('0x1.dcf137d60262ep+29', '0x1.1101621990b3ep+30'), + ('0x1.3a2d006e288b0p+30', '0x1.e9a240914326cp+29'), + ('0x1.62a32f7f53c61p+29', '0x1.47eb6cd72684fp+29'), + ('0x1.d3bcb60748ef2p+29', '0x1.3f13c4056312cp+30'), + ('0x1.282bdb82f17f3p+30', '0x1.640ba4c4eed3ap+30'), + ('0x1.89d8c423ea0c6p+29', '0x1.d35dcfe902bc3p+29'), + ]: + x = float.fromhex(hx) + y = float.fromhex(hy) + with self.subTest(hx=hx, hy=hy, x=x, y=y): + with decimal.localcontext(high_precision): + z = float((Decimal(x)**2 + Decimal(y)**2).sqrt()) + self.assertEqual(hypot(x, y), z) + def testDist(self): from decimal import Decimal as D from fractions import Fraction as F @@ -904,8 +970,8 @@ class MathTests(unittest.TestCase): for n in range(32): p = (fourthmax,) * n q = (0.0,) * n - self.assertEqual(dist(p, q), fourthmax * math.sqrt(n)) - self.assertEqual(dist(q, p), fourthmax * math.sqrt(n)) + self.assertTrue(math.isclose(dist(p, q), fourthmax * math.sqrt(n))) + self.assertTrue(math.isclose(dist(q, p), fourthmax * math.sqrt(n))) # Verify scaling for extremely small values for exp in range(32): @@ -968,8 +1034,7 @@ class MathTests(unittest.TestCase): with self.assertRaises(TypeError): math.isqrt(value) - # Python 3.9 - def testlcm(self): + def test_lcm(self): lcm = math.lcm self.assertEqual(lcm(0, 0), 0) self.assertEqual(lcm(1, 0), 0) @@ -1011,7 +1076,7 @@ class MathTests(unittest.TestCase): self.assertRaises(TypeError, lcm, 120.0, 84) self.assertRaises(TypeError, lcm, 120, 84.0) self.assertRaises(TypeError, lcm, 120, 0, 84.0) - # self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) # TODO: RUSTPYTHON + self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) def testLdexp(self): self.assertRaises(TypeError, math.ldexp) @@ -1987,7 +2052,7 @@ class MathTests(unittest.TestCase): # min and max self.assertEqual(math.ulp(0.0), - sys.float_info.min * sys.float_info.epsilon) + sys.float_info.min * sys.float_info.epsilon) self.assertEqual(math.ulp(FLOAT_MAX), FLOAT_MAX - math.nextafter(FLOAT_MAX, -INF)) @@ -2024,6 +2089,7 @@ class MathTests(unittest.TestCase): def assertEqualSign(self, x, y): """Similar to assertEqual(), but compare also the sign with copysign(). + Function useful to compare signed zeros. """ self.assertEqual(x, y) From fd3a0f6d868534bd1dc9ab8d5e16c0b91f402c35 Mon Sep 17 00:00:00 2001 From: yyun-d Date: Sat, 20 Nov 2021 18:59:18 +0900 Subject: [PATCH 35/40] implement reduce method to PySlice Signed-off-by: yyun-d --- vm/src/builtins/slice.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/vm/src/builtins/slice.rs b/vm/src/builtins/slice.rs index 99bcec732..68e5d6886 100644 --- a/vm/src/builtins/slice.rs +++ b/vm/src/builtins/slice.rs @@ -194,6 +194,20 @@ impl PySlice { let (start, stop, step) = self.inner_indices(length, vm)?; Ok(vm.new_tuple((start, stop, step))) } + + #[allow(clippy::type_complexity)] + #[pymethod(magic)] + fn reduce( + zelf: PyRef, + ) -> PyResult<( + PyTypeRef, + (Option, PyObjectRef, Option), + )> { + Ok(( + zelf.clone_class(), + (zelf.start.clone(), zelf.stop.clone(), zelf.step.clone()), + )) + } } impl Comparable for PySlice { From e49d7118b863994393f3c58bb90475220d3cae19 Mon Sep 17 00:00:00 2001 From: yyun-d Date: Sat, 20 Nov 2021 18:59:59 +0900 Subject: [PATCH 36/40] fix test_pickle in test_slice Signed-off-by: yyun-d --- Lib/test/test_slice.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index ecdd92b6c..9e79775ac 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -234,8 +234,6 @@ class SliceTest(unittest.TestCase): x[1:2] = 42 self.assertEqual(tmp, [(slice(1, 2), 42)]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pickle(self): s = slice(10, 20, 3) for protocol in (0,1,2): From 51e1d7607ca9af56a23a30755176f05714bf160e Mon Sep 17 00:00:00 2001 From: waroad Date: Sat, 20 Nov 2021 20:56:26 +0900 Subject: [PATCH 37/40] add reduce in enumerate --- Lib/test/test_enumerate.py | 2 -- vm/src/builtins/enumerate.rs | 10 +++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 92d52c3d1..5785cb464 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -94,8 +94,6 @@ class EnumerateTestCase(unittest.TestCase, PickleTest): self.assertEqual(list(self.enum(self.seq)), self.res) self.enum.__doc__ - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pickle(self): self.check_pickle(self.enum(self.seq), self.res) diff --git a/vm/src/builtins/enumerate.rs b/vm/src/builtins/enumerate.rs index 34809fa45..699d2d3cb 100644 --- a/vm/src/builtins/enumerate.rs +++ b/vm/src/builtins/enumerate.rs @@ -4,7 +4,8 @@ use crate::{ function::{IntoPyObject, OptionalArg}, protocol::{PyIter, PyIterReturn}, types::{Constructor, IterNext, IterNextIterable}, - ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, VirtualMachine, + ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + VirtualMachine, }; use num_bigint::BigInt; use num_traits::Zero; @@ -52,6 +53,13 @@ impl PyEnumerate { fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::new(cls, args, vm) } + #[pymethod(magic)] + fn reduce(zelf: PyRef) -> (PyTypeRef, (PyIter, BigInt)) { + ( + zelf.clone_class(), + (zelf.iterator.clone(), zelf.counter.read().clone()), + ) + } } impl IterNextIterable for PyEnumerate {} From a41d7c5d29755edbea206e0bb520fda3c70bf3f5 Mon Sep 17 00:00:00 2001 From: Moreal Date: Sun, 21 Nov 2021 01:19:10 +0900 Subject: [PATCH 38/40] Remove staled comments --- extra_tests/not_impl_gen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/extra_tests/not_impl_gen.py b/extra_tests/not_impl_gen.py index 21d8c9158..3162bc0ec 100644 --- a/extra_tests/not_impl_gen.py +++ b/extra_tests/not_impl_gen.py @@ -72,8 +72,6 @@ def attr_is_not_inherited(type_, attr): def extra_info(obj): - # RustPython doesn't support __text_signature__ for getting signatures of builtins - # https://github.com/RustPython/RustPython/issues/2410 if callable(obj) and not inspect._signature_is_builtin(obj): try: sig = str(inspect.signature(obj)) From 7de142fcd18dd2e4ccd03ad919b354995d7d298d Mon Sep 17 00:00:00 2001 From: YYun-D Date: Sun, 21 Nov 2021 12:37:01 +0900 Subject: [PATCH 39/40] remove bytearray and byte docs Signed-off-by: YYun-D --- Lib/test/test_bytes.py | 2 -- vm/src/builtins/bytearray.rs | 11 ----------- vm/src/builtins/bytes.rs | 9 --------- 3 files changed, 22 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index c0127cabe..4df060766 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1753,8 +1753,6 @@ class AssortedBytesTest(unittest.TestCase): self.assertEqual(bytes(b"abc") < b"ab", False) self.assertEqual(bytes(b"abc") <= b"ab", False) - # TODO: RUSTPYTHON - @unittest.expectedFailure @test.support.requires_docstrings def test_doc(self): self.assertIsNotNone(bytearray.__doc__) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 4dc7efe71..898745881 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -35,17 +35,6 @@ use crate::{ use bstr::ByteSlice; use std::mem::size_of; -/// "bytearray(iterable_of_ints) -> bytearray\n\ -/// bytearray(string, encoding[, errors]) -> bytearray\n\ -/// bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\ -/// bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\ -/// bytearray() -> empty bytes array\n\n\ -/// Construct a mutable bytearray object from:\n \ -/// - an iterable yielding integers in range(256)\n \ -/// - a text string encoded using the specified encoding\n \ -/// - a bytes or a buffer object\n \ -/// - any object implementing the buffer API.\n \ -/// - an integer"; #[pyclass(module = false, name = "bytearray")] #[derive(Debug, Default)] pub struct PyByteArray { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 0b0ee5228..0ad88c8dc 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -23,15 +23,6 @@ use bstr::ByteSlice; use std::mem::size_of; use std::ops::Deref; -/// "bytes(iterable_of_ints) -> bytes\n\ -/// bytes(string, encoding[, errors]) -> bytes\n\ -/// bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ -/// bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\ -/// bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \ -/// - an iterable yielding integers in range(256)\n \ -/// - a text string encoded using the specified encoding\n \ -/// - any object implementing the buffer API.\n \ -/// - an integer"; #[pyclass(module = false, name = "bytes")] #[derive(Clone, Debug)] pub struct PyBytes { From 908b239572725cea277a3a27952f76085cc058eb Mon Sep 17 00:00:00 2001 From: Steve Shi Date: Mon, 22 Nov 2021 09:21:23 +0200 Subject: [PATCH 40/40] Refactor and new sequence traits, generic sequence operation (#3445) * refactor sequence generic mutable sequence item equal op * sequence generic for pydeque * replace usize::MAX with Optioned * sequence op for array * fix overflow error instead memory error for index overflow * fixup * optimize reserve vec for imul --- Cargo.lock | 7 + stdlib/src/array.rs | 48 ++--- vm/Cargo.toml | 1 + vm/src/builtins/bytearray.rs | 22 +-- vm/src/builtins/bytes.rs | 11 +- vm/src/builtins/list.rs | 227 ++++------------------ vm/src/builtins/pystr.rs | 10 +- vm/src/builtins/tuple.rs | 10 +- vm/src/bytesinner.rs | 12 +- vm/src/lib.rs | 2 +- vm/src/sequence.rs | 363 ++++++++++++++++++++++++++--------- vm/src/stdlib/collections.rs | 176 +++++++---------- vm/src/vm.rs | 15 +- 13 files changed, 444 insertions(+), 460 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a216107f9..4ee04733e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1315,6 +1315,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "optional" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1964,6 +1970,7 @@ dependencies = [ "num_cpus", "num_enum", "once_cell", + "optional", "parking_lot", "paste", "rand", diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 1ce1c9a18..22189cfda 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -23,6 +23,7 @@ mod array { BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn, PyMappingMethods, }, + sequence::{SequenceMutOp, SequenceOp}, sliceable::{PySliceableSequence, PySliceableSequenceMut, SaturatedSlice, SequenceIndex}, types::{ AsBuffer, AsMapping, Comparable, Constructor, IterNext, IterNextIterable, Iterable, @@ -387,34 +388,24 @@ mod array { } } - fn mul(&self, counter: usize) -> Self { + fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult { match self { $(ArrayContentType::$n(v) => { - let elements = v.repeat(counter); - ArrayContentType::$n(elements) + // MemoryError instead Overflow Error, hard to says it is right + // but it is how cpython doing right now + let elements = v.mul(vm, value).map_err(|_| vm.new_memory_error("".to_owned()))?; + Ok(ArrayContentType::$n(elements)) })* } } - fn clear(&mut self) { + fn imul(&mut self, value: isize, vm: &VirtualMachine) -> PyResult<()> { match self { - $(ArrayContentType::$n(v) => v.clear(),)* - } - } - - fn imul(&mut self, counter: usize) { - if counter == 0 { - self.clear(); - } else if counter != 1 { - match self { - $(ArrayContentType::$n(v) => { - let old = v.clone(); - v.reserve((counter - 1) * old.len()); - for _ in 1..counter { - v.extend(&old); - } - })* - } + $(ArrayContentType::$n(v) => { + // MemoryError instead Overflow Error, hard to says it is right + // but it is how cpython doing right now + v.imul(vm, value).map_err(|_| vm.new_memory_error("".to_owned())) + })* } } @@ -742,8 +733,7 @@ mod array { fn extend(zelf: PyRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { let mut w = zelf.try_resizable(vm)?; if zelf.is(&obj) { - w.imul(2); - Ok(()) + w.imul(2, vm) } else if let Some(array) = obj.payload::() { w.iadd(&*array.read(), vm) } else { @@ -856,7 +846,7 @@ mod array { if n < 0 { return Err(vm.new_value_error("negative count".to_owned())); } - let n = vm.check_repeat_or_memory_error(itemsize, n)?; + let n = vm.check_repeat_or_overflow_error(itemsize, n)?; let nbytes = n * itemsize; let b = vm.call_method(&f, "read", (nbytes,))?; @@ -1044,7 +1034,7 @@ mod array { vm: &VirtualMachine, ) -> PyResult> { if zelf.is(&other) { - zelf.try_resizable(vm)?.imul(2); + zelf.try_resizable(vm)?.imul(2, vm)?; } else if let Some(other) = other.payload::() { zelf.try_resizable(vm)?.iadd(&*other.read(), vm)?; } else { @@ -1059,14 +1049,14 @@ mod array { #[pymethod(name = "__rmul__")] #[pymethod(magic)] fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult> { - let value = vm.check_repeat_or_memory_error(self.len(), value)?; - Ok(Self::from(self.read().mul(value)).into_ref(vm)) + self.read() + .mul(value, vm) + .map(|x| Self::from(x).into_ref(vm)) } #[pymethod(magic)] fn imul(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { - let value = vm.check_repeat_or_memory_error(zelf.len(), value)?; - zelf.try_resizable(vm)?.imul(value); + zelf.try_resizable(vm)?.imul(value, vm)?; Ok(zelf) } diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 30c3d79bb..3989b5648 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -69,6 +69,7 @@ adler32 = "1.0.3" flate2 = "1.0.20" once_cell = "1" memoffset = "0.6" +optional = "0.5" # RustPython crates implementing functionality based on CPython sre-engine = "0.1.2" diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 898745881..c7eb74165 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -285,7 +285,7 @@ impl PyByteArray { } } - fn irepeat(zelf: &crate::PyObjectView, n: usize, vm: &VirtualMachine) -> PyResult<()> { + fn irepeat(zelf: &crate::PyObjectView, n: isize, vm: &VirtualMachine) -> PyResult<()> { if n == 1 { return Ok(()); } @@ -300,19 +300,8 @@ impl PyByteArray { }; } }; - let elements = &mut w.elements; - if n == 0 { - elements.clear(); - } else if n != 1 { - let old = elements.clone(); - - elements.reserve((n - 1) * old.len()); - for _ in 1..n { - elements.extend(&old); - } - } - Ok(()) + w.imul(n, vm) } #[pymethod] @@ -635,14 +624,13 @@ impl PyByteArray { #[pymethod(name = "__rmul__")] #[pymethod(magic)] fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult { - vm.check_repeat_or_memory_error(self.len(), value) - .map(|value| self.inner().repeat(value).into()) + self.inner().mul(value, vm).map(|x| x.into()) } #[pymethod(magic)] fn imul(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { - vm.check_repeat_or_memory_error(zelf.len(), value) - .and_then(|value| Self::irepeat(&zelf, value, vm).map(|_| zelf)) + Self::irepeat(&zelf, value, vm)?; + Ok(zelf) } #[pymethod(name = "__mod__")] diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 0ad88c8dc..750a91425 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -461,14 +461,9 @@ impl PyBytes { // This only works for `bytes` itself, not its subclasses. return Ok(zelf); } - // todo: map err to overflow. - vm.check_repeat_or_memory_error(zelf.inner.len(), value) - .map(|value| { - let bytes: PyBytes = zelf.inner.repeat(value).into(); - bytes.into_ref(vm) - }) - // see issue 45044 on b.p.o. - .map_err(|_| vm.new_overflow_error("repeated bytes are too long".to_owned())) + zelf.inner + .mul(value, vm) + .map(|x| Self::from(x).into_ref(vm)) } #[pymethod(name = "__mod__")] diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 7a9abac7c..835d84acb 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -2,24 +2,24 @@ use super::{PositionIterInternal, PyGenericAlias, PySlice, PyTupleRef, PyTypeRef use crate::common::lock::{ PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard, }; +use crate::sequence::MutObjectSequenceOp; use crate::{ function::{ArgIterable, FuncArgs, IntoPyObject, OptionalArg}, protocol::{PyIterReturn, PyMappingMethods}, - sequence::{self, SimpleSeq}, + sequence::{ObjectSequenceOp, SequenceMutOp, SequenceOp}, sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex}, - stdlib::sys, types::{ - richcompare_wrapper, AsMapping, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, RichCompareFunc, Unconstructible, Unhashable, + AsMapping, Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable, + PyComparisonOp, Unconstructible, Unhashable, }, utils::Either, vm::{ReprGuard, VirtualMachine}, - IdProtocol, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef, - PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, + PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TryFromObject, TypeProtocol, }; use std::fmt; use std::mem::size_of; -use std::ops::{DerefMut, Range}; +use std::ops::DerefMut; /// Built-in mutable sequence. /// @@ -240,189 +240,26 @@ impl PyList { #[pymethod(magic)] #[pymethod(name = "__rmul__")] - fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult> { - let new_elements = sequence::seq_mul(vm, &self.borrow_vec(), value)? - .cloned() - .collect(); - Ok(Self::new_ref(new_elements, &vm.ctx)) + fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult> { + let elements = &*self.borrow_vec(); + let v = elements.mul(vm, n)?; + Ok(Self::new_ref(v, &vm.ctx)) } #[pymethod(magic)] - fn imul(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { - let mut elements = zelf.borrow_vec_mut(); - let mut new_elements: Vec = - sequence::seq_mul(vm, &*elements, value)?.cloned().collect(); - std::mem::swap(elements.deref_mut(), &mut new_elements); - Ok(zelf.clone()) - } - - fn _iter_equal( - &self, - needle: &PyObject, - range: Range, - mut f: F, - vm: &VirtualMachine, - ) -> PyResult { - let needle_cls = needle.class(); - let needle_cmp = needle_cls - .mro_find_map(|cls| cls.slots.richcompare.load()) - .unwrap(); - - let mut borrower = None; - let mut i = range.start; - - let index = loop { - if i >= range.end { - break usize::MAX; - } - let guard = if let Some(x) = borrower.take() { - x - } else { - self.borrow_vec() - }; - - let elem = if let Some(x) = guard.get(i) { - x - } else { - break usize::MAX; - }; - - if elem.is(needle) { - f(); - if SHORT { - break i; - } - borrower = Some(guard); - } else { - let elem_cls = elem.class(); - let reverse_first = !elem_cls.is(&needle_cls) && elem_cls.issubclass(&needle_cls); - - let eq = if reverse_first { - let elem_cmp = elem_cls - .mro_find_map(|cls| cls.slots.richcompare.load()) - .unwrap(); - drop(elem_cls); - - fn cmp( - elem: &PyObject, - needle: &PyObject, - elem_cmp: RichCompareFunc, - needle_cmp: RichCompareFunc, - vm: &VirtualMachine, - ) -> PyResult { - match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { - Either::B(PyComparisonValue::Implemented(value)) => Ok(value), - Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { - obj.try_to_bool(vm) - } - _ => match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { - Either::B(PyComparisonValue::Implemented(value)) => Ok(value), - Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { - obj.try_to_bool(vm) - } - _ => Ok(false), - }, - } - } - - if elem_cmp as usize == richcompare_wrapper as usize { - let elem = elem.clone(); - drop(guard); - cmp(&elem, needle, elem_cmp, needle_cmp, vm)? - } else { - let eq = cmp(elem, needle, elem_cmp, needle_cmp, vm)?; - borrower = Some(guard); - eq - } - } else { - match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { - Either::B(PyComparisonValue::Implemented(value)) => { - drop(elem_cls); - borrower = Some(guard); - value - } - Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { - drop(elem_cls); - borrower = Some(guard); - obj.try_to_bool(vm)? - } - _ => { - let elem_cmp = elem_cls - .mro_find_map(|cls| cls.slots.richcompare.load()) - .unwrap(); - drop(elem_cls); - - fn cmp( - elem: &PyObject, - needle: &PyObject, - elem_cmp: RichCompareFunc, - vm: &VirtualMachine, - ) -> PyResult { - match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { - Either::B(PyComparisonValue::Implemented(value)) => Ok(value), - Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { - obj.try_to_bool(vm) - } - _ => Ok(false), - } - } - - if elem_cmp as usize == richcompare_wrapper as usize { - let elem = elem.clone(); - drop(guard); - cmp(&elem, needle, elem_cmp, vm)? - } else { - let eq = cmp(elem, needle, elem_cmp, vm)?; - borrower = Some(guard); - eq - } - } - } - }; - - if eq { - f(); - if SHORT { - break i; - } - } - } - i += 1; - }; - - // TODO: Optioned - Ok(index) - } - - fn foreach_equal( - &self, - needle: &PyObject, - f: F, - vm: &VirtualMachine, - ) -> PyResult<()> { - self._iter_equal::<_, false>(needle, 0..usize::MAX, f, vm) - .map(|_| ()) - } - - fn find_equal( - &self, - needle: &PyObject, - range: Range, - vm: &VirtualMachine, - ) -> PyResult { - self._iter_equal::<_, true>(needle, range, || {}, vm) + fn imul(zelf: PyRef, n: isize, vm: &VirtualMachine) -> PyResult> { + zelf.borrow_vec_mut().imul(vm, n)?; + Ok(zelf) } #[pymethod] fn count(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let mut count = 0; - self.foreach_equal(&needle, || count += 1, vm)?; - Ok(count) + self.mut_count(vm, &needle) } #[pymethod(magic)] pub(crate) fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - Ok(self.find_equal(&needle, 0..usize::MAX, vm)? != usize::MAX) + self.mut_contains(vm, &needle) } #[pymethod] @@ -437,12 +274,12 @@ impl PyList { let start = start.map(|i| saturate_index(i, len)).unwrap_or(0); let stop = stop .map(|i| saturate_index(i, len)) - .unwrap_or(sys::MAXSIZE as usize); - let index = self.find_equal(&needle, start..stop, vm)?; - if index == usize::MAX { - Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?))) - } else { + .unwrap_or(isize::MAX as usize); + let index = self.mut_index_range(vm, &needle, start..stop)?; + if let Some(index) = index.into() { Ok(index) + } else { + Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?))) } } @@ -464,9 +301,9 @@ impl PyList { #[pymethod] fn remove(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let index = self.find_equal(&needle, 0..usize::MAX, vm)?; + let index = self.mut_index(vm, &needle)?; - if index != usize::MAX { + if let Some(index) = index.into() { // defer delete out of borrow Ok(self.borrow_vec_mut().remove(index)) } else { @@ -540,6 +377,18 @@ impl PyList { } } +impl<'a> MutObjectSequenceOp<'a> for PyList { + type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>; + + fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef> { + guard.get(index) + } + + fn do_lock(&'a self) -> Self::Guard { + self.borrow_vec() + } +} + impl AsMapping for PyList { fn as_mapping(_zelf: &crate::PyObjectView, _vm: &VirtualMachine) -> PyMappingMethods { PyMappingMethods { @@ -593,9 +442,9 @@ impl Comparable for PyList { return Ok(res.into()); } let other = class_or_notimplemented!(Self, other); - let a = zelf.borrow_vec(); - let b = other.borrow_vec(); - sequence::cmp(vm, a.boxed_iter(), b.boxed_iter(), op).map(PyComparisonValue::Implemented) + let a = &*zelf.borrow_vec(); + let b = &*other.borrow_vec(); + a.cmp(vm, b, op).map(PyComparisonValue::Implemented) } } diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index 24d3c537d..080f4f9bd 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -8,6 +8,7 @@ use crate::{ format::{FormatSpec, FormatString, FromTemplate}, function::{ArgIterable, FuncArgs, IntoPyException, IntoPyObject, OptionalArg, OptionalOption}, protocol::{PyIterReturn, PyMappingMethods}, + sequence::SequenceOp, sliceable::PySliceableSequence, types::{ AsMapping, Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable, @@ -493,11 +494,10 @@ impl PyStr { // This only works for `str` itself, not its subclasses. return Ok(zelf); } - // todo: map err to overflow. - vm.check_repeat_or_memory_error(zelf.len(), value) - .map(|value| Self::from(zelf.as_str().repeat(value)).into_ref(vm)) - // see issue 45044 on b.p.o. - .map_err(|_| vm.new_overflow_error("repeated bytes are too long".to_owned())) + zelf.as_str() + .as_bytes() + .mul(vm, value) + .map(|x| Self::from(unsafe { String::from_utf8_unchecked(x) }).into_ref(vm)) } #[pymethod(magic)] diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 2dc699465..b69b520a2 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -3,7 +3,7 @@ use crate::common::hash::PyHash; use crate::{ function::{IntoPyObject, OptionalArg}, protocol::{PyIterReturn, PyMappingMethods}, - sequence::{self, SimpleSeq}, + sequence::{ObjectSequenceOp, SequenceOp}, sliceable::PySliceableSequence, stdlib::sys, types::{ @@ -228,10 +228,8 @@ impl PyTuple { // This only works for `tuple` itself, not its subclasses. zelf } else { - let elements = sequence::seq_mul(vm, &zelf.elements, value)? - .cloned() - .collect::>() - .into_boxed_slice(); + let v = zelf.elements.mul(vm, value)?; + let elements = v.into_boxed_slice(); Self { elements }.into_ref(vm) }) } @@ -360,7 +358,7 @@ impl Comparable for PyTuple { let other = class_or_notimplemented!(Self, other); let a = zelf.as_slice(); let b = other.as_slice(); - sequence::cmp(vm, a.boxed_iter(), b.boxed_iter(), op).map(PyComparisonValue::Implemented) + a.cmp(vm, b, op).map(PyComparisonValue::Implemented) } } diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index e79c83227..31abb5d85 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -4,8 +4,8 @@ use crate::{ pystr, PyByteArray, PyBytes, PyBytesRef, PyInt, PyIntRef, PyStr, PyStrRef, PyTypeRef, }, cformat::CFormatBytes, - function::ArgIterable, - function::{OptionalArg, OptionalOption}, + function::{ArgIterable, OptionalArg, OptionalOption}, + sequence::{SequenceMutOp, SequenceOp}, sliceable::PySliceableSequence, types::PyComparisonOp, utils::Either, @@ -909,8 +909,12 @@ impl PyBytesInner { .format(vm, values) } - pub fn repeat(&self, n: usize) -> Vec { - self.elements.repeat(n) + pub fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult> { + self.elements.mul(vm, n) + } + + pub fn imul(&mut self, n: isize, vm: &VirtualMachine) -> PyResult<()> { + self.elements.imul(vm, n) } } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 804b31344..3d897fe8b 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -66,7 +66,7 @@ mod pyobject; mod pyobjectrc; pub mod readline; pub mod scope; -mod sequence; +pub mod sequence; pub mod signal; pub mod sliceable; pub mod stdlib; diff --git a/vm/src/sequence.rs b/vm/src/sequence.rs index d12be98f3..d06e192f8 100644 --- a/vm/src/sequence.rs +++ b/vm/src/sequence.rs @@ -1,107 +1,294 @@ -use crate::types::PyComparisonOp; -use crate::vm::VirtualMachine; -use crate::{PyObjectRef, PyResult}; +use itertools::Itertools; +use optional::Optioned; +use std::{collections::VecDeque, ops::Range}; -pub(super) type DynPyIter<'a> = Box + 'a>; +use crate::{ + types::{richcompare_wrapper, PyComparisonOp, RichCompareFunc}, + utils::Either, + vm::VirtualMachine, + IdProtocol, PyComparisonValue, PyObject, PyObjectRef, PyResult, TypeProtocol, +}; -#[allow(clippy::len_without_is_empty)] -pub(crate) trait SimpleSeq { - fn len(&self) -> usize; - fn boxed_iter(&self) -> DynPyIter; -} +pub trait ObjectSequenceOp<'a> { + type Iter: ExactSizeIterator; -impl<'a, D> SimpleSeq for D -where - D: 'a + std::ops::Deref, -{ - fn len(&self) -> usize { - self.deref().len() - } + fn iter(&'a self) -> Self::Iter; - fn boxed_iter(&self) -> DynPyIter { - Box::new(self.deref().iter()) - } -} - -pub(crate) fn eq(vm: &VirtualMachine, zelf: DynPyIter, other: DynPyIter) -> PyResult { - if zelf.len() == other.len() { - for (a, b) in Iterator::zip(zelf, other) { + fn eq(&'a self, vm: &VirtualMachine, other: &'a Self) -> PyResult { + let lhs = self.iter(); + let rhs = other.iter(); + if lhs.len() != rhs.len() { + return Ok(false); + } + for (a, b) in lhs.zip_eq(rhs) { if !vm.identical_or_equal(a, b)? { return Ok(false); } } Ok(true) - } else { - Ok(false) } -} -pub fn cmp( - vm: &VirtualMachine, - zelf: DynPyIter, - other: DynPyIter, - op: PyComparisonOp, -) -> PyResult { - let less = match op { - PyComparisonOp::Eq => return eq(vm, zelf, other), - PyComparisonOp::Ne => return eq(vm, zelf, other).map(|eq| !eq), - PyComparisonOp::Lt | PyComparisonOp::Le => true, - PyComparisonOp::Gt | PyComparisonOp::Ge => false, - }; - let (lhs_len, rhs_len) = (zelf.len(), other.len()); - for (a, b) in Iterator::zip(zelf, other) { - let ret = if less { - vm.bool_seq_lt(a, b)? - } else { - vm.bool_seq_gt(a, b)? + fn cmp(&'a self, vm: &VirtualMachine, other: &'a Self, op: PyComparisonOp) -> PyResult { + let less = match op { + PyComparisonOp::Eq => return self.eq(vm, other), + PyComparisonOp::Ne => return self.eq(vm, other).map(|eq| !eq), + PyComparisonOp::Lt | PyComparisonOp::Le => true, + PyComparisonOp::Gt | PyComparisonOp::Ge => false, }; - if let Some(v) = ret { - return Ok(v); - } - } - Ok(op.eval_ord(lhs_len.cmp(&rhs_len))) -} -pub(crate) struct SeqMul<'a> { - seq: &'a dyn SimpleSeq, - repetitions: usize, - iter: Option>, -} - -impl ExactSizeIterator for SeqMul<'_> {} - -impl<'a> Iterator for SeqMul<'a> { - type Item = &'a PyObjectRef; - fn next(&mut self) -> Option { - match self.iter.as_mut().and_then(Iterator::next) { - Some(item) => Some(item), - None => { - if self.repetitions == 0 { - None - } else { - self.repetitions -= 1; - self.iter = Some(self.seq.boxed_iter()); - self.next() - } + let lhs = self.iter(); + let rhs = other.iter(); + let lhs_len = lhs.len(); + let rhs_len = rhs.len(); + for (a, b) in lhs.zip(rhs) { + let ret = if less { + vm.bool_seq_lt(a, b)? + } else { + vm.bool_seq_gt(a, b)? + }; + if let Some(v) = ret { + return Ok(v); } } - } - fn size_hint(&self) -> (usize, Option) { - let size = self.iter.as_ref().map_or(0, ExactSizeIterator::len) - + (self.repetitions * self.seq.len()); - (size, Some(size)) + Ok(op.eval_ord(lhs_len.cmp(&rhs_len))) } } -pub(crate) fn seq_mul<'a>( - vm: &VirtualMachine, - seq: &'a impl SimpleSeq, - repetitions: isize, -) -> PyResult> { - vm.check_repeat_or_memory_error(seq.len(), repetitions) - .map(|repetitions| SeqMul { - seq, - repetitions, - iter: None, - }) +impl<'a> ObjectSequenceOp<'a> for [PyObjectRef] { + type Iter = core::slice::Iter<'a, PyObjectRef>; + + fn iter(&'a self) -> Self::Iter { + self.iter() + } +} + +impl<'a> ObjectSequenceOp<'a> for VecDeque { + type Iter = std::collections::vec_deque::Iter<'a, PyObjectRef>; + + fn iter(&'a self) -> Self::Iter { + self.iter() + } +} + +pub trait MutObjectSequenceOp<'a> { + type Guard; + + fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef>; + fn do_lock(&'a self) -> Self::Guard; + + fn mut_count(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult { + let mut count = 0; + self._mut_iter_equal_skeleton::<_, false>(vm, needle, 0..isize::MAX as usize, || { + count += 1 + })?; + Ok(count) + } + + fn mut_index_range( + &'a self, + vm: &VirtualMachine, + needle: &PyObject, + range: Range, + ) -> PyResult> { + self._mut_iter_equal_skeleton::<_, true>(vm, needle, range, || {}) + } + + fn mut_index(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult> { + self.mut_index_range(vm, needle, 0..isize::MAX as usize) + } + + fn mut_contains(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult { + self.mut_index(vm, needle).map(|x| x.is_some()) + } + + fn _mut_iter_equal_skeleton( + &'a self, + vm: &VirtualMachine, + needle: &PyObject, + range: Range, + mut f: F, + ) -> PyResult> + where + F: FnMut(), + { + let needle_cls = needle.class(); + let needle_cmp = needle_cls + .mro_find_map(|cls| cls.slots.richcompare.load()) + .unwrap(); + + let mut borrower = None; + let mut i = range.start; + + let index = loop { + if i >= range.end { + break Optioned::::none(); + } + let guard = if let Some(x) = borrower.take() { + x + } else { + self.do_lock() + }; + + let elem = if let Some(x) = Self::do_get(i, &guard) { + x + } else { + break Optioned::::none(); + }; + + if elem.is(needle) { + f(); + if SHORT { + break Optioned::::some(i); + } + borrower = Some(guard); + } else { + let elem_cls = elem.class(); + let reverse_first = !elem_cls.is(&needle_cls) && elem_cls.issubclass(&needle_cls); + + let eq = if reverse_first { + let elem_cmp = elem_cls + .mro_find_map(|cls| cls.slots.richcompare.load()) + .unwrap(); + drop(elem_cls); + + fn cmp( + elem: &PyObject, + needle: &PyObject, + elem_cmp: RichCompareFunc, + needle_cmp: RichCompareFunc, + vm: &VirtualMachine, + ) -> PyResult { + match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { + Either::B(PyComparisonValue::Implemented(value)) => Ok(value), + Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { + obj.try_to_bool(vm) + } + _ => match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { + Either::B(PyComparisonValue::Implemented(value)) => Ok(value), + Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { + obj.try_to_bool(vm) + } + _ => Ok(false), + }, + } + } + + if elem_cmp as usize == richcompare_wrapper as usize { + let elem = elem.clone(); + drop(guard); + cmp(&elem, needle, elem_cmp, needle_cmp, vm)? + } else { + let eq = cmp(elem, needle, elem_cmp, needle_cmp, vm)?; + borrower = Some(guard); + eq + } + } else { + match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? { + Either::B(PyComparisonValue::Implemented(value)) => { + drop(elem_cls); + borrower = Some(guard); + value + } + Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { + drop(elem_cls); + borrower = Some(guard); + obj.try_to_bool(vm)? + } + _ => { + let elem_cmp = elem_cls + .mro_find_map(|cls| cls.slots.richcompare.load()) + .unwrap(); + drop(elem_cls); + + fn cmp( + elem: &PyObject, + needle: &PyObject, + elem_cmp: RichCompareFunc, + vm: &VirtualMachine, + ) -> PyResult { + match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? { + Either::B(PyComparisonValue::Implemented(value)) => Ok(value), + Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => { + obj.try_to_bool(vm) + } + _ => Ok(false), + } + } + + if elem_cmp as usize == richcompare_wrapper as usize { + let elem = elem.clone(); + drop(guard); + cmp(&elem, needle, elem_cmp, vm)? + } else { + let eq = cmp(elem, needle, elem_cmp, vm)?; + borrower = Some(guard); + eq + } + } + } + }; + + if eq { + f(); + if SHORT { + break Optioned::::some(i); + } + } + } + i += 1; + }; + + Ok(index) + } +} + +pub trait SequenceOp { + fn as_slice(&self) -> &[T]; + + fn mul(&self, vm: &VirtualMachine, n: isize) -> PyResult> { + let n = vm.check_repeat_or_overflow_error(self.as_slice().len(), n)?; + let mut v = Vec::with_capacity(n * self.as_slice().len()); + for _ in 0..n { + v.extend_from_slice(self.as_slice()); + } + Ok(v) + } +} + +impl SequenceOp for [T] { + fn as_slice(&self) -> &[T] { + self + } +} + +pub trait SequenceMutOp { + fn as_slice(&self) -> &[T]; + fn as_vec_mut(&mut self) -> &mut Vec; + + fn imul(&mut self, vm: &VirtualMachine, n: isize) -> PyResult<()> { + let n = vm.check_repeat_or_overflow_error(self.as_slice().len(), n)?; + if n == 0 { + self.as_vec_mut().clear(); + } else if n != 1 { + let mut sample = self.as_slice().to_vec(); + if n != 2 { + self.as_vec_mut().reserve(sample.len() * (n - 1)); + for _ in 0..n - 2 { + self.as_vec_mut().extend_from_slice(&sample); + } + } + self.as_vec_mut().append(&mut sample); + } + Ok(()) + } +} + +impl SequenceMutOp for Vec { + fn as_slice(&self) -> &[T] { + self.as_slice() + } + + fn as_vec_mut(&mut self) -> &mut Vec { + self + } } diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 5565596bc..5a3da10fa 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -2,16 +2,17 @@ pub(crate) use _collections::make_module; #[pymodule] mod _collections { - use crate::builtins::{PositionIterInternal, PyGenericAlias}; - use crate::common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard}; use crate::{ builtins::{ IterStatus::{Active, Exhausted}, - PyInt, PyTypeRef, + PositionIterInternal, PyGenericAlias, PyInt, PyTypeRef, }, + common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard}, function::{FuncArgs, KwArgs, OptionalArg}, protocol::PyIterReturn, - sequence, sliceable, + sequence::{MutObjectSequenceOp, ObjectSequenceOp}, + sliceable, + sliceable::saturate_index, types::{ Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable, PyComparisonOp, Unhashable, @@ -22,7 +23,7 @@ mod _collections { }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; - use std::cmp::{max, min}; + use std::cmp::max; use std::collections::VecDeque; #[pyattr] @@ -54,24 +55,6 @@ mod _collections { } } - struct SimpleSeqDeque<'a>(PyRwLockReadGuard<'a, VecDeque>); - - impl sequence::SimpleSeq for SimpleSeqDeque<'_> { - fn len(&self) -> usize { - self.0.len() - } - - fn boxed_iter(&self) -> sequence::DynPyIter { - Box::new(self.0.iter()) - } - } - - impl<'a> From>> for SimpleSeqDeque<'a> { - fn from(from: PyRwLockReadGuard<'a, VecDeque>) -> Self { - Self(from) - } - } - #[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Iterable))] impl PyDeque { #[pyslot] @@ -178,17 +161,11 @@ mod _collections { #[pymethod] fn count(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let mut count = 0; let start_state = self.state.load(); - let deque = self.borrow_deque().clone(); - for elem in deque.iter() { - if vm.identical_or_equal(elem, &obj)? { - count += 1; - } + let count = self.mut_count(vm, &obj)?; - if start_state != self.state.load() { - return Err(vm.new_runtime_error("deque mutated during iteration".to_owned())); - } + if start_state != self.state.load() { + return Err(vm.new_runtime_error("deque mutated during iteration".to_owned())); } Ok(count) } @@ -235,14 +212,6 @@ mod _collections { Ok(()) } - fn adjust_negative_index(&self, index: isize) -> usize { - if index.is_negative() { - max(index + self.borrow_deque().len() as isize, 0) as usize - } else { - index as usize - } - } - #[pymethod] fn index( &self, @@ -251,35 +220,25 @@ mod _collections { stop: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - let deque = self.borrow_deque().clone(); let start_state = self.state.load(); - let start = self.adjust_negative_index(start.unwrap_or(0)); - let stop = min( - self.adjust_negative_index(stop.unwrap_or_else(|| deque.len() as isize)), - deque.len(), - ); - - for (i, elem) in deque - .iter() - .skip(start) - .take(stop.saturating_sub(start)) - .enumerate() - { - let is_element = vm.identical_or_equal(elem, &obj)?; - - if start_state != self.state.load() { - return Err(vm.new_runtime_error("deque mutated during iteration".to_owned())); - } - if is_element { - return Ok(i + start); - } + let len = self.len(); + let start = start.map(|i| saturate_index(i, len)).unwrap_or(0); + let stop = stop + .map(|i| saturate_index(i, len)) + .unwrap_or(isize::MAX as usize); + let index = self.mut_index_range(vm, &obj, start..stop)?; + if start_state != self.state.load() { + Err(vm.new_runtime_error("deque mutated during iteration".to_owned())) + } else if let Some(index) = index.into() { + Ok(index) + } else { + Err(vm.new_value_error( + obj.repr(vm) + .map(|repr| format!("{} is not in deque", repr)) + .unwrap_or_else(|_| String::new()), + )) } - Err(vm.new_value_error( - obj.repr(vm) - .map(|repr| format!("{} is not in deque", repr)) - .unwrap_or_else(|_| String::new()), - )) } #[pymethod] @@ -326,23 +285,17 @@ mod _collections { #[pymethod] fn remove(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let deque = self.borrow_deque().clone(); let start_state = self.state.load(); + let index = self.mut_index(vm, &obj)?; - let mut idx = None; - for (i, elem) in deque.iter().enumerate() { - if vm.identical_or_equal(elem, &obj)? { - idx = Some(i); - break; - } - } - let mut deque = self.borrow_deque_mut(); if start_state != self.state.load() { Err(vm.new_index_error("deque mutated during remove().".to_owned())) - } else { + } else if let Some(index) = index.into() { + let mut deque = self.borrow_deque_mut(); self.state.fetch_add(1); - idx.map(|idx| deque.remove(idx).unwrap()) - .ok_or_else(|| vm.new_value_error("deque.remove(x): x not in deque".to_owned())) + Ok(deque.remove(index).unwrap()) + } else { + Err(vm.new_value_error("deque.remove(x): x not in deque".to_owned())) } } @@ -431,32 +384,32 @@ mod _collections { #[pymethod(magic)] fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { let start_state = self.state.load(); - let deque = self.borrow_deque().clone(); - for element in deque.iter() { - let is_element = vm.identical_or_equal(element, &needle)?; - - if start_state != self.state.load() { - return Err(vm.new_runtime_error("deque mutated during iteration".to_owned())); - } - if is_element { - return Ok(true); - } + let ret = self.mut_contains(vm, &needle)?; + if start_state != self.state.load() { + Err(vm.new_runtime_error("deque mutated during iteration".to_owned())) + } else { + Ok(ret) } + } - Ok(false) + fn _mul(&self, n: isize, vm: &VirtualMachine) -> PyResult> { + let deque = self.borrow_deque(); + let n = vm.check_repeat_or_overflow_error(deque.len(), n)?; + let mul_len = n * deque.len(); + let iter = deque.iter().cycle().take(mul_len); + let skipped = self + .maxlen + .and_then(|maxlen| mul_len.checked_sub(maxlen)) + .unwrap_or(0); + + let deque = iter.skip(skipped).cloned().collect(); + Ok(deque) } #[pymethod(magic)] #[pymethod(name = "__rmul__")] - fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult { - let deque: SimpleSeqDeque = self.borrow_deque().into(); - let mul = sequence::seq_mul(vm, &deque, value)?; - let skipped = self - .maxlen - .and_then(|maxlen| mul.len().checked_sub(maxlen)) - .unwrap_or(0); - - let deque = mul.skip(skipped).cloned().collect(); + fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult { + let deque = self._mul(n, vm)?; Ok(PyDeque { deque: PyRwLock::new(deque), maxlen: self.maxlen, @@ -465,12 +418,9 @@ mod _collections { } #[pymethod(magic)] - fn imul(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { - let mul_deque = zelf.mul(value, vm)?; - std::mem::swap( - &mut *zelf.borrow_deque_mut(), - &mut *mul_deque.borrow_deque_mut(), - ); + fn imul(zelf: PyRef, n: isize, vm: &VirtualMachine) -> PyResult> { + let mul_deque = zelf._mul(n, vm)?; + *zelf.borrow_deque_mut() = mul_deque; Ok(zelf) } @@ -536,6 +486,18 @@ mod _collections { } } + impl<'a> MutObjectSequenceOp<'a> for PyDeque { + type Guard = PyRwLockReadGuard<'a, VecDeque>; + + fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef> { + guard.get(index) + } + + fn do_lock(&'a self) -> Self::Guard { + self.borrow_deque() + } + } + impl Comparable for PyDeque { fn cmp( zelf: &crate::PyObjectView, @@ -547,9 +509,9 @@ mod _collections { return Ok(res.into()); } let other = class_or_notimplemented!(Self, other); - let (lhs, rhs) = (zelf.borrow_deque(), other.borrow_deque()); - sequence::cmp(vm, Box::new(lhs.iter()), Box::new(rhs.iter()), op) - .map(PyComparisonValue::Implemented) + let lhs = zelf.borrow_deque(); + let rhs = other.borrow_deque(); + lhs.cmp(vm, &rhs, op).map(PyComparisonValue::Implemented) } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 49b2583f7..dbf7acf03 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1745,13 +1745,16 @@ impl VirtualMachine { /// Checks that the multiplication is able to be performed. On Ok returns the /// index as a usize for sequences to be able to use immediately. - pub fn check_repeat_or_memory_error(&self, length: usize, n: isize) -> PyResult { - let n = n.to_usize().unwrap_or(0); - if n > 0 && length > stdlib::sys::MAXSIZE as usize / n { - // Empty message is currently used in CPython. - Err(self.new_memory_error("".to_owned())) + pub fn check_repeat_or_overflow_error(&self, length: usize, n: isize) -> PyResult { + if n <= 0 { + Ok(0) } else { - Ok(n) + let n = n as usize; + if length > stdlib::sys::MAXSIZE as usize / n { + Err(self.new_overflow_error("repeated value are too long".to_owned())) + } else { + Ok(n) + } } }