txt2ebook 0.1.142__tar.gz → 0.1.143__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 (111) hide show
  1. txt2ebook-0.1.143/.pre-commit-config.yaml +117 -0
  2. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/CHANGELOG.md +8 -0
  3. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/PKG-INFO +1 -1
  4. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/noxfile.py +10 -13
  5. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/pyproject.toml +4 -3
  6. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/__init__.py +2 -6
  7. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/__init__.py +0 -1
  8. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/base.py +3 -11
  9. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/epub.py +6 -18
  10. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/gmi.py +3 -13
  11. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/md.py +3 -13
  12. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/pdf.py +1 -3
  13. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/tex.py +2 -6
  14. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/txt.py +3 -12
  15. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/typ.py +1 -4
  16. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/models/book.py +2 -6
  17. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/parser.py +9 -13
  18. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/__init__.py +1 -2
  19. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/gmi.py +1 -3
  20. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/massage.py +2 -6
  21. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/md.py +1 -3
  22. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/parse.py +0 -3
  23. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/pdf.py +1 -3
  24. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/tex.py +1 -3
  25. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/typ.py +1 -3
  26. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/tokenizer.py +3 -10
  27. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/zh_utils.py +1 -4
  28. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_input_file_arg.py +1 -4
  29. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_output_file_arg.py +1 -3
  30. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_purge_flag.py +1 -3
  31. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_split_volume_and_chapter_flag.py +1 -3
  32. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_subcommand_massage.py +4 -1
  33. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/uv.lock +43 -25
  34. txt2ebook-0.1.142/.pre-commit-config.yaml +0 -110
  35. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/.coveragerc +0 -0
  36. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/.gitignore +0 -0
  37. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/.python-version +0 -0
  38. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/CONTRIBUTING.md +0 -0
  39. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/LICENSE.md +0 -0
  40. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/README.md +0 -0
  41. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/Makefile +0 -0
  42. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/make.bat +0 -0
  43. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/CHANGELOG.md +0 -0
  44. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/CONTRIBUTING.md +0 -0
  45. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/LICENSE.md +0 -0
  46. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/README.md +0 -0
  47. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/_static/logo.png +0 -0
  48. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/conf.py +0 -0
  49. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/index.rst +0 -0
  50. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/txt2ebook.formats.rst +0 -0
  51. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/txt2ebook.helpers.rst +0 -0
  52. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/txt2ebook.models.rst +0 -0
  53. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/txt2ebook.parsers.rst +0 -0
  54. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/docs/source/txt2ebook.rst +0 -0
  55. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/__main__.py +0 -0
  56. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/cli.py +0 -0
  57. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/exceptions.py +0 -0
  58. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/__init__.py +0 -0
  59. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
  60. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/clean.css +0 -0
  61. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/condense.css +0 -0
  62. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/noindent.css +0 -0
  63. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/helpers/__init__.py +0 -0
  64. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/languages/__init__.py +0 -0
  65. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/languages/en.py +0 -0
  66. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/languages/zh_cn.py +0 -0
  67. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/languages/zh_tw.py +0 -0
  68. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
  69. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -0
  70. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/txt2ebook.pot +0 -0
  71. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
  72. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -0
  73. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
  74. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -0
  75. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/models/__init__.py +0 -0
  76. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/models/chapter.py +0 -0
  77. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/models/volume.py +0 -0
  78. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/env.py +0 -0
  79. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/epub.py +0 -0
  80. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/__init__.py +0 -0
  81. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/conftest.py +0 -0
  82. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/empty_file.txt +0 -0
  83. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/missing_chapters.txt +0 -0
  84. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample.txt +0 -0
  85. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_all_headers.txt +0 -0
  86. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_long_headers.txt +0 -0
  87. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_remove_wrapping.txt +0 -0
  88. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_unsorted_headers.txt +0 -0
  89. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_with_issues.txt +0 -0
  90. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/fixtures/sample_with_metadata.txt +0 -0
  91. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_epub_writer.py +0 -0
  92. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_filename_format_flag.py +0 -0
  93. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_format_option.py +0 -0
  94. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_header_number_flag.py +0 -0
  95. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_language_option.py +0 -0
  96. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_overwrite_flag.py +0 -0
  97. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_parser.py +0 -0
  98. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_quiet_flag.py +0 -0
  99. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_raise_warnings.py +0 -0
  100. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_sort_volume_and_chapter_flag.py +0 -0
  101. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_subcommand_env.py +0 -0
  102. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_subcommand_epub.py +0 -0
  103. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_test_parsing_flag.py +0 -0
  104. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_tokenizer.py +0 -0
  105. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_translator_option.py +0 -0
  106. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_txt2ebook.py +0 -0
  107. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_verbose_flag.py +0 -0
  108. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_volume_page_flag.py +0 -0
  109. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_zh_utils_zh_halfwidth_to_fullwidth.py +0 -0
  110. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_zh_utils_zh_numeric.py +0 -0
  111. {txt2ebook-0.1.142 → txt2ebook-0.1.143}/tests/test_zh_utils_zh_words_to_numbers.py +0 -0
