txt2ebook 0.1.141__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 (112) hide show
  1. txt2ebook-0.1.143/.pre-commit-config.yaml +117 -0
  2. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/CHANGELOG.md +16 -0
  3. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/PKG-INFO +1 -1
  4. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/noxfile.py +11 -15
  5. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/pyproject.toml +4 -3
  6. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/__init__.py +2 -6
  7. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/__init__.py +0 -1
  8. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/base.py +3 -11
  9. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/epub.py +6 -18
  10. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/gmi.py +3 -13
  11. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/md.py +3 -13
  12. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/pdf.py +1 -3
  13. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/tex.py +2 -6
  14. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/txt.py +3 -12
  15. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/typ.py +1 -4
  16. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/models/book.py +2 -6
  17. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/parser.py +15 -16
  18. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/__init__.py +1 -2
  19. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/gmi.py +1 -3
  20. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/massage.py +2 -6
  21. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/md.py +1 -3
  22. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/parse.py +8 -13
  23. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/pdf.py +1 -3
  24. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/tex.py +1 -3
  25. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/typ.py +1 -3
  26. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/tokenizer.py +3 -10
  27. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/zh_utils.py +1 -4
  28. txt2ebook-0.1.143/tests/test_input_file_arg.py +12 -0
  29. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_output_file_arg.py +1 -3
  30. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_purge_flag.py +1 -3
  31. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_split_volume_and_chapter_flag.py +1 -3
  32. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_subcommand_massage.py +4 -1
  33. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/uv.lock +96 -68
  34. txt2ebook-0.1.141/.pre-commit-config.yaml +0 -110
  35. txt2ebook-0.1.141/tests/test_input_file_arg.py +0 -15
  36. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/.coveragerc +0 -0
  37. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/.gitignore +0 -0
  38. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/.python-version +0 -0
  39. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/CONTRIBUTING.md +0 -0
  40. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/LICENSE.md +0 -0
  41. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/README.md +0 -0
  42. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/Makefile +0 -0
  43. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/make.bat +0 -0
  44. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/CHANGELOG.md +0 -0
  45. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/CONTRIBUTING.md +0 -0
  46. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/LICENSE.md +0 -0
  47. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/README.md +0 -0
  48. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/_static/logo.png +0 -0
  49. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/conf.py +0 -0
  50. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/index.rst +0 -0
  51. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/txt2ebook.formats.rst +0 -0
  52. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/txt2ebook.helpers.rst +0 -0
  53. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/txt2ebook.models.rst +0 -0
  54. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/txt2ebook.parsers.rst +0 -0
  55. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/docs/source/txt2ebook.rst +0 -0
  56. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/__main__.py +0 -0
  57. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/cli.py +0 -0
  58. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/exceptions.py +0 -0
  59. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/__init__.py +0 -0
  60. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/__init__.py +0 -0
  61. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/clean.css +0 -0
  62. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/condense.css +0 -0
  63. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/formats/templates/epub/noindent.css +0 -0
  64. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/helpers/__init__.py +0 -0
  65. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/languages/__init__.py +0 -0
  66. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/languages/en.py +0 -0
  67. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/languages/zh_cn.py +0 -0
  68. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/languages/zh_tw.py +0 -0
  69. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.mo +0 -0
  70. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/en/LC_MESSAGES/txt2ebook.po +0 -0
  71. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/txt2ebook.pot +0 -0
  72. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.mo +0 -0
  73. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-cn/LC_MESSAGES/txt2ebook.po +0 -0
  74. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.mo +0 -0
  75. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/locales/zh-tw/LC_MESSAGES/txt2ebook.po +0 -0
  76. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/models/__init__.py +0 -0
  77. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/models/chapter.py +0 -0
  78. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/models/volume.py +0 -0
  79. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/env.py +0 -0
  80. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/src/txt2ebook/subcommands/epub.py +0 -0
  81. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/__init__.py +0 -0
  82. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/conftest.py +0 -0
  83. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/empty_file.txt +0 -0
  84. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/missing_chapters.txt +0 -0
  85. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample.txt +0 -0
  86. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_all_headers.txt +0 -0
  87. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_long_headers.txt +0 -0
  88. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_remove_wrapping.txt +0 -0
  89. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_unsorted_headers.txt +0 -0
  90. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_with_issues.txt +0 -0
  91. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/fixtures/sample_with_metadata.txt +0 -0
  92. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_epub_writer.py +0 -0
  93. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_filename_format_flag.py +0 -0
  94. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_format_option.py +0 -0
  95. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_header_number_flag.py +0 -0
  96. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_language_option.py +0 -0
  97. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_overwrite_flag.py +0 -0
  98. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_parser.py +0 -0
  99. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_quiet_flag.py +0 -0
  100. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_raise_warnings.py +0 -0
  101. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_sort_volume_and_chapter_flag.py +0 -0
  102. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_subcommand_env.py +0 -0
  103. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_subcommand_epub.py +0 -0
  104. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_test_parsing_flag.py +0 -0
  105. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_tokenizer.py +0 -0
  106. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_translator_option.py +0 -0
  107. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_txt2ebook.py +0 -0
  108. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_verbose_flag.py +0 -0
  109. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_volume_page_flag.py +0 -0
  110. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_zh_utils_zh_halfwidth_to_fullwidth.py +0 -0
  111. {txt2ebook-0.1.141 → txt2ebook-0.1.143}/tests/test_zh_utils_zh_numeric.py +0 -0
  112. {txt2ebook-0.1.141 → 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,22 @@ 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
+
18
+ ## v0.1.142 (2025-06-01)
19
+
20
+ - Bump deps
21
+ - Bump `pre-commit` hook for `mypy`
22
+ - Handle empty content when parsing
23
+ - Update test to use `parse` subcommand
24
+ - Use pre-commit in `venv` in `deps` `nox` job
25
+
10
26
  ## v0.1.141 (2025-05-25)
11
27
 
12
28
  - Bump and sort deps
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: txt2ebook
3
- Version: 0.1.141
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
@@ -25,9 +25,8 @@ nox.options.default_venv_backend = "uv"
25
25
  @nox.session(python="3.9")
26
26
  def deps(session: nox.Session) -> None:
27
27
  """Update pre-commit hooks and deps."""
28
- session.install("pre-commit", "uv")
29
- session.run("pre-commit", "autoupdate", *session.posargs)
30
28
  session.run("uv", "sync", "-U", "--active")
29
+ session.run("pre-commit", "autoupdate", *session.posargs)
31
30
 
32
31
 
33
32
  @nox.session()
@@ -40,7 +39,12 @@ def lint(session: nox.Session) -> None:
40
39
  nox -s lint -- pylint
41
40
  """
42
41
  _uv_install(session)
43
- session.run("pre-commit", "run", "--all-files", *session.posargs)
42
+ session.run(
43
+ "pre-commit",
44
+ "run",
45
+ "--all-files",
46
+ *session.posargs,
47
+ )
44
48
 
45
49
 
46
50
  @nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"])
@@ -69,9 +73,7 @@ def cov(session: nox.Session) -> None:
69
73
  def doc(session: nox.Session) -> None:
70
74
  """Build doc with sphinx."""
71
75
  _uv_install(session)
72
- session.run(
73
- "sphinx-build", "docs/source/", "docs/build/html", *session.posargs
74
- )
76
+ session.run("sphinx-build", "docs/source/", "docs/build/html", *session.posargs)
75
77
 
76
78
 
77
79
  @nox.session(python="3.13")
@@ -189,17 +191,11 @@ def release(session: nox.Session) -> None:
189
191
  """Bump release."""
190
192
  _uv_install(session)
191
193
 
192
- before_version = session.run(
193
- "uv", "version", "--short", silent=True
194
- ).strip()
194
+ before_version = session.run("uv", "version", "--short", silent=True).strip()
195
195
  session.run("uv", "version", "--bump", "patch")
196
- after_version = session.run(
197
- "uv", "version", "--short", silent=True
198
- ).strip()
196
+ after_version = session.run("uv", "version", "--short", silent=True).strip()
199
197
 
200
- _search_and_replace(
201
- "src/txt2ebook/__init__.py", before_version, after_version
202
- )
198
+ _search_and_replace("src/txt2ebook/__init__.py", before_version, after_version)
203
199
 
204
200
  date = datetime.date.today().strftime("%Y-%m-%d")
205
201
  before_header = "## [Unreleased]\n\n"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "txt2ebook"
3
- version = "0.1.141"
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,7 +18,7 @@
18
18
  import argparse
19
19
  import logging
20
20
  from dataclasses import dataclass
21
- from importlib import import_module
21
+ from types import ModuleType
22
22
  from typing import List, Tuple, Union
23
23
 
24
24
  import regex as re
@@ -36,14 +36,18 @@ class Parser:
36
36
 
37
37
  raw_content: str
38
38
  config: argparse.Namespace
39
-
40
- def __init__(self, raw_content: str, config: argparse.Namespace) -> None:
39
+ langconf: ModuleType
40
+
41
+ def __init__(
42
+ self,
43
+ raw_content: str,
44
+ config: argparse.Namespace,
45
+ langconf: ModuleType,
46
+ ) -> None:
41
47
  """Set the constructor for the Parser."""
42
48
  self.raw_content = raw_content
43
49
  self.config = config
44
-
45
- config_lang = config.language.replace("-", "_")
46
- self.langconf = import_module(f"txt2ebook.languages.{config_lang}")
50
+ self.langconf = langconf
47
51
 
48
52
  def parse(self) -> Book:
49
53
  """Parse the content into volumes (optional) and chapters.
@@ -53,8 +57,8 @@ class Parser:
53
57
  """
54
58
  tokenizer = Tokenizer(self.raw_content, self.config)
55
59
 
56
- (book_title, authors, translators, tags, index, toc) = (
57
- self.parse_tokens(tokenizer)
60
+ (book_title, authors, translators, tags, index, toc) = self.parse_tokens(
61
+ tokenizer
58
62
  )
59
63
 
60
64
  book = Book(
@@ -99,17 +103,13 @@ class Parser:
99
103
  match = re.match(rf"第([{self.langconf.HALFWIDTH_NUMS}]*)", words)
100
104
  if match and match.group(1) != "":
101
105
  header_nums = match.group(1)
102
- return words.replace(
103
- header_nums, str(header_nums).rjust(length, "0")
104
- )
106
+ return words.replace(header_nums, str(header_nums).rjust(length, "0"))
105
107
 
106
108
  # left pad the section number if found as fullwidth integer
107
109
  match = re.match(rf"第([{self.langconf.FULLWIDTH_NUMS}]*)", words)
108
110
  if match and match.group(1) != "":
109
111
  header_nums = match.group(1)
110
- return words.replace(
111
- header_nums, str(header_nums).rjust(length, "0")
112
- )
112
+ return words.replace(header_nums, str(header_nums).rjust(length, "0"))
113
113
 
114
114
  replaced_words = zh_words_to_numbers(words, length=length)
115
115
 
@@ -148,8 +148,7 @@ class Parser:
148
148
  if (
149
149
  token.type not in ["CHAPTER", "PARAGRAPH"]
150
150
  or (
151
- token.type == "CHAPTER"
152
- and self.config.verbose >= chapter_verbosity
151
+ token.type == "CHAPTER" and self.config.verbose >= chapter_verbosity
153
152
  )
154
153
  or (
155
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