Skip to content

gh-88405: Use platform defined data directories instead of ~/.python_history #26377

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
16 changes: 13 additions & 3 deletions Doc/library/site.rst
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,25 @@ Readline configuration
On systems that support :mod:`readline`, this module will also import and
configure the :mod:`rlcompleter` module, if Python is started in
:ref:`interactive mode <tut-interactive>` and without the :option:`-S` option.
The default behavior is enable tab-completion and to use
:file:`~/.python_history` as the history save file. To disable it, delete (or
override) the :data:`sys.__interactivehook__` attribute in your
The default behavior is enable tab-completion and to use history file from
platform defined directory.
For Windows, it is :file:`%APPDATA%\\Python\\history`;
for Mac OS, it is :file:`~/Library/Application Support/Python/history`;
for other POSIX platforms, it is :file:`$XDG_STATE_HOME/python/history` or
:file:`~/.local/state/python/history`;
otherwise, it is :file:`~/.python_history`.
Note that for compatibility, if :file:`~/.python_history` is readable, it will
always be taken as default and other paths are ignored. To disable it, delete
(or override) the :data:`sys.__interactivehook__` attribute in your
:mod:`sitecustomize` or :mod:`usercustomize` module or your
:envvar:`PYTHONSTARTUP` file.

.. versionchanged:: 3.4
Activation of rlcompleter and history was made automatic.

.. versionchanged:: 3.13
Prefer platform defined data directory instead of :file:`~/.python_history`.


Module contents
---------------
Expand Down
6 changes: 5 additions & 1 deletion Doc/tutorial/interactive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ the expression up to the final ``'.'`` and then suggest completions from
the attributes of the resulting object. Note that this may execute
application-defined code if an object with a :meth:`__getattr__` method
is part of the expression. The default configuration also saves your
history into a file named :file:`.python_history` in your user directory.
history into a file under platform defined data directory (
:file:`%APPDATA%\\Python\\history` for Windows;
:file:`~/Library/Application Support/Python/history` for Mac OS;
:file:`$XDG_STATE_HOME/python/history` or
:file:`~/.local/state/python/history` for other POSIX platforms like Linux).
The history will be available again during the next interactive interpreter
session.

Expand Down
36 changes: 32 additions & 4 deletions Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def enablerlcompleter():
registering a sys.__interactivehook__.

If the readline module can be imported, the hook will set the Tab key
as completion key and register ~/.python_history as history file.
as completion key and register history file.
This can be overridden in the sitecustomize or usercustomize module,
or in a PYTHONSTARTUP file.
"""
Expand Down Expand Up @@ -460,14 +460,18 @@ def register_readline():
pass

if readline.get_current_history_length() == 0:
# If no history was loaded, default to .python_history.
# If no history was loaded, load history file from
# platform defined directories.
# The guard is necessary to avoid doubling history size at
# each interpreter exit when readline was already configured
# through a PYTHONSTARTUP hook, see:
# http://bugs.python.org/issue5845#msg198636
history = os.path.join(os.path.expanduser('~'),
'.python_history')

history = get_readline_history_path()
history = os.path.abspath(history)
try:
_dir, _ = os.path.split(history)
os.makedirs(_dir, exist_ok=True)
readline.read_history_file(history)
except OSError:
pass
Expand All @@ -482,6 +486,30 @@ def write_history():

atexit.register(write_history)

def get_readline_history_path():
def joinuser(*args):
return os.path.expanduser(os.path.join(*args))

# If the legacy path "~/.python_history" is readable, always use it.
legacy = joinuser('~', '.python_history')
if os.access(legacy, os.R_OK):
return legacy

# Otherwise, use platform defined data directory.
if os.name == 'nt':
base = os.environ.get('APPDATA') or '~'
return joinuser(base, 'Python', 'history')

if sys.platform == 'darwin':
return joinuser('~', 'Library', 'Application Support', 'Python', 'history')

if os.name == 'posix':
base = os.environ.get('XDG_STATE_HOME') or joinuser('~', '.local', 'state')
return joinuser(base, 'python', 'history')

# Unknown platform, use the legacy path.
return legacy

sys.__interactivehook__ = register_readline

def venv(known_paths):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use platform defined data directories instead of :file:`~/.python_history`