Skip to content
19 changes: 7 additions & 12 deletions Doc/c-api/capsule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,25 +108,20 @@ Refer to :ref:`using-capsules` for more information on using these objects.

Import a pointer to a C object from a capsule attribute in a module. The
*name* parameter should specify the full name to the attribute, as in
``module.attribute``. The *name* stored in the capsule must match this
string exactly.

This function splits *name* on the ``.`` character, and imports the first
element. It then processes further elements using attribute lookups.
``package.module.attribute``.
Modules are imported if needed,
other components are looked up as attributes.
The *name* stored in the capsule must match this string exactly.

Return the capsule's internal *pointer* on success. On failure, set an
exception and return ``NULL``.

.. note::

If *name* points to an attribute of some submodule or subpackage, this
submodule or subpackage must be previously imported using other means
(for example, by using :c:func:`PyImport_ImportModule`) for the
attribute lookups to succeed.

.. versionchanged:: 3.3
*no_block* has no effect anymore.

.. versionchanged:: next
Submodules are now imported if needed.


.. c:function:: int PyCapsule_IsValid(PyObject *capsule, const char *name)

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ process and user.
process. For most purposes, it is more useful to use
:func:`getpass.getuser` since the latter checks the environment variables
:envvar:`LOGNAME` or :envvar:`USERNAME` to find out who the user is, and
falls back to ``pwd.getpwuid(os.getuid())[0]`` to get the login name of the
falls back to ``pwd.getpwuid(os.getuid()).pw_name`` to get the login name of the
current real user id.

.. availability:: Unix, Windows, not WASI.
Expand Down
2 changes: 1 addition & 1 deletion Lib/getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def getuser():

try:
import pwd
return pwd.getpwuid(os.getuid())[0]
return pwd.getpwuid(os.getuid()).pw_name
except (ImportError, KeyError) as e:
raise OSError('No username set in the environment') from e

Expand Down
4 changes: 2 additions & 2 deletions Lib/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ def send_head(self):

self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Content-Length", str(fs.st_size))
self.send_header("Last-Modified",
self.date_time_string(fs.st_mtime))
self._send_extra_response_headers()
Expand Down Expand Up @@ -1013,7 +1013,7 @@ def nobody_uid():
except ImportError:
return -1
try:
nobody = pwd.getpwnam('nobody')[2]
nobody = pwd.getpwnam('nobody').pw_uid
except KeyError:
nobody = 1 + max(x[2] for x in pwd.getpwall())
return nobody
Expand Down
11 changes: 6 additions & 5 deletions Lib/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,8 +934,9 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
Start up a socket server on the specified port, and listen for new
configurations.

These will be sent as a file suitable for processing by fileConfig().
Returns a Thread object on which you can call start() to start the server,
These will be sent as a file suitable for processing by dictConfig() or
fileConfig(). Returns a Thread object on which you can call start() to
start the server,
and which you can join() when appropriate. To stop the server, call
stopListening().

Expand All @@ -953,16 +954,16 @@ class ConfigStreamHandler(StreamRequestHandler):
"""
Handler for a logging configuration request.

It expects a completely new logging configuration and uses fileConfig
to install it.
It expects a completely new logging configuration and uses dictConfig
or fileConfig to install it.
"""
def handle(self):
"""
Handle a request.

Each request is expected to be a 4-byte length, packed using
struct.pack(">L", n), followed by the config file.
Uses fileConfig() to do the grunt work.
Uses dictConfig() or fileConfig() to do the grunt work.
"""
try:
conn = self.connection
Expand Down
2 changes: 1 addition & 1 deletion Lib/netrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def _can_security_check():
def _getpwuid(uid):
try:
import pwd
return pwd.getpwuid(uid)[0]
return pwd.getpwuid(uid).pw_name
except (ImportError, LookupError):
return f'uid {uid}'

Expand Down
4 changes: 2 additions & 2 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ def _get_gid(name):
except KeyError:
result = None
if result is not None:
return result[2]
return result.gr_gid
return None

