Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 21 additions & 90 deletions Lib/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#
# History:
#
# <see CVS and SVN checkin messages for history>
# <see checkin messages for history>
#
# 1.0.9 - added invalidate_caches() function to invalidate cached values
# 1.0.8 - changed Windows support to read version from kernel32.dll
Expand Down Expand Up @@ -110,8 +110,6 @@

"""

__version__ = '1.0.9'

import collections
import os
import re
Expand All @@ -129,7 +127,7 @@
# Based on the description of the PHP's version_compare():
# http://php.net/manual/en/function.version-compare.php

_ver_stages = {
_ver_stages = frozendict({
# any string not found in this dict, will get 0 assigned
'dev': 10,
'alpha': 20, 'a': 20,
Expand All @@ -138,7 +136,7 @@
'RC': 50, 'rc': 50,
# number, will get 100 assigned
'pl': 200, 'p': 200,
}
})


def _comparable_version(version):
Expand Down Expand Up @@ -307,8 +305,7 @@ def _syscmd_ver(system='', release='', version='',
text=True,
encoding="locale",
shell=True)
except (OSError, subprocess.CalledProcessError) as why:
#print('Command %s failed: %s' % (cmd, why))
except (OSError, subprocess.CalledProcessError):
continue
else:
break
Expand Down Expand Up @@ -539,53 +536,6 @@ def ios_ver(system="", release="", model="", is_simulator=False):
return IOSVersionInfo(system, release, model, is_simulator)


def _java_getprop(name, default):
"""This private helper is deprecated in 3.13 and will be removed in 3.15"""
from java.lang import System
try:
value = System.getProperty(name)
if value is None:
return default
return value
except AttributeError:
return default

def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):

""" Version interface for Jython.

Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
tuple (os_name, os_version, os_arch).

Values which cannot be determined are set to the defaults
given as parameters (which all default to '').

"""
import warnings
warnings._deprecated('java_ver', remove=(3, 15))
# Import the needed APIs
try:
import java.lang # noqa: F401
except ImportError:
return release, vendor, vminfo, osinfo

vendor = _java_getprop('java.vendor', vendor)
release = _java_getprop('java.version', release)
vm_name, vm_release, vm_vendor = vminfo
vm_name = _java_getprop('java.vm.name', vm_name)
vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
vm_release = _java_getprop('java.vm.version', vm_release)
vminfo = vm_name, vm_release, vm_vendor
os_name, os_version, os_arch = osinfo
os_arch = _java_getprop('java.os.arch', os_arch)
os_name = _java_getprop('java.os.name', os_name)
os_version = _java_getprop('java.os.version', os_version)
osinfo = os_name, os_version, os_arch

return release, vendor, vminfo, osinfo


AndroidVer = collections.namedtuple(
"AndroidVer", "release api_level manufacturer model device is_emulator")

Expand Down Expand Up @@ -670,6 +620,9 @@ def system_alias(system, release, version):

### Various internal helpers

# Table for cleaning up characters in filenames.
_SIMPLE_SUBSTITUTIONS = str.maketrans(r' /\:;"()', r'_-------')

def _platform(*args):

""" Helper to format the platform string in a filename
Expand All @@ -679,28 +632,13 @@ def _platform(*args):
platform = '-'.join(x.strip() for x in filter(len, args))

# Cleanup some possible filename obstacles...
platform = platform.replace(' ', '_')
platform = platform.replace('/', '-')
platform = platform.replace('\\', '-')
platform = platform.replace(':', '-')
platform = platform.replace(';', '-')
platform = platform.replace('"', '-')
platform = platform.replace('(', '-')
platform = platform.replace(')', '-')
platform = platform.translate(_SIMPLE_SUBSTITUTIONS)

# No need to report 'unknown' information...
platform = platform.replace('unknown', '')

# Fold '--'s and remove trailing '-'
while True:
cleaned = platform.replace('--', '-')
if cleaned == platform:
break
platform = cleaned
while platform and platform[-1] == '-':
platform = platform[:-1]

return platform
return re.sub(r'-{2,}', '-', platform).rstrip('-')

def _node(default=''):

Expand Down Expand Up @@ -767,11 +705,11 @@ def _syscmd_file(target, default=''):

# Default values for architecture; non-empty strings override the
# defaults given as parameters
_default_architecture = {
_default_architecture = frozendict({
'win32': ('', 'WindowsPE'),
'win16': ('', 'Windows'),
'dos': ('', 'MSDOS'),
}
})

