Skip to content

Commit

Permalink
Merge branch 'main' into isolate-io/clinic-textio
Browse files Browse the repository at this point in the history
  • Loading branch information
erlend-aasland committed May 11, 2023
2 parents 4ee0b46 + 7470321 commit 9c7caa6
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 102 deletions.
5 changes: 4 additions & 1 deletion Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,10 @@ Cursor objects
``INSERT``, ``UPDATE``, ``DELETE``, and ``REPLACE`` statements;
is ``-1`` for other statements,
including :abbr:`CTE (Common Table Expression)` queries.
It is only updated by the :meth:`execute` and :meth:`executemany` methods.
It is only updated by the :meth:`execute` and :meth:`executemany` methods,
after the statement has run to completion.
This means that any resulting rows must be fetched in order for
:attr:`!rowcount` to be updated.

.. attribute:: row_factory

Expand Down
2 changes: 2 additions & 0 deletions Include/cpython/genobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *,

#define PyAsyncGen_CheckExact(op) Py_IS_TYPE((op), &PyAsyncGen_Type)

#define PyAsyncGenASend_CheckExact(op) Py_IS_TYPE((op), &_PyAsyncGenASend_Type)


#undef _PyGenObject_HEAD

Expand Down
17 changes: 12 additions & 5 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1324,11 +1324,18 @@ def _asdict_inner(obj, dict_factory):
if type(obj) in _ATOMIC_TYPES:
return obj
elif _is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _asdict_inner(getattr(obj, f.name), dict_factory)
result.append((f.name, value))
return dict_factory(result)
# fast path for the common case
if dict_factory is dict:
return {
f.name: _asdict_inner(getattr(obj, f.name), dict)
for f in fields(obj)
}
else:
result = []
for f in fields(obj):
value = _asdict_inner(getattr(obj, f.name), dict_factory)
result.append((f.name, value))
return dict_factory(result)
elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
# obj is a namedtuple. Recurse into it, but the returned
# object is another namedtuple of the same type. This is
Expand Down
33 changes: 13 additions & 20 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,21 @@ def _select_from(self, parent_path, scandir):
# avoid exhausting file descriptors when globbing deep trees.
with scandir(parent_path) as scandir_it:
entries = list(scandir_it)
except OSError:
pass
else:
for entry in entries:
if self.dironly:
try:
# "entry.is_dir()" can raise PermissionError
# in some cases (see bpo-38894), which is not
# among the errors ignored by _ignore_error()
if not entry.is_dir():
continue
except OSError as e:
if not _ignore_error(e):
raise
except OSError:
continue
name = entry.name
if self.match(name):
path = parent_path._make_child_relpath(name)
for p in self.successor._select_from(path, scandir):
yield p
except PermissionError:
return


class _RecursiveWildcardSelector(_Selector):
Expand All @@ -175,28 +171,25 @@ def _iterate_directories(self, parent_path, scandir):
# avoid exhausting file descriptors when globbing deep trees.
with scandir(parent_path) as scandir_it:
entries = list(scandir_it)
except OSError:
pass
else:
for entry in entries:
entry_is_dir = False
try:
entry_is_dir = entry.is_dir(follow_symlinks=False)
except OSError as e:
if not _ignore_error(e):
raise
except OSError:
pass
if entry_is_dir:
path = parent_path._make_child_relpath(entry.name)
for p in self._iterate_directories(path, scandir):
yield p
except PermissionError:
return

def _select_from(self, parent_path, scandir):
try:
successor_select = self.successor._select_from
for starting_point in self._iterate_directories(parent_path, scandir):
for p in successor_select(starting_point, scandir):
yield p
except PermissionError:
return
successor_select = self.successor._select_from
for starting_point in self._iterate_directories(parent_path, scandir):
for p in successor_select(starting_point, scandir):
yield p


class _DoubleRecursiveWildcardSelector(_RecursiveWildcardSelector):
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ def test_inner_cell_shadows_outer(self):
outputs = {"y": [4, 4, 4, 4, 4], "i": 20}
self._check_in_scopes(code, outputs)

