This repository was archived by the owner on Nov 19, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmarkdown_strings.py
208 lines (150 loc) · 5.77 KB
/
markdown_strings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""markdown_strings
Modified from https://github.com/awesmubarak/markdown_strings
Markdown is a markup language with plain text formatting syntax. This package
allows the creation of markdown-compliant strings. For information about
markdown see:
- https://commonmark.org/
- https://daringfireball.net/projects/markdown/
"""
# Helper functions
def esc_format(text):
"""Return text with formatting escaped."""
return str(text).replace("_", r"\_").replace("*", r"\*")
def header(header_text, header_level, style="atx"):
"""Return a header of specified level."""
# check types
if not isinstance(header_level, int):
raise TypeError("header_level must be int")
if not isinstance(header_text, str):
raise TypeError("header_text must be str")
# specifics for each style
if style == "atx":
if not 1 <= header_level <= 6:
raise ValueError(f"Invalid level {header_level} for atx")
return f"{'#' * header_level} {esc_format(header_text)}"
elif style == "setext":
if not 0 < header_level < 3:
raise ValueError(f"Invalid level {header_level} for setext")
header_character = "=" if header_level == 1 else "-"
header_string = (header_character * 3) + header_character * (
len(header_text) - 3
)
return f"{esc_format(header_text)}\n{header_string}"
else:
raise ValueError(f"Invalid style {style} (choose 'atx' or 'setext')")
def italics(text):
"""Return italics formatted text."""
return f"_{esc_format(text)}_"
def bold(text):
"""Return bold formatted text."""
return f"**{esc_format(text)}**"
def inline_code(text):
"""Return formatted inline code."""
return f"`{str(text)}`"
def code_block(text, language=""):
"""Return a code block."""
if language:
return f"```{language}\n{text}\n```"
return "\n".join(f" {item}" for item in text.split("\n"))
# Links
def link(text, link_url):
"""Return an inline link."""
return f"[{esc_format(text)}]({link_url})"
def image(alt_text, link_url, title=""):
"""Return an inline image."""
image_string = f""
if title:
image_string += f' "{esc_format(title)}"'
return image_string
# Lists
def unordered_list(text_list):
"""Return an unordered list from an list."""
return "\n".join(f"- {esc_format(item)}" for item in text_list)
def ordered_list(text_list):
"""Return an ordered list from an list."""
ordered_list = [
f"{f'{esc_format(number + 1)}.'.ljust(3)} {esc_format(item)}"
for number, item in enumerate(text_list)
]
return "\n".join(ordered_list)
# Miscellaneous
def blockquote(text):
"""Return a blockquote."""
return "\n".join(f"> {esc_format(item)}" for item in text.split("\n"))
def horizontal_rule(length=79, style="_"):
"""Return a horizontal rule."""
if style not in ("_", "*"):
raise ValueError("Invalid style (choose '_' or '*')")
if length < 3:
raise ValueError("Length must be >= 3")
return style * length
# Non-standard markdown
def strikethrough(text):
"""Return text with strike-through formatting."""
return f"~{esc_format(text)}~"
def task_list(task_list):
"""Return a task list."""
tasks = [
f"- [{'X' if completed else ' '}] {esc_format(item)}"
for item, completed in task_list
]
return "\n".join(tasks)
# Tables
def table_row(text_list, pad=-1):
"""Return a single table row."""
if pad == -1:
pad = [0] * len(text_list)
row = "|"
for column_number in range(len(text_list)):
padding = pad[column_number] + 1
row += (" " + str(text_list[column_number])).ljust(padding) + " |"
return row
def table_delimiter_row(number_of_columns, column_lengths=-1):
"""Return a delimiter row for use in a table."""
if column_lengths == -1:
column_lengths = [0] * number_of_columns
# error checking
if number_of_columns != len(column_lengths):
raise ValueError(
"number_of_columns must be the number of columns in column_lengths"
)
# creating the list with the right number of dashes
delimiter_row = [
"---" + "-" * (column_lengths[column_number] - 3)
for column_number in range(number_of_columns)
]
# use table row for actually creating the table row
return table_row(delimiter_row)
def table(table_list):
"""Return a formatted table, generated from lists representing columns."""
number_of_columns = len(table_list)
number_of_rows_in_column = [len(column) for column in table_list]
string_list = [[str(cell) for cell in column] for column in table_list]
column_lengths = [len(max(column, key=len)) for column in string_list]
table = []
# title row
row_list = [column[0] for column in string_list]
table.append(table_row(row_list, pad=column_lengths))
# delimiter row
table.append(
table_delimiter_row(len(column_lengths), column_lengths=column_lengths)
)
# body rows
for row in range(1, max(number_of_rows_in_column)):
row_list = []
for column_number in range(number_of_columns):
if number_of_rows_in_column[column_number] > row:
row_list.append(string_list[column_number][row])
else:
row_list.append("")
table.append(table_row(row_list, pad=column_lengths))
return "\n".join(table)
def table_from_rows(table_list):
"""Return a formatted table, using each list as the list."""
# transpose the list
number_of_rows = len(table_list)
transposed = []
for column_number in range(number_of_rows):
column_list = [row[column_number] for row in table_list]
transposed.append(column_list)
return table(transposed)