Log item types#
Log item types are classes that define items that you would like to capture in a log. These classes store:
a list of regular expression patterns used to capture your log items,
the placeholder used in your templates to insert the log items,
the text displayed in the template if no log items are found,
and a parser method to process a captured log item into the output text that is inserted into your template.
To capture a custom log item you can define a new subclass of the LogItem base class:
LogItem base class#
LogItem interface, defining the attributes required by any LogItem subclass.
- assumptions.LogItem.matched_items#
list of log item matches that have been found.
- assumptions.LogItem.parsed_items#
list of parsed log items, which can be inserted into log outputs.
- assumptions.LogItem.find_items(text, path)#
search for and store log items from text.
- assumptions.LogItem.parse_items()#
parse matched log items into strings.
Example log item types#
The default log items captured by assumptions are assumptions, caveats and todos. Their class definitions provide examples that implement the abstract properties and methods described above. You can use these as a good starting point when defining your own log item type:
class Assumption(LogItem):
"""
Matches and parses assumptions from hash code comments.
"""
search_patterns = [
(
# Genereric hash comments
# Get indentation level and Assumption title
r"^([ \t]*)# ?Assumption: ?(.+)\n"
# Short or long from RAG ratings
r"^\1# ?Q(?:uality)?: ?(.+)\n"
r"^\1# ?I(?:mpact)?: ?(.+)\n"
# Lazily match everything following, till there's a line that doesn't
# start with the same indent and comment
r"(\1# ?(?:.|\n)*?)^(?!\1#)"
),
(
# R roxygen format
# As above, using #' notation
r"^([ \t]*)#' @section Assumption: ?(.+)\n"
r"^\1#' Q(?:uality)?: ?(.+)\n"
r"^\1#' I(?:mpact)?: ?(.+)\n"
r"(\1#' (?:.|\n)*?)^(?!\1#')"
),
]
template_marker = "{ assumptions }"
empty_message = "Currently no assumptions in this analysis.\n"
def parse(self, idx, file_path, item):
detailed_description = re.sub(
# Remove indentation and comment hash from detailed description
f"\n?{item[0]}#'? ?",
"\n",
item[4],
)
detailed_description = re.sub(
# Reduce whitespace to single spaces and strip
"[ ]{2,}",
" ",
detailed_description.strip(),
)
assumptions_content = "\n".join(
[
f"### Assumption {idx + 1}: {item[1]}",
"",
# Relative path to file
f"* Location: `{file_path}`",
f"* **Quality**: {item[2]}",
f"* **Impact**: {item[3]}",
"",
f"{detailed_description}",
"",
],
)
return assumptions_content
class Caveat(LogItem):
"""
Matches and parses caveats from hash code comments.
"""
search_patterns = [
(
# Generic hash comments
# Get indentation level and Caveat title
r"^([ \t]*)# ?Caveat: ?(.+)\n"
# Lazily match everything following, till there's a line that doesn't start
# with the same indent and comment
# Long description is optional, as caveats might be one liners
r"(\1# ?(?:.|\n)*?)?^(?!\1#)"
),
(
# R roxygen format
r"^([ \t]*)#\' @section Caveat: ?(.+)\n"
r"(\1#\' (?:.|\n)*?)?^(?!\1#\')"
),
]
template_marker = "{ caveats }"
empty_message = "Currently no caveats in this analysis.\n"
def parse(self, idx, file_path, item):
detailed_description = re.sub(
# Remove indentation and comment hash from detailed description
f"\n?{item[0]}#'? ?",
"\n",
item[2],
)
detailed_description = re.sub(
# Reduce whitespace to single spaces and strip
"[ ]{2,}",
" ",
detailed_description.strip(),
)
caveat_content = "\n".join(
[
f"### Caveat {idx + 1}: {item[1]}",
"",
# Relative path to file
f"Location: `{file_path}`",
"",
f"{detailed_description}",
"",
],
)
return caveat_content
class Debt(LogItem):
"""
Matches and parses technical debt items from hash code comments.
"""
search_patterns = [
(
# Get indentation level and title
r"^([ \t]*)# ?Debt: (.+)\n?"
# Lazily match everything following, till there's a line that doesn't start
# with the same indent and comment
r"(\1# ?(?:.|\n)*?)^(?!\1#)"
),
(
# R roxygen format
r"^([ \t]*)#\' @section Debt: ?(.+)\n"
r"(\1#\' (?:.|\n)*?)?^(?!\1#\')"
),
]
template_marker = "{ debt }"
empty_message = "Looks like we're debt free!\n"
def parse(self, idx, file_path, item):
debt_item = re.sub(
# Remove indentation and comment hash from todo item
f"\n?{item[0]}#'? ?",
"\n",
item[2],
)
debt_item = re.sub(
# Reduce whitespace to single spaces and strip
"[ ]{2,}",
" ",
debt_item.strip(),
)
debt_content = "\n".join(
[
f"### Debt {idx + 1}: {item[1]}",
"",
# Relative path to file
f"Location: `{file_path}`",
"",
f"{debt_item}",
"",
],
)
return debt_content
class Todo(LogItem):
"""
Matches and parses todos from hash code comments.
"""
search_patterns = [
(
# Get indentation level
r"^([ \t]*)# ?TODO: (.+)\n?"
# Lazily match everything following, till there's a line that doesn't start
# with the same indent and comment
# Long description is optional, as todos are often one liners
r"(\1# ?(?:.|\n)*?)?^(?!\1#)"
),
]
template_marker = "{ todos }"
empty_message = "Great, there's nothing to do!\n"
def parse(self, idx, file_path, item):
todo_item = item[1] + re.sub(
# Remove indentation and comment hash from todo item
f"\n?{item[0]}#",
"",
item[2],
)
todo_item = re.sub(
# Reduce whitespace to single spaces and strip
"[ ]{2,}",
" ",
todo_item.strip(),
)
return f"- [ ] {todo_item}"