def architecture(executable=sys.executable, bits='', linkage=''):

Expand Down Expand Up @@ -1045,13 +983,6 @@ def uname():
version = '16bit'
system = 'Windows'

elif system[:4] == 'java':
release, vendor, vminfo, osinfo = java_ver()
system = 'Java'
version = ', '.join(vminfo)
if not version:
version = vendor

# System specific extensions
if system == 'OpenVMS':
# OpenVMS seems to have release and version mixed up
Expand Down Expand Up @@ -1386,15 +1317,6 @@ def platform(aliased=False, terse=False):
platform = _platform(system, release, machine, processor,
'with',
libcname+libcversion)
elif system == 'Java':
# Java platforms
r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
if terse or not os_name:
platform = _platform(system, release, version)
else:
platform = _platform(system, release, version,
'on',
os_name, os_version, os_arch)

else:
# Generic handler
Expand Down Expand Up @@ -1483,7 +1405,7 @@ def invalidate_caches():
def _parse_args(args: list[str] | None):
import argparse

parser = argparse.ArgumentParser(color=True)
parser = argparse.ArgumentParser()
parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"])
parser.add_argument(
"--terse",
Expand Down Expand Up @@ -1516,5 +1438,14 @@ def _main(args: list[str] | None = None):
print(platform(aliased, terse))


def __getattr__(name):
if name == "__version__":
from warnings import _deprecated

_deprecated("__version__", remove=(3, 20))
return "1.1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


if __name__ == "__main__":
_main()
74 changes: 66 additions & 8 deletions Lib/test/test_codeccallbacks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from _codecs import _unregister_error as _codecs_unregister_error
import codecs
import html.entities
import itertools
Expand Down Expand Up @@ -754,8 +755,7 @@ def test_badandgoodsurrogatepassexceptions(self):
(s[:1], 1 + n)
)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_badhandlerresults(self):
results = ( 42, "foo", (1,2,3), ("foo", 1, 3), ("foo", None), ("foo",), ("foo", 1, 3), ("foo", None), ("foo",) )
encs = ("ascii", "latin-1", "iso-8859-1", "iso-8859-15")
Expand Down Expand Up @@ -798,8 +798,7 @@ def test_lookup(self):
codecs.lookup_error("namereplace")
)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_encode_nonascii_replacement(self):
def handle(exc):
if isinstance(exc, UnicodeEncodeError):
Expand Down Expand Up @@ -1041,8 +1040,7 @@ def __getitem__(self, key):
self.assertRaises(ValueError, codecs.charmap_encode, "\xff", err, D())
self.assertRaises(TypeError, codecs.charmap_encode, "\xff", err, {0xff: 300})

# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_decodehelper_bug36819(self):
handler = RepeatedPosReturn("x")
codecs.register_error("test.bug36819", handler.handle)
Expand Down Expand Up @@ -1130,7 +1128,7 @@ def test_bug828737(self):
text = 'abc<def>ghi'*n
text.translate(charmap)

def test_mutatingdecodehandler(self):
def test_mutating_decode_handler(self):
baddata = [
("ascii", b"\xff"),
("utf-7", b"++"),
Expand Down Expand Up @@ -1165,6 +1163,42 @@ def mutating(exc):
for (encoding, data) in baddata:
self.assertEqual(data.decode(encoding, "test.mutating"), "\u4242")

def test_mutating_decode_handler_unicode_escape(self):
decode = codecs.unicode_escape_decode
def mutating(exc):
if isinstance(exc, UnicodeDecodeError):
r = data.get(exc.object[:exc.end])
if r is not None:
exc.object = r[0] + exc.object[exc.end:]
return ('\u0404', r[1])
raise AssertionError("don't know how to handle %r" % exc)

codecs.register_error('test.mutating2', mutating)
data = {
br'\x0': (b'\\', 0),
br'\x3': (b'xxx\\', 3),
br'\x5': (b'x\\', 1),
}
def check(input, expected, msg):
with self.assertWarns(DeprecationWarning) as cm:
self.assertEqual(decode(input, 'test.mutating2'), (expected, len(input)))
self.assertIn(msg, str(cm.warning))

check(br'\x0n\z', '\u0404\n\\z', r'"\z" is an invalid escape sequence')
check(br'\x0n\501', '\u0404\n\u0141', r'"\501" is an invalid octal escape sequence')
check(br'\x0z', '\u0404\\z', r'"\z" is an invalid escape sequence')

check(br'\x3n\zr', '\u0404\n\\zr', r'"\z" is an invalid escape sequence')
check(br'\x3zr', '\u0404\\zr', r'"\z" is an invalid escape sequence')
check(br'\x3z5', '\u0404\\z5', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x3z5x')[:-1], '\u0404\\z5', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x3z5xy')[:-2], '\u0404\\z5', r'"\z" is an invalid escape sequence')

