PYTODOTXT(1)

NAME
pytodotxt - pytodotxt

INSTALLING

The easiest way to install pytodotxt is through PyPi:

    pip install pytodotxt

If you’d rather download the source from the authoritative website, visit https://vonshednob.cc/pytodotxt/releases.html and get the signed source tarball from there.

CHANGELOG

This file contains the changes made between released versions.

The format is based on Keep a changelog and the versioning tries to follow Semantic Versioning.

  Not released yet

  Added

•   Task’s method to serialize itself can be configured through the serializer parameter and property

•   TodoTxt can be configured to use a different than the default serialization method (serializer parameter and property)

•   Two serialization methods: serialize_pedantic and serialize_relaxed. The pedantic variant is the default and provides the same behaviour as previous versions.

    The serialize_relaxed method uses a relaxed interpretation of the todo.txt description and will also produce todo.txt tasks that have a “completed on” date, but not a “created on” date (e.g. x 2015-07-21 Prepare tea). It will also insert a priority marker into completed tasks.

  3.0.0

  Breaking change

•   TodoTxt.add() now actually sets the Task.linenr property is accordance to the documentation of Task.linenr, starting at 0 (thanks to Adam)

•   TodoTxt.save(target="...", safe=True) will create the temporary file in the location of the target instead of the location of self.filename (which might be read-only or on a different filesystem than the target)

  Fixed

•   License specification in pyproject.toml is now a proper SPDX identifier

  2.0.0

  Changed

•   Behaviour when encountering a a:b:c was changed to interprete this as key a with value b:c instead of a:b with value c

•   Switched to pyproject.toml

  1.5.0

  Fixed

•   Blank lines in a todo.txt file are ignored (they used to create a completely empty Task)

  1.4.3

  Fixed

•   Task.remove_attribute used to remove everything after the first occurrence of a matching attribute name (thanks to Gal)

  1.4.2

  Fixed