@@ -0,0 +1,117 @@
1
+ # See https://pre-commit.com for more information
2
+ # See https://pre-commit.com/hooks.html for more hooks
3
+ repos:
4
+ - repo: https://github.com/pre-commit/pre-commit-hooks
5
+ rev: v5.0.0
6
+ hooks:
7
+ - id: check-case-conflict
8
+ - id: check-merge-conflict
9
+ - id: check-toml
10
+ - id: check-yaml
11
+ - id: debug-statements
12
+ - id: detect-private-key
13
+ - id: end-of-file-fixer
14
+ exclude: "locales/.*$"
15
+ - id: mixed-line-ending
16
+ - id: trailing-whitespace
17
+
18
+ - repo: https://github.com/abravalheri/validate-pyproject
19
+ rev: v0.24.1
20
+ hooks:
21
+ - id: validate-pyproject
22
+ name: validate pyproject
23
+
24
+ - repo: https://github.com/codespell-project/codespell
25
+ rev: v2.4.1
26
+ hooks:
27
+ - id: codespell
28
+ args:
29
+ - --skip=uv.lock
30
+
31
+ - repo: https://github.com/pre-commit/mirrors-prettier
32
+ rev: v4.0.0-alpha.8
33
+ hooks:
34
+ - id: prettier
35
+ exclude: (poetry.lock)
36
+
37
+ - repo: https://github.com/pycqa/isort
38
+ rev: 6.0.1
39
+ hooks:
40
+ - id: isort
41
+ additional_dependencies:
42
+ - isort[pyproject]
43
+ args:
44
+ - --profile=black
45
+ - --line-length=79
46
+ - --py=312
47
+
48
+ - repo: https://github.com/astral-sh/ruff-pre-commit
49
+ rev: v0.11.12
50
+ hooks:
51
+ - id: ruff-check
52
+ args: [--fix]
53
+ - id: ruff-format
54
+
55
+ # - repo: https://github.com/psf/black-pre-commit-mirror
56
+ # rev: 25.1.0
57
+ # hooks:
58
+ # - id: black
59
+ # args:
60
+ # - --line-length=79
61
+ # - --target-version=py38
62
+ # - --target-version=py39
63
+ # - --target-version=py310
64
+ # - --target-version=py311
65
+ # - --target-version=py312
66
+
67
+ # - repo: https://github.com/asottile/blacken-docs
68
+ # rev: 1.19.1
69
+ # hooks:
70
+ # - id: blacken-docs
71
+ # additional_dependencies:
72
+ # - black==22.8.0
73
+
74
+ # - repo: https://github.com/PyCQA/autoflake
75
+ # rev: v2.3.1
76
+ # hooks:
77
+ # - id: autoflake
78
+ # args:
79
+ # - --in-place
80
+ # - --remove-unused-variables
81
+ # - --remove-all-unused-imports
82
+ # language: python
83
+ # files: \.py$
84
+
85
+ # - repo: https://github.com/PyCQA/flake8
86
+ # rev: 7.2.0
87
+ # hooks:
88
+ # - id: flake8
89
+ # exclude: docs/
90
+ # additional_dependencies:
91
+ # - flake8-docstrings
92
+ # - flake8-print
93
+ # - flake8-pytest-style
94
+ # - flake8-simplify
95
+ # args:
96
+ # - --show-source
97
+ # - --max-line-length=79
98
+ # - --docstring-convention=google
99
+ # - --per-file-ignores=src/*:D212,D208,T201 tests/*:ANN101,ANN201,D100,D103,D104,E501
100
+
101
+ # - repo: local
102
+ # hooks:
103
+ # - id: pylint
104
+ # name: pylint
105
+ # entry: pylint
106
+ # language: system
107
+ # types:
108
+ # - python
109
+ # exclude: docs/
110
+ # args:
111
+ # - --disable=C0114,R0801,R0902,R0903,R0912,R0914,R0915
112
+
113
+ - repo: https://github.com/pre-commit/mirrors-mypy
114
+ rev: v1.16.0
115
+ hooks:
116
+ - id: mypy
117
+ exclude: docs/
@@ -7,6 +7,14 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.1.143 (2025-06-08)
11
+
12
+ - Add `ruff` to `pre-commit` hook
13
+ - Bump deps
14
+ - Bump `pre-commit` deps to the right version
15
+ - No need to run `pre-commit` under active session
16
+ - Skip `uv.lock` from `codespell`
17
+
10
18
  ## v0.1.142 (2025-06-01)
