Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cafe4f4

Browse files
authored
Merge branch 'main' into gh-25949
2 parents 1f74101 + a956c2f commit cafe4f4

27 files changed

Lines changed: 735 additions & 72 deletions

Doc/howto/logging-cookbook.rst

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3712,6 +3712,71 @@ Of course, the examples above show output according to the format used by
37123712
:func:`~logging.basicConfig`, but you can use a different formatter when you
37133713
configure logging.
37143714

3715+
Note that with the above scheme, you are somewhat at the mercy of buffering and
3716+
the sequence of write calls which you are intercepting. For example, with the
3717+
definition of ``LoggerWriter`` above, if you have the snippet
3718+
3719+
.. code-block:: python
3720+
3721+
sys.stderr = LoggerWriter(logger, logging.WARNING)
3722+
1 / 0
3723+
3724+
then running the script results in
3725+
3726+
.. code-block:: text
3727+
3728+
WARNING:demo:Traceback (most recent call last):
3729+
3730+
WARNING:demo: File "/home/runner/cookbook-loggerwriter/test.py", line 53, in <module>
3731+
3732+
WARNING:demo:
3733+
WARNING:demo:main()
3734+
WARNING:demo: File "/home/runner/cookbook-loggerwriter/test.py", line 49, in main
3735+
3736+
WARNING:demo:
3737+
WARNING:demo:1 / 0
3738+
WARNING:demo:ZeroDivisionError
3739+
WARNING:demo::
3740+
WARNING:demo:division by zero
3741+
3742+
As you can see, this output isn't ideal. That's because the underlying code
3743+
which writes to ``sys.stderr`` makes mutiple writes, each of which results in a
3744+
separate logged line (for example, the last three lines above). To get around
3745+
this problem, you need to buffer things and only output log lines when newlines
3746+
are seen. Let's use a slghtly better implementation of ``LoggerWriter``:
3747+
3748+
.. code-block:: python
3749+
3750+
class BufferingLoggerWriter(LoggerWriter):
3751+
def __init__(self, logger, level):
3752+
super().__init__(logger, level)
3753+
self.buffer = ''
3754+
3755+
def write(self, message):
3756+
if '\n' not in message:
3757+
self.buffer += message
3758+
else:
3759+
parts = message.split('\n')
3760+
if self.buffer:
3761+
s = self.buffer + parts.pop(0)
3762+
self.logger.log(self.level, s)
3763+
self.buffer = parts.pop()
3764+
for part in parts:
3765+
self.logger.log(self.level, part)
3766+
3767+
This just buffers up stuff until a newline is seen, and then logs complete
3768+
lines. With this approach, you get better output:
3769+
3770+
.. code-block:: text
3771+
3772+
WARNING:demo:Traceback (most recent call last):
3773+
WARNING:demo: File "/home/runner/cookbook-loggerwriter/main.py", line 55, in <module>
3774+
WARNING:demo: main()
3775+
WARNING:demo: File "/home/runner/cookbook-loggerwriter/main.py", line 52, in main
3776+
WARNING:demo: 1/0
3777+
WARNING:demo:ZeroDivisionError: division by zero
3778+
3779+
37153780
.. patterns-to-avoid:
37163781
37173782
Patterns to avoid

Doc/library/os.rst

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,44 @@ process and user.
590590
See the documentation for :func:`getgroups` for cases where it may not
591591
return the same group list set by calling setgroups().
592592

593+
.. function:: setns(fd, nstype=0)
594+
595+
Reassociate the current thread with a Linux namespace.
596+
See the :manpage:`setns(2)` and :manpage:`namespaces(7)` man pages for more
597+
details.
598+
599+
If *fd* refers to a :file:`/proc/{pid}/ns/` link, ``setns()`` reassociates the
600+
calling thread with the namespace associated with that link,
601+
and *nstype* may be set to one of the
602+
:ref:`CLONE_NEW* constants <os-unshare-clone-flags>`
603+
to impose constraints on the operation
604+
(``0`` means no constraints).
605+
606+
Since Linux 5.8, *fd* may refer to a PID file descriptor obtained from
607+
:func:`~os.pidfd_open`. In this case, ``setns()`` reassociates the calling thread
608+
into one or more of the same namespaces as the thread referred to by *fd*.
609+
This is subject to any constraints imposed by *nstype*,
610+
which is a bit mask combining one or more of the
611+
:ref:`CLONE_NEW* constants <os-unshare-clone-flags>`,
612+
e.g. ``setns(fd, os.CLONE_NEWUTS | os.CLONE_NEWPID)``.
613+
The caller's memberships in unspecified namespaces are left unchanged.
614+
615+
*fd* can be any object with a :meth:`~io.IOBase.fileno` method, or a raw file descriptor.
616+
617+
This example reassociates the thread with the ``init`` process's network namespace::
618+
619+
fd = os.open("/proc/1/ns/net", os.O_RDONLY)
620+
os.setns(fd, os.CLONE_NEWNET)
621+
os.close(fd)
622+
623+
.. availability:: Linux >= 3.0 with glibc >= 2.14.
624+
625+
.. versionadded:: 3.12
626+
627+
.. seealso::
628+
629+
The :func:`~os.unshare` function.
630+
593631
.. function:: setpgrp()
594632