•   Saving an empty TodoTxt would result in a file with a single newline (#9)

  1.4.1

  Fixed

•   Missing trailing line separator when serialising a TodoTxt

  Changed

•   TodoTxt.lines calls TodoTxt.build_lines

  1.4.0

  Fixed

•   Task would add the completion date to its str representation of a completed task even if there was no creation date set

  Added

•   TodoTxtParser to make the parsing of a todo.txt file extendable

•   TodoTxt.write_to_stream to write to an io stream instead of to disk

•   Documentation. Finally.

  1.3.0

  Added

•   __repr__ for both Task and TodoTxt added (thanks to Sean Breckenridge)

  1.2.1

  Fixed

•   Fix crash when creating Task() without any line content

  1.2.0

  Added

•   Detection of line ending when loading todo.txt files (#3, thanks to chaotika)

•   Configurable line ending when saving todo.txt files (thanks to chaotika)

  1.1.0

  Fixed

•   A task that had no description used to crash pytodotxt when attempting to add an attribute (or a context or a project)

  Added

•   bare_description function of Task to get the description without any properties or contexts/projects (#2)

  1.0.7

  Fixed

•   Fixed __getattr__ error (thanks to sandervoerman)

  1.0.6

  Fixed

•   Fixed regression when saving with “safe-save” option

  1.0.5

  Fixed

•   Bug when saving files on Windows with non-ansi characters in the path name

  1.0.4

  Added

•   TodoTxt can be configured for an encoding

  1.0.3

  Added

•   Caching attributes to prevent repeated reparsing

•   Have a reference in a task to the file that it belongs to

•   Convenient access to task’s attributes by task.attr_name, eg. task.attr_due for due date

  1.0.2

  Fixed

•   Bug fix when task consists only of a date

  1.0.0

•   Splitting package into the basic library and its user-facing packages

USAGE EXAMPLES

  Adding a task to a todo.txt file

    """Example how to add a task to an existing todo.txt file"""
    import pytodotxt
    todotxt = pytodotxt.TodoTxt("todo.txt")
    todotxt.parse()
    new_task = pytodotxt.Task("(A) Do some more stuff")
    todotxt.add(new_task)
    todotxt.save()

Instead of calling todotxt.add, you could of course also just call todotxt.tasks.append, but add will conveniently update new_task.todotxt and linenr.

  Mark all tasks of a file as completed

    """How to mark tasks as completed"""
    import datetime
    import pytodotxt
    todotxt = pytodotxt.TodoTxt("todo.txt")
    todotxt.parse()
    for task in todotxt.parse():
        if task.is_completed:
            continue
        task.completion_date = datetime.date.today()
        task.is_completed = True
    todotxt.save()

  Have your own custom Task subclass

    """How to customize your task class"""
    import pytodotxt
    class MyTask(pytodotxt.Task): pass
    todotxt = pytodotxt.TodoTxt("todo.txt",
                                parser=pytodotxt.TodoTxtParser(task_type=MyTask))
    for task in todotxt.parse():
        assert isinstance(task, MyTask)

  Use the relaxed serialization when exporting

    """Example how to use the relaxed serialization"""
    import pytodotxt
    todotxt = pytodotxt.TodoTxt("todo.txt",
                                serializer=pytodotxt.serialize_relaxed)
    todotxt.parse()
    # Completed task with a completion date, but without creation date
    # would lose the completion date with the default (pedantic) serializer
    new_task = pytodotxt.Task("x 2010-01-01 Prepare new year")
    todotxt.add(new_task)
    todotxt.save()

AUTHORS

For the time being, this project is authored by

•   Robert Labudda (https://vonshednob.cc/pytodotxt)

    Gratitude goes to these individuals for their contribution:

•   Sander Voerman (https://github.com/sandervoerman)

•   Markus Katharina Brechtel (https://github.com/chaotika)

•   purarue (https://github.com/purarue)

•   Sylvain303 (https://github.com/Sylvain303)

•   gal064 (https://github.com/gal064)

LICENSE

MIT License

Copyright (c) 2020-2025 Robert Labudda <https://vonshednob.cc/pytodotxt/>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

API REFERENCE

  TodoTxt

class pytodotxt.TodoTxt(filename, encoding='utf-8', parser=None, serializer=None)
    Convenience wrapper for a single todo.txt file

    The most common use is:

        todotxt = TodoTxt("todo.txt")
        todotxt.parse()

    Use the tasks property to access the parsed entries.

__init__(filename, encoding='utf-8', parser=None, serializer=None)
    Create a new TodoTxt instance from filename

Parameters

•   filename (str or pathlib.Path) – the file you wish to use

•   encoding – what encoding to assume for the content of the file

•   parser (TodoTxtParser) – This may be a custom parser instance to use instead of the default TodoTxtParser.

•   serializer – The serializer that will be used when writing the tasks back to disk. Defaults to serialize_pedantic.

add(task)
    Add a task to this container

    You could also just append to self.tasks, but this function call will also update the todotxt and linenr properties of task.

Parameters
    task (Task) – The task that should be added to this todo.txt file

build_lines()
    Returns a list of all tasks converted to string and sorted by linenr

property lines
    Convenience access to self.build_lines

parse()
    (Re)parse self.filename

    This will try to detect the line separator of the file automatically and remember it as self.linesep for the time when you save the file back to disk.

    Tasks will receive the same serializer as this TodoTxt instance.

Returns
    the list of all tasks read from the file.

save(target=None, safe=True, linesep=None)
    Save all tasks to disk

    The self.serializer property will be used to serialize each individual task.

    If target is not provided, the filename property is being used as the target file to save to.

    If safe is set (the default), the file will first be written to a temporary file in the same folder as the target file and after the successful write to disk, it will be moved in place of target. This can cause trouble though with folders that are synchronised to some cloud storage.

    With linesep you can specify the line seperator. If it is not set it defaults to the systems default line seperator (or the detected line separator, if the file has been parsed from disk first).

Parameters

•   target – The file to save to, or None to use self.filename

•   safe – Whether or not to save the file in a way that does not destroy the previous file in case on errors.

•   linesep – The line separator, or None to use self.linesep

tasks
    The tasks of this file

write_to_stream(stream, linesep=None)
    Save all tasks, ordered by their line number, to the stream

Parameters

•   stream – an io.IOBase like stream that accepts bytes being written to it.

•   linesep – the line separator, or None to use self.linesep

  Task

class pytodotxt.Task(line=None, linenr=None, todotxt=None, serializer=None)
    A task of a todo.txt file

    The usual way to create a task is to create it from an initial string:

        task = Task("(B) some task")

    or:

        task = Task()
        task.parse("(B) some task")

    The inverse operation to parsing is to convert the task to a string:

        task = Task("(B) some task")
        assert str(task) == "(B) some task"

__init__(line=None, linenr=None, todotxt=None, serializer=None)
    Create a new task

    If no parameters are provided, an empty task is created.

    You may provide a todo.txtlike textual representation of a task with the line parameter, which will be parsed and all properties will be set accordingly.

    In case this task belongs to a container file (todotxt parameter), you can provide the linenr, if you want to be able to refer to it later-on.

    Both todotxt and linenr parameter are entirely optional and are not connected to any functionality in this class’s functions.

    To access key:value attributes of the task, you can use the convenience notion of task['attr_key'] which will always result in a list. If the task doesn’t have the key attribute that list will be empty though.

    Note that attribute keys and values are split at the first :. That means a task with a:b:c is understood to have the key a and the value b:c.

Parameters

•   line – is the raw string representation (one line of a todo.txt file).

•   linenr – is the line number within the todotxt file, if any. This is purely optional.

•   todotxt (None or TodoTxt.) – the TodoTxt container to which this task belongs, if any.

•   serializer – What serializer funtion to use. Defaults to serialize_pedantic.

add_attribute(key, value)
    Add the key:value attribute to the end of the task

add_context(context)
    Add @context to the end of the task

Parameters
    context – the name of the project, without the leading @

add_project(project)
    Add +project to the end of the task

Parameters
    project – the name of the project, without the leading +

append(text, add_space=True)
    Append text to the end of the task

Parameters

•   text – The text to append

•   add_space – Whether or not to add a space before text.

property attributes
    A dict of all key:values attributes

    The values portion of the dictionary is always a list.

bare_description()
    The description of the task without contexts, projects or other attributes

    Some parts of the description may look like attributes, but are not, like URLs. To make sure that these are not stripped from the description, add them to KEYVALUE_ALLOW.

completion_date
    datetime.date of when the task was completed, or None if no completion date given

property contexts
    A list of all @context attributes

creation_date
    datetime.date of when the task was created, or None if no creation date given

description
    The descriptive portion of the task (i.e. without the completion marker, dates, and priority)

is_completed
    Whether or not the task is completed

linenr
    Line number of this task within its todo.txt file (0-based; the first task of a file will have self.linenr == 0). Do not count on this always being set!

parse(line)
    (Re)parse the task

    line is the raw string representation of a task, i.e. one line of a todo.txt file.

priority
    Priority of the task, or None if no priority given

property projects
    A list of all +project attributes

remove_attribute(key, value=None)
    Remove attribute key from the task

    If you provide a value only the attribute with that key and value is removed. If no value is provided all attributes with that key are removed.

Parameters

•   key – The name of the attribute to remove.

•   value – The value of the attribute to remove.

Returns
    True on success, otherwise False

remove_context(context)
    Remove the first @context attribute from the description.

Parameters
    context – the name of the context attribute to remove, without the leading @.

Returns
    True on success, otherwise False.

remove_project(project)
    Remove the first +project attribute from the description.

Parameters
    project – the name of the project attribute to remove, without the leading +

Returns
    True on success, otherwise False.

remove_tag(text, regex)
    Remove the first attribute text from the description

Parameters
    regex – The regular expression to use for matching the right type of attribute.

replace_attribute(key, value, newvalue)
    Replace the value of key:value in place with key:newvalue

replace_context(context, newcontext)
    Replace the first occurrence of @context with @newcontext

replace_project(project, newproject)
    Replace the first occurrence of @project with @newproject

replace_tag(value, newvalue, regex)
    Replace the first value attribute with newvalue

Parameters
    regex – the regular expression to use for matching the right type of attribute

serialize()
    Serialize this task.

    Calls the configured serializer, which by default will return a todo.txt-compatible string representation of this task.

serializer
    The serializer function to use.

todotxt
    The TodoTxt instance that this task belongs to. Do not count on this always being set!

  TodoTxtParser

class pytodotxt.TodoTxtParser(encoding='utf-8', task_type=None)
    A parser for todo.txt-like formatted strings or files

__init__(encoding='utf-8', task_type=None)
    Create a new todo.txt parser

Parameters

•   encoding – The encoding to assume for the parsing process

•   task_type – A subclass of Task to use when parsing the tasks.

linesep
    The line separator. After running any of the parse functions, this property will be set to the line separator that was detected in the parsed object.

parse(target)
    Parse the given object

    The behaviour of the parser depends a bit on what you pass in. It might be a bit surprising, but if you pass in a str, parse will attempt to find tasks in that string.

    If you want to parse a file and pass the filename, wrap it in pathlib.Path first or use parse_file directly.

    When parsing is completed, you can query linesep to see what the line separator was.

Parameters
    target (pathlib.Path, str, bytes, or any io.IOBase.) – the object to parse

Returns
    a list of tasks found in target

parse_file(path)
    Parse the content of the file at path

Parameters
    path – The file to parse

Returns
    a list of tasks found in file.

parse_str(text)
    Parse tasks from the given text

Parameters
    text – The string containing todo.txt-like tasks

Returns
    a list of tasks found in text

parse_stream(stream)
    Parse an io stream

Parameters
    stream – an io stream to read and parse tasks from

Returns
    a list of tasks found in stream

  Serialization

task.serialize_pedantic()
    Serialize the Task task in a very strict manner

    This serialisation mechanism interpretes the todo.txt standard in a restricted manner, not allowing priority markers in completed tasks nor “completed on” dates if there is no “created on” date.

    The output of this format can still be parsed by pytodotxt.

task.serialize_relaxed()
    Serialize the Task task in a relaxed manner

    This serialisation mechanism interpretes the todo.txt standard in a relaxed manner, allowing priority markers (“(A)”) in completed tasks and “completed on” dates even if there is no “created on” date.

    The output of this format can still be parsed by pytodotxt.

    pytodotxt is a small library to access todo.txt-like task lists in a convenient manner from Python.

    The quickest possible way to use pytodotxt is to install it through pypi:

        pip install pytodotxt

    And then access a todo.txt through Python like this, for example to print all tasks that are not completed yet:

        import pytodotxt
        todotxt = pytodotxt.TodoTxt('todo.txt')
        todotxt.parse()
        for task in todotxt.tasks:
            if not task.is_completed:
                print(task.description)

    There are more examples available, but if you prefer, there’s also the full API reference here.

AUTHOR
Robert Labudda

COPYRIGHT
2025, Robert Labudda