Add an update mechanism for old repositories

The update is done transparently, and displays a warning message explaining the change.
All the update machinery has been moved to the new update module.
main
Fabien Benureau 9 years ago
parent 93c54939b3
commit 757a8b300e

@ -1 +1 @@
__version__ = '0.5.0' __version__ = '0.6.0'

@ -1,39 +0,0 @@
import sys
from .. import repo
from .. import color
from ..uis import get_ui
from ..__init__ import __version__
def parser(subparsers):
parser = subparsers.add_parser('update', help='update the repository to the lastest format')
return parser
def command(conf, args):
ui = get_ui()
code_version = __version__.split('.')
if len(conf['internal']['version']) == 1: # support for deprecated version scheme.
conf['internal']['version'] = '0.{}.0'.format(conf['internal']['version'])
repo_version = conf['internal']['version'].split('.')
if repo_version == code_version:
ui.message('Your pubs repository is up-to-date.')
sys.exit(0)
elif repo_version > code_version:
ui.message('Your repository was generated with an newer version of pubs.\n'
'You should not use pubs until you install the newest version.')
sys.exit(0)
else:
msg = ("You should backup the pubs directory {} before continuing."
"Continue ?").format(color.dye_out(conf['main']['pubsdir'], color.filepath))
sure = ui.input_yn(question=msg, default='n')
if not sure:
sys.exit(0)
#TODO: update!!
# conf['internal']['version'] = repo_version
# conf['internal']['version']

@ -1 +1 @@
from .conf import load_default_conf, load_conf, save_conf, get_pubspath from .conf import get_confpath, load_default_conf, load_conf, save_conf, check_conf

@ -15,7 +15,7 @@ def load_default_conf():
default_conf.validate(validator, copy=True) default_conf.validate(validator, copy=True)
return default_conf return default_conf
def get_pubspath(verify=True): def get_confpath(verify=True):
"""Returns the pubs path. """Returns the pubs path.
If verify is True, verify that pubs.conf exist in the directory, If verify is True, verify that pubs.conf exist in the directory,
and exit with an error if not. and exit with an error if not.
@ -31,19 +31,26 @@ def get_pubspath(verify=True):
ui.exit(error_code=1) ui.exit(error_code=1)
return confpath return confpath
def load_conf(check_conf=True): def check_conf(conf):
"""Load the user config""" """Type checks a configuration"""
pubspath = get_pubspath(verify=True)
with open(pubspath, 'r') as f:
conf = configobj.ConfigObj(f.readlines(), configspec=configspec)
if check_conf:
validator = validate.Validator() validator = validate.Validator()
results = conf.validate(validator, copy=True) results = conf.validate(validator, copy=True)
assert results == True, '{}'.format(results) # TODO: precise error dialog when parsing error assert results == True, '{}'.format(results) # TODO: precise error dialog when parsing error
def load_conf(check=True, path=None):
"""Load the user config"""
if path is None:
path = get_confpath(verify=True)
with open(path, 'rb') as f:
conf = configobj.ConfigObj(f.readlines(), configspec=configspec)
if check:
check_conf(conf)
return conf return conf
def save_conf(conf): def save_conf(conf, path=None):
with open(get_pubspath(verify=False), 'w') as f: if path is None:
path = get_confpath(verify=False)
with open(path, 'wb') as f:
conf.write(outfile=f) conf.write(outfile=f)

