Skip to content

Commit 766931a

Browse files
committed
merge
2 parents eff7f27 + 61cd40a commit 766931a

File tree

9 files changed

+99
-127
lines changed

9 files changed

+99
-127
lines changed

CHANGELOG

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
Changelog
22
=========
33

4+
v0.9.8
5+
------
6+
* Config files are now located according to the XDG Base Directory
7+
Specification. The support for the old bpythonrc files has been
8+
dropped and ~/.bpython.ini as config file location is no longer supported.
9+
See issue #91.
10+
11+
v0.9.7.1
12+
--------
13+
14+
A bugfix release. The fixed bugs are:
15+
16+
* #128: bpython-gtk is broken
17+
* #134: crash when using pastebin and no active internet connection
18+
419
v0.9.7
520
------
621

@@ -15,7 +30,7 @@ integrates with a Twisted reactor and through that with things like the GTK
1530
event loop.
1631

1732
At the same time we have done a lot of work on the GTK frontend. The GTK
18-
frontend is now 'usable'. Please give that a spin as well by running python-gtk
33+
frontend is now 'usable'. Please give that a spin as well by running bpython-gtk
1934
on you system.
2035

2136
We also welcome a new contributor in the name of Michele Orrù who we hope will

README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ http://bitbucket.org/bobf/bpython/issues/
4747
For any other ways of communicating with bpython
4848
users and devs you can find us at the communication
4949
page on the projects homepage:
50-
http://www.noiseforfree.com/bpython/community.html
50+
http://bpython-interpreter.org/community
5151

5252
Hope to see you there!
5353

bpython/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# THE SOFTWARE.
2222

2323

24-
__version__ = '0.9.7'
24+
__version__ = '0.9.7.1'
2525

2626

2727
def embed(locals_=None, args=['-i', '-q'], banner=None):

bpython/args.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from optparse import OptionParser, OptionGroup
1010

1111
from bpython import __version__
12-
from bpython.config import loadini, Struct, migrate_rc
12+
from bpython.config import default_config_path, loadini, Struct
1313

1414

