Skip to content

Instantly share code, notes, and snippets.

@jzohrab
Last active April 4, 2021 20:25
Show Gist options
  • Save jzohrab/7d655a8e506da09955fe695da3fe54f6 to your computer and use it in GitHub Desktop.
Save jzohrab/7d655a8e506da09955fe695da3fe54f6 to your computer and use it in GitHub Desktop.
Python list TODOs
# Helper method to dump project TODOs to stdout.
#
# Script can be stored anywhere, but should be run from the project
# root dir:
# python tools/list_todos.py
#
# Sample output:
#
# Current TODOs by category:
#
# base (code fixes that will require some rearchitecting)
#
# ./path/to/file.py (medium): rest of comment
#
# fix (code fixes)
#
# ./other/file.py (high): add config validation
# ...
import os, fnmatch, re
# Types of todos:
# docs = documentation
# base = fundamental project changes/investigations
# nice = nice-to-have
TODO_TYPES = {
'base': 'code fixes that will require some rearchitecting',
'fix': 'code fixes',
'docs': 'documentation',
'nice': 'nice-to-haves'
}
PRIORITIES = { 'high': 'high',
'med': 'medium',
'low': 'low' }
# Config
ignore_dirs = ['./venv', './.git', './docs/_build', './tools']
ignore_file_exts = ['.pyc']
ignore_files = ['./docs/conf.py', './README.md']
def find_files(directory, pattern, ignore_dirs, ignore_file_exts):
"Generator: find files, prune search tree during search."
for root, subdirs, files in os.walk(directory):
prune_subdirs = []
for d in subdirs:
if os.path.join(root, d) in ignore_dirs:
prune_subdirs.append(d)
for d in prune_subdirs:
subdirs.remove(d)
for basename in files:
filename, ext = os.path.splitext(basename)
if ext not in ignore_file_exts and fnmatch.fnmatch(basename, pattern):
filename = os.path.join(root, basename)
yield filename
def make_todo(filename, line):
"""Breaks a line up into a hash.
expected TODO format: 'TODO type/priority: rest of line'"""
header, rest = line.split(':')
p = 'medium' # default
for s in PRIORITIES:
if re.search(s, header, re.IGNORECASE):
p = PRIORITIES[s]
break
todo_type = ''
for s in TODO_TYPES:
if re.search(s, header, re.IGNORECASE):
todo_type = s
break
hsh = {
'filename': filename,
'line': line.lstrip(' '),
'priority': p,
'type': todo_type,
'rest': rest
}
return hsh
def collect_todos(filename):
with open(filename, 'r') as f:
data = f.read()
return [make_todo(filename, lin) for lin in data.split('\n') if re.search('todo', lin, re.IGNORECASE)]
def print_todos(all_todos):
"""Prints todos collected by make_todo."""
def print_section(todo_type, todos):
if len(todos) == 0:
return
desc = TODO_TYPES.get(todo_type, "uncategorized")
print '\n{0} ({1})\n'.format(todo_type, desc)
for t in todos:
print ' {0} ({1}): {2}'.format(t['filename'], t['priority'], t['rest'])
for tt in ('base', 'fix', 'docs', 'nice'):
todos = [t for t in all_todos if t['type'] == tt]
print_section(tt, todos)
all_todos = [t for t in all_todos if t not in todos]
print_section('remaining', all_todos)
# Output.
print "\nCurrent TODOs by category:"
todos = []
for filename in find_files('.', '*.*', ignore_dirs, ignore_file_exts):
if filename not in ignore_files:
todos.extend(collect_todos(filename))
print_todos(todos)
print
@avatar-lavventura
Copy link

I am having following error:

Current TODOs by category:
Traceback (most recent call last):
  File "list_todos.py", line 113, in <module>
    todos.extend(collect_todos(filename))
  File "list_todos.py", line 87, in collect_todos
    return [make_todo(filename, lin) for lin in data.split('\n') if re.search('todo', lin, re.IGNORECASE)]
  File "list_todos.py", line 62, in make_todo
    header, rest = line.split(':')
ValueError: need more than 1 value to unpack

@jzohrab
Copy link
Author

jzohrab commented Jan 22, 2021

Hi @avatar-lavventura -- not sure offhand, but if you have a file that has "todo" but no ":", it may cause this error. Ref this stackoverflow. Print the line and see what it has, and maybe add a check to ensure the line contains ":". Cheers!

@avatar-lavventura
Copy link

avatar-lavventura commented Jan 23, 2021

Should I run the code using python2.7 or python3? // All my Python files have # TODO: pattern , it forces this error to be generated

@jzohrab
Copy link
Author

jzohrab commented Apr 3, 2021

Per the comment: expected TODO format: 'TODO type/priority: rest of line'

@avatar-lavventura
Copy link

Can't is list them without any type/priority option?

example line:

# print("hello world")  # TODO: uncomment

@jzohrab
Copy link
Author

jzohrab commented Apr 4, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment