X Tutup
Skip to content

Commit 629759e

Browse files
committed
Merge in Brandon Navra's changes to be able to run bpython in the Windows CLI. Also move Brandon's Windows_Support.txt in the README file. Note to self: Include in documentation after 0.10 release
2 parents 0cea36a + 9b41964 commit 629759e

File tree

3 files changed

+172
-22
lines changed

3 files changed

+172
-22
lines changed

README

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,38 @@ For known bugs please see bpython's issue tracker at
118118
bitbucket:
119119

120120
http://bitbucket.org/bobf/bpython/issues/
121+
122+
CLI Windows Support
123+
===================
124+
125+
Dependencies
126+
------------
127+
Curses
128+
Use the appropriate version compiled by Christoph Gohlke
129+
http://www.lfd.uci.edu/~gohlke/pythonlibs/
130+
131+
pyreadline
132+
Use the version in the cheeseshop
133+
http://pypi.python.org/pypi/pyreadline/
134+
135+
Recommended
136+
-----------
137+
Obtain the less program from GnuUtils. This makes the pager work as intended.
138+
It can be obtained from cygwin or GnuWin32 or msys
139+
140+
Current version is tested with
141+
------------------------------
142+
Curses 2.2
143+
pyreadline 1.7
144+
145+
Curses Notes
146+
------------
147+
The curses used has a bug where the colours are displayed incorrectly:
148+
red is swapped with blue
149+
cyan is swapped with yellow
150+
151+
To correct this I have provided my windows.theme file.
152+
153+
This curses implementation has 16 colors (dark and light versions of the colours)
154+
155+

bpython/cli.py

Lines changed: 109 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,38 @@
2222
# THE SOFTWARE.
2323
#
2424

25+
# Modified by Brandon Navra
26+
# Notes for Windows
27+
# Prerequsites
28+
# - Curses
29+
# - pyreadline
30+
#
31+
# Added
32+
#
33+
# - Support for running on windows command prompt
34+
# - input from numpad keys
35+
#
36+
# Issues
37+
#
38+
# - Suspend doesn't work nor does detection of resizing of screen
39+
# - Instead the suspend key exits the program
40+
# - View source doesn't work on windows unless you install the less program (From GnuUtils or Cygwin)
41+
2542
from __future__ import division, with_statement
2643

44+
import platform
2745
import os
2846
import sys
2947
import curses
3048
import math
3149
import re
3250
import time
33-
import signal
51+
3452
import struct
35-
import termios
36-
import fcntl
53+
if platform.system() != 'Windows':
54+
import signal #Windows does not have job control
55+
import termios #Windows uses curses
56+
import fcntl #Windows uses curses
3757
import unicodedata
3858
import errno
3959

@@ -76,6 +96,7 @@
7696
DO_RESIZE = False
7797
# ---
7898

99+
79100
def getpreferredencoding():
80101
return locale.getpreferredencoding() or sys.getdefaultencoding()
81102

@@ -136,7 +157,7 @@ def readline(self, size=-1):
136157

137158
curses.raw(True)
138159
try:
139-
while not buffer.endswith('\n'):
160+
while not (buffer.endswith('\n') or buffer.endswith('\r')):
140161
key = self.interface.get_key()
141162
if key in [curses.erasechar(), 'KEY_BACKSPACE']:
142163
y, x = self.interface.scr.getyx()
@@ -147,7 +168,7 @@ def readline(self, size=-1):
147168
elif key == chr(4) and not buffer:
148169
# C-d
149170
return ''
150-
elif (key != '\n' and
171+
elif (key not in ('\n', '\r') and
151172
(len(key) > 1 or unicodedata.category(key) == 'Cc')):
152173
continue
153174
sys.stdout.write(key)
@@ -224,6 +245,21 @@ def make_colors(config):
224245
'w': 7,
225246
'd': -1,
226247
}
248+
249+
if platform.system() == 'Windows':
250+
c = dict(c.items() +
251+
[
252+
('K', 8),
253+
('R', 9),
254+
('G', 10),
255+
('Y', 11),
256+
('B', 12),
257+
('M', 13),
258+
('C', 14),
259+
('W', 15),
260+
]
261+
)
262+
227263
for i in range(63):
228264
if i > 7:
229265
j = i // 8
@@ -773,14 +809,22 @@ def p_key(self, key):
773809

774810
config = self.config
775811

776-
if key == chr(8): # C-Backspace (on my computer anyway!)
812+
if platform.system() == 'Windows':
813+
C_BACK = chr(127)
814+
BACKSP = chr(8)
815+
else:
816+
C_BACK = chr(8)
817+
BACKSP = chr(127)
818+
819+
if key == C_BACK: # C-Backspace (on my computer anyway!)
777820
self.clrtobol()
778821
key = '\n'
779822
# Don't return; let it get handled
780-
if key == chr(27):
823+
824+
if key == chr(27): #Escape Key
781825
return ''
782826

783-
if key in (chr(127), 'KEY_BACKSPACE'):
827+
if key in (BACKSP, 'KEY_BACKSPACE'):
784828
self.bs()
785829
self.complete()
786830
return ''
@@ -893,7 +937,7 @@ def p_key(self, key):
893937
self.statusbar.message(_('Cannot show source.'))
894938
return ''
895939

896-
elif key == '\n':
940+
elif key in ('\n', '\r', 'PADENTER'):
897941
self.lf()
898942
return None
899943