1515
class OptionParserFailed(ValueError):
@@ -58,7 +58,7 @@ def parse(args, extras=None, ignore_stdin=False):
5858
# That's probably fixable though, for example by having that
5959
# option swallow all remaining arguments in a callback.
6060
parser.disable_interspersed_args()
61-
parser.add_option('--config', '-c', default='~/.bpython/config',
61+
parser.add_option('--config', '-c', default=default_config_path(),
6262
help='use CONFIG instead of default config file')
6363
parser.add_option('--interactive', '-i', action='store_true',
6464
help='Drop to bpython shell after running file '
@@ -92,12 +92,7 @@ def parse(args, extras=None, ignore_stdin=False):
9292
interpreter.runsource(sys.stdin.read())
9393
raise SystemExit
9494

95-
path = os.path.expanduser('~/.bpythonrc')
96-
# migrating old configuration file
97-
if os.path.isfile(path):
98-
migrate_rc(path)
9995
config = Struct()
100-
10196
loadini(config, options.config)
10297

10398
return config, options, args

bpython/config.py

Lines changed: 60 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1+
from __future__ import with_statement
12
import os
23
import sys
34
from ConfigParser import ConfigParser
45
from itertools import chain
56
from bpython.keys import key_dispatch
6-
import errno
77

88

99
class Struct(object):
1010
"""Simple class for instantiating objects we can add arbitrary attributes
1111
to and use for various arbitrary things."""
1212

13+
def get_config_home():
14+
"""Returns the base directory for bpython's configuration files."""
15+
xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '~/.config')
16+
return os.path.join(xdg_config_home, 'bpython')
17+
18+
def default_config_path():
19+
"""Returns bpython's default configuration file path."""
20+
return os.path.join(get_config_home(), 'config')
1321

1422
def fill_config_with_default_values(config, default_values):
1523
for section in default_values.iterkeys():
@@ -25,11 +33,11 @@ def loadini(struct, configfile):
2533
"""Loads .ini configuration file and stores its values in struct"""
2634

2735
config_path = os.path.expanduser(configfile)
28-
if not os.path.isfile(config_path) and configfile == '~/.bpython/config':
29-
# FIXME: I decided ~/.bpython.ini was a crappy place for a config file,
30-
# so this is just a fallback if the default is passed - remove this
31-
# eventually please.
32-
config_path = os.path.expanduser('~/.bpython.ini')
36+
if not os.path.isfile(config_path) and configfile == default_config_path():
37+
# We decided that '~/.bpython/config' still was a crappy
38+
# place, use XDG Base Directory Specification instead. Fall
39+
# back to old config, though.
40+
config_path = os.path.expanduser('~/.bpython/config')
3341

3442
config = ConfigParser()
3543
fill_config_with_default_values(config, {
@@ -69,7 +77,13 @@ def loadini(struct, configfile):
6977
'gtk': {
7078
'font': 'monospace 10',
7179
'color_scheme': 'default'}})
72-
config.read(config_path)
80+
if not config.read(config_path):
81+
# No config file. If the user has it in the old place then complain
82+
if os.path.isfile(os.path.expanduser('~/.bpython.ini')):
83+
sys.stderr.write("Error: It seems that you have a config file at "
84+
"~/.bpython.ini. Please move your config file to "
85+
"%s\n" % default_config_path())
86+
sys.exit(1)
7387

7488
struct.dedent_after = config.getint('general', 'dedent_after')
7589
struct.tab_length = config.getint('general', 'tab_length')
@@ -126,7 +140,7 @@ def loadini(struct, configfile):
126140
'prompt': 'c',
127141
'prompt_more': 'g',
128142
}
129-
143+
130144
default_gtk_colors = {
131145
'keyword': 'b',
132146
'name': 'k',
@@ -145,123 +159,61 @@ def loadini(struct, configfile):
145159
'prompt_more': 'g',
146160
}
147161

148-
# TODO consolidate
149162
if color_scheme_name == 'default':
150163
struct.color_scheme = default_colors
151164
else:
152-
path = os.path.expanduser('~/.bpython/%s.theme' % (color_scheme_name,))
153-
load_theme(struct, path, config_path, default_colors)
165+
struct.color_scheme = dict()
166+
167+
theme_filename = color_scheme_name + '.theme'
168+
path = os.path.expanduser(os.path.join(get_config_home(),
169+
theme_filename))
170+
old_path = os.path.expanduser(os.path.join('~/.bpython',
171+
theme_filename))
172+
173+
for path in [path, old_path]:
174+
try:
175+
load_theme(struct, path, struct.color_scheme, default_colors)
176+
except EnvironmentError:
177+
continue
178+
else:
179+
break
180+
else:
181+
sys.stderr.write("Could not load theme '%s'.\n" %
182+
(color_scheme_name, ))
183+
sys.exit(1)
154184

155185
if color_gtk_scheme_name == 'default':
156186
struct.color_gtk_scheme = default_gtk_colors
157187
else:
158-
path = os.path.expanduser('~/.bpython/%s.theme' % (color_gtk_scheme_name,))
159-
load_gtk_theme(struct, path, config_path, default_gtk_colors)
160-
188+
struct.color_gtk_scheme = dict()
189+
# Note: This is a new config option, hence we don't have a
190+
# fallback directory.
191+
path = os.path.expanduser(os.path.join(get_config_home(),
192+
color_gtk_scheme_name + '.theme'))
193+
194+
try:
195+
load_theme(struct, path, struct.color_gtk_scheme, default_colors)
196+
except EnvironmentError:
197+
sys.stderr.write("Could not load gtk theme '%s'.\n" %
198+
(color_gtk_scheme_name, ))
199+
sys.exit(1)
161200

162201
# checks for valid key configuration this part still sucks
163202
for key in (struct.pastebin_key, struct.save_key):
164203
key_dispatch[key]
165204

