X Tutup
Skip to content

Commit aefac86

Browse files
committed
Use XDG_CONFIG_HOME for configuration files.
Support for old bpythonrc files and ~/.bpython.ini as config file location has been dropped. This closes issue #91.
1 parent 7d9c45c commit aefac86

File tree

4 files changed

+75
-119
lines changed

4 files changed

+75
-119
lines changed

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
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+
411
v0.9.7.1
512
--------
613

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/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

0 commit comments

Comments
 (0)
X Tutup