Merge pull request #197 from pubs/pr-191
Pr 191 - Git plugin by Amlesh Sivanantham.
This commit is contained in:
commit
eedd342a2d
@ -7,8 +7,10 @@
|
|||||||
|
|
||||||
### Implemented enhancements
|
### Implemented enhancements
|
||||||
|
|
||||||
|
- New git plugin to commit changes to the repository ([#191](https://github.com/pubs/pubs/pull/191) by [Amlesh Sivanantham](http://github.com/zamlz))
|
||||||
|
- The import command now warn, rather than fail on existing citekeys. ([#198](https://github.com/pubs/pubs/pull/198) by [Kyle Sunden](https://github.com/ksunden))
|
||||||
- Add `citekey` filter to `query` ([#193](https://github.com/pubs/pubs/pull/193) by [Shane Stone](https://github.com/shanewstone))
|
- Add `citekey` filter to `query` ([#193](https://github.com/pubs/pubs/pull/193) by [Shane Stone](https://github.com/shanewstone))
|
||||||
|
- The `--config` and `--force-colors` command line options now appear when invoking `pubs --help`
|
||||||
|
|
||||||
### Fixed bugs
|
### Fixed bugs
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from ..uis import get_ui
|
|||||||
from ..endecoder import EnDecoder
|
from ..endecoder import EnDecoder
|
||||||
from ..utils import resolve_citekey
|
from ..utils import resolve_citekey
|
||||||
from ..completion import CiteKeyCompletion
|
from ..completion import CiteKeyCompletion
|
||||||
|
from ..events import ModifyEvent
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers, conf):
|
def parser(subparsers, conf):
|
||||||
@ -88,4 +89,8 @@ def command(conf, args):
|
|||||||
# else edit again
|
# else edit again
|
||||||
# Also handle malformed bibtex and metadata
|
# Also handle malformed bibtex and metadata
|
||||||
|
|
||||||
|
if meta:
|
||||||
|
ModifyEvent(citekey, "metadata").send()
|
||||||
|
else:
|
||||||
|
ModifyEvent(citekey, "bibtex").send()
|
||||||
rp.close()
|
rp.close()
|
||||||
|
@ -2,6 +2,7 @@ from .. import repo
|
|||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from ..utils import resolve_citekey
|
from ..utils import resolve_citekey
|
||||||
from ..completion import CiteKeyCompletion
|
from ..completion import CiteKeyCompletion
|
||||||
|
from ..events import NoteEvent
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers, conf):
|
def parser(subparsers, conf):
|
||||||
@ -19,4 +20,5 @@ def command(conf, args):
|
|||||||
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True)
|
||||||
notepath = rp.databroker.real_notepath(citekey, rp.conf['main']['note_extension'])
|
notepath = rp.databroker.real_notepath(citekey, rp.conf['main']['note_extension'])
|
||||||
ui.edit_file(notepath, temporary=False)
|
ui.edit_file(notepath, temporary=False)
|
||||||
|
NoteEvent(citekey).send()
|
||||||
rp.close()
|
rp.close()
|
||||||
|
@ -26,6 +26,7 @@ from .. import pretty
|
|||||||
from .. import color
|
from .. import color
|
||||||
from ..utils import resolve_citekey
|
from ..utils import resolve_citekey
|
||||||
from ..completion import CiteKeyOrTagCompletion, TagModifierCompletion
|
from ..completion import CiteKeyOrTagCompletion, TagModifierCompletion
|
||||||
|
from ..events import TagEvent
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers, conf):
|
def parser(subparsers, conf):
|
||||||
@ -101,7 +102,8 @@ def command(conf, args):
|
|||||||
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, event=False)
|
||||||
|
TagEvent(citekeyOrTag).send()
|
||||||
elif tags is not None:
|
elif tags is not None:
|
||||||
ui.error(ui.error('No entry found for citekey {}.'.format(citekeyOrTag)))
|
ui.error(ui.error('No entry found for citekey {}.'.format(citekeyOrTag)))
|
||||||
ui.exit()
|
ui.exit()
|
||||||
|
@ -95,6 +95,27 @@ active = force_list(default=list('alias'))
|
|||||||
# command = !pubs list -k | wc -l
|
# command = !pubs list -k | wc -l
|
||||||
# description = lists number of pubs in repo
|
# description = lists number of pubs in repo
|
||||||
|
|
||||||
|
[[git]]
|
||||||
|
# The git plugin will commit changes to the repository in a git repository
|
||||||
|
# created at the root of the pubs directory. All detected changes will be
|
||||||
|
# commited every time a change is made by a pubs command.
|
||||||
|
# The plugin also propose the `pubs git` subcommand, to directory send git
|
||||||
|
# command to the pubs repository. Therefore, `pubs git status` is equivalent
|
||||||
|
# to `git -C <pubsdir> status`, with the `-C` flag instructing
|
||||||
|
# to invoke git as if the current directory was <pubsdir>. Note that a
|
||||||
|
# limitation of the subcommand is that you cannot use git commands with the
|
||||||
|
# `-c` option (pubs will interpret it first.)
|
||||||
|
|
||||||
|
# if False, will display git output when automatic commit are made.
|
||||||
|
# Invocation of `pubs git` will always have output displayed.
|
||||||
|
quiet = boolean(default=True)
|
||||||
|
# if True, git will not automatically commit changes
|
||||||
|
manual = boolean(default=False)
|
||||||
|
# if True, color will be conserved from git output (this add `-c color:always`
|
||||||
|
# to the git invocation).
|
||||||
|
force_color = boolean(default=True)
|
||||||
|
|
||||||
|
|
||||||
[internal]
|
[internal]
|
||||||
# The version of this configuration file. Do not edit.
|
# The version of this configuration file. Do not edit.
|
||||||
version = string(min=5, default='{}')
|
version = string(min=5, default='{}')
|
||||||
|
@ -25,17 +25,72 @@ class Event(object):
|
|||||||
return wrap
|
return wrap
|
||||||
|
|
||||||
|
|
||||||
class RemoveEvent(Event):
|
# Command events
|
||||||
|
|
||||||
|
class PreCommandEvent(Event):
|
||||||
|
description = "Triggered before the command is executed"
|
||||||
|
|
||||||
|
class PostCommandEvent(Event):
|
||||||
|
description = "Triggered after the command is executed"
|
||||||
|
|
||||||
|
|
||||||
|
# Paper changes
|
||||||
|
|
||||||
|
class PaperChangeEvent(Event):
|
||||||
|
_format = "Unspecified modification of paper {citekey}."
|
||||||
|
|
||||||
def __init__(self, citekey):
|
def __init__(self, citekey):
|
||||||
self.citekey = citekey
|
self.citekey = citekey
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self._format.format(citekey=self.citekey)
|
||||||
|
|
||||||
|
# Used by repo.push_paper()
|
||||||
|
class AddEvent(PaperChangeEvent):
|
||||||
|
_format = "Added paper {citekey}."
|
||||||
|
|
||||||
|
# Used by repo.push_doc()
|
||||||
|
class DocAddEvent(PaperChangeEvent):
|
||||||
|
_format = "Added document for {citekey}."
|
||||||
|
|
||||||
|
# Used by repo.remove_paper()
|
||||||
|
class RemoveEvent(PaperChangeEvent):
|
||||||
|
_format = "Removed paper for {citekey}."
|
||||||
|
|
||||||
|
# Used by repo.remove_doc()
|
||||||
|
class DocRemoveEvent(PaperChangeEvent):
|
||||||
|
_format = "Removed document for {citekey}."
|
||||||
|
|
||||||
|
# Used by commands.tag_cmd.command()
|
||||||
|
class TagEvent(PaperChangeEvent):
|
||||||
|
_format = "Updated tags for {citekey}."
|
||||||
|
|
||||||
|
# Used by commands.edit_cmd.command()
|
||||||
|
class ModifyEvent(PaperChangeEvent):
|
||||||
|
_format = "Modified {file_type} file of {citekey}."
|
||||||
|
|
||||||
|
def __init__(self, citekey, file_type):
|
||||||
|
super(ModifyEvent, self).__init__(citekey)
|
||||||
|
self.file_type = file_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self._format.format(citekey=self.citekey, file_type=self.file_type)
|
||||||
|
|
||||||
|
# Used by repo.rename_paper()
|
||||||
|
class RenameEvent(PaperChangeEvent):
|
||||||
|
_format = "Renamed paper {old_citekey} to {citekey}."
|
||||||
|
|
||||||
class RenameEvent(Event):
|
|
||||||
def __init__(self, paper, old_citekey):
|
def __init__(self, paper, old_citekey):
|
||||||
|
super(RenameEvent, self).__init__(paper.citekey)
|
||||||
self.paper = paper
|
self.paper = paper
|
||||||
self.old_citekey = old_citekey
|
self.old_citekey = old_citekey
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self._format.format(citekey=self.citekey, old_citekey=self.old_citekey)
|
||||||
|
|
||||||
class AddEvent(Event):
|
# Used by commands.note_cmd.command()
|
||||||
def __init__(self, citekey):
|
class NoteEvent(PaperChangeEvent):
|
||||||
self.citekey = citekey
|
_format = "Modified note of {citekey}."
|
||||||
|
@ -27,6 +27,10 @@ class PapersPlugin(object):
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError("{} instance not created".format(cls.__name__))
|
raise RuntimeError("{} instance not created".format(cls.__name__))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_loaded(cls):
|
||||||
|
return cls in _instances
|
||||||
|
|
||||||
|
|
||||||
def load_plugins(conf, ui):
|
def load_plugins(conf, ui):
|
||||||
"""Imports the modules for a sequence of plugin names. Each name
|
"""Imports the modules for a sequence of plugin names. Each name
|
||||||
@ -34,6 +38,8 @@ def load_plugins(conf, ui):
|
|||||||
package in sys.path; the module indicated should contain the
|
package in sys.path; the module indicated should contain the
|
||||||
PapersPlugin subclasses desired.
|
PapersPlugin subclasses desired.
|
||||||
"""
|
"""
|
||||||
|
global _classes, _instances
|
||||||
|
_classes, _instances = [], {}
|
||||||
for name in conf['plugins']['active']:
|
for name in conf['plugins']['active']:
|
||||||
if len(name) > 0:
|
if len(name) > 0:
|
||||||
modname = '{}.{}.{}.{}'.format('pubs', PLUGIN_NAMESPACE, name, name)
|
modname = '{}.{}.{}.{}'.format('pubs', PLUGIN_NAMESPACE, name, name)
|
||||||
@ -50,7 +56,7 @@ def load_plugins(conf, ui):
|
|||||||
if isinstance(obj, type) and issubclass(obj, PapersPlugin) \
|
if isinstance(obj, type) and issubclass(obj, PapersPlugin) \
|
||||||
and obj != PapersPlugin:
|
and obj != PapersPlugin:
|
||||||
_classes.append(obj)
|
_classes.append(obj)
|
||||||
_instances[obj] = obj(conf)
|
_instances[obj] = obj(conf, ui)
|
||||||
|
|
||||||
|
|
||||||
def get_plugins():
|
def get_plugins():
|
||||||
|
@ -65,7 +65,7 @@ class AliasPlugin(PapersPlugin):
|
|||||||
|
|
||||||
name = 'alias'
|
name = 'alias'
|
||||||
|
|
||||||
def __init__(self, conf):
|
def __init__(self, conf, ui):
|
||||||
self.aliases = []
|
self.aliases = []
|
||||||
if 'alias' in conf['plugins']:
|
if 'alias' in conf['plugins']:
|
||||||
for name, entry in conf['plugins']['alias'].items():
|
for name, entry in conf['plugins']['alias'].items():
|
||||||
|
0
pubs/plugs/git/__init__.py
Normal file
0
pubs/plugs/git/__init__.py
Normal file
117
pubs/plugs/git/git.py
Normal file
117
pubs/plugs/git/git.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
from pipes import quote as shell_quote
|
||||||
|
|
||||||
|
from ... import uis
|
||||||
|
from ...plugins import PapersPlugin
|
||||||
|
from ...events import PaperChangeEvent, PostCommandEvent
|
||||||
|
|
||||||
|
|
||||||
|
GITIGNORE = """# files or directories for the git plugin to ignore
|
||||||
|
.gitignore
|
||||||
|
.cache/
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class GitPlugin(PapersPlugin):
|
||||||
|
"""The git plugin creates a git repository in the pubs directory and commit the changes
|
||||||
|
to the pubs repository everytime a paper is modified.
|
||||||
|
|
||||||
|
It also add the `pubs git` subcommand, so git commands can be executed in the git repository
|
||||||
|
from the command line.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'git'
|
||||||
|
description = "Run git commands in the pubs directory"
|
||||||
|
|
||||||
|
def __init__(self, conf, ui):
|
||||||
|
self.ui = ui
|
||||||
|
self.pubsdir = os.path.expanduser(conf['main']['pubsdir'])
|
||||||
|
self.manual = conf['plugins'].get('git', {}).get('manual', False)
|
||||||
|
self.force_color = conf['plugins'].get('git', {}).get('force_color', True)
|
||||||
|
self.quiet = conf['plugins'].get('git', {}).get('quiet', True)
|
||||||
|
self.list_of_changes = []
|
||||||
|
self._gitinit()
|
||||||
|
|
||||||
|
def _gitinit(self):
|
||||||
|
"""Initialize the git repository if necessary."""
|
||||||
|
# check that a `.git` directory is present in the pubs dir
|
||||||
|
git_path = os.path.join(self.pubsdir, '.git')
|
||||||
|
if not os.path.isdir(git_path):
|
||||||
|
try:
|
||||||
|
self.shell('init')
|
||||||
|
except RuntimeError as exc:
|
||||||
|
self.ui.error(exc.args[0])
|
||||||
|
sys.exit(1)
|
||||||
|
# check that a `.gitignore` file is present
|
||||||
|
gitignore_path = os.path.join(self.pubsdir, '.gitignore')
|
||||||
|
if not os.path.isfile(gitignore_path):
|
||||||
|
with open(gitignore_path, 'w') as fd:
|
||||||
|
fd.write(GITIGNORE)
|
||||||
|
|
||||||
|
def update_parser(self, subparsers, conf):
|
||||||
|
"""Allow the usage of the pubs git subcommand"""
|
||||||
|
git_parser = subparsers.add_parser(self.name, help=self.description)
|
||||||
|
# FIXME: there may be some problems here with the -c argument being ambiguous between
|
||||||
|
# pubs and git.
|
||||||
|
git_parser.add_argument('arguments', nargs=argparse.REMAINDER, help="look at man git")
|
||||||
|
git_parser.set_defaults(func=self.command)
|
||||||
|
|
||||||
|
def command(self, conf, args):
|
||||||
|
"""Execute a git command in the pubs directory"""
|
||||||
|
self.shell(' '.join([shell_quote(a) for a in args.arguments]), command=True)
|
||||||
|
|
||||||
|
def shell(self, cmd, input_stdin=None, command=False):
|
||||||
|
"""Runs the git program in a shell
|
||||||
|
|
||||||
|
:param cmd: the git command, and all arguments, as a single string (e.g. 'add .')
|
||||||
|
:param input_stdin: if Python 3, must be bytes (i.e., from str, s.encode('utf-8'))
|
||||||
|
:param command: if True, we're dealing with an explicit `pubs git` invocation.
|
||||||
|
"""
|
||||||
|
colorize = ' -c color.ui=always' if self.force_color else ''
|
||||||
|
git_cmd = 'git -C {}{} {}'.format(self.pubsdir, colorize, cmd)
|
||||||
|
#print(git_cmd)
|
||||||
|
p = Popen(git_cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=True)
|
||||||
|
output, err = p.communicate(input_stdin)
|
||||||
|
p.wait()
|
||||||
|
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise RuntimeError('The git plugin encountered an error when running the git command:\n' +
|
||||||
|
'{}\n\nReturned output:\n{}\n'.format(git_cmd, output.decode('utf-8')) +
|
||||||
|
'If needed, you may fix the state of the {} git repository '.format(self.pubsdir) +
|
||||||
|
'manually.\nIf relevant, you may submit a bug report at ' +
|
||||||
|
'https://github.com/pubs/pubs/issues')
|
||||||
|
elif command:
|
||||||
|
self.ui.message(output.decode('utf-8'), end='')
|
||||||
|
elif not self.quiet:
|
||||||
|
self.ui.info(output.decode('utf-8'))
|
||||||
|
return output, err, p.returncode
|
||||||
|
|
||||||
|
|
||||||
|
@PaperChangeEvent.listen()
|
||||||
|
def paper_change_event(event):
|
||||||
|
"""When a paper is changed, commit the changes to the directory."""
|
||||||
|
if GitPlugin.is_loaded():
|
||||||
|
git = GitPlugin.get_instance()
|
||||||
|
if not git.manual:
|
||||||
|
event_desc = event.description
|
||||||
|
for a, b in [('\\','\\\\'), ('"','\\"'), ('$','\\$'), ('`','\\`')]:
|
||||||
|
event_desc = event_desc.replace(a, b)
|
||||||
|
git.list_of_changes.append(event_desc)
|
||||||
|
|
||||||
|
@PostCommandEvent.listen()
|
||||||
|
def git_commit(event):
|
||||||
|
if GitPlugin.is_loaded():
|
||||||
|
try:
|
||||||
|
git = GitPlugin.get_instance()
|
||||||
|
if len(git.list_of_changes) > 0:
|
||||||
|
if not git.manual:
|
||||||
|
title = ' '.join(sys.argv) + '\n'
|
||||||
|
message = '\n'.join([title] + git.list_of_changes)
|
||||||
|
|
||||||
|
git.shell('add .')
|
||||||
|
git.shell('commit -F-', message.encode('utf-8'))
|
||||||
|
except RuntimeError as exc:
|
||||||
|
uis.get_ui().warning(exc.args[0])
|
@ -1,10 +1,13 @@
|
|||||||
# PYTHON_ARGCOMPLETE_OK
|
# PYTHON_ARGCOMPLETE_OK
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from . import uis
|
from . import uis
|
||||||
from . import p3
|
from . import p3
|
||||||
from . import config
|
from . import config
|
||||||
|
from . import events
|
||||||
from . import commands
|
from . import commands
|
||||||
from . import update
|
from . import update
|
||||||
from . import plugins
|
from . import plugins
|
||||||
@ -38,15 +41,16 @@ CORE_CMDS = collections.OrderedDict([
|
|||||||
def execute(raw_args=sys.argv):
|
def execute(raw_args=sys.argv):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conf_parser = p3.ArgumentParser(prog="pubs", add_help=False)
|
desc = 'Pubs: your bibliography on the command line.\nVisit https://github.com/pubs/pubs for more information.'
|
||||||
conf_parser.add_argument("-c", "--config", help="path to config file",
|
parser = p3.ArgumentParser(prog="pubs", add_help=False, description=desc)
|
||||||
|
parser.add_argument("-c", "--config", help="path to an alternate configuration file",
|
||||||
type=str, metavar="FILE")
|
type=str, metavar="FILE")
|
||||||
conf_parser.add_argument('--force-colors', dest='force_colors',
|
parser.add_argument('--force-colors', dest='force_colors',
|
||||||
action='store_true', default=False,
|
action='store_true', default=False,
|
||||||
help='color are not disabled when piping to a file or other commands')
|
help='colors are not disabled when piping to a file or other commands')
|
||||||
#conf_parser.add_argument("-u", "--update", help="update config if needed",
|
#parser.add_argument("-u", "--update", help="update config if needed",
|
||||||
# default=False, action='store_true')
|
# default=False, action='store_true')
|
||||||
top_args, remaining_args = conf_parser.parse_known_args(raw_args[1:])
|
top_args, remaining_args = parser.parse_known_args(raw_args[1:])
|
||||||
|
|
||||||
if top_args.config:
|
if top_args.config:
|
||||||
conf_path = top_args.config
|
conf_path = top_args.config
|
||||||
@ -70,10 +74,9 @@ def execute(raw_args=sys.argv):
|
|||||||
uis.init_ui(conf, force_colors=top_args.force_colors)
|
uis.init_ui(conf, force_colors=top_args.force_colors)
|
||||||
ui = uis.get_ui()
|
ui = uis.get_ui()
|
||||||
|
|
||||||
desc = 'Pubs: your bibliography on the command line.\nVisit https://github.com/pubs/pubs for more information.'
|
parser.add_argument('-v', '--version', action='version', version=__version__)
|
||||||
parser = p3.ArgumentParser(description=desc,
|
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
|
||||||
prog="pubs", add_help=True)
|
help='Show this help message and exit.')
|
||||||
parser.add_argument('--version', action='version', version=__version__)
|
|
||||||
subparsers = parser.add_subparsers(title="commands", dest="command")
|
subparsers = parser.add_subparsers(title="commands", dest="command")
|
||||||
|
|
||||||
# Populate the parser with core commands
|
# Populate the parser with core commands
|
||||||
@ -96,9 +99,12 @@ def execute(raw_args=sys.argv):
|
|||||||
parser.print_help(file=sys.stderr)
|
parser.print_help(file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
events.PreCommandEvent().send()
|
||||||
args.prog = "pubs" # FIXME?
|
args.prog = "pubs" # FIXME?
|
||||||
args.func(conf, args)
|
args.func(conf, args)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not uis.get_ui().handle_exception(e):
|
if not uis.get_ui().handle_exception(e):
|
||||||
raise
|
raise
|
||||||
|
finally:
|
||||||
|
events.PostCommandEvent().send()
|
||||||
|
@ -126,6 +126,7 @@ class Repository(object):
|
|||||||
p = self.pull_paper(citekey)
|
p = self.pull_paper(citekey)
|
||||||
p.docpath = None
|
p.docpath = None
|
||||||
self.push_paper(p, overwrite=True, event=False)
|
self.push_paper(p, overwrite=True, event=False)
|
||||||
|
events.DocRemoveEvent(citekey).send()
|
||||||
except IOError:
|
except IOError:
|
||||||
# FIXME: if IOError is about being unable to
|
# FIXME: if IOError is about being unable to
|
||||||
# remove the file, we need to issue an error.I
|
# remove the file, we need to issue an error.I
|
||||||
@ -191,6 +192,7 @@ class Repository(object):
|
|||||||
docfile = system_path(docfile)
|
docfile = system_path(docfile)
|
||||||
p.docpath = docfile
|
p.docpath = docfile
|
||||||
self.push_paper(p, overwrite=True, event=False)
|
self.push_paper(p, overwrite=True, event=False)
|
||||||
|
events.DocAddEvent(citekey).send()
|
||||||
|
|
||||||
def unique_citekey(self, base_key, bibentry):
|
def unique_citekey(self, base_key, bibentry):
|
||||||
"""Create a unique citekey for a given base key.
|
"""Create a unique citekey for a given base key.
|
||||||
|
13
pubs/uis.py
13
pubs/uis.py
@ -105,6 +105,19 @@ class PrintUI(object):
|
|||||||
self.exit()
|
self.exit()
|
||||||
return True # never happens
|
return True # never happens
|
||||||
|
|
||||||
|
def test_handle_exception(self, exc):
|
||||||
|
"""Attempts to handle exception.
|
||||||
|
|
||||||
|
:returns: True if exception has been handled (currently never happens)
|
||||||
|
"""
|
||||||
|
self.error(ustr(exc))
|
||||||
|
if DEBUG or self.debug:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self.exit()
|
||||||
|
return True # never happens
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InputUI(PrintUI):
|
class InputUI(PrintUI):
|
||||||
"""UI class. Stores configuration parameters and system information.
|
"""UI class. Stores configuration parameters and system information.
|
||||||
|
@ -130,6 +130,8 @@ You can access the self-documented configuration by using `pubs conf`, and all t
|
|||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
|
### Creators
|
||||||
|
|
||||||
- [Fabien Benureau](http://fabien.benureau.com)
|
- [Fabien Benureau](http://fabien.benureau.com)
|
||||||
- [Olivier Mangin](http://olivier.mangin.com)
|
- [Olivier Mangin](http://olivier.mangin.com)
|
||||||
|
|
||||||
@ -141,5 +143,6 @@ You can access the self-documented configuration by using `pubs conf`, and all t
|
|||||||
- [Tyler Earnest](https://github.com/tmearnest)
|
- [Tyler Earnest](https://github.com/tmearnest)
|
||||||
- [Dennis Wilson](https://github.com/d9w)
|
- [Dennis Wilson](https://github.com/d9w)
|
||||||
- [Bill Flynn](https://github.com/wflynny)
|
- [Bill Flynn](https://github.com/wflynny)
|
||||||
- [ksunden](https://github.com/ksunden)
|
- [Kyle Sunden](https://github.com/ksunden)
|
||||||
- [Shane Stone](https://github.com/shanewstone)
|
- [Shane Stone](https://github.com/shanewstone)
|
||||||
|
- [Amlesh Sivanantham](http://github.com/zamlz)
|
3
setup.py
3
setup.py
@ -36,7 +36,8 @@ setup(
|
|||||||
'pubs.commands',
|
'pubs.commands',
|
||||||
'pubs.templates',
|
'pubs.templates',
|
||||||
'pubs.plugs',
|
'pubs.plugs',
|
||||||
'pubs.plugs.alias'],
|
'pubs.plugs.alias',
|
||||||
|
'pubs.plugs.git'],
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'pubs=pubs.pubs_cmd:execute',
|
'pubs=pubs.pubs_cmd:execute',
|
||||||
|
@ -9,7 +9,7 @@ import dotdot
|
|||||||
from pyfakefs import fake_filesystem, fake_filesystem_unittest
|
from pyfakefs import fake_filesystem, fake_filesystem_unittest
|
||||||
|
|
||||||
from pubs.p3 import input, _fake_stdio, _get_fake_stdio_ucontent
|
from pubs.p3 import input, _fake_stdio, _get_fake_stdio_ucontent
|
||||||
from pubs import content, filebroker
|
from pubs import content, filebroker, uis
|
||||||
|
|
||||||
# code for fake fs
|
# code for fake fs
|
||||||
|
|
||||||
@ -20,6 +20,8 @@ real_shutil = shutil
|
|||||||
real_glob = glob
|
real_glob = glob
|
||||||
real_io = io
|
real_io = io
|
||||||
|
|
||||||
|
original_exception_handler = uis.InputUI.handle_exception
|
||||||
|
|
||||||
|
|
||||||
# capture output
|
# capture output
|
||||||
|
|
||||||
@ -70,6 +72,7 @@ class FakeInput():
|
|||||||
self.inputs = list(inputs) or []
|
self.inputs = list(inputs) or []
|
||||||
self.module_list = module_list
|
self.module_list = module_list
|
||||||
self._cursor = 0
|
self._cursor = 0
|
||||||
|
self._original_handler = None
|
||||||
|
|
||||||
def as_global(self):
|
def as_global(self):
|
||||||
for md in self.module_list:
|
for md in self.module_list:
|
||||||
@ -78,13 +81,11 @@ class FakeInput():
|
|||||||
md.InputUI.editor_input = self
|
md.InputUI.editor_input = self
|
||||||
md.InputUI.edit_file = self.input_to_file
|
md.InputUI.edit_file = self.input_to_file
|
||||||
# Do not catch UnexpectedInput
|
# Do not catch UnexpectedInput
|
||||||
original_handler = md.InputUI.handle_exception
|
|
||||||
|
|
||||||
def handler(ui, exc):
|
def handler(ui, exc):
|
||||||
if isinstance(exc, self.UnexpectedInput):
|
if isinstance(exc, self.UnexpectedInput):
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
original_handler(ui, exc)
|
original_exception_handler(ui, exc)
|
||||||
|
|
||||||
md.InputUI.handle_exception = handler
|
md.InputUI.handle_exception = handler
|
||||||
|
|
||||||
|
251
tests/sand_env.py
Normal file
251
tests/sand_env.py
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from pubs import pubs_cmd, color, content, uis, p3, events
|
||||||
|
from pubs.config import conf
|
||||||
|
from pubs.p3 import _fake_stdio, _get_fake_stdio_ucontent
|
||||||
|
|
||||||
|
|
||||||
|
# makes the tests very noisy
|
||||||
|
PRINT_OUTPUT = False
|
||||||
|
CAPTURE_OUTPUT = True
|
||||||
|
|
||||||
|
original_exception_handler = uis.InputUI.handle_exception
|
||||||
|
|
||||||
|
|
||||||
|
class FakeSystemExit(Exception):
|
||||||
|
"""\
|
||||||
|
SystemExit exceptions are replaced by FakeSystemExit in the execute_cmds()
|
||||||
|
function, so they can be catched by ExpectedFailure tests in Python 2.x.
|
||||||
|
|
||||||
|
If a code is expected to raise SystemExit, catch FakeSystemExit instead.
|
||||||
|
|
||||||
|
Added explicit __init__ so SystemExit.code functionality could be emulated.
|
||||||
|
Taking form from https://stackoverflow.com/a/26938914/1634191
|
||||||
|
"""
|
||||||
|
def __init__(self, code=None, *args):
|
||||||
|
self.code = code
|
||||||
|
super(FakeSystemExit, self).__init__(
|
||||||
|
"Exited with code: {}.".format(self.code), *args)
|
||||||
|
|
||||||
|
|
||||||
|
# capture output
|
||||||
|
|
||||||
|
def capture(f, verbose=False):
|
||||||
|
"""Capture the stdout and stderr output.
|
||||||
|
|
||||||
|
Useful for comparing the output with the expected one during tests.
|
||||||
|
|
||||||
|
:param f: The function to capture output from.
|
||||||
|
:param verbose: If True, print call will still display their outputs.
|
||||||
|
If False, they will be silenced.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def newf(*args, **kwargs):
|
||||||
|
old_stderr, old_stdout = sys.stderr, sys.stdout
|
||||||
|
sys.stdout = _fake_stdio(additional_out=old_stderr if verbose else None)
|
||||||
|
sys.stderr = _fake_stdio(additional_out=old_stderr if False else None)
|
||||||
|
try:
|
||||||
|
return f(*args, **kwargs), _get_fake_stdio_ucontent(sys.stdout), _get_fake_stdio_ucontent(sys.stderr)
|
||||||
|
finally:
|
||||||
|
sys.stderr, sys.stdout = old_stderr, old_stdout
|
||||||
|
return newf
|
||||||
|
|
||||||
|
|
||||||
|
# scriptable input
|
||||||
|
|
||||||
|
class FakeInput():
|
||||||
|
""" Replace the input() command, and mock user input during tests
|
||||||
|
|
||||||
|
Instanciate as :
|
||||||
|
input = FakeInput(['yes', 'no'])
|
||||||
|
then replace the input command in every module of the package :
|
||||||
|
input.as_global()
|
||||||
|
Then :
|
||||||
|
input() returns 'yes'
|
||||||
|
input() returns 'no'
|
||||||
|
input() raises IndexError
|
||||||
|
"""
|
||||||
|
class UnexpectedInput(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, inputs, module_list=tuple()):
|
||||||
|
self.inputs = list(inputs) or []
|
||||||
|
self.module_list = module_list
|
||||||
|
self._cursor = 0
|
||||||
|
|
||||||
|
def as_global(self):
|
||||||
|
for md in self.module_list:
|
||||||
|
md.input = self
|
||||||
|
if md.__name__ == 'pubs.uis':
|
||||||
|
md.InputUI.editor_input = self
|
||||||
|
md.InputUI.edit_file = self.input_to_file
|
||||||
|
|
||||||
|
def handler(ui, exc):
|
||||||
|
if isinstance(exc, self.UnexpectedInput):
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
original_exception_handler(ui, exc)
|
||||||
|
|
||||||
|
md.InputUI.handle_exception = handler
|
||||||
|
|
||||||
|
def input_to_file(self, path_to_file, temporary=True):
|
||||||
|
content.write_file(path_to_file, self())
|
||||||
|
|
||||||
|
def add_input(self, inp):
|
||||||
|
self.inputs.append(inp)
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
inp = self.inputs[self._cursor]
|
||||||
|
self._cursor += 1
|
||||||
|
return inp
|
||||||
|
except IndexError:
|
||||||
|
raise self.UnexpectedInput('Unexpected user input in test.')
|
||||||
|
|
||||||
|
|
||||||
|
class SandboxedCommandTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
maxDiff = 1000000
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(SandboxedCommandTestCase, self).setUp()
|
||||||
|
self.temp_dir = tempfile.mkdtemp()
|
||||||
|
self.default_pubs_dir = os.path.join(self.temp_dir, 'pubs')
|
||||||
|
self.default_conf_path = os.path.join(self.temp_dir, 'pubsrc')
|
||||||
|
os.chdir(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _normalize(s):
|
||||||
|
"""Normalize a string for robust comparisons."""
|
||||||
|
s = color.undye(s)
|
||||||
|
try:
|
||||||
|
s = s.decode('utf-8')
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return s
|
||||||
|
|
||||||
|
def _compare_output(self, s1, s2):
|
||||||
|
if s1 is not None and s2 is not None:
|
||||||
|
return self.assertEqual(self._normalize(s1), self._normalize(s2))
|
||||||
|
|
||||||
|
def _preprocess_cmd(self, cmd):
|
||||||
|
"""Sandbox the pubs command into a temporary directory"""
|
||||||
|
cmd_chunks = cmd.split(' ')
|
||||||
|
assert cmd_chunks[0] == 'pubs'
|
||||||
|
prefix = ['pubs', '-c', self.default_conf_path]
|
||||||
|
if cmd_chunks[1] == 'init':
|
||||||
|
return ' '.join(prefix + ['init', '-p', self.default_pubs_dir] + cmd_chunks[2:])
|
||||||
|
else:
|
||||||
|
return ' '.join(prefix + cmd_chunks[1:])
|
||||||
|
|
||||||
|
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, 3 or 4.
|
||||||
|
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 expected output on stdout, verified with assertEqual.
|
||||||
|
4. the expected output on stderr, verified with assertEqual. (this does not work yet)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
outs = []
|
||||||
|
for cmd in cmds:
|
||||||
|
inputs = []
|
||||||
|
expected_out, expected_err = None, None
|
||||||
|
assert isinstance(cmd, tuple)
|
||||||
|
actual_cmd = cmd[0]
|
||||||
|
if len(cmd) >= 2 and cmd[1] is not None: # Inputs provided
|
||||||
|
inputs = cmd[1]
|
||||||
|
if len(cmd) >= 3: # Expected output provided
|
||||||
|
capture_output = True
|
||||||
|
if cmd[2] is not None:
|
||||||
|
expected_out = color.undye(cmd[2])
|
||||||
|
if len(cmd) >= 4 and cmd[3] is not None: # Expected error output provided
|
||||||
|
expected_err = color.undye(cmd[3])
|
||||||
|
actual_cmd = self._preprocess_cmd(actual_cmd)
|
||||||
|
# Always set fake input: test should not ask unexpected user input
|
||||||
|
input = FakeInput(inputs, [content, uis, p3])
|
||||||
|
input.as_global()
|
||||||
|
try:
|
||||||
|
if capture_output:
|
||||||
|
execute_captured = capture(pubs_cmd.execute, verbose=PRINT_OUTPUT)
|
||||||
|
_, stdout, stderr = execute_captured(actual_cmd.split())
|
||||||
|
self._compare_output(stdout, expected_out)
|
||||||
|
self._compare_output(stderr, expected_err)
|
||||||
|
outs.append(self._normalize(stdout))
|
||||||
|
else:
|
||||||
|
pubs_cmd.execute(actual_cmd.split())
|
||||||
|
except FakeInput.UnexpectedInput:
|
||||||
|
self.fail('Unexpected input asked by command: {}.'.format(actual_cmd))
|
||||||
|
return outs
|
||||||
|
except SystemExit as exc:
|
||||||
|
exc_class, exc, tb = sys.exc_info()
|
||||||
|
if sys.version_info.major == 2:
|
||||||
|
# using six to avoid a SyntaxError in Python 3.x
|
||||||
|
six.reraise(FakeSystemExit, FakeSystemExit(*exc.args), tb)
|
||||||
|
else:
|
||||||
|
raise FakeSystemExit(*exc.args).with_traceback(tb)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Testing the test environments
|
||||||
|
|
||||||
|
|
||||||
|
class TestInput(unittest.TestCase):
|
||||||
|
"""Test that the fake input mechanisms work correctly in the tests"""
|
||||||
|
|
||||||
|
def test_input(self):
|
||||||
|
input = FakeInput(['yes', 'no'])
|
||||||
|
self.assertEqual(input(), 'yes')
|
||||||
|
self.assertEqual(input(), 'no')
|
||||||
|
with self.assertRaises(FakeInput.UnexpectedInput):
|
||||||
|
input()
|
||||||
|
|
||||||
|
def test_input2(self):
|
||||||
|
other_input = FakeInput(['yes', 'no'], module_list=[color])
|
||||||
|
other_input.as_global()
|
||||||
|
self.assertEqual(color.input(), 'yes')
|
||||||
|
self.assertEqual(color.input(), 'no')
|
||||||
|
with self.assertRaises(FakeInput.UnexpectedInput):
|
||||||
|
color.input()
|
||||||
|
|
||||||
|
def test_editor_input(self):
|
||||||
|
sample_conf = conf.load_default_conf()
|
||||||
|
ui = uis.InputUI(sample_conf)
|
||||||
|
|
||||||
|
other_input = FakeInput(['yes', 'no'], module_list=[uis])
|
||||||
|
other_input.as_global()
|
||||||
|
self.assertEqual(ui.editor_input('fake_editor'), 'yes')
|
||||||
|
self.assertEqual(ui.editor_input('fake_editor'), 'no')
|
||||||
|
with self.assertRaises(FakeInput.UnexpectedInput):
|
||||||
|
ui.editor_input()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSandboxedCommandTestCase(SandboxedCommandTestCase):
|
||||||
|
|
||||||
|
def test_init_add(self):
|
||||||
|
"""Simple init and add example"""
|
||||||
|
correct = ("added to pubs:\n"
|
||||||
|
"[Page99] Page, Lawrence et al. \"The PageRank Citation Ranking: Bringing Order to the Web.\" (1999) \n")
|
||||||
|
cmds = [('pubs init',),
|
||||||
|
('pubs add data/pagerank.bib', [], correct),
|
||||||
|
#('pubs add abc', [], '', 'error: File does not exist: /Users/self/Volumes/ResearchSync/projects/pubs/abc\n')
|
||||||
|
]
|
||||||
|
self.execute_cmds(cmds)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
106
tests/test_git.py
Normal file
106
tests/test_git.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import unittest
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import sand_env
|
||||||
|
|
||||||
|
from pubs import config
|
||||||
|
|
||||||
|
|
||||||
|
def git_hash(pubsdir):
|
||||||
|
"""Return the git revision"""
|
||||||
|
hash_cmd = ('git', '-C', pubsdir, 'rev-parse', 'HEAD')
|
||||||
|
return subprocess.check_output(hash_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGitPlugin(sand_env.SandboxedCommandTestCase):
|
||||||
|
|
||||||
|
def setUp(self, nsec_stat=True):
|
||||||
|
super(TestGitPlugin, self).setUp()
|
||||||
|
self.execute_cmds([('pubs init',)])
|
||||||
|
conf = config.load_conf(path=self.default_conf_path)
|
||||||
|
conf['plugins']['active'] = ['git']
|
||||||
|
config.save_conf(conf, path=self.default_conf_path)
|
||||||
|
|
||||||
|
def test_git(self):
|
||||||
|
self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
hash_a = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
hash_b = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs rename Page99a ABC',)])
|
||||||
|
hash_c = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs remove ABC', ['y']),])
|
||||||
|
hash_d = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs doc add testrepo/doc/Page99.pdf Page99',)])
|
||||||
|
hash_e = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs doc remove Page99', ['y'])])
|
||||||
|
hash_f = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs tag Page99 bla+bli',)])
|
||||||
|
hash_g = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs list',)])
|
||||||
|
hash_h = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs edit Page99', ['@misc{Page99, title="TTT" author="X. YY"}', 'y',
|
||||||
|
'@misc{Page99, title="TTT", author="X. YY"}', ''])])
|
||||||
|
hash_i = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.assertNotEqual(hash_a, hash_b)
|
||||||
|
self.assertNotEqual(hash_b, hash_c)
|
||||||
|
self.assertNotEqual(hash_c, hash_d)
|
||||||
|
self.assertNotEqual(hash_d, hash_e)
|
||||||
|
self.assertNotEqual(hash_e, hash_f)
|
||||||
|
self.assertNotEqual(hash_f, hash_g)
|
||||||
|
self.assertEqual(hash_g, hash_h)
|
||||||
|
self.assertNotEqual(hash_h, hash_i)
|
||||||
|
|
||||||
|
# # basically can't test that because each command is not completely independent in
|
||||||
|
# # SandoboxedCommands.
|
||||||
|
# # will work if we use subprocess.
|
||||||
|
# conf = config.load_conf(path=self.default_conf_path)
|
||||||
|
# conf['plugins']['active'] = []
|
||||||
|
# config.save_conf(conf, path=self.default_conf_path)
|
||||||
|
#
|
||||||
|
# self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
# hash_j = git_hash(self.default_pubs_dir)
|
||||||
|
#
|
||||||
|
# self.assertEqual(hash_i, hash_j)
|
||||||
|
|
||||||
|
def test_manual(self):
|
||||||
|
conf = config.load_conf(path=self.default_conf_path)
|
||||||
|
conf['plugins']['active'] = ['git']
|
||||||
|
conf['plugins']['git']['manual'] = True
|
||||||
|
config.save_conf(conf, path=self.default_conf_path)
|
||||||
|
|
||||||
|
# this three lines just to initialize the git HEAD
|
||||||
|
self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
self.execute_cmds([('pubs git add .',)])
|
||||||
|
self.execute_cmds([('pubs git commit -m "initial_commit"',)])
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
hash_j = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs add data/pagerank.bib',)])
|
||||||
|
hash_k = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.assertEqual(hash_j, hash_k)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs git add .',)])
|
||||||
|
hash_l = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.assertEqual(hash_k, hash_l)
|
||||||
|
|
||||||
|
self.execute_cmds([('pubs git commit -m "abc"',)])
|
||||||
|
hash_m = git_hash(self.default_pubs_dir)
|
||||||
|
|
||||||
|
self.assertNotEqual(hash_l, hash_m)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -69,11 +69,11 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
self.conf['plugins']['active'] = ['alias']
|
self.conf['plugins']['active'] = ['alias']
|
||||||
|
|
||||||
def testAliasPluginCreated(self):
|
def testAliasPluginCreated(self):
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
|
|
||||||
def testAliasPluginOneCommnand(self):
|
def testAliasPluginOneCommnand(self):
|
||||||
self.conf['plugins']['alias'] = {'print': 'open -w lpppp'}
|
self.conf['plugins']['alias'] = {'print': 'open -w lpppp'}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.assertEqual(len(self.plugin.aliases), 1)
|
self.assertEqual(len(self.plugin.aliases), 1)
|
||||||
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
||||||
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
||||||
@ -81,7 +81,7 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def testAliasPluginOneShell(self):
|
def testAliasPluginOneShell(self):
|
||||||
self.conf['plugins']['alias'] = {'count': '!pubs list -k | wc -l'}
|
self.conf['plugins']['alias'] = {'count': '!pubs list -k | wc -l'}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.assertEqual(len(self.plugin.aliases), 1)
|
self.assertEqual(len(self.plugin.aliases), 1)
|
||||||
self.assertEqual(type(self.plugin.aliases[0]), ShellAlias)
|
self.assertEqual(type(self.plugin.aliases[0]), ShellAlias)
|
||||||
self.assertEqual(self.plugin.aliases[0].name, 'count')
|
self.assertEqual(self.plugin.aliases[0].name, 'count')
|
||||||
@ -91,13 +91,13 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
def testAliasPluginTwoCommnands(self):
|
def testAliasPluginTwoCommnands(self):
|
||||||
self.conf['plugins']['alias'] = {'print': 'open -w lpppp',
|
self.conf['plugins']['alias'] = {'print': 'open -w lpppp',
|
||||||
'count': '!pubs list -k | wc -l'}
|
'count': '!pubs list -k | wc -l'}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.assertEqual(len(self.plugin.aliases), 2)
|
self.assertEqual(len(self.plugin.aliases), 2)
|
||||||
|
|
||||||
def testAliasPluginNestedDefinitionType(self):
|
def testAliasPluginNestedDefinitionType(self):
|
||||||
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
||||||
'command': 'open -w lpppp'}}
|
'command': 'open -w lpppp'}}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.assertEqual(len(self.plugin.aliases), 1)
|
self.assertEqual(len(self.plugin.aliases), 1)
|
||||||
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
||||||
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
||||||
@ -106,7 +106,7 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
def testAliasPluginNestedDefinitionNoDescription(self):
|
def testAliasPluginNestedDefinitionNoDescription(self):
|
||||||
self.conf['plugins']['alias'] = {'print': {'command': 'open -w lpppp'}}
|
self.conf['plugins']['alias'] = {'print': {'command': 'open -w lpppp'}}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.assertEqual(len(self.plugin.aliases), 1)
|
self.assertEqual(len(self.plugin.aliases), 1)
|
||||||
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
||||||
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
||||||
@ -118,7 +118,7 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
||||||
'command': 'open -w lpppp'},
|
'command': 'open -w lpppp'},
|
||||||
'count': '!pubs list -k | wc -l'}
|
'count': '!pubs list -k | wc -l'}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
self.plugin.aliases = sorted(self.plugin.aliases, key=lambda a: a.name)
|
self.plugin.aliases = sorted(self.plugin.aliases, key=lambda a: a.name)
|
||||||
|
|
||||||
self.assertEqual(len(self.plugin.aliases), 2)
|
self.assertEqual(len(self.plugin.aliases), 2)
|
||||||
@ -139,7 +139,7 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
self.conf['plugins']['alias'] = {'print': {'description': 'print this',
|
||||||
'command': 'open -w lpppp',
|
'command': 'open -w lpppp',
|
||||||
'count': '!pubs list -k | wc -l'}}
|
'count': '!pubs list -k | wc -l'}}
|
||||||
self.plugin = AliasPlugin(self.conf)
|
self.plugin = AliasPlugin(self.conf, None)
|
||||||
|
|
||||||
self.assertEqual(len(self.plugin.aliases), 1)
|
self.assertEqual(len(self.plugin.aliases), 1)
|
||||||
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
self.assertEqual(type(self.plugin.aliases[0]), CommandAlias)
|
||||||
@ -147,3 +147,8 @@ class AliasPluginTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
self.assertEqual(self.plugin.aliases[0].name, 'print')
|
||||||
self.assertEqual(self.plugin.aliases[0].description, 'print this')
|
self.assertEqual(self.plugin.aliases[0].description, 'print this')
|
||||||
self.assertEqual(self.plugin.aliases[0].definition, 'open -w lpppp')
|
self.assertEqual(self.plugin.aliases[0].definition, 'open -w lpppp')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user