166-
# TODO consolidate
167-
def load_theme(struct, path, inipath, default_colors):
205+
def load_theme(struct, path, colors, default_colors):
168206
theme = ConfigParser()
169-
try:
170-
f = open(path, 'r')
171-
except (IOError, OSError), e:
172-
sys.stdout.write("Error loading theme file specified in '%s':\n%s\n" %
173-
(inipath, e))
174-
sys.exit(1)
175-
theme.readfp(f)
176-
struct.color_scheme = {}
207+
with open(path, 'r') as f:
208+
theme.readfp(f)
177209
for k, v in chain(theme.items('syntax'), theme.items('interface')):
178210
if theme.has_option('syntax', k):
179-
struct.color_scheme[k] = theme.get('syntax', k)
211+
colors[k] = theme.get('syntax', k)
180212
else:
181-
struct.color_scheme[k] = theme.get('interface', k)
213+
colors[k] = theme.get('interface', k)
182214

183215
# Check against default theme to see if all values are defined
184216
for k, v in default_colors.iteritems():
185-
if k not in struct.color_scheme:
186-
struct.color_scheme[k] = v
187-
f.close()
188-
189-
190-
def load_gtk_theme(struct, path, inipath, default_colors):
191-
theme = ConfigParser()
192-
try:
193-
f = open(path, 'r')
194-
except (IOError, OSError), e:
195-
sys.stdout.write("Error loading theme file specified in '%s':\n%s\n" %
196-
(inipath, e))
197-
sys.exit(1)
198-
theme.readfp(f)
199-
struct.color_gtk_scheme = {}
200-
for k, v in chain(theme.items('syntax'), theme.items('interface')):
201-
if theme.has_option('syntax', k):
202-
struct.color_gtk_scheme[k] = theme.get('syntax', k)
203-
else:
204-
struct.color_gtk_scheme[k] = theme.get('interface', k)
205-
206-
# Check against default theme to see if all values are defined
207-
for k, v in default_colors.iteritems():
208-
if k not in struct.color_gtk_scheme:
209-
struct.color_gtk_scheme[k] = v
210-
f.close()
211-
212-
213-
214-
def migrate_rc(path):
215-
"""Use the shlex module to convert the old configuration file to the new
216-
format.
217-
The old configuration file is renamed but not removed by now."""
218-
import shlex
219-
f = open(path)
220-
parser = shlex.shlex(f)
221-
222-
bools = {
223-
'true': True,
224-
'yes': True,
225-
'on': True,
226-
'false': False,
227-
'no': False,
228-
'off': False}
229-
230-
config = ConfigParser()
231-
config.add_section('general')
232-
233-
while True:
234-
k = parser.get_token()
235-
v = None
236-
237-
if not k:
238-
break
239-
240-
k = k.lower()
241-
242-
if parser.get_token() == '=':
243-
v = parser.get_token() or None
244-
245-
if v is not None:
246-
try:
247-
v = int(v)
248-
except ValueError:
249-
if v.lower() in bools:
250-
v = bools[v.lower()]
251-
config.set('general', k, v)
252-
f.close()
253-
try:
254-
os.makedirs(os.path.expanduser('~/.bpython'))
255-
except OSError, e:
256-
if e.errno != errno.EEXIST:
257-
raise
258-
f = open(os.path.expanduser('~/.bpython/config'), 'w')
259-
config.write(f)
217+
if k not in colors:
218+
colors[k] = v
260219
f.close()
261-
os.rename(path, os.path.expanduser('~/.bpythonrc.bak'))
262-
print ("The configuration file for bpython has been changed. A new "
263-
"config file has been created as ~/.bpython/config")
264-
print ("The existing .bpythonrc file has been renamed to .bpythonrc.bak "
265-
"and it can be removed.")
266-
print "Press enter to continue."
267-
raw_input()