check(br'\x5n\z', '\u0404\n\\z', r'"\z" is an invalid escape sequence')
check(br'\x5n\501', '\u0404\n\u0141', r'"\501" is an invalid octal escape sequence')
check(br'\x5z', '\u0404\\z', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x5zy')[:-1], '\u0404\\z', r'"\z" is an invalid escape sequence')

# issue32583
def test_crashing_decode_handler(self):
# better generating one more character to fill the extra space slot
Expand Down Expand Up @@ -1216,7 +1250,6 @@ def replace_with_long(exc):
'\ufffd\x00\x00'
)


def test_fake_error_class(self):
handlers = [
codecs.strict_errors,
Expand All @@ -1241,6 +1274,31 @@ class FakeUnicodeError(Exception):
with self.assertRaises((TypeError, FakeUnicodeError)):
handler(FakeUnicodeError())

def test_reject_unregister_builtin_error_handler(self):
for name in [
'strict', 'ignore', 'replace', 'backslashreplace', 'namereplace',
'xmlcharrefreplace', 'surrogateescape', 'surrogatepass',
]:
with self.subTest(name):
self.assertRaises(ValueError, _codecs_unregister_error, name)

def test_unregister_custom_error_handler(self):
def custom_handler(exc):
raise exc

custom_name = 'test.test_unregister_custom_error_handler'
self.assertRaises(LookupError, codecs.lookup_error, custom_name)
codecs.register_error(custom_name, custom_handler)
self.assertIs(codecs.lookup_error(custom_name), custom_handler)
self.assertTrue(_codecs_unregister_error(custom_name))
self.assertRaises(LookupError, codecs.lookup_error, custom_name)

def test_unregister_custom_unknown_error_handler(self):
unknown_name = 'test.test_unregister_custom_unknown_error_handler'
self.assertRaises(LookupError, codecs.lookup_error, unknown_name)
self.assertFalse(_codecs_unregister_error(unknown_name))
self.assertRaises(LookupError, codecs.lookup_error, unknown_name)


if __name__ == "__main__":
unittest.main()
20 changes: 18 additions & 2 deletions Lib/test/test_exception_group.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import collections
import types
import unittest
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, exceeds_recursion_limit
from test.support import (skip_emscripten_stack_overflow,
skip_wasi_stack_overflow, skip_if_huge_c_stack,
exceeds_recursion_limit)

class TestExceptionGroupTypeHierarchy(unittest.TestCase):
def test_exception_group_types(self):
Expand Down Expand Up @@ -234,6 +236,18 @@ class MyEG(ExceptionGroup):
"ExceptionGroup('test', deque([ValueError(1), TypeError(2)]))"
)

def test_repr_small_size_args(self):
eg = ExceptionGroup("msg", [ValueError()])
eg.args = ()
# repr of the ExceptionGroup with empty args should not crash
self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))")

eg.args = (1,)
# repr of the ExceptionGroup with 1-size args should not crash
self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))")



def test_repr_raises(self):
class MySeq(collections.abc.Sequence):
def __init__(self, raises):
Expand All @@ -255,7 +269,7 @@ def __repr__(self):
seq = MySeq(None)
with self.assertRaisesRegex(
TypeError,
r"__repr__ returned non-string \(type NoneType\)"
r".*MySeq\.__repr__\(\) must return a str, not NoneType"
):
ExceptionGroup("test", seq)

Expand Down Expand Up @@ -535,13 +549,15 @@ def make_deep_eg(self):
e = ExceptionGroup('eg', [e])
return e

@skip_if_huge_c_stack()
@skip_emscripten_stack_overflow()
@skip_wasi_stack_overflow()
def test_deep_split(self):
e = self.make_deep_eg()
with self.assertRaises(RecursionError):
e.split(TypeError)

@skip_if_huge_c_stack()
@skip_emscripten_stack_overflow()
@skip_wasi_stack_overflow()
def test_deep_subgroup(self):
Expand Down
Loading
Loading