gdb: startup commands to control Python extension language
authorAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 27 Aug 2020 15:53:13 +0000 (16:53 +0100)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Wed, 28 Apr 2021 08:56:22 +0000 (09:56 +0100)
Add two new commands to GDB that can be placed into the early
initialization to control how Python starts up.  The new options are:

  set python ignore-environment on|off
  set python dont-write-bytecode auto|on|off

  show python ignore-environment
  show python dont-write-bytecode

These can be used from GDB's startup file to control how the Python
extension language behaves.  These options are equivalent to the -E
and -B flags to python respectively, their descriptions from the
Python man page:

  -E     Ignore environment variables like PYTHONPATH and PYTHONHOME
         that modify the  behavior  of  the  interpreter.

  -B     Don't write .pyc files on import.

gdb/ChangeLog:

* NEWS: Mention new commands.
* python/python.c (python_ignore_environment): New static global.
(show_python_ignore_environment): New function.
(set_python_ignore_environment): New function.
(python_dont_write_bytecode): New static global.
(show_python_dont_write_bytecode): New function.
(set_python_dont_write_bytecode): New function.
(_initialize_python): Register new commands.

gdb/doc/ChangeLog:

* python.texinfo (Python Commands): Mention new commands.

gdb/testsuite/ChangeLog:

* gdb.python/py-startup-opt.exp: New file.

gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/python.texi
gdb/python/python.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-startup-opt.exp [new file with mode: 0644]

index 4a4fa6300dd8f821d9b8a6696e29a9bc3d857391..3d266ea96bb4b5ec76b1c9c8c0427e7c98d7cd5f 100644 (file)
@@ -1,3 +1,14 @@
+2021-04-28  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * NEWS: Mention new commands.
+       * python/python.c (python_ignore_environment): New static global.
+       (show_python_ignore_environment): New function.
+       (set_python_ignore_environment): New function.
+       (python_dont_write_bytecode): New static global.
+       (show_python_dont_write_bytecode): New function.
+       (set_python_dont_write_bytecode): New function.
+       (_initialize_python): Register new commands.
+
 2021-04-28  Andrew Burgess  <andrew.burgess@embecosm.com>
 
        * extension-priv.h (struct extension_language_ops): Rename
index 1cdf19c09a01ad00a09e2ab3a31e330a876b0459..6550ea352ac53e57a69f7e1408efd44ce71b3f6d 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -106,6 +106,22 @@ show print type hex
   When 'on', the 'ptype' command uses hexadecimal notation to print sizes
   and offsets of struct members.  When 'off', decimal notation is used.
 
+set python ignore-environment on|off
+show python ignore-environment
+  When 'on', this causes GDB's builtin Python to ignore any
+  environment variables that would otherwise effect how Python
+  behaves.  This command needs to be added to an early initialization
+  file (e.g. ~/.config/gdb/gdbearlyinit) in order to affect GDB.
+
+set python dont-write-bytecode auto|on|off
+show python dont-write-bytecode
+  When 'on', this causes GDB's builtin Python to not write any
+  byte-code (.pyc files) to disk.  This command needs to be added to
+  an early initialization file (e.g. ~/.config/gdb/gdbearlyinit) in
+  order to affect GDB.  When 'off' byte-code will always be written.
+  When set to 'auto' (the default) Python will check the
+  PYTHONDONTWRITEBYTECODE. environment variable.
+
 * Changed commands
 
 break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM]
index c22d4a6a6fecba22aeb15b590526937bc8861c2b..e00d0cfb4a1c4c22a3e32ba58e2321e15986bd74 100644 (file)
@@ -1,3 +1,7 @@
+2021-04-28  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * python.texinfo (Python Commands): Mention new commands.
+
 2021-04-25  Lancelot Six  <lsix@lancelotsix.com>
 
        PR gdb/22640
index 9135d415dd110aece4875c37ce43b0d43fad9aa6..20d65742964dfd5e2ae07c25b2c1598960d5e7b3 100644 (file)
@@ -103,6 +103,44 @@ controlled using @code{set python print-stack}: if @code{full}, then
 full Python stack printing is enabled; if @code{none}, then Python stack
 and message printing is disabled; if @code{message}, the default, only
 the message component of the error is printed.
