Skip to content

Commit

Permalink
Merge branch 'main' into nestedcomps
Browse files Browse the repository at this point in the history
* main:
  pythongh-74690: Don't set special protocol attributes on non-protocol subclasses of protocols (python#104622)
  pythongh-104623: Update Windows installer to use SQLite 3.42.0 (python#104625)
  pythongh-104050: Add more type annotations to Argument Clinic (python#104628)
  pythongh-104629: Don't skip test_clinic if _testclinic is missing (python#104630)
  pythongh-104549: Set __module__ on TypeAliasType (python#104550)
  pythongh-104050: Improve some typing around `default`s and sentinel values (python#104626)
  pythongh-104146: Remove unused vars from Argument Clinic (python#104627)
  pythongh-104615: don't make unsafe swaps in apply_static_swaps (python#104620)
  pythonGH-104484: Add case_sensitive argument to `pathlib.PurePath.match()` (pythonGH-104565)
  pythonGH-96803: Document and test new unstable internal frame API functions (pythonGH-104211)
  pythonGH-104580: Don't cache eval breaker in interpreter (pythonGH-104581)
  • Loading branch information
carljm committed May 18, 2023
2 parents 96f17a8 + f7835fc commit 54095f0
Show file tree
Hide file tree
Showing 46 changed files with 669 additions and 372 deletions.
35 changes: 35 additions & 0 deletions Doc/c-api/frame.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,38 @@ See also :ref:`Reflection <reflection>`.
.. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)
Return the line number that *frame* is currently executing.
Internal Frames
---------------
Unless using :pep:`523`, you will not need this.
.. c:struct:: _PyInterpreterFrame
The interpreter's internal frame representation.
.. versionadded:: 3.11
.. c:function:: PyObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
Return a :term:`strong reference` to the code object for the frame.
.. versionadded:: 3.12
.. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame);
Return the byte offset into the last executed instruction.
.. versionadded:: 3.12
.. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame);
Return the currently executing line number, or -1 if there is no line number.
.. versionadded:: 3.12
7 changes: 6 additions & 1 deletion Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ Pure paths provide the following methods and properties:
PureWindowsPath('c:/Program Files')


.. method:: PurePath.match(pattern)
.. method:: PurePath.match(pattern, *, case_sensitive=None)

Match this path against the provided glob-style pattern. Return ``True``
if matching is successful, ``False`` otherwise.
Expand Down Expand Up @@ -576,6 +576,11 @@ Pure paths provide the following methods and properties:
>>> PureWindowsPath('b.py').match('*.PY')
True

Set *case_sensitive* to ``True`` or ``False`` to override this behaviour.

.. versionadded:: 3.12
The *case_sensitive* argument.


.. method:: PurePath.relative_to(other, walk_up=False)

Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,9 @@ pathlib
* Add :meth:`pathlib.Path.is_junction` as a proxy to :func:`os.path.isjunction`.
(Contributed by Charles Machalow in :gh:`99547`.)

* Add *case_sensitive* optional parameter to :meth:`pathlib.Path.glob`,
:meth:`pathlib.Path.rglob` and :meth:`pathlib.PurePath.match` for matching
the path's case sensitivity, allowing for more precise control over the matching process.

dis
---
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);

/* Returns the code object of the frame (strong reference).
* Does not raise an exception. */
PyAPI_FUNC(PyCodeObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);

/* Returns a byte ofsset into the last executed instruction.
* Does not raise an exception. */
Expand Down
6 changes: 3 additions & 3 deletions Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ struct _pending_calls {
};

struct _ceval_state {
int recursion_limit;
struct _gil_runtime_state *gil;
int own_gil;
/* This single variable consolidates all requests to break out of
the fast path in the eval loop. */
_Py_atomic_int eval_breaker;
/* Request for dropping the GIL */
_Py_atomic_int gil_drop_request;
int recursion_limit;
struct _gil_runtime_state *gil;
int own_gil;
/* The GC is ready to be executed */
_Py_atomic_int gc_scheduled;
struct _pending_calls pending;
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ PyAPI_FUNC(PyObject*) _PyCompile_CodeGen(

PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg(
PyObject *instructions,
PyObject *consts);
PyObject *consts,
int nlocals);

PyAPI_FUNC(PyCodeObject*)
_PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
Expand Down
1 change: 0 additions & 1 deletion Include/internal/pycore_global_objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ struct _Py_interp_cached_objects {
PyTypeObject *paramspec_type;
PyTypeObject *paramspecargs_type;
PyTypeObject *paramspeckwargs_type;
PyTypeObject *typealias_type;
};

#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(newline)
STRUCT_FOR_ID(newlines)
STRUCT_FOR_ID(next)
STRUCT_FOR_ID(nlocals)
STRUCT_FOR_ID(node_depth)
STRUCT_FOR_ID(node_offset)
STRUCT_FOR_ID(ns)
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct _Py_long_state {
*/
struct _is {

struct _ceval_state ceval;
PyInterpreterState *next;

uint64_t monitoring_version;
Expand Down Expand Up @@ -92,7 +93,6 @@ struct _is {

struct _obmalloc_state obmalloc;

struct _ceval_state ceval;
struct _gc_runtime_state gc;

struct _import_state imports;
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_typevarobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *);
extern int _Py_initialize_generic(PyInterpreterState *);
extern void _Py_clear_generic_types(PyInterpreterState *);

extern PyTypeObject _PyTypeAlias_Type;

#ifdef __cplusplus
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 14 additions & 6 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ def _make_selector(pattern_parts, flavour, case_sensitive):
return cls(pat, child_parts, flavour, case_sensitive)


@functools.lru_cache(maxsize=256)
def _compile_pattern(pat, case_sensitive):
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
return re.compile(fnmatch.translate(pat), flags).match


class _Selector:
"""A selector matches a specific glob pattern part against the children
of a given path."""
Expand Down Expand Up @@ -133,8 +139,7 @@ def __init__(self, pat, child_parts, flavour, case_sensitive):
if case_sensitive is None:
# TODO: evaluate case-sensitivity of each directory in _select_from()
case_sensitive = _is_case_sensitive(flavour)
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
self.match = re.compile(fnmatch.translate(pat), flags=flags).fullmatch
self.match = _compile_pattern(pat, case_sensitive)

def _select_from(self, parent_path, scandir):
try:
Expand Down Expand Up @@ -680,22 +685,25 @@ def is_reserved(self):
name = self._tail[-1].partition('.')[0].partition(':')[0].rstrip(' ')
return name.upper() in _WIN_RESERVED_NAMES

def match(self, path_pattern):
def match(self, path_pattern, *, case_sensitive=None):
"""
Return True if this path matches the given pattern.
"""
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self._flavour)
pat = self.with_segments(path_pattern)
if not pat.parts:
raise ValueError("empty pattern")
pat_parts = pat._parts_normcase
parts = self._parts_normcase
pat_parts = pat.parts
parts = self.parts
if pat.drive or pat.root:
if len(pat_parts) != len(parts):
return False
elif len(pat_parts) > len(parts):
return False
for part, pat in zip(reversed(parts), reversed(pat_parts)):
if not fnmatch.fnmatchcase(part, pat):
match = _compile_pattern(pat, case_sensitive)
if not match(part):
return False
return True

Expand Down
5 changes: 4 additions & 1 deletion Lib/test/mod_generics_cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module for testing the behavior of generics across different modules."""

from typing import TypeVar, Generic, Optional
from typing import TypeVar, Generic, Optional, TypeAliasType

default_a: Optional['A'] = None
default_b: Optional['B'] = None
Expand All @@ -19,3 +19,6 @@ class A(Generic[T]):
my_inner_a1: 'B.A'
my_inner_a2: A
my_outer_a: 'A' # unless somebody calls get_type_hints with localns=B.__dict__

type Alias = int
OldStyle = TypeAliasType("OldStyle", int)
4 changes: 2 additions & 2 deletions Lib/test/support/bytecode_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ def generate_code(self, ast):

class CfgOptimizationTestCase(CompilationStepTestCase):

def get_optimized(self, insts, consts):
def get_optimized(self, insts, consts, nlocals=0):
insts = self.normalize_insts(insts)
insts = self.complete_insts_info(insts)
insts = optimize_cfg(insts, consts)
insts = optimize_cfg(insts, consts, nlocals)
return insts, consts

class AssemblerTestCase(CompilationStepTestCase):
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,30 @@ class Subclass(BaseException, self.module.StateAccessType):
self.assertIs(Subclass().get_defining_module(), self.module)


class TestInternalFrameApi(unittest.TestCase):

@staticmethod
def func():
return sys._getframe()

def test_code(self):
frame = self.func()
code = _testinternalcapi.iframe_getcode(frame)
self.assertIs(code, self.func.__code__)

def test_lasti(self):
frame = self.func()
lasti = _testinternalcapi.iframe_getlasti(frame)
self.assertGreater(lasti, 0)
self.assertLess(lasti, len(self.func.__code__.co_code))

def test_line(self):
frame = self.func()
line = _testinternalcapi.iframe_getline(frame)
firstline = self.func.__code__.co_firstlineno
self.assertEqual(line, firstline + 2)


SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100

class Test_Pep523API(unittest.TestCase):
Expand Down
7 changes: 5 additions & 2 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,9 +868,12 @@ def test_external(self):
self.assertEqual(new_mtime_ns, old_mtime_ns)


ac_tester = import_helper.import_module('_testclinic')

try:
import _testclinic as ac_tester
except ImportError:
ac_tester = None

@unittest.skipIf(ac_tester is None, "_testclinic is missing")
class ClinicFunctionalTest(unittest.TestCase):
locals().update((name, getattr(ac_tester, name))
for name in dir(ac_tester) if name.startswith('test_'))
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,24 @@ def foo(param, lambda_exp):
""")
compile(code, "<test>", "exec")

def test_apply_static_swaps(self):
def f(x, y):
a, a = x, y
return a
self.assertEqual(f("x", "y"), "y")

def test_apply_static_swaps_2(self):
def f(x, y, z):
a, b, a = x, y, z
return a
self.assertEqual(f("x", "y", "z"), "z")

def test_apply_static_swaps_3(self):
def f(x, y, z):
a, a, b = x, y, z
return a
self.assertEqual(f("x", "y", "z"), "y")


@requires_debug_ranges()
class TestSourcePositions(unittest.TestCase):
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_listcomps.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ def test_nested_listcomp_in_lambda(self):
"""
self._check_in_scopes(code, {"z": 1, "out": [(3, 2, 1)]})

def test_assign_to_comp_iter_var_in_outer_function(self):
code = """
a = [1 for a in [0]]
"""
self._check_in_scopes(code, {"a": [1]}, scopes=["function"])


__test__ = {'doctests' : doctests}

Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@ def test_match_common(self):
# Multi-part glob-style pattern.
self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
# Case-sensitive flag
self.assertFalse(P('A.py').match('a.PY', case_sensitive=True))
self.assertTrue(P('A.py').match('a.PY', case_sensitive=False))
self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', case_sensitive=True))
self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))

def test_ordering_common(self):
# Ordering is tuple-alike.
Expand Down Expand Up @@ -916,7 +921,7 @@ def test_as_uri(self):
self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
'file://some/share/a/b%25%23c%C3%A9')

def test_match_common(self):
def test_match(self):
P = self.cls
# Absolute patterns.
self.assertTrue(P('c:/b.py').match('*:/*.py'))
Expand Down
20 changes: 17 additions & 3 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,13 +971,14 @@ def trace(frame, event, arg):
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")


class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
class DirectCfgOptimizerTests(CfgOptimizationTestCase):

def cfg_optimization_test(self, insts, expected_insts,
consts=None, expected_consts=None):
consts=None, expected_consts=None,
nlocals=0):
if expected_consts is None:
expected_consts = consts
opt_insts, opt_consts = self.get_optimized(insts, consts)
opt_insts, opt_consts = self.get_optimized(insts, consts, nlocals)
expected_insts = self.normalize_insts(expected_insts)
self.assertInstructionsMatch(opt_insts, expected_insts)
self.assertEqual(opt_consts, expected_consts)
Expand Down Expand Up @@ -1058,6 +1059,19 @@ def test_conditional_jump_backward_const_condition(self):
]
self.cfg_optimization_test(insts, expected_insts, consts=list(range(5)))

def test_no_unsafe_static_swap(self):
# We can't change order of two stores to the same location
insts = [
('LOAD_CONST', 0, 1),
('LOAD_CONST', 1, 2),
('LOAD_CONST', 2, 3),
('SWAP', 3, 4),
('STORE_FAST', 1, 4),
('STORE_FAST', 1, 4),
('POP_TOP', 0, 4),
('RETURN_VALUE', 5)
]
self.cfg_optimization_test(insts, insts, consts=list(range(3)), nlocals=1)

if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 54095f0

Please sign in to comment.