Skip to content

Commit 65248c6

Browse files
committed
Auto upload
1 parent cfe2450 commit 65248c6

File tree

8 files changed

+245
-163
lines changed

8 files changed

+245
-163
lines changed

console.py

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
'''
2-
Windows only. If not windows, start IPython.
3-
Advantage over IPython:
2+
Windows only. If not windows, console() calls IPython.embed().
3+
Advantages over IPython:
44
1. Lighter
55
2. Other threads can still print things when user is inputting commands
6-
Issue:
7-
If you wanna scroll up, you need to input().
8-
Changing global bindings doesn't work. Sorry.
6+
3. Tab auto-completes your phrase, even under Windows! (I'm proud.)
7+
Issues:
8+
1. If you wanna scroll up, you need to input().
9+
2. Reassigning module global variables will not be visible to module native codes. Sorry.
910
'''
1011
import platform
11-
from listen import listen
12+
from interactive import listen, strCommonStart
1213
from kernal import Kernal
14+
from graphic_terminal import clearLine
15+
import string
1316

1417
CURSOR = '|'
1518

@@ -18,7 +21,7 @@ def console(namespace = {}, prompt = '>>> ', use_input = False, fixer = None):
1821
from IPython import embed
1922
embed()
2023
return
21-
print('console.console Warning: no support for change in global bindings. ')
24+
print('console.console Warning: no support for reassigning module global variables. ')
2225
history = []
2326
kernal = Kernal(namespace)
2427
next(kernal)
@@ -38,7 +41,8 @@ def console(namespace = {}, prompt = '>>> ', use_input = False, fixer = None):
3841
op = listen(timeout = .5)
3942
last_len = len(command)
4043
if op == b'\r':
41-
print(prompt + command.replace('\x1a', '^Z') + ' ')
44+
clearLine()
45+
print(prompt + command.replace('\x1a', '^Z'))
4246
break
4347
elif op is None:
4448
cursor_bright = not cursor_bright
@@ -100,18 +104,53 @@ def console(namespace = {}, prompt = '>>> ', use_input = False, fixer = None):
100104
word_ended = True
101105
else:
102106
cursor = len(command)
107+
elif op == b'\t':
108+
# auto complete
109+
legal_prefix = string.ascii_letters + string.digits + '_'
110+
reversed_names = []
111+
name_end = cursor
112+
name_start = cursor - 1
113+
while True:
114+
if name_start >= 0 and command[name_start] in legal_prefix:
115+
name_start -= 1
116+
else:
117+
reversed_names.append(command[name_start + 1:name_end])
118+
name_end = name_start
119+
name_start -= 1
120+
if name_start < 0 or command[name_end] != '.':
121+
break
122+
keyword = reversed_names.pop(0)
123+
names = reversed(reversed_names)
124+
to_search = kernal.send('dir(%s)' % '.'.join(names))
125+
if len(reversed_names) == 0:
126+
# include builtins
127+
to_search += dir(__builtins__)
128+
next(kernal)
129+
candidates = [x for x in to_search if x.startswith(keyword)]
130+
if len(candidates) >= 1:
131+
if len(candidates) == 1:
132+
to_become = candidates[0]
133+
if len(candidates) > 1:
134+
clearLine()
135+
print('auto-complete: ', end = '')
136+
[print(x, end = '\t') for x in candidates]
137+
print()
138+
to_become = strCommonStart(candidates, len(keyword))
139+
to_insert = to_become[len(keyword):]
140+
command = command[:cursor] + to_insert + command[cursor:]
141+
cursor += len(to_insert)
103142
elif op[0] in range(1, 26) or op[0] in (0, 224):
104143
pass
105144
else:
106145
command = command[:cursor] + op.decode() + command[cursor:]
107146
cursor += 1
108-
padding = max(0, last_len - len(command)) * 2
109147
if cursor_bright:
110148
cursor_show = CURSOR
111149
else:
112150
cursor_show = '_'
113151
cursed_command = command[:cursor] + cursor_show + command[cursor:]
114-
print(prompt + cursed_command.replace('\x1a', '^Z') + ' '*padding, end='\r')
152+
clearLine()
153+
print(prompt + cursed_command.replace('\x1a', '^Z'), end='\r')
115154
if fixer is not None:
116155
command = fixer(command)
117156
if command in ('exit', 'exit()', '\x1a'):
@@ -123,7 +162,10 @@ def console(namespace = {}, prompt = '>>> ', use_input = False, fixer = None):
123162
if command == '':
124163
continue
125164
history.append(command)
126-
kernal.send(command)
165+
result = kernal.send(command)
166+
next(kernal)
167+
if result is not None:
168+
print(result)
127169

