Skip to content
Merged
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
20 changes: 19 additions & 1 deletion Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ The debugger's prompt is ``(Pdb)``, which is the indicator that you are in debug
You can also invoke :mod:`pdb` from the command line to debug other scripts. For
example::

python -m pdb [-c command] (-m module | pyfile) [args ...]
python -m pdb [-c command] (-m module | -p pid | pyfile) [args ...]

When invoked as a module, pdb will automatically enter post-mortem debugging if
the program being debugged exits abnormally. After post-mortem debugging (or
Expand All @@ -104,6 +104,24 @@ useful than quitting the debugger upon program's exit.
.. versionchanged:: 3.7
Added the ``-m`` option.

.. option:: -p, --pid <pid>

Attach to the process with the specified PID.

.. versionadded:: 3.14


To attach to a running Python process for remote debugging, use the ``-p`` or
``--pid`` option with the target process's PID::

python -m pdb -p 1234

.. note::

Attaching to a process that is blocked in a system call or waiting for I/O
will only work once the next bytecode instruction is executed or when the
process receives a signal.

Typical usage to execute a statement under control of the debugger is::

>>> import pdb
Expand Down
75 changes: 38 additions & 37 deletions Include/internal/pycore_uop_ids.h

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

4 changes: 4 additions & 0 deletions Include/internal/pycore_uop_metadata.h

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

3 changes: 2 additions & 1 deletion Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3489,7 +3489,8 @@ def help():
_usage = """\
Debug the Python program given by pyfile. Alternatively,
an executable module or package to debug can be specified using
the -m switch.
the -m switch. You can also attach to a running Python process
using the -p option with its PID.

Initial commands are read from .pdbrc files in your home directory
and in the current directory, if they exist. Commands supplied with
Expand Down
63 changes: 63 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,50 @@ def testfunc(n):
self.assertNotIn("_GUARD_NOS_INT", uops)
self.assertNotIn("_GUARD_TOS_INT", uops)

def test_get_len_with_const_tuple(self):
def testfunc(n):
x = 0.0
for _ in range(n):
match (1, 2, 3, 4):
case [_, _, _, _]:
x += 1.0
return x
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(int(res), TIER2_THRESHOLD)
uops = get_opnames(ex)
self.assertNotIn("_GUARD_NOS_INT", uops)
self.assertNotIn("_GET_LEN", uops)
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)

def test_get_len_with_non_const_tuple(self):
def testfunc(n):
x = 0.0
for _ in range(n):
match object(), object():
case [_, _]:
x += 1.0
return x
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(int(res), TIER2_THRESHOLD)
uops = get_opnames(ex)
self.assertNotIn("_GUARD_NOS_INT", uops)
self.assertNotIn("_GET_LEN", uops)
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)

def test_get_len_with_non_tuple(self):
def testfunc(n):
x = 0.0
for _ in range(n):
match [1, 2, 3, 4]:
case [_, _, _, _]:
x += 1.0
return x
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(int(res), TIER2_THRESHOLD)
uops = get_opnames(ex)
self.assertNotIn("_GUARD_NOS_INT", uops)
self.assertIn("_GET_LEN", uops)

def test_binary_op_subscr_tuple_int(self):
def testfunc(n):
x = 0
Expand Down Expand Up @@ -2093,6 +2137,25 @@ def testfunc(n):
self.assertNotIn("_TO_BOOL_BOOL", uops)
self.assertIn("_GUARD_IS_TRUE_POP", uops)

def test_set_type_version_sets_type(self):
class C:
A = 1

def testfunc(n):
x = 0
c = C()
for _ in range(n):
x += c.A # Guarded.
x += type(c).A # Unguarded!
return x

res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, 2 * TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_GUARD_TYPE_VERSION", uops)
self.assertNotIn("_CHECK_ATTR_CLASS", uops)


def global_identity(x):
return x
Expand Down
19 changes: 19 additions & 0 deletions Lib/test/test_zipfile/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,25 @@ def test_is_zip_erroneous_file(self):
self.assertFalse(zipfile.is_zipfile(fp))
fp.seek(0, 0)
self.assertFalse(zipfile.is_zipfile(fp))
# - passing non-zipfile with ZIP header elements
# data created using pyPNG like so:
# d = [(ord('P'), ord('K'), 5, 6), (ord('P'), ord('K'), 6, 6)]
# w = png.Writer(1,2,alpha=True,compression=0)
# f = open('onepix.png', 'wb')
# w.write(f, d)
# w.close()
data = (b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00"
b"\x00\x02\x08\x06\x00\x00\x00\x99\x81\xb6'\x00\x00\x00\x15I"
b"DATx\x01\x01\n\x00\xf5\xff\x00PK\x05\x06\x00PK\x06\x06\x07"
b"\xac\x01N\xc6|a\r\x00\x00\x00\x00IEND\xaeB`\x82")
# - passing a filename
with open(TESTFN, "wb") as fp:
fp.write(data)
self.assertFalse(zipfile.is_zipfile(TESTFN))
# - passing a file-like object
fp = io.BytesIO()
fp.write(data)
self.assertFalse(zipfile.is_zipfile(fp))

def test_damaged_zipfile(self):
"""Check that zipfiles with missing bytes at the end raise BadZipFile."""
Expand Down
48 changes: 34 additions & 14 deletions Lib/zipfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,19 @@ def strip(cls, data, xids):

def _check_zipfile(fp):
try:
if _EndRecData(fp):
return True # file has correct magic number
endrec = _EndRecData(fp)
if endrec:
if endrec[_ECD_ENTRIES_TOTAL] == 0 and endrec[_ECD_SIZE] == 0 and endrec[_ECD_OFFSET] == 0:
return True # Empty zipfiles are still zipfiles
elif endrec[_ECD_DISK_NUMBER] == endrec[_ECD_DISK_START]:
# Central directory is on the same disk
fp.seek(sum(_handle_prepended_data(endrec)))
if endrec[_ECD_SIZE] >= sizeCentralDir:
data = fp.read(sizeCentralDir) # CD is where we expect it to be
if len(data) == sizeCentralDir:
centdir = struct.unpack(structCentralDir, data) # CD is the right size
if centdir[_CD_SIGNATURE] == stringCentralDir:
return True # First central directory entry has correct magic number
except OSError:
pass
return False
Expand All @@ -258,6 +269,22 @@ def is_zipfile(filename):
pass
return result

def _handle_prepended_data(endrec, debug=0):
size_cd = endrec[_ECD_SIZE] # bytes in central directory
offset_cd = endrec[_ECD_OFFSET] # offset of central directory

# "concat" is zero, unless zip was concatenated to another file
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
if endrec[_ECD_SIGNATURE] == stringEndArchive64:
# If Zip64 extension structures are present, account for them
concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)

if debug > 2:
inferred = concat + offset_cd
print("given, inferred, offset", offset_cd, inferred, concat)

return offset_cd, concat

def _EndRecData64(fpin, offset, endrec):
"""
Read the ZIP64 end-of-archive records and use that to update endrec
Expand Down Expand Up @@ -1501,28 +1528,21 @@ def _RealGetContents(self):
raise BadZipFile("File is not a zip file")
if self.debug > 1:
print(endrec)
size_cd = endrec[_ECD_SIZE] # bytes in central directory
offset_cd = endrec[_ECD_OFFSET] # offset of central directory
self._comment = endrec[_ECD_COMMENT] # archive comment

# "concat" is zero, unless zip was concatenated to another file
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
if endrec[_ECD_SIGNATURE] == stringEndArchive64:
# If Zip64 extension structures are present, account for them
concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)
offset_cd, concat = _handle_prepended_data(endrec, self.debug)

