Skip to content

Commit 6ceeb32

Browse files
committed
Add tests, docs, and implementation for #133307
1 parent 308ceff commit 6ceeb32

File tree

3 files changed

+85
-26
lines changed

3 files changed

+85
-26
lines changed

Doc/library/pdb.rst

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,20 @@ There are four preset *convenience variables*:
378378
pair: .pdbrc; file
379379
triple: debugger; configuration; file
380380

381-
If a file :file:`.pdbrc` exists in the user's home directory or in the current
382-
directory, it is read with ``'utf-8'`` encoding and executed as if it had been
383-
typed at the debugger prompt, with the exception that empty lines and lines
384-
starting with ``#`` are ignored. This is particularly useful for aliases. If both
385-
files exist, the one in the home directory is read first and aliases defined there
386-
can be overridden by the local file.
381+
If a file :file:`.pdbrc` exists in any of the supported locations, it is read
382+
with ``'utf-8'`` encoding and executed as if it had been typed at the debugger
383+
prompt, with the exception that empty lines and lines starting with ``#`` are
384+
ignored. This is particularly useful for aliases. Supported locations for
385+
:file:`.pdbrc` file are
386+
387+
1. ``$XDG_CONFIG_HOME/pdb`` (defaults to ``~/.config/pdb``, if environment
388+
variable :envvar:`XDG_CONFIG_HOME` is not set)
389+
2. User's home directory
390+
3. Current working directory
391+
392+
If :file:`.pdbrc` files exist in multiple locations, they are processed in
393+
order, so that ``$XDG_CONFIG_HOME/pdb/.pdbrc`` is loaded first, and extended
394+
by :file:`.pdbrc` files in user's home and current directory.
387395

388396
.. versionchanged:: 3.2
389397
:file:`.pdbrc` can now contain commands that continue debugging, such as
@@ -394,6 +402,10 @@ can be overridden by the local file.
394402
:file:`.pdbrc` is now read with ``'utf-8'`` encoding. Previously, it was read
395403
with the system locale encoding.
396404

405+
.. versionadded:: 3.15
406+
:file:`.pdbrc` is now searched in ``$XDG_CONFIG_HOME/pdb`` directory first.
407+
Previously, this location was ignored.
408+
397409

398410
.. pdbcommand:: h(elp) [command]
399411

Lib/pdb.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,24 @@
4646
input is split at the first ';;', even if it is in the middle of a
4747
quoted string.
4848
49-
If a file ".pdbrc" exists in your home directory or in the current
50-
directory, it is read in and executed as if it had been typed at the
51-
debugger prompt. This is particularly useful for aliases. If both
52-
files exist, the one in the home directory is read first and aliases
53-
defined there can be overridden by the local file. This behavior can be
54-
disabled by passing the "readrc=False" argument to the Pdb constructor.
49+
If a '.pdbrc' exists in any of the supported locations, it is read with 'utf-8'
50+
encoding and executed as if it had been typed at the debugger prompt, with the
51+
exception that empty lines and lines starting with '#' are ignored. This is
52+
particularly useful for aliases.
53+
54+
This behavior can be disabled by passing the 'readrc=False' argument to the Pdb
55+
constructor.
56+
57+
Supported locations for '.pdbrc' file are
58+
59+
1. '$XDG_CONFIG_HOME/pdb' (defaults to '~/.config/pdb', if environment variable
60+
$XDG_CONFIG_HOME is not set)
61+
2. User's home directory
62+
3. Current working directory
63+
64+
If '.pdbrc' files exist in multiple locations, they are processed in order, so
65+
that '$XDG_CONFIG_HOME/pdb/.pdbrc' is loaded first, and extended by '.pdbrc'
66+
files in user's home and current directory.
5567
5668
Aside from aliases, the debugger is not directly programmable; but it
5769
is implemented as a class from which you can derive your own debugger
@@ -98,7 +110,7 @@
98110
import _colorize
99111
import _pyrepl.utils
100112

101-
from contextlib import ExitStack, closing, contextmanager
113+
from contextlib import ExitStack, closing, contextmanager, suppress
102114
from rlcompleter import Completer
103115
from types import CodeType
104116
from warnings import deprecated
@@ -369,19 +381,19 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
369381
# c.a or c['a'], it won't be recognized as a c(ontinue) command
370382
self.identchars = cmd.Cmd.identchars + '=.[](),"\'+-*/%@&|<>~^'
371383

