diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 03a848d68ed7aba..e3ffe494c0b6ef9 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -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) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 0a4a02c45b533bd..fceadde7df6bf48 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -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. diff --git a/Lib/getpass.py b/Lib/getpass.py index cfbd63dded6cc19..b9eec4c57abc97c 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -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 diff --git a/Lib/http/server.py b/Lib/http/server.py index 095b5744bd12fc6..2fdfc78725ea4b7 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -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() @@ -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 diff --git a/Lib/logging/config.py b/Lib/logging/config.py index fab91e663a0f6aa..f566de5750dbf55 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -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(). @@ -953,8 +954,8 @@ 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): """ @@ -962,7 +963,7 @@ def handle(self): 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 diff --git a/Lib/netrc.py b/Lib/netrc.py index a28ea297df894b6..e9b5538d2c4399d 100644 --- a/Lib/netrc.py +++ b/Lib/netrc.py @@ -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}' diff --git a/Lib/shutil.py b/Lib/shutil.py index 94617ec296f5087..ce6969d6a4bf5a9 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -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): @@ -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, diff --git a/Lib/ssl.py b/Lib/ssl.py index 3c0361330d7e951..db66c59c05cc542 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -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") diff --git a/Lib/tarfile.py b/Lib/tarfile.py index d12bd15aa2d2319..dc5c3a59744cbc4 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -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] @@ -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: diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 897c210f4767e1d..c1460b806806f81 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -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) diff --git a/Lib/test/support/smtpd.py b/Lib/test/support/smtpd.py index 6537679db9ad24f..9800332a27f86cf 100755 --- a/Lib/test/support/smtpd.py +++ b/Lib/test/support/smtpd.py @@ -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: diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 8646cfcad58cea8..15cce2b30da5768 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -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: diff --git a/Lib/test/test_capi/test_capsule.py b/Lib/test/test_capi/test_capsule.py index 981caf3fad426bd..871b7b88571c349 100644 --- a/Lib/test/test_capi/test_capsule.py +++ b/Lib/test/test_capi/test_capsule.py @@ -93,12 +93,11 @@ 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') @@ -106,36 +105,34 @@ def test_submodule(self): 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') @@ -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, diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index b8c09c7f43e3e3b..a0ba5a8351aebdf 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -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) @@ -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()) diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 272414a62048561..23f8a328506c6ee 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -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) diff --git a/Lib/test/test_hash.py b/Lib/test/test_hash.py index cf9db66a29ae110..63b745f7a9f52fb 100644 --- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -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): diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index cf579d4da4e0dfb..d47f9acf019dca6 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -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): diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 328a0dbeb99f8fa..bcf83a314f1a6eb 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -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 diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index f3d67027ad37277..814f945aac7453c 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -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)) @@ -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): diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index bdf57776c82be13..82acce85f1db572 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -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]) diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py index eeea6c4842b4881..e65b3efdcd0a701 100644 --- a/Lib/test/test_script_helper.py +++ b/Lib/test/test_script_helper.py @@ -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. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index ed5d15ecc7ddad6..d6b3b6a642bee1e 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1999,8 +1999,8 @@ def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support if UID_GID_SUPPORT: - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] + group = grp.getgrgid(0).gr_name + owner = pwd.getpwuid(0).pw_name else: group = owner = 'root' @@ -2027,8 +2027,8 @@ def test_make_archive_owner_group(self): def test_tarfile_root_owner(self): root_dir, base_dir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] + group = grp.getgrgid(0).gr_name + owner = pwd.getpwuid(0).pw_name with os_helper.change_cwd(root_dir), no_chdir: archive_name = make_archive(base_name, 'gztar', root_dir, 'dist', owner=owner, group=group) @@ -2433,8 +2433,8 @@ def check_chown(path, uid=None, gid=None): check_chown(dirname, gid=gid) try: - user = pwd.getpwuid(uid)[0] - group = grp.getgrgid(gid)[0] + user = pwd.getpwuid(uid).pw_name + group = grp.getgrgid(gid).gr_name except KeyError: # On some systems uid/gid cannot be resolved. pass diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 4cea07b3d2c7745..fc94b9a972828c1 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2051,8 +2051,9 @@ def test_encoding_warning(self): run("echo hello", shell=True, text=True) check_output("echo hello", shell=True, text=True) """) + env = support.make_clean_env() cp = subprocess.run([sys.executable, "-Xwarn_default_encoding", "-c", code], - capture_output=True) + capture_output=True, env=env) lines = cp.stderr.splitlines() self.assertEqual(len(lines), 2, lines) self.assertStartsWith(lines[0], b":2: EncodingWarning: ") diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index dab03ef06a8b8e5..f2adce532595e70 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -425,6 +425,7 @@ def test_getwindowsversion(self): self.assertEqual(v[2], v.build) self.assertEqual(v[3], v.platform) self.assertEqual(v[4], v.service_pack) + support.check_immutable_type(self, type(v)) # This is how platform.py calls it. Make sure tuple # still has 5 elements @@ -690,6 +691,7 @@ def test_attributes(self): self.assertEqual(algo, 0) self.assertGreaterEqual(sys.hash_info.cutoff, 0) self.assertLess(sys.hash_info.cutoff, 8) + support.check_immutable_type(self, type(sys.hash_info)) self.assertIsInstance(sys.maxsize, int) self.assertIsInstance(sys.maxunicode, int) @@ -893,11 +895,13 @@ def assert_raise_on_new_sys_type(self, sys_attr): # sys.flags, sys.version_info, and sys.getwindowsversion. support.check_disallow_instantiation(self, type(sys_attr), sys_attr) - def test_sys_flags_no_instantiation(self): + def test_sys_flags_type(self): self.assert_raise_on_new_sys_type(sys.flags) + support.check_immutable_type(self, type(sys.flags)) - def test_sys_version_info_no_instantiation(self): + def test_sys_version_info_type(self): self.assert_raise_on_new_sys_type(sys.version_info) + support.check_immutable_type(self, type(sys.version_info)) def test_sys_getwindowsversion_no_instantiation(self): # Skip if not being run on Windows. @@ -1954,6 +1958,7 @@ def test_asyncgen_hooks(self): cur = sys.get_asyncgen_hooks() self.assertIsNone(cur.firstiter) self.assertIsNone(cur.finalizer) + support.check_immutable_type(self, type(cur)) # gh-118473 with self.assertRaises(TypeError): @@ -1997,6 +2002,7 @@ def write(self, s): self.assertEqual(out, b"") self.assertEqual(err, b"") + @test.support.support_remote_exec_only @test.support.cpython_only class TestRemoteExec(unittest.TestCase): diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index c86bcb79eb85d89..5fa97e2ac226c43 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -3351,12 +3351,12 @@ def root_is_uid_gid_0(): except ImportError: return False try: - if pwd.getpwuid(0)[0] != 'root': + if pwd.getpwuid(0).pw_name != 'root': return False except KeyError: # On Cygwin, there is no root user (uid 0) return False - if grp.getgrgid(0)[0] != 'root': + if grp.getgrgid(0).gr_name != 'root': return False return True diff --git a/Lib/test/test_utf8_mode.py b/Lib/test/test_utf8_mode.py index b8e49440c9f7da6..6cd156b7e9293ac 100644 --- a/Lib/test/test_utf8_mode.py +++ b/Lib/test/test_utf8_mode.py @@ -29,11 +29,11 @@ def posix_locale(self): def get_output(self, *args, failure=False, **kw): kw = dict(self.DEFAULT_ENV, **kw) if failure: - out = assert_python_failure(*args, **kw) - out = out[2] + proc = assert_python_failure(*args, **kw) + out = proc.err else: - out = assert_python_ok(*args, **kw) - out = out[1] + proc = assert_python_ok(*args, **kw) + out = proc.out return out.decode().rstrip("\n\r") @unittest.skipIf(MS_WINDOWS, 'Windows has no POSIX locale') diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index 270b9d6da8e7b9e..2a18396afb60881 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -194,8 +194,7 @@ def test_immutable_types(self): ) for tp in dataset: with self.subTest(tp=tp): - with self.assertRaisesRegex(TypeError, "immutable"): - tp.foo = 1 + support.check_immutable_type(self, tp) @support.cpython_only def test_disallow_instantiation(self): diff --git a/Misc/NEWS.d/next/C_API/2018-05-16-13-47-18.bpo-32414.NODPbj.rst b/Misc/NEWS.d/next/C_API/2018-05-16-13-47-18.bpo-32414.NODPbj.rst new file mode 100644 index 000000000000000..17f7a096ca8a384 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2018-05-16-13-47-18.bpo-32414.NODPbj.rst @@ -0,0 +1,3 @@ +:c:func:`PyCapsule_Import` now imports submodules if needed. Previously +names like ``package.module.attribute`` worked only if ``package.module`` +was already imported. diff --git a/Objects/capsule.c b/Objects/capsule.c index 16ae65905ef5ac0..bd90cb25f78862e 100644 --- a/Objects/capsule.c +++ b/Objects/capsule.c @@ -4,6 +4,7 @@ #include "pycore_capsule.h" // export _PyCapsule_SetTraverse() #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_pymem.h" // _PyMem_Strdup() /* Internal structure of PyCapsule */ @@ -227,58 +228,58 @@ _PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_f void * -PyCapsule_Import(const char *name, int no_block) +PyCapsule_Import(const char *name, int Py_UNUSED(no_block)) { PyObject *object = NULL; void *return_value = NULL; - char *trace; - size_t name_length = (strlen(name) + 1) * sizeof(char); - char *name_dup = (char *)PyMem_Malloc(name_length); + char *name_dup = _PyMem_Strdup(name); if (!name_dup) { return PyErr_NoMemory(); } - memcpy(name_dup, name, name_length); - - trace = name_dup; - while (trace) { + char *trace = name_dup; + while (1) { char *dot = strchr(trace, '.'); if (dot) { - *dot++ = '\0'; + *dot = '\0'; } - - if (object == NULL) { - object = PyImport_ImportModule(trace); - if (!object) { - PyErr_Format(PyExc_ImportError, "PyCapsule_Import could not import module \"%s\"", trace); + if (object) { + PyObject *attr; + if (PyObject_GetOptionalAttrString(object, trace, &attr) < 0) { + Py_CLEAR(object); + break; } - } else { - PyObject *object2 = PyObject_GetAttrString(object, trace); - Py_SETREF(object, object2); + Py_SETREF(object, attr); } - if (!object) { - goto EXIT; + if (!dot) { + // We are done + break; } - trace = dot; + if (!object) { + object = PyImport_ImportModule(name_dup); + if (!object) { + break; + } + } + *dot = '.'; + trace = dot + 1; } /* compare attribute name to module.name by hand */ if (PyCapsule_IsValid(object, name)) { PyCapsule *capsule = (PyCapsule *)object; return_value = capsule->pointer; - } else { + } + else if (!PyErr_Occurred()) { PyErr_Format(PyExc_AttributeError, "PyCapsule_Import \"%s\" is not valid", name); } -EXIT: Py_XDECREF(object); - if (name_dup) { - PyMem_Free(name_dup); - } + PyMem_Free(name_dup); return return_value; } diff --git a/Tools/c-analyzer/c_common/fsutil.py b/Tools/c-analyzer/c_common/fsutil.py index a8cf8d0537e40db..eb9b74d552ece00 100644 --- a/Tools/c-analyzer/c_common/fsutil.py +++ b/Tools/c-analyzer/c_common/fsutil.py @@ -411,7 +411,7 @@ def _get_user_info(user): if user is None: uid = os.geteuid() #username = os.getlogin() - username = pwd.getpwuid(uid)[0] + username = pwd.getpwuid(uid).pw_name gid = os.getgid() groups = os.getgroups() else: diff --git a/Tools/c-analyzer/distutils/dep_util.py b/Tools/c-analyzer/distutils/dep_util.py index 318c830f2eab3e3..a97f925d9ee4187 100644 --- a/Tools/c-analyzer/distutils/dep_util.py +++ b/Tools/c-analyzer/distutils/dep_util.py @@ -20,9 +20,8 @@ def newer (source, target): if not os.path.exists(target): return 1 - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] + mtime1 = os.stat(source).st_mtime + mtime2 = os.stat(target).st_mtime return mtime1 > mtime2