diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 91b164814d7..29b4efa58e9 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -399,6 +399,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
+ python/py-corefile.c \
python/py-dap.c \
python/py-disasm.c \
python/py-event.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 2c73776944f..ebd0e063227 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -83,6 +83,16 @@ qExecAndArgs
** The gdb.write() function now takes an additional, optional,
'style' argument, which can be used to style the output.
+ ** New gdb.Corefile class which represents a loaded core file. This
+ has an attribute Corefile.filename, the file name of the loaded
+ core file, and a method Corefile.is_valid(), which returns False
+ when a Corefile object becomes invalid (e.g. when the core file
+ is unloaded).
+
+ ** New Inferior.corefile attribute. This read only attribute
+ contains the gdb.Corefile object if a core file is loaded into
+ the inferior, otherwise, this contains None.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 29eafe8bdfd..2f202dc1fbf 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -53,6 +53,7 @@
#include "xml-tdesc.h"
#include "memtag.h"
#include "cli/cli-style.h"
+#include "observable.h"
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
@@ -678,6 +679,9 @@ core_target::clear_core ()
clear_solib (current_program_space);
current_program_space->cbfd.reset (nullptr);
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
}
@@ -1278,6 +1282,9 @@ core_target_open (const char *arg, int from_tty)
exception_print (gdb_stderr, except);
}
}
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
void
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 2f74a2311a6..c671d231345 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -234,6 +234,7 @@ optional arguments while skipping others. Example:
* Disassembly In Python:: Instruction Disassembly In Python
* Missing Debug Info In Python:: Handle missing debug info from Python.
* Missing Objfiles In Python:: Handle objfiles from Python.
+* Core Files In Python:: Python representation of core files.
@end menu
@node Basic Python
@@ -3634,6 +3635,15 @@ necessary quoting for the shell; when a sequence is assigned, the
quoting is applied by @value{GDBN}.
@end defvar
+@defvar Inferior.corefile
+If a core file has been loaded into this inferior (@pxref{core-file
+command}), then this contains a @code{gdb.Corefile} object that
+represents the loaded core file (@pxref{Core Files In Python}).
+
+If no core file has been loaded into this inferior, then this
+attribute contains @code{None}.
+@end defvar
+
A @code{gdb.Inferior} object has the following methods:
@defun Inferior.is_valid ()
@@ -8884,6 +8894,45 @@ handlers, all of the matching handlers are enabled. The
@code{enabled} field of each matching handler is set to @code{True}.
@end table
+@node Core Files In Python
+@subsubsection Core Files In Python
+@cindex python, core files
+
+When a core file is loaded into an inferior (@pxref{Inferiors In
+Python}) for examination (@pxref{core-file command}), information
+about the core file is contained in a @code{gdb.Corefile} object.
+
+The @code{gdb.Corefile} for an inferior can be accessed using the
+@code{Inferior.corefile} attribute. This will be @code{None} if
+no core file is loaded.
+
+A @code{gdb.Corefile} object has the following attributes:
+
+@defvar Corefile.filename
+This read only attribute contains a non-empty string, the file name of
+the core file. Attempting to access this attribute on an invalid
+@code{gdb.Corefile} object will raise a @code{RuntimeError} exception.
+@end defvar
+
+A @code{gdb.Corefile} object has the following methods:
+
+@defun Corefile.is_valid ()
+Returns @code{True} if the @code{gdb.Corefile} object is valid,
+@code{False} if not. A @code{gdb.Corefile} object will become invalid
+when the core file is unloaded from the inferior using the
+@kbd{core-file} command (@pxref{core-file command}), or if the
+inferior in which the core file is loaded is deleted. All other
+@code{gdb.Corefile} methods and attributes will throw an exception if
+it is invalid at the time the method is called, or the attribute
+accessed.
+@end defun
+
+One may add arbitrary attributes to @code{gdb.Corefile} objects in the
+usual Python way. This is useful if, for example, one needs to do
+some extra record keeping associated with the corefile.
+@xref{choosing attribute names}, for guidance on selecting a suitable
+name for new attributes.
+
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading
diff --git a/gdb/observable.c b/gdb/observable.c
index 1233a1943a6..8439f11c15d 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -76,6 +76,7 @@ DEFINE_OBSERVABLE (target_post_wait);
DEFINE_OBSERVABLE (new_program_space);
DEFINE_OBSERVABLE (free_program_space);
DEFINE_OBSERVABLE (tui_enabled);
+DEFINE_OBSERVABLE (core_file_changed);
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/observable.h b/gdb/observable.h
index 4d913010c56..5f064cf1fc8 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -260,6 +260,12 @@ extern observable free_program_space;
extern observable tui_enabled;
+/* The core file loaded into the program space inferior INF has changed.
+ The process of changing has completed, i.e. when unloading, the unload
+ is now complete. When loading a new core file, the load is complete,
+ shared libraries have been loaded, registers and threads read in, etc. */
+extern observable core_file_changed;
+
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
new file mode 100644
index 00000000000..bda93fa1325
--- /dev/null
+++ b/gdb/python/py-corefile.c
@@ -0,0 +1,291 @@
+/* Python interface to core files.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+#include "python-internal.h"
+#include "progspace.h"
+#include "observable.h"
+#include "inferior.h"
+
+/* A gdb.Corefile object. */
+
+struct corefile_object
+{
+ PyObject_HEAD
+
+ /* The inferior this core file is attached to. This will be set to NULL
+ when the inferior is deleted, or if a different core file is loaded
+ for the inferior. When this is NULL the gdb.Corefile object is
+ considered invalid.*/
+ struct inferior *inferior;
+
+ /* Dictionary holding user-added attributes. This is the __dict__
+ attribute of the object. This is an owning reference. */
+ PyObject *dict;
+};
+
+extern PyTypeObject corefile_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
+
+/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
+ deleted. */
+
+struct inferior_corefile_deleter
+{
+ void operator() (corefile_object *obj)
+ {
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py;
+
+ /* When OBJECT goes out of scope this will Py_DECREF on OBJ. */
+ gdbpy_ref object (obj);
+
+ /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
+ object->inferior = nullptr;
+ }
+};
+
+/* Store a gdb.Corefile object in an inferior's registry. */
+
+static const registry::key
+ cfpy_inferior_corefile_data_key;
+
+/* See python-internal.h. */
+
+gdbpy_ref<>
+gdbpy_core_file_from_inferior (inferior *inf)
+{
+ gdb_assert (inf != nullptr);
+ gdb_assert (inf->pspace != nullptr);
+
+ program_space *pspace = inf->pspace;
+
+ if (pspace->core_bfd () == nullptr)
+ return gdbpy_ref<>::new_reference (Py_None);
+
+ PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
+ if (result == nullptr)
+ {
+ gdbpy_ref object
+ (PyObject_New (corefile_object, &corefile_object_type));
+ if (object == nullptr)
+ return nullptr;
+
+ /* Ensure the 'inferior' field is set to NULL. If the PyDict_New
+ call fails then the gdb.Corefile will be discarded and
+ cfpy_dealloc will be called, which requires that the 'inferior' be
+ set to NULL. */
+ object->inferior = nullptr;
+ object->dict = PyDict_New ();
+ if (object->dict == nullptr)
+ return nullptr;
+
+ /* Now that the gdb.Corefile has been successfully initialised and we
+ know that it is going to be passed back to the user, move it out
+ of the invalid state by setting the 'inferior' field to a non NULL
+ value. */
+ object->inferior = inf;
+ cfpy_inferior_corefile_data_key.set (inf, object.get ());
+ result = (PyObject *) object.release ();
+ }
+
+ return gdbpy_ref<>::new_reference (result);
+}
+
+/* Return true if OBJ is valid. */
+
+static bool
+cfpy_corefile_object_is_valid (const corefile_object *obj)
+{
+ if (obj->inferior == nullptr)
+ return false;
+
+ gdb_assert (obj->inferior->pspace != nullptr);
+
+ return obj->inferior->pspace->core_bfd () != nullptr;
+}
+
+/* Require that COREFILE_OBJ be a valid core file. A valid core file
+ object has a valid program space, and the program space has a core file
+ loaded into it. */
+#define CFPY_REQUIRE_VALID(corefile_obj) \
+ do { \
+ if (!cfpy_corefile_object_is_valid (corefile_obj)) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("Corefile no longer exists.")); \
+ return nullptr; \
+ } \
+ } while (0)
+
+/* Read the gdb.Corefile.filename attribute. */
+
+static PyObject *
+cfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If the program space's core file had been cleared, then this Corefile
+ object would have been invalidated. */
+ bfd *abfd = obj->inferior->pspace->core_bfd ();
+ gdb_assert (abfd != nullptr);
+
+ return host_string_to_python_string (bfd_get_filename (abfd)).release ();
+}
+
+/* Implementation of gdb.Corefile.is_valid (self) -> Boolean.
+ Returns True if this core file object is associated with a program space
+ that still exists, an the program space still has a core file loaded. */
+
+static PyObject *
+cfpy_is_valid (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+
+/* Callback from gdb::observers::core_file_changed. The core file in
+ PSPACE has been changed. */
+
+static void
+cfpy_corefile_changed (inferior *inf)
+{
+ cfpy_inferior_corefile_data_key.clear (inf);
+}
+
+/* Called when a gdb.Corefile is destroyed. */
+
+static void
+cfpy_dealloc (PyObject *obj)
+{
+ corefile_object *corefile = (corefile_object *) obj;
+
+ /* Every gdb.Corefile is cached in an inferior's registry. The only way
+ for a gdb.Corefile to be deallocated is to remove the object reference
+ from the registry (and dec its ref count), but before we do that, we
+ set the object's inferior pointer to NULL. */
+ gdb_assert (corefile->inferior == nullptr);
+
+ Py_XDECREF (corefile->dict);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* __repr__ implementation for gdb.Corefile. */
+
+static PyObject *
+cfpy_repr (PyObject *self)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ return gdb_py_invalid_object_repr (self);
+
+ program_space *pspace = obj->inferior->pspace;
+ gdb_assert (pspace != nullptr);
+ return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
+ Py_TYPE (self)->tp_name,
+ obj->inferior->num,
+ bfd_get_filename (pspace->core_bfd ()));
+}
+
+
+
+static int
+gdbpy_initialize_corefile ()
+{
+ gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
+ "py-corefile");
+
+ if (gdbpy_type_ready (&corefile_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
+
+
+
+static gdb_PyGetSetDef corefile_getset[] =
+{
+ { "__dict__", gdb_py_generic_dict, nullptr,
+ "The __dict__ for the gdb.Corefile.", &corefile_object_type },
+ { "filename", cfpy_get_filename, nullptr,
+ "The filename of a valid Corefile object.", nullptr },
+ { nullptr }
+};
+
+static PyMethodDef corefile_object_methods[] =
+{
+ { "is_valid", cfpy_is_valid, METH_NOARGS,
+ "is_valid () -> Boolean.\n\
+Return true if this Corefile is valid, false if not." },
+ { nullptr }
+};
+
+PyTypeObject corefile_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.Corefile", /*tp_name*/
+ sizeof (corefile_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ cfpy_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ corefile_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ corefile_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof (corefile_object, dict), /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 2aa11d3160d..d926923915a 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -973,6 +973,22 @@ infpy_get_main_name (PyObject *self, void *closure)
return host_string_to_python_string (name).release ();
}
+/* Implement the Inferior.corefile getter. Returns a gdb.Corefile
+ object, or None. */
+
+static PyObject *
+infpy_get_core_file (PyObject *self, void *closure)
+{
+ inferior_object *inf = (inferior_object *) self;
+
+ INFPY_REQUIRE_VALID (inf);
+
+ inferior *inferior = inf->inferior;
+ gdb_assert (inferior != nullptr);
+
+ return gdbpy_core_file_from_inferior (inferior).release ();
+}
+
static void
infpy_dealloc (PyObject *obj)
{
@@ -1062,6 +1078,8 @@ static gdb_PyGetSetDef inferior_object_getset[] =
{ "progspace", infpy_get_progspace, NULL, "Program space of this inferior" },
{ "main_name", infpy_get_main_name, nullptr,
"Name of 'main' function, if known.", nullptr },
+ { "corefile", infpy_get_core_file, nullptr,
+ "The corefile loaded in to this inferior, or None.", nullptr },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index dbb2d7eb7e0..51ace132ddc 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1127,6 +1127,14 @@ extern std::optional gdbpy_print_insn (struct gdbarch *gdbarch,
CORE_ADDR address,
disassemble_info *info);
+/* Return the gdb.Corefile object representing the core file loaded into
+ the program space of INF, or None if there is no core file loaded. INF
+ must not be NULL. If an error occurs then NULL is returned, and a
+ suitable Python error will be set. */
+
+extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf);
+
+
/* A wrapper for PyType_Ready that also automatically registers the
type in the appropriate module. Returns 0 on success, -1 on error.
If MOD is supplied, then the type is added to that module. If MOD
diff --git a/gdb/testsuite/gdb.python/py-corefile.c b/gdb/testsuite/gdb.python/py-corefile.c
new file mode 100644
index 00000000000..1334ff65143
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.c
@@ -0,0 +1,25 @@
+/* Copyright 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+#include
+
+int
+main (void)
+{
+ /* With correct ulimit, etc. this should cause a core dump. */
+ abort ();
+}
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
new file mode 100644
index 00000000000..e9254bd9e78
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -0,0 +1,178 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# This file is part of the GDB testsuite. It tests the core file
+# support in Python.
+
+require isnative
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+if {[build_executable "build executable" $testfile $srcfile] == -1} {
+ return
+}
+
+set corefile [core_find $binfile]
+if {$corefile == ""} {
+ unsupported "couldn't create or find corefile"
+ return
+}
+
+# Create a copy of the corefile.
+set other_corefile [standard_output_file ${testfile}-other.core]
+remote_exec build "cp $corefile $other_corefile"
+
+clean_restart
+
+gdb_test_no_output "python inf = gdb.selected_inferior()" \
+ "capture current inferior"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None before loading a core file"
+
+gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+set file_re [string_to_regexp $corefile]
+gdb_test "python print(inf.corefile)" "^" \
+ "Inferior.corefile is a valid object after loading a core file"
+
+gdb_test_no_output "python core1=inf.corefile" "capture gdb.Corefile object"
+
+gdb_test "python print(core1.__dict__)" "^\\{\\}" \
+ "print Corefile.__dict__ when empty"
+
+gdb_test_no_output "python core1._my_attribute = \"Hello\"" \
+ "write new attribute into Corefile object"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "immediately read new attribute"
+
+gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
+ "print Corefile.__dict__ after adding an attribute"
+
+gdb_test "python print(core1.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+gdb_test "python print(core1.is_valid())" "^True" \
+ "Corefile.is_valid() is True while corefile is loaded"
+
+gdb_test "core-file" "^No core file now\\." "unload current core file"
+
+gdb_test "python print(core1.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded"
+
+gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
+ "print Corefile.__dict__ with attribute when invalid"
+
+gdb_test "python print(core1)" "^" \
+ "print an invalid gdb.Corefile object"
+
+gdb_test "python print(core1.filename)" \
+ [multi_line \
+ "Python Exception : Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None again after corefile unload"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "read new attribute from invalid core file"
+
+# Create a second inferior.
+gdb_test "add-inferior"
+gdb_test "inferior 2"
+
+with_test_prefix "in second inferior" {
+ gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+ gdb_test "python print(inf.corefile)" "^None" \
+ "first inferior still has no core file"
+
+ gdb_test_no_output "python core2=gdb.selected_inferior().corefile" \
+ "capture gdb.Corefile object"
+
+ # The _my_attribute was added to CORE1, not CORE2. Check it
+ # doesn't somehow appear on CORE2.
+ gdb_test "python print(core2._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+ gdb_test "inferior 1"
+}
+
+# Read the name of the core file from the second program space while
+# the current program space is the first one.
+gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works from different progspace"
+
+# Load the other corefile into the first inferior.
+gdb_test "core $other_corefile" ".*" \
+ "load other corefile into inferior 1"
+
+# Delete the second inferior. We need to switch to the second
+# inferior and unload its corefile before we can do that. Then,
+# switch back to the first inferior, delete the second, and try to
+# read the filename of the core file from the (now deleted) second
+# inferior. We should get an error about the gdb.Corefile being
+# invalid.
+with_test_prefix "remove second inferior" {
+ gdb_test "inferior 2"
+
+ gdb_test "python print(inf.corefile.filename)" \
+ "^[string_to_regexp $other_corefile]" \
+ "read inferior 1 corefile when in inferior 2"
+
+ gdb_test_no_output "python core1=inf.corefile" \
+ "capture inferior 1 gdb.Corefile while in inferior 2"
+
+ # This is a new CORE1 object, check that _my_attribute is gone.
+ gdb_test "python print(core1._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "core-file"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception : Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+ gdb_test "inferior 1"
+
+ gdb_test "remove-inferiors 2"
+
+ gdb_test "python print(core2.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception : Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename of an invalid Corefile, from deleted program space"
+
+ gdb_test "python print(core1.is_valid())" "^True" \
+ "check inferior 1 core file is still valid"
+}