@ -6,8 +6,8 @@ import collections
from . import uis from . import uis
from . import config from . import config
from . import commands from . import commands
from . import update
from . import plugins from . import plugins
from .__init__ import __version__
CORE_CMDS = collections.OrderedDict([ CORE_CMDS = collections.OrderedDict([
@ -27,37 +27,17 @@ CORE_CMDS = collections.OrderedDict([
('websearch', commands.websearch_cmd), ('websearch', commands.websearch_cmd),
('edit', commands.edit_cmd), ('edit', commands.edit_cmd),
# ('update', commands.update_cmd),
]) ])
def _update_check(conf, ui):
code_version = __version__.split('.')
if len(conf['internal']['version']) == 1: # support for deprecated version scheme.
conf['internal']['version'] = '0.{}.0'.format(conf['internal']['version'])
repo_version = conf['internal']['version'].split('.')
if repo_version > code_version:
ui.warning(
'your repository was generated with an newer version'
' of pubs (v{}) than the one you are using (v{}).'
'\n'.format(repo_version, code_version) +
'You should not use pubs until you install the '
'newest version.')
sys.exit()
elif repo_version < code_version:
ui.message(
'warning: your repository version (v{})'.format(repo_version)
+ 'must be updated to version {}.\n'.format(code_version)
+ "run 'pubs update'.")
sys.exit()
def execute(raw_args=sys.argv): def execute(raw_args=sys.argv):
# loading config # loading config
if len(raw_args) > 1 and raw_args[1] != 'init': if len(raw_args) > 1 and raw_args[1] != 'init':
try: try:
conf = config.load_conf(check_conf=True) conf = config.load_conf(check=False)
if update.update_check(conf): # an update happened, reload conf.
conf = config.load_conf(check=False)
config.check_conf(conf)
except IOError as e: except IOError as e:
print('error: {}'.format(str(e))) print('error: {}'.format(str(e)))
sys.exit() sys.exit()
@ -67,8 +47,6 @@ def execute(raw_args=sys.argv):
uis.init_ui(conf) uis.init_ui(conf)
ui = uis.get_ui() ui = uis.get_ui()
_update_check(conf, ui)
parser = argparse.ArgumentParser(description="research papers repository") parser = argparse.ArgumentParser(description="research papers repository")
subparsers = parser.add_subparsers(title="valid commands", dest="command") subparsers = parser.add_subparsers(title="valid commands", dest="command")

@ -0,0 +1,63 @@
from . import config
from . import uis
from .__init__ import __version__
def update_check(conf):
"""Runs an update if necessary, and return True in that case."""
code_version = __version__.split('.')
try:
repo_version = conf['internal']['version'].split('.')
except KeyError:
repo_version = ['0', '5', '0']
if repo_version > code_version:
uis.init_ui(config.load_default_conf())
ui = uis.get_ui()
ui.warning(
'Your repository was generated with an newer version'
' of pubs (v{}) than the one you are using (v{}).'
'\n'.format(repo_version, code_version) +
'You should not use pubs until you install the '
'newest version.')
sys.exit()
elif repo_version < code_version:
return update(conf, code_version, repo_version)
return False
def update(conf, code_version, repo_version):
"""Runs an update if necessary, and return True in that case."""
if repo_version == ['0', '5', '0']: # we need to update
default_conf = config.load_default_conf()
uis.init_ui(config.load_default_conf())
ui = uis.get_ui()
for key in ['pubsdir', 'docsdir', 'edit_cmd', 'open_cmd']:
default_conf['main'][key] = conf['pubs'][key]
if conf['pubs']['import_move']:
default_conf['main']['add_doc'] = 'move'
elif conf['pubs']['import_copy']:
default_conf['main']['add_doc'] = 'copy'
else:
default_conf['main']['add_doc'] = 'link'
backup_path = config.get_confpath() + '.old'
config.save_conf(conf, path=backup_path)
config.save_conf(default_conf)
ui.warning(
'Your configuration file has been updated. '
'The old file has been moved to `{}`. '.format(backup_path) +
'Some, but not all, of your settings has been transferred '
'to the new file.\n'
'You can inspect and modify your configuration '
' using the `pubs config` command.'
)
return True
return False

@ -2,71 +2,70 @@
import unittest import unittest
import dotdot import dotdot
from pubs import configs from pubs.config import conf
from pubs.configs import config
from pubs.p3 import configparser from pubs.p3 import configparser
class TestConfig(unittest.TestCase): # class TestConfig(unittest.TestCase):
#
def test_create_config(self): # def test_create_config(self):
a = configs.Config() # a = configs.Config()
a.as_global() # a.as_global()
self.assertEqual(a, config()) # self.assertEqual(a, config())
#
def test_config_content(self): # def test_config_content(self):
a = configs.Config() # a = configs.Config()
a.as_global() # a.as_global()
#
self.assertEqual(config().pubsdir, configs.DFT_CONFIG['pubsdir']) # self.assertEqual(config().pubsdir, configs.DFT_CONFIG['pubsdir'])
self.assertEqual(config().color, configs.str2bool(configs.DFT_CONFIG['color'])) # self.assertEqual(config().color, configs.str2bool(configs.DFT_CONFIG['color']))
#
def test_set(self): # def test_set(self):
a = configs.Config() # a = configs.Config()
a.as_global() # a.as_global()
config().color = 'no' # config().color = 'no'
self.assertEqual(config().color, False) # self.assertEqual(config().color, False)
self.assertEqual(config('pubs').color, False) # self.assertEqual(config('pubs').color, False)
# booleans type for new variables are memorized, but not saved. # # booleans type for new variables are memorized, but not saved.
config().bla = True # config().bla = True
self.assertEqual(config().bla, True) # self.assertEqual(config().bla, True)
self.assertEqual(config('pubs').bla, True) # self.assertEqual(config('pubs').bla, True)
#
with self.assertRaises(configparser.NoOptionError): # with self.assertRaises(configparser.NoOptionError):
config()._cfg.get(configs.MAIN_SECTION, '_section') # config()._cfg.get(configs.MAIN_SECTION, '_section')
#
def test_reload(self): # def test_reload(self):
#
default_color = configs.DFT_CONFIG['color'] # default_color = configs.DFT_CONFIG['color']
#
a = configs.Config() # a = configs.Config()
a.as_global() # a.as_global()
a.color = False # a.color = False
a.bla = 'foo' # a.bla = 'foo'
config.color = not configs.str2bool(default_color) # config.color = not configs.str2bool(default_color)
self.assertEqual(config().color, not configs.str2bool(default_color)) # self.assertEqual(config().color, not configs.str2bool(default_color))
#
b = configs.Config() # b = configs.Config()
b.as_global() # b.as_global()
self.assertEqual(b, config()) # self.assertEqual(b, config())
self.assertEqual(config().color, configs.str2bool(default_color)) # self.assertEqual(config().color, configs.str2bool(default_color))
#
def test_exception(self): # def test_exception(self):
#
a = configs.Config() # a = configs.Config()
a.as_global() # a.as_global()
#
with self.assertRaises(configparser.NoOptionError): # with self.assertRaises(configparser.NoOptionError):
config().color2 # config().color2
self.assertEqual(config().get('color2', default = 'blue'), 'blue') # self.assertEqual(config().get('color2', default = 'blue'), 'blue')
#
with self.assertRaises(configparser.NoSectionError): # with self.assertRaises(configparser.NoSectionError):
config(section = 'bla3').color # config(section = 'bla3').color
self.assertEqual(config(section = 'bla3').get('color', default = 'green'), 'green') # self.assertEqual(config(section = 'bla3').get('color', default = 'green'), 'green')
self.assertEqual(config(section = 'bla3').get('color', default = config().color), True) # self.assertEqual(config(section = 'bla3').get('color', default = config().color), True)
#
def test_keywords(self): # def test_keywords(self):
a = configs.Config(pubs_dir = '/blabla') # a = configs.Config(pubs_dir = '/blabla')
self.assertEqual(a.pubs_dir, '/blabla') # self.assertEqual(a.pubs_dir, '/blabla')
if __name__ == '__main__': if __name__ == '__main__':

@ -5,7 +5,8 @@ import os
import dotdot import dotdot
import fake_env import fake_env
from pubs import content, filebroker, databroker, datacache, configs from pubs import content, filebroker, databroker, datacache
from pubs.config import conf
import str_fixtures import str_fixtures
from pubs import endecoder from pubs import endecoder
@ -20,7 +21,7 @@ class TestDataBroker(unittest.TestCase):
page99_bibentry = ende.decode_bibdata(str_fixtures.bibtex_raw0) page99_bibentry = ende.decode_bibdata(str_fixtures.bibtex_raw0)
for db_class in [databroker.DataBroker, datacache.DataCache]: for db_class in [databroker.DataBroker, datacache.DataCache]:
self.fs = fake_env.create_fake_fs([content, filebroker, configs]) self.fs = fake_env.create_fake_fs([content, filebroker, conf])
db = db_class('tmp', create=True) db = db_class('tmp', create=True)

Loading…
Cancel
Save