11
19
 
12
20
  - Bump deps
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2ebook
3
- Version: 0.1.142
3
+ Version: 0.1.143
4
4
  Summary: CLI tool to convert txt file to ebook format
5
5
  Project-URL: Homepage, https://github.com/kianmeng/txt2ebook
6
6
  Project-URL: Repository, https://github.com/kianmeng/txt2ebook
@@ -39,7 +39,12 @@ def lint(session: nox.Session) -> None:
39
39
  nox -s lint -- pylint
40
40
  """
41
41
  _uv_install(session)
42
- session.run("pre-commit", "run", "--all-files", *session.posargs)
42
+ session.run(
43
+ "pre-commit",
44
+ "run",
45
+ "--all-files",
46
+ *session.posargs,
47
+ )
43
48
 
44
49
 
45
50
  @nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"])
@@ -68,9 +73,7 @@ def cov(session: nox.Session) -> None:
68
73
  def doc(session: nox.Session) -> None:
69
74
  """Build doc with sphinx."""
70
75
  _uv_install(session)
71
- session.run(
72
- "sphinx-build", "docs/source/", "docs/build/html", *session.posargs
73
- )
76
+ session.run("sphinx-build", "docs/source/", "docs/build/html", *session.posargs)
74
77
 
75
78
 
76
79
  @nox.session(python="3.13")
@@ -188,17 +191,11 @@ def release(session: nox.Session) -> None:
188
191
  """Bump release."""
189
192
  _uv_install(session)
190
193
 
191
- before_version = session.run(
192
- "uv", "version", "--short", silent=True
193
- ).strip()
194
+ before_version = session.run("uv", "version", "--short", silent=True).strip()
194
195
  session.run("uv", "version", "--bump", "patch")
195
- after_version = session.run(
196
- "uv", "version", "--short", silent=True
197
- ).strip()
196
+ after_version = session.run("uv", "version", "--short", silent=True).strip()
198
197
 
199
- _search_and_replace(
200
- "src/txt2ebook/__init__.py", before_version, after_version
201
- )
198
+ _search_and_replace("src/txt2ebook/__init__.py", before_version, after_version)
202
199
 
203
200
  date = datetime.date.today().strftime("%Y-%m-%d")
204
201
  before_header = "## [Unreleased]\n\n"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "txt2ebook"
3
- version = "0.1.142"
3
+ version = "0.1.143"
4
4
  description = "CLI tool to convert txt file to ebook format"
5
5
  authors = [{ name = "Kian-Meng Ang", email = "kianmeng@cpan.org" }]
6
6
  requires-python = "~=3.9"
@@ -57,7 +57,7 @@ tte = "txt2ebook.cli:main"
57
57
 
58
58
  [dependency-groups]
59
59
  dev = [
60
- "babel>=2.12.1,<3",
60
+ "babel>=2.17.0",
61
61
  "bandit~=1.7.1",
62
62
  "flake8-simplify>=0.21.0,<0.22",
63
63
  "mypy>=1.10.0,<2",
@@ -65,12 +65,13 @@ dev = [
65
65
  "nox-poetry>=1.0.3,<2",
66
66
  "nox>=2024.4.15,<2025",
67
67
  "pep8-naming>=0.13.3,<0.14",
68
- "pre-commit>=2.20,<2.21",
68
+ "pre-commit>=4.2.0",
69
69
  "pylint>=3.2.0,<4",
70
70
  "pytest-cov>=5.0.0,<6",
71
71
  "pytest-randomly>=3.15.0,<4",
72
72
  "pytest-xdist>=3.6.1,<4",
73
73
  "pytest>=8.2.0,<9",
74
+ "ruff>=0.11.12",
74
75
  "scripttest~=1.3",
75
76
  "sphinx-autodoc-typehints>=2.2.2,<3",
76
77
  "sphinx-copybutton>=0.5.2,<0.6",
@@ -38,9 +38,7 @@ def setup_logger(config: argparse.Namespace) -> None:
38
38
  return
39
39
 
40
40
  log_level = logging.DEBUG if config.debug else logging.INFO
41
- log_format = (
42
- "%(levelname)5s: %(message)s" if config.debug else "%(message)s"
43
- )
41
+ log_format = "%(levelname)5s: %(message)s" if config.debug else "%(message)s"
44
42
 
45
43
  logging.basicConfig(
46
44
  level=log_level,
@@ -50,9 +48,7 @@ def setup_logger(config: argparse.Namespace) -> None:
50
48
  )
51
49
 
52
50
 
53
- def log_or_raise_on_warning(
54
- message: str, raise_on_warning: bool = False
55
- ) -> None:
51
+ def log_or_raise_on_warning(message: str, raise_on_warning: bool = False) -> None:
56
52
  """Logs a warning message or raises an exception.
