Commit 751105ea authored by Bram Schoenmakers's avatar Bram Schoenmakers

Fixed pylint errors

Among others, moved methods by turning them into (inline) functions or
static methods.
parent e276dcd5
from setuptools import setup, find_packages
import os
import re
import codecs
import sys
here = os.path.abspath(os.path.dirname(__file__))
from setuptools import setup, find_packages
_HERE = os.path.abspath(os.path.dirname(__file__))
def read(*parts):
# intentionally *not* adding an encoding option to open
return codecs.open(os.path.join(here, *parts), 'r').read()
return codecs.open(os.path.join(_HERE, *parts), 'r').read()
def find_version(*file_paths):
......@@ -24,17 +24,17 @@ WATCHDOG = 'watchdog >= 0.8.3'
ICALENDAR = 'icalendar'
setup(
name = "topydo",
packages = find_packages(exclude=["test"]),
version = find_version('topydo', 'lib', 'Version.py'),
description = "A powerful todo.txt application for the console",
author = "Bram Schoenmakers",
author_email = "bram@topydo.org",
url = "https://www.topydo.org",
install_requires = [
name="topydo",
packages=find_packages(exclude=["test"]),
version=find_version('topydo', 'lib', 'Version.py'),
description="A powerful todo.txt application for the console",
author="Bram Schoenmakers",
author_email="bram@topydo.org",
url="https://www.topydo.org",
install_requires=[
'arrow >= 0.7.0',
],
extras_require = {
extras_require={
':sys_platform=="win32"': ['colorama>=0.2.5'],
':python_version=="3.2"': ['backports.shutil_get_terminal_size>=1.0.0'],
'columns': ['urwid >= 1.3.0', WATCHDOG],
......@@ -43,10 +43,10 @@ setup(
'test': ['coverage>=4.3', 'freezegun', 'green', ICALENDAR, 'pylint>=1.7.1'],
'test:python_version=="3.2"': ['mock'],
},
entry_points= {
'console_scripts': ['topydo = topydo.ui.UILoader:main'],
entry_points={
'console_scripts': ['topydo=topydo.ui.UILoader:main'],
},
classifiers = [
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
......@@ -60,7 +60,7 @@ setup(
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Utilities",
],
long_description = """\
long_description="""\
topydo is a powerful and customizable todo.txt application for the console, inspired by the todo.txt CLI by Gina Trapani.
Highlights of the additional features it provides:
......@@ -74,5 +74,5 @@ Highlights of the additional features it provides:
* Some conveniences when adding new items (e.g. adding creation date and use relative dates)
""",
test_suite = "test",
test_suite="test",
)
......@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from unittest import skip
from test.topydo_testcase import TopydoTest
from topydo.lib.Config import config
......@@ -167,7 +166,7 @@ class ConfigTest(TopydoTest):
def test_config28(self):
""" test duplicates. """
keymap, keystates = config("test/data/ConfigTest7.conf").column_keymap()
keymap, _ = config("test/data/ConfigTest7.conf").column_keymap()
self.assertEqual(keymap['k'], 'bar')
self.assertEqual(keymap['z'], 'foobar')
......
......@@ -100,18 +100,17 @@ class DoCommandTest(CommandTest):
self.assertEqual(self.errors, "")
def test_do_subtasks_force1(self):
prompt_shown = False
def prompt(p_prompt):
global prompt_shown
prompt_shown = True
prompt.prompt_shown = True
prompt.prompt_shown = False
command = DoCommand(["-f", "1"], self.todolist, self.out, self.error,
prompt)
command.execute()
command.execute_post_archive_actions()
self.assertFalse(prompt_shown)
self.assertFalse(prompt.prompt_shown)
self.assertEqual(self.errors, "")
self.assertFalse(self.todolist.todo(2).is_completed())
......
......@@ -27,7 +27,7 @@ from topydo.lib.Config import config, ConfigError
class GetSubcommandTest(TopydoTest):
def test_normal_cmd(self):
args = ["add"]
real_cmd, final_args = get_subcommand(args)
real_cmd, _ = get_subcommand(args)
self.assertTrue(issubclass(real_cmd, AddCommand))
def test_cmd_help(self):
......
......@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from datetime import date, timedelta
from datetime import date
from freezegun import freeze_time
from test.topydo_testcase import TopydoTest
......
......@@ -35,6 +35,9 @@ class DoCommand(DCommand):
super().__init__(
p_args, p_todolist, p_out, p_err, p_prompt)
self.condition = lambda todo: not todo.is_completed()
self.condition_failed_text = "Todo has already been completed."
def get_flags(self):
""" Additional flags. """
opts, long_opts = super().get_flags()
......@@ -75,15 +78,6 @@ class DoCommand(DCommand):
def prefix(self):
return "Completed: "
def condition(self, p_todo):
"""
An additional condition whether execute_specific should be executed.
"""
return not p_todo.is_completed()
def condition_failed_text(self):
return "Todo has already been completed."
def execute_specific(self, p_todo):
""" Actions specific to this command. """
self._handle_recurrence(p_todo)
......
......@@ -44,11 +44,11 @@ class EditCommand(MultiCommand):
def get_flags(self):
return ("dE:", [])
def process_flag(self, p_opt, p_value):
if p_opt == '-d':
def process_flag(self, p_option, p_value):
if p_option == '-d':
self.edit_archive = True
self.multi_mode = False
elif p_opt == '-E':
elif p_option == '-E':
self.editor = shlex.split(p_value)
def _process_flags(self):
......@@ -71,9 +71,10 @@ class EditCommand(MultiCommand):
return f
def _todos_from_temp(self, p_temp_file):
f = codecs.open(p_temp_file.name, encoding='utf-8')
todos = f.read().splitlines()
@staticmethod
def _todos_from_temp(p_temp_file):
with codecs.open(p_temp_file.name, encoding='utf-8') as temp:
todos = temp.read().splitlines()
todo_objs = []
for todo in todos:
......@@ -112,7 +113,7 @@ class EditCommand(MultiCommand):
orig_mtime = _get_file_mtime(temp_todos)
if not self._open_in_editor(temp_todos.name):
new_todos = self._todos_from_temp(temp_todos)
new_todos = EditCommand._todos_from_temp(temp_todos)
if _is_edited(orig_mtime, temp_todos):
for todo in self.todos:
......
......@@ -90,7 +90,7 @@ class ListCommand(ExpressionCommand):
elif opt == '-N':
# 2 lines are assumed to be taken up by printing the next prompt
# display at least one item
self.limit = self._N_lines()
self.limit = ListCommand._N_lines()
elif opt == '-n':
try:
self.limit = int(value)
......@@ -164,7 +164,8 @@ class ListCommand(ExpressionCommand):
return View(sorter, filters, self.todolist)
def _N_lines(self):
@staticmethod
def _N_lines():
''' Determine how many lines to print, such that the number of items
displayed will fit on the terminal (i.e one 'screen-ful' of items)
......@@ -176,13 +177,13 @@ class ListCommand(ExpressionCommand):
Otherwise, it looks for a newline ('\n') in the environmental variable
PS1.
'''
'''
lines_in_prompt = 1 # prompt is assumed to take up one line, even
# without any newlines in it
if "win32" in sys.platform:
lines_in_prompt += 1 # Windows will typically print a free line after
# the program output
a = re.findall('\$_', os.getenv('PROMPT', ''))
a = re.findall(r'\$_', os.getenv('PROMPT', ''))
lines_in_prompt += len(a)
else:
a = re.findall('\\n', os.getenv('PS1', ''))
......
......@@ -37,8 +37,8 @@ class PostponeCommand(MultiCommand):
def get_flags(self):
return("s", [])
def process_flag(self, p_opt, p_value):
if p_opt == '-s':
def process_flag(self, p_option, p_value):
if p_option == '-s':
self.move_start_date = True
def _execute_multi_specific(self):
......
......@@ -43,11 +43,11 @@ def get_backup_path():
class ChangeSet(object):
""" Class for operations related with backup management. """
def __init__(self, p_todolist=None, p_archive=None, p_label=[]):
def __init__(self, p_todolist=None, p_archive=None, p_label=None):
self.todolist = deepcopy(p_todolist)
self.archive = deepcopy(p_archive)
self.timestamp = str(int(time.time()))
self.label = ' '.join(p_label)
self.label = ' '.join(p_label if p_label else [])
try:
self.json_file = open(get_backup_path(), 'r+b')
......
......@@ -36,13 +36,14 @@ class DCommand(MultiCommand):
self.force = False
self._delta = []
self.condition = lambda _: True
self.condition_failed_text = ""
def get_flags(self):
return ("f", ["force"])
def process_flag(self, p_opt, p_value):
if p_opt == "-f" or p_opt == "--force":
def process_flag(self, p_option, p_value):
if p_option == "-f" or p_option == "--force":
self.force = True
def _uncompleted_children(self, p_todo):
......@@ -93,15 +94,6 @@ class DCommand(MultiCommand):
return [todo for todo in self.todolist.todos()
if not self._uncompleted_children(todo) and todo.is_active()]
def condition(self, _):
"""
An additional condition whether execute_specific should be executed.
"""
return True
def condition_failed_text(self):
raise NotImplementedError
def execute_specific(self, _):
raise NotImplementedError
......@@ -120,7 +112,7 @@ class DCommand(MultiCommand):
self._process_subtasks(todo)
self.execute_specific(todo)
else:
self.error(self.condition_failed_text())
self.error(self.condition_failed_text)
current_active = self._active_todos()
self._delta = [todo for todo in current_active
......
......@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from topydo.lib import Filter
from topydo.lib.Command import Command
from topydo.lib.Config import config
......
......@@ -205,7 +205,7 @@ class ListFormatParser(object):
'T': lambda t: humanize_date(t.start_date()) if t.start_date() else '',
# unique text ID
'u': lambda t: self.todolist.uid(t),
'u': self.todolist.uid,
# unique text ID, padded with spaces
'U': lambda t: _filler(self.todolist.uid(t),
......
......@@ -148,9 +148,81 @@ class Sorter(object):
"""
def __init__(self, p_sortstring="desc:priority", p_groupstring=""):
self.groupfunctions = self._parse(p_groupstring, p_group=True) if p_groupstring else []
self.pregroupfunctions = self._parse(p_groupstring, p_group=False) if p_groupstring else []
self.sortfunctions = self._parse(p_sortstring, p_group=False)
def parse(p_string, p_group):
"""
Parses a sort/group string and returns a list of functions and the
desired order.
"""
def get_field_function(p_field, p_group=False):
"""
Turns a field, part of a sort/group string, into a lambda that
takes a todo item and returns the field value.
"""
compose = lambda i: i.sort if not p_group else (i.group, i.label)
def group_value(p_todo):
"""
Returns a value to assign the given todo to a group. Date tags
are grouped according to the relative date (1 day, 1 month,
...)
"""
result = 'No value'
if p_todo.has_tag(p_field):
if p_field == config().tag_due():
result = humanize_date(p_todo.due_date())
elif p_field == config().tag_start():
result = humanize_date(p_todo.start_date())
else:
result = p_todo.tag_value(p_field)
try:
result = humanize_date(date_string_to_date(result))
except ValueError:
pass
return result
if p_field in FIELD_MAP:
return compose(FIELDS[FIELD_MAP[p_field]])
else:
# treat it as a tag value
return compose(Field(
sort=lambda t: '0' + t.tag_value(p_field) if t.has_tag(p_field) else '1',
group=group_value,
label=p_field,
))
result = []
fields = p_string.lower().split(',')
for field in fields:
parsed_field = re.match(
r'(?P<order>(asc|desc)(ending)?:)?(?P<field>\S+)',
field)
if not parsed_field:
continue
order = parsed_field.group('order')
order = 'desc' if order and order.startswith('desc') else 'asc'
field = parsed_field.group('field')
if field:
function = get_field_function(field, p_group)
# reverse order for priority: lower characters have higher
# priority
if field in FIELD_MAP and FIELD_MAP[field] == 'priority':
order = 'asc' if order == 'desc' else 'desc'
result.append((function, order))
return result
self.groupfunctions = parse(p_groupstring, p_group=True) if p_groupstring else []
self.pregroupfunctions = parse(p_groupstring, p_group=False) if p_groupstring else []
self.sortfunctions = parse(p_sortstring, p_group=False)
def sort(self, p_todos):
"""
......@@ -198,75 +270,3 @@ class Sorter(object):
return result
def _parse(self, p_string, p_group):
"""
Parses a sort/group string and returns a list of functions and the
desired order.
"""
def get_field_function(p_field, p_group=False):
"""
Turns a field, part of a sort/group string, into a lambda that
takes a todo item and returns the field value.
"""
compose = lambda i: i.sort if not p_group else (i.group, i.label)
def group_value(p_todo):
"""
Returns a value to assign the given todo to a group. Date tags
are grouped according to the relative date (1 day, 1 month,
...)
"""
result = 'No value'
if p_todo.has_tag(p_field):
if p_field == config().tag_due():
result = humanize_date(p_todo.due_date())
elif p_field == config().tag_start():
result = humanize_date(p_todo.start_date())
else:
result = p_todo.tag_value(p_field)
try:
result = humanize_date(date_string_to_date(result))
except ValueError:
pass
return result
if p_field in FIELD_MAP:
return compose(FIELDS[FIELD_MAP[p_field]])
else:
# treat it as a tag value
return compose(Field(
sort=lambda t: '0' + t.tag_value(p_field) if t.has_tag(p_field) else '1',
group=group_value,
label=p_field,
))
result = []
fields = p_string.lower().split(',')
for field in fields:
parsed_field = re.match(
r'(?P<order>(asc|desc)(ending)?:)?(?P<field>\S+)',
field)
if not parsed_field:
continue
order = parsed_field.group('order')
order = 'desc' if order and order.startswith('desc') else 'asc'
field = parsed_field.group('field')
if field:
function = get_field_function(field, p_group)
# reverse order for priority: lower characters have higher
# priority
if field in FIELD_MAP and FIELD_MAP[field] == 'priority':
order = 'asc' if order == 'desc' else 'desc'
result.append((function, order))
return result
......@@ -116,7 +116,7 @@ class TodoList(TodoListBase):
super().add_todos(p_todos)
for todo in self._todos:
todo.parents = types.MethodType(lambda i: self.parents(i), todo)
todo.parents = types.MethodType(self.parents, todo)
# only do administration when the dependency info is initialized,
# otherwise we postpone it until it's really needed (through the
......
......@@ -177,7 +177,8 @@ class CLIApplicationBase(object):
self._post_archive_action = None
self.backup = None
def _usage(self):
@staticmethod
def _usage():
usage()
sys.exit(0)
......@@ -208,7 +209,7 @@ class CLIApplicationBase(object):
elif opt in ("-v", "--version"):
version()
else:
self._usage()
CLIApplicationBase._usage()
if alt_config_path:
config(alt_config_path, overrides)
......@@ -237,21 +238,17 @@ class CLIApplicationBase(object):
if archive.dirty:
archive_file.write(archive.print_todos())
def _help(self, args):
if args is None:
pass # TODO
else:
pass # TODO
def is_read_only(self, p_command):
@staticmethod
def is_read_only(p_command):
""" Returns True when the given command class is read-only. """
read_only_commands = tuple(cmd for cmd
in ('revert', ) + READ_ONLY_COMMANDS)
return p_command.name() in read_only_commands
def _backup(self, p_command, p_args=[], p_label=None):
if config().backup_count() > 0 and p_command and not self.is_read_only(p_command):
call = [p_command.name()]+ p_args
def _backup(self, p_command, p_args=None, p_label=None):
if config().backup_count() > 0 and p_command and not CLIApplicationBase.is_read_only(p_command):
p_args = p_args if p_args else []
call = [p_command.name()] + p_args
from topydo.lib.ChangeSet import ChangeSet
label = p_label if p_label else call
......
......@@ -57,7 +57,7 @@ class CLIApplication(CLIApplicationBase):
sys.exit(1)
if subcommand is None:
self._usage()
CLIApplicationBase._usage()
if self._execute(subcommand, args) == False:
sys.exit(1)
......
......@@ -196,10 +196,63 @@ class UIApplication(CLIApplicationBase):
self._screen = urwid.raw_display.Screen()
def create_color_palette():
project_color = to_urwid_color(config().project_color())
context_color = to_urwid_color(config().context_color())
metadata_color = to_urwid_color(config().metadata_color())
link_color = to_urwid_color(config().link_color())
focus_background_color = to_urwid_color(config().focus_background_color())
marked_background_color = to_urwid_color(config().marked_background_color())
palette = [
(PaletteItem.PROJECT, '', '', '', project_color, ''),
(PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, focus_background_color),
(PaletteItem.CONTEXT, '', '', '', context_color, ''),
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_color),
(PaletteItem.METADATA, '', '', '', metadata_color, ''),
(PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, focus_background_color),
(PaletteItem.LINK, '', '', '', link_color, ''),
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
(PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
(PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
]
for C in ascii_uppercase:
pri_color_cfg = config().priority_color(C)
pri_color = to_urwid_color(pri_color_cfg)
pri_color_focus = pri_color if not pri_color_cfg.is_neutral() else 'black'
palette.append((
'pri_' + C, '', '', '', pri_color, ''
))
palette.append((
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
))
return palette
def create_mono_palette():
palette = [
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'),
(PaletteItem.PROJECT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.CONTEXT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.METADATA_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.LINK_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.MARKED, 'default,underline,bold', 'default'),
]
for C in ascii_uppercase:
palette.append(
('pri_' + C + '_focus', PaletteItem.DEFAULT_FOCUS)
)
return palette
if config().colors():
self._screen.register_palette(self._create_color_palette())
self._screen.register_palette(create_color_palette())
else:
self._screen.register_palette(self._create_mono_palette())
self._screen.register_palette(create_mono_palette())
self._screen.set_terminal_properties(256)
......@@ -213,59 +266,6 @@ class UIApplication(CLIApplicationBase):
self.column_mode = _APPEND_COLUMN
self._set_alarm_for_next_midnight_update()
def _create_color_palette(self):
project_color = to_urwid_color(config().project_color())
context_color = to_urwid_color(config().context_color())
metadata_color = to_urwid_color(config().metadata_color())
link_color = to_urwid_color(config().link_color())
focus_background_color = to_urwid_color(config().focus_background_color())
marked_background_color = to_urwid_color(config().marked_background_color())
palette = [
(PaletteItem.PROJECT, '', '', '', project_color, ''),
(PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, focus_background_color),
(PaletteItem.CONTEXT, '', '', '', context_color, ''),
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_color),
(PaletteItem.METADATA, '', '', '', metadata_color, ''),
(PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, focus_background_color),
(PaletteItem.LINK, '', '', '', link_color, ''),
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
(PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
(PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
]
for C in ascii_uppercase:
pri_color_cfg = config().priority_color(C)
pri_color = to_urwid_color(pri_color_cfg)
pri_color_focus = pri_color if not pri_color_cfg.is_neutral() else 'black'
palette.append((
'pri_' + C, '', '', '', pri_color, ''
))
palette.append((
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
))
return palette
def _create_mono_palette(self):
palette = [
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'),
(PaletteItem.PROJECT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.CONTEXT_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.METADATA_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.LINK_FOCUS, PaletteItem.DEFAULT_FOCUS),
(PaletteItem.MARKED, 'default,underline,bold', 'default'),
]
for C in ascii_uppercase:
palette.append(
('pri_' + C + '_focus', PaletteItem.DEFAULT_FOCUS)
)
return palette
def _set_alarm_for_next_midnight_update(self):
def callback(p_loop, p_data):
TodoWidget.wipe_cache()
......
......@@ -91,7 +91,6 @@ class TodoWidget(urwid.WidgetWrap):
priority_widget = urwid.Text(priority_text)
self.text_widget = urwid.Text(txt_markup)
progress = to_urwid_color(progress_color(p_todo)) if config().colors() else PaletteItem.DEFAULT
self.progress_bar = urwid.AttrMap(
urwid.SolidFill(' '),
{},
......
......@@ -16,7 +16,6 @@
""" Entry file for the topydo Prompt interface (CLI). """
import os.path
import shlex
import sys
......@@ -38,7 +37,6 @@ except ConfigError as config_error:
from topydo.Commands import get_subcommand
from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.lib import TodoList
class PromptApplication(CLIApplicationBase):
......
......@@ -52,15 +52,14 @@ class PromptCompleter(CompleterBase, Completer):
for candidate in candidates:
yield Completion(candidate, -len(p_word))
def get_completions(self, p_document, _):
def get_completions(self, p_word, p_is_first_word=False):
# include all characters except whitespaces (for + and @)
word_before_cursor = p_document.get_word_before_cursor(True)
word_before_cursor = p_word.get_word_before_cursor(True)
is_first_word = not re.match(r'\s*\S+\s',
p_document.current_line_before_cursor)
p_word.current_line_before_cursor)
if word_before_cursor.startswith(config().tag_due() + ':'):
return _dates(word_before_cursor)
elif word_before_cursor.startswith(config().tag_start() + ':'):
return _dates(word_before_cursor)
else:
return self._completion_generator(word_before_cursor, is_first_word)
return self._completion_generator(word_before_cursor, is_first_word)
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