def _get_uid(name):
Expand All @@ -1001,7 +1001,7 @@ def _get_uid(name):
except KeyError:
result = None
if result is not None:
return result[2]
return result.pw_uid
return None

def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
Expand Down
2 changes: 0 additions & 2 deletions Lib/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,8 +757,6 @@ def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE,
context.check_hostname = check_hostname
if cert_reqs is not None:
context.verify_mode = cert_reqs
if check_hostname:
context.check_hostname = True

if keyfile and not certfile:
raise ValueError("certfile must be specified")
Expand Down
8 changes: 4 additions & 4 deletions Lib/tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2282,14 +2282,14 @@ def gettarinfo(self, name=None, arcname=None, fileobj=None):
if pwd:
if tarinfo.uid not in self._unames:
try:
self._unames[tarinfo.uid] = pwd.getpwuid(tarinfo.uid)[0]
self._unames[tarinfo.uid] = pwd.getpwuid(tarinfo.uid).pw_name
except KeyError:
self._unames[tarinfo.uid] = ''
tarinfo.uname = self._unames[tarinfo.uid]
if grp:
if tarinfo.gid not in self._gnames:
try:
self._gnames[tarinfo.gid] = grp.getgrgid(tarinfo.gid)[0]
self._gnames[tarinfo.gid] = grp.getgrgid(tarinfo.gid).gr_name
except KeyError:
self._gnames[tarinfo.gid] = ''
tarinfo.gname = self._gnames[tarinfo.gid]
Expand Down Expand Up @@ -2837,12 +2837,12 @@ def chown(self, tarinfo, targetpath, numeric_owner):
if not numeric_owner:
try:
if grp and tarinfo.gname:
g = grp.getgrnam(tarinfo.gname)[2]
g = grp.getgrnam(tarinfo.gname).gr_gid
except KeyError:
pass
try:
if pwd and tarinfo.uname:
u = pwd.getpwnam(tarinfo.uname)[2]
u = pwd.getpwnam(tarinfo.uname).pw_uid
except KeyError:
pass
if g is None:
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3465,3 +3465,9 @@ def skip_on_low_desktop_heap_memory_subprocess(returncode):
if returncode == STATUS_DLL_INIT_FAILED:
raise unittest.SkipTest('gh-150436: DLL init failed, likely because '
'of low desktop heap memory')


def check_immutable_type(testcase, type):
regex = r'cannot set .* attribute of immutable type'
with testcase.assertRaisesRegex(TypeError, regex):
setattr(type, 'custom_attr', 123)
2 changes: 1 addition & 1 deletion Lib/test/support/smtpd.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ def parseargs():
except ImportError:
print('Cannot import module "pwd"; try running with -n option.', file=sys.stderr)
sys.exit(1)
nobody = pwd.getpwnam('nobody')[2]
nobody = pwd.getpwnam('nobody').pw_uid
try:
os.setuid(nobody)
except PermissionError:
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,8 @@ def run_cli_ok(self, *args):
return stdout.buffer.read()

def run_cmd_ok(self, *args):
return assert_python_ok('-m', 'calendar', *args)[1]
proc = assert_python_ok('-m', 'calendar', *args)
return proc.out

def assertCLIFails(self, *args):
with self.captured_stderr_with_buffer() as stderr:
Expand Down
61 changes: 27 additions & 34 deletions Lib/test/test_capi/test_capsule.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,49 +93,46 @@ def test_non_ascii_module_name(self):
self.check_import(f'{name}.capsule')

def test_submodule(self):
# Only the first component is imported; a submodule not imported
# by its package is not found.
self.assertRaises(AttributeError,
_testlimitedcapi.PyCapsule_Import, 'capsule_pkg.sub.capsule')
# It is found after explicit import.
importlib.import_module('capsule_pkg.sub')
# A submodule not imported by its package is imported if needed.
self.assertNotIn('capsule_pkg.sub', sys.modules)
self.check_import('capsule_pkg.sub.capsule')
self.assertIn('capsule_pkg.sub', sys.modules)
# It is also found if already imported.
self.check_import('capsule_pkg.sub.capsule')
# A submodule imported by its package is found.
self.check_import('capsule_autopkg.sub.capsule')