57
53
 
58
54
  Args:
@@ -15,7 +15,6 @@
15
15
 
16
16
  """Packpage of different e-book formats."""
17
17
 
18
-
19
18
  from txt2ebook.formats.epub import TEMPLATES as EPUB_TEMPLATES
20
19
  from txt2ebook.formats.epub import EpubWriter
21
20
  from txt2ebook.formats.gmi import GmiWriter
@@ -84,8 +84,7 @@ class BaseWriter(ABC):
84
84
  shutil.rmtree(cwd)
85
85
  else:
86
86
  answer = input(
87
- "Are you sure to purge output folder: "
88
- f"{cwd.absolute()}? [y/N] "
87
+ f"Are you sure to purge output folder: {cwd.absolute()}? [y/N] "
89
88
  )
90
89
  if answer.lower() == "y":
91
90
  logger.debug("Purge output folder: %s", cwd.absolute())
@@ -127,9 +126,7 @@ class BaseWriter(ABC):
127
126
  # do not create to output folder when we explicit set the output path
128
127
  # and file
129
128
  if self.config.output_file:
130
- return Path(file.parent, lower_underscore(file.stem)).with_suffix(
131
- extension
132
- )
129
+ return Path(file.parent, lower_underscore(file.stem)).with_suffix(extension)
133
130
 
134
131
  return Path(
135
132
  file.parent, self.config.output_folder, lower_underscore(file.stem)
@@ -142,12 +139,7 @@ class BaseWriter(ABC):
142
139
  self._("translator:") + ",".join(self.book.translators),
143
140
  self._("tag:") + ",".join(self.book.tags),
144
141
  ]
