From d758df2ac977f001f800ef50868a111571a642e4 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Wed, 28 May 2014 17:30:52 +0200 Subject: [PATCH 1/7] A few python3 fixes. - Fix one implicit relative import. - Fix call to file builtin in test_env. - Fix deprecated lambda syntax. --- pubs/p3.py | 2 ++ pubs/templates/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pubs/p3.py b/pubs/p3.py index a73276c..d8f1792 100644 --- a/pubs/p3.py +++ b/pubs/p3.py @@ -12,12 +12,14 @@ else: import configparser ustr = str uchr = chr + file = None from urllib.parse import urlparse from urllib.request import urlopen from http.client import HTTPConnection configparser = configparser input = input +file = file def isbasestr(obj): diff --git a/pubs/templates/__init__.py b/pubs/templates/__init__.py index 8bb9e7b..22cbe49 100644 --- a/pubs/templates/__init__.py +++ b/pubs/templates/__init__.py @@ -1 +1 @@ -from str_templates import * \ No newline at end of file +from . str_templates import * From 136b8f83dcb5dafc61967d91f3ad397f40fe9771 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Tue, 9 Sep 2014 12:48:54 +0200 Subject: [PATCH 2/7] Fix fake_env, fix configparser issues, uniform 'open'. - Update fake_env for compatibility with io. - Uniform open (through _my_open in content). - Fix read issue for config (although still using SafeConfigParser that is deprecated in py3). --- pubs/configs.py | 15 ++++++--------- pubs/content.py | 8 ++++++-- pubs/p3.py | 8 ++++++-- pubs/paper.py | 3 ++- pubs/uis.py | 6 +----- setup.py | 3 ++- tests/fake_env.py | 35 ++++++++++++++++++++++------------- tests/test_endecoder.py | 1 - tests/test_filebroker.py | 8 ++++---- tests/test_usecase.py | 5 +++-- 10 files changed, 52 insertions(+), 40 deletions(-) diff --git a/pubs/configs.py b/pubs/configs.py index bfb51dc..50e13de 100644 --- a/pubs/configs.py +++ b/pubs/configs.py @@ -1,10 +1,9 @@ import os import collections -from .p3 import configparser -from . import content +from .p3 import configparser, _read_config -from .content import system_path, check_file +from .content import check_file, _open # constant stuff (DFT = DEFAULT) @@ -64,17 +63,15 @@ class Config(object): _config = self def load(self, path=DFT_CONFIG_PATH): - if not content.check_file(path, fail=False): + if not check_file(path, fail=False): raise IOError(("The configuration file {} does not exist." " Did you run 'pubs init' ?").format(path)) - with open(content.system_path(path), 'r') as f: - read = self._cfg.readfp(f) - # if len(read) == 0: - # raise IOError("Syntax error in {} config file. Aborting.".format(path)) + with _open(path, 'r') as f: + _read_config(self._cfg, f) return self def save(self, path=DFT_CONFIG_PATH): - with open(content.system_path(path), 'w') as f: + with _open(path, 'w') as f: self._cfg.write(f) def __setattr__(self, name, value): diff --git a/pubs/content.py b/pubs/content.py index 18f8c50..9d015fc 100644 --- a/pubs/content.py +++ b/pubs/content.py @@ -43,6 +43,10 @@ def system_path(path): return os.path.abspath(os.path.expanduser(path)) +def _open(path, mode): + return io.open(system_path(path), mode, encoding=ENCODING) + + def check_file(path, fail=True): syspath = system_path(path) return (_check_system_path_exists(syspath, fail=fail) @@ -57,14 +61,14 @@ def check_directory(path, fail=True): def read_file(filepath): check_file(filepath) - with io.open(system_path(filepath), 'r', encoding=ENCODING) as f: + with _open(filepath, 'r') as f: content = f.read() return content def write_file(filepath, data): check_directory(os.path.dirname(filepath)) - with io.open(system_path(filepath), 'w', encoding=ENCODING) as f: + with _open(filepath, 'w') as f: f.write(data) diff --git a/pubs/p3.py b/pubs/p3.py index d8f1792..034260c 100644 --- a/pubs/p3.py +++ b/pubs/p3.py @@ -1,25 +1,29 @@ +import io import sys if sys.version_info[0] == 2: import ConfigParser as configparser + _read_config = configparser.SafeConfigParser.readfp input = raw_input ustr = unicode uchr = unichr from urlparse import urlparse from urllib2 import urlopen from httplib import HTTPConnection + file = None + _fake_stdio = io.BytesIO # Only for tests to capture std{out,err} else: import configparser + _read_config = configparser.SafeConfigParser.read_file ustr = str uchr = chr - file = None from urllib.parse import urlparse from urllib.request import urlopen from http.client import HTTPConnection + _fake_stdio = io.StringIO # Only for tests to capture std{out,err} configparser = configparser input = input -file = file def isbasestr(obj): diff --git a/pubs/paper.py b/pubs/paper.py index 7798366..5abc3be 100644 --- a/pubs/paper.py +++ b/pubs/paper.py @@ -2,6 +2,7 @@ import copy from dateutil.parser import parse as datetime_parse from . import bibstruct +from .p3 import ustr DEFAULT_META = {'docfile': None, 'tags': set()} @@ -11,7 +12,7 @@ def _clean_metadata(metadata): meta = copy.deepcopy(DEFAULT_META) meta.update(metadata or {}) # handles None metadata meta['tags'] = set(meta.get('tags', [])) # tags should be a set - if 'added' in meta and isinstance(meta['added'], basestring): + if 'added' in meta and isinstance(meta['added'], ustr): meta['added'] = datetime_parse(meta['added']) return meta diff --git a/pubs/uis.py b/pubs/uis.py index ce98b2a..7a965d2 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -4,7 +4,6 @@ import sys from .beets_ui import _encoding, input_ from .content import editor_input -from .p3 import ustr from . import color # package-shared ui that can be accessed using : @@ -39,10 +38,7 @@ class UI: is not in the terminal's encoding's character set, just silently replaces it. """ - txt = [s.encode(self.encoding, 'replace') - if isinstance(s, ustr) else s - for s in strings] - print(' '.join(txt)) + print(' '.join(strings).encode(self.encoding, 'replace')) def input_choice(self, options, option_chars, default=None, question=''): """Ask the user to chose between a set of options. The iser is asked diff --git a/setup.py b/setup.py index feaa100..06fbb91 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ from setuptools import setup, find_packages + setup(name='pubs', version='4', author='Fabien Benureau, Olivier Mangin, Jonathan Grizou', @@ -10,5 +11,5 @@ setup(name='pubs', description='research papers manager', requires=['pyyaml', 'bibtexparser', 'dateutil'], packages=find_packages(), - scripts=['pubs/pubs'] + scripts=['pubs/pubs'], ) diff --git a/tests/fake_env.py b/tests/fake_env.py index c6fa458..7719c94 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -10,14 +10,13 @@ import fake_filesystem import fake_filesystem_shutil import fake_filesystem_glob -from pubs.p3 import input +from pubs.p3 import input, _fake_stdio from pubs import content, filebroker # code for fake fs real_os = os real_open = open -real_file = file real_shutil = shutil real_glob = glob real_io = io @@ -41,7 +40,7 @@ ENCODING = 'utf8' class UnicodeStringIOWrapper(object): - """This is a hack because fake_filesystem does not provied mock of io. + """This is a hack because fake_filesystem does not provide mock of io. """ override = ['read', 'readline', 'readlines', 'write', 'writelines'] @@ -55,6 +54,10 @@ class UnicodeStringIOWrapper(object): else: return self._strio.__getattribute__(name) + def __iter__(self): + for l in self.readlines(): + yield l + def read(self, *args): return self._strio.read(*args).decode(ENCODING) @@ -78,13 +81,24 @@ class UnicodeStringIOWrapper(object): return self._strio.__exit__(*args) +def _force_binary_mode(mode): + if 'b' in mode: + raise ValueError('Open should not happen in binary mode.') + return mode + 'b' + + class FakeIO(object): def __init__(self, fake_open): self.fake_open = fake_open def open(self, *args, **kwargs): - # Forces python3 mode for FakeFileOpen + # Forces binary mode for FakeFileOpen + args = list(args) + if len(args) > 1: + args[1] = _force_binary_mode(args[1]) + else: + kwargs['mode'] = _force_binary_mode(kwargs.get('mode', 'r')) fakefs_stringio = self.fake_open.Call(*args, **kwargs) return UnicodeStringIOWrapper(fakefs_stringio) @@ -100,8 +114,6 @@ def create_fake_fs(module_list): fake_fs.CreateDirectory(fake_os.path.expanduser('~')) - __builtins__.update({'open': fake_open, 'file': fake_open}) - sys.modules['os'] = fake_os sys.modules['shutil'] = fake_shutil sys.modules['glob'] = fake_glob @@ -111,7 +123,7 @@ def create_fake_fs(module_list): md.os = fake_os md.shutil = fake_shutil md.open = fake_open - md.file = fake_open + md.file = None md.io = fake_io return {'fs': fake_fs, @@ -125,10 +137,8 @@ def create_fake_fs(module_list): def unset_fake_fs(module_list): try: __builtins__.open = real_open - __builtins__.file = real_file except AttributeError: __builtins__['open'] = real_open - __builtins__['file'] = real_file sys.modules['os'] = real_os sys.modules['shutil'] = real_shutil @@ -139,7 +149,6 @@ def unset_fake_fs(module_list): md.os = real_os md.shutil = real_shutil md.open = real_open - md.file = real_file md.io = real_io @@ -163,8 +172,8 @@ def copy_dir(fs, real_dir, fake_dir = None): def redirect(f): def newf(*args, **kwargs): old_stderr, old_stdout = sys.stderr, sys.stdout - stdout = io.BytesIO() - stderr = io.BytesIO() + stdout = _fake_stdio() + stderr = _fake_stdio() sys.stdout, sys.stderr = stdout, stderr try: return f(*args, **kwargs), stdout, stderr @@ -190,7 +199,7 @@ class FakeInput(): Then : input() returns 'yes' input() returns 'no' - input() raise IndexError + input() raises IndexError """ def __init__(self, inputs, module_list=tuple()): diff --git a/tests/test_endecoder.py b/tests/test_endecoder.py index 96e91b1..d5e51f1 100644 --- a/tests/test_endecoder.py +++ b/tests/test_endecoder.py @@ -34,7 +34,6 @@ class TestEnDecode(unittest.TestCase): data = decoder.encode_metadata(dummy_metadata) self.assertIsInstance(data, ustr) - def test_endecode_bibtex(self): decoder = endecoder.EnDecoder() entry = decoder.decode_bibdata(bibtex_raw0) diff --git a/tests/test_filebroker.py b/tests/test_filebroker.py index 53a1f23..67fa48e 100644 --- a/tests/test_filebroker.py +++ b/tests/test_filebroker.py @@ -29,11 +29,11 @@ class TestFileBroker(fake_env.TestFakeFs): fake_env.copy_dir(self.fs, os.path.join(os.path.dirname(__file__), 'testrepo'), 'testrepo') fb = filebroker.FileBroker('testrepo', create = True) - with open('testrepo/bib/Page99.bib', 'r') as f: - self.assertEqual(fb.pull_bibfile('Page99'), f.read()) + bib_content = content.read_file('testrepo/bib/Page99.bib') + self.assertEqual(fb.pull_bibfile('Page99'), bib_content) - with open('testrepo/meta/Page99.yaml', 'r') as f: - self.assertEqual(fb.pull_metafile('Page99'), f.read()) + meta_content = content.read_file('testrepo/meta/Page99.yaml') + self.assertEqual(fb.pull_metafile('Page99'), meta_content) def test_errors(self): diff --git a/tests/test_usecase.py b/tests/test_usecase.py index e6d7c42..47fbdb5 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -66,12 +66,12 @@ class CommandTestCase(unittest.TestCase): In the latter case, the command is : 1. a string reprensenting the command to execute 2. the user inputs to feed to the command during execution - 3. the output excpected, verified with assertEqual + 3. the output expected, verified with assertEqual """ outs = [] for cmd in cmds: - if hasattr(cmd, '__iter__'): + if not isinstance(cmd, p3.ustr): if len(cmd) == 2: input = fake_env.FakeInput(cmd[1], [content, uis, beets_ui, p3]) input.as_global() @@ -293,6 +293,7 @@ class TestUsecase(DataCommandTestCase): print(self.fs['os'].listdir(docdir)) self.assertNotIn('turing-mind-1950.pdf', self.fs['os'].listdir(docdir)) + def test_tag_list(self): correct = [b'Initializing pubs in /paper_first\n', b'', From fd49c3acf2ec77db38f5a45ca58add559e97bba1 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Tue, 9 Sep 2014 13:04:26 +0200 Subject: [PATCH 3/7] Removes unappropriate encoding of string. --- pubs/pretty.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubs/pretty.py b/pubs/pretty.py index 6e9854f..bc3a333 100644 --- a/pubs/pretty.py +++ b/pubs/pretty.py @@ -53,9 +53,9 @@ def paper_oneliner(p, citekey_only = False): return p.citekey else: bibdesc = bib_oneliner(p.bibentry) - return (u'[{citekey}] {descr} {tags}'.format( + return u'[{citekey}] {descr} {tags}'.format( citekey=color.dye(p.citekey, color.purple), descr=bibdesc, tags=color.dye(' '.join(p.tags), color.tag, bold=False), - )).encode('utf-8') + ) From 52813439dd60adfa852e11f4a898baa756c6d7e1 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Thu, 11 Sep 2014 16:46:17 +0200 Subject: [PATCH 4/7] Removes useless beets_ui and FIX input issue between python 2 and 3. --- pubs/beets_ui.py | 57 ------------------------------------------- pubs/p3.py | 5 +++- pubs/uis.py | 25 ++++++++++++++++--- tests/test_usecase.py | 4 +-- 4 files changed, 28 insertions(+), 63 deletions(-) delete mode 100644 pubs/beets_ui.py diff --git a/pubs/beets_ui.py b/pubs/beets_ui.py deleted file mode 100644 index 1adf87a..0000000 --- a/pubs/beets_ui.py +++ /dev/null @@ -1,57 +0,0 @@ -# This file contains functions taken from the user interface of the beet -# tool (http://beets.radbox.org). -# -# Copyright 2013, Adrian Sampson. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - - -import locale -import sys - -from .p3 import input - - -class UserError(Exception): - """UI exception. Commands should throw this in order to display - nonrecoverable errors to the user. - """ - pass - - -def _encoding(config): - """Tries to guess the encoding used by the terminal.""" - # Configured override? - # Determine from locale settings. - try: - default_enc = locale.getdefaultlocale()[1] or 'utf8' - except ValueError: - # Invalid locale environment variable setting. To avoid - # failing entirely for no good reason, assume UTF-8. - default_enc = 'utf8' - return config.get('terminal-encoding', default_enc) - - -def input_(): - """Get input and decodes the result to a Unicode string. - Raises a UserError if stdin is not available. The prompt is sent to - stdout rather than stderr. A printed between the prompt and the - input cursor. - """ - # raw_input incorrectly sends prompts to stderr, not stdout, so we - # use print() explicitly to display prompts. - # http://bugs.python.org/issue1927 - try: - resp = input() - except EOFError: - raise UserError('stdin stream ended while input required') - return resp.decode(sys.stdin.encoding or 'utf8', 'ignore') diff --git a/pubs/p3.py b/pubs/p3.py index 034260c..db439b9 100644 --- a/pubs/p3.py +++ b/pubs/p3.py @@ -4,7 +4,10 @@ import sys if sys.version_info[0] == 2: import ConfigParser as configparser _read_config = configparser.SafeConfigParser.readfp - input = raw_input + + def input(): + raw_input().decode(sys.stdin.encoding or 'utf8', 'ignore') + ustr = unicode uchr = unichr from urlparse import urlparse diff --git a/pubs/uis.py b/pubs/uis.py index 7a965d2..aecf480 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -2,9 +2,10 @@ from __future__ import print_function import sys -from .beets_ui import _encoding, input_ from .content import editor_input from . import color +import locale + # package-shared ui that can be accessed using : # from uis import get_ui @@ -13,6 +14,16 @@ from . import color _ui = None +def _get_encoding(config): + """Get local terminal encoding or user preference in config.""" + enc = 'utf8' + try: + enc = locale.getdefaultlocale()[1] or 'utf8' + except ValueError: + pass # Keep default + return config.get('terminal-encoding', enc) + + def get_ui(): if _ui is None: raise ValueError('ui not instanciated yet') @@ -29,7 +40,7 @@ class UI: """ def __init__(self, config): - self.encoding = _encoding(config) + self.encoding = _get_encoding(config) color.setup(config.color) self.editor = config.edit_cmd @@ -40,6 +51,14 @@ class UI: """ print(' '.join(strings).encode(self.encoding, 'replace')) + def input(self): + try: + data = input() + except EOFError: + self.error('Standard input ended while waiting for answer.') + self.exit(1) + return data + def input_choice(self, options, option_chars, default=None, question=''): """Ask the user to chose between a set of options. The iser is asked to input a char corresponding to the option he choses. @@ -61,7 +80,7 @@ class UI: for c, o in zip(displayed_chars, options)]) self.print_(question, option_str) while True: - answer = input_() + answer = self.input() if answer is None or answer == '': if default is not None: return default diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 47fbdb5..ded9647 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -8,7 +8,7 @@ import dotdot import fake_env from pubs import pubs_cmd -from pubs import color, content, filebroker, uis, beets_ui, p3, endecoder, configs +from pubs import color, content, filebroker, uis, p3, endecoder, configs import str_fixtures import fixtures @@ -73,7 +73,7 @@ class CommandTestCase(unittest.TestCase): for cmd in cmds: if not isinstance(cmd, p3.ustr): if len(cmd) == 2: - input = fake_env.FakeInput(cmd[1], [content, uis, beets_ui, p3]) + input = fake_env.FakeInput(cmd[1], [content, uis, p3]) input.as_global() if capture_output: From 0479636393bd18b6bd50d0616251b79afa78d945 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Thu, 11 Sep 2014 18:09:25 +0200 Subject: [PATCH 5/7] Fix issues with stdout and updates tests. Not so clean since trying to change stdout encoding requires accessing sys.stdout.buffer, so fake_env has to mock this layer also. The basic differences between p2 and p3 are handled in p3.py. --- pubs/p3.py | 25 ++++++++++++++++++++++++- pubs/uis.py | 16 ++++++++++------ tests/fake_env.py | 4 ++-- tests/test_usecase.py | 26 +++++++++++++------------- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/pubs/p3.py b/pubs/p3.py index db439b9..86b4165 100644 --- a/pubs/p3.py +++ b/pubs/p3.py @@ -7,6 +7,11 @@ if sys.version_info[0] == 2: def input(): raw_input().decode(sys.stdin.encoding or 'utf8', 'ignore') + + # The following has to be a function so that it can be mocked + # for test_usecase. + def _get_raw_stdout(): + return sys.stdout ustr = unicode uchr = unichr @@ -15,6 +20,12 @@ if sys.version_info[0] == 2: from httplib import HTTPConnection file = None _fake_stdio = io.BytesIO # Only for tests to capture std{out,err} + + def _get_fake_stdio_ucontent(stdio): + ustdio = io.TextIOWrapper(stdio) + ustdio.seek(0) + return ustdio.read() + else: import configparser _read_config = configparser.SafeConfigParser.read_file @@ -23,7 +34,19 @@ else: from urllib.parse import urlparse from urllib.request import urlopen from http.client import HTTPConnection - _fake_stdio = io.StringIO # Only for tests to capture std{out,err} + + def _fake_stdio(): + return io.TextIOWrapper(io.BytesIO()) # Only for tests to capture std{out,err} + + def _get_fake_stdio_ucontent(stdio): + stdio.flush() + stdio.seek(0) + return stdio.read() + + # The following has to be a function so that it can be mocked + # for test_usecase. + def _get_raw_stdout(): + return sys.stdout.buffer configparser = configparser input = input diff --git a/pubs/uis.py b/pubs/uis.py index aecf480..868e813 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -1,10 +1,12 @@ from __future__ import print_function import sys +import locale +import codecs from .content import editor_input from . import color -import locale +from .p3 import _get_raw_stdout # package-shared ui that can be accessed using : @@ -16,12 +18,12 @@ _ui = None def _get_encoding(config): """Get local terminal encoding or user preference in config.""" - enc = 'utf8' + enc = None try: - enc = locale.getdefaultlocale()[1] or 'utf8' + enc = locale.getdefaultlocale()[1] except ValueError: pass # Keep default - return config.get('terminal-encoding', enc) + return config.get('terminal-encoding', enc or 'utf8') def get_ui(): @@ -40,16 +42,18 @@ class UI: """ def __init__(self, config): - self.encoding = _get_encoding(config) color.setup(config.color) self.editor = config.edit_cmd + self.encoding = _get_encoding(config) + self._stdout = codecs.getwriter(self.encoding)(_get_raw_stdout(), + errors='replace') def print_(self, *strings): """Like print, but rather than raising an error when a character is not in the terminal's encoding's character set, just silently replaces it. """ - print(' '.join(strings).encode(self.encoding, 'replace')) + print(' '.join(strings), file=self._stdout) def input(self): try: diff --git a/tests/fake_env.py b/tests/fake_env.py index 7719c94..4bd199b 100644 --- a/tests/fake_env.py +++ b/tests/fake_env.py @@ -10,7 +10,7 @@ import fake_filesystem import fake_filesystem_shutil import fake_filesystem_glob -from pubs.p3 import input, _fake_stdio +from pubs.p3 import input, _fake_stdio, _get_fake_stdio_ucontent from pubs import content, filebroker # code for fake fs @@ -176,7 +176,7 @@ def redirect(f): stderr = _fake_stdio() sys.stdout, sys.stderr = stdout, stderr try: - return f(*args, **kwargs), stdout, stderr + return f(*args, **kwargs), _get_fake_stdio_ucontent(stdout), _get_fake_stdio_ucontent(stderr) finally: sys.stderr, sys.stdout = old_stderr, old_stdout return newf diff --git a/tests/test_usecase.py b/tests/test_usecase.py index ded9647..6964305 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -59,7 +59,7 @@ class CommandTestCase(unittest.TestCase): self.fs = fake_env.create_fake_fs([content, filebroker, configs, init_cmd, import_cmd]) self.default_pubs_dir = self.fs['os'].path.expanduser('~/.pubs') - def execute_cmds(self, cmds, fs=None, capture_output=CAPTURE_OUTPUT): + def execute_cmds(self, cmds, capture_output=CAPTURE_OUTPUT): """ Execute a list of commands, and capture their output A command can be a string, or a tuple of size 2 or 3. @@ -79,7 +79,7 @@ class CommandTestCase(unittest.TestCase): if capture_output: _, stdout, stderr = fake_env.redirect(pubs_cmd.execute)(cmd[0].split()) if len(cmd) == 3 and capture_output: - actual_out = color.undye(stdout.getvalue()) + actual_out = color.undye(stdout) correct_out = color.undye(cmd[2]) self.assertEqual(actual_out, correct_out) else: @@ -93,8 +93,8 @@ class CommandTestCase(unittest.TestCase): pubs_cmd.execute(cmd.split()) if capture_output: - assert(stderr.getvalue() == '') - outs.append(color.undye(stdout.getvalue())) + assert(stderr == '') + outs.append(color.undye(stdout)) if PRINT_OUTPUT: print(outs) return outs @@ -119,13 +119,13 @@ class TestInit(CommandTestCase): def test_init(self): pubsdir = os.path.expanduser('~/pubs_test2') - pubs_cmd.execute('pubs init -p {}'.format(pubsdir).split()) + self.execute_cmds(['pubs init -p {}'.format(pubsdir)]) self.assertEqual(set(self.fs['os'].listdir(pubsdir)), {'bib', 'doc', 'meta', 'notes'}) def test_init2(self): pubsdir = os.path.expanduser('~/.pubs') - pubs_cmd.execute('pubs init'.split()) + self.execute_cmds(['pubs init']) self.assertEqual(set(self.fs['os'].listdir(pubsdir)), {'bib', 'doc', 'meta', 'notes'}) @@ -295,11 +295,11 @@ class TestUsecase(DataCommandTestCase): def test_tag_list(self): - correct = [b'Initializing pubs in /paper_first\n', - b'', - b'', - b'', - b'search network\n', + correct = ['Initializing pubs in /paper_first\n', + '', + '', + '', + 'search network\n', ] cmds = ['pubs init -p paper_first/', @@ -363,8 +363,8 @@ class TestUsecase(DataCommandTestCase): 'pubs export Page99', ] outs = self.execute_cmds(cmds) - out_raw = outs[2].decode() - self.assertEqual(endecoder.EnDecoder().decode_bibdata(out_raw), fixtures.page_bibdata) + self.assertEqual(endecoder.EnDecoder().decode_bibdata(outs[2]), + fixtures.page_bibdata) def test_import(self): cmds = ['pubs init', From f2125b4c19f465c64c420f1458a57275a73a2c04 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Thu, 11 Sep 2014 18:47:11 +0200 Subject: [PATCH 6/7] Tags have to be printed in alphabetical order. --- pubs/commands/tag_cmd.py | 4 ++-- pubs/pretty.py | 2 +- tests/test_usecase.py | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pubs/commands/tag_cmd.py b/pubs/commands/tag_cmd.py index 3f700ee..c5b2ae2 100644 --- a/pubs/commands/tag_cmd.py +++ b/pubs/commands/tag_cmd.py @@ -83,12 +83,12 @@ def command(args): rp = Repository(config()) if citekeyOrTag is None: - ui.print_(color.dye(' '.join(rp.get_tags()), color=color.blue)) + ui.print_(color.dye(' '.join(sorted(rp.get_tags())), color=color.blue)) else: if rp.databroker.exists(citekeyOrTag): p = rp.pull_paper(citekeyOrTag) if tags == []: - ui.print_(color.dye(' '.join(p.tags), + ui.print_(color.dye(' '.join(sorted(p.tags)), color=color.blue)) else: add_tags, remove_tags = _tag_groups(_parse_tags(tags)) diff --git a/pubs/pretty.py b/pubs/pretty.py index bc3a333..bd45872 100644 --- a/pubs/pretty.py +++ b/pubs/pretty.py @@ -56,6 +56,6 @@ def paper_oneliner(p, citekey_only = False): return u'[{citekey}] {descr} {tags}'.format( citekey=color.dye(p.citekey, color.purple), descr=bibdesc, - tags=color.dye(' '.join(p.tags), + tags=color.dye(' '.join(sorted(p.tags)), color.tag, bold=False), ) diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 6964305..3e82b7b 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -246,13 +246,13 @@ class TestList(DataCommandTestCase): class TestUsecase(DataCommandTestCase): def test_first(self): - correct = [b'Initializing pubs in /paper_first\n', - b'', - b'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n', - b'\n', - b'', - b'search network\n', - b'[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) search network\n' + correct = ['Initializing pubs in /paper_first\n', + '', + '[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) \n', + '\n', + '', + 'network search\n', + '[Page99] Page, Lawrence et al. "The PageRank Citation Ranking: Bringing Order to the Web." (1999) network search\n' ] cmds = ['pubs init -p paper_first/', From adb0caa3ed5afb9590abbaa450cc5ab428f5bad2 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Thu, 11 Sep 2014 18:47:54 +0200 Subject: [PATCH 7/7] Use default python in executable. --- pubs/pubs | 4 ++-- pubs/pubs_cmd.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pubs/pubs b/pubs/pubs index 99991d4..fa2eb8a 100755 --- a/pubs/pubs +++ b/pubs/pubs @@ -1,5 +1,5 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding:utf-8 -*- from pubs import pubs_cmd -pubs_cmd.execute() \ No newline at end of file +pubs_cmd.execute() diff --git a/pubs/pubs_cmd.py b/pubs/pubs_cmd.py index 62e2499..55249af 100644 --- a/pubs/pubs_cmd.py +++ b/pubs/pubs_cmd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python2 -# -*- coding:utf-8 -*- import sys import argparse