+
+@kindex set python ignore-environment
+@item set python ignore-environment @r{[}on@r{|}off@r{]}
+By default this option is @samp{off}, and, when @value{GDBN}
+initializes its internal Python interpreter, the Python interpreter
+will check the environment for variables that will effect how it
+behaves, for example @env{PYTHONHOME}, and
+@env{PYTHONPATH}@footnote{See the ENVIRONMENT VARIABLES section of
+@command{man 1 python} for a comprehensive list.}.
+
+If this option is set to @samp{on} before Python is initialized then
+Python will ignore all such environment variables.  As Python is
+initialized early during @value{GDBN}'s startup process, then this
+option must be placed into the early initialization file
+(@pxref{Initialization Files}) to have the desired effect.
+
+This option is equivalent to passing @option{-E} to the real
+@command{python} executable.
+
+@kindex set python dont-write-bytecode
+@item set python dont-write-bytecode @r{[}auto@r{|}on@r{|}off@r{]}
+When this option is @samp{off}, then, once @value{GDBN} has
+initialized the Python interpreter, the interpreter will byte-compile
+any Python modules that it imports and write the byte code to disk in
+@file{.pyc} files.
+
+If this option is set to @samp{on} before Python is initialized then
+Python will no longer write the byte code to disk.  As Python is
+initialized early during @value{GDBN}'s startup process, then this
+option must be placed into the early initialization file
+(@pxref{Initialization Files}) to have the desired effect.
+
+By default this option is set to @samp{auto}, in this mode Python will
+check the environment variable @env{PYTHONDONTWRITEBYTECODE} to see
+if it should write out byte-code or not.
+
+This option is equivalent to passing @option{-B} to the real
+@command{python} executable.
 @end table
 
 It is also possible to execute a Python script from the @value{GDBN}
index 1d0d86d5c49d7395cdbbdf0b331379be54bfa465..c46d68b73ed56f50303fe1162cd384ad5a8e68f2 100644 (file)
@@ -1578,6 +1578,80 @@ python_command (const char *arg, int from_tty)
 
 #endif /* HAVE_PYTHON */
 
+/* When this is turned on before Python is initialised then Python will
+   ignore any environment variables related to Python.  This is equivalent
+   to passing `-E' to the python program.  */
+static bool python_ignore_environment = false;
+
+/* Implement 'show python ignore-environment'.  */
+
+static void
+show_python_ignore_environment (struct ui_file *file, int from_tty,
+                               struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Python's ignore-environment setting is %s.\n"),
+                   value);
+}
+
+/* Implement 'set python ignore-environment'.  This sets Python's internal
+   flag no matter when the command is issued, however, if this is used
+   after Py_Initialize has been called then most of the environment will
+   already have been read.  */
+
+static void
+set_python_ignore_environment (const char *args, int from_tty,
+                              struct cmd_list_element *c)
+{
+#ifdef HAVE_PYTHON
+  Py_IgnoreEnvironmentFlag = python_ignore_environment ? 1 : 0;
+#endif
+}
+
+/* When this is turned on before Python is initialised then Python will
+   not write `.pyc' files on import of a module.  */
+static enum auto_boolean python_dont_write_bytecode = AUTO_BOOLEAN_AUTO;
+
+/* Implement 'show python dont-write-bytecode'.  */
+
+static void
+show_python_dont_write_bytecode (struct ui_file *file, int from_tty,
+                                struct cmd_list_element *c, const char *value)
+{
+  if (python_dont_write_bytecode == AUTO_BOOLEAN_AUTO)
+    {
+      const char *auto_string
+       = (python_ignore_environment
+          || getenv ("PYTHONDONTWRITEBYTECODE") == nullptr) ? "off" : "on";
+
+      fprintf_filtered (file,
+                       _("Python's dont-write-bytecode setting is %s (currently %s).\n"),
+                       value, auto_string);
+    }
+  else
+    fprintf_filtered (file, _("Python's dont-write-bytecode setting is %s.\n"),
+                     value);
+}
+
+/* Implement 'set python dont-write-bytecode'.  This sets Python's internal
+   flag no matter when the command is issued, however, if this is used
+   after Py_Initialize has been called then many modules could already
+   have been imported and their byte code written out.  */
+
+static void
+set_python_dont_write_bytecode (const char *args, int from_tty,
+                               struct cmd_list_element *c)
+{
+#ifdef HAVE_PYTHON
+  if (python_dont_write_bytecode == AUTO_BOOLEAN_AUTO)
+    Py_DontWriteBytecodeFlag
+      = (!python_ignore_environment
+        && getenv ("PYTHONDONTWRITEBYTECODE") != nullptr) ? 1 : 0;
+  else
+    Py_DontWriteBytecodeFlag
+      = python_dont_write_bytecode == AUTO_BOOLEAN_TRUE ? 1 : 0;
+#endif /* HAVE_PYTHON */
+}
+
 \f
 
 /* Lists for 'set python' commands.  */
@@ -1880,6 +1954,30 @@ message == an error message without a stack will be printed."),
                        NULL, NULL,
                        &user_set_python_list,
                        &user_show_python_list);
+
+  add_setshow_boolean_cmd ("ignore-environment", no_class,
+                          &python_ignore_environment, _("\
+Set whether the Python interpreter should ignore environment variables."), _(" \
+Show whether the Python interpreter showlist ignore environment variables."), _(" \
+When enabled GDB's Python interpreter will ignore any Python related\n \
+flags in the environment.  This is equivalent to passing `-E' to a\n   \
+python executable."),
+                          set_python_ignore_environment,
+                          show_python_ignore_environment,
+                          &user_set_python_list,
+                          &user_show_python_list);
+
+  add_setshow_auto_boolean_cmd ("dont-write-bytecode", no_class,
+                               &python_dont_write_bytecode, _("\
+Set whether the Python interpreter should ignore environment variables."), _(" \
+Show whether the Python interpreter showlist ignore environment variables."), _(" \
+When enabled GDB's Python interpreter will ignore any Python related\n \
+flags in the environment.  This is equivalent to passing `-E' to a\n   \
+python executable."),
+                               set_python_dont_write_bytecode,
+                               show_python_dont_write_bytecode,
+                               &user_set_python_list,
+                               &user_show_python_list);
 }
 
 #ifdef HAVE_PYTHON