145
- return (
146
- "---\n"
147
- + "\n".join(metadata)
148
- + "\n---"
149
- + self.config.paragraph_separator
150
- )
142
+ return "---\n" + "\n".join(metadata) + "\n---" + self.config.paragraph_separator
151
143
 
152
144
  def _to_toc(self, list_symbol, header_symbol="") -> str:
153
145
  toc = ""
@@ -81,9 +81,7 @@ class EpubWriter(BaseWriter):
81
81
  logger.debug("Create separate volume page: %s", section)
82
82
  book.toc.append((html_volume, html_chapters))
83
83
  else:
84
- book.toc.append(
85
- (epub.Section(section.title), html_chapters)
86
- )
84
+ book.toc.append((epub.Section(section.title), html_chapters))
87
85
 
88
86
  if isinstance(section, Chapter):
89
87
  html_chapter = self._build_chapter(section)
@@ -115,16 +113,12 @@ class EpubWriter(BaseWriter):
115
113
  book.add_item(book_css)
116
114
 
117
115
  nav = epub.EpubNav()
118
- nav.add_link(
119
- href="style/book.css", rel="stylesheet", type="text/css"
120
- )
116
+ nav.add_link(href="style/book.css", rel="stylesheet", type="text/css")
121
117
  book.add_item(nav)
122
118
  book.spine.append("nav")
123
119
 
124
120
  except FileNotFoundError as error:
125
- logger.error(
126
- "Unknown EPUB template name: %s", self.config.epub_template
127
- )
121
+ logger.error("Unknown EPUB template name: %s", self.config.epub_template)
128
122
  raise SystemExit() from error
129
123
 
130
124
  def _gen_id(self) -> str:
@@ -149,9 +143,7 @@ class EpubWriter(BaseWriter):
149
143
  lang=self.book.language,
150
144
  content=html,
151
145
  )
152
- cover.add_link(
153
- href="style/book.css", rel="stylesheet", type="text/css"
154
- )
146
+ cover.add_link(href="style/book.css", rel="stylesheet", type="text/css")
155
147
  return cover
156
148
 
157
149
  def _build_volume(self, volume: Volume) -> epub.EpubHtml:
@@ -174,9 +166,7 @@ class EpubWriter(BaseWriter):
174
166
  lang=self.book.language,
175
167
  content=html,
176
168
  )
177
- epub_html.add_link(
178
- href="style/book.css", rel="stylesheet", type="text/css"
179
- )
169
+ epub_html.add_link(href="style/book.css", rel="stylesheet", type="text/css")
180
170
 
181
171
  return epub_html
182
172
 
@@ -202,8 +192,6 @@ class EpubWriter(BaseWriter):
202
192
  lang=self.book.language,
203
193
  content=html,
204
194
  )
205
- epub_html.add_link(
206
- href="style/book.css", rel="stylesheet", type="text/css"
207
- )
195
+ epub_html.add_link(href="style/book.css", rel="stylesheet", type="text/css")
208
196
 
209
197
  return epub_html
@@ -96,18 +96,11 @@ class GmiWriter(BaseWriter):
96
96
  export_filename.parent.mkdir(parents=True, exist_ok=True)
97
97
  logger.info("Creating %s", export_filename)
98
98
  with open(export_filename, "w", encoding="utf8") as file:
99
- file.write(
100
- self._to_volume_chapter_txt(section, chapter)
101
- )
99
+ file.write(self._to_volume_chapter_txt(section, chapter))
102
100
  ct_seq = ct_seq + 1
103
101
  if isinstance(section, Chapter):
104
102
  filename = lower_underscore(
105
- (
106
- f"{section_seq}"
107
- f"_{txt_filename.stem}"
108
- f"_{section.title}"
109
- ".gmi"
110
- )
103
+ (f"{section_seq}_{txt_filename.stem}_{section.title}.gmi")
111
104
  )
112
105
 