def test_inner_cell_shadows_outer_no_store(self):
code = """
def f(x):
return [lambda: x for x in range(x)], x
fns, x = f(2)
y = [fn() for fn in fns]
"""
outputs = {"y": [1, 1], "x": 2}
self._check_in_scopes(code, outputs)

def test_closure_can_jump_over_comp_scope(self):
code = """
items = [(lambda: y) for i in range(5)]
Expand Down
38 changes: 12 additions & 26 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1949,33 +1949,19 @@ def test_glob_permissions(self):
P = self.cls
base = P(BASE) / 'permissions'
base.mkdir()
self.addCleanup(os_helper.rmtree, base)

file1 = base / "file1"
file1.touch()
file2 = base / "file2"
file2.touch()

subdir = base / "subdir"

file3 = base / "file3"
file3.symlink_to(subdir / "other")

# Patching is needed to avoid relying on the filesystem
# to return the order of the files as the error will not
# happen if the symlink is the last item.
real_scandir = os.scandir
def my_scandir(path):
with real_scandir(path) as scandir_it:
entries = list(scandir_it)
entries.sort(key=lambda entry: entry.name)
return contextlib.nullcontext(entries)

with mock.patch("os.scandir", my_scandir):
self.assertEqual(len(set(base.glob("*"))), 3)
subdir.mkdir()
self.assertEqual(len(set(base.glob("*"))), 4)
subdir.chmod(000)
self.assertEqual(len(set(base.glob("*"))), 4)
for i in range(100):
link = base / f"link{i}"
if i % 2:
link.symlink_to(P(BASE, "dirE", "nonexistent"))
else:
link.symlink_to(P(BASE, "dirC"))

self.assertEqual(len(set(base.glob("*"))), 100)
self.assertEqual(len(set(base.glob("*/"))), 50)
self.assertEqual(len(set(base.glob("*/fileC"))), 50)
self.assertEqual(len(set(base.glob("*/file*"))), 50)

@os_helper.skip_unless_symlink
def test_glob_long_symlink(self):
Expand Down
2 changes: 2 additions & 0 deletions Lib/zipfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ def _sanitize_filename(filename):
# ZIP format specification.
if os.sep != "/" and os.sep in filename:
filename = filename.replace(os.sep, "/")
if os.altsep and os.altsep != "/" and os.altsep in filename:
filename = filename.replace(os.altsep, "/")
return filename


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
When creating zip files using :mod:`zipfile`, ``os.altsep``, if not ``None``,
will always be treated as a path separator even when it is not ``/``.
Patch by Carey Metcalfe.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed issue where :meth:`pathlib.Path.glob` returned incomplete results when
it encountered a :exc:`PermissionError`. This method now suppresses all
:exc:`OSError` exceptions, except those raised from calling
:meth:`~pathlib.Path.is_dir` on the top-level path.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve performance of :func:`dataclasses.asdict` for the common case where
*dict_factory* is ``dict``. Patch by David C Ellis.
3 changes: 0 additions & 3 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1406,9 +1406,6 @@ typedef struct _PyAsyncGenWrappedValue {
#define _PyAsyncGenWrappedValue_CheckExact(o) \
Py_IS_TYPE(o, &_PyAsyncGenWrappedValue_Type)

#define PyAsyncGenASend_CheckExact(o) \
Py_IS_TYPE(o, &_PyAsyncGenASend_Type)


static int
async_gen_traverse(PyAsyncGenObject *gen, visitproc visit, void *arg)
Expand Down
86 changes: 45 additions & 41 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,27 +436,28 @@ _PyCode_Quicken(PyCodeObject *code)
#define SPEC_FAIL_COMPARE_OP_FLOAT_LONG 21
#define SPEC_FAIL_COMPARE_OP_LONG_FLOAT 22

/* FOR_ITER */
#define SPEC_FAIL_FOR_ITER_GENERATOR 10
#define SPEC_FAIL_FOR_ITER_COROUTINE 11
#define SPEC_FAIL_FOR_ITER_ASYNC_GENERATOR 12
#define SPEC_FAIL_FOR_ITER_LIST 13
#define SPEC_FAIL_FOR_ITER_TUPLE 14
#define SPEC_FAIL_FOR_ITER_SET 15
#define SPEC_FAIL_FOR_ITER_STRING 16
#define SPEC_FAIL_FOR_ITER_BYTES 17
#define SPEC_FAIL_FOR_ITER_RANGE 18
#define SPEC_FAIL_FOR_ITER_ITERTOOLS 19
#define SPEC_FAIL_FOR_ITER_DICT_KEYS 20
#define SPEC_FAIL_FOR_ITER_DICT_ITEMS 21
#define SPEC_FAIL_FOR_ITER_DICT_VALUES 22
#define SPEC_FAIL_FOR_ITER_ENUMERATE 23
#define SPEC_FAIL_FOR_ITER_MAP 24
#define SPEC_FAIL_FOR_ITER_ZIP 25
#define SPEC_FAIL_FOR_ITER_SEQ_ITER 26
#define SPEC_FAIL_FOR_ITER_REVERSED_LIST 27
#define SPEC_FAIL_FOR_ITER_CALLABLE 28
#define SPEC_FAIL_FOR_ITER_ASCII_STRING 29
/* FOR_ITER and SEND */
#define SPEC_FAIL_ITER_GENERATOR 10
#define SPEC_FAIL_ITER_COROUTINE 11
#define SPEC_FAIL_ITER_ASYNC_GENERATOR 12
#define SPEC_FAIL_ITER_LIST 13
#define SPEC_FAIL_ITER_TUPLE 14
#define SPEC_FAIL_ITER_SET 15
#define SPEC_FAIL_ITER_STRING 16
#define SPEC_FAIL_ITER_BYTES 17
#define SPEC_FAIL_ITER_RANGE 18
#define SPEC_FAIL_ITER_ITERTOOLS 19
#define SPEC_FAIL_ITER_DICT_KEYS 20
#define SPEC_FAIL_ITER_DICT_ITEMS 21
#define SPEC_FAIL_ITER_DICT_VALUES 22
#define SPEC_FAIL_ITER_ENUMERATE 23
#define SPEC_FAIL_ITER_MAP 24
#define SPEC_FAIL_ITER_ZIP 25
#define SPEC_FAIL_ITER_SEQ_ITER 26
#define SPEC_FAIL_ITER_REVERSED_LIST 27
#define SPEC_FAIL_ITER_CALLABLE 28
#define SPEC_FAIL_ITER_ASCII_STRING 29
#define SPEC_FAIL_ITER_ASYNC_GENERATOR_SEND 30

// UNPACK_SEQUENCE

Expand Down Expand Up @@ -2122,66 +2123,69 @@ int
_PySpecialization_ClassifyIterator(PyObject *iter)
{
if (PyGen_CheckExact(iter)) {
return SPEC_FAIL_FOR_ITER_GENERATOR;
return SPEC_FAIL_ITER_GENERATOR;
}
if (PyCoro_CheckExact(iter)) {
return SPEC_FAIL_FOR_ITER_COROUTINE;
return SPEC_FAIL_ITER_COROUTINE;
}
if (PyAsyncGen_CheckExact(iter)) {
return SPEC_FAIL_FOR_ITER_ASYNC_GENERATOR;
return SPEC_FAIL_ITER_ASYNC_GENERATOR;
}
if (PyAsyncGenASend_CheckExact(iter)) {
return SPEC_FAIL_ITER_ASYNC_GENERATOR_SEND;
}
PyTypeObject *t = Py_TYPE(iter);
if (t == &PyListIter_Type) {
return SPEC_FAIL_FOR_ITER_LIST;
return SPEC_FAIL_ITER_LIST;
}
if (t == &PyTupleIter_Type) {
return SPEC_FAIL_FOR_ITER_TUPLE;
return SPEC_FAIL_ITER_TUPLE;
}
if (t == &PyDictIterKey_Type) {
return SPEC_FAIL_FOR_ITER_DICT_KEYS;
return SPEC_FAIL_ITER_DICT_KEYS;
}
if (t == &PyDictIterValue_Type) {
return SPEC_FAIL_FOR_ITER_DICT_VALUES;
return SPEC_FAIL_ITER_DICT_VALUES;
}
if (t == &PyDictIterItem_Type) {
return SPEC_FAIL_FOR_ITER_DICT_ITEMS;
return SPEC_FAIL_ITER_DICT_ITEMS;
}
if (t == &PySetIter_Type) {
return SPEC_FAIL_FOR_ITER_SET;
return SPEC_FAIL_ITER_SET;
}
if (t == &PyUnicodeIter_Type) {
return SPEC_FAIL_FOR_ITER_STRING;
return SPEC_FAIL_ITER_STRING;
}
if (t == &PyBytesIter_Type) {
return SPEC_FAIL_FOR_ITER_BYTES;
return SPEC_FAIL_ITER_BYTES;
}
if (t == &PyRangeIter_Type) {
return SPEC_FAIL_FOR_ITER_RANGE;
return SPEC_FAIL_ITER_RANGE;
}
if (t == &PyEnum_Type) {
return SPEC_FAIL_FOR_ITER_ENUMERATE;
return SPEC_FAIL_ITER_ENUMERATE;
}
if (t == &PyMap_Type) {
return SPEC_FAIL_FOR_ITER_MAP;
return SPEC_FAIL_ITER_MAP;
}
if (t == &PyZip_Type) {
return SPEC_FAIL_FOR_ITER_ZIP;
return SPEC_FAIL_ITER_ZIP;
}
if (t == &PySeqIter_Type) {
return SPEC_FAIL_FOR_ITER_SEQ_ITER;
return SPEC_FAIL_ITER_SEQ_ITER;
}
if (t == &PyListRevIter_Type) {
return SPEC_FAIL_FOR_ITER_REVERSED_LIST;
return SPEC_FAIL_ITER_REVERSED_LIST;
}
if (t == &_PyUnicodeASCIIIter_Type) {
return SPEC_FAIL_FOR_ITER_ASCII_STRING;
return SPEC_FAIL_ITER_ASCII_STRING;
}
const char *name = t->tp_name;
if (strncmp(name, "itertools", 9) == 0) {
return SPEC_FAIL_FOR_ITER_ITERTOOLS;
return SPEC_FAIL_ITER_ITERTOOLS;
}
if (strncmp(name, "callable_iterator", 17) == 0) {
return SPEC_FAIL_FOR_ITER_CALLABLE;
return SPEC_FAIL_ITER_CALLABLE;
}
return SPEC_FAIL_OTHER;
}
Expand Down
19 changes: 13 additions & 6 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,12 +607,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
SET_SCOPE(scopes, k, scope);
}
else {
// free vars in comprehension that are locals in outer scope can
// now simply be locals, unless they are free in comp children
if ((PyLong_AsLong(existing) & DEF_BOUND) &&
!is_free_in_any_child(comp, k)) {
if (PySet_Discard(comp_free, k) < 0) {
return 0;
if (PyLong_AsLong(existing) & DEF_BOUND) {
// cell vars in comprehension that are locals in outer scope
// must be promoted to cell so u_cellvars isn't wrong
if (scope == CELL && ste->ste_type == FunctionBlock) {
SET_SCOPE(scopes, k, scope);
}

// free vars in comprehension that are locals in outer scope can
// now simply be locals, unless they are free in comp children
if (!is_free_in_any_child(comp, k)) {
if (PySet_Discard(comp_free, k) < 0) {
return 0;
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions Tools/scripts/summarize_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ def kind_to_text(kind, defines, opname):
return pretty(defines[kind][0])
if opname.endswith("ATTR"):
opname = "ATTR"
if opname in ("FOR_ITER", "SEND"):
opname = "ITER"
if opname.endswith("SUBSCR"):
opname = "SUBSCR"
for name in defines[kind]:
Expand Down

0 comments on commit 9c7caa6

Please sign in to comment.