def test_invalid_name(self):
pycapsule_import = _testlimitedcapi.PyCapsule_Import
# Non-existing module.
self.assertRaisesRegex(ImportError,
'PyCapsule_Import could not import module "capsule_nonexistent"',
self.assertRaisesRegex(ModuleNotFoundError,
"No module named 'capsule_nonexistent'",
pycapsule_import, 'capsule_nonexistent.capsule')
# Non-UTF-8 module name.
self.assertRaisesRegex(ImportError,
'PyCapsule_Import could not import module',
pycapsule_import, b'\xff\xfe.capsule')
self.assertRaises(UnicodeDecodeError,
pycapsule_import, b'\xff\xfe.capsule')
# Empty module name.
self.assertRaisesRegex(ImportError,
'PyCapsule_Import could not import module ""',
pycapsule_import, '.capsule_mod.capsule')
self.assertRaisesRegex(ValueError, 'Empty module name',
pycapsule_import, '.capsule_mod.capsule')
# Empty name.
self.assertRaisesRegex(ImportError,
'PyCapsule_Import could not import module ""',
pycapsule_import, '')
self.assertRaisesRegex(AttributeError, 'is not valid',
pycapsule_import, '')
# Only a dot.
self.assertRaisesRegex(ImportError,
'PyCapsule_Import could not import module ""',
pycapsule_import, '.')
self.assertRaisesRegex(ValueError, 'Empty module name',
pycapsule_import, '.')
# Non-existing attribute.
self.assertRaises(AttributeError,
pycapsule_import, 'capsule_mod.nonexistent')
self.assertRaisesRegex(AttributeError, 'is not valid',
pycapsule_import, 'capsule_mod.nonexistent')
# Empty attribute name.
self.assertRaises(AttributeError, pycapsule_import, 'capsule_mod.')
self.assertRaisesRegex(AttributeError, 'is not valid',
pycapsule_import, 'capsule_mod.')
# Consecutive dots.
self.assertRaises(AttributeError,
pycapsule_import, 'capsule_mod..capsule')
self.assertRaisesRegex(ModuleNotFoundError,
"No module named 'capsule_mod.'",
pycapsule_import, 'capsule_mod..capsule')
# Attribute of an object which is not a module.
self.assertRaises(AttributeError,
pycapsule_import, 'capsule_mod.not_capsule.capsule')
self.assertRaisesRegex(AttributeError, 'is not valid',
pycapsule_import, 'capsule_mod.not_capsule.capsule')
# No attribute name.
self.assertRaisesRegex(AttributeError, 'is not valid',
pycapsule_import, 'capsule_mod')
Expand All @@ -162,13 +159,9 @@ def test_invalid_capsule(self):
pycapsule_import, 'capsule_mod.nullname')

def test_error_from_import(self):
# The exception raised during importing the module is replaced
# with generic ImportError.
with self.assertRaises(ImportError) as cm:
_testlimitedcapi.PyCapsule_Import('capsule_broken.capsule')
self.assertEqual(str(cm.exception),
'PyCapsule_Import could not import '
'module "capsule_broken"')
# The exception raised during importing the module is propagated.
self.assertRaises(ZeroDivisionError,
_testlimitedcapi.PyCapsule_Import, 'capsule_broken.capsule')

def test_error_from_attribute_lookup(self):
self.assertRaises(FloatingPointError,
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import unittest
import numbers
import locale
from test import support
from test.support import (is_resource_enabled,
requires_IEEE_754, requires_docstrings,
check_disallow_instantiation)
Expand Down Expand Up @@ -5806,8 +5807,7 @@ def test_c_immutable_types(self):
)
for tp in types:
with self.subTest(tp=tp):
with self.assertRaisesRegex(TypeError, "immutable"):
tp.foo = 1
support.check_immutable_type(self, tp)