128170
if __name__ == '__main__':
129171
console({})

fakep2p.py

Lines changed: 0 additions & 64 deletions
This file was deleted.

graphic_terminal.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from terminalsize import get_terminal_size
2+
3+
def clearLine():
4+
'''
5+
Clears the current line, then \r.
6+
Used when previously printed without \n.
7+
'''
8+
terminal_width = get_terminal_size()[0] - 4
9+
print('\r', ' ' * terminal_width, end = '\r', sep = '')

interactive.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'''
2+
Terminal interactivity utils.
3+
'''
4+
#===============================================================================
5+
# I do not know why this was here.
6+
# if 'flush' not in getargspec(print).args:
7+
# print_3 = print
8+
# def print(*args, flush = False, **kw):
9+
# print_3(*args, **kw)
10+
#===============================================================================
11+
__all__ = ['listen', 'strCommonStart']
12+
from time import sleep
13+
FPS = 30
14+
15+
try:
16+
import msvcrt
17+
import sys
18+
def getFullCh():
19+
first=msvcrt.getch()
20+
if first in (b'\x00', b'\xe0'):
21+
return first + msvcrt.getch()
22+
else:
23+
return first
24+
# if sys.getwindowsversion() >= (10, 0, 17134) and False: # Strange windows update on 2018/10/22
25+
# __getFullCh = getFullCh
26+
# def getFullCh():
27+
# ch = __getFullCh()
28+
# if len(ch) == 1:
29+
# assert msvcrt.getch() == b'\x00'
30+
# return ch
31+
except ImportError:
32+
msvcrt = None
33+
34+
def listen(choice=None, timeout=0):
35+
'''
36+
choice can be a list of choices or a single choice.
37+
Elements can be b'' or ''.
38+
If timeout=0, it's blocking.
39+
timeout is in second.
40+
Supports non-windows.
41+
'''
42+
if choice is not None:
43+
if type(choice) in (bytes, str):
44+
choice = (choice, )
45+
bChoice=[]
46+
for i in choice:
47+
if type(i) is bytes:
48+
bChoice.append(i)
49+
else:
50+
bChoice.append(i.encode())
51+
print('', end = '', flush = True) # Just to flush
52+
if msvcrt is None:
53+
if choice is None:
54+
op = eval('b"%s"' % input())
55+
if op == b'':
56+
return b'\r' # So android doesn't need to type "\r"
57+
else:
58+
print(bChoice)
59+
op = None
60+
while op not in bChoice:
61+
op = eval('b"%s"' % input())
62+
if op == b'' and b'\r' in bChoice:
63+
return b'\r' # So android doesn't need to type "\r"
64+
return op
65+
if timeout != 0:
66+
for i in range(int(timeout*FPS)):
67+
if msvcrt.kbhit():
68+
op = getFullCh()
69+
if choice is None or op in bChoice:
70+
return op
71+
sleep(1/FPS)
72+
return None
73+
op=getFullCh()
74+
while not (choice is None or op in bChoice):
75+
op=getFullCh()
76+
return op
77+
78+
def strCommonStart(list_strs, known_len = 0):
79+
'''
80+
Find the common start for a list of strings.
81+
Useful for auto-complete for the user.
82+
`known_len` is known length of common start - for performance.
83+
'''
84+
columns = zip(* list_strs)
85+
i = -1 # in case one string is ''
86+
for i, column in enumerate(columns):
87+
if i >= known_len:
88+
shifted_column = iter(column)
89+
next(shifted_column)
90+
if not all(x == y for x, y in zip(column, shifted_column)):
91+
i -= 1
92+
break
93+
return list_strs[0][:i + 1]
94+
95+
if __name__=='__main__':
96+
from console import console
97+
console(globals())

kernal.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,20 @@ def cls():
1919
if __command == 'exit':
2020
return
2121
try:
22-
__result = eval(__command + '\r')
23-
if __result is not None:
24-
print(__result)
25-
del __result
26-
__eval_good = True
22+
yield eval([__command + '\r', exec('del __command')][0])
23+
# Tries to hide `__command` in dir().
24+
# doesn't work tho.
25+
# But you have to admit, it's a clever try.
26+
continue
2727
except SyntaxError:
28-
__eval_good = False
29-
if not __eval_good:
30-
del __eval_good
31-
exec(__command + '\r')
32-
else:
33-
del __eval_good
28+
pass
29+
exec(__command + '\r')
30+
yield None
3431
except:
3532
import traceback
3633
traceback.print_exc()
3734
del traceback
35+
yield None
3836

3937
if __name__ == '__main__':
4038
kernal = Kernal({})

0 commit comments

Comments
 (0)