@@ -904,9 +948,25 @@ def p_key(self, key):
904948
return self.tab(back=True)
905949

906950
elif key in key_dispatch[config.suspend_key]:
907-
self.suspend()
908-
return ''
951+
if platform.system() != 'Windows':
952+
self.suspend()
953+
return ''
954+
else:
955+
self.do_exit = True
956+
return None
909957

958+
elif key[0:3] == 'PAD' and not key in ('PAD0', 'PADSTOP'):
959+
pad_keys = {
960+
'PADMINUS': '-',
961+
'PADPLUS': '+',
962+
'PADSLASH': '/',
963+
'PADSTAR': '*',
964+
}
965+
try:
966+
self.addstr(pad_keys[key])
967+
self.print_line(self.s)
968+
except KeyError:
969+
return ''
910970
elif len(key) == 1 and not unicodedata.category(key) == 'Cc':
911971
self.addstr(key)
912972
self.print_line(self.s)
@@ -1296,8 +1356,9 @@ def size(self):
12961356

12971357
def suspend(self):
12981358
"""Suspend the current process for shell job control."""
1299-
curses.endwin()
1300-
os.kill(os.getpid(), signal.SIGSTOP)
1359+
if platform.system() != 'Windows':
1360+
curses.endwin()
1361+
os.kill(os.getpid(), signal.SIGSTOP)
13011362

13021363
def tab(self, back=False):
13031364
"""Process the tab key being hit. If there's only whitespace
@@ -1575,9 +1636,32 @@ def gethw():
15751636
So I'm not going to ask any questions.
15761637
15771638
"""
1578-
h, w = struct.unpack(
1579-
"hhhh",
1580-
fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000" * 8))[0:2]
1639+
1640+
if platform.system() != 'Windows':
1641+
h, w = struct.unpack(
1642+
"hhhh",
1643+
fcntl.ioctl(sys.__stdout__, termios.TIOCGWINSZ, "\000" * 8))[0:2]
1644+
else:
1645+
from ctypes import windll, create_string_buffer
1646+
1647+
# stdin handle is -10
1648+
# stdout handle is -11
1649+
# stderr handle is -12
1650+
1651+
h = windll.kernel32.GetStdHandle(-12)
1652+
csbi = create_string_buffer(22)
1653+
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
1654+
1655+
if res:
1656+
import struct
1657+
(bufx, bufy, curx, cury, wattr,
1658+
left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
1659+
sizex = right - left + 1
1660+
sizey = bottom - top + 1
1661+
else:
1662+
sizex, sizey = stdscr.getmaxyx()# can't determine actual size - return default values
1663+
1664+
h, w = sizey, sizex
15811665
return h, w
15821666

15831667

@@ -1679,10 +1763,11 @@ def main_curses(scr, args, config, interactive=True, locals_=None,
16791763
global colors
16801764
DO_RESIZE = False
16811765

1682-
old_sigwinch_handler = signal.signal(signal.SIGWINCH,
1683-
lambda *_: sigwinch(scr))
1684-
# redraw window after being suspended
1685-
old_sigcont_handler = signal.signal(signal.SIGCONT, lambda *_: sigcont(scr))
1766+
if platform.system() != 'Windows':
1767+
old_sigwinch_handler = signal.signal(signal.SIGWINCH,
1768+
lambda *_: sigwinch(scr))
1769+
# redraw window after being suspended
1770+
old_sigcont_handler = signal.signal(signal.SIGCONT, lambda *_: sigcont(scr))
16861771

16871772
stdscr = scr
16881773
try:
@@ -1733,8 +1818,9 @@ def main_curses(scr, args, config, interactive=True, locals_=None,
17331818
curses.raw(False)
17341819

17351820
# Restore signal handlers
1736-
signal.signal(signal.SIGWINCH, old_sigwinch_handler)
1737-
signal.signal(signal.SIGCONT, old_sigcont_handler)
1821+
if platform.system() != 'Windows':
1822+
signal.signal(signal.SIGWINCH, old_sigwinch_handler)
1823+
signal.signal(signal.SIGCONT, old_sigcont_handler)
17381824

17391825
return clirepl.getstdout()
17401826

@@ -1743,6 +1829,7 @@ def main(args=None, locals_=None, banner=None):
17431829
locale.setlocale(locale.LC_ALL, "")
17441830
translations.init()
17451831

1832+
17461833
config, options, exec_args = bpython.args.parse(args)
17471834

17481835
# Save stdin, stdout and stderr for later restoration

windows.theme

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Each letter represents a colour marker:
2+
# k, r, g, y, b, m, c, w, d
3+
# which stands for:
4+
# blacK, Red, Green, Yellow, Blue, Magenta, Cyan, White, Default
5+
#
6+
# But in Windows (for now)
7+
# K, R, G, Y, B, M, C, W, k
8+
# blacK, blue, Green, cyan, red, Magenta, yellow, hite, grey
9+
# Capital letters represent bold (brighter)
10+
# Copy to %USERPROFILE%\.bpython\windows.theme and set "color_scheme = windows" in %USERPROFILE%\.bpython\config
11+
12+
[syntax]
13+
keyword = G
14+
name = Y
15+
comment = c
16+
string = M
17+
error = B
18+
number = C
19+
operator = G
20+
punctuation = C
21+
token = M
22+
23+
[interface]
24+
background = r
25+
output = w
26+
main = W
27+
prompt = W
28+
prompt_more = K

0 commit comments

Comments
 (0)
X Tutup