113
106
  export_filename = Path(
@@ -152,10 +145,7 @@ class GmiWriter(BaseWriter):
152
145
  f"# {volume.title}"
153
146
  + self.config.paragraph_separator
154
147
  + self.config.paragraph_separator.join(
155
- [
156
- self._to_chapter_txt(chapter, True)
157
- for chapter in volume.chapters
158
- ]
148
+ [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
159
149
  )
160
150
  )
161
151
 
@@ -95,18 +95,11 @@ class MdWriter(BaseWriter):
95
95
  export_filename.parent.mkdir(parents=True, exist_ok=True)
96
96
  logger.info("Creating %s", export_filename)
97
97
  with open(export_filename, "w", encoding="utf8") as file:
98
- file.write(
99
- self._to_volume_chapter_txt(section, chapter)
100
- )
98
+ file.write(self._to_volume_chapter_txt(section, chapter))
101
99
  ct_seq = ct_seq + 1
102
100
  if isinstance(section, Chapter):
103
101
  filename = lower_underscore(
104
- (
105
- f"{section_seq}"
106
- f"_{txt_filename.stem}"
107
- f"_{section.title}"
108
- ".md"
109
- )
102
+ (f"{section_seq}_{txt_filename.stem}_{section.title}.md")
110
103
  )
111
104
 
112
105
  export_filename = Path(
@@ -151,10 +144,7 @@ class MdWriter(BaseWriter):
151
144
  f"# {volume.title}"
152
145
  + self.config.paragraph_separator
153
146
  + self.config.paragraph_separator.join(
154
- [
155
- self._to_chapter_txt(chapter, True)
156
- for chapter in volume.chapters
157
- ]
147
+ [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
158
148
  )
159
149
  )
160
150
 
@@ -153,9 +153,7 @@ class PdfWriter(BaseWriter):
153
153
  canvas.restoreState()
154
154
 
155
155
  def _get_pagesize(self) -> Tuple:
156
- page_size = (
157
- self.config.page_size or self.langconf.DEFAULT_PDF_PAGE_SIZE
158
- )
156
+ page_size = self.config.page_size or self.langconf.DEFAULT_PDF_PAGE_SIZE
159
157
  return portrait(getattr(reportlab.lib.pagesizes, page_size.upper()))
160
158
 
161
159
  def _init_styles(self) -> None:
@@ -100,9 +100,7 @@ class TexWriter(BaseWriter):
100
100
  doc.append(NoEsc(r"\maketitle"))
101
101
  doc.append(NoEsc(r"\thispagestyle{empty}"))
102
102
  doc.append(NoEsc(r"\addtocontents{toc}{\protect\pagestyle{empty}}"))
103
- doc.append(
104
- NoEsc(r"\addtocontents{toc}{\protect\thispagestyle{empty}}")
105
- )
103
+ doc.append(NoEsc(r"\addtocontents{toc}{\protect\thispagestyle{empty}}"))
106
104
  doc.append(NoEsc(r"\tableofcontents"))
107
105
  doc.append(NoEsc(r"\pagestyle{empty}"))
108
106
  doc.append(NoEsc(r"\cleardoublepage"))
@@ -128,9 +126,7 @@ class TexWriter(BaseWriter):
128
126
 
129
127
  filename = str(new_filename.parent / new_filename.stem)
130
128
  pdf_filename = Path(filename).with_suffix(".pdf")
131
- doc.generate_pdf(
132
- filename, compiler="latexmk", clean_tex=self.config.clean_tex
133
- )
129
+ doc.generate_pdf(filename, compiler="latexmk", clean_tex=self.config.clean_tex)
134
130
  logger.info("Generate PDF file: %s", pdf_filename.resolve())
135
131
 
136
132
  if self.config.open:
@@ -105,18 +105,11 @@ class TxtWriter(BaseWriter):
105
105
  export_filename.parent.mkdir(parents=True, exist_ok=True)
106
106
  logger.info("Creating %s", export_filename)
107
107
  with open(export_filename, "w", encoding="utf8") as file:
108
- file.write(
109
- self._to_volume_chapter_txt(section, chapter)
110
- )
108
+ file.write(self._to_volume_chapter_txt(section, chapter))
111
109
  ct_seq = ct_seq + 1
112
110
  if isinstance(section, Chapter):
113
111
  filename = lower_underscore(
114
- (
115
- f"{section_seq}"
116
- f"_{txt_filename.stem}"
117
- f"_{section.title}"
118
- ".txt"
119
- )
112
+ (f"{section_seq}_{txt_filename.stem}_{section.title}.txt")
120
113
  )
121
114
 
122
115
  export_filename = Path(
@@ -139,9 +132,7 @@ class TxtWriter(BaseWriter):
139
132
  ymd_hms = dt.now().strftime("%Y%m%d_%H%M%S")
140
133
  new_filename = Path(
141
134
  txt_filename.resolve().parent.joinpath(
142
- lower_underscore(
143
- txt_filename.stem + "_" + ymd_hms + ".txt"
144
- )
135
+ lower_underscore(txt_filename.stem + "_" + ymd_hms + ".txt")
145
136
  )
146
137
  )
147
138
 
@@ -175,10 +175,7 @@ class TypWriter(BaseWriter):
175
175
  f"= {volume.title}"
176
176
  + self.config.paragraph_separator
177
177
  + self.config.paragraph_separator.join(
178
- [
179
- self._to_chapter_txt(chapter, True)
180
- for chapter in volume.chapters
181
- ]
178
+ [self._to_chapter_txt(chapter, True) for chapter in volume.chapters]
182
179
  )
183
180
  )
184
181
 
@@ -52,9 +52,7 @@ class Book:
52
52
  logger.debug("Book stats: %s", repr(stats))
53
53
  return stats
54
54
 
55
- def filename_format(
56
- self, filename_format: Union[str, Literal[True]]
57
- ) -> str:
55
+ def filename_format(self, filename_format: Union[str, Literal[True]]) -> str:
58
56
  """Generate the filename format based on the available selection."""
59
57
  authors = ", ".join(self.authors)
60
58
  format_options = {
@@ -64,9 +62,7 @@ class Book:
64
62
  try:
65
63
  return format_options[filename_format]
66
64
  except KeyError:
67
- raise AttributeError(
68
- f"Invalid filename format: '{filename_format}'!"
69
- )
65
+ raise AttributeError(f"Invalid filename format: '{filename_format}'!")
70
66
 
71
67
  def debug(self, verbosity: int = 1) -> None:
72
68
  """Dump debug log of sections in self.toc."""
@@ -18,8 +18,6 @@
18
18
  import argparse
19
19
  import logging
20
20
  from dataclasses import dataclass
21
- from importlib import import_module
22
- from importlib import import_module
23
21
  from types import ModuleType
24
22
  from typing import List, Tuple, Union
25
23
 
@@ -41,7 +39,10 @@ class Parser:
41
39
  langconf: ModuleType
42
40
 
43
41
  def __init__(
44
- self, raw_content: str, config: argparse.Namespace, langconf: ModuleType
42
+ self,
43
+ raw_content: str,
44
+ config: argparse.Namespace,
45
+ langconf: ModuleType,
45
46
  ) -> None:
46
47
  """Set the constructor for the Parser."""
47
48
  self.raw_content = raw_content
@@ -56,8 +57,8 @@ class Parser:
56
57
  """
57
58
  tokenizer = Tokenizer(self.raw_content, self.config)
58
59
 
59
- (book_title, authors, translators, tags, index, toc) = (
60
- self.parse_tokens(tokenizer)
60
+ (book_title, authors, translators, tags, index, toc) = self.parse_tokens(
61
+ tokenizer
61
62
  )
62
63
 
63
64
  book = Book(
@@ -102,17 +103,13 @@ class Parser:
102
103
  match = re.match(rf"第([{self.langconf.HALFWIDTH_NUMS}]*)", words)
103
104
  if match and match.group(1) != "":
104
105
  header_nums = match.group(1)
105
- return words.replace(
106
- header_nums, str(header_nums).rjust(length, "0")
107
- )
106
+ return words.replace(header_nums, str(header_nums).rjust(length, "0"))
108
107
 
109
108
  # left pad the section number if found as fullwidth integer
110
109
  match = re.match(rf"第([{self.langconf.FULLWIDTH_NUMS}]*)", words)
111
110
  if match and match.group(1) != "":
112
111
  header_nums = match.group(1)
113
- return words.replace(
114
- header_nums, str(header_nums).rjust(length, "0")
115
- )
112
+ return words.replace(header_nums, str(header_nums).rjust(length, "0"))
116
113
 
117
114
  replaced_words = zh_words_to_numbers(words, length=length)
118
115
 
@@ -151,8 +148,7 @@ class Parser:
151
148
  if (
152
149
  token.type not in ["CHAPTER", "PARAGRAPH"]
153
150
  or (
154
- token.type == "CHAPTER"
155
- and self.config.verbose >= chapter_verbosity
151
+ token.type == "CHAPTER" and self.config.verbose >= chapter_verbosity
156
152
  )
157
153
  or (
158
154
  token.type == "PARAGRAPH"
@@ -24,8 +24,7 @@ def build_subparser(subparsers):
24
24
  iter_namespace = pkgutil.iter_modules(__path__, __name__ + ".")
25
25
 
26
26
  subcommands = {
27
- name: importlib.import_module(name)
28
- for finder, name, ispkg in iter_namespace
27
+ name: importlib.import_module(name) for finder, name, ispkg in iter_namespace
29
28
  }
30
29
 
31
30
  for subcommand in subcommands.values():
@@ -27,9 +27,7 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  def build_subparser(subparsers) -> None:
29
29
  """Build the subparser."""
30
- gmi_parser = subparsers.add_parser(
31
- "gmi", help="generate ebook in Gemtext format"
32
- )
30
+ gmi_parser = subparsers.add_parser("gmi", help="generate ebook in Gemtext format")
33
31
 
34
32
  gmi_parser.set_defaults(func=run)
35
33
 
@@ -228,9 +228,7 @@ def header_number(args: argparse.Namespace, book: Book) -> Book:
228
228
  for toc_item in book.toc:
229
229
  toc_type = type(toc_item).__name__
230
230
  if toc_type in seq_lengths:
231
- toc_item.title = words_to_nums(
232
- args, toc_item.title, seq_lengths[toc_type]
233
- )
231
+ toc_item.title = words_to_nums(args, toc_item.title, seq_lengths[toc_type])
234
232
 
235
233
  return book
236
234
 
@@ -337,9 +335,7 @@ def do_delete_regex(args, content: str) -> str:
337
335
  str: The formatted book content.
338
336
  """
339
337
  for delete_regex in args.re_delete:
340
- content = re.sub(
341
- re.compile(rf"{delete_regex}", re.MULTILINE), "", content
342
- )
338
+ content = re.sub(re.compile(rf"{delete_regex}", re.MULTILINE), "", content)
343
339
  return content
344
340
 
345
341
 
@@ -27,9 +27,7 @@ logger = logging.getLogger(__name__)
27
27
 
28
28
  def build_subparser(subparsers) -> None:
29
29
  """Build the subparser."""
30
- md_parser = subparsers.add_parser(
31
- "md", help="generate ebook in Markdown format"
32
- )
30
+ md_parser = subparsers.add_parser("md", help="generate ebook in Markdown format")
33
31
 
34
32
  md_parser.set_defaults(func=run)
35
33
 
@@ -16,9 +16,6 @@
16
16
  """Env subcommand."""
17
17
 
18
18
  import argparse
19
- import logging
20
- import sys
21
-
22
19
  import logging
23
20
  import sys
24
21
  from importlib import import_module
@@ -28,9 +28,7 @@ logger = logging.getLogger(__name__)
28
28
 
29
29
  def build_subparser(subparsers) -> None:
30
30
  """Build the subparser."""
31
- pdf_parser = subparsers.add_parser(
32
- "pdf", help="generate ebook in Markdown format"
33
- )
31
+ pdf_parser = subparsers.add_parser("pdf", help="generate ebook in Markdown format")
34
32
 
35
33
  pdf_parser.set_defaults(func=run)
36
34