flowmark 0.4.2__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {flowmark-0.4.2 → flowmark-0.4.4}/PKG-INFO +10 -4
  2. {flowmark-0.4.2 → flowmark-0.4.4}/README.md +9 -3
  3. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/__init__.py +3 -0
  4. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/cli.py +22 -41
  5. flowmark-0.4.4/src/flowmark/reformat_api.py +91 -0
  6. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/text_wrapping.py +1 -1
  7. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_wrapping.py +2 -2
  8. {flowmark-0.4.2 → flowmark-0.4.4}/tests/testdocs/testdoc.orig.md +2 -0
  9. {flowmark-0.4.2 → flowmark-0.4.4}/tests/testdocs/testdoc.out.cleaned.md +5 -0
  10. {flowmark-0.4.2 → flowmark-0.4.4}/tests/testdocs/testdoc.out.plain.md +5 -0
  11. {flowmark-0.4.2 → flowmark-0.4.4}/tests/testdocs/testdoc.out.semantic.md +5 -0
  12. {flowmark-0.4.2 → flowmark-0.4.4}/.copier-answers.yml +0 -0
  13. {flowmark-0.4.2 → flowmark-0.4.4}/.github/workflows/ci.yml +0 -0
  14. {flowmark-0.4.2 → flowmark-0.4.4}/.github/workflows/publish.yml +0 -0
  15. {flowmark-0.4.2 → flowmark-0.4.4}/.gitignore +0 -0
  16. {flowmark-0.4.2 → flowmark-0.4.4}/LICENSE +0 -0
  17. {flowmark-0.4.2 → flowmark-0.4.4}/Makefile +0 -0
  18. {flowmark-0.4.2 → flowmark-0.4.4}/development.md +0 -0
  19. {flowmark-0.4.2 → flowmark-0.4.4}/devtools/lint.py +0 -0
  20. {flowmark-0.4.2 → flowmark-0.4.4}/installation.md +0 -0
  21. {flowmark-0.4.2 → flowmark-0.4.4}/poetry.lock +0 -0
  22. {flowmark-0.4.2 → flowmark-0.4.4}/publishing.md +0 -0
  23. {flowmark-0.4.2 → flowmark-0.4.4}/pyproject.toml +0 -0
  24. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/cleanups.py +0 -0
  25. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/custom_marko.py +0 -0
  26. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/frontmatter.py +0 -0
  27. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/line_wrappers.py +0 -0
  28. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/markdown_filling.py +0 -0
  29. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/py.typed +0 -0
  30. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/sentence_split_regex.py +0 -0
  31. {flowmark-0.4.2 → flowmark-0.4.4}/src/flowmark/text_filling.py +0 -0
  32. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_cleanups.py +0 -0
  33. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_filling.py +0 -0
  34. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_frontmatter.py +0 -0
  35. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_ref_docs.py +0 -0
  36. {flowmark-0.4.2 → flowmark-0.4.4}/tests/test_sentences.py +0 -0
  37. {flowmark-0.4.2 → flowmark-0.4.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowmark
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Better line wrapping and formatting for plaintext and Markdown
5
5
  Project-URL: Repository, https://github.com/jlevy/flowmark
6
6
  Author-email: Joshua Levy <joshua@cal.berkeley.edu>
@@ -153,7 +153,9 @@ experience.
153
153
  Flowmark can be used as a library or as a CLI.
154
154
 
155
155
  ```
156
- usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-i] [--nobackup] [--auto] [--version] [file]
156
+ usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-c] [-i] [--nobackup] [--auto]
157
+ [--version]
158
+ [file]
157
159
 
158
160
  Flowmark: Better line wrapping and formatting for plaintext and Markdown
159
161
 
@@ -165,10 +167,14 @@ options:
165
167
  -o, --output OUTPUT Output file (use '-' for stdout)
166
168
  -w, --width WIDTH Line width to wrap to
167
169
  -p, --plaintext Process as plaintext (no Markdown parsing)
168
- -s, --semantic Enable semantic (sentence-based) line breaks (only applies to Markdown mode)
170
+ -s, --semantic Enable semantic (sentence-based) line breaks (only applies to
171
+ Markdown mode)
172
+ -c, --cleanups Enable (safe) cleanups for common issues like accidentally
173
+ boldfaced section headers (only applies to Markdown mode)
169
174
  -i, --inplace Edit the file in place (ignores --output)
170
175
  --nobackup Do not make a backup of the original file when using --inplace
171
- --auto Same as `--inplace --nobackup --semantic`, as a convenience for auto-formatting files
176
+ --auto Same as `--inplace --nobackup --semantic --cleanups`, as a
177
+ convenience for fully auto-formatting files
172
178
  --version Show version information and exit
173
179
 
174
180
  Flowmark provides enhanced text wrapping capabilities with special handling for
@@ -129,7 +129,9 @@ experience.
129
129
  Flowmark can be used as a library or as a CLI.
130
130
 
131
131
  ```
132
- usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-i] [--nobackup] [--auto] [--version] [file]
132
+ usage: flowmark [-h] [-o OUTPUT] [-w WIDTH] [-p] [-s] [-c] [-i] [--nobackup] [--auto]
133
+ [--version]
134
+ [file]
133
135
 
