Skip to content

Commit ecc3bff

Browse files
committed
2 parents d0eda9d + 54596ef commit ecc3bff

36 files changed

+1152
-291
lines changed

Doc/library/logging.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,35 @@ Formatter Objects
662662
:func:`traceback.print_stack`, but with the last newline removed) as a
663663
string. This default implementation just returns the input value.
664664

665+
.. class:: BufferingFormatter(linefmt=None)
666+
667+
A base formatter class suitable for subclassing when you want to format a
668+
number of records. You can pass a :class:`Formatter` instance which you want
669+
to use to format each line (that corresponds to a single record). If not
670+
specified, the default formatter (which just outputs the event message) is
671+
used as the line formatter.
672+
673+
.. method:: formatHeader(records)
674+
675+
Return a header for a list of *records*. The base implementation just
676+
returns the empty string. You will need to override this method if you
677+
want specific behaviour, e.g. to show the count of records, a title or a
678+
separator line.
679+
680+
.. method:: formatFooter(records)
681+
682+
Return a footer for a list of *records*. The base implementation just
683+
returns the empty string. You will need to override this method if you
684+
want specific behaviour, e.g. to show the count of records or a separator
685+
line.
686+
687+
.. method:: format(records)
688+
689+
Return formatted text for a list of *records*. The base implementation
690+
just returns the empty string if there are no records; otherwise, it
691+
returns the concatenation of the header, each record formatted with the
692+
line formatter, and the footer.
693+
665694
.. _filter:
666695