595633
Call the system call :c:func:`setpgrp` or ``setpgrp(0, 0)`` depending on
@@ -756,6 +794,49 @@ process and user.
756794
The function is now always available and is also available on Windows.
757795

758796

797+
.. function:: unshare(flags)
798+
799+
Disassociate parts of the process execution context, and move them into a
800+
newly created namespace.
801+
See the :manpage:`unshare(2)`
802+
man page for more details.
803+
The *flags* argument is a bit mask, combining zero or more of the
804+
:ref:`CLONE_* constants <os-unshare-clone-flags>`,
805+
that specifies which parts of the execution context should be
806+
unshared from their existing associations and moved to a new namespace.
807+
If the *flags* argument is ``0``, no changes are made to the calling process's
808+
execution context.
809+
810+
.. availability:: Linux >= 2.6.16.
811+
812+
.. versionadded:: 3.12
813+
814+
.. seealso::
815+
816+
The :func:`~os.setns` function.
817+
818+
.. _os-unshare-clone-flags:
819+
820+
Flags to the :func:`unshare` function, if the implementation supports them.
821+
See :manpage:`unshare(2)` in the Linux manual
822+
for their exact effect and availability.
823+
824+
.. data:: CLONE_FILES
825+
CLONE_FS
826+
CLONE_NEWCGROUP
827+
CLONE_NEWIPC
828+
CLONE_NEWNET
829+
CLONE_NEWNS
830+
CLONE_NEWPID
831+
CLONE_NEWTIME
832+
CLONE_NEWUSER
833+
CLONE_NEWUTS
834+
CLONE_SIGHAND
835+
CLONE_SYSVSEM
836+
CLONE_THREAD
837+
CLONE_VM
838+
839+
759840
.. _os-newstreams:
760841