134
136
  Flowmark: Better line wrapping and formatting for plaintext and Markdown
135
137
 
@@ -141,10 +143,14 @@ options:
141
143
  -o, --output OUTPUT Output file (use '-' for stdout)
142
144
  -w, --width WIDTH Line width to wrap to
143
145
  -p, --plaintext Process as plaintext (no Markdown parsing)
144
- -s, --semantic Enable semantic (sentence-based) line breaks (only applies to Markdown mode)
146
+ -s, --semantic Enable semantic (sentence-based) line breaks (only applies to
147
+ Markdown mode)
148
+ -c, --cleanups Enable (safe) cleanups for common issues like accidentally
149
+ boldfaced section headers (only applies to Markdown mode)
145
150
  -i, --inplace Edit the file in place (ignores --output)
146
151
  --nobackup Do not make a backup of the original file when using --inplace
147
- --auto Same as `--inplace --nobackup --semantic`, as a convenience for auto-formatting files
152
+ --auto Same as `--inplace --nobackup --semantic --cleanups`, as a
153
+ convenience for fully auto-formatting files
148
154
  --version Show version information and exit
149
155
 
150
156
  Flowmark provides enhanced text wrapping capabilities with special handling for
@@ -6,6 +6,8 @@ __all__ = (
6
6
  "html_md_word_splitter",
7
7
  "line_wrap_by_sentence",
8
8
  "line_wrap_to_width",
9
+ "reformat_file",
10
+ "reformat_text",
9
11
  "split_sentences_regex",
10
12
  "wrap_paragraph",
11
13
  "wrap_paragraph_lines",
@@ -14,6 +16,7 @@ __all__ = (
14
16
 
15
17
  from .line_wrappers import line_wrap_by_sentence, line_wrap_to_width
16
18
  from .markdown_filling import fill_markdown
19
+ from .reformat_api import reformat_file, reformat_text
17
20
  from .sentence_split_regex import first_sentence, first_sentences, split_sentences_regex
18
21
  from .text_filling import Wrap, fill_text
19
22
  from .text_wrapping import html_md_word_splitter, wrap_paragraph, wrap_paragraph_lines
@@ -41,9 +41,7 @@ import importlib.metadata
41
41
  import sys
42
42
  from dataclasses import dataclass
43
43
 
44
- from strif import atomic_output_file
45
-
46
- from flowmark import Wrap, fill_markdown, fill_text, html_md_word_splitter
44
+ from flowmark.reformat_api import reformat_file
47
45
 
48
46
 
49
47
  @dataclass
@@ -104,7 +102,8 @@ def _parse_args(args: list[str] | None = None) -> Options:
104
102
  "--cleanups",
105
103
  action="store_true",
106
104
  default=False,
107
- help="Enable (safe) cleanups for common issues (only applies to Markdown mode)",
105
+ help="Enable (safe) cleanups for common issues like accidentally boldfaced section "
106
+ "headers (only applies to Markdown mode)",
108
107
  )
109
108
  parser.add_argument(
110
109
  "-i", "--inplace", action="store_true", help="Edit the file in place (ignores --output)"
@@ -117,7 +116,8 @@ def _parse_args(args: list[str] | None = None) -> Options:
117
116
  parser.add_argument(
118
117
  "--auto",
119
118
  action="store_true",
120
- help="Same as `--inplace --nobackup --semantic`, as a convenience for auto-formatting files",
119
+ help="Same as `--inplace --nobackup --semantic --cleanups`, as a convenience for "
120
+ "fully auto-formatting files",
121
121
  )
122
122
  parser.add_argument(
123
123
  "--version",
@@ -130,6 +130,7 @@ def _parse_args(args: list[str] | None = None) -> Options:
130
130
  opts.inplace = True
131
131
  opts.nobackup = True
132
132
  opts.semantic = True
133
+ opts.cleanups = True
133
134
 
134
135
  return Options(
135
136
  file=opts.file,
@@ -165,46 +166,26 @@ def main(args: list[str] | None = None) -> int:
165
166
  print("unknown (package not installed)")
166
167
  return 0
167
168
 
168
- # Handle input.
169
- if options.file == "-":
170
- text = sys.stdin.read()
171
- else:
172
- with open(options.file) as f:
173
- text = f.read()
174
-
175
- if options.plaintext:
176
- # Plaintext mode
177
- result = fill_text(
178
- text,
179
- text_wrap=Wrap.WRAP,
180
- width=options.width,
181
- word_splitter=html_md_word_splitter, # Still use HTML/MD aware splitter by default
182
- )
183
- else:
184
- # Markdown mode
185
- result = fill_markdown(
186
- text,
169
+ try:
170
+ reformat_file(
171
+ path=options.file,
172
+ output=options.output,
187
173
  width=options.width,
174
+ inplace=options.inplace,
175
+ nobackup=options.nobackup,
176
+ plaintext=options.plaintext,
188
177
  semantic=options.semantic,
189
178
  cleanups=options.cleanups,
190
- dedent_input=True,
179
+ make_parents=True,
191
180
  )
192
-
193
- # Handle output
194
- if options.inplace:
195
- if options.file == "-":
196
- print("Error: Cannot use --inplace with stdin", file=sys.stderr)
197
- return 1
198
- backup_suffix = ".orig" if not options.nobackup else ""
199
- with atomic_output_file(options.file, backup_suffix=backup_suffix) as tmp_path:
200
- with open(tmp_path, "w") as f:
201
- f.write(result)
202
- else:
203
- if options.output == "-":
204
- sys.stdout.write(result)
205
- else:
206
- with open(options.output, "w") as f:
207
- f.write(result)
181
+ except ValueError as e:
182
+ # Handle errors reported by reformat_file, like using --inplace with stdin.
183
+ print(f"Error: {e}", file=sys.stderr)
184
+ return 1
185
+ except Exception as e:
186
+ # Catch other potential file or processing errors.
187
+ print(f"Error: {e}", file=sys.stderr)
188
+ return 2
208
189
 
209
190
  return 0
210
191
 
@@ -0,0 +1,91 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from strif import atomic_output_file
5
+
6
+ from flowmark.markdown_filling import fill_markdown
7
+ from flowmark.text_filling import Wrap, fill_text
8
+ from flowmark.text_wrapping import html_md_word_splitter
9
+
10
+
11
+ def reformat_text(
12
+ text: str,
13
+ width: int = 88,
14
+ plaintext: bool = False,
15
+ semantic: bool = True,
16
+ cleanups: bool = True,
17
+ ) -> str:
18
+ """
19
+ Reformat text or markdown and wrap lines. Simply a convenient wrapper
20
+ around `fill_text()` and `fill_markdown()` with reasonable defaults.
21
+ """
22
+ if plaintext:
23
+ # Plaintext mode
24
+ result = fill_text(
25
+ text,
26
+ text_wrap=Wrap.WRAP,
27
+ width=width,
28
+ word_splitter=html_md_word_splitter, # Still use HTML/MD aware splitter by default
29
+ )
30
+ else:
31
+ # Markdown mode
32
+ result = fill_markdown(text, width=width, semantic=semantic, cleanups=cleanups)
33
+
34
+ return result
35
+
36
+
37
+ def reformat_file(
38
+ path: Path | str,
39
+ output: Path | str | None,
40
+ width: int = 88,
41
+ inplace: bool = False,
42
+ nobackup: bool = False,
43
+ plaintext: bool = False,
44
+ semantic: bool = False,
45
+ cleanups: bool = True,
46
+ make_parents: bool = True,
47
+ ) -> None:
48
+ """
49
+ Reformat text or markdown and wrap lines on the given files.
50
+ Accepts "-" for stdin. Can omit output if `inplace` is True.
51
+ Throws usual file-related exceptions if the input or output is invalid.
52
+
53
+ Args:
54
+ path: Path to the input file, or "-" for stdin.
55
+ output: Path to the output file, or "-" for stdout.
56
+ width: The width to wrap lines to.
57
+ inplace: Whether to write the file back to the same path (atomically only on success).
58
+ nobackup: Whether to not make a backup of the original file
59
+ plaintext: Use plaintext instead of Markdown mode wrapping.
60
+ semantic: Use semantic line breaks (based on sentences) heuristic.
61
+ cleanups: Enable (safe) cleanups for common issues like accidentally boldfaced section
62
+ headers (only applies to Markdown mode).
63
+ make_parents: Whether to make parent directories if they don't exist.
64
+ """
65
+ read_stdin = path == "-"
66
+ write_stdout = output == "-"
67
+
68
+ if inplace and read_stdin:
69
+ raise ValueError("Cannot use `inplace` with stdin")
70
+ if inplace and output and output != path:
71
+ raise ValueError("Cannot use `inplace` with an output path")
72
+
73
+ if read_stdin:
74
+ text = sys.stdin.read()
75
+ else:
76
+ text = Path(path).read_text()
77
+
78
+ result = reformat_text(text, width, plaintext, semantic, cleanups)
79
+
80
+ if inplace:
81
+ backup_suffix = ".orig" if not nobackup else ""
82
+ with atomic_output_file(
83
+ path, backup_suffix=backup_suffix, make_parents=make_parents
84
+ ) as tmp_path:
85
+ tmp_path.write_text(result)
86
+ else:
87
+ if not output or write_stdout:
88
+ sys.stdout.write(result)
89
+ else:
90
+ with atomic_output_file(output, make_parents=make_parents) as tmp_path:
91
+ tmp_path.write_text(result)
@@ -74,7 +74,7 @@ Split words, but not within HTML tags or Markdown links.
74
74
  # Pattern to identify words that need escaping if they start a wrapped markdown line.
75
75
  # Matches list markers (*, +, -) bare or before a space (but not before a letter for
76
76
  # example), blockquotes (> ), headings (#, ##, etc.).
77
- _md_specials_pat = re.compile(r"^[-*+](?: |$)|> |#+$")
77
+ _md_specials_pat = re.compile(r"^([-*+>]|#+)$")
78
78
 
79
79
  # Separate pattern to specifically find the numbered list cases for targeted escaping
80
80
  _md_numeral_pat = re.compile(r"^[0-9]+[.)]$")
@@ -15,7 +15,7 @@ def test_markdown_escape_word_function() -> None:
15
15
  assert markdown_escape_word("-") == "\\-"
16
16
  assert markdown_escape_word("+") == "\\+"
17
17
  assert markdown_escape_word("*") == "\\*"
18
- assert markdown_escape_word(">") == ">"
18
+ assert markdown_escape_word(">") == "\\>"
19
19
  assert markdown_escape_word("#") == "\\#"
20
20
  assert markdown_escape_word("##") == "\\##"
21
21
  assert markdown_escape_word("1.") == "1\\."
@@ -52,7 +52,7 @@ def test_wrap_paragraph_lines_markdown_escaping():
52
52
  "word",
53
53
  "\\+",
54
54
  "word",
55
- ">",
55
+ "\\>",
56
56
  "word",
57
57
  "\\#",
58
58
  "word",
@@ -546,6 +546,8 @@ And people often put them before punctuation like this[^a], or this[^a]. Or even
546
546
 
547
547
  ❗️️️ Having multiple automatic conversion thresholds can give the investor with a higher threshold leverage to block an IPO.[^210]
548
548
 
549
+ 1. **Initial Scan with -X importtime:** Run the application with python -X importtime... > import.log. Visualize the output using tuna import.log.<sup>42</sup> Look for modules with large *cumulative* times at the top level or deep in the call stack. These are the primary candidates for further investigation.<sup>1</sup>
550
+
549
551
  [^2]: Aulet, Bill. *Disciplined Entrepreneurship*: 24 Steps to a Successful Startup (Kindle Location 1220). Wiley, 2013. Kindle Edition.
550
552
 
551
553
  [^191]: http://paulgraham.com/fr.html
@@ -867,6 +867,11 @@ not complaining)[^urbanthowt.wy49lp].
867
867
  ❗️️️ Having multiple automatic conversion thresholds can give the investor with a higher
868
868
  threshold leverage to block an IPO.[^210]
869
869
 
870
+ 1. **Initial Scan with -X importtime:** Run the application with python -X importtime...
871
+ \> import.log. Visualize the output using tuna import.log.<sup>42</sup> Look for
872
+ modules with large *cumulative* times at the top level or deep in the call stack.
873
+ These are the primary candidates for further investigation.<sup>1</sup>
874
+
870
875
  [^2]: Aulet, Bill. *Disciplined Entrepreneurship*: 24 Steps to a Successful Startup (Kindle
871
876
  Location 1220). Wiley, 2013. Kindle Edition.
872
877
 
@@ -829,6 +829,11 @@ not complaining)[^urbanthowt.wy49lp].
829
829
  ❗️️️ Having multiple automatic conversion thresholds can give the investor with a higher
830
830
  threshold leverage to block an IPO.[^210]
831
831
 
832
+ 1. **Initial Scan with -X importtime:** Run the application with python -X importtime...
833
+ \> import.log. Visualize the output using tuna import.log.<sup>42</sup> Look for
834
+ modules with large *cumulative* times at the top level or deep in the call stack.
835
+ These are the primary candidates for further investigation.<sup>1</sup>
836
+
832
837
  [^2]: Aulet, Bill. *Disciplined Entrepreneurship*: 24 Steps to a Successful Startup
833
838
  (Kindle Location 1220). Wiley, 2013. Kindle Edition.
834
839
 
@@ -867,6 +867,11 @@ not complaining)[^urbanthowt.wy49lp].
867
867
  ❗️️️ Having multiple automatic conversion thresholds can give the investor with a higher
868
868
  threshold leverage to block an IPO.[^210]
869
869
 
870
+ 1. **Initial Scan with -X importtime:** Run the application with python -X importtime...
871
+ \> import.log. Visualize the output using tuna import.log.<sup>42</sup> Look for
872
+ modules with large *cumulative* times at the top level or deep in the call stack.
873
+ These are the primary candidates for further investigation.<sup>1</sup>
874
+
870
875
  [^2]: Aulet, Bill. *Disciplined Entrepreneurship*: 24 Steps to a Successful Startup (Kindle
871
876
  Location 1220). Wiley, 2013. Kindle Edition.
872
877
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes