Merge branch 'master' into feat/new_config
Add StringIO and BytesIO to FakeIO. Fixes bytes/string problem in update()
This commit is contained in:
commit
392ee0c3c1
@ -19,33 +19,29 @@ The different use cases are :
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ..repo import Repository, InvalidReference
|
from ..repo import Repository
|
||||||
|
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import pretty
|
from .. import pretty
|
||||||
from .. import color
|
from .. import color
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers):
|
def parser(subparsers):
|
||||||
parser = subparsers.add_parser('tag', help="add, remove and show tags")
|
parser = subparsers.add_parser('tag', help="add, remove and show tags")
|
||||||
parser.add_argument('citekeyOrTag', nargs='?', default = None,
|
parser.add_argument('citekeyOrTag', nargs='?', default=None,
|
||||||
help='citekey or tag.')
|
help='citekey or tag.')
|
||||||
parser.add_argument('tags', nargs='*', default = None,
|
parser.add_argument('tags', nargs='?', default=None,
|
||||||
help='If the previous argument was a citekey, then '
|
help='If the previous argument was a citekey, then '
|
||||||
'then a list of tags separated by a +.')
|
'a list of tags separated by a +.')
|
||||||
# TODO find a way to display clear help for multiple command semantics,
|
# TODO find a way to display clear help for multiple command semantics,
|
||||||
# indistinguisable for argparse. (fabien, 201306)
|
# indistinguisable for argparse. (fabien, 201306)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _parse_tags(list_tags):
|
|
||||||
"""Transform 'math-ai network -search' in ['+math', '-ai', '+network', '-search']"""
|
|
||||||
tags = []
|
|
||||||
for s in list_tags:
|
|
||||||
tags += _parse_tag_seq(s)
|
|
||||||
return tags
|
|
||||||
|
|
||||||
def _parse_tag_seq(s):
|
def _parse_tag_seq(s):
|
||||||
"""Transform 'math-ai' in ['+math', '-ai']"""
|
"""Transform 'math-ai' in ['+math', '-ai']"""
|
||||||
tags = []
|
tags = []
|
||||||
|
if s[0] == ':':
|
||||||
|
s = '-' + s[1:]
|
||||||
if s[0] not in ['+', '-']:
|
if s[0] not in ['+', '-']:
|
||||||
s = '+' + s
|
s = '+' + s
|
||||||
last = 0
|
last = 0
|
||||||
@ -62,6 +58,7 @@ def _parse_tag_seq(s):
|
|||||||
tags.append(s[last:])
|
tags.append(s[last:])
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
|
||||||
def _tag_groups(tags):
|
def _tag_groups(tags):
|
||||||
plus_tags, minus_tags = [], []
|
plus_tags, minus_tags = [], []
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@ -79,7 +76,6 @@ def command(conf, args):
|
|||||||
citekeyOrTag = args.citekeyOrTag
|
citekeyOrTag = args.citekeyOrTag
|
||||||
tags = args.tags
|
tags = args.tags
|
||||||
|
|
||||||
|
|
||||||
rp = Repository(conf)
|
rp = Repository(conf)
|
||||||
|
|
||||||
if citekeyOrTag is None:
|
if citekeyOrTag is None:
|
||||||
@ -87,20 +83,21 @@ def command(conf, args):
|
|||||||
else:
|
else:
|
||||||
if rp.databroker.exists(citekeyOrTag):
|
if rp.databroker.exists(citekeyOrTag):
|
||||||
p = rp.pull_paper(citekeyOrTag)
|
p = rp.pull_paper(citekeyOrTag)
|
||||||
if tags == []:
|
if tags is None:
|
||||||
ui.message(color.dye_out(' '.join(sorted(p.tags)), 'tag'))
|
ui.message(color.dye_out(' '.join(sorted(p.tags)), 'tag'))
|
||||||
else:
|
else:
|
||||||
add_tags, remove_tags = _tag_groups(_parse_tags(tags))
|
add_tags, remove_tags = _tag_groups(_parse_tag_seq(tags))
|
||||||
for tag in add_tags:
|
for tag in add_tags:
|
||||||
p.add_tag(tag)
|
p.add_tag(tag)
|
||||||
for tag in remove_tags:
|
for tag in remove_tags:
|
||||||
p.remove_tag(tag)
|
p.remove_tag(tag)
|
||||||
rp.push_paper(p, overwrite=True)
|
rp.push_paper(p, overwrite=True)
|
||||||
|
elif tags is not None:
|
||||||
|
ui.error(ui.error('no entry found for citekey {}.'.format(citekeyOrTag)))
|
||||||
|
ui.exit()
|
||||||
else:
|
else:
|
||||||
# case where we want to find papers with specific tags
|
# case where we want to find papers with specific tags
|
||||||
all_tags = [citekeyOrTag]
|
included, excluded = _tag_groups(_parse_tag_seq(citekeyOrTag))
|
||||||
all_tags += tags
|
|
||||||
included, excluded = _tag_groups(_parse_tags(all_tags))
|
|
||||||
papers_list = []
|
papers_list = []
|
||||||
for p in rp.all_papers():
|
for p in rp.all_papers():
|
||||||
if (p.tags.issuperset(included) and
|
if (p.tags.issuperset(included) and
|
||||||
@ -108,4 +105,4 @@ def command(conf, args):
|
|||||||
papers_list.append(p)
|
papers_list.append(p)
|
||||||
|
|
||||||
ui.message('\n'.join(pretty.paper_oneliner(p)
|
ui.message('\n'.join(pretty.paper_oneliner(p)
|
||||||
for p in papers_list))
|
for p in papers_list))
|
||||||
|
@ -27,11 +27,13 @@ def _get_encoding(conf):
|
|||||||
return enc or 'utf-8'
|
return enc or 'utf-8'
|
||||||
return conf.get('terminal-encoding', enc or 'utf-8')
|
return conf.get('terminal-encoding', enc or 'utf-8')
|
||||||
|
|
||||||
|
|
||||||
def get_ui():
|
def get_ui():
|
||||||
if _ui is None:
|
if _ui is None:
|
||||||
return PrintUI() # no editor support. (#FIXME?)
|
return PrintUI() # no editor support. (#FIXME?)
|
||||||
return _ui
|
return _ui
|
||||||
|
|
||||||
|
|
||||||
def init_ui(conf):
|
def init_ui(conf):
|
||||||
global _ui
|
global _ui
|
||||||
_ui = InputUI(conf)
|
_ui = InputUI(conf)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import StringIO
|
|
||||||
|
|
||||||
|
import io
|
||||||
from . import config
|
from . import config
|
||||||
from . import uis
|
from . import uis
|
||||||
from . import color
|
from . import color
|
||||||
@ -81,7 +81,7 @@ def update(conf, code_version, repo_version, path=None):
|
|||||||
# comparing potential changes
|
# comparing potential changes
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r') as f:
|
||||||
old_conf_text = f.read()
|
old_conf_text = f.read()
|
||||||
new_conf_text = StringIO.StringIO()
|
new_conf_text = io.BytesIO()
|
||||||
default_conf.write(outfile=new_conf_text)
|
default_conf.write(outfile=new_conf_text)
|
||||||
|
|
||||||
if new_conf_text.getvalue() != old_conf_text:
|
if new_conf_text.getvalue() != old_conf_text:
|
||||||
|
11
readme.md
11
readme.md
@ -52,6 +52,17 @@ This ensure that your reference file is always up-to-date; you can cite a paper
|
|||||||
|
|
||||||
and then add `\cite{Loeb_2012}` in your manuscript. After running the bash script, the citation will correctly appear in your compiled pdf.
|
and then add `\cite{Loeb_2012}` in your manuscript. After running the bash script, the citation will correctly appear in your compiled pdf.
|
||||||
|
|
||||||
|
Customization
|
||||||
|
-------------
|
||||||
|
Pubs is designed to interact well with your command line tool chain. You can add custom commands to pubs by defining aliases in your config file. Here are a few examples.
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
print = open -w lp
|
||||||
|
count = !pubs list -k | wc -l
|
||||||
|
|
||||||
|
For more advanced functionalities, pubs also support plugins. Actually *alias* is itself a plugin!
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
- python >= 2.7 or >= 3.3
|
- python >= 2.7 or >= 3.3
|
||||||
|
@ -103,6 +103,8 @@ class FakeIO(object):
|
|||||||
fakefs_stringio = self.fake_open.Call(*args, **kwargs)
|
fakefs_stringio = self.fake_open.Call(*args, **kwargs)
|
||||||
return UnicodeStringIOWrapper(fakefs_stringio)
|
return UnicodeStringIOWrapper(fakefs_stringio)
|
||||||
|
|
||||||
|
BytesIO = real_io.BytesIO
|
||||||
|
StringIO = real_io.StringIO
|
||||||
|
|
||||||
def create_fake_fs(module_list):
|
def create_fake_fs(module_list):
|
||||||
|
|
||||||
|
@ -2,20 +2,25 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import dotdot
|
import dotdot
|
||||||
from pubs.commands.tag_cmd import _parse_tags, _tag_groups
|
from pubs.commands.tag_cmd import _parse_tag_seq, _tag_groups
|
||||||
|
|
||||||
class TestTag(unittest.TestCase):
|
class TestTag(unittest.TestCase):
|
||||||
|
|
||||||
def test_tag_parsing(self):
|
def test_parse_tags(self):
|
||||||
|
self.assertEqual(['+abc', '+def9'], _parse_tag_seq('abc+def9'))
|
||||||
|
self.assertEqual(['+abc', '-def9'], _parse_tag_seq('abc-def9'))
|
||||||
|
self.assertEqual(['-abc', '-def9'], _parse_tag_seq('-abc-def9'))
|
||||||
|
self.assertEqual(['+abc', '-def9'], _parse_tag_seq('+abc-def9'))
|
||||||
|
|
||||||
self.assertEqual(['+abc', '+def9'], _parse_tags([ 'abc+def9']))
|
def test_tag_groups(self):
|
||||||
self.assertEqual(['+abc', '-def9'], _parse_tags([ 'abc-def9']))
|
self.assertEqual(({'math', 'romance'}, {'war'}),
|
||||||
self.assertEqual(['-abc', '-def9'], _parse_tags(['-abc-def9']))
|
_tag_groups(_parse_tag_seq('-war+math+romance')))
|
||||||
self.assertEqual(['+abc', '-def9'], _parse_tags(['+abc-def9']))
|
self.assertEqual(({'math', 'romance'}, {'war'}),
|
||||||
|
_tag_groups(_parse_tag_seq(':war+math+romance')))
|
||||||
self.assertEqual(({'math', 'romance'}, {'war'}), _tag_groups(_parse_tags(['-war+math+romance'])))
|
self.assertEqual(({'math', 'romance'}, {'war'}),
|
||||||
self.assertEqual(({'math', 'romance'}, {'war'}), _tag_groups(_parse_tags(['+math+romance-war'])))
|
_tag_groups(_parse_tag_seq('+math+romance-war')))
|
||||||
self.assertEqual(({'math', 'romance'}, {'war'}), _tag_groups(_parse_tags(['math+romance-war'])))
|
self.assertEqual(({'math', 'romance'}, {'war'}),
|
||||||
|
_tag_groups(_parse_tag_seq('math+romance-war')))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -7,8 +7,7 @@ import os
|
|||||||
import dotdot
|
import dotdot
|
||||||
import fake_env
|
import fake_env
|
||||||
|
|
||||||
from pubs import pubs_cmd
|
from pubs import pubs_cmd, update, color, content, filebroker, uis, p3, endecoder
|
||||||
from pubs import color, content, filebroker, uis, p3, endecoder
|
|
||||||
from pubs.config import conf
|
from pubs.config import conf
|
||||||
import configobj
|
import configobj
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ class CommandTestCase(unittest.TestCase):
|
|||||||
maxDiff = 1000000
|
maxDiff = 1000000
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.fs = fake_env.create_fake_fs([content, filebroker, conf, init_cmd, import_cmd, configobj])
|
self.fs = fake_env.create_fake_fs([content, filebroker, conf, init_cmd, import_cmd, configobj, update])
|
||||||
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):
|
||||||
@ -118,7 +117,7 @@ class DataCommandTestCase(CommandTestCase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
CommandTestCase.setUp(self)
|
super(DataCommandTestCase, self).setUp()
|
||||||
fake_env.copy_dir(self.fs, os.path.join(os.path.dirname(__file__), 'data'), 'data')
|
fake_env.copy_dir(self.fs, os.path.join(os.path.dirname(__file__), 'data'), 'data')
|
||||||
|
|
||||||
|
|
||||||
@ -251,6 +250,76 @@ class TestList(DataCommandTestCase):
|
|||||||
self.assertEqual(0 + 1, len(outs[-1].split('\n')))
|
self.assertEqual(0 + 1, len(outs[-1].split('\n')))
|
||||||
|
|
||||||
|
|
||||||
|
class TestTag(DataCommandTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTag, self).setUp()
|
||||||
|
init = ['pubs init',
|
||||||
|
'pubs add data/pagerank.bib',
|
||||||
|
'pubs add -k Turing1950 data/turing1950.bib',
|
||||||
|
]
|
||||||
|
self.execute_cmds(init)
|
||||||
|
|
||||||
|
def test_add_tag(self):
|
||||||
|
cmds = ['pubs tag Page99 search',
|
||||||
|
'pubs tag Turing1950 ai',
|
||||||
|
'pubs list',
|
||||||
|
]
|
||||||
|
correct = ['',
|
||||||
|
'',
|
||||||
|
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) | search\n' +
|
||||||
|
'[Turing1950] Turing, Alan M "Computing machinery and intelligence" Mind (1950) | ai\n',
|
||||||
|
]
|
||||||
|
out = self.execute_cmds(cmds)
|
||||||
|
self.assertEqual(out, correct)
|
||||||
|
|
||||||
|
def test_add_tags(self):
|
||||||
|
"""Adds several tags at once.
|
||||||
|
Also checks that tags printed in alphabetic order.
|
||||||
|
"""
|
||||||
|
cmds = ['pubs tag Page99 search+network',
|
||||||
|
'pubs list',
|
||||||
|
]
|
||||||
|
correct = ['',
|
||||||
|
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) | network,search\n' +
|
||||||
|
'[Turing1950] Turing, Alan M "Computing machinery and intelligence" Mind (1950) \n',
|
||||||
|
]
|
||||||
|
out = self.execute_cmds(cmds)
|
||||||
|
self.assertEqual(out, correct)
|
||||||
|
|
||||||
|
def test_remove_tag(self):
|
||||||
|
cmds = ['pubs tag Page99 search+network',
|
||||||
|
'pubs tag Page99 :network',
|
||||||
|
'pubs list',
|
||||||
|
]
|
||||||
|
correct = ['',
|
||||||
|
'',
|
||||||
|
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) | search\n' +
|
||||||
|
'[Turing1950] Turing, Alan M "Computing machinery and intelligence" Mind (1950) \n',
|
||||||
|
]
|
||||||
|
out = self.execute_cmds(cmds)
|
||||||
|
self.assertEqual(out, correct)
|
||||||
|
|
||||||
|
def test_add_remove_tag(self):
|
||||||
|
cmds = ['pubs tag Page99 a',
|
||||||
|
'pubs tag Page99 b-a',
|
||||||
|
'pubs list',
|
||||||
|
]
|
||||||
|
correct = ['',
|
||||||
|
'',
|
||||||
|
'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) | b\n' +
|
||||||
|
'[Turing1950] Turing, Alan M "Computing machinery and intelligence" Mind (1950) \n',
|
||||||
|
]
|
||||||
|
out = self.execute_cmds(cmds)
|
||||||
|
self.assertEqual(out, correct)
|
||||||
|
|
||||||
|
def test_wrong_citekey(self):
|
||||||
|
cmds = ['pubs tag Page999 a',
|
||||||
|
]
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
self.execute_cmds(cmds)
|
||||||
|
|
||||||
|
|
||||||
class TestUsecase(DataCommandTestCase):
|
class TestUsecase(DataCommandTestCase):
|
||||||
|
|
||||||
def test_first(self):
|
def test_first(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user