761842
File Object Creation

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ struct _Py_global_strings {
489489
STRUCT_FOR_ID(node_depth)
490490
STRUCT_FOR_ID(node_offset)
491491
STRUCT_FOR_ID(ns)
492+
STRUCT_FOR_ID(nstype)
492493
STRUCT_FOR_ID(number)
493494
STRUCT_FOR_ID(obj)
494495
STRUCT_FOR_ID(object)

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.

Lib/pydoc.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1997,7 +1997,10 @@ def __repr__(self):
19971997
_GoInteractive = object()
19981998
def __call__(self, request=_GoInteractive):
19991999
if request is not self._GoInteractive:
2000-
self.help(request)
2000+
try:
2001+
self.help(request)
2002+
except ImportError as e:
2003+
self.output.write(f'{e}\n')
20012004
else:
20022005
self.intro()
20032006
self.interact()

Lib/test/test_imp.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,40 @@ def test_find_and_load_checked_pyc(self):
378378
mod = imp.load_module('mymod', file, path, description)
379379
self.assertEqual(mod.x, 42)
380380

381+
def test_issue98354(self):
382+
# _imp.create_builtin should raise TypeError
383+
# if 'name' attribute of 'spec' argument is not a 'str' instance
384+
385+
create_builtin = support.get_attribute(_imp, "create_builtin")
386+
387+
class FakeSpec:
388+
def __init__(self, name):
389+
self.name = self
390+
spec = FakeSpec("time")
391+
with self.assertRaises(TypeError):
392+
create_builtin(spec)
393+
394+
class FakeSpec2:
395+
name = [1, 2, 3, 4]
396+
spec = FakeSpec2()
397+
with self.assertRaises(TypeError):
398+
create_builtin(spec)
399+
400+
import builtins
401+
class UnicodeSubclass(str):
402+
pass
403+
class GoodSpec:
404+
name = UnicodeSubclass("builtins")
405+
spec = GoodSpec()
406+
bltin = create_builtin(spec)
407+
self.assertEqual(bltin, builtins)
408+
409+
class UnicodeSubclassFakeSpec(str):
410+
def __init__(self, name):
411+
self.name = self
412+
spec = UnicodeSubclassFakeSpec("builtins")
413+
bltin = create_builtin(spec)
414+
self.assertEqual(bltin, builtins)
381415

382416
class ReloadTests(unittest.TestCase):
383417

Lib/test/test_launcher.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,13 @@ def test_filter_to_company(self):
369369
self.assertEqual(company, data["env.company"])
370370
self.assertEqual("3.100", data["env.tag"])
371371

372+
def test_filter_to_company_with_default(self):
373+
company = "PythonTestSuite"
374+
data = self.run_py([f"-V:{company}/"], env=dict(PY_PYTHON="3.0"))
375+
self.assertEqual("X.Y.exe", data["LaunchCommand"])
376+
self.assertEqual(company, data["env.company"])
377+
self.assertEqual("3.100", data["env.tag"])
378+
372379
def test_filter_to_tag(self):
373380
company = "PythonTestSuite"
374381
data = self.run_py([f"-V:3.100"])

Lib/test/test_posix.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,53 @@ def test_utime(self):
21912191
os.utime("path", dir_fd=0)
21922192

21932193

2194+
class NamespacesTests(unittest.TestCase):
2195+
"""Tests for os.unshare() and os.setns()."""
2196+
2197+
@unittest.skipUnless(hasattr(os, 'unshare'), 'needs os.unshare()')
2198+
@unittest.skipUnless(hasattr(os, 'setns'), 'needs os.setns()')
2199+
@unittest.skipUnless(os.path.exists('/proc/self/ns/uts'), 'need /proc/self/ns/uts')
2200+
@support.requires_linux_version(3, 0, 0)
2201+
def test_unshare_setns(self):
2202+
code = """if 1:
2203+
import errno
2204+
import os
2205+
import sys
2206+
fd = os.open('/proc/self/ns/uts', os.O_RDONLY)
2207+
try:
2208+
original = os.readlink('/proc/self/ns/uts')
2209+
try:
2210+
os.unshare(os.CLONE_NEWUTS)
2211+
except OSError as e:
2212+
if e.errno == errno.ENOSPC:
2213+
# skip test if limit is exceeded
2214+
sys.exit()
2215+
raise
2216+
new = os.readlink('/proc/self/ns/uts')
2217+
if original == new:
2218+
raise Exception('os.unshare failed')
2219+
os.setns(fd, os.CLONE_NEWUTS)
2220+
restored = os.readlink('/proc/self/ns/uts')
2221+
if original != restored:
2222+
raise Exception('os.setns failed')
2223+
except PermissionError:
2224+
# The calling process did not have the required privileges
2225+
# for this operation
2226+
pass
2227+
except OSError as e:
2228+
# Skip the test on these errors:
2229+
# - ENOSYS: syscall not available
2230+
# - EINVAL: kernel was not configured with the CONFIG_UTS_NS option
2231+
# - ENOMEM: not enough memory
2232+
if e.errno not in (errno.ENOSYS, errno.EINVAL, errno.ENOMEM):
2233+
raise
2234+
finally:
2235+
os.close(fd)
2236+
"""
2237+
2238+
assert_python_ok("-c", code)
2239+
2240+
21942241
def tearDownModule():
21952242
support.reap_children()
21962243

Lib/test/test_sys_setprofile.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,12 +437,8 @@ def __del__(self):
437437
sys.setprofile(bar)
438438

439439
sys.setprofile(A())
440-
with support.catch_unraisable_exception() as cm:
441-
sys.setprofile(foo)
442-
self.assertEqual(cm.unraisable.object, A.__del__)
443-
self.assertIsInstance(cm.unraisable.exc_value, RuntimeError)
444-
445-
self.assertEqual(sys.getprofile(), foo)
440+
sys.setprofile(foo)
441+
self.assertEqual(sys.getprofile(), bar)
446442

447443

448444
def test_same_object(self):

Lib/test/test_sys_settrace.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,12 +2796,8 @@ def __del__(self):
27962796
sys.settrace(bar)
27972797

27982798
sys.settrace(A())
2799-
with support.catch_unraisable_exception() as cm:
2800-
sys.settrace(foo)
2801-
self.assertEqual(cm.unraisable.object, A.__del__)
2802-
self.assertIsInstance(cm.unraisable.exc_value, RuntimeError)
2803-
2804-
self.assertEqual(sys.gettrace(), foo)
2799+
sys.settrace(foo)
2800+
self.assertEqual(sys.gettrace(), bar)
28052801

28062802

28072803
def test_same_object(self):

0 commit comments

Comments
 (0)