Rewritten config module
It depends on configobj, is cleaner and simpler than the previous implementation. It adds comments in the config file, and type verification. Related: #18
This commit is contained in:
parent
da4723d6db
commit
93c54939b3
@ -1,5 +1,4 @@
|
|||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..configs import config
|
|
||||||
from .. import bibstruct
|
from .. import bibstruct
|
||||||
from .. import content
|
from .. import content
|
||||||
from .. import repo
|
from .. import repo
|
||||||
@ -28,12 +27,12 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def bibentry_from_editor(ui, rp):
|
def bibentry_from_editor(conf, ui, rp):
|
||||||
again = True
|
again = True
|
||||||
bibstr = templates.add_bib
|
bibstr = templates.add_bib
|
||||||
while again:
|
while again:
|
||||||
try:
|
try:
|
||||||
bibstr = content.editor_input(config().edit_cmd,
|
bibstr = content.editor_input(conf['main']['edit_cmd'],
|
||||||
bibstr,
|
bibstr,
|
||||||
suffix='.bib')
|
suffix='.bib')
|
||||||
if bibstr == templates.add_bib:
|
if bibstr == templates.add_bib:
|
||||||
@ -57,7 +56,7 @@ def bibentry_from_editor(ui, rp):
|
|||||||
return bibentry
|
return bibentry
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
||||||
:param docfile: path (no url yet) to a pdf or ps file
|
:param docfile: path (no url yet) to a pdf or ps file
|
||||||
@ -69,12 +68,12 @@ def command(args):
|
|||||||
tags = args.tags
|
tags = args.tags
|
||||||
citekey = args.citekey
|
citekey = args.citekey
|
||||||
|
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
|
|
||||||
# get bibtex entry
|
# get bibtex entry
|
||||||
if bibfile is None:
|
if bibfile is None:
|
||||||
if args.doi is None and args.isbn is None:
|
if args.doi is None and args.isbn is None:
|
||||||
bibentry = bibentry_from_editor(ui, rp)
|
bibentry = bibentry_from_editor(conf, ui, rp)
|
||||||
else:
|
else:
|
||||||
if args.doi is not None:
|
if args.doi is not None:
|
||||||
bibentry_raw = apis.doi2bibtex(args.doi)
|
bibentry_raw = apis.doi2bibtex(args.doi)
|
||||||
@ -124,18 +123,28 @@ def command(args):
|
|||||||
'{}, using {} instead.').format(bib_docfile, docfile))
|
'{}, using {} instead.').format(bib_docfile, docfile))
|
||||||
|
|
||||||
# create the paper
|
# create the paper
|
||||||
|
copy = args.copy
|
||||||
|
if copy is None:
|
||||||
|
copy = conf['main']['doc_add'] in ('copy', 'move')
|
||||||
|
move = args.move
|
||||||
|
if move is None:
|
||||||
|
move = conf['main']['doc_add'] == 'move'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rp.push_paper(p)
|
rp.push_paper(p)
|
||||||
if docfile is not None:
|
if docfile is not None:
|
||||||
rp.push_doc(p.citekey, docfile, copy=args.copy or args.move)
|
rp.push_doc(p.citekey, docfile, copy=copy or args.move)
|
||||||
if args.copy:
|
if copy:
|
||||||
if args.move:
|
if move:
|
||||||
content.remove_file(docfile)
|
content.remove_file(docfile)
|
||||||
# elif ui.input_yn('{} has been copied into pubs; should the original be removed?'.format(color.dye_out(docfile, 'bold'))):
|
|
||||||
# content.remove_file(docfile)
|
|
||||||
|
|
||||||
ui.message('added to pubs:\n{}'.format(pretty.paper_oneliner(p)))
|
ui.message('added to pubs:\n{}'.format(pretty.paper_oneliner(p)))
|
||||||
|
if copy:
|
||||||
|
if move:
|
||||||
|
ui.message('{} was moved to the pubs repository.'.format(docfile))
|
||||||
|
else:
|
||||||
|
ui.message('{} was copied to the pubs repository.'.format(docfile))
|
||||||
|
|
||||||
except ValueError as v:
|
except ValueError as v:
|
||||||
ui.error(v.message)
|
ui.error(v.message)
|
||||||
ui.exit(1)
|
ui.exit(1)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from .. import repo
|
from .. import repo
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import content
|
from .. import content
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
||||||
:param docfile: path (no url yet) to a pdf or ps file
|
:param docfile: path (no url yet) to a pdf or ps file
|
||||||
@ -28,7 +27,7 @@ def command(args):
|
|||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
|
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
paper = rp.pull_paper(args.citekey)
|
paper = rp.pull_paper(args.citekey)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from ..paper import Paper
|
from ..paper import Paper
|
||||||
from .. import repo
|
from .. import repo
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..endecoder import EnDecoder
|
from ..endecoder import EnDecoder
|
||||||
from ..utils import resolve_citekey
|
from ..utils import resolve_citekey
|
||||||
@ -16,12 +16,12 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
meta = args.meta
|
meta = args.meta
|
||||||
|
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
||||||
paper = rp.pull_paper(citekey)
|
paper = rp.pull_paper(citekey)
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from .. import repo
|
from .. import repo
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import endecoder
|
from .. import endecoder
|
||||||
|
|
||||||
@ -14,14 +13,14 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
# :param bib_format (only 'bibtex' now)
|
# :param bib_format (only 'bibtex' now)
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
|
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
papers = [rp.pull_paper(c) for c in args.citekeys]
|
papers = [rp.pull_paper(c) for c in args.citekeys]
|
||||||
|
@ -6,7 +6,7 @@ from .. import endecoder
|
|||||||
from .. import bibstruct
|
from .. import bibstruct
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..paper import Paper
|
from ..paper import Paper
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..content import system_path, read_file
|
from ..content import system_path, read_file
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ def many_from_path(bibpath):
|
|||||||
return papers
|
return papers
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
:param bibpath: path (no url yet) to a bibliography file
|
:param bibpath: path (no url yet) to a bibliography file
|
||||||
"""
|
"""
|
||||||
@ -66,10 +66,10 @@ def command(args):
|
|||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
bibpath = args.bibpath
|
bibpath = args.bibpath
|
||||||
copy = args.copy
|
copy = args.copy
|
||||||
|
|
||||||
if copy is None:
|
if copy is None:
|
||||||
copy = config().import_copy
|
copy = conf['main']['doc_add'] in ('copy', 'move')
|
||||||
rp = repo.Repository(config())
|
|
||||||
|
rp = repo.Repository(conf)
|
||||||
# Extract papers from bib
|
# Extract papers from bib
|
||||||
papers = many_from_path(bibpath)
|
papers = many_from_path(bibpath)
|
||||||
keys = args.keys or papers.keys()
|
keys = args.keys or papers.keys()
|
||||||
@ -85,7 +85,8 @@ def command(args):
|
|||||||
if docfile is None:
|
if docfile is None:
|
||||||
ui.warning("no file for {}.".format(p.citekey))
|
ui.warning("no file for {}.".format(p.citekey))
|
||||||
else:
|
else:
|
||||||
rp.push_doc(p.citekey, docfile, copy=args.copy)
|
rp.push_doc(p.citekey, docfile, copy=copy)
|
||||||
|
#FIXME should move the file if configured to do so.
|
||||||
except KeyError:
|
except KeyError:
|
||||||
ui.error('no entry found for citekey {}.'.format(k))
|
ui.error('no entry found for citekey {}.'.format(k))
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
|
@ -2,25 +2,25 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..repo import Repository
|
from ..repo import Repository
|
||||||
from ..content import system_path, check_directory
|
from ..content import system_path, check_directory
|
||||||
|
from .. import config
|
||||||
|
|
||||||
def parser(subparsers):
|
def parser(subparsers):
|
||||||
parser = subparsers.add_parser('init',
|
parser = subparsers.add_parser('init',
|
||||||
help="initialize the pubs directory")
|
help="initialize the pubs directory")
|
||||||
parser.add_argument('-p', '--pubsdir', default=None,
|
parser.add_argument('-p', '--pubsdir', default=None,
|
||||||
help='path to pubs directory (if none, ~/.ubs is used)')
|
help='directory where to put the pubs repository (if none, ~/.pubs is used)')
|
||||||
parser.add_argument('-d', '--docsdir', default='docsdir://',
|
parser.add_argument('-d', '--docsdir', default='docsdir://',
|
||||||
help=('path to document directory (if not specified, documents will'
|
help=('path to document directory (if not specified, documents will'
|
||||||
'be stored in /path/to/pubsdir/doc/)'))
|
'be stored in /path/to/pubsdir/doc/)'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""Create a .pubs directory"""
|
"""Create a .pubs directory"""
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
@ -39,8 +39,8 @@ def command(args):
|
|||||||
|
|
||||||
ui.message('Initializing pubs in {}'.format(color.dye_out(pubsdir, color.filepath)))
|
ui.message('Initializing pubs in {}'.format(color.dye_out(pubsdir, color.filepath)))
|
||||||
|
|
||||||
config().pubsdir = pubsdir
|
conf['main']['pubsdir'] = pubsdir
|
||||||
config().docsdir = docsdir
|
conf['main']['docsdir'] = docsdir
|
||||||
config().save()
|
config.save_conf(conf)
|
||||||
|
|
||||||
Repository(config(), create=True)
|
Repository(conf, create=True)
|
||||||
|
@ -3,7 +3,6 @@ from datetime import datetime
|
|||||||
from .. import repo
|
from .. import repo
|
||||||
from .. import pretty
|
from .. import pretty
|
||||||
from .. import bibstruct
|
from .. import bibstruct
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
|
|
||||||
|
|
||||||
@ -36,9 +35,9 @@ def date_added(p):
|
|||||||
return p.added or datetime(1, 1, 1)
|
return p.added or datetime(1, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
papers = filter(lambda p: filter_paper(p, args.query,
|
papers = filter(lambda p: filter_paper(p, args.query,
|
||||||
case_sensitive=args.case_sensitive),
|
case_sensitive=args.case_sensitive),
|
||||||
rp.all_papers())
|
rp.all_papers())
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from .. import repo
|
from .. import repo
|
||||||
from .. import content
|
from .. import content
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers):
|
def parser(subparsers):
|
||||||
parser = subparsers.add_parser('note',
|
parser = subparsers.add_parser('note',
|
||||||
help='edit the note attached to a paper')
|
help='edit the note attached to a paper')
|
||||||
@ -11,17 +11,16 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
|
|
||||||
|
rp = repo.Repository(conf)
|
||||||
rp = repo.Repository(config())
|
|
||||||
if not rp.databroker.exists(args.citekey):
|
if not rp.databroker.exists(args.citekey):
|
||||||
ui.error("citekey {} not found".format(args.citekey))
|
ui.error("citekey {} not found".format(args.citekey))
|
||||||
ui.exit(1)
|
ui.exit(1)
|
||||||
|
|
||||||
notepath = rp.databroker.real_notepath(args.citekey)
|
notepath = rp.databroker.real_notepath(args.citekey)
|
||||||
content.edit_file(config().edit_cmd, notepath, temporary=False)
|
content.edit_file(conf['main']['edit_cmd'], notepath, temporary=False)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .. import repo
|
from .. import repo
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..content import system_path
|
from ..content import system_path
|
||||||
@ -17,17 +17,19 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
with_command = args.with_command
|
with_command = args.with_command
|
||||||
|
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
||||||
paper = rp.pull_paper(citekey)
|
paper = rp.pull_paper(citekey)
|
||||||
|
|
||||||
if with_command is None:
|
if with_command is None:
|
||||||
with_command = config().open_cmd
|
with_command = conf['main']['open_cmd']
|
||||||
|
if with_command is None: # default in conf have not been changed
|
||||||
|
pass # TODO platform specific
|
||||||
|
|
||||||
if paper.docpath is None:
|
if paper.docpath is None:
|
||||||
ui.error('No document associated with the entry {}.'.format(
|
ui.error('No document associated with the entry {}.'.format(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from .. import repo
|
from .. import repo
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
|
|
||||||
|
|
||||||
@ -13,11 +12,11 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
force = args.force
|
force = args.force
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
|
|
||||||
if force is None:
|
if force is None:
|
||||||
are_you_sure = (("Are you sure you want to delete paper(s) [{}]"
|
are_you_sure = (("Are you sure you want to delete paper(s) [{}]"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..configs import config
|
|
||||||
from .. import bibstruct
|
from .. import bibstruct
|
||||||
from .. import content
|
from .. import content
|
||||||
from .. import repo
|
from .. import repo
|
||||||
@ -14,14 +13,14 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""
|
"""
|
||||||
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
:param bibfile: bibtex file (in .bib, .bibml or .yaml format.
|
||||||
:param docfile: path (no url yet) to a pdf or ps file
|
:param docfile: path (no url yet) to a pdf or ps file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
rp = repo.Repository(config())
|
rp = repo.Repository(conf)
|
||||||
|
|
||||||
paper = rp.pull_paper(args.citekey)
|
paper = rp.pull_paper(args.citekey)
|
||||||
rp.rename_paper(paper, args.new_citekey)
|
rp.rename_paper(paper, args.new_citekey)
|
||||||
|
@ -20,7 +20,7 @@ The different use cases are :
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from ..repo import Repository, InvalidReference
|
from ..repo import Repository, InvalidReference
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import pretty
|
from .. import pretty
|
||||||
from .. import color
|
from .. import color
|
||||||
@ -72,7 +72,7 @@ def _tag_groups(tags):
|
|||||||
minus_tags.append(tag[1:])
|
minus_tags.append(tag[1:])
|
||||||
return set(plus_tags), set(minus_tags)
|
return set(plus_tags), set(minus_tags)
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
"""Add, remove and show tags"""
|
"""Add, remove and show tags"""
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
@ -80,7 +80,7 @@ def command(args):
|
|||||||
tags = args.tags
|
tags = args.tags
|
||||||
|
|
||||||
|
|
||||||
rp = Repository(config())
|
rp = Repository(conf)
|
||||||
|
|
||||||
if citekeyOrTag is None:
|
if citekeyOrTag is None:
|
||||||
ui.message(color.dye_out(' '.join(sorted(rp.get_tags())), color.tag))
|
ui.message(color.dye_out(' '.join(sorted(rp.get_tags())), color.tag))
|
||||||
|
@ -2,7 +2,7 @@ import sys
|
|||||||
|
|
||||||
from .. import repo
|
from .. import repo
|
||||||
from .. import color
|
from .. import color
|
||||||
from ..configs import config
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..__init__ import __version__
|
from ..__init__ import __version__
|
||||||
|
|
||||||
@ -11,12 +11,14 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
|
|
||||||
code_version = __version__
|
code_version = __version__.split('.')
|
||||||
repo_version = int(config().version)
|
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:
|
if repo_version == code_version:
|
||||||
ui.message('Your pubs repository is up-to-date.')
|
ui.message('Your pubs repository is up-to-date.')
|
||||||
@ -27,11 +29,11 @@ def command(args):
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
msg = ("You should backup the pubs directory {} before continuing."
|
msg = ("You should backup the pubs directory {} before continuing."
|
||||||
"Continue ?").format(color.dye_out(config().papers_dir, color.filepath))
|
"Continue ?").format(color.dye_out(conf['main']['pubsdir'], color.filepath))
|
||||||
sure = ui.input_yn(question=msg, default='n')
|
sure = ui.input_yn(question=msg, default='n')
|
||||||
if not sure:
|
if not sure:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
#TODO: update!!
|
||||||
# config().version = repo_version
|
# conf['internal']['version'] = repo_version
|
||||||
# config().save()
|
# conf['internal']['version']
|
||||||
|
@ -11,7 +11,7 @@ def parser(subparsers):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def command(args):
|
def command(conf, args):
|
||||||
|
|
||||||
ui = get_ui()
|
ui = get_ui()
|
||||||
search_string = args.search_string
|
search_string = args.search_string
|
||||||
|
1
pubs/config/__init__.py
Normal file
1
pubs/config/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .conf import load_default_conf, load_conf, save_conf, get_pubspath
|
49
pubs/config/conf.py
Normal file
49
pubs/config/conf.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import configobj
|
||||||
|
import validate
|
||||||
|
|
||||||
|
from .spec import configspec
|
||||||
|
|
||||||
|
|
||||||
|
DFT_CONFIG_PATH = os.path.expanduser('~/.pubsrc')
|
||||||
|
|
||||||
|
def load_default_conf():
|
||||||
|
"""Loads the default configuration"""
|
||||||
|
default_conf = configobj.ConfigObj(configspec=configspec)
|
||||||
|
validator = validate.Validator()
|
||||||
|
default_conf.validate(validator, copy=True)
|
||||||
|
return default_conf
|
||||||
|
|
||||||
|
def get_pubspath(verify=True):
|
||||||
|
"""Returns the pubs path.
|
||||||
|
If verify is True, verify that pubs.conf exist in the directory,
|
||||||
|
and exit with an error if not.
|
||||||
|
"""
|
||||||
|
confpath = DFT_CONFIG_PATH
|
||||||
|
if 'PUBSCONF' in os.environ:
|
||||||
|
confpath = os.path.abspath(os.path.expanduser(os.environ['PUBSCONF']))
|
||||||
|
if verify:
|
||||||
|
if not os.path.isfile(confpath):
|
||||||
|
from .. import uis
|
||||||
|
ui = uis.get_ui()
|
||||||
|
ui.error('configuration file not found at `{}`'.format(confpath))
|
||||||
|
ui.exit(error_code=1)
|
||||||
|
return confpath
|
||||||
|
|
||||||
|
def load_conf(check_conf=True):
|
||||||
|
"""Load the user config"""
|
||||||
|
pubspath = get_pubspath(verify=True)
|
||||||
|
with open(pubspath, 'r') as f:
|
||||||
|
conf = configobj.ConfigObj(f.readlines(), configspec=configspec)
|
||||||
|
|
||||||
|
if check_conf:
|
||||||
|
validator = validate.Validator()
|
||||||
|
results = conf.validate(validator, copy=True)
|
||||||
|
assert results == True, '{}'.format(results) # TODO: precise error dialog when parsing error
|
||||||
|
|
||||||
|
return conf
|
||||||
|
|
||||||
|
def save_conf(conf):
|
||||||
|
with open(get_pubspath(verify=False), 'w') as f:
|
||||||
|
conf.write(outfile=f)
|
55
pubs/config/spec.py
Normal file
55
pubs/config/spec.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
from .. import __version__
|
||||||
|
|
||||||
|
configspec = """
|
||||||
|
[main]
|
||||||
|
|
||||||
|
# Where the pubs repository files (bibtex, metadata, notes) are located
|
||||||
|
pubsdir = string(default='~/pubs')
|
||||||
|
|
||||||
|
# Where the documents files are located (default: $(pubsdir)/doc/)
|
||||||
|
docsdir = string(default="docsdir://")
|
||||||
|
|
||||||
|
# Specify if a document should be copied or moved in the docdir, or only
|
||||||
|
# linked when adding a publication.
|
||||||
|
doc_add = option('copy', 'move', 'link', default='move')
|
||||||
|
|
||||||
|
# if True, pubs will ask confirmation before copying/moving/linking the
|
||||||
|
# document file.
|
||||||
|
doc_add_ask = boolean(default=True)
|
||||||
|
|
||||||
|
# the command to use when opening document files
|
||||||
|
open_cmd = string(default=None)
|
||||||
|
|
||||||
|
# which editor to use when editing bibtex files.
|
||||||
|
# if using a graphical editor, use the --wait or --block option, i.e.:
|
||||||
|
# "atom --wait"
|
||||||
|
# "kate --block"
|
||||||
|
edit_cmd = string(default=None)
|
||||||
|
|
||||||
|
|
||||||
|
[formating]
|
||||||
|
|
||||||
|
# Enable bold formatting, if the terminal supports it.
|
||||||
|
bold = boolean(default=True)
|
||||||
|
|
||||||
|
# Enable italics, if the terminal supports it.
|
||||||
|
italics = boolean(default=True)
|
||||||
|
|
||||||
|
# Enable colors, if the terminal supports it.
|
||||||
|
color = boolean(default=True)
|
||||||
|
|
||||||
|
|
||||||
|
[theme]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
# comma-separated list of the plugins to load
|
||||||
|
active = list(default=list())
|
||||||
|
|
||||||
|
[internal]
|
||||||
|
# The version of this configuration file. Do not edit.
|
||||||
|
version = string(min=5, default='{}')
|
||||||
|
|
||||||
|
""".format(__version__).split('\n')
|
112
pubs/configs.py
112
pubs/configs.py
@ -1,112 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import collections
|
|
||||||
|
|
||||||
from .p3 import configparser, ConfigParser, _read_config
|
|
||||||
|
|
||||||
from .content import check_file, _open
|
|
||||||
from . import __version__
|
|
||||||
|
|
||||||
# constant stuff (DFT = DEFAULT)
|
|
||||||
|
|
||||||
MAIN_SECTION = 'pubs'
|
|
||||||
DFT_CONFIG_PATH = os.path.expanduser('~/.pubsrc')
|
|
||||||
try:
|
|
||||||
DFT_EDIT_CMD = os.environ['EDITOR']
|
|
||||||
except KeyError:
|
|
||||||
DFT_EDIT_CMD = 'vi'
|
|
||||||
|
|
||||||
DFT_PLUGINS = ''
|
|
||||||
|
|
||||||
DFT_CONFIG = collections.OrderedDict([
|
|
||||||
('pubsdir', os.path.expanduser('~/.pubs')),
|
|
||||||
('docsdir', ''),
|
|
||||||
('import_copy', True),
|
|
||||||
('import_move', False),
|
|
||||||
('color', True),
|
|
||||||
('version', __version__),
|
|
||||||
('version_warning', True),
|
|
||||||
('open_cmd', 'open'),
|
|
||||||
('edit_cmd', DFT_EDIT_CMD),
|
|
||||||
('plugins', DFT_PLUGINS)
|
|
||||||
])
|
|
||||||
|
|
||||||
BOOLEANS = {'import_copy', 'import_move', 'color', 'version_warning'}
|
|
||||||
|
|
||||||
|
|
||||||
# package-shared config that can be accessed using :
|
|
||||||
# from configs import config
|
|
||||||
_config = None
|
|
||||||
|
|
||||||
|
|
||||||
def config(section=MAIN_SECTION):
|
|
||||||
if _config is None:
|
|
||||||
raise ValueError('not config instanciated yet')
|
|
||||||
_config._section = section
|
|
||||||
return _config
|
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
object.__setattr__(self, '_section', MAIN_SECTION) # active section
|
|
||||||
object.__setattr__(self, '_cfg', ConfigParser())
|
|
||||||
|
|
||||||
self._cfg.add_section(self._section)
|
|
||||||
for name, value in DFT_CONFIG.items():
|
|
||||||
self._cfg.set(self._section, name, str(value))
|
|
||||||
|
|
||||||
for name, value in kwargs.items():
|
|
||||||
self.__setattr__(name, value)
|
|
||||||
|
|
||||||
def as_global(self):
|
|
||||||
global _config
|
|
||||||
_config = self
|
|
||||||
|
|
||||||
def load(self, path=DFT_CONFIG_PATH):
|
|
||||||
if not check_file(path, fail=False):
|
|
||||||
raise IOError(("The configuration file {} does not exist."
|
|
||||||
" Did you run 'pubs init' ?").format(path))
|
|
||||||
b_flag = ''
|
|
||||||
if sys.version_info[0] == 2: # HACK, FIXME please
|
|
||||||
b_flag = 'b'
|
|
||||||
with _open(path, 'r{}+'.format(b_flag)) as f:
|
|
||||||
_read_config(self._cfg, f)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def save(self, path=DFT_CONFIG_PATH):
|
|
||||||
b_flag = ''
|
|
||||||
if sys.version_info[0] == 2: # HACK, FIXME please
|
|
||||||
b_flag = 'b'
|
|
||||||
with _open(path, 'w{}+'.format(b_flag)) as f:
|
|
||||||
self._cfg.write(f)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
if name in ('_cfg', '_section'):
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
else:
|
|
||||||
if type(value) is bool:
|
|
||||||
BOOLEANS.add(name)
|
|
||||||
self._cfg.set(self._section, name, str(value))
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
value = self._cfg.get(self._section, name)
|
|
||||||
if name in BOOLEANS:
|
|
||||||
value = str2bool(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get(self, name, default=None):
|
|
||||||
try:
|
|
||||||
return self.__getattr__(name)
|
|
||||||
except (configparser.NoOptionError, configparser.NoSectionError):
|
|
||||||
return default
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
for name, value in self._cfg.items(self._section):
|
|
||||||
if name in BOOLEANS:
|
|
||||||
value = str2bool(value)
|
|
||||||
yield name, value
|
|
||||||
|
|
||||||
|
|
||||||
def str2bool(s):
|
|
||||||
return str(s).lower() in ('yes', 'true', 't', 'y', '1')
|
|
@ -4,7 +4,7 @@ import argparse
|
|||||||
import collections
|
import collections
|
||||||
|
|
||||||
from . import uis
|
from . import uis
|
||||||
from . import configs
|
from . import config
|
||||||
from . import commands
|
from . import commands
|
||||||
from . import plugins
|
from . import plugins
|
||||||
from .__init__ import __version__
|
from .__init__ import __version__
|
||||||
@ -31,12 +31,11 @@ CORE_CMDS = collections.OrderedDict([
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def _update_check(config, ui):
|
def _update_check(conf, ui):
|
||||||
if config.version_warning:
|
|
||||||
code_version = __version__.split('.')
|
code_version = __version__.split('.')
|
||||||
if len(config.version) == 1: # support for deprecated version scheme.
|
if len(conf['internal']['version']) == 1: # support for deprecated version scheme.
|
||||||
config.version = '0.{}.0'.format(config.version)
|
conf['internal']['version'] = '0.{}.0'.format(conf['internal']['version'])
|
||||||
repo_version = config.version.split('.')
|
repo_version = conf['internal']['version'].split('.')
|
||||||
|
|
||||||
if repo_version > code_version:
|
if repo_version > code_version:
|
||||||
ui.warning(
|
ui.warning(
|
||||||
@ -44,8 +43,7 @@ def _update_check(config, ui):
|
|||||||
' of pubs (v{}) than the one you are using (v{}).'
|
' of pubs (v{}) than the one you are using (v{}).'
|
||||||
'\n'.format(repo_version, code_version) +
|
'\n'.format(repo_version, code_version) +
|
||||||
'You should not use pubs until you install the '
|
'You should not use pubs until you install the '
|
||||||
'newest version. (use version_warning in you pubsrc '
|
'newest version.')
|
||||||
'to bypass this error)')
|
|
||||||
sys.exit()
|
sys.exit()
|
||||||
elif repo_version < code_version:
|
elif repo_version < code_version:
|
||||||
ui.message(
|
ui.message(
|
||||||
@ -57,19 +55,19 @@ def _update_check(config, ui):
|
|||||||
|
|
||||||
def execute(raw_args=sys.argv):
|
def execute(raw_args=sys.argv):
|
||||||
# loading config
|
# loading config
|
||||||
config = configs.Config()
|
|
||||||
if len(raw_args) > 1 and raw_args[1] != 'init':
|
if len(raw_args) > 1 and raw_args[1] != 'init':
|
||||||
try:
|
try:
|
||||||
config.load()
|
conf = config.load_conf(check_conf=True)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
print('error: {}'.format(str(e)))
|
print('error: {}'.format(str(e)))
|
||||||
sys.exit()
|
sys.exit()
|
||||||
config.as_global()
|
else:
|
||||||
|
conf = config.load_default_conf()
|
||||||
|
|
||||||
uis.init_ui(config)
|
uis.init_ui(conf)
|
||||||
ui = uis.get_ui()
|
ui = uis.get_ui()
|
||||||
|
|
||||||
_update_check(config, 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")
|
||||||
@ -80,7 +78,7 @@ def execute(raw_args=sys.argv):
|
|||||||
cmd_funcs[cmd_name] = cmd_mod.command
|
cmd_funcs[cmd_name] = cmd_mod.command
|
||||||
|
|
||||||
# Extend with plugin commands
|
# Extend with plugin commands
|
||||||
plugins.load_plugins(ui, config.plugins.split())
|
plugins.load_plugins(ui, conf['plugins']['active'])
|
||||||
for p in plugins.get_plugins().values():
|
for p in plugins.get_plugins().values():
|
||||||
cmd_funcs.update(p.get_commands(subparsers))
|
cmd_funcs.update(p.get_commands(subparsers))
|
||||||
|
|
||||||
@ -89,4 +87,4 @@ def execute(raw_args=sys.argv):
|
|||||||
cmd = args.command
|
cmd = args.command
|
||||||
del args.command
|
del args.command
|
||||||
|
|
||||||
cmd_funcs[cmd](args)
|
cmd_funcs[cmd](conf, args)
|
||||||
|
@ -22,10 +22,10 @@ class InvalidReference(Exception):
|
|||||||
|
|
||||||
class Repository(object):
|
class Repository(object):
|
||||||
|
|
||||||
def __init__(self, config, create=False):
|
def __init__(self, conf, create=False):
|
||||||
self.config = config
|
self.conf = conf
|
||||||
self._citekeys = None
|
self._citekeys = None
|
||||||
self.databroker = DataCache(self.config.pubsdir, create=create)
|
self.databroker = DataCache(self.conf['main']['pubsdir'], create=create)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def citekeys(self):
|
def citekeys(self):
|
||||||
@ -133,7 +133,7 @@ class Repository(object):
|
|||||||
def push_doc(self, citekey, docfile, copy=None):
|
def push_doc(self, citekey, docfile, copy=None):
|
||||||
p = self.pull_paper(citekey)
|
p = self.pull_paper(citekey)
|
||||||
if copy is None:
|
if copy is None:
|
||||||
copy = self.config.import_copy
|
copy = self.conf['main']['doc_add'] in ('copy', 'move')
|
||||||
if copy:
|
if copy:
|
||||||
docfile = self.databroker.add_doc(citekey, docfile)
|
docfile = self.databroker.add_doc(citekey, docfile)
|
||||||
else:
|
else:
|
||||||
|
12
pubs/uis.py
12
pubs/uis.py
@ -39,16 +39,14 @@ def init_ui(conf):
|
|||||||
|
|
||||||
class PrintUI(object):
|
class PrintUI(object):
|
||||||
|
|
||||||
def __init__(self, conf=None):
|
def __init__(self, conf):
|
||||||
"""
|
"""
|
||||||
:param conf: if None, conservative default values are used.
|
:param conf: if None, conservative default values are used.
|
||||||
Useful to instanciate the UI before parsing the config file.
|
Useful to instanciate the UI before parsing the config file.
|
||||||
"""
|
"""
|
||||||
if conf is None:
|
color.setup(color=conf['formating']['color'],
|
||||||
color.setup()
|
bold=conf['formating']['bold'],
|
||||||
else:
|
italic=conf['formating']['italics'])
|
||||||
color.setup(color=True, bold=True, italic=True)
|
|
||||||
# color.setup(color=conf.color, bold=conf.bold, italic=conf.italic)
|
|
||||||
self.encoding = _get_encoding(conf)
|
self.encoding = _get_encoding(conf)
|
||||||
self._stdout = codecs.getwriter(self.encoding)(_get_raw_stdout(),
|
self._stdout = codecs.getwriter(self.encoding)(_get_raw_stdout(),
|
||||||
errors='replace')
|
errors='replace')
|
||||||
@ -77,7 +75,7 @@ class InputUI(PrintUI):
|
|||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
super(InputUI, self).__init__(conf)
|
super(InputUI, self).__init__(conf)
|
||||||
self.editor = conf.edit_cmd
|
self.editor = conf['main']['edit_cmd']
|
||||||
|
|
||||||
def input(self):
|
def input(self):
|
||||||
try:
|
try:
|
||||||
|
7
setup.py
7
setup.py
@ -13,10 +13,13 @@ setup(
|
|||||||
url = 'https://github.com/pubs/pubs',
|
url = 'https://github.com/pubs/pubs',
|
||||||
|
|
||||||
description = 'command-line scientific bibliography manager',
|
description = 'command-line scientific bibliography manager',
|
||||||
packages = ['pubs', 'pubs.commands', 'pubs.templates', 'pubs.plugs'],
|
packages = ['pubs', 'pubs.config',
|
||||||
|
'pubs.commands',
|
||||||
|
'pubs.templates',
|
||||||
|
'pubs.plugs'],
|
||||||
scripts = ['pubs/pubs'],
|
scripts = ['pubs/pubs'],
|
||||||
|
|
||||||
install_requires = ['pyyaml', 'bibtexparser', 'python-dateutil', 'requests',
|
install_requires = ['pyyaml', 'bibtexparser', 'python-dateutil', 'requests', 'configobj',
|
||||||
'beautifulsoup4'], # to be made optional?
|
'beautifulsoup4'], # to be made optional?
|
||||||
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
@ -7,14 +7,15 @@ import fixtures
|
|||||||
|
|
||||||
from pubs.repo import Repository, _base27, CiteKeyCollision, InvalidReference
|
from pubs.repo import Repository, _base27, CiteKeyCollision, InvalidReference
|
||||||
from pubs.paper import Paper
|
from pubs.paper import Paper
|
||||||
from pubs import configs
|
from pubs import config
|
||||||
|
|
||||||
|
|
||||||
class TestRepo(fake_env.TestFakeFs):
|
class TestRepo(fake_env.TestFakeFs):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRepo, self).setUp()
|
super(TestRepo, self).setUp()
|
||||||
self.repo = Repository(configs.Config(), create=True)
|
default_conf = config.load_default_conf()
|
||||||
|
self.repo = Repository(default_conf, create=True)
|
||||||
self.repo.push_paper(Paper.from_bibentry(fixtures.turing_bibentry))
|
self.repo.push_paper(Paper.from_bibentry(fixtures.turing_bibentry))
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ import dotdot
|
|||||||
import fake_env
|
import fake_env
|
||||||
|
|
||||||
from pubs import pubs_cmd
|
from pubs import pubs_cmd
|
||||||
from pubs import color, content, filebroker, uis, p3, endecoder, configs
|
from pubs import color, content, filebroker, uis, p3, endecoder
|
||||||
|
from pubs.config import conf
|
||||||
|
import configobj
|
||||||
|
|
||||||
import str_fixtures
|
import str_fixtures
|
||||||
import fixtures
|
import fixtures
|
||||||
@ -56,7 +58,7 @@ class CommandTestCase(unittest.TestCase):
|
|||||||
maxDiff = 1000000
|
maxDiff = 1000000
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.fs = fake_env.create_fake_fs([content, filebroker, configs, init_cmd, import_cmd])
|
self.fs = fake_env.create_fake_fs([content, filebroker, conf, init_cmd, import_cmd, configobj])
|
||||||
self.default_pubs_dir = self.fs['os'].path.expanduser('~/.pubs')
|
self.default_pubs_dir = self.fs['os'].path.expanduser('~/.pubs')
|
||||||
|
|
||||||
def execute_cmds(self, cmds, capture_output=CAPTURE_OUTPUT):
|
def execute_cmds(self, cmds, capture_output=CAPTURE_OUTPUT):
|
||||||
@ -107,7 +109,7 @@ class CommandTestCase(unittest.TestCase):
|
|||||||
return outs
|
return outs
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
fake_env.unset_fake_fs([content, filebroker, configs, init_cmd, import_cmd])
|
fake_env.unset_fake_fs([content, filebroker, conf, init_cmd, import_cmd, configobj])
|
||||||
|
|
||||||
|
|
||||||
class DataCommandTestCase(CommandTestCase):
|
class DataCommandTestCase(CommandTestCase):
|
||||||
@ -253,7 +255,8 @@ class TestUsecase(DataCommandTestCase):
|
|||||||
|
|
||||||
def test_first(self):
|
def test_first(self):
|
||||||
correct = ['Initializing pubs in /paper_first\n',
|
correct = ['Initializing pubs in /paper_first\n',
|
||||||
'added to pubs:\n[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n',
|
'added to pubs:\n[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n'
|
||||||
|
'data/pagerank.pdf was copied to the pubs repository.\n',
|
||||||
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n',
|
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n',
|
||||||
'\n',
|
'\n',
|
||||||
'',
|
'',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user