This repository has been archived by the owner on Apr 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
FIX: compatibility with Python v2.x #9
Open
nnako
wants to merge
20
commits into
EpocDotFr:master
Choose a base branch
from
nnako:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
deb9dc2
FIX: compatibility with Python v2.x
nnako b9d9e7a
FIX: the CREATION DATE will always appear in the 4th group of the def…
nnako 5f5af12
removed some comments
nnako ec2f205
update of documentation and Python version info
nnako d87c529
NEW: PERSON specifications by using different SQUARE BRACKET formats
nnako 71b6937
some minimal comment changes
f84d5bf
FIX: usage of serialize() instead of __str__() so in Python v2 unicod…
ac5c86f
FIX: facilitate normal time string "12:34" within text by forcing sta…
e46fc3a
FIX: occasional creation date mismatch corrected on import of TODOTXT…
dae5165
FIX: return REMARKS not as list but as string containing '\n'
c878bf7
FIX: remove warning message for REMARK to be no string
0bda3de
FIX: evaluate REMARKS before PERSONS to facilitate square brackets wi…
11080b6
NEW: prioritized evaluation of specific link attributes (http, https,
983b3a2
FIX: for detection of tags, take tag name until FIRST colon char
36cadce
added better comments
161b031
FIX: ignore single PLUS symbol for project specification (while readi…
87d9eaa
FIX: change order of member detection. now, e.g. projects are not rea…
9a16193
todotxt-v1.1.2
nnako 68d2b20
FIX: stabilize project / subject identification against special chara…
nnako fe5d5f2
FIX: use recursive regular expressions for identifying remarks correctly
nnako File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
|
||
import os | ||
import re | ||
import io | ||
|
||
__version__ = '0.2.2' | ||
|
||
|
@@ -16,14 +20,20 @@ | |
'search' | ||
] | ||
|
||
todo_data_regex = re.compile('^(?:(x) )?(?:(\d{4}-\d{2}-\d{2}) )?(?:\(([A-Z])\) )?(?:(\d{4}-\d{2}-\d{2}) )?') | ||
todo_data_regex = re.compile( \ | ||
'^(?:(x) )?' + \ | ||
'(?:(\d{4}-\d{2}-\d{2}) )?' + \ | ||
'(?:\(([A-Z])\) )?' + \ | ||
'(?:(\d{4}-\d{2}-\d{2}) )?' \ | ||
) | ||
todo_project_regex = re.compile(' \+(\S*)') | ||
todo_context_regex = re.compile(' @(\S*)') | ||
todo_tag_regex = re.compile(' (\S*):(\S*)') | ||
|
||
|
||
def from_dicts(todos): | ||
"""Convert a list of todo dicts to a list of :class:`todotxtio.Todo` objects. | ||
""" | ||
Convert a list of todo dicts to a list of :class:`todotxtio.Todo` objects. | ||
|
||
:param list todos: A list of todo dicts | ||
:rtype: list | ||
|
@@ -32,7 +42,8 @@ def from_dicts(todos): | |
|
||
|
||
def from_stream(stream, close=True): | ||
"""Load a todo list from an already-opened stream. | ||
""" | ||
Load a todo list from an already-opened stream. | ||
|
||
:param file stream: A file-like object | ||
:param bool close: Whetever to close the stream or not after all operation are finised | ||
|
@@ -47,7 +58,8 @@ def from_stream(stream, close=True): | |
|
||
|
||
def from_file(file_path, encoding='utf-8'): | ||
"""Load a todo list from a file. | ||
""" | ||
Load a todo list from a file. | ||
|
||
:param str file_path: Path to the file | ||
:param str encoding: The encoding of the file to open | ||
|
@@ -56,26 +68,43 @@ def from_file(file_path, encoding='utf-8'): | |
if not os.path.isfile(file_path): | ||
raise FileNotFoundError('File doesn\'t exists: ' + file_path) | ||
|
||
stream = open(file_path, 'r', encoding=encoding) | ||
stream = io.open(file_path, 'r', encoding=encoding) | ||
|
||
return from_stream(stream) | ||
|
||
|
||
def from_string(string): | ||
"""Load a todo list from a string. | ||
""" | ||
Load a todo list from a string. | ||
|
||
:param str string: The string to parse | ||
:rtype: list | ||
""" | ||
|
||
# init | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sorry but the comments you added in this function doesn't add any useful information, given the statements are simple enough to understand. Please remove them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are right. the comments don't add any functionality. It is just a question of style and readability in general (which is in the end a question of personal taste). I can remove it. |
||
todos = [] | ||
|
||
|
||
|
||
# | ||
# evaluate each line | ||
# | ||
|
||
for line in string.strip().splitlines(): | ||
|
||
line = line.strip() | ||
|
||
todo_pre_data = todo_data_regex.match(line) | ||
|
||
todo = Todo() | ||
|
||
|
||
|
||
|
||
# | ||
# evaluate prefix data | ||
# | ||
|
||
if todo_pre_data: | ||
todo.completed = todo_pre_data.group(1) == 'x' | ||
|
||
|
@@ -93,35 +122,49 @@ def from_string(string): | |
else: | ||
text = line | ||
|
||
todo_projects = todo_project_regex.findall(text) | ||
|
||
|
||
# | ||
# evaluate remaining data | ||
# | ||
|
||
# projects | ||
todo_projects = todo_project_regex.findall(text) | ||
if len(todo_projects) > 0: | ||
todo.projects = todo_projects | ||
text = todo_project_regex.sub('', text).strip() | ||
|
||
# contexts | ||
todo_contexts = todo_context_regex.findall(text) | ||
|
||
if len(todo_contexts) > 0: | ||
todo.contexts = todo_contexts | ||
text = todo_context_regex.sub('', text).strip() | ||
|
||
# tags | ||
todo_tags = todo_tag_regex.findall(text) | ||
|
||
if len(todo_tags) > 0: | ||
for todo_tag in todo_tags: | ||
todo.tags[todo_tag[0]] = todo_tag[1] | ||
|
||
text = todo_tag_regex.sub('', text).strip() | ||
|
||
# text | ||
todo.text = text | ||
|
||
|
||
|
||
# | ||
# add this TODO to list of todos | ||
# | ||
|
||
todos.append(todo) | ||
|
||
return todos | ||
|
||
|
||
def to_dicts(todos): | ||
"""Convert a list of :class:`todotxtio.Todo` objects to a list of todo dict. | ||
""" | ||
Convert a list of :class:`todotxtio.Todo` objects to a list of todo dict. | ||
|
||
:param list todos: List of :class:`todotxtio.Todo` objects | ||
:rtype: list | ||
|
@@ -130,7 +173,8 @@ def to_dicts(todos): | |
|
||
|
||
def to_stream(stream, todos, close=True): | ||
"""Write a list of todos to an already-opened stream. | ||
""" | ||
Write a list of todos to an already-opened stream. | ||
|
||
:param file stream: A file-like object | ||
:param list todos: List of :class:`todotxtio.Todo` objects | ||
|
@@ -144,28 +188,31 @@ def to_stream(stream, todos, close=True): | |
|
||
|
||
def to_file(file_path, todos, encoding='utf-8'): | ||
"""Write a list of todos to a file. | ||
""" | ||
Write a list of todos to a file. | ||
|
||
:param str file_path: Path to the file | ||
:param list todos: List of :class:`todotxtio.Todo` objects | ||
:param str encoding: The encoding of the file to open | ||
:rtype: None | ||
""" | ||
stream = open(file_path, 'w', encoding=encoding) | ||
stream = io.open(file_path, 'w', encoding=encoding) | ||
to_stream(stream, todos) | ||
|
||
|
||
def to_string(todos): | ||
"""Convert a list of todos to a string. | ||
""" | ||
Convert a list of todos to a string. | ||
|
||
:param list todos: List of :class:`todotxtio.Todo` objects | ||
:rtype: str | ||
""" | ||
return '\n'.join([str(todo) for todo in todos]) | ||
|
||
|
||
class Todo: | ||
"""Represent one todo. | ||
class Todo(object): | ||
""" | ||
Represent one todo. | ||
|
||
:param str text: The text of the todo | ||
:param bool completed: Should this todo be marked as completed? | ||
|
@@ -199,7 +246,8 @@ def __init__(self, text=None, completed=False, completion_date=None, priority=No | |
self.tags = tags | ||
|
||
def to_dict(self): | ||
"""Return a dict representation of this Todo instance. | ||
""" | ||
Return a dict representation of this Todo instance. | ||
|
||
:rtype: dict | ||
""" | ||
|
@@ -217,29 +265,31 @@ def to_dict(self): | |
def __setattr__(self, name, value): | ||
if name == 'completed': | ||
if not value: | ||
super().__setattr__('completion_date', None) # Uncompleted todo must not have any completion date | ||
super(Todo, self).__setattr__('completion_date', None) # Uncompleted todo must not have any completion date | ||
elif name == 'completion_date': | ||
if value: | ||
super().__setattr__('completed', True) # Setting the completion date must set this todo as completed... | ||
super(Todo, self).__setattr__('completed', True) # Setting the completion date must set this todo as completed... | ||
else: | ||
super().__setattr__('completed', False) # ...and vice-versa | ||
super(Todo, self).__setattr__('completed', False) # ...and vice-versa | ||
elif name in ['projects', 'contexts']: | ||
if not value: | ||
super().__setattr__(name, []) # Force contexts, projects to be lists when setting them to a falsely value | ||
super(Todo, self).__setattr__(name, []) # Force contexts, projects to be lists when setting them to a falsely value | ||
return | ||
elif type(value) is not list: # Make sure, otherwise, that the provided value is a list | ||
raise ValueError(name + ' should be a list') | ||
elif name == 'tags': | ||
if not value: | ||
super().__setattr__(name, {}) # Force tags to be a dict when setting them to a falsely value | ||
super(Todo, self).__setattr__(name, {}) # Force tags to be a dict when setting them to a falsely value | ||
return | ||
elif type(value) is not dict: # Make sure, otherwise, that the provided value is a dict | ||
raise ValueError(name + ' should be a dict') | ||
|
||
super().__setattr__(name, value) | ||
super(Todo, self).__setattr__(name, value) | ||
|
||
def __str__(self): | ||
"""Convert this Todo object in a valid Todo.txt line.""" | ||
""" | ||
Convert this Todo object in a valid Todo.txt line. | ||
""" | ||
ret = [] | ||
|
||
if self.completed: | ||
|
@@ -268,12 +318,15 @@ def __str__(self): | |
return ' '.join(ret) | ||
|
||
def __repr__(self): | ||
"""Call the __str__ method to return a textual representation of this Todo object.""" | ||
""" | ||
Call the __str__ method to return a textual representation of this Todo object. | ||
""" | ||
return self.__str__() | ||
|
||
|
||
def search(todos, text=None, completed=None, completion_date=None, priority=None, creation_date=None, projects=None, contexts=None, tags=None): | ||
"""Return a list of todos that matches the provided filters. | ||
""" | ||
Return a list of todos that matches the provided filters. | ||
|
||
It takes the exact same parameters as the :class:`todotxtio.Todo` object constructor, and return a list of :class:`todotxtio.Todo` objects as well. | ||
All criteria defaults to ``None`` which means that the criteria is ignored. | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file isn't meant to be executable. What is the purpose of this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm... I always create my librarys so that in the end, they can be used as libraries AND can be executed as standalone tools (where this makes sense). So, you are right, here the line makes no sense, yet.