bpython/repl.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from glob import glob
3636
from itertools import takewhile
3737
from locale import getpreferredencoding
38-
from socket import error as socketerror
38+
from socket import error as SocketError
3939
from string import Template
4040
from urllib import quote as urlquote
4141
from xmlrpclib import ServerProxy, Error as XMLRPCError
@@ -714,8 +714,8 @@ def pastebin(self, s=None):
714714
self.interact.notify('Posting data to pastebin...')
715715
try:
716716
paste_id = pasteservice.pastes.newPaste('pycon', s)
717-
except (XMLRPCError, socketerror), e:
718-
self.interact.notify('Upload failed: %s' % e)
717+
except (SocketError, XMLRPCError), e:
718+
self.interact.notify('Upload failed: %s' % (str(e), ) )
719719
return
720720

721721
paste_url_template = Template(self.config.pastebin_show_url)

bpython/test/test_config.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,26 @@
88
class TestConfig(unittest.TestCase):
99
def test_load_theme(self):
1010
struct = config.Struct()
11-
config.load_theme(struct, TEST_THEME_PATH, "test.ini", dict())
11+
struct.color_scheme = dict()
12+
config.load_theme(struct, TEST_THEME_PATH, struct.color_scheme, dict())
1213
expected = {"keyword": "y"}
1314
self.assertEquals(struct.color_scheme, expected)
1415

1516
defaults = {"name": "c"}
1617
expected.update(defaults)
17-
config.load_theme(struct, TEST_THEME_PATH, "test.ini", defaults)
18+
config.load_theme(struct, TEST_THEME_PATH, struct.color_scheme, defaults)
1819
self.assertEquals(struct.color_scheme, expected)
1920

2021
def test_load_gtk_scheme(self):
2122
struct = config.Struct()
22-
config.load_gtk_theme(struct, TEST_THEME_PATH, "test.ini", dict())
23+
struct.color_gtk_scheme = dict()
24+
config.load_theme(struct, TEST_THEME_PATH, struct.color_gtk_scheme, dict())
2325
expected = {"keyword": "y"}
2426
self.assertEquals(struct.color_gtk_scheme, expected)
2527

2628
defaults = {"name": "c"}
2729
expected.update(defaults)
28-
config.load_gtk_theme(struct, TEST_THEME_PATH, "test.ini", defaults)
30+
config.load_theme(struct, TEST_THEME_PATH, struct.color_gtk_scheme, defaults)
2931
self.assertEquals(struct.color_gtk_scheme, expected)
3032

3133

bpython/urwid.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,14 @@ def mouse_event(self, *args):
304304
finally:
305305
self._bpy_may_move_cursor = False
306306

307+
class BPythonListBox(urwid.ListBox):
308+
"""Like `urwid.ListBox`, except that it does not eat up and
309+
down keys.
310+
"""
311+
def keypress(self, size, key):
312+
if key not in ["up", "down"]:
313+
return urwid.ListBox.keypress(self, size, key)
314+
return key
307315

308316
class Tooltip(urwid.BoxWidget):
309317

@@ -408,7 +416,7 @@ class URWIDRepl(repl.Repl):
408416
def __init__(self, event_loop, palette, interpreter, config):
409417
repl.Repl.__init__(self, interpreter, config)
410418

411-
self.listbox = urwid.ListBox(urwid.SimpleListWalker([]))
419+
self.listbox = BPythonListBox(urwid.SimpleListWalker([]))
412420

413421
# String is straight from bpython.cli
414422
self.statusbar = Statusbar(config,

doc/bpython.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ http://docs.bpython-interpreter.org/configuration.html#keyboard
8989
.SH FILES
9090
~/.bpython/config
9191
.RS
92-
Your bpython config. See sample-config (in /usr/share/docs/bpython/examples on Debian) for various options you can use, or read
92+
Your bpython config. See sample-config (in /usr/share/doc/bpython/examples on Debian) for various options you can use, or read
9393
.BR bpython-config (5)
9494
.
9595
.RE

0 commit comments

Comments
 (0)