Commit 13b748ac authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge remote-tracking branch 'origin/master'

parents 34653821 19b5623a
0.9
---
* Dropped support for Python 2.7.
* Add ability to filter on creation/completion dates:
topydo ls created:today
topydo ls completed:today
topydo -t done.txt completed:today # if auto-archiving is set
* `ls -F` supports `%P` that expands to a single space when no priority is set,
in contrast to `%p` which expands to an empty string (thanks to @MinchinWeb).
* `ls -N` prints enough todo items such that it fits on one screen (thanks to
@MinchinWeb).
* Aliases can have a `{}` placeholder which is substituted with the alias'
arguments (thanks to @mruwek).
* `pri` accepts priorities in lowercase (thanks to @MinchinWeb).
* Several bugfixes for `dep gc`.
* Various test/CI improvements.
0.8
---
......
......@@ -29,9 +29,18 @@ Simply install with:
### Optional dependencies
* [icalendar][7] : To print your todo.txt file as an iCalendar file
(not supported for Python 3.2).
(not supported for PyPy3).
* [prompt-toolkit][6] : For topydo's _prompt_ mode, which offers a shell-like
interface with auto-completion.
* [arrow][8] : Used to turn dates into a human readable version.
* [backports.shutil_get_terminal_size][9] : Used to determine your terminal
window size. This function was
added to the standard library in
Python 3.3 and so is only
required for PyPy3.
* [python-dateutil][10]: A dependency of *arrow*.
* [mock][11] : Used for testing. This was added to the standard
library in Python 3.3.
Demo
----
......@@ -46,3 +55,7 @@ Demo
[5]: https://raw.githubusercontent.com/bram85/topydo/master/doc/topydo.gif
[6]: https://github.com/jonathanslenders/python-prompt-toolkit
[7]: https://github.com/collective/icalendar
[8]: https://github.com/crsmithdev/arrow
[9]: https://github.com/chrippa/backports.shutil_get_terminal_size
[10]: https://dateutil.readthedocs.org/
[11]: https://github.com/testing-cabal/mock
......@@ -33,9 +33,11 @@ setup(
],
extras_require = {
':sys_platform=="win32"': ['colorama>=0.2.5'],
':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'],
'ical': ['icalendar'],
'prompt-toolkit': ['prompt-toolkit >= 0.53'],
'test': ['coverage', 'freezegun', 'green', ],
'test:python_version=="3.2"': ['mock'],
},
entry_points= {
'console_scripts': ['topydo = topydo.cli.UILoader:main'],
......
......@@ -20,7 +20,7 @@ from topydo.lib.Utils import escape_ansi
class CommandTest(TopydoTest):
def __init__(self, *args, **kwargs):
super(CommandTest, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.output = ""
self.errors = ""
......
(A) item 1
(B) item 2
(C) item 3
(D) item 4
(E) item 5
(F) item 6
(G) item 7
(H) item 8
(I) item 9
(J) item 10
(K) item 11
(L) item 12
(M) item 13
(N) item 14
(O) item 15
(P) item 16
(Q) item 17
(R) item 18
(S) item 19
(T) item 20
(U) item 21
(V) item 22
(W) item 23
(X) item 24
(Y) item 25
(Z) item 26
(A) item 27
(B) item 28
(C) item 29
(D) item 30
(E) item 31
(F) item 32
(G) item 33
(H) item 34
(I) item 35
(J) item 36
(K) item 37
(L) item 38
(M) item 39
(N) item 40
(O) item 41
(P) item 42
(Q) item 43
(R) item 44
(S) item 45
(T) item 46
(U) item 47
(V) item 48
(W) item 49
(X) item 50
......@@ -3,3 +3,4 @@ foo = rm -f test
baz = FooBar
format = ls -F "|I| x c d {(}p{)} s k" -n 25
smile = ls
star = tag {} star 1
......@@ -33,7 +33,7 @@ except ImportError:
class AddCommandTest(CommandTest):
def setUp(self):
super(AddCommandTest, self).setUp()
super().setUp()
self.todolist = TodoList.TodoList([])
self.today = date.today().isoformat()
......
......@@ -23,7 +23,7 @@ from topydo.lib.TodoList import TodoList
class AppendCommandTest(CommandTest):
def setUp(self):
super(AppendCommandTest, self).setUp()
super().setUp()
self.todolist = TodoList([])
self.todolist.add("Foo")
......
......@@ -33,7 +33,7 @@ def _no_prompt(self):
class DeleteCommandTest(CommandTest):
def setUp(self):
super(DeleteCommandTest, self).setUp()
super().setUp()
todos = [
"Foo id:1",
"Bar p:1",
......
......@@ -23,7 +23,7 @@ from topydo.lib.TodoList import TodoList
class DepCommandTest(CommandTest):
def setUp(self):
super(DepCommandTest, self).setUp()
super().setUp()
todos = [
"Foo id:1",
"Bar p:1",
......
......@@ -23,7 +23,7 @@ from topydo.lib.TodoList import TodoList
class DepriCommandTest(CommandTest):
def setUp(self):
super(DepriCommandTest, self).setUp()
super().setUp()
todos = [
"(A) Foo",
"Bar",
......
......@@ -32,7 +32,7 @@ def _no_prompt(self):
class DoCommandTest(CommandTest):
def setUp(self):
super(DoCommandTest, self).setUp()
super().setUp()
todos = [
"Foo id:1",
"Bar p:1",
......
......@@ -33,7 +33,7 @@ except ImportError:
class EditCommandTest(CommandTest):
def setUp(self):
super(EditCommandTest, self).setUp()
super().setUp()
todos = [
"Foo id:1",
"Bar p:1 @test",
......
......@@ -301,7 +301,7 @@ class FilterTest(TopydoTest):
class OrdinalTagFilterTest(TopydoTest):
def setUp(self):
super(OrdinalTagFilterTest, self).setUp()
super().setUp()
today = date.today()
tomorrow = today + timedelta(1)
......@@ -379,9 +379,87 @@ class OrdinalTagFilterTest(TopydoTest):
self.assertEqual(result[0].source(), self.todo3)
class CreationFilterTest(TopydoTest):
def setUp(self):
super().setUp()
self.todo1 = "2015-12-19 With creation date."
self.todo2 = "Without creation date."
self.todos = [Todo(self.todo1), Todo(self.todo2)]
def test_filter1(self):
cf = Filter.CreationFilter('create:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo1)
def test_filter2(self):
cf = Filter.CreationFilter('creation:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo1)
def test_filter3(self):
cf = Filter.CreationFilter('created:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo1)
class CompletionFilterTest(TopydoTest):
def setUp(self):
super().setUp()
self.todo1 = "2015-12-19 With creation date."
self.todo2 = "x 2015-12-19 2015-12-18 Without creation date."
self.todo3 = "x 2015-12-18 Without creation date."
self.todos = [Todo(self.todo1), Todo(self.todo2), Todo(self.todo3)]
def test_filter1(self):
cf = Filter.CompletionFilter('complete:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo2)
def test_filter2(self):
cf = Filter.CompletionFilter('completed:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo2)
def test_filter3(self):
cf = Filter.CompletionFilter('completion:2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].source(), self.todo2)
def test_filter4(self):
cf = Filter.CompletionFilter('completion:<=2015-12-19')
result = cf.filter(self.todos)
self.assertEqual(len(result), 2)
self.assertEqual(result[0].source(), self.todo2)
self.assertEqual(result[1].source(), self.todo3)
class PriorityFilterTest(TopydoTest):
def setUp(self):
super(PriorityFilterTest, self).setUp()
super().setUp()
self.todo1 = "(A) Foo"
self.todo2 = "(B) Bar"
......
......@@ -22,6 +22,7 @@ from topydo.commands.AddCommand import AddCommand
from topydo.commands.DeleteCommand import DeleteCommand
from topydo.commands.ListCommand import ListCommand
from topydo.commands.ListProjectCommand import ListProjectCommand
from topydo.commands.TagCommand import TagCommand
from topydo.lib.Config import config
class GetSubcommandTest(TopydoTest):
......@@ -60,6 +61,14 @@ class GetSubcommandTest(TopydoTest):
self.assertTrue(issubclass(real_cmd, ListCommand))
self.assertEqual(final_args, [u"\u263b"])
def test_alias04(self):
config("test/data/aliases.conf")
args = ["star", "foo"]
real_cmd, final_args = get_subcommand(args)
self.assertTrue(issubclass(real_cmd, TagCommand))
self.assertEqual(final_args, ["foo", "star", "1"])
def test_default_cmd01(self):
args = ["bar"]
real_cmd, final_args = get_subcommand(args)
......
......@@ -22,7 +22,7 @@ from topydo.lib.Graph import DirectedGraph
class GraphTest(TopydoTest):
def setUp(self):
super(GraphTest, self).setUp()
super().setUp()
self.graph = DirectedGraph()
......
......@@ -17,17 +17,27 @@
import codecs
import re
import unittest
from collections import namedtuple
from test.command_testcase import CommandTest
from test.facilities import load_file_to_todolist
from topydo.commands.ListCommand import ListCommand
from topydo.lib.Config import config
# We're searching for 'mock'
# 'mock' was added as 'unittest.mock' in Python 3.3, but PyPy 3 is based on Python 3.2
# pylint: disable=no-name-in-module
try:
from unittest import mock
except ImportError:
import mock
class ListCommandTest(CommandTest):
def setUp(self):
super(ListCommandTest, self).setUp()
super().setUp()
self.todolist = load_file_to_todolist("test/data/ListCommandTest.txt")
self.terminal_size = namedtuple('terminal_size', ['columns', 'lines'])
def test_list01(self):
command = ListCommand([""], self.todolist, self.out, self.error)
......@@ -313,8 +323,64 @@ class ListCommandTest(CommandTest):
self.assertEqual(self.errors, "option -z not recognized\n")
def test_list42(self):
command = ListCommand(["-x", "+Project1", "-id:1"], self.todolist, self.out,
self.error)
command = ListCommand(["-x", "+Project1", "-id:1"], self.todolist,
self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
self.assertEqual(self.errors, "")
def test_list43(self):
"""Test basic 'N' parameter."""
command = ListCommand(["-N"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n| 4| (C) Drink beer @ home\n| 5| (C) 13 + 29 = 42\n| 2| (D) Bar @Context1 +Project2\n")
self.assertEqual(self.errors, "")
@mock.patch('topydo.commands.ListCommand.get_terminal_size')
def test_list44(self, mock_terminal_size):
"""
Test 'N' parameter with output longer than available terminal lines.
"""
self.todolist = load_file_to_todolist("test/data/ListCommand_50_items.txt")
mock_terminal_size.return_value = self.terminal_size(80, 23)
command = ListCommand(["-N"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (A) item 1\n| 27| (A) item 27\n| 2| (B) item 2\n| 28| (B) item 28\n| 3| (C) item 3\n| 29| (C) item 29\n| 4| (D) item 4\n| 30| (D) item 30\n| 5| (E) item 5\n| 31| (E) item 31\n| 6| (F) item 6\n| 32| (F) item 32\n| 7| (G) item 7\n| 33| (G) item 33\n| 8| (H) item 8\n| 34| (H) item 34\n| 9| (I) item 9\n| 35| (I) item 35\n| 10| (J) item 10\n| 36| (J) item 36\n| 11| (K) item 11\n")
self.assertEqual(self.errors, "")
@mock.patch('topydo.commands.ListCommand.get_terminal_size')
def test_list45(self, mock_terminal_size):
"""Test basic 'N' parameter with nine line terminal."""
# have 9 lines on the terminal will print 7 items and leave 2 lines
# for the next prompt
mock_terminal_size.return_value = self.terminal_size(100, 9)
self.todolist = load_file_to_todolist("test/data/ListCommand_50_items.txt")
command = ListCommand(["-N"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (A) item 1\n| 27| (A) item 27\n| 2| (B) item 2\n| 28| (B) item 28\n| 3| (C) item 3\n| 29| (C) item 29\n| 4| (D) item 4\n")
self.assertEqual(self.errors, "")
@mock.patch('topydo.commands.ListCommand.get_terminal_size')
def test_list46(self, mock_terminal_size):
"""Test basic 'N' parameter with zero height terminal."""
# we still print at least 1 item
mock_terminal_size.return_value = self.terminal_size(100, 0)
self.todolist = load_file_to_todolist("test/data/ListCommand_50_items.txt")
command = ListCommand(["-N"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (A) item 1\n")
self.assertEqual(self.errors, "")
def test_list47(self):
command = ListCommand(["created:2015-11-05"], self.todolist, self.out, self.error)
command.execute()
self.assertEqual(self.output, "| 1| (C) 2015-11-05 Foo @Context2 Not@Context +Project1 Not+Project\n")
......@@ -331,7 +397,7 @@ class ListCommandTest(CommandTest):
class ListCommandUnicodeTest(CommandTest):
def setUp(self):
super(ListCommandUnicodeTest, self).setUp()
super().setUp()
self.todolist = load_file_to_todolist("test/data/ListCommandUnicodeTest.txt")
def test_list_unicode1(self):
......
This diff is collapsed.
......@@ -24,7 +24,7 @@ from topydo.lib.TodoList import TodoList
class PostponeCommandTest(CommandTest):
def setUp(self):
super(PostponeCommandTest, self).setUp()
super().setUp()
self.today = date.today()
self.past = date.today() - timedelta(1)
self.future = date.today() + timedelta(1)
......
......@@ -23,7 +23,7 @@ from topydo.lib.TodoList import TodoList
class PriorityCommandTest(CommandTest):
def setUp(self):
super(PriorityCommandTest, self).setUp()
super().setUp()
todos = [
"(A) Foo",
"Bar",
......
......@@ -25,7 +25,7 @@ from topydo.lib.Todo import Todo
class RecurrenceTest(TopydoTest):
def setUp(self):
super(RecurrenceTest, self).setUp()
super().setUp()
self.todo = Todo("Test rec:1w")
self.stricttodo = Todo("Test rec:+1w")
......
......@@ -25,7 +25,7 @@ from topydo.lib.RelativeDate import relative_date_to_date
@freeze_time('2015, 11, 06')
class RelativeDateTester(TopydoTest):
def setUp(self):
super(RelativeDateTester, self).setUp()
super().setUp()
self.yesterday = date(2015, 11, 5)
self.today = date(2015, 11, 6)
self.tomorrow = date(2015, 11, 7)
......
......@@ -35,7 +35,7 @@ from topydo.lib.TodoList import TodoList
class RevertCommandTest(CommandTest):
def setUp(self):
super(RevertCommandTest, self).setUp()
super().setUp()
todos = [
"Foo",
"Bar",
......
......@@ -24,7 +24,7 @@ from topydo.lib.Config import config
class SortCommandTest(CommandTest):
def setUp(self):
super(SortCommandTest, self).setUp()
super().setUp()
self.todolist = load_file_to_todolist("test/data/SorterTest1.txt")
def test_sort1(self):
......
......@@ -24,7 +24,7 @@ from topydo.lib.TodoList import TodoList
class TagCommandTest(CommandTest):
def setUp(self):
super(TagCommandTest, self).setUp()
super().setUp()
todos = [
"Foo",
"Bar due:2014-10-22",
......
......@@ -29,7 +29,7 @@ from topydo.lib.TodoListBase import InvalidTodoException, TodoListBase
class TodoListTester(TopydoTest):
def setUp(self):
super(TodoListTester, self).setUp()
super().setUp()
self.todofile = TodoFile('test/data/TodoListTest.txt')
lines = [line for line in self.todofile.read()
......@@ -237,7 +237,7 @@ class TodoListTester(TopydoTest):
class TodoListDependencyTester(TopydoTest):
def setUp(self):
super(TodoListDependencyTester, self).setUp()
super().setUp()
self.todolist = TodoList([])
self.todolist.add("Foo id:1")
......@@ -391,7 +391,7 @@ class TodoListCleanDependencyTester(TopydoTest):
"""
def setUp(self):
super(TodoListCleanDependencyTester, self).setUp()
super().setUp()
self.todolist = TodoList([])
def test_clean_dependencies1(self):
......
......@@ -52,7 +52,9 @@ append_parent_contexts = 0
[aliases]
;showall = ls -x
;next = ls -n 1
;top = ls -F '|%I| %x %p %S %k %{(}H{)}'
;top = ls -F '%I %x %P %S %k %{(}H{)}' -N
;star = tag {} star 1
;unstar = tag {} star
;lsproj = lsprj
;listprj = lsprj
;listproj = lsprj
......
......@@ -71,6 +71,18 @@ def get_subcommand(p_args):
__import__(modulename, globals(), locals(), [classname], 0)
return getattr(sys.modules[modulename], classname)
def join_args(p_cli_args, p_alias_args):
"""
Returns properly joined args from alias definition and from user input.
"""
if '{}' in p_alias_args:
pos = p_alias_args.index('{}')
args = p_alias_args[:pos] + p_cli_args + p_alias_args[pos+1:]
else:
args = p_alias_args + p_cli_args
return args
def resolve_alias(p_alias, p_args):
"""
Resolves a subcommand alias and returns a tuple (Command, args).
......@@ -81,7 +93,7 @@ def get_subcommand(p_args):
real_subcommand, alias_args = alias_map[p_alias]
try:
result = import_subcommand(real_subcommand)
args = alias_args + p_args
args = join_args(p_args, alias_args)
return (result, args)
except KeyError:
return get_subcommand(['help'])
......
......@@ -41,7 +41,7 @@ class CLIApplication(CLIApplicationBase):
"""
def __init__(self):
super(CLIApplication, self).__init__()
super().__init__()
def run(self):
""" Main entry function. """
......
......@@ -59,7 +59,7 @@ class PromptApplication(CLIApplicationBase):
"""
def __init__(self):
super(PromptApplication, self).__init__()
super().__init__()
self._process_flags()
self.mtime = None
......
......@@ -34,7 +34,7 @@ class AddCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(AddCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.text = ' '.join(p_args)
self.from_file = None
......@@ -119,7 +119,7 @@ class AddCommand(Command):
def execute(self):
""" Adds a todo item to the list. """
if not super(AddCommand, self).execute():
if not super().execute():
return False
self.printer.add_filter(PrettyPrinterNumbers(self.todolist))
......
......@@ -24,11 +24,11 @@ class AppendCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(AppendCommand, self).__init__(p_args, p_todolist, p_out, p_err,
super().__init__(p_args, p_todolist, p_out, p_err,
p_prompt)
def execute(self):
if not super(AppendCommand, self).execute():
if not super().execute():
return False
try:
......
......@@ -27,7 +27,7 @@ class ArchiveCommand(Command):
TodoListBase class which does no dependency checking, so a better
choice for huge done.txt files.
"""
super(ArchiveCommand, self).__init__([], p_todolist)
super().__init__([], p_todolist)
self.archive = p_archive_list
def execute(self):
......
......@@ -22,7 +22,7 @@ class DeleteCommand(DCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(DeleteCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def prompt_text(self):
......
......@@ -28,7 +28,7 @@ class DepCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(DepCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
try:
......@@ -109,7 +109,7 @@ class DepCommand(Command):
self.error(self.usage())
def execute(self):
if not super(DepCommand, self).execute():
if not super().execute():
return False
dispatch = {
......
......@@ -23,7 +23,7 @@ class DepriCommand(MultiCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(DepriCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def _execute_multi_specific(self):
......
......@@ -33,17 +33,17 @@ class DoCommand(DCommand):
self.strict_recurrence = False
self.completion_date = date.today()
super(DoCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def get_flags(self):
""" Additional flags. """
opts, long_opts = super(DoCommand, self).get_flags()
opts, long_opts = super().get_flags()
return ("d:s" + opts, ["date=", "strict"] + long_opts)
def process_flag(self, p_opt, p_value):
super(DoCommand, self).process_flag(p_opt, p_value)
super().process_flag(p_opt, p_value)
if p_opt == "-s" or p_opt == "--strict":
self.strict_recurrence = True
......
......@@ -39,7 +39,7 @@ def _is_edited(p_orig_mtime, p_file):
class EditCommand(MultiCommand):
def __init__(self, p_args, p_todolist, p_output, p_error, p_input):
super(EditCommand, self).__init__(p_args, p_todolist, p_output,
super().__init__(p_args, p_todolist, p_output,
p_error, p_input)
if len(self.args) == 0:
......
......@@ -26,11 +26,11 @@ class ExitCommand(Command):
"""
def __init__(self, p_args, p_todolist, p_output, p_error, p_input):
super(ExitCommand, self).__init__(p_args, p_todolist, p_output, p_error,
super().__init__(p_args, p_todolist, p_output, p_error,
p_input)
def execute(self):
if not super(ExitCommand, self).execute():
if not super().execute():
return False
sys.exit(0)
......@@ -20,6 +20,7 @@ from topydo.lib.Filter import InstanceFilter
from topydo.lib.PrettyPrinter import pretty_printer_factory
from topydo.lib.prettyprinters.Format import PrettyPrinterFormatFilter
from topydo.lib.TodoListBase import InvalidTodoException
from topydo.lib.Utils import get_terminal_size
class ListCommand(ExpressionCommand):
......@@ -27,7 +28,7 @@ class ListCommand(ExpressionCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(ListCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.printer = None
......@@ -50,7 +51,7 @@ class ListCommand(ExpressionCommand):
return True
def _process_flags(self):
opts, args = self.getopt('f:F:i:n:s:x')
opts, args = self.getopt('f:F:i:n:Ns:x')
for opt, value in opts:
if opt == '-x':
......@@ -69,11 +70,15 @@ class ListCommand(ExpressionCommand):
self.printer = None
elif opt == '-F':
self.format = value
elif opt == '-N':
# 2 lines are assumed to be taken up by printing the next prompt
# display at least one item
self.limit = max(get_terminal_size().lines - 2, 1)
elif opt == '-n':
try:
self.limit = int(value)
except ValueError:
pass # use default value in configuration
pass # use default value in configuration
elif opt == '-i':
self.ids = value.split(',')
......@@ -87,7 +92,7 @@ class ListCommand(ExpressionCommand):
Additional filters to select particular todo items given with the -i
flag.
"""
filters = super(ListCommand, self)._filters()
filters = super()._filters()
if self.ids:
def get_todo(p_id):
......@@ -127,7 +132,7 @@ class ListCommand(ExpressionCommand):
self.out(self.printer.print_list(self._view().todos))
def execute(self):
if not super(ListCommand, self).execute():
if not super().execute():
return False
try:
......@@ -142,7 +147,7 @@ class ListCommand(ExpressionCommand):
def usage(self):
return """Synopsis: ls [-x] [-s <sort_expression>] [-f <output format>]
[-F <format string>] [expression]"""
[-F <format string>] [-i <item numbers>] [-N | -n <integer>] [expression]"""
def help(self):
return """\
......@@ -177,6 +182,7 @@ When an expression is given, only the todos matching that expression are shown.
%k: List of tags separated by spaces (excluding hidden tags).
%K: List of all tags separated by spaces.
%p: Priority.
%P: Priority or placeholder space if no priority.
%s: Todo text.
%S: Todo text, truncated such that an item fits on one line.
%t: Absolute creation date.
......@@ -193,6 +199,8 @@ When an expression is given, only the todos matching that expression are shown.
A tab character serves as a marker to start right alignment.
-i : Comma separated list of todo IDs to print.
-n : Number of items to display. Defaults to the value in the configuration.
-N : Limit number of items displayed such that they fit on the terminal.
-s : Sort the list according to a sort expression. Defaults to the expression
in the configuration.
-x : Show all todos (i.e. do not filter on dependencies or relevance).
......
......@@ -22,11 +22,11 @@ class ListContextCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(ListContextCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def execute(self):
if not super(ListContextCommand, self).execute():
if not super().execute():
return False
for context in sorted(self.todolist.contexts(), key=lambda s: s.lower()):
......
......@@ -22,11 +22,11 @@ class ListProjectCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(ListProjectCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def execute(self):
if not super(ListProjectCommand, self).execute():
if not super().execute():
return False
for project in sorted(self.todolist.projects(), key=lambda s: s.lower()):
......
......@@ -28,7 +28,7 @@ class PostponeCommand(MultiCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(PostponeCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.move_start_date = False
......
......@@ -26,7 +26,7 @@ class PriorityCommand(MultiCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(PriorityCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.last_argument = True
......
......@@ -25,11 +25,11 @@ class RevertCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(RevertCommand, self).__init__(p_args, p_todolist, p_out, p_err,
super().__init__(p_args, p_todolist, p_out, p_err,
p_prompt)
def execute(self):
if not super(RevertCommand, self).execute():
if not super().execute():
return False
archive_file = TodoFile.TodoFile(config().archive())
......
......@@ -24,11 +24,11 @@ class SortCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(SortCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
def execute(self):
if not super(SortCommand, self).execute():
if not super().execute():
return False
try:
......
......@@ -26,7 +26,7 @@ class TagCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(TagCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.force = False
......@@ -124,7 +124,7 @@ class TagCommand(Command):
self._print()
def execute(self):
if not super(TagCommand, self).execute():
if not super().execute():
return False
self._process_args()
......
......@@ -31,7 +31,7 @@ class DCommand(MultiCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(DCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.force = False
......
......@@ -32,7 +32,7 @@ class ExpressionCommand(Command):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(ExpressionCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.sort_expression = config().sort_string()
......@@ -58,11 +58,13 @@ class ExpressionCommand(Command):
is_negated = len(arg) > 1 and arg[0] == '-'
arg = arg[1:] if is_negated else arg
if re.match(Filter.ORDINAL_TAG_MATCH, arg):
argfilter = Filter.OrdinalTagFilter(arg)
elif re.match(Filter.PRIORITY_MATCH, arg):
argfilter = Filter.PriorityFilter(arg)
else:
argfilter = None
for match, _filter in Filter.MATCHES:
if re.match(match, arg):
argfilter = _filter(arg)
break
if not argfilter:
argfilter = Filter.GrepFilter(arg)
if is_negated:
......
......@@ -62,7 +62,7 @@ class GrepFilter(Filter):
""" Matches when the todo text contains a text. """
def __init__(self, p_expression, p_case_sensitive=None):
super(GrepFilter, self).__init__()
super().__init__()
# convert to string in case we receive integers
self.expression = p_expression
......@@ -115,7 +115,7 @@ class DependencyFilter(Filter):
Pass on a TodoList instance such that the dependencies can be
looked up.
"""
super(DependencyFilter, self).__init__()
super().__init__()
self.todolist = p_todolist
def match(self, p_todo):
......@@ -138,7 +138,7 @@ class InstanceFilter(Filter):
This is handy for constructing a view given a plain list of Todo items.
"""
super(InstanceFilter, self).__init__()
super().__init__()
self.todos = p_todos
def match(self, p_todo):
......@@ -154,20 +154,20 @@ class InstanceFilter(Filter):
class LimitFilter(Filter):
def __init__(self, p_limit):
super(LimitFilter, self).__init__()
super().__init__()
self.limit = p_limit
def filter(self, p_todos):
return p_todos[:self.limit] if self.limit >= 0 else p_todos
OPERATOR_MATCH = r"(?P<operator><=?|=|>=?|!)?"
_OPERATOR_MATCH = r"(?P<operator><=?|=|>=?|!)?"
class OrdinalFilter(Filter):
""" Base class for ordinal filters. """
def __init__(self, p_expression, p_pattern):
super(OrdinalFilter, self).__init__()
super().__init__()
self.expression = p_expression
......@@ -200,12 +200,13 @@ class OrdinalFilter(Filter):
return False
ORDINAL_TAG_MATCH = r"(?P<key>[^:]*):" + OPERATOR_MATCH + r"(?P<value>\S+)"
_VALUE_MATCH = r"(?P<value>\S+)"
_ORDINAL_TAG_MATCH = r"(?P<key>[^:]*):" + _OPERATOR_MATCH + _VALUE_MATCH
class OrdinalTagFilter(OrdinalFilter):
def __init__(self, p_expression):
super(OrdinalTagFilter, self).__init__(p_expression, ORDINAL_TAG_MATCH)
super().__init__(p_expression, _ORDINAL_TAG_MATCH)
def match(self, p_todo):
"""
......@@ -243,12 +244,55 @@ class OrdinalTagFilter(OrdinalFilter):
return self.compare_operands(operand1, operand2)
PRIORITY_MATCH = r"\(" + OPERATOR_MATCH + r"(?P<value>[A-Z]{1})\)"
class _DateAttributeFilter(OrdinalFilter):
def __init__(self, p_expression, p_match, p_getter):
super().__init__(p_expression, p_match)
self.getter = p_getter
def match(self, p_todo):
operand1 = self.getter(p_todo)
operand2 = relative_date_to_date(self.value)
if not operand2:
operand2 = date_string_to_date(self.value)
if operand1 and operand2:
return self.compare_operands(operand1, operand2)
else:
return False
_CREATED_MATCH = r'creat(ion|ed?):' + _OPERATOR_MATCH + _VALUE_MATCH
class CreationFilter(_DateAttributeFilter):
def __init__(self, p_expression):
super().__init__(
p_expression,
_CREATED_MATCH,
lambda t: t.creation_date() # pragma: no branch
)
_COMPLETED_MATCH = r'complet(ed?|ion):' + _OPERATOR_MATCH + _VALUE_MATCH
class CompletionFilter(_DateAttributeFilter):
def __init__(self, p_expression):
super().__init__(
p_expression,
_COMPLETED_MATCH,
lambda t: t.completion_date() # pragma: no branch
)
_PRIORITY_MATCH = r"\(" + _OPERATOR_MATCH + r"(?P<value>[A-Z]{1})\)"
class PriorityFilter(OrdinalFilter):
def __init__(self, p_expression):
super(PriorityFilter, self).__init__(p_expression, PRIORITY_MATCH)
super().__init__(p_expression, _PRIORITY_MATCH)
def match(self, p_todo):
"""
......@@ -265,3 +309,10 @@ class PriorityFilter(OrdinalFilter):
operand2 = p_todo.priority() or 'ZZ'
return self.compare_operands(operand1, operand2)
MATCHES = [
(_CREATED_MATCH, CreationFilter),
(_COMPLETED_MATCH, CompletionFilter),
(_ORDINAL_TAG_MATCH, OrdinalTagFilter),
(_PRIORITY_MATCH, PriorityFilter),
]
......@@ -66,7 +66,7 @@ class IcalPrinter(Printer):
"""
def __init__(self, p_todolist):
super(IcalPrinter, self).__init__()
super().__init__()
self.todolist = p_todolist
try:
......
......@@ -52,7 +52,7 @@ class JsonPrinter(Printer):
"""
def __init__(self):
super(JsonPrinter, self).__init__()
super().__init__()
def print_todo(self, p_todo):
return json.dumps(_convert_todo(p_todo), ensure_ascii=False,
......
......@@ -152,6 +152,7 @@ class ListFormatParser(object):
# relative dates in form: creation, due, start
'H': lambda t: humanize_dates(t.due_date(), t.start_date(), t.creation_date()),
# todo ID
'i': lambda t: str(self.todolist.number(t)),
......@@ -172,6 +173,9 @@ class ListFormatParser(object):
# priority
'p': lambda t: t.priority() if t.priority() else '',
# priority (or placeholder space)
'P': lambda t: t.priority() if t.priority() else ' ',
# text
's': lambda t: t.text(),
......
......@@ -27,7 +27,7 @@ class MultiCommand(ExpressionCommand):
p_out=lambda a: None,
p_err=lambda a: None,
p_prompt=lambda a: None):
super(MultiCommand, self).__init__(
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.todos = []
......@@ -114,7 +114,7 @@ class MultiCommand(ExpressionCommand):
raise NotImplementedError
def execute(self):
if not super(MultiCommand, self).execute():
if not super().execute():
return False
self._process_flags()
......
......@@ -50,7 +50,7 @@ class PrettyPrinter(Printer):
"""
Constructor.
"""
super(PrettyPrinter, self).__init__()
super().__init__()
self.filters = []
def add_filter(self, p_filter):
......
......@@ -41,7 +41,7 @@ class TodoList(TodoListBase):
self._tododict = {} # hash(todo) to todo lookup
self._depgraph = DirectedGraph()
super(TodoList, self).__init__(p_todostrings)
super().__init__(p_todostrings)
def todo_by_dep_id(self, p_dep_id):
"""
......
......@@ -23,6 +23,12 @@ import re
from collections import namedtuple
from datetime import date
# shutil.get_terminal_size was added to the standard library in Python 3.3
try:
from shutil import get_terminal_size as _get_terminal_size # pylint: disable=no-name-in-module
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size # pylint: disable=import-error
def date_string_to_date(p_date):
"""
......@@ -54,15 +60,14 @@ def escape_ansi(p_string):
escape_ansi.pattern = re.compile(r'\x1b[^m]*m')
def get_terminal_size():
"""
Try to determine terminal size at run time. If that is not possible,
returns the default size of 80x24.
"""
from shutil import get_terminal_size # pylint: disable=no-name-in-module
try:
sz = get_terminal_size()
sz = _get_terminal_size()
except ValueError:
"""
This can result from the 'underlying buffer being detached', which
......
""" Version of Topydo. """
VERSION = '0.8'
VERSION = '0.9'
LICENSE = """Copyright (C) 2014-2015 Bram Schoenmakers
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
......
......@@ -25,7 +25,7 @@ from topydo.lib.ListFormat import ListFormatParser
class PrettyPrinterFormatFilter(PrettyPrinterFilter):
def __init__(self, p_todolist, p_format=None):
super(PrettyPrinterFormatFilter, self).__init__()
super().__init__()
self.parser = ListFormatParser(p_todolist, p_format)
def filter(self, p_todo_str, p_todo):
......
......@@ -23,7 +23,7 @@ class PrettyPrinterNumbers(PrettyPrinterFilter):
""" Prepends the todo's number, retrieved from the todolist. """
def __init__(self, p_todolist):
super(PrettyPrinterNumbers, self).__init__()
super().__init__()
self.todolist = p_todolist
def filter(self, p_todo_str, p_todo):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment