Open Ryanb58 opened 8 years ago
i am curious too, what i have been doing is taking the sources out of
~/.ptpython/history
@Duality4Y I like what you have been doing but it isn't very optimal. Plus then you have to manually remove the +
symbol from each line.. :/
I believe adding a shortcut to the menu that would allow us to save the code typed in during the current session to a file would meet my specific expectations. What do you think?
yes, that would be pretty nice and handy.
By the way, this is an easy way to remove all the additional stuff from the history and get a quick summary (I'm sure there are tons of better ones).
cat ~/.ptpython/history | sed 's/^\+//' | sed 's/^\#.*//' | tr -s '\n'
Hi all, thanks a lot for this feature request! It's definitely something useful.
And right now, there is indeed no easy way to do this. (Except for looking into ~/.ptpython/history
.)
I'll have a look at what options we have and maybe add a save button somewhere. (Last few weeks I've been really busy, so development here is slowing down a little for the moment.)
edit: Maybe I'll make the whole session input available as a string variable in the session (Similar to IPython's "In" and "Out" variables.) Then you can easily save it by typing open(filename).write(input_data)
. Or make a save function from that in your Python startup file. I'll think about it.
Any news? Thoughts?
@jonathanslenders This would be a handy feature to have. Let me know if you want me to code this up. I like your approach of storing the session input in a string variable.
Here's a quick/dirty function that chunks the history file into a vector of features:
import os
import re
from itertools import grouper
def load_ptpython_history(fp=os.path.sep.join([os.getenv('HOME'),".ptpython/history"])):
delimiter = re.compile(r'\n*\n#\s*(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.\
with open(fp, "r") as f:
raw = f.read().replace("\n+", "\n") # nix the continuation operator (a '+' sign)
return grouper(re.split(delimiter, raw)[1:], 7)
defs = list(load_ptpython_history())
Demo usage:
In [85]: defs[0]
Out[85]: ('2017', '10', '01', '13', '59', '21', 'globals().keys()')
In [86]: eval(defs[0][-1])
Out[86]: dict_keys(['__name__', '__doc__', '__package__', '__loader__',
This tries to solve the opposite problem, of "well heck, now that my session is saved to a log, how can I deserialize the previous session's contents?" It's more of a proof-of-concept, and I hope to do more with it later. In addition to fetching the previous session's contents it also suggests a possible workaround for the fact that inspect.getsourcelines
can't typically locate the source code of functions/objects defined within the same session (a perennial frustration of mine unrelated to ptpython). I'm very open to ideas where to go with it, but my dream is eventually to commit each row (at least those worth saving) to a namespaced, version-controlled database that checks for things like unscoped variables, global dependencies, etc., probably just by blocking an attempt to write if the function doesn't exec
in an empty environment. Usage might look something like
>>> from ptpython import VCM
>>> vcm = VCM(ns='math')
math
>>> vcm.knows('square')
True
>>> vcm.exec('square')
{ 'ns' : 'math',
'fname' : 'square',
'v' : '0.3',
'deps' : [],
'src' : ['def square(x):', ' return x*x+1'],
'params' : ['x'],
'tests' : [(4, 16), (-1, 1)]
}
>>> square(5)
26 # oh no
>>> vcm.rollback('square')
{ 'ns' : 'math',
'v' : '0.2',
'deps' : [],
'src' : ['def square(x):', ' return x*x'],
'fname' : 'square',
'params' : ['x'],
'tests' : [(4, 16), (-1, 1)]
}
>>> vcm.run_tests('square')
{'v' : 0.0.2,
'src' : ['def square(x):', ' return x*x'],
'ok' : True,
'passed' : True,
'source' : 'def square(x):\n return x*x',
'verbose' : ['Testing function square with source:\n'
'\n'
'def square(x):\n'
' return x*x\n'
'\n',
'=========== TEST #0/2: OK ========== \n'
" Now evaluating the expression '4'.\n"
' Args: square(4)\n'
' Expecting: 16\n'
' Got: 16\n'
' Passed: True\n'
'\n',
'=========== TEST #1/2: OK ========== \n'
" Now evaluating the expression '-1'.\n"
' Args: square(-1)\n'
' Expecting: 1\n'
' Got: 1\n'
' Passed: True\n'
'\n',
'\n\nPassed 2 out of 2 tests in 0.0001s.']}
>>> def cube(x):
return x**3
>>> vcm.add('cube')
ok
>>> vcm.commit('cube', m="maybe a commit message")
...
@jonathanslenders thanks for this wonderful tool. I'm happy to contribute these changes to the repo if you see a place for them here -- or would releasing as an optional extension to ptpython be best? Use of the tool I'm imagining would require having Postgres or mySQL installed locally, but since you're already writing to the history log on each input, I don't imagine this really needs to share state with ptpython itself... could just hang back and periodically check the log file for changes in a separate Python process.
I'm curious -- can IPython's %notebook abc.ipynb
and nbconvert --to python abc.ipynb
like functionality be used here? (or maybe as an inspiration?)
One of the major differences between ptpython and ipython in terms of history saving is the format: ipython saves history into an sqlite3 database for each session (which has the advantage of being able to keep several simultaneous sessions separate) -- as of now, ptpython
mingles the history from two different sessions into the history log file. Not saying that sqlite3 is the only way to keep this separate but it does have some advantages.
I'm very impressed so far with how much more responsive ptpython
is compared to ipython
(especially for completion) ...
I was happy to get a bump notification for this. I've made lots of tweaks to ptpython since my last post, enough that my local fork is a bit of a mess. I did manage to implement a session-based history serializer at one point, but I seem to recall there was a big update a few years ago which borked my crude hacks and I had to revert to master.
I've tinkered with the code quite a bit over the years, and have a slightly better understanding of the challenges. Some of the things that make ptpython awesome, like its support for iPython macros and shell commands, mean that not every valid REPL entry is valid Python. There's also, of course, magic methods like quit()
which need to be erased before that file can be evaluated. My biggest fear is the disturbingly high possibility that calling eval/exec on some previous REPL session's transcript might cause horrific side effects, like filesystem modification, owing to the fact that I use ptpython as a replacement shell a lot. I think that this can probably be prevented by restoring previous sessions from globals()
, but I'm not sure that I could ever be 100% certain that a restoring from a previous state wouldn't cause nasty side effects.
One option I've considered, though, and could be interesting, would be to use cloudpickle
to serialize/deserialize the session environment. IIRC I did encounter a strange bug when a callable object that references globals()
is reinitialized in another ptpython process, and sort of stopped there, but it's totally possible that my elaborate config.py
is to blame for that. I'd be interested to try this again though.
Say I am writing something as I go in ptpython.. how can I go about saving the code I typed in and ran here to a file for later use?
Is this possible with ptpython?