14

Since upgrading to Python 3.4, all interactive commands are logged to ~/.python_history. I don't want Python to create or write to this file.

Creating a symlink to /dev/null does not work, Python removes the file and recreates it. The documentation suggests to delete the sys.__interactivehook__, but this also removes tab-completion. What should be done to disable writing this history file but still preserve tab-completion?

Additional details:

Lekensteyn
  • 20,173
  • 18
  • 71
  • 111
  • Have you tried chowning it to root? – goldilocks Mar 25 '14 at 15:45
  • @goldilocks I do not consider that an acceptable solution, and neither is using `chattr`. I am looking for a single (user) configuration file or environment variable. – Lekensteyn Mar 25 '14 at 15:47
  • Okay, but you already know there is no such acceptable solution since you've looked at the documentation. – goldilocks Mar 25 '14 at 15:50
  • In fact, `chmod` is the solution suggested by python devs in [this bug report](http://bugs.python.org/issue20886) from a few weeks ago, although there is also some mention of `~/.inputrc` there (there is no `man readline`, BTW, except for the library function). – goldilocks Mar 25 '14 at 15:55
  • @goldilocks Yes, I was about to link to that bug report. I don't know how to use `inputrc` though and `chmod` does not work for me either, the file gets modified anyway (Arch Linux). There exists an `info inputrc` page, but I am generally not so happy with reading info pages as they are difficult to navigate through. – Lekensteyn Mar 25 '14 at 15:56
  • I don't know much about `inputrc` usage either -- there's [a guide here](http://www.gnu.org/software/bash/manual/html_node/Readline-Init-File.html) -- but my gut tells me you won't be able to target the python shell directly that way and pitrou is playing a smoke and mirrors shell game in that report to blow it off. `chown` may be the closest you can get, since if python is run non-privileged it won't be able to circumvent it. – goldilocks Mar 25 '14 at 16:01
  • The trick we used to use to prevent creation of `core` files, before there was support for doing so in `ulimit`, was `mkdir ~/core`. Maybe `mkdir ~/.python_history` will work until the python maintainers provide a proper fix. – Mark Plotnick Mar 26 '14 at 14:40
  • @MarkPlotnick Good suggestion, unfortunately it does not work as Python 3.4 still removes the directory (changing permissions does not make a difference). – Lekensteyn Mar 26 '14 at 15:03
  • Can you add the version of `readline`, and the OS and distro you're running, to your question? I just compiled Python 3.4.0 from source on Centos 6.5 (readline 6.0.4), and python did not remove the `~/.python_history` directory I had made (and didn't create a history file). When exiting, it output the error message `Error in atexit._run_exitfuncs: IsADirectoryError: [Errno 21] Is a directory`. It's possible that different versions of `readline` are more aggressive at trying to write the history file. – Mark Plotnick Mar 26 '14 at 22:44
  • @MarkPlotnick Arch Linux has readline 6.3, it must be Python's implementation then. bash 4.3 does not delete `.bash_history` if it is a symlink. – Lekensteyn Mar 27 '14 at 09:17

8 Answers8

13

Another ~/.pythonrc solution:

import readline
readline.write_history_file = lambda *args: None
Waxrat
  • 239
  • 2
  • 3
9

As of Python 3.6, you can use readline.set_auto_history to disable this:

import readline
readline.set_auto_history(False)
Colin Watson
  • 3,590
  • 1
  • 9
  • 13
  • That's good for the Python shell, but beware: it doesn't seem to work in ipython. – z0r Sep 02 '19 at 08:58
  • This disables history entirely. Personally I'm fine with history being preserved in my interpreter session, but I just don't want my history written to disk and preserved across sessions. – jamesdlin Sep 04 '19 at 18:28
  • 2
    Doesn't work. This doesn't stop the file being saved, and instead it breaks the history during the session. In any case, Python silently turns the "feature" back on next time you run it. – Boann Dec 08 '19 at 20:51
  • This works for me, and can be applied to every session with an alias. For example: `alias python='python -i -c "import readline; readline.set_auto_history(False)"`. – Luke Davis Mar 06 '22 at 19:00
  • For those saying this doesn't work, it's supposed to be added to `~/.pythonrc` so it runs before every session. – MestreLion Jun 01 '22 at 09:48
6

This works for me.

Creating ~/.pythonrc file:

import os
import atexit
import readline

readline_history_file = os.path.join(os.path.expanduser('~'), '.python_history')
try:
    readline.read_history_file(readline_history_file)
except IOError:
    pass

readline.set_history_length(0)
atexit.register(readline.write_history_file, readline_history_file)

Then export it:

export PYTHONSTARTUP=~/.pythonrc
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • This seems to *enable* writing a history file instead of *disabling* it if I am not mistaken? Unless [`set_history_length`](http://docs.python.org/3/library/readline.html#readline.set_history_length) does something magical? Tab-completion is done for functions, not history. – Lekensteyn Mar 25 '14 at 16:52
  • @Lekensteyn: yes, it make history size equal zero so nothing is written to history file. Oh, I though you want to preserve command between sessions. – cuonglm Mar 25 '14 at 16:54
  • 2
    This still causes `~/.python_history` to be written (verified with `PYTHONSTARTUP=$HOME/.pythonrc strace -e file,write -o /tmp/st python`). I start to think that there is no way to disable this, but keep tab-completion without duplicating code from `/usr/lib/python3.4/site.py`. – Lekensteyn Mar 25 '14 at 16:58
  • AFAIK, there is no way to really disable it. – cuonglm Mar 25 '14 at 17:06
2

To prevent Python from writing ~/.python_history, disable the hook that activates this functionality:

import sys
# Disable history (...but also auto-completion :/ )
if hasattr(sys, '__interactivehook__'):
    del sys.__interactivehook__

If you would like to enable tab-completion and disable the history feature, you can adapt the site.enablerlcompleter code. Write the following code to ~/.pythonrc and set export PYTHONSTARTUP=~/.pythonrc in your shell to enable it.

import sys
def register_readline_completion():
    # rlcompleter must be loaded for Python-specific completion
    try: import readline, rlcompleter
    except ImportError: return
    # Enable tab-completion
    readline_doc = getattr(readline, '__doc__', '')
    if readline_doc is not None and 'libedit' in readline_doc:
        readline.parse_and_bind('bind ^I rl_complete')
    else:
        readline.parse_and_bind('tab: complete')
sys.__interactivehook__ = register_readline_completion
Lekensteyn
  • 20,173
  • 18
  • 71
  • 111
  • This does the job. I wonder why they're not using `${XDG_STATE_HOME}/python/history` for this purpose, though. – mazunki Sep 27 '21 at 01:02
  • Any idea where the history filename path (`~/.python_history`) is stored? – not2qubit Jan 24 '22 at 01:12
  • 1
    See the `site.enablerlcompleter` link in my answer. It is hard-coded there. On Python 3.10.2 on Arch Linux, it can be found on line 475 of `/usr/lib/python3.10/site.py`. – Lekensteyn Feb 01 '22 at 22:28
2

My current solution (for recent enough versions of Python 3) prevents default use of ~/.python_history but leaves the possibility of explicit writing of the history to a given file (using readline.write_history_file(filename) or readline.append_history_file(...)) is to have the following in one's PYTHONSTARTUP file:

import readline
import time

readline.add_history("# " + time.asctime()) # prevent default use of ~/.python_history
readline.set_history_length(-1) # unlimited

It has the pleasant (for me) side effect labeling any explicitly written history with the startup time of the interpreter. It works because of the fix for bug 5845 which can be seen here.

Ron Kaminsky
  • 131
  • 3
  • History length of `-1` (i.e. unlimited) is already the default (verify with `readline.get_history_length()`), so that line is optional. – MestreLion Jun 01 '22 at 12:23
1

Until it's fixed in some way in Python itself you can do this on UNIX systems:

rm ~/.python-history
mkdir ~/.python-history
sudo chattr +i ~/.python-history || sudo chflags simmutable ~/.python-history

After that you will be getting

Error in atexit._run_exitfuncs:

IsADirectoryError: [Errno 21] Is a directory

every time you terminate a python shell. Exit status will still be 0.

Notice that if you leave it as file you need to create and make immutable another file, ~/.python_history

int_ua
  • 243
  • 1
  • 7
0

Put the following in a file and set PYTHONSTARTUP to it (or call the file sitecustomize.py and make it accessible from the PYTHONPATH)

import readline
import atexit
import sys

sys.__interactivehook__()
atexit.unregister(readline.write_history_file)

This way you'll still have access to tab-completion and previous history, but the commands you enter won't be added to the history file.

berdario
  • 162
  • 9
  • This does not seem to work (Python 3.5.1 on Arch Linux), the history file is still written on exit even through the `PYTHONSTARTUP` script is indeed executed. – Lekensteyn Feb 09 '16 at 14:39
  • I just tested it on my Arch Linux vm, and it works fine: https://gist.github.com/berdario/640b3ab00b128fdf3338 – berdario Feb 10 '16 at 00:54
0

Here's my method (which turned out to be basically a more reliable and pythonic version of berdario's method). It only disables writing to .python_history, but not reading from it if it exists, or appending new lines to the current instance's history. I recommend saving it as site-packages/sitecustomize.py, because site is the module that both writes to .python_history and imports sitecustomize if it exists, although naming it something else and pointing to it with PYTHONSTARTUP works too.

import sys
oldhook = getattr(sys, '__interactivehook__', None)
if oldhook:
    def newhook():
        import readline, atexit
        oldhook()
        atexit.unregister(readline.write_history_file)
    sys.__interactivehook__ = newhook

update: what i did for 3.7, specific to my system, not pep8:

import rlcompleter,readline as r,sys
def f():r.parse_and_bind('tab:complete');r.read_init_file()
sys.__interactivehook__=f
landfill baby
  • 93
  • 1
  • 6