almenon / arepl-electron Goto Github PK
View Code? Open in Web Editor NEWpython scratchpad with real-time code evaluation
License: MIT License
python scratchpad with real-time code evaluation
License: MIT License
When an error appears there something should appear on the corresponding line number. For example, an error icon in the gutter.
When using AREPL I frequently do wrap expressions in print statements to see the value of them. Instead of doing this it would be nice if there was a shortcut that would automatically evaluate the expression and print it. For example, in FoxDot you do control-enter to evaluate the line and print the output to the console.
I could extend this idea even further. Wrapping a line that ends in a . with print(dir()) could act as a primitive form of intellisense. Or maybe doing alt-enter could wrap the line in help().
When anything is printed the last character is left off.
print('hello world')
hello worl
Add new submenu item for auto-download of update. Change old submenu item to "release notes". Old submenu item should link back to the releases page for release notes. If you have time use the releaseNotes returned by auto-updater to show it locally.
In AREPL json.decode fails to decode a simple pickled enum. In Pycharm the decoding happens without any error. The reason behind this is that AREPL uses exec to execute the user's code. In the example below you can see the exact same code that works outside of exec() fails inside exec().
from enum import Enum
from jsonpickle import encode, decode
class C(Enum):
VALUE = 1
my_list_encode = encode(C.VALUE)
decode(my_list_encode) # no error
execCode = """ # exact same code as above follows!
from enum import Enum
from jsonpickle import encode, decode
class C(Enum):
VALUE = 1
my_list_encode = encode(C.VALUE)
decode(my_list_encode)
"""
execLocals = {}
exec(execCode,execLocals) # error!
Traceback (most recent call last):
File "C:/dev/random python scripts/misc.py", line 24, in
exec(execCode,execLocals)
File "", line 10, in
File "C:\dev\AREPL\src\python\jsonpickle_init_.py", line 152, in decode
return unpickler.decode(string, backend=backend, keys=keys)
File "C:\dev\AREPL\src\python\jsonpickle\unpickler.py", line 27, in decode
return context.restore(backend.decode(string), reset=reset)
File "C:\dev\AREPL\src\python\jsonpickle\unpickler.py", line 120, in restore
value = self._restore(obj)
File "C:\dev\AREPL\src\python\jsonpickle\unpickler.py", line 162, in _restore
return restore(obj)
File "C:\dev\AREPL\src\python\jsonpickle\unpickler.py", line 183, in _restore_reduce
if f == tags.NEWOBJ or f.__name__ == '__newobj__':
AttributeError: 'dict' object has no attribute '__name__'
"""
There should be a menu item in the top right saying "New update availible"
There is not. ๐ค
No clue why - when I debug it works perfectly.
It would be nice if the electron javascript did some logging somewhere.
Right now the pythonResultHandler Changes the html directly, but it should pass back the results to the app, where it could decide what to do with the results. This allows me to add in more languages as well as just being better architecture design.
see https://github.com/caldwell/renderjson
renderjson.set_max_string_length(length);
Strings will be truncated and made expandable if they are longer than
length
. As a special case, iflength
is the string"none"
then there
will be no truncation. The default is"none"
.
Python's JSON allows NaN, Infinity, and negative Infinity.
Javascript's JSON does not ๐
This puts me in a bit of a pickle.
Literally, I have to make my own pickler: ๐ฅ
class customPickler(jsonpickle.pickler.Pickler):
"""
encodes float values like inf / nan as strings to follow JSON spec while keeping meaning
Im doing this in custom class because handlers do not fire for floats
"""
inf = float('inf')
negativeInf = float('-inf')
def _get_flattener(self, obj):
if type(obj) == type(float()):
if obj == self.inf:
return lambda obj: 'Infinity'
if obj == self.negativeInf:
return lambda obj: '-Infinity'
if isnan(obj):
return lambda obj: 'NaN'
return super(customPickler, self)._get_flattener(obj)
jsonpickle.pickler.Pickler = customPickler
see https://github.com/sindresorhus/electron-store
and https://github.com/nathanbuchar/electron-settings
Provide a GUI for users to be able to edit settings
On a scale from 1-10, 1 being lowest, this has to be near a 1 on the severity list.
Still, it would be nice. This worked:
print(sys.stdout.encoding) # cp1252
import sys, codecs
w = codecs.getwriter('utf-8')
sys.stdout = w(sys.stdout.buffer)
import emoji
print(emoji.emojize(':eggplant:'))
but then certain accents (like รก) started printing malformed: รยก
I really hate encodings. Why can't everyone just talk in esperanto???
Process: arepl [55016]
Path: /Users/USER/Downloads/*/arepl.app/Contents/MacOS/arepl
Identifier: com.electron.arepl
Version: ???
Code Type: X86-64 (Native)
Responsible: arepl [55016]
OS Version: Mac OS X 10.11.6 (15G1611)
Crashed Thread: 0
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000002, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Application Specific Information:
dyld: launch, loading dependent libraries
Dyld Error Message:
Library not loaded: @rpath/libnode.dylib
Referenced from: /Users/anon/Downloads/arepl-darwin-x64/arepl.app/Contents/Frameworks/Electron Framework.framework/Electron Framework
Reason: image not found
regex match objects display as "py/object": "_sre.SRE_Match"
It would be better if they displayed actually useful information.
Breakpoint line style is only applied when you create a new line after the last line, but not if you create a new line in the middle of the breakpoint section
Not sure why this is the case. I do label: require("electron").app.getName() and I have "arepl" as the name in package.json so the first item in the file menu should be arepl.
If unit tests are not automated then there is a dangerous likelyhood of the dev forgetting to run them, in which case the unit tests are pointless. I already have the python unit tests automated with travis, it would be easy to run the JS tests as well.
I used app here, but without requiring electron app will be undefined, and the adaptTemplateForMac method will fail.
Instead of evaluating the entire program I can pass in the old locals into eval with the section of new code. (assuming the user is adding new code, and not modifying existing code)
Every time there is a new line I would deep copy all of the old locals. For each change to the new line I would exec the new line and pass in the old locals.
This has two benefits:
increased speed with larger programs
No re-evaluation of old code (so repeated side-effects are avoided)
The drawback is overhead associated with deep-copying all the locals every time there is a new line. I might want to do some performance tests to see what the impact of this would be.
see https://www.npmjs.com/package/electron-builder.
I already have this in the works with the auto-update branch.
It would save processing power for travis (who so kindly offer free CI for open-source projects) to skip builds if a non-python file is modified. Oddly they do not have this option yet.
see https://codemirror.net/demo/complete.html
also take a look at https://github.com/BayardRock/WebIntellisense
print('a') and press enter - two a's or more appear
Python programs which display windows (through turtle / matplotlib / other libraries) run indefinitely to maintain the window. It would be nice to have the default behavior be to restart the user's python program whenever the code is changed. Then the user would not have to manually exit the window every time.
This would also be a nice solution to the infinite loop issue. Instead of having to press a hotkey to restart it would restart automatically upon change.
gives scary message to user when opening for first time
My current approach of adding the open/save event handlers to the menu in the renderer process would not work without sharing the menu template and recreating it from scratch.
https://github.com/Almenon/AREPL/blob/734e9b4a543feb48faefdbf02e4abfd9287f2e69/src/app.js#L28
If the user presses the left arrow (or any other key on the keyboard), the code would be evaluated, despite it not having changed in the slighest. Instead I should be listening to codemirror's change or changes event, which is only fired upon a change in the document.
would be useful to know
x = locals()
โ There has been a error when trying to display your variables. Sorry :(
Traceback (most recent call last):\n File "C:\dev\AREPL\src\python\pythonEvaluator.py", line 53, in exec_input\n returnInfo['userVariables'] = jsonpickle.encode(userVariables)#, max_depth=100) # any depth above 245 resuls in error and anything above 100 takes too long to process\n File "C:\dev\AREPL\src\python\jsonpickle_init_.py", line 135, in encode\n numeric_keys=numeric_keys)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 44, in encode\n return backend.encode(context.flatten(value, reset=reset))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 157, in flatten\n return self._flatten(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 161, in _flatten\n return self._pop(self._flatten_obj(obj))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj\n return flatten_func(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 429, in _flatten_dict_obj\n flatten(k, v, data)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 498, in _flatten_key_value_pair\n data[k] = self._flatten(v)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 161, in _flatten\n return self._pop(self._flatten_obj(obj))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj\n return flatten_func(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 429, in _flatten_dict_obj\n flatten(k, v, data)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 498, in _flatten_key_value_pair\n data[k] = self._flatten(v)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 161, in _flatten\n
... a rediculous number of lines later...
return self._pop(self._flatten_obj(obj))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj\n return flatten_func(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 429, in _flatten_dict_obj\n flatten(k, v, data)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 481, in _flatten_key_value_pair\n if not util.is_picklable(k, v):\n File "C:\dev\AREPL\src\python\jsonpickle\util.py", line 309, in is_picklable\n return is_module_function(value) or not is_function(value)\n File "C:\dev\AREPL\src\python\jsonpickle\util.py", line 253, in is_function\n module = translate_module_name(obj.class.module)\n File "C:\dev\AREPL\src\python\jsonpickle\util.py", line 415, in translate_module_name\n if (PY3 and module == 'builtins') or module == 'exceptions':\nRecursionError: maximum recursion depth exceeded in comparison\n
import csv
with open('stocks.csv') as f:
x = csv.reader(f)
Traceback (most recent call last):\n File "C:\dev\AREPL\src\python\pythonEvaluator.py", line 53, in exec_input\n returnInfo['userVariables'] = jsonpickle.encode(userVariables)\n File "C:\dev\AREPL\src\python\jsonpickle_init_.py", line 135, in encode\n numeric_keys=numeric_keys)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 44, in encode\n return backend.encode(context.flatten(value, reset=reset))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 157, in flatten\n return self._flatten(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 161, in _flatten\n return self._pop(self._flatten_obj(obj))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj\n return flatten_func(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 429, in _flatten_dict_obj\n flatten(k, v, data)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 498, in _flatten_key_value_pair\n data[k] = self._flatten(v)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 161, in _flatten\n return self._pop(self._flatten_obj(obj))\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj\n return flatten_func(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 235, in _ref_obj_instance\n return self._flatten_obj_instance(obj)\n File "C:\dev\AREPL\src\python\jsonpickle\pickler.py", line 367, in _flatten_obj_instance\n data[tags.ITERATOR] = list(map(self._flatten, islice(obj, self._max_iter)))\nValueError: I/O operation on closed file.\n
It looks like JSONpickle is failing to encode x. The JSONpickle API mentions that it can't encode file descriptors so this might be related to that. Still, I would've expected x to be encoded as None rather than an unhandled exception.
events.js:163 Uncaught Error: python: can't open file
'Users/anon/Documents/AREPL\src\python\pythonEvaluator.py':
[Errno 2] No such file or directory
at PythonShell.parseError (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:190)
at terminateIfNeeded (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:98)
at Socket.<anonymous> (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:78)
at emitNone (events.js:91)
at Socket.emit (events.js:188)
at endReadableNT (_stream_readable.js:975)
at _combinedTickCallback (internal/process/next_tick.js:80)
at process._tickCallback (internal/process/next_tick.js:104)
This appears to be due to extrabacon/python-shell#38
when running the python evaluator I get the following error:
Uncaught Error: spawn python3 ENOENT
(ENOENT is not found)
But I am able to run the same exact path with python....
python3 /Users/anon/Documents/AREPL/arepl-darwin-x64/arepl.app/Contents/Resources/app/src/python/pythonEvaluator.py
Not sure what's up with that.
https://www.npmjs.com/package/electron-log
log = require("electron-log") // log level is warn by default
I already have a function to restart the python process
https://github.com/Almenon/AREPL/blob/69d1fc6a73e454e321b93958897bb0126b7dedb1/src/evaluators.js#L46
I just need to link it up to a shortcut.
A button or menu option would be nice too.
It would be more efficient to update the html with prints in batches rather than doing it for every single call.
print(input())
Type in 5 in the input box and press enter. You should see 5 appear back in output
Type in 5 again and you get an error in the console:
Uncaught Error: TypeError: 'int' object is not subscriptable
at PythonShell.parseError (C:\dev\arepl\node_modules\python-shell\index.js:183:17)
at terminateIfNeeded (C:\dev\arepl\node_modules\python-shell\index.js:98:28)
at ChildProcess. (C:\dev\arepl\node_modules\python-shell\index.js:88:9)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:194:7)
at Process.ChildProcess._handle.onexit (internal/child_process.js:215:12)
----- Python Traceback -----
File "C:\dev\arepl\src\python\pythonEvaluator.py", line 60, in
returnInfoJSON = exec_input(data['evalCode'])
If program is not running input should be ignored or (even better) the program should be restarted so the user can see the change in output with the new input
does not work:
d = {'a':5}
x = ((k,v) for (k,v) in d)
does work:
d = {'a':5}
x = ((k,v) for (k,v) in d.items())
x = [(k,v) for k,v in d.items()]
x = {k:v for k,v in d.items()}
x = ((k) for k in d)
โ There has been a error when trying to display your variables. Sorry :(
Traceback (most recent call last):
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\pythonEvaluator.py", line 77, in exec_input
returnInfo['userVariables'] = jsonpickle.encode(userVariables, max_depth=100) # any depth above 245 resuls in error and anything above 100 takes too long to process
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle_init_.py", line 135, in encode
numeric_keys=numeric_keys)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 44, in encode
return backend.encode(context.flatten(value, reset=reset))
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 157, in flatten
return self._flatten(obj)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 161, in _flatten
return self._pop(self._flatten_obj(obj))
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj
return flatten_func(obj)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 429, in _flatten_dict_obj
flatten(k, v, data)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 498, in _flatten_key_value_pair
data[k] = self._flatten(v)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 161, in _flatten
return self._pop(self._flatten_obj(obj))
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 177, in _flatten_obj
return flatten_func(obj)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 235, in _ref_obj_instance
return self._flatten_obj_instance(obj)
File "C:\Users\anon\Downloads\arepl-win32-x64\arepl-win32-x64\resources\app\src\python\jsonpickle\pickler.py", line 367, in _flatten_obj_instance
data[tags.ITERATOR] = list(map(self._flatten, islice(obj, self._max_iter)))
File "", line 2, in
ValueError: not enough values to unpack (expected 2, got 1)
print(input())
then add a # at end
Expected Result: nothing is printed
Actual result: {"evalCode":"print(input())#"}
I made the mistake of assuming pyshell.end would terminate the process. What it actually does is just close the standard input stream. When I realized this I had to kill the child directly: ๐ช
pyshell.childProcess.kill();
But the kid still wouldn't die, so I gave the kid some time to run and sent SIGKILL over to finish the job:
pyshell.childProcess.kill();
setTimeout(()=>{
dead = this.pyshell.childProcess.kill('SIGKILL');
}, 50);
But dead was false -> it was still alive! I went into the kill method and found this:
if (this._handle) {
var err = this._handle.kill(signal);
if (err === 0) {
/* Success. */
this.killed = true;
return true;
}
if (err === uv.UV_ESRCH) {
/* Already dead. */
} else if (err === uv.UV_EINVAL || err === uv.UV_ENOSYS) {
/* The underlying platform doesn't support this signal. */
throw errnoException(err, 'kill');
} else {
/* Other error, almost certainly EPERM. */
this.emit('error', errnoException(err, 'kill'));
}
}
/* Kill didn't succeed. */
return false;
this._handle was null, so if(this._handle) returned false, and the kill failed. But why was the handle null? What even is a handle?
this._handle = new Process();
So the handle is the process, but if the process was null... does that mean... the process was dead already?
๐ค
Looking back at my code, I realized that in most scenarios* the first kill would work, causing the second kill to fail because the process was already dead.
To fix this I added a simple check:
dead = pyshell.childProcess.kill();
if (!dead) pyshell.childProcess.kill('SIGKILL');
okay, everything is good to go know, right?
...right?
Wrong. I had registered a function to restart the process upon close, but the pyshell close event was not always firing! Time for some more debugging:
this.childProcess.on('exit', function (code) {
self.exitCode = code;
terminateIfNeeded();
});
function terminateIfNeeded() {
if (!self.stderrHasEnded || !self.stdoutHasEnded || self.exitCode == null) {
return;
}
// ... code continues
}
Pyshell only fires the close event if exitCode is not null, but the child_process documentation says:
The 'exit' event is emitted after the child process ends. If the process exited, code is the final exit code of the process, otherwise null. If the process terminated due to receipt of a signal, signal is the string name of the signal, otherwise null. One of the two will always be non-null.
It's worded a bit wierdly, but what it's saying is that if the process is closed due to a signal the exit code will be null. So pyshell only emits close in the opposite situation - if the process exits voluntarily. Why that is I do not know - either the pyshell code or documentation needs to be updated.
Instead of using pyshell's close event I just used the child_process close event directly:
this.pyshell.childProcess.on('exit',()=>{
this.startPython();
});
Done! ๐
In mac python refers to python 2 by default.
see https://stackoverflow.com/questions/5846167/how-to-change-default-python-version
same issue with ubuntu: https://askubuntu.com/questions/320996/how-to-make-python-program-command-execute-python-3
Uncaught Error: KeyError: '__loader__'
at PythonShell.parseError (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:183:17)
at terminateIfNeeded (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:98:28)
at ChildProcess. (/Users/anon/Documents/AREPL/node_modules/python-shell/index.js:88:9)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:194:7)
at Process.ChildProcess._handle.onexit (internal/child_process.js:215:12)
----- Python Traceback -----
File "/Users/anon/Documents/AREPL/src/python/pythonEvaluator.py", line 14, in
startingLocals[var] = locals()[var]
a program should have documentation on its usage.
x = [[i for i in range(10000000)]]
Timings in ms:
List Size | Python Time | Total Time (py & JS) |
---|---|---|
10^4 | 0 | 23 |
10^5 | 2.98 | 225 |
10^6 | 48.1 | 2363 |
10^7 | 489 | 24337 |
I'd be okay with a doubling or even tripling of the execution time but this is clearly rediculous. Only ~2% of the execution time is actually spent executing the user's code. God knows what the computer is doing the rest of the time. Sipping martinis on the beach? ๐๏ธ ๐ธ
i don't have any linux OS to test with
Pressing F1 does nothing. The function that is supposed to be called, restartExec, is never called.
Pressing F4 brings up launchpad instead of toggling the real-time eval.
Currently I am using window.onkeydown for my shortcuts.
But instead I could register a global shortcut, (which would take effect even if window is not in focus), or I could register a menu shortcut.
https://github.com/electron/electron/blob/master/docs/tutorial/keyboard-shortcuts.md
Currently there is no way of changing the editor size short of modifying the source code directly. Even if there was a nice setting to change, it would still be nice to change the size of the editor with a simple drag. Users may want to adjust the size of the editor depending on their preferences and the size of the user variable display on the right.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.