index d4e02d1539f5584f743c93b58633e3bbc1677ace..d2ed989b17b1cc4c79ab8bbd9bf5042ff29a7f94 100644 (file)
@@ -1,3 +1,7 @@
+2021-04-28  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * gdb.python/py-startup-opt.exp: New file.
+
 2021-04-27  Luis Machado  <luis.machado@linaro.org>
 
        * gdb.base/maint.exp: Drop a pattern that is not needed.
diff --git a/gdb/testsuite/gdb.python/py-startup-opt.exp b/gdb/testsuite/gdb.python/py-startup-opt.exp
new file mode 100644 (file)
index 0000000..842add3
--- /dev/null
@@ -0,0 +1,118 @@
+# Copyright 2021 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 <http://www.gnu.org/licenses/>.
+
+# Test the flags within GDB that can be used to control how Python is
+# initialized.
+
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+gdb_exit
+
+# Return a list containing two directory paths for newly created home
+# directories.
+#
+# The first directory is a HOME style home directory, it contains a
+# .gdbearlyinit file containing CONTENT.
+#
+# The second directory is an XDG_CONFIG_HOME style home directory, it
+# contains a sub-directory gdb/, inside which is a file gdbearlyinit
+# that also contains CONTENT.
+#
+# The PREFIX is used in both directory names and should be unique for
+# each call to this function.
+proc setup_home_directories { prefix content } {
+    set home_dir [standard_output_file "${prefix}-home"]
+    set xdg_home_dir [standard_output_file "${prefix}-xdg"]
+
+    file mkdir $home_dir
+    file mkdir "$xdg_home_dir/gdb"
+
+    # Write the content into the HOME directory.
+    set fd [open "$home_dir/.gdbearlyinit" w]
+    puts $fd $content
+    close $fd
+
+    # Copy this from the HOME directory into the XDG_CONFIG_HOME
+    # directory.
+    file copy -force "$home_dir/.gdbearlyinit" "$xdg_home_dir/gdb/gdbearlyinit"
+
+    return [list $home_dir $xdg_home_dir]
+}
+
+# Start GDB and check the status of the Python system flags that we
+# can control from within GDB.
+proc test_python_settings { exp_state } {
+    gdb_start
+
+    gdb_test_no_output "python import sys"
+
+    foreach_with_prefix attr {ignore_environment dont_write_bytecode} {
+       gdb_test_multiline "testname" \
+           "python" "" \
+           "if hasattr(sys, 'flags') and getattr(sys.flags, '${attr}', False):" "" \
+           "  print (\"${attr} is on\")" "" \
+           "else:" "" \
+           "  print (\"${attr} is off\")" "" \
+           "end" "${attr} is ${exp_state}"
+    }
+
+    gdb_exit
+}
+
+save_vars { env(TERM) } {
+    # We need an ANSI-capable terminal to get the output.
+    setenv TERM ansi
+
+    # Check the features are off by default.
+    test_python_settings "off"
+
+    # Create an empty directory we can use as HOME for some of the
+    # tests below.  When we set XDG_CONFIG_HOME we still need to point
+    # HOME at something otherwise GDB complains that it doesn't know
+    # where to create the index cache.
+    set empty_home_dir [standard_output_file fake-empty-home]
+
+    # Create two directories to use for the style setting test.
+    set dirs [setup_home_directories "style" \
+                 [multi_line_input \
+                      "set python dont-write-bytecode on" \
+                      "set python ignore-environment on"]]
+    set home_dir [lindex $dirs 0]
+    set xdg_home_dir [lindex $dirs 1]
+
+    # Now arrange to use the fake home directory early init file.
+    save_vars { INTERNAL_GDBFLAGS env(HOME) env(XDG_CONFIG_HOME) } {
+       set INTERNAL_GDBFLAGS [string map {"-nx" ""} $INTERNAL_GDBFLAGS]
+
+       with_test_prefix "using HOME config" {
+           # Now test GDB when using the HOME directory.
+           set env(HOME) $home_dir
+           unset -nocomplain env(XDG_CONFIG_HOME)
+           test_python_settings "on"
+       }
+
+       with_test_prefix "using XDG_CONFIG_HOME config" {
+           # Now test using the XDG_CONFIG_HOME folder.  We still need to
+           # have a HOME directory set otherwise GDB will issue an error
+           # about not knowing where to place the index cache.
+           set env(XDG_CONFIG_HOME) $xdg_home_dir
+           set env(HOME) $empty_home_dir
+           test_python_settings "on"
+       }
+    }
+}