Merge pull request #7184 from youknowone/winsound

Update test_winsound from v3.14.3
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-02-18 00:47:16 +09:00
commit 31aae8ef5c
15 files changed
+398

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+187
View File
@@ -0,0 +1,187 @@
# Ridiculously simple test of the winsound module for Windows.
import functools
import os
import time
import unittest
from test import support
from test.support import import_helper
from test.support import os_helper
support.requires('audio')
winsound = import_helper.import_module('winsound')
# Unless we actually have an ear in the room, we have no idea whether a sound
# actually plays, and it's incredibly flaky trying to figure out if a sound
# even *should* play. Instead of guessing, just call the function and assume
# it either passed or raised the RuntimeError we expect in case of failure.
def sound_func(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
ret = func(*args, **kwargs)
except RuntimeError as e:
if support.verbose:
print(func.__name__, 'failed:', e)
else:
if support.verbose:
print(func.__name__, 'returned')
return ret
return wrapper
safe_Beep = sound_func(winsound.Beep)
safe_MessageBeep = sound_func(winsound.MessageBeep)
safe_PlaySound = sound_func(winsound.PlaySound)
class BeepTest(unittest.TestCase):
def test_errors(self):
self.assertRaises(TypeError, winsound.Beep)
self.assertRaises(ValueError, winsound.Beep, 36, 75)
self.assertRaises(ValueError, winsound.Beep, 32768, 75)
def test_extremes(self):
safe_Beep(37, 75)
safe_Beep(32767, 75)
def test_increasingfrequency(self):
for i in range(100, 2000, 100):
safe_Beep(i, 75)
def test_keyword_args(self):
safe_Beep(duration=75, frequency=2000)
class MessageBeepTest(unittest.TestCase):
def tearDown(self):
time.sleep(0.5)
def test_default(self):
self.assertRaises(TypeError, winsound.MessageBeep, "bad")
self.assertRaises(TypeError, winsound.MessageBeep, 42, 42)
safe_MessageBeep()
def test_ok(self):
safe_MessageBeep(winsound.MB_OK)
def test_asterisk(self):
safe_MessageBeep(winsound.MB_ICONASTERISK)
def test_exclamation(self):
safe_MessageBeep(winsound.MB_ICONEXCLAMATION)
def test_hand(self):
safe_MessageBeep(winsound.MB_ICONHAND)
def test_question(self):
safe_MessageBeep(winsound.MB_ICONQUESTION)
def test_error(self):
safe_MessageBeep(winsound.MB_ICONERROR)
def test_information(self):
safe_MessageBeep(winsound.MB_ICONINFORMATION)
def test_stop(self):
safe_MessageBeep(winsound.MB_ICONSTOP)
def test_warning(self):
safe_MessageBeep(winsound.MB_ICONWARNING)
def test_keyword_args(self):
safe_MessageBeep(type=winsound.MB_OK)
class PlaySoundTest(unittest.TestCase):
def test_errors(self):
self.assertRaises(TypeError, winsound.PlaySound)
self.assertRaises(TypeError, winsound.PlaySound, "bad", "bad")
self.assertRaises(
RuntimeError,
winsound.PlaySound,
"none", winsound.SND_ASYNC | winsound.SND_MEMORY
)
self.assertRaises(TypeError, winsound.PlaySound, b"bad", 0)
self.assertRaises(TypeError, winsound.PlaySound, "bad",
winsound.SND_MEMORY)
self.assertRaises(TypeError, winsound.PlaySound, 1, 0)
# embedded null character
self.assertRaises(ValueError, winsound.PlaySound, 'bad\0', 0)
def test_keyword_args(self):
safe_PlaySound(flags=winsound.SND_ALIAS, sound="SystemExit")
def test_snd_memory(self):
with open(support.findfile('pluck-pcm8.wav',
subdir='audiodata'), 'rb') as f:
audio_data = f.read()
safe_PlaySound(audio_data, winsound.SND_MEMORY)
audio_data = bytearray(audio_data)
safe_PlaySound(audio_data, winsound.SND_MEMORY)
def test_snd_filename(self):
fn = support.findfile('pluck-pcm8.wav', subdir='audiodata')
safe_PlaySound(fn, winsound.SND_FILENAME | winsound.SND_NODEFAULT)
def test_snd_filepath(self):
fn = support.findfile('pluck-pcm8.wav', subdir='audiodata')
path = os_helper.FakePath(fn)
safe_PlaySound(path, winsound.SND_FILENAME | winsound.SND_NODEFAULT)
def test_snd_filepath_as_bytes(self):
fn = support.findfile('pluck-pcm8.wav', subdir='audiodata')
self.assertRaises(
TypeError,
winsound.PlaySound,
os_helper.FakePath(os.fsencode(fn)),
winsound.SND_FILENAME | winsound.SND_NODEFAULT
)
def test_aliases(self):
aliases = [
"SystemAsterisk",
"SystemExclamation",
"SystemExit",
"SystemHand",
"SystemQuestion",
]
for alias in aliases:
with self.subTest(alias=alias):
safe_PlaySound(alias, winsound.SND_ALIAS)
def test_alias_fallback(self):
safe_PlaySound('!"$%&/(#+*', winsound.SND_ALIAS)
def test_alias_nofallback(self):
safe_PlaySound('!"$%&/(#+*', winsound.SND_ALIAS | winsound.SND_NODEFAULT)
def test_stopasync(self):
safe_PlaySound(
'SystemQuestion',
winsound.SND_ALIAS | winsound.SND_ASYNC | winsound.SND_LOOP
)
time.sleep(0.5)
safe_PlaySound('SystemQuestion', winsound.SND_ALIAS | winsound.SND_NOSTOP)
# Issue 8367: PlaySound(None, winsound.SND_PURGE)
# does not raise on systems without a sound card.
winsound.PlaySound(None, winsound.SND_PURGE)
def test_sound_sentry(self):
safe_PlaySound("SystemExit", winsound.SND_ALIAS | winsound.SND_SENTRY)
def test_sound_sync(self):
safe_PlaySound("SystemExit", winsound.SND_ALIAS | winsound.SND_SYNC)
def test_sound_system(self):
safe_PlaySound("SystemExit", winsound.SND_ALIAS | winsound.SND_SYSTEM)
if __name__ == "__main__":
unittest.main()
+1
View File
@@ -119,6 +119,7 @@ workspace = true
features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_Media_Audio",
"Win32_Networking_WinSock",
"Win32_Security",
"Win32_Security_Authorization",
+4
View File
@@ -67,6 +67,8 @@ pub mod sys;
mod winapi;
#[cfg(all(feature = "host_env", windows))]
mod winreg;
#[cfg(all(feature = "host_env", windows))]
mod winsound;
use crate::{Context, builtins::PyModuleDef};
@@ -132,6 +134,8 @@ pub fn builtin_module_defs(ctx: &Context) -> Vec<&'static PyModuleDef> {
#[cfg(all(feature = "host_env", windows))]
winreg::module_def(ctx),
#[cfg(all(feature = "host_env", windows))]
winsound::module_def(ctx),
#[cfg(all(feature = "host_env", windows))]
_wmi::module_def(ctx),
]
}
+206
View File
@@ -0,0 +1,206 @@
// spell-checker:ignore pszSound fdwSound
#![allow(non_snake_case)]
pub(crate) use winsound::module_def;
mod win32 {
#[link(name = "winmm")]
unsafe extern "system" {
pub fn PlaySoundW(pszSound: *const u16, hmod: isize, fdwSound: u32) -> i32;
}
unsafe extern "system" {
pub fn Beep(dwFreq: u32, dwDuration: u32) -> i32;
pub fn MessageBeep(uType: u32) -> i32;
}
}
#[pymodule]
mod winsound {
use crate::builtins::{PyBytes, PyStr};
use crate::common::windows::ToWideString;
use crate::convert::{IntoPyException, TryFromBorrowedObject};
use crate::protocol::PyBuffer;
use crate::{AsObject, PyObjectRef, PyResult, VirtualMachine};
// PlaySound flags
#[pyattr]
const SND_SYNC: u32 = 0x0000;
#[pyattr]
const SND_ASYNC: u32 = 0x0001;
#[pyattr]
const SND_NODEFAULT: u32 = 0x0002;
#[pyattr]
const SND_MEMORY: u32 = 0x0004;
#[pyattr]
const SND_LOOP: u32 = 0x0008;
#[pyattr]
const SND_NOSTOP: u32 = 0x0010;
#[pyattr]
const SND_PURGE: u32 = 0x0040;
#[pyattr]
const SND_APPLICATION: u32 = 0x0080;
#[pyattr]
const SND_NOWAIT: u32 = 0x00002000;
#[pyattr]
const SND_ALIAS: u32 = 0x00010000;
#[pyattr]
const SND_FILENAME: u32 = 0x00020000;
#[pyattr]
const SND_SENTRY: u32 = 0x00080000;
#[pyattr]
const SND_SYSTEM: u32 = 0x00200000;
// MessageBeep types
#[pyattr]
const MB_OK: u32 = 0x00000000;
#[pyattr]
const MB_ICONHAND: u32 = 0x00000010;
#[pyattr]
const MB_ICONQUESTION: u32 = 0x00000020;
#[pyattr]
const MB_ICONEXCLAMATION: u32 = 0x00000030;
#[pyattr]
const MB_ICONASTERISK: u32 = 0x00000040;
#[pyattr]
const MB_ICONERROR: u32 = MB_ICONHAND;
#[pyattr]
const MB_ICONSTOP: u32 = MB_ICONHAND;
#[pyattr]
const MB_ICONINFORMATION: u32 = MB_ICONASTERISK;
#[pyattr]
const MB_ICONWARNING: u32 = MB_ICONEXCLAMATION;
#[derive(FromArgs)]
struct PlaySoundArgs {
#[pyarg(any)]
sound: PyObjectRef,
#[pyarg(any)]
flags: i32,
}
#[pyfunction]
fn PlaySound(args: PlaySoundArgs, vm: &VirtualMachine) -> PyResult<()> {
let sound = args.sound;
let flags = args.flags as u32;
if vm.is_none(&sound) {
let ok = unsafe { super::win32::PlaySoundW(core::ptr::null(), 0, flags) };
if ok == 0 {
return Err(vm.new_runtime_error("Failed to play sound".to_owned()));
}
return Ok(());
}
if flags & SND_MEMORY != 0 {
if flags & SND_ASYNC != 0 {
return Err(
vm.new_runtime_error("Cannot play asynchronously from memory".to_owned())
);
}
let buffer = PyBuffer::try_from_borrowed_object(vm, &sound)?;
let buf = buffer.as_contiguous().ok_or_else(|| {
vm.new_type_error("a bytes-like object is required, not 'str'".to_owned())
})?;
let ok = unsafe { super::win32::PlaySoundW(buf.as_ptr() as *const u16, 0, flags) };
if ok == 0 {
return Err(vm.new_runtime_error("Failed to play sound".to_owned()));
}
return Ok(());
}
if sound.downcastable::<PyBytes>() {
let type_name = sound.class().name().to_string();
return Err(vm.new_type_error(format!(
"'sound' must be str, os.PathLike, or None, not {type_name}"
)));
}
// os.fspath(sound)
let path = match sound.downcast_ref::<PyStr>() {
Some(s) => s.as_str().to_owned(),
None => {
let fspath = vm.get_method_or_type_error(
sound.clone(),
identifier!(vm, __fspath__),
|| {
let type_name = sound.class().name().to_string();
format!("'sound' must be str, os.PathLike, or None, not {type_name}")
},
)?;
if vm.is_none(&fspath) {
return Err(vm.new_type_error(format!(
"'sound' must be str, os.PathLike, or None, not {}",
sound.class().name()
)));
}
let result = fspath.call((), vm)?;
if result.downcastable::<PyBytes>() {
return Err(
vm.new_type_error("'sound' must resolve to str, not bytes".to_owned())
);
}
let s: &PyStr = result.downcast_ref().ok_or_else(|| {
vm.new_type_error(format!(
"expected {}.__fspath__() to return str or bytes, not {}",
sound.class().name(),
result.class().name()
))
})?;
s.as_str().to_owned()
}
};
// Check for embedded null characters
if path.contains('\0') {
return Err(vm.new_value_error("embedded null character".to_owned()));
}
let wide = path.to_wide_with_nul();
let ok = unsafe { super::win32::PlaySoundW(wide.as_ptr(), 0, flags) };
if ok == 0 {
return Err(vm.new_runtime_error("Failed to play sound".to_owned()));
}
Ok(())
}
#[derive(FromArgs)]
struct BeepArgs {
#[pyarg(any)]
frequency: i32,
#[pyarg(any)]
duration: i32,
}
#[pyfunction]
fn Beep(args: BeepArgs, vm: &VirtualMachine) -> PyResult<()> {
if !(37..=32767).contains(&args.frequency) {
return Err(vm.new_value_error("frequency must be in 37 thru 32767".to_owned()));
}
let ok = unsafe { super::win32::Beep(args.frequency as u32, args.duration as u32) };
if ok == 0 {
return Err(vm.new_runtime_error("Failed to beep".to_owned()));
}
Ok(())
}
#[derive(FromArgs)]
struct MessageBeepArgs {
#[pyarg(any, default = 0)]
r#type: u32,
}
#[pyfunction]
fn MessageBeep(args: MessageBeepArgs, vm: &VirtualMachine) -> PyResult<()> {
let ok = unsafe { super::win32::MessageBeep(args.r#type) };
if ok == 0 {
return Err(std::io::Error::last_os_error().into_pyexception(vm));
}
Ok(())
}
}