372-
# Read ~/.pdbrc and ./.pdbrc
384+
# Read $XDG_CONFIG_HOME/pdb/.pdbrc, ~/.pdbrc, and ./.pdbrc
373385
self.rcLines = []
374386
if readrc:
375-
try:
376-
with open(os.path.expanduser('~/.pdbrc'), encoding='utf-8') as rcFile:
377-
self.rcLines.extend(rcFile)
378-
except OSError:
379-
pass
380-
try:
381-
with open(".pdbrc", encoding='utf-8') as rcFile:
382-
self.rcLines.extend(rcFile)
383-
except OSError:
384-
pass
387+
# $XDG_CONFIG_HOME defaults to $HOME/.config
388+
config_home = os.environ.get('XDG_CONFIG_HOME', '~/.config')
389+
for rc_path in (
390+
os.path.expanduser(os.path.join(config_home, 'pdb', '.pdbrc')),
391+
os.path.expanduser('~/.pdbrc'),
392+
'.pdbrc',
393+
):
394+
with suppress(OSError):
395+
with open(rc_path, encoding='utf-8') as rcFile:
396+
self.rcLines.extend(rcFile)
385397

386398
self.commands = {} # associates a command list to breakpoint numbers
387399
self.commands_defining = False # True while in the process of defining
@@ -3492,8 +3504,8 @@ def help():
34923504
an executable module or package to debug can be specified using
34933505
the -m switch.
34943506
3495-
Initial commands are read from .pdbrc files in your home directory
3496-
and in the current directory, if they exist. Commands supplied with
3507+
Initial commands are read from .pdbrc files in "$XDG_CONFIG_HOME/pdb", your home
3508+
directory, and in the current directory, if they exist. Commands supplied with
34973509
-c are executed after commands from .pdbrc files.
34983510
34993511
To let the script run until an exception occurs, use "-c continue".

Lib/test/test_pdb.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,6 +3935,41 @@ def test_readrc_homedir(self):
39353935
f.write("invalid")
39363936
self.assertEqual(pdb.Pdb().rcLines[0], "invalid")
39373937

3938+
def test_pdbrc_xdg_config_home(self):
3939+
with (
3940+
os_helper.temp_dir() as temp_dir,
3941+
os_helper.change_cwd(temp_dir),
3942+
os_helper.EnvironmentVarGuard() as env,
3943+
patch(
3944+
'os.path.expanduser',
3945+
lambda p: p.replace('~', os.environ.get('HOME', '~')),
3946+
),
3947+
):
3948+
for rc_dir, rcLines in (
3949+
(os.path.join(temp_dir, 'xdg', '.config', 'pdb'), '#xdgconf'),
3950+
(os.path.join(temp_dir, 'home', '.config', 'pdb'), '#homeconf'),
3951+
(os.path.join(temp_dir, 'home'), '#home'),
3952+
(temp_dir, '#cwd'),
3953+
):
3954+
if not os.path.exists(rc_dir):
3955+
os.makedirs(rc_dir)
3956+
with open(os.path.join(rc_dir, '.pdbrc'), 'w') as f:
3957+
f.write(rcLines)
3958+
3959+
env.unset('HOME')
3960+
env.unset('XDG_CONFIG_HOME')
3961+
3962+
# when both XDG_CONFIG_HOME and $HOME are unset, ./.pdbrc is still loaded
3963+
self.assertListEqual(pdb.Pdb().rcLines, ['#cwd'])
3964+
# # when XDG_CONFIG_HOME is unset, it defaults to $HOME/.config
3965+
env.set('HOME', os.path.join(temp_dir, 'home'))
3966+
self.assertListEqual(pdb.Pdb().rcLines, ['#homeconf', '#home', '#cwd'])
3967+
# when XDG_CONFIG_HOME is set, .pdbrc is loaded from there
3968+
env.set('XDG_CONFIG_HOME', os.path.join(temp_dir, 'xdg', '.config'))
3969+
self.assertListEqual(pdb.Pdb().rcLines, ['#xdgconf', '#home', '#cwd'])
3970+
# when readrc=False, .pdbrc is not loaded from any location
3971+
self.assertListEqual(pdb.Pdb(readrc=False).rcLines, [])
3972+
39383973
def test_header(self):
39393974
stdout = StringIO()
39403975
header = 'Nobody expects... blah, blah, blah'

0 commit comments

Comments
 (0)