# self.start_dir: Position of start of central directory
self.start_dir = offset_cd + concat

# store the offset to the beginning of data for the
# .data_offset property
self._data_offset = concat

if self.debug > 2:
inferred = concat + offset_cd
print("given, inferred, offset", offset_cd, inferred, concat)
# self.start_dir: Position of start of central directory
self.start_dir = offset_cd + concat
if self.start_dir < 0:
raise BadZipFile("Bad offset for central directory")
fp.seek(self.start_dir, 0)
size_cd = endrec[_ECD_SIZE]
data = fp.read(size_cd)
fp = io.BytesIO(data)
total = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Allow the JIT to remove int guards after ``_GET_LEN`` by setting the return
type to int.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve the JIT's ability to narrow unknown classes to constant values.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve Zip file validation false positive rate in :func:`zipfile.is_zipfile`.
4 changes: 2 additions & 2 deletions Modules/_remote_debugging_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,7 @@ get_stack_trace(PyObject* self, PyObject* args)
&address_of_current_frame)
< 0)
{
Py_DECREF(result);
Py_CLEAR(result);
goto result_err;
}

Expand All @@ -1565,7 +1565,7 @@ get_stack_trace(PyObject* self, PyObject* args)
}

if (PyList_Append(result, frame_info) == -1) {
Py_DECREF(result);
Py_CLEAR(result);
goto result_err;
}

Expand Down
5 changes: 5 additions & 0 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ dummy_func(
PyStackRef_CLOSE(value);
}

tier2 op(_POP_TWO, (nos, tos --)) {
PyStackRef_CLOSE(tos);
PyStackRef_CLOSE(nos);
}

pure inst(PUSH_NULL, (-- res)) {
res = PyStackRef_NULL;
}
Expand Down
Loading
Loading