667696
Filter Objects

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ struct _Py_global_strings {
527527
STRUCT_FOR_ID(protocol)
528528
STRUCT_FOR_ID(ps1)
529529
STRUCT_FOR_ID(ps2)
530+
STRUCT_FOR_ID(query)
530531
STRUCT_FOR_ID(quotetabs)
531532
STRUCT_FOR_ID(r)
532533
STRUCT_FOR_ID(raw)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_typeobject.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ extern "C" {
1111

1212
/* runtime lifecycle */
1313

14-
extern PyStatus _PyTypes_InitState(PyInterpreterState *);
1514
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
1615
extern void _PyTypes_FiniTypes(PyInterpreterState *);
1716
extern void _PyTypes_Fini(PyInterpreterState *);
@@ -67,8 +66,6 @@ struct types_state {
6766
};
6867

6968

70-
extern PyStatus _PyTypes_InitSlotDefs(void);
71-
7269
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
7370
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
7471
extern void _PyStaticType_ClearWeakRefs(PyTypeObject *type);

Lib/ntpath.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,8 @@ def realpath(path, *, strict=False):
732732
return path
733733

734734

735-
# Win9x family and earlier have no Unicode filename support.
736-
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
737-
sys.getwindowsversion()[3] >= 2)
735+
# All supported version have Unicode filename support.
736+
supports_unicode_filenames = True
738737

739738
def relpath(path, start=None):
740739
"""Return a relative version of a path"""

Lib/platform.py

Lines changed: 105 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -309,34 +309,52 @@ def _syscmd_ver(system='', release='', version='',
309309
version = _norm_version(version)
310310
return system, release, version
311311

312-
_WIN32_CLIENT_RELEASES = {
313-
(5, 0): "2000",
314-
(5, 1): "XP",
315-
# Strictly, 5.2 client is XP 64-bit, but platform.py historically
316-
# has always called it 2003 Server
317-
(5, 2): "2003Server",
318-
(5, None): "post2003",
319-
320-
(6, 0): "Vista",
321-
(6, 1): "7",
322-
(6, 2): "8",
323-
(6, 3): "8.1",
324-
(6, None): "post8.1",
325-
326-
(10, 0): "10",
327-
(10, None): "post10",
328-
}
329-
330-
# Server release name lookup will default to client names if necessary
331-
_WIN32_SERVER_RELEASES = {
332-
(5, 2): "2003Server",
333-
334-
(6, 0): "2008Server",
335-
(6, 1): "2008ServerR2",
336-
(6, 2): "2012Server",
337-
(6, 3): "2012ServerR2",
338-
(6, None): "post2012ServerR2",
339-
}
312+
try:
313+
import _wmi
314+
except ImportError:
315+
def _wmi_query(*keys):
316+
raise OSError("not supported")
317+
else:
318+
def _wmi_query(table, *keys):
319+
table = {
320+
"OS": "Win32_OperatingSystem",
321+
"CPU": "Win32_Processor",
322+
}[table]
323+
data = _wmi.exec_query("SELECT {} FROM {}".format(
324+
",".join(keys),
325+
table,
326+
)).split("\0")
327+
split_data = (i.partition("=") for i in data)
328+
dict_data = {i[0]: i[2] for i in split_data}
329+
return (dict_data[k] for k in keys)
330+
331+
332+
_WIN32_CLIENT_RELEASES = [
333+
((10, 1, 0), "post11"),
334+
((10, 0, 22000), "11"),
335+
((6, 4, 0), "10"),
336+
((6, 3, 0), "8.1"),
337+
((6, 2, 0), "8"),
338+
((6, 1, 0), "7"),
339+
((6, 0, 0), "Vista"),
340+
((5, 2, 3790), "XP64"),
341+
((5, 2, 0), "XPMedia"),
342+
((5, 1, 0), "XP"),
343+
((5, 0, 0), "2000"),
344+
]
345+
346+
_WIN32_SERVER_RELEASES = [
347+
((10, 1, 0), "post2022Server"),
348+
((10, 0, 20348), "2022Server"),
349+
((10, 0, 17763), "2019Server"),
350+
((6, 4, 0), "2016Server"),
351+
((6, 3, 0), "2012ServerR2"),
352+
((6, 2, 0), "2012Server"),
353+
((6, 1, 0), "2008ServerR2"),
354+
((6, 0, 0), "2008Server"),
355+
((5, 2, 0), "2003Server"),
356+
((5, 0, 0), "2000Server"),
357+
]
340358

341359
def win32_is_iot():
342360
return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS')
@@ -359,22 +377,40 @@ def win32_edition():
359377

360378
return None
361379

362-
def win32_ver(release='', version='', csd='', ptype=''):
380+
def _win32_ver(version, csd, ptype):
381+
# Try using WMI first, as this is the canonical source of data
382+
try:
383+
(version, product_type, ptype, spmajor, spminor) = _wmi_query(
384+
'OS',
385+
'Version',
386+
'ProductType',
387+
'BuildType',
388+
'ServicePackMajorVersion',
389+
'ServicePackMinorVersion',
390+
)
391+
is_client = (int(product_type) == 1)
392+
if spminor and spminor != '0':
393+
csd = f'SP{spmajor}.{spminor}'
394+
else:
395+
csd = f'SP{spmajor}'
396+
return version, csd, ptype, is_client
397+
except OSError:
398+
pass
399+
400+
# Fall back to a combination of sys.getwindowsversion and "ver"
363401
try:
364402
from sys import getwindowsversion
365403
except ImportError:
366-
return release, version, csd, ptype
404+
return version, csd, ptype, True
367405

368406
winver = getwindowsversion()
407+
is_client = (getattr(winver, 'product_type', 1) == 1)
369408
try:
370-
major, minor, build = map(int, _syscmd_ver()[2].split('.'))
409+
version = _syscmd_ver()[2]
410+
major, minor, build = map(int, version.split('.'))
371411
except ValueError:
372412
major, minor, build = winver.platform_version or winver[:3]
373-
version = '{0}.{1}.{2}'.format(major, minor, build)
374-
375-
release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or
376-
_WIN32_CLIENT_RELEASES.get((major, None)) or
377-
release)
413+
version = '{0}.{1}.{2}'.format(major, minor, build)
378414

379415
# getwindowsversion() reflect the compatibility mode Python is
380416
# running under, and so the service pack value is only going to be
@@ -386,12 +422,6 @@ def win32_ver(release='', version='', csd='', ptype=''):
386422
if csd[:13] == 'Service Pack ':
387423
csd = 'SP' + csd[13:]
388424

389-
# VER_NT_SERVER = 3
390-
if getattr(winver, 'product_type', None) == 3:
391-
release = (_WIN32_SERVER_RELEASES.get((major, minor)) or
392-
_WIN32_SERVER_RELEASES.get((major, None)) or
393-
release)
394-
395425
try:
396426
try:
397427
import winreg
@@ -407,6 +437,18 @@ def win32_ver(release='', version='', csd='', ptype=''):
407437
except OSError:
408438
pass
409439

440+
return version, csd, ptype, is_client
441+
442+
def win32_ver(release='', version='', csd='', ptype=''):
443+
is_client = False
444+
445+
version, csd, ptype, is_client = _win32_ver(version, csd, ptype)
446+
447+
if version:
448+
intversion = tuple(map(int, version.split('.')))
449+
releases = _WIN32_CLIENT_RELEASES if is_client else _WIN32_SERVER_RELEASES
450+
release = next((r for v, r in releases if v <= intversion), release)
451+
410452
return release, version, csd, ptype
411453

412454

@@ -725,6 +767,21 @@ def _get_machine_win32():
725767
# http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
726768

727769
# WOW64 processes mask the native architecture
770+
try:
771+
[arch, *_] = _wmi_query('CPU', 'Architecture')
772+
except OSError:
773+
pass
774+
else:
775+
try:
776+
arch = ['x86', 'MIPS', 'Alpha', 'PowerPC', None,
777+
'ARM', 'ia64', None, None,
778+
'AMD64', None, None, 'ARM64',
779+
][int(arch)]
780+
except (ValueError, IndexError):
781+
pass
782+
else:
783+
if arch:
784+
return arch
728785
return (
729786
os.environ.get('PROCESSOR_ARCHITEW6432', '') or
730787
os.environ.get('PROCESSOR_ARCHITECTURE', '')
@@ -738,7 +795,12 @@ def get(cls):
738795
return func() or ''
739796

740797
def get_win32():
741-
return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
798+
try:
799+
manufacturer, caption = _wmi_query('CPU', 'Manufacturer', 'Caption')
800+
except OSError:
801+
return os.environ.get('PROCESSOR_IDENTIFIER', _get_machine_win32())
802+
else:
803+
return f'{caption}, {manufacturer}'
742804

743805
def get_OpenVMS():
744806
try:

Lib/test/audit-tests.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,17 @@ def hook(event, args):
419419
sys._getframe()
420420

421421

422+
def test_wmi_exec_query():
423+
import _wmi
424+
425+
def hook(event, args):
426+
if event.startswith("_wmi."):
427+
print(event, args[0])
428+
429+
sys.addaudithook(hook)
430+
_wmi.exec_query("SELECT * FROM Win32_OperatingSystem")
431+
432+
422433
if __name__ == "__main__":
423434
from test.support import suppress_msvcrt_asserts
424435

Lib/test/test_audit.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,5 +185,20 @@ def test_sys_getframe(self):
185185

186186
self.assertEqual(actual, expected)
187187

188+
189+
def test_wmi_exec_query(self):
190+
import_helper.import_module("_wmi")
191+
returncode, events, stderr = self.run_python("test_wmi_exec_query")
192+
if returncode:
193+
self.fail(stderr)
194+
195+
if support.verbose:
196+
print(*events, sep='\n')
197+
actual = [(ev[0], ev[2]) for ev in events]
198+
expected = [("_wmi.exec_query", "SELECT * FROM Win32_OperatingSystem")]
199+
200+
self.assertEqual(actual, expected)
201+
202+
188203
if __name__ == "__main__":
189204
unittest.main()

Lib/test/test_exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,11 @@ class A:
20992099
except AttributeError as exc:
21002100
self.assertEqual("bluch", exc.name)
21012101
self.assertEqual(obj, exc.obj)
2102+
try:
2103+
object.__getattribute__(obj, "bluch")
2104+
except AttributeError as exc:
2105+
self.assertEqual("bluch", exc.name)
2106+
self.assertEqual(obj, exc.obj)
21022107

21032108
def test_getattr_has_name_and_obj_for_method(self):
21042109
class A:

0 commit comments

Comments
 (0)