Skip to content
Draft
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
12 changes: 12 additions & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,17 @@ typedef _Py_CODEUNIT *(*_PyJitEntryFuncPtr)(struct _PyExecutorObject *exec, _PyI

#define _PyInterpreterGuard_GUARDS_NOT_ALLOWED UINTPTR_MAX

typedef struct {
PyTypeObject *async_gen_hooks_type;
PyTypeObject *flags_type;
#if defined(MS_WINDOWS)
PyTypeObject *windows_version_type;
#endif
#ifdef __EMSCRIPTEN__
PyTypeObject *emscripten_info_type;
#endif
} _PySys_State;

/* PyInterpreterState holds the global state for one of the runtime's
interpreters. Typically the initial (main) interpreter is the only one.

Expand Down Expand Up @@ -899,6 +910,7 @@ struct _is {

// Dictionary of the sys module
PyObject *sysdict;
_PySys_State sys_state;

// Dictionary of the builtins module
PyObject *builtins;
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extern PyStatus _PySys_Create(
extern PyStatus _PySys_ReadPreinitWarnOptions(PyWideStringList *options);
extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config);
extern int _PySys_UpdateConfig(PyThreadState *tstate);
extern void _PySys_FiniTypes(PyInterpreterState *interp);
extern void _PySys_Fini(PyInterpreterState *interp);
extern int _PyBuiltins_AddExceptions(PyObject * bltinmod);
extern PyStatus _Py_HashRandomization_Init(const PyConfig *);

Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_structseq.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ extern "C" {
// Export for '_curses' shared extension
PyAPI_FUNC(PyTypeObject*) _PyStructSequence_NewType(
PyStructSequence_Desc *desc,
unsigned long tp_flags);
unsigned long tp_flags,
int deprecate_tuple_api);

extern int _PyStructSequence_InitBuiltinWithFlags(
PyInterpreterState *interp,
Expand Down
13 changes: 11 additions & 2 deletions Lib/collections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,10 @@ def __ror__(self, other):
except ImportError:
_tuplegetter = lambda index, doc: property(_itemgetter(index), doc=doc)

def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None):
def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None, deprecate_tuple_api=True):
"""Returns a new subclass of tuple with named fields.

>>> Point = namedtuple('Point', ['x', 'y'])
>>> Point = namedtuple('Point', ['x', 'y'], deprecate_tuple_api=False)
>>> Point.__doc__ # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22) # instantiate with positional args or keywords
Expand Down Expand Up @@ -512,6 +512,15 @@ def __getnewargs__(self):
doc = _sys.intern(f'Alias for field number {index}')
class_namespace[name] = _tuplegetter(index, doc)

if deprecate_tuple_api:
def __getitem__(self, key):
import warnings
warnings.warn('tuple API is deprecated, use named attributes',
DeprecationWarning, stacklevel=2)
return tuple.__getitem__(self, key)

class_namespace['__getitem__'] = __getitem__

result = type(typename, (tuple,), class_namespace)

# For pickling to work, the __module__ variable needs to be set to the frame
Expand Down
2 changes: 1 addition & 1 deletion Lib/difflib.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ def ratio(self):
1.0
"""

matches = sum(triple[-1] for triple in self.get_matching_blocks())
matches = sum(triple.size for triple in self.get_matching_blocks())
return _calculate_ratio(matches, len(self.a) + len(self.b))

def quick_ratio(self):
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
2 changes: 1 addition & 1 deletion Lib/http/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def request_host(request):
"""
url = request.get_full_url()
host = urllib.parse.urlparse(url)[1]
host = urllib.parse.urlparse(url).netloc
if host == "":
host = request.get_header("Host", "")

Expand Down
6 changes: 3 additions & 3 deletions Lib/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,8 +796,8 @@ def send_head(self):
if not parts.path.endswith(('/', '%2f', '%2F')):
# redirect browser - doing basically what apache does
self.send_response(HTTPStatus.MOVED_PERMANENTLY)
new_parts = (parts[0], parts[1], parts[2] + '/',
parts[3], parts[4])
new_parts = (parts.scheme, parts.netloc, parts.path + '/',
parts.query, parts.fragment)
new_url = urllib.parse.urlunsplit(new_parts)
self.send_header("Location", new_url)
self.send_header("Content-Length", "0")
Expand Down 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
4 changes: 2 additions & 2 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ def getmembers_static(object, predicate=None):
"""
return _getmembers(object, predicate, getattr_static)

Attribute = namedtuple('Attribute', 'name kind defining_class object')
Attribute = namedtuple('Attribute', 'name kind defining_class object', deprecate_tuple_api=False)

def classify_class_attrs(cls):
"""Return list of attribute-descriptor tuples.
Expand Down Expand Up @@ -1643,7 +1643,7 @@ def getlineno(frame):
"""Get the line number from a frame object, allowing for optimization."""
return frame.f_lineno

_FrameInfo = namedtuple('_FrameInfo', ('frame',) + Traceback._fields)
_FrameInfo = namedtuple('_FrameInfo', ('frame',) + Traceback._fields, deprecate_tuple_api=False)
class FrameInfo(_FrameInfo):
def __new__(cls, frame, filename, lineno, function, code_context, index, *, positions=None):
instance = super().__new__(cls, frame, filename, lineno, function, code_context, index)
Expand Down
2 changes: 1 addition & 1 deletion Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2022,7 +2022,7 @@ def output(self):
return self._output or sys.stdout

def __repr__(self):
if inspect.stack()[1][3] == '?':
if inspect.stack()[1].function == '?':
self()
return ''
return '<%s.%s instance>' % (self.__class__.__module__,
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
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
2 changes: 1 addition & 1 deletion Lib/test/ssl_servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def translate_path(self, path):

"""
# abandon query parameters
path = urllib.parse.urlparse(path)[2]
path = urllib.parse.urlparse(path).path
path = os.path.normpath(urllib.parse.unquote(path))
words = path.split('/')
words = filter(None, words)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ def open_urlresource(url, *args, **kw):

check = kw.pop('check', None)

filename = urllib.parse.urlparse(url)[2].split('/')[-1] # '/': it's URL!
filename = urllib.parse.urlparse(url).path.split('/')[-1] # '/': it's URL!

fn = os.path.join(TEST_DATA_DIR, filename)

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
6 changes: 3 additions & 3 deletions Lib/test/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def __ror__(self, other):
class TestNamedTuple(unittest.TestCase):

def test_factory(self):
Point = namedtuple('Point', 'x y')
Point = namedtuple('Point', 'x y', deprecate_tuple_api=False)
self.assertEqual(Point.__name__, 'Point')
self.assertEqual(Point.__slots__, ())
self.assertEqual(Point.__module__, __name__)
Expand Down Expand Up @@ -398,7 +398,7 @@ def test_defaults(self):
self.assertEqual(Point(), (10, 20))

def test_readonly(self):
Point = namedtuple('Point', 'x y')
Point = namedtuple('Point', 'x y', deprecate_tuple_api=False)
p = Point(11, 22)
with self.assertRaises(AttributeError):
p.x = 33
Expand Down Expand Up @@ -504,7 +504,7 @@ def test_instance(self):
self.assertEqual(repr(p), 'Point(x=11, y=22)')

def test_tupleness(self):
Point = namedtuple('Point', 'x y')
Point = namedtuple('Point', 'x y', deprecate_tuple_api=False)
p = Point(11, 22)

self.assertIsInstance(p, tuple)
Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import sys
import textwrap
import unittest
import warnings
from unittest.mock import Mock
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
from typing import get_type_hints
Expand Down Expand Up @@ -1779,7 +1780,9 @@ class C:

# Make sure that the returned dicts are actually OrderedDicts.
self.assertIs(type(d), OrderedDict)
self.assertIs(type(d['y'][1]), OrderedDict)
with warnings.catch_warnings(category=DeprecationWarning):
warnings.simplefilter("ignore", category=DeprecationWarning)
self.assertIs(type(d['y'][1]), OrderedDict)

def test_helper_asdict_namedtuple_key(self):
# Ensure that a field that contains a dict which has a
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
14 changes: 9 additions & 5 deletions Lib/test/test_grp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import string
import sys
import unittest
import warnings
from test.support import import_helper


Expand All @@ -14,16 +15,19 @@ class GroupDatabaseTestCase(unittest.TestCase):
def check_value(self, value):
# check that a grp tuple has the entries and
# attributes promised by the docs
self.assertEqual(len(value), 4)
self.assertEqual(value[0], value.gr_name)
self.assertIsInstance(value.gr_name, str)
self.assertEqual(value[1], value.gr_passwd)
self.assertIsInstance(value.gr_passwd, str)
self.assertEqual(value[2], value.gr_gid)
self.assertIsInstance(value.gr_gid, int)
self.assertEqual(value[3], value.gr_mem)
self.assertIsInstance(value.gr_mem, list)

with warnings.catch_warnings(category=DeprecationWarning):
warnings.simplefilter("ignore", category=DeprecationWarning)
self.assertEqual(len(value), 4)
self.assertEqual(value[0], value.gr_name)
self.assertEqual(value[1], value.gr_passwd)
self.assertEqual(value[2], value.gr_gid)
self.assertEqual(value[3], value.gr_mem)

def test_values(self):
entries = grp.getgrall()

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
Loading
Loading