Skip to content

Commit

Permalink
Merge branch 'main' into pythongh-104114-windowspath-literal-pattern-…
Browse files Browse the repository at this point in the history
…case
  • Loading branch information
barneygale committed May 3, 2023
2 parents 1f58b94 + af886ff commit 6fd03f4
Show file tree
Hide file tree
Showing 24 changed files with 351 additions and 128 deletions.
15 changes: 9 additions & 6 deletions Doc/library/pathlib.rst
Expand Up @@ -819,9 +819,14 @@ call fails (for example because the path doesn't exist).
.. versionchanged:: 3.10
The *follow_symlinks* parameter was added.

.. method:: Path.exists()
.. method:: Path.exists(*, follow_symlinks=True)

Whether the path points to an existing file or directory::
Return ``True`` if the path points to an existing file or directory.

This method normally follows symlinks; to check if a symlink exists, add
the argument ``follow_symlinks=False``.

::

>>> Path('.').exists()
True
Expand All @@ -832,10 +837,8 @@ call fails (for example because the path doesn't exist).
>>> Path('nonexistentfile').exists()
False

.. note::
If the path points to a symlink, :meth:`exists` returns whether the
symlink *points to* an existing file or directory.

.. versionchanged:: 3.12
The *follow_symlinks* parameter was added.

.. method:: Path.expanduser()

Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/initconfig.h
Expand Up @@ -252,7 +252,7 @@ typedef struct {
int allow_threads;
int allow_daemon_threads;
int check_multi_interp_extensions;
} _PyInterpreterConfig;
} PyInterpreterConfig;

#define _PyInterpreterConfig_INIT \
{ \
Expand Down
4 changes: 2 additions & 2 deletions Include/cpython/pylifecycle.h
Expand Up @@ -62,9 +62,9 @@ PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);

PyAPI_FUNC(PyStatus) _Py_NewInterpreterFromConfig(
PyAPI_FUNC(PyStatus) Py_NewInterpreterFromConfig(
PyThreadState **tstate_p,
const _PyInterpreterConfig *config);
const PyInterpreterConfig *config);

typedef void (*atexit_datacallbackfunc)(void *);
PyAPI_FUNC(int) _Py_AtExit(
Expand Down
26 changes: 12 additions & 14 deletions Include/internal/pycore_intrinsics.h
@@ -1,26 +1,24 @@
// Auto-generated by Tools/build/generate_opcode_h.py from Lib/opcode.py

/* Unary Functions: */
#define INTRINSIC_1_INVALID 0
#define INTRINSIC_PRINT 1
#define INTRINSIC_IMPORT_STAR 2
#define INTRINSIC_STOPITERATION_ERROR 3
#define INTRINSIC_ASYNC_GEN_WRAP 4
#define INTRINSIC_UNARY_POSITIVE 5
#define INTRINSIC_LIST_TO_TUPLE 6

#define INTRINSIC_PRINT 1
#define INTRINSIC_IMPORT_STAR 2
#define INTRINSIC_STOPITERATION_ERROR 3
#define INTRINSIC_ASYNC_GEN_WRAP 4
#define INTRINSIC_UNARY_POSITIVE 5
#define INTRINSIC_LIST_TO_TUPLE 6

#define MAX_INTRINSIC_1 6
#define MAX_INTRINSIC_1 6


/* Binary Functions: */
#define INTRINSIC_2_INVALID 0
#define INTRINSIC_PREP_RERAISE_STAR 1

#define INTRINSIC_PREP_RERAISE_STAR 1

#define MAX_INTRINSIC_2 1

#define MAX_INTRINSIC_2 1

typedef PyObject *(*instrinsic_func1)(PyThreadState* tstate, PyObject *value);
typedef PyObject *(*instrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);

extern const instrinsic_func1 _PyIntrinsics_UnaryFunctions[];
extern const instrinsic_func2 _PyIntrinsics_BinaryFunctions[];

7 changes: 7 additions & 0 deletions Include/internal/pycore_typeobject.h
Expand Up @@ -44,6 +44,13 @@ struct type_cache {

typedef struct {
PyTypeObject *type;
int readying;
int ready;
// XXX tp_dict, tp_bases, and tp_mro can probably be statically
// allocated, instead of dynamically and stored on the interpreter.
PyObject *tp_dict;
PyObject *tp_bases;
PyObject *tp_mro;
PyObject *tp_subclasses;
/* We never clean up weakrefs for static builtin types since
they will effectively never get triggered. However, there
Expand Down
8 changes: 8 additions & 0 deletions Lib/dis.py
Expand Up @@ -11,6 +11,8 @@
_cache_format,
_inline_cache_entries,
_nb_ops,
_intrinsic_1_descs,
_intrinsic_2_descs,
_specializations,
_specialized_instructions,
)
Expand Down Expand Up @@ -42,6 +44,8 @@
SEND = opmap['SEND']
LOAD_ATTR = opmap['LOAD_ATTR']
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
CALL_INTRINSIC_1 = opmap['CALL_INTRINSIC_1']
CALL_INTRINSIC_2 = opmap['CALL_INTRINSIC_2']

CACHE = opmap["CACHE"]

Expand Down Expand Up @@ -506,6 +510,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
if arg & (1<<i))
elif deop == BINARY_OP:
_, argrepr = _nb_ops[arg]
elif deop == CALL_INTRINSIC_1:
argrepr = _intrinsic_1_descs[arg]
elif deop == CALL_INTRINSIC_2:
argrepr = _intrinsic_2_descs[arg]
yield Instruction(_all_opname[op], op,
arg, argval, argrepr,
offset, starts_line, is_jump_target, positions)
Expand Down
2 changes: 1 addition & 1 deletion Lib/http/server.py
Expand Up @@ -791,7 +791,7 @@ def list_directory(self, path):
displaypath = urllib.parse.unquote(self.path,
errors='surrogatepass')
except UnicodeDecodeError:
displaypath = urllib.parse.unquote(path)
displaypath = urllib.parse.unquote(self.path)
displaypath = html.escape(displaypath, quote=False)
enc = sys.getfilesystemencoding()
title = f'Directory listing for {displaypath}'
Expand Down
15 changes: 15 additions & 0 deletions Lib/opcode.py
Expand Up @@ -306,6 +306,21 @@ def pseudo_op(name, op, real_ops):
("NB_INPLACE_XOR", "^="),
]

_intrinsic_1_descs = [
"INTRINSIC_1_INVALID",
"INTRINSIC_PRINT",
"INTRINSIC_IMPORT_STAR",
"INTRINSIC_STOPITERATION_ERROR",
"INTRINSIC_ASYNC_GEN_WRAP",
"INTRINSIC_UNARY_POSITIVE",
"INTRINSIC_LIST_TO_TUPLE",
]

_intrinsic_2_descs = [
'INTRINSIC_2_INVALID',
'INTRINSIC_PREP_RERAISE_STAR',
]

_specializations = {
"BINARY_OP": [
"BINARY_OP_ADD_FLOAT",
Expand Down
7 changes: 5 additions & 2 deletions Lib/pathlib.py
Expand Up @@ -1097,12 +1097,15 @@ def hardlink_to(self, target):

# Convenience functions for querying the stat results

def exists(self):
def exists(self, *, follow_symlinks=True):
"""
Whether this path exists.
This method normally follows symlinks; to check whether a symlink exists,
add the argument follow_symlinks=False.
"""
try:
self.stat()
self.stat(follow_symlinks=follow_symlinks)
except OSError as e:
if not _ignore_error(e):
raise
Expand Down
41 changes: 40 additions & 1 deletion Lib/test/test_dis.py
Expand Up @@ -247,6 +247,35 @@ def wrap_func_w_kwargs():
""" % (wrap_func_w_kwargs.__code__.co_firstlineno,
wrap_func_w_kwargs.__code__.co_firstlineno + 1)

dis_intrinsic_1_2 = """\
0 RESUME 0
1 LOAD_CONST 0 (0)
LOAD_CONST 1 (('*',))
IMPORT_NAME 0 (math)
CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
POP_TOP
RETURN_CONST 2 (None)
"""

dis_intrinsic_1_5 = """\
0 RESUME 0
1 LOAD_NAME 0 (a)
CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE)
RETURN_VALUE
"""

dis_intrinsic_1_6 = """\
0 RESUME 0
1 BUILD_LIST 0
LOAD_NAME 0 (a)
LIST_EXTEND 1
CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE)
RETURN_VALUE
"""

_BIG_LINENO_FORMAT = """\
1 RESUME 0
Expand Down Expand Up @@ -549,7 +578,7 @@ async def _asyncwith(c):
>> COPY 3
POP_EXCEPT
RERAISE 1
>> CALL_INTRINSIC_1 3
>> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
RERAISE 1
ExceptionTable:
12 rows
Expand Down Expand Up @@ -942,6 +971,16 @@ def test_kw_names(self):
# Test that value is displayed for KW_NAMES
self.do_disassembly_test(wrap_func_w_kwargs, dis_kw_names)

def test_intrinsic_1(self):
# Test that argrepr is displayed for CALL_INTRINSIC_1
self.do_disassembly_test("from math import *", dis_intrinsic_1_2)
self.do_disassembly_test("+a", dis_intrinsic_1_5)
self.do_disassembly_test("(*a,)", dis_intrinsic_1_6)

def test_intrinsic_2(self):
self.assertIn("CALL_INTRINSIC_2 1 (INTRINSIC_PREP_RERAISE_STAR)",
self.get_disassembly("try: pass\nexcept* Exception: x"))

def test_big_linenos(self):
def func(count):
namespace = {}
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_httpservers.py
Expand Up @@ -418,6 +418,14 @@ def test_undecodable_filename(self):
self.check_status_and_reason(response, HTTPStatus.OK,
data=os_helper.TESTFN_UNDECODABLE)

def test_undecodable_parameter(self):
# sanity check using a valid parameter
response = self.request(self.base_url + '/?x=123').read()
self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1'))
# now the bogus encoding
response = self.request(self.base_url + '/?x=%bb').read()
self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1'))

def test_get_dir_redirect_location_domain_injection_bug(self):
"""Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location.
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_pathlib.py
Expand Up @@ -1700,6 +1700,8 @@ def test_exists(self):
self.assertIs(True, (p / 'linkB').exists())
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
self.assertIs(False, (p / 'linkA' / 'bah').exists())
self.assertIs(False, (p / 'brokenLink').exists())
self.assertIs(True, (p / 'brokenLink').exists(follow_symlinks=False))
self.assertIs(False, (p / 'foo').exists())
self.assertIs(False, P('/xyzzy').exists())
self.assertIs(False, P(BASE + '\udfff').exists())
Expand Down Expand Up @@ -1806,6 +1808,8 @@ def _check(glob, expected):
_check(p.glob("*/fileB"), ['dirB/fileB'])
else:
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
if os_helper.can_symlink():
_check(p.glob("brokenLink"), ['brokenLink'])

if not os_helper.can_symlink():
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
Expand Down
@@ -0,0 +1,5 @@
We've added ``Py_NewInterpreterFromConfig()`` and ``PyInterpreterConfig`` to
the public C-API (but not the stable ABI; not yet at least). The new
function may be used to create a new interpreter with various features
configured. The function was added to support PEP 684 (per-interpreter
GIL).
@@ -0,0 +1,5 @@
Fixed the bug in :meth:`pathlib.Path.glob` -- previously a dangling symlink
would not be found by this method when the pattern is an exact match, but
would be found when the pattern contains a wildcard or the recursive
wildcard (``**``). With this change, a dangling symlink will be found in
both cases.
@@ -0,0 +1 @@
Make :mod:`dis` display the names of the args for :opcode:`CALL_INTRINSIC_*`.
@@ -0,0 +1,2 @@
Do not expose the local on-disk location in directory indexes
produced by :class:`http.client.SimpleHTTPRequestHandler`.
1 change: 1 addition & 0 deletions Modules/_abc.c
Expand Up @@ -7,6 +7,7 @@
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyType_GetSubclasses()
#include "pycore_runtime.h" // _Py_ID()
#include "pycore_typeobject.h" // _PyType_GetMRO()
#include "clinic/_abc.c.h"

/*[clinic input]
Expand Down
4 changes: 2 additions & 2 deletions Modules/_testcapimodule.c
Expand Up @@ -1538,15 +1538,15 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)

PyThreadState_Swap(NULL);

const _PyInterpreterConfig config = {
const PyInterpreterConfig config = {
.use_main_obmalloc = use_main_obmalloc,
.allow_fork = allow_fork,
.allow_exec = allow_exec,
.allow_threads = allow_threads,
.allow_daemon_threads = allow_daemon_threads,
.check_multi_interp_extensions = check_multi_interp_extensions,
};
PyStatus status = _Py_NewInterpreterFromConfig(&substate, &config);
PyStatus status = Py_NewInterpreterFromConfig(&substate, &config);
if (PyStatus_Exception(status)) {
/* Since no new thread state was created, there is no exception to
propagate; raise a fresh one after swapping in the old thread
Expand Down
8 changes: 4 additions & 4 deletions Modules/_xxsubinterpretersmodule.c
Expand Up @@ -513,12 +513,12 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)

// Create and initialize the new interpreter.
PyThreadState *save_tstate = _PyThreadState_GET();
const _PyInterpreterConfig config = isolated
? (_PyInterpreterConfig)_PyInterpreterConfig_INIT
: (_PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
const PyInterpreterConfig config = isolated
? (PyInterpreterConfig)_PyInterpreterConfig_INIT
: (PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
// XXX Possible GILState issues?
PyThreadState *tstate = NULL;
PyStatus status = _Py_NewInterpreterFromConfig(&tstate, &config);
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
PyThreadState_Swap(save_tstate);
if (PyStatus_Exception(status)) {
/* Since no new thread state was created, there is no exception to
Expand Down
31 changes: 3 additions & 28 deletions Modules/gcmodule.c
Expand Up @@ -2174,41 +2174,16 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp)
}


static void
gc_fini_untrack(PyGC_Head *list)
{
PyGC_Head *gc;
for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(list)) {
PyObject *op = FROM_GC(gc);
_PyObject_GC_UNTRACK(op);
// gh-92036: If a deallocator function expect the object to be tracked
// by the GC (ex: func_dealloc()), it can crash if called on an object
// which is no longer tracked by the GC. Leak one strong reference on
// purpose so the object is never deleted and its deallocator is not
// called.
Py_INCREF(op);
}
}


void
_PyGC_Fini(PyInterpreterState *interp)
{
GCState *gcstate = &interp->gc;
Py_CLEAR(gcstate->garbage);
Py_CLEAR(gcstate->callbacks);

if (!_Py_IsMainInterpreter(interp)) {
// bpo-46070: Explicitly untrack all objects currently tracked by the
// GC. Otherwise, if an object is used later by another interpreter,
// calling PyObject_GC_UnTrack() on the object crashs if the previous
// or the next object of the PyGC_Head structure became a dangling
// pointer.
for (int i = 0; i < NUM_GENERATIONS; i++) {
PyGC_Head *gen = GEN_HEAD(gcstate, i);
gc_fini_untrack(gen);
}
}
/* We expect that none of this interpreters objects are shared
with other interpreters.
See https://github.com/python/cpython/issues/90228. */
}

/* for debugging */
Expand Down

0 comments on commit 6fd03f4

Please sign in to comment.