def test_c_disallow_instantiation(self):
ContextManager = type(C.localcontext())
Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@ def test_username_falls_back_to_pwd(self, environ):
expected_name = 'some_name'
environ.get.return_value = None
if pwd:
class User:
pass
with mock.patch('os.getuid') as uid, \
mock.patch('pwd.getpwuid') as getpw:
uid.return_value = 42
getpw.return_value = [expected_name]
getpw.return_value = User()
getpw.return_value.pw_name = expected_name
self.assertEqual(expected_name,
getpass.getuser())
getpw.assert_called_once_with(42)
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,10 @@ def get_hash(self, repr_, seed=None):
env['PYTHONHASHSEED'] = str(seed)
else:
env.pop('PYTHONHASHSEED', None)
out = assert_python_ok(
proc = assert_python_ok(
'-c', self.get_hash_command(repr_),
**env)
stdout = out[1].strip()
stdout = proc.out.strip()
return int(stdout)

def test_randomized_hash(self):
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_itertools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1541,8 +1541,7 @@ def test_immutable_types(self):
)
for tp in dataset:
with self.subTest(tp=tp):
with self.assertRaisesRegex(TypeError, "immutable"):
tp.foobar = 1
support.check_immutable_type(self, tp)


class TestExamples(unittest.TestCase):
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_os/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -2459,8 +2459,8 @@ def get_urandom_subprocess(self, count):
'data = os.urandom(%s)' % count,
'sys.stdout.buffer.write(data)',
'sys.stdout.buffer.flush()'))
out = assert_python_ok('-c', code)
stdout = out[1]
proc = assert_python_ok('-c', code)
stdout = proc.out
self.assertEqual(len(stdout), count)
return stdout

Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_os/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1322,8 +1322,8 @@ def _create_and_do_getcwd(dirname, current_path_length = 0):
@unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()")
@unittest.skipUnless(hasattr(os, 'getuid'), "test needs os.getuid()")
def test_getgrouplist(self):
user = pwd.getpwuid(os.getuid())[0]
group = pwd.getpwuid(os.getuid())[3]
user = pwd.getpwuid(os.getuid()).pw_name
group = pwd.getpwuid(os.getuid()).pw_gid
self.assertIn(group, posix.getgrouplist(user, group))


Expand Down Expand Up @@ -1823,8 +1823,8 @@ def test_link_dir_fd(self):
self.skipTest('posix.link(): %s' % e)
self.addCleanup(posix.unlink, fulllinkname)
# should have same inodes
self.assertEqual(posix.stat(fullname)[1],
posix.stat(fulllinkname)[1])
self.assertEqual(posix.stat(fullname).st_ino,
posix.stat(fulllinkname).st_ino)

@unittest.skipUnless(os.mkdir in os.supports_dir_fd, "test needs dir_fd support in os.mkdir()")
def test_mkdir_dir_fd(self):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_pwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_values_extended(self):
# check whether the entry returned by getpwuid()
# for each uid is among those from getpwall() for this uid
for e in entries:
if not e[0] or e[0] == '+':
if not e.pw_name or e.pw_name == '+':
continue # skip NIS entries etc.
self.assertIn(pwd.getpwnam(e.pw_name), entriesbyname[e.pw_name])
self.assertIn(pwd.getpwuid(e.pw_uid), entriesbyuid[e.pw_uid])
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_script_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class TestScriptHelper(unittest.TestCase):

def test_assert_python_ok(self):
t = script_helper.assert_python_ok('-c', 'import sys; sys.exit(0)')
self.assertEqual(0, t[0], 'return code was not 0')
self.assertEqual(0, t.rc, 'return code was not 0')

def test_assert_python_failure(self):
# I didn't import the sys module so this child will fail.
Expand Down
Loading
Loading