From e9a95715ca33a5f591f910421289753836970622 Mon Sep 17 00:00:00 2001 From: Saeed Rasooli Date: Wed, 23 Oct 2024 21:11:00 +0330 Subject: [PATCH] use `from __future__ import annotations` and get rid of quotation around types --- pyglossary/apple_utils.py | 6 +- pyglossary/compression.py | 5 +- pyglossary/core.py | 22 ++--- pyglossary/ebook_base.py | 32 ++++---- pyglossary/entry.py | 4 +- pyglossary/entry_base.py | 2 +- pyglossary/entry_filters.py | 81 ++++++++++--------- pyglossary/entry_list.py | 7 +- pyglossary/flags.py | 6 +- pyglossary/glossary.py | 7 +- pyglossary/glossary_info.py | 5 +- pyglossary/glossary_types.py | 18 ++--- pyglossary/glossary_v2.py | 51 ++++++------ pyglossary/image_utils.py | 2 +- pyglossary/io_utils.py | 12 +-- pyglossary/iter_utils.py | 8 +- pyglossary/json_utils.py | 8 +- pyglossary/langs/__init__.py | 12 +-- pyglossary/langs/writing_system.py | 4 +- pyglossary/option.py | 15 ++-- pyglossary/os_utils.py | 4 +- pyglossary/plugin_lib/dictdlib.py | 17 ++-- pyglossary/plugin_manager.py | 29 +++---- pyglossary/plugin_prop.py | 37 ++++----- pyglossary/plugins/aard2_slob.py | 9 ++- pyglossary/plugins/abc_medical_notes.py | 3 +- pyglossary/plugins/almaany.py | 3 +- pyglossary/plugins/appledict/_content.py | 6 +- pyglossary/plugins/appledict/_dict.py | 7 +- pyglossary/plugins/appledict/_normalize.py | 7 +- pyglossary/plugins/appledict/indexes/ru.py | 10 ++- pyglossary/plugins/appledict/indexes/zh.py | 2 +- pyglossary/plugins/appledict/jing/main.py | 2 +- pyglossary/plugins/appledict_bin/__init__.py | 20 ++--- .../appledict_bin/appledict_properties.py | 9 ++- pyglossary/plugins/appledict_bin/key_data.py | 4 +- pyglossary/plugins/ayandict_sqlite.py | 7 +- pyglossary/plugins/babylon_bgl/bgl_pos.py | 2 + pyglossary/plugins/babylon_bgl/bgl_reader.py | 21 ++--- pyglossary/plugins/cc_kedict.py | 17 ++-- pyglossary/plugins/crawler_dir.py | 8 +- pyglossary/plugins/csv_plugin.py | 4 +- pyglossary/plugins/dicformids.py | 7 +- pyglossary/plugins/dict_cc.py | 7 +- pyglossary/plugins/dict_cc_split.py | 3 +- pyglossary/plugins/dictunformat.py | 2 +- pyglossary/plugins/digitalnk.py | 3 +- pyglossary/plugins/dsl/__init__.py | 10 +-- pyglossary/plugins/dsl/transform.py | 2 +- pyglossary/plugins/ebook_epub2.py | 7 +- pyglossary/plugins/ebook_kobo.py | 11 +-- pyglossary/plugins/ebook_kobo_dictfile.py | 16 ++-- pyglossary/plugins/ebook_mobi.py | 17 ++-- pyglossary/plugins/edict2/__init__.py | 4 +- pyglossary/plugins/edict2/conv.py | 10 ++- pyglossary/plugins/edlin.py | 4 +- pyglossary/plugins/freedict.py | 67 +++++++-------- pyglossary/plugins/gettext_po.py | 2 +- pyglossary/plugins/html_dir.py | 16 ++-- pyglossary/plugins/iupac_goldbook.py | 11 +-- pyglossary/plugins/jmdict.py | 33 ++++---- pyglossary/plugins/jmnedict.py | 28 ++++--- pyglossary/plugins/lingoes_ldf.py | 8 +- .../plugins/octopus_mdict_new/__init__.py | 5 +- pyglossary/plugins/quickdic6.py | 8 +- pyglossary/plugins/sql.py | 5 +- pyglossary/plugins/stardict.py | 11 +-- pyglossary/plugins/stardict_textual.py | 12 +-- pyglossary/plugins/testformat.py | 6 +- pyglossary/plugins/wiktextract.py | 25 +++--- pyglossary/plugins/wordnet.py | 4 +- pyglossary/plugins/xdxf/__init__.py | 22 ++--- pyglossary/plugins/xdxf_css/__init__.py | 22 ++--- pyglossary/plugins/xdxf_lax.py | 23 +++--- pyglossary/plugins/yomichan.py | 17 ++-- pyglossary/reverse.py | 18 +++-- pyglossary/sdsqlite.py | 6 +- pyglossary/slob.py | 61 +++++++------- pyglossary/sort_keys.py | 15 ++-- pyglossary/sort_keys_types.py | 10 ++- pyglossary/sort_modules/dicformids.py | 10 ++- pyglossary/sort_modules/ebook.py | 14 ++-- pyglossary/sort_modules/ebook_length3.py | 8 +- pyglossary/sort_modules/headword.py | 20 ++--- .../sort_modules/headword_bytes_lower.py | 14 ++-- pyglossary/sort_modules/headword_lower.py | 20 ++--- pyglossary/sort_modules/random.py | 12 +-- pyglossary/sort_modules/stardict.py | 14 ++-- pyglossary/sq_entry_list.py | 7 +- pyglossary/text_reader.py | 14 ++-- pyglossary/text_utils.py | 11 +-- pyglossary/text_writer.py | 6 +- pyglossary/ui/argparse_utils.py | 6 +- pyglossary/ui/dependency.py | 2 +- pyglossary/ui/option_ui.py | 4 +- pyglossary/ui/tools/diff_glossary.py | 3 +- pyglossary/ui/tools/format_entry.py | 4 +- pyglossary/ui/tools/view_glossary.py | 5 +- pyglossary/ui/tools/word_diff.py | 4 +- pyglossary/ui/ui_cmd.py | 21 ++--- pyglossary/ui/ui_cmd_interactive.py | 16 ++-- pyglossary/ui/ui_gtk.py | 8 +- pyglossary/ui/ui_gtk4.py | 8 +- pyglossary/ui/ui_tk.py | 17 ++-- pyglossary/xdxf/css_js_transform.py | 72 +++++++++-------- pyglossary/xdxf/transform.py | 72 +++++++++-------- pyglossary/xdxf/xsl_transform.py | 4 +- scripts/wiktextract/extract-schema.py | 10 +-- tests/g_appledict_bin_test.py | 2 +- tests/glossary_test.py | 2 +- tests/glossary_v2_test.py | 2 +- tests/option_test.py | 2 +- whitelist.py | 36 ++++----- 113 files changed, 826 insertions(+), 697 deletions(-) diff --git a/pyglossary/apple_utils.py b/pyglossary/apple_utils.py index 51885cf20..3033e4576 100644 --- a/pyglossary/apple_utils.py +++ b/pyglossary/apple_utils.py @@ -12,6 +12,8 @@ # also see: # https://github.com/servo/servo/blob/master/components/style/properties/counted_unknown_properties.py +from __future__ import annotations + import re from .core import log @@ -35,7 +37,7 @@ rb"[ \t]*(" + b"|".join(cssKeyRemove) + rb")\s*:[^;}]*;\s*", ) -cssMapping: "dict[str, str]" = { +cssMapping: dict[str, str] = { # I didn't actually find these font values: "-apple-system-body": '"Helvetica Neue"', "-apple-system": '"Helvetica Neue"', @@ -98,7 +100,7 @@ ) -def _subCSS(m: "re.Match") -> bytes: +def _subCSS(m: re.Match) -> bytes: b_key = m.group(0) value = cssMapping.get(b_key.decode("ascii")) if value is None: diff --git a/pyglossary/compression.py b/pyglossary/compression.py index 78a2a5cb5..1b44c3735 100644 --- a/pyglossary/compression.py +++ b/pyglossary/compression.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import logging import os @@ -85,7 +86,7 @@ def zipFileOrDir(filename: str) -> None: from .os_utils import indir - def _zipFileAdd(zf: "zipfile.ZipFile", filename: str) -> None: + def _zipFileAdd(zf: zipfile.ZipFile, filename: str) -> None: if isfile(filename): zf.write(filename) return @@ -115,7 +116,7 @@ def _zipFileAdd(zf: "zipfile.ZipFile", filename: str) -> None: _zipFileAdd(zf, fname) -def compress(_glos: "GlossaryType", filename: str, compression: str) -> str: +def compress(_glos: GlossaryType, filename: str, compression: str) -> str: """ Filename is the existing file path. diff --git a/pyglossary/core.py b/pyglossary/core.py index cf7c4559f..1b5c4acaa 100644 --- a/pyglossary/core.py +++ b/pyglossary/core.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import inspect import logging import os @@ -78,11 +80,11 @@ def trace(log: logging.Logger, msg: str) -> None: class _Formatter(logging.Formatter): def __init__(self, *args, **kwargs) -> None: # noqa: ANN101 logging.Formatter.__init__(self, *args, **kwargs) - self.fill: "Callable[[str], str] | None" = None + self.fill: Callable[[str], str] | None = None def formatMessage( self, - record: "logging.LogRecord", + record: logging.LogRecord, ) -> str: msg = logging.Formatter.formatMessage(self, record) if self.fill is not None: @@ -125,7 +127,7 @@ def getVerbosity(self) -> int: def trace(self, msg: str) -> None: self.log(TRACE, msg) - def pretty(self, data: "Any", header: str = "") -> None: + def pretty(self, data: Any, header: str = "") -> None: from pprint import pformat self.debug(header + pformat(data)) @@ -144,7 +146,7 @@ def setTimeEnable(self, timeEnable: bool) -> None: for handler in self.handlers: handler.setFormatter(formatter) - def addHandler(self, hdlr: "logging.Handler") -> None: + def addHandler(self, hdlr: logging.Handler) -> None: # if want to add separate format (new config keys and flags) for ui_gtk # and ui_tk, you need to remove this function and run handler.setFormatter # in ui_gtk and ui_tk @@ -153,7 +155,7 @@ def addHandler(self, hdlr: "logging.Handler") -> None: def _formatVarDict( - dct: "dict[str, Any]", + dct: dict[str, Any], indent: int = 4, max_width: int = 80, ) -> str: @@ -174,7 +176,7 @@ def _formatVarDict( def format_exception( - exc_info: "ExcInfoType | None" = None, + exc_info: ExcInfoType | None = None, add_locals: bool = False, add_globals: bool = False, ) -> str: @@ -341,8 +343,8 @@ def isDebug() -> bool: if os.sep == "\\": def _windows_show_exception( - _type: "type[BaseException]", - exc: "BaseException", + _type: type[BaseException], + exc: BaseException, tback: "TracebackType | None", ) -> None: if not (_type and exc and tback): @@ -362,8 +364,8 @@ def _windows_show_exception( else: def _unix_show_exception( - _type: "type[BaseException]", - exc: "BaseException", + _type: type[BaseException], + exc: BaseException, tback: "TracebackType | None", ) -> None: if not (_type and exc and tback): diff --git a/pyglossary/ebook_base.py b/pyglossary/ebook_base.py index 0c35266b1..b7243ea63 100644 --- a/pyglossary/ebook_base.py +++ b/pyglossary/ebook_base.py @@ -22,6 +22,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations + import logging import os import shutil @@ -46,7 +48,7 @@ class GroupState: - def __init__(self, writer: "EbookWriter") -> None: + def __init__(self, writer: EbookWriter) -> None: self.writer = writer self.last_prefix = "" self.group_index = -1 @@ -55,12 +57,12 @@ def __init__(self, writer: "EbookWriter") -> None: def reset(self) -> None: self.first_word = "" self.last_word = "" - self.group_contents: "list[str]" = [] + self.group_contents: list[str] = [] def is_new(self, prefix: str) -> bool: return bool(self.last_prefix) and prefix != self.last_prefix - def add(self, entry: "EntryType", prefix: str) -> None: + def add(self, entry: EntryType, prefix: str) -> None: word = entry.s_word defi = entry.defi if not self.first_word: @@ -137,7 +139,7 @@ class EbookWriter: def __init__( self, - glos: "GlossaryType", + glos: GlossaryType, escape_strings: bool = False, # ignore_synonyms=False, # flatten_synonyms=False, @@ -157,9 +159,9 @@ def __init__( self._tmpDir = tempfile.mkdtemp() self.cover = "" - self.files: "list[dict[str, Any]]" = [] - self.manifest_files: "list[dict[str, str]]" = [] - self._group_labels: "list[str]" = [] + self.files: list[dict[str, Any]] = [] + self.manifest_files: list[dict[str, str]] = [] + self._group_labels: list[str] = [] def finish(self) -> None: self._filename = "" @@ -245,13 +247,13 @@ def get_group_xhtml_file_name_from_index(self, index: int) -> str: def get_prefix(self, word: str) -> str: raise NotImplementedError - def sortKey(self, words: "list[str]") -> "Any": + def sortKey(self, words: list[str]) -> Any: raise NotImplementedError def _add_group( self, - group_labels: "list[str]", - state: "GroupState", + group_labels: list[str], + state: GroupState, ) -> None: if not state.last_prefix: return @@ -284,7 +286,7 @@ def _add_group( "application/xhtml+xml", ) - def write_data_entry(self, entry: "EntryType") -> None: + def write_data_entry(self, entry: EntryType) -> None: if entry.getFileName() == "style.css": self.add_file_manifest( "OEBPS/style.css", @@ -297,7 +299,7 @@ def write_groups(self) -> "Generator[None, EntryType, None]": # TODO: rtl=False option # TODO: handle alternates better (now shows word1|word2... in title) - group_labels: "list[str]" = [] + group_labels: list[str] = [] state = GroupState(self) while True: @@ -323,7 +325,7 @@ def format_group_content( self, word: str, defi: str, - variants: "list[str] | None" = None, # noqa: ARG002 + variants: list[str] | None = None, # noqa: ARG002 ) -> str: return self.GROUP_XHTML_WORD_DEFINITION_TEMPLATE.format( headword=self.escape_if_needed(word), @@ -342,7 +344,7 @@ def escape_if_needed(self, string: str) -> str: .replace("<", "<") ) - def write_index(self, group_labels: "list[str]") -> None: + def write_index(self, group_labels: list[str]) -> None: """group_labels: a list of labels.""" links = [] for label_i, label in enumerate(group_labels): @@ -420,7 +422,7 @@ def write_opf(self) -> None: self.add_file("OEBPS/content.opf", opf_contents) - def write_ncx(self, group_labels: "list[str]") -> None: + def write_ncx(self, group_labels: list[str]) -> None: """ write_ncx. diff --git a/pyglossary/entry.py b/pyglossary/entry.py index 5d032300b..8a8969d8a 100644 --- a/pyglossary/entry.py +++ b/pyglossary/entry.py @@ -101,7 +101,7 @@ def s_word(self) -> str: return self._fname @property - def l_word(self) -> "list[str]": + def l_word(self) -> list[str]: return [self._fname] @property @@ -259,7 +259,7 @@ def s_word(self) -> str: return joinByBar(self._word) @property - def l_word(self) -> "list[str]": + def l_word(self) -> list[str]: """Returns list of the word and all the alternate words.""" if isinstance(self._word, str): return [self._word] diff --git a/pyglossary/entry_base.py b/pyglossary/entry_base.py index 9e34cc4aa..f35b4808e 100644 --- a/pyglossary/entry_base.py +++ b/pyglossary/entry_base.py @@ -10,7 +10,7 @@ class BaseEntry: - __slots__: "list[str]" = [ + __slots__: list[str] = [ "_word", ] diff --git a/pyglossary/entry_filters.py b/pyglossary/entry_filters.py index b58109218..bf529684e 100644 --- a/pyglossary/entry_filters.py +++ b/pyglossary/entry_filters.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import logging import re @@ -33,13 +34,13 @@ class EntryFilterType(typing.Protocol): desc: str = "" falseComment: str = "" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: raise NotImplementedError def prepare(self) -> None: raise NotImplementedError - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": raise NotImplementedError @@ -48,13 +49,13 @@ class EntryFilter: desc: str = "" falseComment: str = "" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self.glos = glos def prepare(self) -> None: """Run this after glossary info is set and ready.""" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 """ Return an Entry object, or None to skip. @@ -69,7 +70,7 @@ class TrimWhitespaces(EntryFilter): name = "trim_whitespaces" desc = "Remove leading/trailing whitespaces from word(s) and definition" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 entry.strip() entry.replace("\r", "") return entry @@ -79,7 +80,7 @@ class NonEmptyWordFilter(EntryFilter): name = "non_empty_word" desc = "Skip entries with empty word" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 if not entry.s_word: return None return entry @@ -89,7 +90,7 @@ class NonEmptyDefiFilter(EntryFilter): name = "non_empty_defi" desc = "Skip entries with empty definition" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 if not entry.defi: return None return entry @@ -99,7 +100,7 @@ class RemoveEmptyAndDuplicateAltWords(EntryFilter): name = "remove_empty_dup_alt_words" desc = "Remove empty and duplicate alternate words" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 entry.removeEmptyAndDuplicateAltWords() if not entry.l_word: return None @@ -111,7 +112,7 @@ class FixUnicode(EntryFilter): desc = "Fix Unicode in word(s) and definition" falseComment = "Do not fix Unicode in word(s) and definition" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 entry.editFuncWord(fixUtf8) entry.editFuncDefi(fixUtf8) return entry @@ -122,7 +123,7 @@ class LowerWord(EntryFilter): desc = "Lowercase word(s)" falseComment = "Do not lowercase words before writing" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: EntryFilter.__init__(self, glos) self._re_word_ref = re.compile("href=[\"'](bword://[^\"']+)[\"']") @@ -132,7 +133,7 @@ def lowerWordRefs(self, defi: str) -> str: defi, ) - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": entry.editFuncWord(str.lower) entry.editFuncDefi(self.lowerWordRefs) return entry @@ -142,7 +143,7 @@ class RTLDefi(EntryFilter): name = "rtl" desc = "Make definition right-to-left" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 entry.editFuncDefi(lambda defi: f'
{defi}
') return entry @@ -153,7 +154,7 @@ class RemoveHtmlTagsAll(EntryFilter): def __init__( self, - glos: "GlossaryType", # noqa: ARG002 + glos: GlossaryType, # noqa: ARG002 ) -> None: self._p_pattern = re.compile( "]*?)?>(.*?)

", @@ -168,7 +169,7 @@ def __init__( re.IGNORECASE, ) - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": from bs4 import BeautifulSoup def fixStr(st: str) -> str: @@ -193,14 +194,14 @@ class RemoveHtmlTags(EntryFilter): name = "remove_html" desc = "Remove given comma-separated HTML tags (not their contents) from definition" - def __init__(self, glos: "GlossaryType", tagsStr: str) -> None: + def __init__(self, glos: GlossaryType, tagsStr: str) -> None: tags = tagsStr.split(",") self.glos = glos self.tags = tags tagsRE = "|".join(self.tags) self.pattern = re.compile(f"]*)?>") - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": def fixStr(st: str) -> str: return self.pattern.sub("", st) @@ -214,12 +215,12 @@ class StripFullHtml(EntryFilter): def __init__( self, - glos: "GlossaryType", # noqa: ARG002 + glos: GlossaryType, # noqa: ARG002 errorHandler: "Callable[[EntryType, str], None] | None", ) -> None: self._errorHandler = errorHandler - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": err = entry.stripFullHtml() if err and self._errorHandler: self._errorHandler(entry, err) @@ -257,7 +258,7 @@ class NormalizeHtml(EntryFilter): def __init__( self, - glos: "GlossaryType", # noqa: ARG002 + glos: GlossaryType, # noqa: ARG002 ) -> None: log.info("Normalizing HTML tags") self._pattern = re.compile( @@ -272,7 +273,7 @@ def _subLower(m: "re.Match") -> str: def _fixDefi(self, st: str) -> str: return self._pattern.sub(self._subLower, st) - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": entry.editFuncDefi(self._fixDefi) return entry @@ -281,7 +282,7 @@ class SkipDataEntry(EntryFilter): name = "skip_resources" desc = "Skip resources / data files" - def run(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 if entry.isData(): return None return entry @@ -291,7 +292,7 @@ class LanguageCleanup(EntryFilter): name = "lang" desc = "Language-specific cleanup/fixes" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: EntryFilter.__init__(self, glos) self._run_func: "Callable[[EntryType], EntryType | None] | None" = None @@ -305,7 +306,7 @@ def prepare(self) -> None: self._run_func = self.run_fa log.info("Using Persian filter") - def run_fa(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 + def run_fa(self, entry: EntryType) -> "EntryType | None": # noqa: PLR6301 from .persian_utils import faEditStr entry.editFuncWord(faEditStr) @@ -315,7 +316,7 @@ def run_fa(self, entry: "EntryType") -> "EntryType | None": # noqa: PLR6301 # for GoldenDict ^^ FIXME return entry - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": if self._run_func: return self._run_func(entry) return entry @@ -349,7 +350,7 @@ def cleanDefi(self, st: str) -> str: return st # noqa: RET504 - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": entry.editFuncDefi(self.cleanDefi) return entry @@ -358,11 +359,11 @@ class PreventDuplicateWords(EntryFilter): name = "prevent_duplicate_words" desc = "Prevent duplicate words" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: EntryFilter.__init__(self, glos) - self._wordSet: "set[str]" = set() + self._wordSet: set[str] = set() - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": if entry.isData(): return entry @@ -389,11 +390,11 @@ class SkipEntriesWithDuplicateHeadword(EntryFilter): name = "skip_duplicate_headword" desc = "Skip entries with a duplicate headword" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: EntryFilter.__init__(self, glos) - self._wset: "set[str]" = set() + self._wset: set[str] = set() - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": word = entry.l_word[0] if word in self._wset: return None @@ -405,11 +406,11 @@ class TrimArabicDiacritics(EntryFilter): name = "trim_arabic_diacritics" desc = "Trim Arabic diacritics from headword" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: EntryFilter.__init__(self, glos) self._pat = re.compile("[\u064b-\u065f]") - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": words = list(entry.l_word) hw = words[0] hw_t = self._pat.sub("", hw) @@ -424,7 +425,7 @@ class UnescapeWordLinks(EntryFilter): name = "unescape_word_links" desc = "Unescape Word Links" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: from pyglossary.html_utils import unescape_unicode EntryFilter.__init__(self, glos) @@ -437,7 +438,7 @@ def __init__(self, glos: "GlossaryType") -> None: def _sub(self, m: "re.Match") -> str: return self._unescape(m.group(0)) - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": if entry.isData(): return entry entry._defi = self._pat.sub(self._sub, entry.defi) # type: ignore @@ -448,15 +449,15 @@ class ShowProgressBar(EntryFilter): name = "progressbar" desc = "Progress Bar" - def __init__(self, glos: "GlossaryExtendedType") -> None: + def __init__(self, glos: GlossaryExtendedType) -> None: EntryFilter.__init__(self, glos) - self.glos: "GlossaryExtendedType" = glos + self.glos: GlossaryExtendedType = glos self._wordCount = -1 self._wordCountThreshold = 0 self._lastPos = 0 self._index = 0 - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": index = self._index self._index = index + 1 @@ -487,7 +488,7 @@ class ShowMaxMemoryUsage(EntryFilter): desc = "Show Max Memory Usage" MAX_WORD_LEN = 30 - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: import os import psutil @@ -496,7 +497,7 @@ def __init__(self, glos: "GlossaryType") -> None: self._process = psutil.Process(os.getpid()) self._max_mem_usage = 0 - def run(self, entry: "EntryType") -> "EntryType | None": + def run(self, entry: EntryType) -> "EntryType | None": usage = self._process.memory_info().rss // 1024 if usage > self._max_mem_usage: self._max_mem_usage = usage diff --git a/pyglossary/entry_list.py b/pyglossary/entry_list.py index 4f970b6af..8ceaaaa26 100644 --- a/pyglossary/entry_list.py +++ b/pyglossary/entry_list.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import logging from typing import TYPE_CHECKING @@ -41,7 +42,7 @@ def __init__( entryToRaw: "Callable[[EntryType], RawEntryType]", entryFromRaw: "Callable[[RawEntryType], EntryType]", ) -> None: - self._l: "list[RawEntryType]" = [] + self._l: list[RawEntryType] = [] self._entryToRaw = entryToRaw self._entryFromRaw = entryFromRaw self._sortKey: "Callable[[RawEntryType], Any] | None" = None @@ -55,7 +56,7 @@ def rawEntryCompress(self) -> bool: def rawEntryCompress(self, enable: bool) -> None: self._rawEntryCompress = enable - def append(self, entry: "EntryType") -> None: + def append(self, entry: EntryType) -> None: self._l.append(self._entryToRaw(entry)) def clear(self) -> None: @@ -71,7 +72,7 @@ def __iter__(self) -> "Iterator[EntryType]": def setSortKey( self, - namedSortKey: "NamedSortKey", + namedSortKey: NamedSortKey, sortEncoding: "str | None", writeOptions: "dict[str, Any]", ) -> None: diff --git a/pyglossary/flags.py b/pyglossary/flags.py index bfb24944c..b030576f9 100644 --- a/pyglossary/flags.py +++ b/pyglossary/flags.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -20,7 +22,7 @@ class StrWithDesc(str): desc: str __slots__ = ["desc"] - def __new__(cls: "type", name: str, desc: str) -> "StrWithDesc": + def __new__(cls: type, name: str, desc: str) -> StrWithDesc: s: StrWithDesc = str.__new__(cls, name) s.desc = desc flagsByName[name] = s @@ -33,4 +35,4 @@ def __new__(cls: "type", name: str, desc: str) -> "StrWithDesc": NEVER = StrWithDesc("never", "Never") # to satisfy mypy: -YesNoAlwaysNever: "TypeAlias" = StrWithDesc +YesNoAlwaysNever: TypeAlias = StrWithDesc diff --git a/pyglossary/glossary.py b/pyglossary/glossary.py index adf15b182..956c12e90 100644 --- a/pyglossary/glossary.py +++ b/pyglossary/glossary.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations from os.path import relpath from time import perf_counter as now @@ -90,7 +91,7 @@ def read( **kwargs, ) - def addEntryObj(self, entry: "EntryType") -> None: + def addEntryObj(self, entry: EntryType) -> None: self._data.append(entry) @staticmethod @@ -99,8 +100,8 @@ def updateIter() -> None: def sortWords( self, - sortKeyName: "str" = "headword_lower", - sortEncoding: "str" = "utf-8", + sortKeyName: str = "headword_lower", + sortEncoding: str = "utf-8", writeOptions: "dict[str, Any] | None" = None, ) -> None: """sortKeyName: see doc/sort-key.md.""" diff --git a/pyglossary/glossary_info.py b/pyglossary/glossary_info.py index 712266cce..b572ca68c 100644 --- a/pyglossary/glossary_info.py +++ b/pyglossary/glossary_info.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import logging from collections import OrderedDict as odict @@ -46,7 +47,7 @@ class GlossaryInfo: def __init__(self) -> None: self._info: "dict[str, str]" = odict() - def infoKeys(self) -> "list[str]": + def infoKeys(self) -> list[str]: return list(self._info) # def formatInfoKeys(self, format: str):# FIXME @@ -79,7 +80,7 @@ def setInfo(self, key: str, value: "str | None") -> None: key = infoKeysAliasDict.get(key.lower(), key) self._info[key] = value - def getExtraInfos(self, excludeKeys: "list[str]") -> "odict": + def getExtraInfos(self, excludeKeys: list[str]) -> odict: """ excludeKeys: a list of (basic) info keys to be excluded returns an OrderedDict including the rest of info keys, diff --git a/pyglossary/glossary_types.py b/pyglossary/glossary_types.py index 0f9052193..cfa0b91db 100644 --- a/pyglossary/glossary_types.py +++ b/pyglossary/glossary_types.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import typing from collections.abc import ( Callable, @@ -26,15 +28,13 @@ "RawEntryType", ] -MultiStr: "TypeAlias" = "str | list[str]" +MultiStr: TypeAlias = "str | list[str]" # 3 different types in order: # - compressed # - uncompressed, without defiFormat # - uncompressed, with defiFormat -RawEntryType: "TypeAlias" = ( - "bytes |tuple[list[str], bytes] |tuple[list[str], bytes, str]" -) +RawEntryType: TypeAlias = "bytes |tuple[list[str], bytes] |tuple[list[str], bytes, str]" class EntryType(typing.Protocol): # noqa: PLR0904 @@ -55,7 +55,7 @@ def save(self, directory: str) -> str: ... def s_word(self) -> str: ... @property - def l_word(self) -> "list[str]": ... + def l_word(self) -> list[str]: ... @property def defi(self) -> str: ... @@ -112,7 +112,7 @@ def rawEntryCompress(self) -> bool: ... @rawEntryCompress.setter def rawEntryCompress(self, enable: bool) -> None: ... - def append(self, entry: "EntryType") -> None: ... + def append(self, entry: EntryType) -> None: ... def clear(self) -> None: ... @@ -122,7 +122,7 @@ def __iter__(self) -> "Iterator[EntryType]": ... def setSortKey( self, - namedSortKey: "NamedSortKey", + namedSortKey: NamedSortKey, sortEncoding: "str | None", writeOptions: "dict[str, Any]", ) -> None: ... @@ -158,7 +158,7 @@ def getInfo(self, key: str) -> str: ... def setInfo(self, key: str, value: str) -> None: ... - def getExtraInfos(self, excludeKeys: "list[str]") -> "OrderedDict": ... + def getExtraInfos(self, excludeKeys: list[str]) -> OrderedDict: ... @property def author(self) -> str: ... @@ -208,7 +208,7 @@ def addEntry(self, entry: EntryType) -> None: ... def newEntry( self, - word: "MultiStr", + word: MultiStr, defi: str, defiFormat: str = "", byteProgress: "tuple[int, int] | None" = None, diff --git a/pyglossary/glossary_v2.py b/pyglossary/glossary_v2.py index 8387bb376..517fc8bf5 100644 --- a/pyglossary/glossary_v2.py +++ b/pyglossary/glossary_v2.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import os import os.path @@ -89,7 +90,7 @@ __all__ = ["ConvertArgs", "Glossary", "GlossaryCommon"] -# sortKeyType = Callable[ +# SortKeyType = Callable[ # [[list[str]], # Any, # ] @@ -151,13 +152,13 @@ def clear(self) -> None: reader.close() except Exception: # noqa: PERF203 log.exception("") - self._readers: "list[Any]" = [] + self._readers: list[Any] = [] self._defiHasWordTitle = False - self._iter: "Iterator[EntryType] | None" = None - self._entryFilters: "list[EntryFilterType]" = [] - self._entryFiltersExtra: "list[EntryFilterType]" = [] - self._entryFiltersName: "set[str]" = set() + self._iter: Iterator[EntryType] | None = None + self._entryFilters: list[EntryFilterType] = [] + self._entryFiltersExtra: list[EntryFilterType] = [] + self._entryFiltersName: set[str] = set() self._sort = False self._filename = "" @@ -178,13 +179,13 @@ def __init__( GlossaryInfo.__init__(self) GlossaryProgress.__init__(self, ui=ui) self._config: "dict[str, Any]" = {} - self._data: "EntryListType" = EntryList( + self._data: EntryListType = EntryList( entryToRaw=self._entryToRaw, entryFromRaw=self._entryFromRaw, ) self._sqlite = False self._rawEntryCompress = False - self._cleanupPathList: "set[str]" = set() + self._cleanupPathList: set[str] = set() self._readOptions: dict[str, Any] | None = None self.clear() @@ -221,7 +222,7 @@ def cleanup(self) -> None: log.error(f"no such file or directory: {cleanupPath}") self._cleanupPathList = set() - def _dataEntryToRaw(self, entry: "DataEntry") -> "RawEntryType": + def _dataEntryToRaw(self, entry: DataEntry) -> RawEntryType: b_fpath = b"" if self.tmpDataDir: b_fpath = entry.save(self.tmpDataDir).encode("utf-8") @@ -234,7 +235,7 @@ def _dataEntryToRaw(self, entry: "DataEntry") -> "RawEntryType": return zlib_compress(pickle_dumps(tpl), level=9) return tpl - def _entryToRaw(self, entry: "EntryType") -> "RawEntryType": + def _entryToRaw(self, entry: EntryType) -> RawEntryType: """ Return a tuple (word, defi) or (word, defi, defiFormat) where both word and defi might be string or list of strings. @@ -254,7 +255,7 @@ def _entryToRaw(self, entry: "EntryType") -> "RawEntryType": return tpl - def _entryFromRaw(self, rawEntryArg: "RawEntryType") -> "EntryType": + def _entryFromRaw(self, rawEntryArg: RawEntryType) -> EntryType: if isinstance(rawEntryArg, bytes): rawEntry = pickle_loads(zlib_decompress(rawEntryArg)) else: @@ -434,7 +435,7 @@ def _readersEntryGen(self) -> "Iterator[EntryType]": # no point of returning None entries anymore. def _applyEntryFiltersGen( self, - gen: "Iterator[EntryType]", + gen: Iterator[EntryType], ) -> "Iterator[EntryType]": entry: "EntryType | None" for entry in gen: @@ -576,16 +577,16 @@ def wordTitleStr( def getConfig(self, name: str, default: "str | None") -> "str | None": return self._config.get(name, default) - def addEntry(self, entry: "EntryType") -> None: + def addEntry(self, entry: EntryType) -> None: self._data.append(entry) def newEntry( self, - word: "MultiStr", + word: MultiStr, defi: str, defiFormat: str = "", byteProgress: "tuple[int, int] | None" = None, - ) -> "Entry": + ) -> Entry: """ Create and return a new entry object. @@ -604,7 +605,7 @@ def newEntry( byteProgress=byteProgress, ) - def newDataEntry(self, fname: str, data: bytes) -> "EntryType": + def newDataEntry(self, fname: str, data: bytes) -> EntryType: import uuid if self._readers: @@ -637,7 +638,7 @@ def _createReader( self, format: str, options: "dict[str, Any]", - ) -> "Any": + ) -> Any: readerClass = self.plugins[format].readerClass if readerClass is None: raise ReadError("_createReader: readerClass is None") @@ -680,7 +681,7 @@ def _validateReadoptions( ) del options[key] - def _openReader(self, reader: "Any", filename: str): + def _openReader(self, reader: Any, filename: str): # reader.open returns "Iterator[tuple[int, int]] | None" progressbar: bool = self.progressbar try: @@ -767,7 +768,7 @@ def _read( return True - def loadReader(self, reader: "Any") -> None: + def loadReader(self, reader: Any) -> None: """ Iterate over `reader` object and loads the whole data into self._data must call `reader.open(filename)` before calling this function. @@ -790,7 +791,7 @@ def _createWriter( self, format: str, options: "dict[str, Any]", - ) -> "Any": + ) -> Any: validOptions = self.formatsWriteOptions.get(format) if validOptions is None: raise WriteError(f"No write support for {format!r} format") @@ -849,7 +850,7 @@ def write( def _writeEntries( self, - writerList: "list[Any]", + writerList: list[Any], filename: str, ) -> None: writer = writerList[0] @@ -881,7 +882,7 @@ def _writeEntries( @staticmethod def _openWriter( - writer: "Any", + writer: Any, filename: str, ): try: @@ -982,7 +983,7 @@ def _switchToSQLite( @staticmethod def _checkSortFlag( - plugin: "PluginProp", + plugin: PluginProp, sort: "bool | None", ) -> bool: sortOnWrite = plugin.sortOnWrite @@ -1011,7 +1012,7 @@ def _checkSortFlag( def _resolveSortParams( self, args: ConvertArgs, - plugin: "PluginProp", + plugin: PluginProp, ) -> "tuple[bool, bool]": """ sortKeyName: see doc/sort-key.md. @@ -1063,7 +1064,7 @@ def _resolveSortParams( @staticmethod def _checkSortKey( - plugin: "PluginProp", + plugin: PluginProp, sortKeyName: "str | None", sortEncoding: "str | None", ) -> "tuple[NamedSortKey, str]": diff --git a/pyglossary/image_utils.py b/pyglossary/image_utils.py index c2d82066d..10d06de9f 100644 --- a/pyglossary/image_utils.py +++ b/pyglossary/image_utils.py @@ -37,7 +37,7 @@ def subFunc(m: "re.Match[str]") -> str: defi = re_inline_image.sub(subFunc, defi) - images: "list[tuple[str, str]]" = [] + images: list[tuple[str, str]] = [] for imgFname, imgData in imageDataDict.items(): imgPath = join(outDir, imgFname) with open(imgPath, mode="wb") as _file: diff --git a/pyglossary/io_utils.py b/pyglossary/io_utils.py index a0d2494e7..b997f4325 100644 --- a/pyglossary/io_utils.py +++ b/pyglossary/io_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io from typing import TYPE_CHECKING @@ -72,10 +74,10 @@ def __next__(self) -> bytes: def readline(self, size: "int | None" = -1) -> bytes: raise NotImplementedError - def readlines(self, hint: int = -1) -> "list[bytes]": + def readlines(self, hint: int = -1) -> list[bytes]: raise NotImplementedError - def writelines(self, lines: "list[bytes]") -> None: # type: ignore + def writelines(self, lines: list[bytes]) -> None: # type: ignore raise NotImplementedError @@ -132,7 +134,7 @@ def readinto1(self, buffer) -> "io.BufferedIOBase": raise NotImplementedError # data: "bytearray|memoryview|array[Any]|io.mmap|io._CData|io.PickleBuffer" - def write(self, data: "bytes") -> int: # type: ignore + def write(self, data: bytes) -> int: # type: ignore raise NotImplementedError def __iter__(self) -> "Iterator[str]": # type: ignore @@ -144,10 +146,10 @@ def __next__(self) -> str: # type: ignore def readline(self, size: "int | None" = -1) -> str: # type: ignore raise NotImplementedError - def readlines(self, hint: int = -1) -> "list[str]": # type: ignore + def readlines(self, hint: int = -1) -> list[str]: # type: ignore raise NotImplementedError - def writelines(self, lines: "list[str]") -> None: # type: ignore + def writelines(self, lines: list[str]) -> None: # type: ignore raise NotImplementedError diff --git a/pyglossary/iter_utils.py b/pyglossary/iter_utils.py index 1e9d2d03f..8b40d49d3 100644 --- a/pyglossary/iter_utils.py +++ b/pyglossary/iter_utils.py @@ -1,16 +1,13 @@ # Copyright (c) 2019 Saeed Rasooli # Copyright (c) 2012 Erik Rose - # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: - # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -18,6 +15,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from typing import TYPE_CHECKING @@ -29,12 +27,12 @@ # from https://github.com/erikrose/more-itertools -def unique_everseen(iterable: "Iterable") -> "Iterator": +def unique_everseen(iterable: Iterable) -> Iterator: """List unique elements, preserving order. Remember all elements ever seen.""" from itertools import filterfalse # unique_everseen('AAAABBBCCDAABBB') --> A B C D - seen: "set[Any]" = set() + seen: set[Any] = set() seen_add = seen.add for element in filterfalse(seen.__contains__, iterable): seen_add(element) diff --git a/pyglossary/json_utils.py b/pyglossary/json_utils.py index ac8ce9166..5d5d16cd9 100644 --- a/pyglossary/json_utils.py +++ b/pyglossary/json_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json from collections import OrderedDict from typing import TYPE_CHECKING @@ -7,7 +9,7 @@ __all__ = ["dataToPrettyJson", "jsonToData", "jsonToOrderedData"] -JsonEncodable: "TypeAlias" = "dict | list" +JsonEncodable: TypeAlias = "dict | list" # OrderedDict is also subclass of Dict, issubclass(OrderedDict, Dict) is True @@ -24,11 +26,11 @@ def dataToPrettyJson( ) -def jsonToData(st: "AnyStr") -> JsonEncodable: +def jsonToData(st: AnyStr) -> JsonEncodable: return json.loads(st) -def jsonToOrderedData(text: str) -> "OrderedDict": +def jsonToOrderedData(text: str) -> OrderedDict: return json.JSONDecoder( object_pairs_hook=OrderedDict, ).decode(text) diff --git a/pyglossary/langs/__init__.py b/pyglossary/langs/__init__.py index 21e1604ed..aa3c99a8c 100644 --- a/pyglossary/langs/__init__.py +++ b/pyglossary/langs/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import logging from os.path import join @@ -10,8 +12,8 @@ class Lang: def __init__( self, - codes: "list[str]", - names: "list[str]", + codes: list[str], + names: list[str], titleTag: str = "b", rtl: int = 0, ) -> None: @@ -33,11 +35,11 @@ def __str__(self) -> str: return f"Lang({self._codes + self._names})" @property - def codes(self) -> "list[str]": + def codes(self) -> list[str]: return self._codes @property - def names(self) -> "list[str]": + def names(self) -> list[str]: return self._names @property @@ -58,7 +60,7 @@ def rtl(self) -> int: class LangDict(dict): - def _addLang(self, lang: "Lang") -> None: + def _addLang(self, lang: Lang) -> None: for key in lang.codes: if key in self: log.error(f"duplicate language code: {key}") diff --git a/pyglossary/langs/writing_system.py b/pyglossary/langs/writing_system.py index 431b80f3a..8549cc9b3 100644 --- a/pyglossary/langs/writing_system.py +++ b/pyglossary/langs/writing_system.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unicodedata from typing import Literal, NamedTuple @@ -362,7 +364,7 @@ class WritingSystem(NamedTuple): titleTag="big", ), WritingSystem( - name="Khudabadi", # aka: "Khudawadi", "Sindhi" + name="Khudabadi", # aka: Khudawadi, "Sindhi" iso=[(318, "Sind")], unicode=["KHUDAWADI"], titleTag="big", diff --git a/pyglossary/option.py b/pyglossary/option.py index fb79bd7fd..0f90a5afa 100644 --- a/pyglossary/option.py +++ b/pyglossary/option.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import logging import re @@ -22,7 +23,7 @@ log = logging.getLogger("pyglossary") -def optionFromDict(data: "dict[str, Any]") -> "Option": +def optionFromDict(data: "dict[str, Any]") -> Option: className = data.pop("class") optClass: type if className == "Option": @@ -38,7 +39,7 @@ class Option: classes: "dict[str, type]" = {} @classmethod - def register(cls: "type[Option]", optClass: "type") -> "type": + def register(cls: type[Option], optClass: type) -> type: cls.classes[optClass.__name__] = optClass return optClass @@ -46,7 +47,7 @@ def __init__( # noqa: PLR0913 self, typ: str, customValue: bool = False, - values: "list[Any] | None" = None, + values: list[Any] | None = None, allowNone: bool = False, comment: str = "", multiline: bool = False, @@ -108,7 +109,7 @@ def evaluate(cls, raw: str) -> "tuple[Any, bool]": return None, True return raw, True - def validate(self, value: "Any") -> bool: + def validate(self, value: Any) -> bool: if not self.customValue: if not self.values: log.error( @@ -140,7 +141,7 @@ def __init__( allowNone: bool = False, **kwargs, # noqa: ANN003 ) -> None: - values: "list[bool | None]" = [False, True] + values: list[bool | None] = [False, True] if allowNone: values.append(None) Option.__init__( @@ -190,7 +191,7 @@ def __init__( **kwargs, ) - def validate(self, value: "Any") -> bool: + def validate(self, value: Any) -> bool: if not self.customValue: if not self.values: log.error( @@ -448,7 +449,7 @@ def groupValues(self) -> "dict[str, Any] | None": from collections import OrderedDict groups: "dict[str, list[str]]" = OrderedDict() - others: "list[str]" = [] + others: list[str] = [] for value in self.values or []: cats = self.re_category.findall(value) if not cats: diff --git a/pyglossary/os_utils.py b/pyglossary/os_utils.py index 20682ab9f..f4ceb3f28 100644 --- a/pyglossary/os_utils.py +++ b/pyglossary/os_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import shutil @@ -119,7 +121,7 @@ def runDictzip(filename: str | Path, method: str = "") -> None: def _rmtreeError( - _func: "Callable", + _func: Callable, _direc: str, exc_info: "tuple[type, Exception, types.TracebackType] | None", ) -> None: diff --git a/pyglossary/plugin_lib/dictdlib.py b/pyglossary/plugin_lib/dictdlib.py index 09d755fd0..9b72be3e4 100644 --- a/pyglossary/plugin_lib/dictdlib.py +++ b/pyglossary/plugin_lib/dictdlib.py @@ -87,7 +87,7 @@ def sortNormalize(inp: str) -> str: return st2.upper() + "\0" + inp.upper() -def sortKey(x: str) -> "list[str]": +def sortKey(x: str) -> list[str]: """Emulate sort -df.""" return x.split("\0") @@ -287,7 +287,7 @@ def setLongInfo(self, longinfo: str) -> None: def addEntry( self, s_defi: str, - headwords: "list[str]", + headwords: list[str], ) -> None: r""" Writes an entry. defstr holds the content of the definition. @@ -322,12 +322,11 @@ def finish(self, dosort: bool = True) -> None: if dosort: self.update("Sorting index: converting") - indexList: "list[str]" = [] - for word, defs in self.indexEntries.items(): - for thisdef in defs: - indexList.append( - f"{word}\t{b64_encode(thisdef[0])}\t{b64_encode(thisdef[1])}", - ) + indexList: list[str] = [ + f"{word}\t{b64_encode(thisdef[0])}\t{b64_encode(thisdef[1])}" + for word, defs in self.indexEntries.items() + for thisdef in defs + ] self.update(" mapping") @@ -392,7 +391,7 @@ def getDef(self, word: str) -> "list[bytes]": matching definitions. This is an *exact* match, not a case-insensitive one. Returns [] if word is not in the dictionary. """ - retval: "list[bytes]" = [] + retval: list[bytes] = [] if not self.hasDef(word): return retval for start, length in self.indexEntries[word]: diff --git a/pyglossary/plugin_manager.py b/pyglossary/plugin_manager.py index 5fd79341c..23f780ab8 100644 --- a/pyglossary/plugin_manager.py +++ b/pyglossary/plugin_manager.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import logging import os @@ -50,17 +51,17 @@ class DetectedFormat(NamedTuple): class PluginManager: plugins: "dict[str, PluginProp]" = {} pluginByExt: "dict[str, PluginProp]" = {} - loadedModules: "set[str]" = set() + loadedModules: set[str] = set() formatsReadOptions: "dict[str, dict[str, Any]]" = {} formatsWriteOptions: "dict[str, dict[str, Any]]" = {} # for example formatsReadOptions[format][optName] gives you the default value - readFormats: "list[str]" = [] - writeFormats: "list[str]" = [] + readFormats: list[str] = [] + writeFormats: list[str] = [] @classmethod - def loadPluginsFromJson(cls: "type[PluginManager]", jsonPath: str) -> None: + def loadPluginsFromJson(cls: type[PluginManager], jsonPath: str) -> None: import json with open(jsonPath, encoding="utf-8") as _file: @@ -76,7 +77,7 @@ def loadPluginsFromJson(cls: "type[PluginManager]", jsonPath: str) -> None: @classmethod def loadPlugins( - cls: "type[PluginManager]", + cls: type[PluginManager], directory: str, skipDisabled: bool = True, ) -> None: @@ -105,7 +106,7 @@ def loadPlugins( @classmethod def _loadPluginByDict( - cls: "type[PluginManager]", + cls: type[PluginManager], attrs: "dict[str, Any]", modulePath: str, ) -> None: @@ -144,7 +145,7 @@ def _loadPluginByDict( @classmethod def _loadPlugin( - cls: "type[PluginManager]", + cls: type[PluginManager], moduleName: str, skipDisabled: bool = True, ) -> None: @@ -191,7 +192,7 @@ def _loadPlugin( @classmethod def _findPlugin( - cls: "type[PluginManager]", + cls: type[PluginManager], query: str, ) -> "PluginProp | None": """Find plugin by name or extension.""" @@ -205,11 +206,11 @@ def _findPlugin( @classmethod def detectInputFormat( - cls: "type[PluginManager]", + cls: type[PluginManager], filename: str, format: str = "", quiet: bool = False, # noqa: ARG003 - ) -> "DetectedFormat": + ) -> DetectedFormat: filenameOrig = filename _, filename, ext, compression = splitFilenameExt(filename) @@ -236,7 +237,7 @@ def detectInputFormat( @classmethod def _outputPluginByFormat( - cls: "type[PluginManager]", + cls: type[PluginManager], format: str, ) -> "tuple[PluginProp | None, str]": if not format: @@ -257,13 +258,13 @@ def _outputPluginByFormat( # PLR0912 Too many branches (14 > 12) @classmethod def detectOutputFormat( # noqa: PLR0912, PLR0913, C901 - cls: "type[PluginManager]", + cls: type[PluginManager], filename: str = "", format: str = "", inputFilename: str = "", quiet: bool = False, # noqa: ARG003, TODO: remove addExt: bool = False, - ) -> "DetectedFormat": + ) -> DetectedFormat: from os.path import splitext # Ugh, mymy @@ -323,7 +324,7 @@ def detectOutputFormat( # noqa: PLR0912, PLR0913, C901 @classmethod def init( - cls: "type[PluginManager]", + cls: type[PluginManager], usePluginsJson: bool = True, skipDisabledPlugins: bool = True, ) -> None: diff --git a/pyglossary/plugin_prop.py b/pyglossary/plugin_prop.py index 0ce0fa55c..2acd7fffc 100644 --- a/pyglossary/plugin_prop.py +++ b/pyglossary/plugin_prop.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import logging from collections import OrderedDict as odict @@ -54,7 +55,7 @@ def optionsPropFromDict( return props -def sortOnWriteFromStr(sortOnWriteStr: "str | None") -> "StrWithDesc": +def sortOnWriteFromStr(sortOnWriteStr: "str | None") -> StrWithDesc: if sortOnWriteStr is None: return DEFAULT_NO return flagsByName[sortOnWriteStr] @@ -93,10 +94,10 @@ class PluginProp: # noqa: PLR0904 ] def __init__(self) -> None: - self._mod: "Any" - self._Reader: "Any" + self._mod: Any + self._Reader: Any self._ReaderLoaded: bool - self._Writer: "Any" + self._Writer: Any self._WriterLoaded: bool self._moduleName: str @@ -105,7 +106,7 @@ def __init__(self) -> None: self._lname: str self._name: str self._description: str - self._extensions: "list[str]" + self._extensions: list[str] self._extensionCreate: str self._singleFile: bool self._optionsProp: "dict[str, Option]" @@ -115,13 +116,13 @@ def __init__(self) -> None: self._canWrite: bool self._readOptions: "dict[str, Any]" self._writeOptions: "dict[str, Any]" - self._readCompressions: "list[str]" + self._readCompressions: list[str] self._readDepends: "dict[str, str]" self._writeDepends: "dict[str, str]" @classmethod def fromDict( - cls: "type", + cls: type, attrs: "dict[str, Any]", modulePath: str, ) -> None: @@ -155,7 +156,7 @@ def fromDict( return self @classmethod - def fromModule(cls: "type", mod: "Any") -> "PluginProp": + def fromModule(cls: type, mod: Any) -> PluginProp: self = cls() self._mod = mod self._Reader = None @@ -198,7 +199,7 @@ def enable(self) -> bool: return self._enable @property - def module(self) -> "Any": + def module(self) -> Any: if self._mod is not None: return self._mod moduleName = self._moduleName @@ -237,7 +238,7 @@ def description(self) -> str: return self._description @property - def extensions(self) -> "list[str]": + def extensions(self) -> list[str]: return self._extensions @property @@ -304,7 +305,7 @@ def canWrite(self) -> bool: return self._canWrite @staticmethod - def _getOptionAttrNamesFromClass(rwclass: "type") -> "list[str]": + def _getOptionAttrNamesFromClass(rwclass: type) -> list[str]: nameList = [] for cls in (*rwclass.__bases__, rwclass): @@ -320,7 +321,7 @@ def _getOptionAttrNamesFromClass(rwclass: "type") -> "list[str]": return nameList - def _getOptionsFromClass(self, rwclass: "type") -> "dict[str, Any]": + def _getOptionsFromClass(self, rwclass: type) -> "dict[str, Any]": optionsProp = self.optionsProp options = odict() if rwclass is None: @@ -360,7 +361,7 @@ def getWriteOptions(self) -> "dict[str, Any]": return self._writeOptions @property - def readCompressions(self) -> "list[str]": + def readCompressions(self) -> list[str]: if self._readCompressions is None: self._readCompressions = getattr(self.readerClass, "compressions", []) return self._readCompressions @@ -489,13 +490,13 @@ def checkWriterClass(self) -> bool: return True - # def _getReadExtraOptions(self) -> "list[str]": # noqa: F811 + # def _getReadExtraOptions(self) -> list[str]: # noqa: F811 # cls = self.readerClass # if cls is None: # return [] # return self.__class__.getExtraOptionsFromFunc(cls.open, self.name) - # def _getWriteExtraOptions(self) -> "list[str]": # noqa: F811 + # def _getWriteExtraOptions(self) -> list[str]: # noqa: F811 # cls = self.writerClass # if cls is None: # return [] @@ -503,10 +504,10 @@ def checkWriterClass(self) -> bool: # @classmethod # def getExtraOptionsFromFunc( - # cls: "type", - # func: "Callable", + # cls: type, + # func: Callable, # format: str, - # ) -> "list[str]": + # ) -> list[str]: # import inspect # extraOptNames = [] diff --git a/pyglossary/plugins/aard2_slob.py b/pyglossary/plugins/aard2_slob.py index 5178ce9a6..58d45bdc1 100644 --- a/pyglossary/plugins/aard2_slob.py +++ b/pyglossary/plugins/aard2_slob.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import os import re @@ -10,9 +11,9 @@ from collections.abc import Generator, Iterator from pyglossary import slob + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.core import cacheDir, log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.option import ( BoolOption, FileSizeOption, @@ -116,7 +117,7 @@ class Reader: "icu": "PyICU", # >=1.5 } - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() self._re_bword = re.compile( @@ -329,7 +330,7 @@ def finish(self) -> None: log.info(f"Finalizing slob file took {perf_counter() - t0:.1f} seconds") self._slobWriter = None - def addDataEntry(self, entry: "EntryType") -> None: + def addDataEntry(self, entry: EntryType) -> None: slobWriter = self._slobWriter if slobWriter is None: raise ValueError("slobWriter is None") @@ -349,7 +350,7 @@ def addDataEntry(self, entry: "EntryType") -> None: return slobWriter.add(content, key, content_type=content_type) - def addEntry(self, entry: "EntryType") -> None: + def addEntry(self, entry: EntryType) -> None: words = entry.l_word b_defi = entry.defi.encode("utf-8") _ctype = self._content_type diff --git a/pyglossary/plugins/abc_medical_notes.py b/pyglossary/plugins/abc_medical_notes.py index eaadf995e..e58396500 100644 --- a/pyglossary/plugins/abc_medical_notes.py +++ b/pyglossary/plugins/abc_medical_notes.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html from typing import TYPE_CHECKING @@ -43,7 +44,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() diff --git a/pyglossary/plugins/almaany.py b/pyglossary/plugins/almaany.py index 79346da31..0e4255a62 100644 --- a/pyglossary/plugins/almaany.py +++ b/pyglossary/plugins/almaany.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html from typing import TYPE_CHECKING @@ -42,7 +43,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() diff --git a/pyglossary/plugins/appledict/_content.py b/pyglossary/plugins/appledict/_content.py index 0253d41ea..325865ec9 100644 --- a/pyglossary/plugins/appledict/_content.py +++ b/pyglossary/plugins/appledict/_content.py @@ -15,13 +15,13 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. - # FIXME: # MDX-specific parts should be isolated and moved to MDX Reader # and parts that are specific to one glossary # (like Oxford_Advanced_English-Chinese_Dictionary_9th_Edition.mdx) # should be moved to separate modules (like content processors) and enabled # per-glossary (by title or something else) +from __future__ import annotations import logging import re @@ -55,7 +55,7 @@ def prepare_content( title: "str | None", body: str, - BeautifulSoup: "Any", + BeautifulSoup: Any, ) -> str: # heavily integrated with output of dsl reader plugin! # and with xdxf also. @@ -169,7 +169,7 @@ def _prepare_onclick(soup) -> None: def prepare_content_with_soup( # noqa: PLR0912 title: "str | None", body: str, - BeautifulSoup: "Any", + BeautifulSoup: Any, ) -> str: soup = BeautifulSoup.BeautifulSoup(body, features="lxml") # difference between "lxml" and "html.parser" diff --git a/pyglossary/plugins/appledict/_dict.py b/pyglossary/plugins/appledict/_dict.py index c398357ec..80ddc82c3 100644 --- a/pyglossary/plugins/appledict/_dict.py +++ b/pyglossary/plugins/appledict/_dict.py @@ -17,6 +17,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import logging import string @@ -56,7 +57,7 @@ def id_generator() -> "Iterator[str]": cnt += 1 -def quote_string(value: str, BeautifulSoup: "Any") -> str: +def quote_string(value: str, BeautifulSoup: Any) -> str: if BeautifulSoup: return BeautifulSoup.dammit.EntitySubstitution.substitute_xml( value, @@ -91,9 +92,9 @@ def indexes_generator( def generate_indexes( title: str, - alts: "list[str]", + alts: list[str], content: str, - BeautifulSoup: "Any", + BeautifulSoup: Any, ) -> str: indexes = [title] indexes.extend(alts) diff --git a/pyglossary/plugins/appledict/_normalize.py b/pyglossary/plugins/appledict/_normalize.py index c6cf39e85..41f1a6f37 100644 --- a/pyglossary/plugins/appledict/_normalize.py +++ b/pyglossary/plugins/appledict/_normalize.py @@ -17,6 +17,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import re from typing import Any @@ -110,7 +111,7 @@ def truncate(text: str, length: int = 449) -> str: return text # noqa: RET504 -def title(title: str, BeautifulSoup: "Any") -> str: +def title(title: str, BeautifulSoup: Any) -> str: """Strip double quotes and html tags.""" if BeautifulSoup: title = title.replace("\xef\xbb\xbf", "") @@ -135,7 +136,7 @@ def title_long(s: str) -> str: Example: ------- - title_long("str[ing]") -> "string". + title_long("str[ing]") -> string. """ return s.replace("[", "").replace("]", "") @@ -147,7 +148,7 @@ def title_short(s: str) -> str: Example: ------- - title_short("str[ing]") -> "str". + title_short("str[ing]") -> str. """ return spaces(re_title_short.sub("", s)) diff --git a/pyglossary/plugins/appledict/indexes/ru.py b/pyglossary/plugins/appledict/indexes/ru.py index 1f9f70903..f7c824c97 100644 --- a/pyglossary/plugins/appledict/indexes/ru.py +++ b/pyglossary/plugins/appledict/indexes/ru.py @@ -16,6 +16,8 @@ # GNU General Public License for more details. """Russian indexes based on pymorphy.""" +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -38,20 +40,20 @@ morphy = pymorphy2.MorphAnalyzer() -def ru(titles: "Sequence[str]", _: str) -> "set[str]": +def ru(titles: Sequence[str], _: str) -> "set[str]": """ Give a set of all declines, cases and other forms of word `title`. note that it works only if title is one word. """ - indexes: "set[str]" = set() - indexes_norm: "set[str]" = set() + indexes: set[str] = set() + indexes_norm: set[str] = set() for title in titles: # in-place modification _ru(title, indexes, indexes_norm) return indexes -def _ru(title: str, a: "set[str]", a_norm: "set[str]") -> None: +def _ru(title: str, a: set[str], a_norm: "set[str]") -> None: # uppercase abbreviature if title.isupper(): return diff --git a/pyglossary/plugins/appledict/indexes/zh.py b/pyglossary/plugins/appledict/indexes/zh.py index 5acec475a..7b62ead18 100644 --- a/pyglossary/plugins/appledict/indexes/zh.py +++ b/pyglossary/plugins/appledict/indexes/zh.py @@ -39,7 +39,7 @@ nonHieroglyphPattern = re.compile(r"[^\u4e00-\u9fff]") -def zh(titles: "Sequence[str]", content: str) -> "set[str]": +def zh(titles: Sequence[str], content: str) -> "set[str]": """ Chinese indexes. diff --git a/pyglossary/plugins/appledict/jing/main.py b/pyglossary/plugins/appledict/jing/main.py index 20fd5513d..9408f8df5 100644 --- a/pyglossary/plugins/appledict/jing/main.py +++ b/pyglossary/plugins/appledict/jing/main.py @@ -22,7 +22,7 @@ class JingTestError(subprocess.CalledProcessError): def __init__( self, returncode: int, - cmd: "list[str]", + cmd: list[str], output: bytes, ) -> None: super().__init__(returncode, cmd, output) diff --git a/pyglossary/plugins/appledict_bin/__init__.py b/pyglossary/plugins/appledict_bin/__init__.py index 48b7cf849..7c99e578b 100644 --- a/pyglossary/plugins/appledict_bin/__init__.py +++ b/pyglossary/plugins/appledict_bin/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # Copyright © 2019 Saeed Rasooli (ilius) # # This program is a free software; you can redistribute it and/or modify @@ -13,6 +12,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import os import re @@ -51,6 +51,7 @@ HtmlProcessingInstruction, ) + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element from .appledict_properties import AppleDictProperties @@ -60,7 +61,6 @@ from pyglossary import core from pyglossary.apple_utils import substituteAppleCSS from pyglossary.core import log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.io_utils import nullBinaryIO from pyglossary.option import BoolOption, Option @@ -144,7 +144,7 @@ def tostring( method="html", ).decode("utf-8") - def fixLink(self, a: "Element") -> "Element": + def fixLink(self, a: Element) -> Element: href = a.attrib.get("href", "") if href.startswith("x-dictionary:d:"): @@ -335,7 +335,7 @@ def close(self) -> None: def _getDefi( self, - entryElem: "Element", + entryElem: Element, ) -> str: if not self._html: # FIXME: this produces duplicate text for Idioms.dictionary, see #301 @@ -396,7 +396,7 @@ def getChunkLenOffset( def createEntry( self, entryBytes: bytes, - articleAddress: "ArticleAddress", + articleAddress: ArticleAddress, ) -> "EntryType | None": # 1. create and validate XML of the entry's body entryRoot = self.convertEntryBytesToXml(entryBytes) @@ -415,7 +415,7 @@ def createEntry( words = [word] - keyDataList: "list[KeyData]" = [ + keyDataList: list[KeyData] = [ KeyData.fromRaw(rawKeyData, keyTextFieldOrder) for rawKeyData in self._keyTextData.get(articleAddress, []) ] @@ -488,7 +488,7 @@ def readEntryIds(self) -> None: def setKeyTextData( self, morphoFilePath: str, - properties: "AppleDictProperties", + properties: AppleDictProperties, ) -> "Iterator[tuple[int, int]]": """ Prepare `KeyText.data` file for extracting morphological data. @@ -539,7 +539,7 @@ def readKeyTextData( # noqa: PLR0912 buff: "io.BufferedIOBase", bufferOffset: int, bufferLimit: int, - properties: "AppleDictProperties", + properties: AppleDictProperties, ) -> "Iterator[tuple[int, int]]": """ Returns an iterator/generator for the progress @@ -615,7 +615,7 @@ def readKeyTextData( # noqa: PLR0912 ) return - keyTextFields: "list[str]" = [] + keyTextFields: list[str] = [] while buff.tell() < next_lexeme_offset: word_form_len = read_2_bytes_here(buff) if word_form_len == 0: @@ -707,7 +707,7 @@ def __iter__(self) -> Iterator[EntryType]: def yieldEntryBytes( self, body_file: "io.BufferedIOBase", - properties: "AppleDictProperties", + properties: AppleDictProperties, ) -> "Iterator[tuple[bytes, ArticleAddress]]": fileDataOffset, fileLimit = guessFileOffsetLimit(body_file) sectionOffset = fileDataOffset diff --git a/pyglossary/plugins/appledict_bin/appledict_properties.py b/pyglossary/plugins/appledict_bin/appledict_properties.py index 64636c326..cad86038b 100644 --- a/pyglossary/plugins/appledict_bin/appledict_properties.py +++ b/pyglossary/plugins/appledict_bin/appledict_properties.py @@ -12,6 +12,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations from dataclasses import dataclass @@ -20,11 +21,11 @@ @dataclass(slots=True, frozen=True) class AppleDictProperties: - # in plist file: "IDXDictionaryVersion" + # in plist file: IDXDictionaryVersion # values := (1 | 2 | 3) format_version: int - # in plist file: "HeapDataCompressionType" values := (absent | 1 | 2) + # in plist file: HeapDataCompressionType values := (absent | 1 | 2) body_compression_type: int # in plist file: for field with "IDXDataFieldName" equal "DCSExternalBodyID" @@ -35,11 +36,11 @@ class AppleDictProperties: # 'TrieAuxiliaryDataOptions' -> 'HeapDataCompressionType' key_text_compression_type: int - # in plist file: "IDXIndexDataFields" / "IDXFixedDataFields" + # in plist file: IDXIndexDataFields / "IDXFixedDataFields" # Example: ["DCSPrivateFlag"] key_text_fixed_fields: list[str] - # in plist file: "IDXIndexDataFields" / "IDXVariableDataFields" + # in plist file: IDXIndexDataFields / "IDXVariableDataFields" # Example: ["DCSKeyword", "DCSHeadword", "DCSEntryTitle", # "DCSAnchor", "DCSYomiWord"] key_text_variable_fields: list[str] diff --git a/pyglossary/plugins/appledict_bin/key_data.py b/pyglossary/plugins/appledict_bin/key_data.py index 86697ac5e..b7e4d209a 100644 --- a/pyglossary/plugins/appledict_bin/key_data.py +++ b/pyglossary/plugins/appledict_bin/key_data.py @@ -12,6 +12,8 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations + import typing __all__ = ["KeyData", "RawKeyData"] @@ -105,7 +107,7 @@ def toDict(self) -> "dict[str, typing.Any]": } @staticmethod - def fromRaw(rawKeyData: RawKeyData, keyTextFieldOrder: "list[str]") -> "KeyData": + def fromRaw(rawKeyData: RawKeyData, keyTextFieldOrder: list[str]) -> KeyData: priority, parentalControl, keyTextFields = rawKeyData keyword = "" headword = "" diff --git a/pyglossary/plugins/ayandict_sqlite.py b/pyglossary/plugins/ayandict_sqlite.py index edab3db80..5cc170cce 100644 --- a/pyglossary/plugins/ayandict_sqlite.py +++ b/pyglossary/plugins/ayandict_sqlite.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations from collections.abc import Generator, Iterator from typing import ( @@ -51,7 +52,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() @@ -107,7 +108,7 @@ def close(self) -> None: class Writer: _fuzzy: int = True - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() @@ -230,7 +231,7 @@ def addFuzzy(self, _id: int, terms: list[str]) -> None: if cur is None: raise ValueError("cur is None") for term in terms: - subs: "set[str]" = set() + subs: set[str] = set() for word in term.split(" "): eword = "\n" + word subs.update(eword[i : i + 3] for i in range(len(eword) - 2)) diff --git a/pyglossary/plugins/babylon_bgl/bgl_pos.py b/pyglossary/plugins/babylon_bgl/bgl_pos.py index 6334fd1f6..98294420d 100644 --- a/pyglossary/plugins/babylon_bgl/bgl_pos.py +++ b/pyglossary/plugins/babylon_bgl/bgl_pos.py @@ -20,6 +20,8 @@ # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations + __all__ = ["partOfSpeechByCode"] partOfSpeechByCode = { diff --git a/pyglossary/plugins/babylon_bgl/bgl_reader.py b/pyglossary/plugins/babylon_bgl/bgl_reader.py index 134489d3f..e428f7ed2 100644 --- a/pyglossary/plugins/babylon_bgl/bgl_reader.py +++ b/pyglossary/plugins/babylon_bgl/bgl_reader.py @@ -20,6 +20,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import io import os @@ -341,7 +342,7 @@ class BglReader: selected encoding, so the user may fix the encoding if needed. """ - def __init__(self, glos: "GlossaryType") -> None: # no more arguments + def __init__(self, glos: GlossaryType) -> None: # no more arguments self._glos = glos self._filename = "" self.info = odict() @@ -565,7 +566,7 @@ def __del__(self) -> None: log.debug(f"BGL: unknown html entity: {entity}") # returns False if error - def readBlock(self, block: "Block") -> bool: + def readBlock(self, block: Block) -> bool: block.offset = self.file.tell() length = self.readBytes(1) if length == -1: @@ -617,7 +618,7 @@ def readBytes(self, num: int) -> int: return -1 return uintFromBytes(buf) - def readType0(self, block: "Block") -> bool: + def readType0(self, block: Block) -> bool: code = block.data[0] if code == 2: # this number is vary close to self.bgl_numEntries, @@ -634,7 +635,7 @@ def readType0(self, block: "Block") -> bool: return False return True - def readType2(self, block: "Block") -> "EntryType | None": + def readType2(self, block: Block) -> "EntryType | None": """ Process type 2 block. @@ -679,7 +680,7 @@ def readType2(self, block: "Block") -> "EntryType | None": b_data, ) - def readType3(self, block: "Block") -> None: + def readType3(self, block: Block) -> None: """ Reads block with type 3, and updates self.info returns None. @@ -760,7 +761,7 @@ def detectEncoding(self) -> None: # noqa: PLR0912 else: self.targetEncoding = self.defaultEncoding - def logUnknownBlock(self, block: "Block") -> None: + def logUnknownBlock(self, block: Block) -> None: log.debug( f"Unknown block: type={block.type}" f", number={self.numBlocks}" @@ -830,7 +831,7 @@ def __iter__(self) -> "Iterator[EntryType]": # noqa: PLR0912 def readEntryWord( self, - block: "Block", + block: Block, pos: int, ) -> "EntryWordData | None": """ @@ -878,7 +879,7 @@ def readEntryWord( def readEntryDefi( self, - block: "Block", + block: Block, pos: int, word: EntryWordData, ) -> "tuple[bool, int | None, bytes | None, bytes | None]": @@ -922,7 +923,7 @@ def readEntryDefi( def readEntryAlts( self, - block: "Block", + block: Block, pos: int, word: EntryWordData, ) -> "tuple[bool, int | None, list[str] | None]": @@ -964,7 +965,7 @@ def readEntryAlts( def readEntry_Type11( self, - block: "Block", + block: Block, ) -> "tuple[bool, str | None, list[str] | None, str | None]": """Return (succeed, u_word, u_alts, u_defi).""" Err = (False, None, None, None) diff --git a/pyglossary/plugins/cc_kedict.py b/pyglossary/plugins/cc_kedict.py index 0b10b1e58..8c272982e 100644 --- a/pyglossary/plugins/cc_kedict.py +++ b/pyglossary/plugins/cc_kedict.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # mypy: ignore-errors +from __future__ import annotations from collections.abc import Callable, Iterator from io import BytesIO @@ -58,7 +59,7 @@ class YamlReader(TextGlossaryReader): def __init__( # noqa: PLR0913 self, - glos: "GlossaryType", + glos: GlossaryType, spellKey: str = "", posKey: str = "", synsKey: str = "", @@ -97,8 +98,8 @@ def fixInfoWord(cls, _word: str) -> str: @staticmethod def _makeList( hf: "lxml.etree.htmlfile", - input_objects: "list[Any]", - processor: "Callable", + input_objects: list[Any], + processor: Callable, single_prefix: "str | None" = None, skip_single: bool = True, ) -> None: @@ -122,7 +123,7 @@ def _makeList( def _processExample( # noqa: PLR6301 self, hf: "lxml.etree.htmlfile", - exampleDict: "dict", + exampleDict: dict, _count: int, ) -> None: from lxml import etree as ET @@ -148,7 +149,7 @@ def _processExample( # noqa: PLR6301 def _processDef( self, hf: "lxml.etree.htmlfile", - defDict: "dict", + defDict: dict, count: int, ) -> None: from lxml import etree as ET @@ -183,7 +184,7 @@ def _processNote( # noqa: PLR6301 def _processEntry( self, hf: "lxml.etree.htmlfile", - edict: "dict", + edict: dict, ) -> None: from lxml import etree as ET @@ -270,7 +271,7 @@ def _createEntry( defi = f.getvalue().decode("utf-8") return word, defi, None - def nextBlock(self) -> "EntryType": + def nextBlock(self) -> EntryType: if not self._file: raise StopIteration lines = [] @@ -301,7 +302,7 @@ class Reader: "lxml": "lxml", } - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._yaml = YamlReader( glos, diff --git a/pyglossary/plugins/crawler_dir.py b/pyglossary/plugins/crawler_dir.py index 74168126e..a9473f0ee 100644 --- a/pyglossary/plugins/crawler_dir.py +++ b/pyglossary/plugins/crawler_dir.py @@ -1,15 +1,16 @@ # mypy: ignore-errors +from __future__ import annotations from collections.abc import Generator, Iterator from hashlib import sha1 from os import listdir, makedirs from os.path import dirname, isdir, isfile, join, splitext +from typing import TYPE_CHECKING from pyglossary.compression import ( compressionOpenFunc, ) from pyglossary.core import log -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.option import ( Option, StrOption, @@ -19,6 +20,9 @@ splitByBarUnescapeNTB, ) +if TYPE_CHECKING: + from pyglossary.glossary_types import EntryType, GlossaryType + __all__ = [ "Reader", "Writer", @@ -158,7 +162,7 @@ def close(self) -> None: def __len__(self) -> int: return self._wordCount - def _fromFile(self, fpath: str) -> "EntryType": + def _fromFile(self, fpath: str) -> EntryType: _, ext = splitext(fpath) c_open = compressionOpenFunc(ext.lstrip(".")) if not c_open: diff --git a/pyglossary/plugins/csv_plugin.py b/pyglossary/plugins/csv_plugin.py index d217f42d3..f63a3dc4d 100644 --- a/pyglossary/plugins/csv_plugin.py +++ b/pyglossary/plugins/csv_plugin.py @@ -113,7 +113,7 @@ def clear(self) -> None: self._pos = -1 self._csvReader: "Iterable[list[str]] | None" = None self._resDir = "" - self._resFileNames: "list[str]" = [] + self._resFileNames: list[str] = [] self._bufferRow: "list[str] | None" = None def open( @@ -189,7 +189,7 @@ def _iterRows(self) -> "Iterator[list[str]]": yield self._bufferRow yield from self._csvReader - def _processRow(self, row: "list[str]") -> "EntryType | None": + def _processRow(self, row: list[str]) -> "EntryType | None": if not row: return None diff --git a/pyglossary/plugins/dicformids.py b/pyglossary/plugins/dicformids.py index 98fc04dc8..95ddf86a3 100644 --- a/pyglossary/plugins/dicformids.py +++ b/pyglossary/plugins/dicformids.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # mypy: ignore-errors +from __future__ import annotations import operator import os @@ -88,7 +89,7 @@ class Reader: re_number = re.compile(r"\d+") - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._tabFileNames = [] self._tabFileReader = None @@ -118,7 +119,7 @@ def __len__(self) -> int: def __iter__(self) -> "Iterator[EntryType]": return self - def __next__(self) -> "EntryType": + def __next__(self) -> EntryType: for _ in range(10): try: return next(self._tabFileReader) @@ -146,7 +147,7 @@ def close(self) -> None: class Writer: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self.linesPerDirectoryFile = 500 # 200 self.indexFileMaxSize = 32722 # 30000 diff --git a/pyglossary/plugins/dict_cc.py b/pyglossary/plugins/dict_cc.py index ce20d73e3..470d2cdcd 100644 --- a/pyglossary/plugins/dict_cc.py +++ b/pyglossary/plugins/dict_cc.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html from collections.abc import Callable, Iterator @@ -47,7 +48,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() @@ -75,8 +76,8 @@ def __len__(self) -> int: @staticmethod def makeList( hf: "T_htmlfile", - input_elements: "list[Element]", - processor: "Callable", + input_elements: list[Element], + processor: Callable, single_prefix: str = "", skip_single: bool = True, ) -> None: diff --git a/pyglossary/plugins/dict_cc_split.py b/pyglossary/plugins/dict_cc_split.py index c6fa4af77..118228ffe 100644 --- a/pyglossary/plugins/dict_cc_split.py +++ b/pyglossary/plugins/dict_cc_split.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html from collections.abc import Iterator @@ -44,7 +45,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() diff --git a/pyglossary/plugins/dictunformat.py b/pyglossary/plugins/dictunformat.py index 19e410183..6327e33e8 100644 --- a/pyglossary/plugins/dictunformat.py +++ b/pyglossary/plugins/dictunformat.py @@ -82,7 +82,7 @@ def nextBlock(self) -> "tuple[str | list[str], str, None] | None": if not self._file: raise StopIteration word = "" - defiLines: "list[str]" = [] + defiLines: list[str] = [] while True: line = self.readline() diff --git a/pyglossary/plugins/digitalnk.py b/pyglossary/plugins/digitalnk.py index 582047bd7..d83b7ff27 100644 --- a/pyglossary/plugins/digitalnk.py +++ b/pyglossary/plugins/digitalnk.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html from collections.abc import Iterator @@ -42,7 +43,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() diff --git a/pyglossary/plugins/dsl/__init__.py b/pyglossary/plugins/dsl/__init__.py index 7a372b06a..04b9f1d29 100644 --- a/pyglossary/plugins/dsl/__init__.py +++ b/pyglossary/plugins/dsl/__init__.py @@ -162,7 +162,7 @@ def __init__(self, glos: GlossaryType) -> None: self._file: "io.TextIOBase" = nullTextIO self._fileSize = 0 self._bufferLine = "" - self._resFileSet: "set[str]" = set() + self._resFileSet: set[str] = set() self._includes: list[Reader] = [] self._abbrevDict: dict[str, str] = {} @@ -327,8 +327,8 @@ def __iter__(self) -> "Iterator[EntryType]": yield from reader reader.close() - term_lines: "list[str]" = [] - text_lines: "list[str]" = [] + term_lines: list[str] = [] + text_lines: list[str] = [] for line in self._iterLines(): if not line.strip(): continue @@ -359,8 +359,8 @@ def __iter__(self) -> "Iterator[EntryType]": def parseEntryBlock( self, - term_lines: "list[str]", - text_lines: "list[str]", + term_lines: list[str], + text_lines: list[str], ) -> EntryType: terms = [] defiTitles = [] diff --git a/pyglossary/plugins/dsl/transform.py b/pyglossary/plugins/dsl/transform.py index 6b7e191d3..5a63bc261 100644 --- a/pyglossary/plugins/dsl/transform.py +++ b/pyglossary/plugins/dsl/transform.py @@ -34,7 +34,7 @@ def __init__( # noqa: PLR0913 self.labelOpen = False self.label = "" self.output = "" - self.resFileSet: "set[str]" = set() + self.resFileSet: set[str] = set() self.abbrev = abbrev self.abbrevDict = abbrevDict diff --git a/pyglossary/plugins/ebook_epub2.py b/pyglossary/plugins/ebook_epub2.py index 168d68995..f9c0bb963 100644 --- a/pyglossary/plugins/ebook_epub2.py +++ b/pyglossary/plugins/ebook_epub2.py @@ -17,6 +17,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations from typing import TYPE_CHECKING, Any @@ -219,7 +220,7 @@ class Writer(EbookWriter): COVER_TEMPLATE = '' - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: import uuid EbookWriter.__init__( @@ -230,7 +231,7 @@ def __init__(self, glos: "GlossaryType") -> None: @classmethod def cls_get_prefix( - cls: "type[EbookWriter]", + cls: type[EbookWriter], options: "dict[str, Any]", word: str, ) -> str: @@ -251,7 +252,7 @@ def get_prefix(self, word: str) -> str: return "SPECIAL" return prefix - def write_ncx(self, group_labels: "list[str]") -> None: + def write_ncx(self, group_labels: list[str]) -> None: """ write_ncx only for epub. diff --git a/pyglossary/plugins/ebook_kobo.py b/pyglossary/plugins/ebook_kobo.py index 9d1d1f372..9c6397f65 100644 --- a/pyglossary/plugins/ebook_kobo.py +++ b/pyglossary/plugins/ebook_kobo.py @@ -1,19 +1,15 @@ # -*- coding: utf-8 -*- # The MIT License (MIT) - # Copyright © 2012-2016 Alberto Pettarin (alberto@albertopettarin.it) # Copyright © 2022 Saeed Rasooli - # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: - # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,6 +17,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import re import unicodedata @@ -114,13 +111,13 @@ class Writer: } @staticmethod - def stripFullHtmlError(entry: "EntryType", error: str) -> None: + def stripFullHtmlError(entry: EntryType, error: str) -> None: log.error(f"error in stripFullHtml: {error}, words={entry.l_word!r}") - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" - self._words: "list[str]" = [] + self._words: list[str] = [] self._img_pattern = re.compile( ']*?)?>', re.DOTALL, diff --git a/pyglossary/plugins/ebook_kobo_dictfile.py b/pyglossary/plugins/ebook_kobo_dictfile.py index 7d46512c3..88b11ba26 100644 --- a/pyglossary/plugins/ebook_kobo_dictfile.py +++ b/pyglossary/plugins/ebook_kobo_dictfile.py @@ -1,18 +1,14 @@ # -*- coding: utf-8 -*- # The MIT License (MIT) - # Copyright © 2020-2021 Saeed Rasooli - # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: - # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,6 +16,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import os from collections.abc import Generator @@ -27,7 +24,6 @@ from typing import TYPE_CHECKING from pyglossary.core import log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.image_utils import extractInlineHtmlImages from pyglossary.io_utils import nullTextIO from pyglossary.option import ( @@ -40,6 +36,8 @@ if TYPE_CHECKING: import io + from pyglossary.glossary_types import EntryType, GlossaryType + __all__ = [ "Reader", "Writer", @@ -92,7 +90,7 @@ class Reader(TextGlossaryReader): _extract_inline_images: bool = True - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: TextGlossaryReader.__init__(self, glos, hasInfo=False) def open(self, filename: str) -> None: @@ -144,8 +142,8 @@ def fixDefi( def nextBlock( self, ) -> "tuple[list[str], str, list[tuple[str, str]] | None]": - words: "list[str]" = [] - defiLines: "list[str]" = [] + words: list[str] = [] + defiLines: list[str] = [] html = False while True: @@ -184,7 +182,7 @@ class Writer: _encoding: str = "utf-8" @staticmethod - def stripFullHtmlError(entry: "EntryType", error: str) -> None: + def stripFullHtmlError(entry: EntryType, error: str) -> None: log.error(f"error in stripFullHtml: {error}, words={entry.l_word!r}") def __init__(self, glos: GlossaryType) -> None: diff --git a/pyglossary/plugins/ebook_mobi.py b/pyglossary/plugins/ebook_mobi.py index 64abceb62..4b1683ca8 100644 --- a/pyglossary/plugins/ebook_mobi.py +++ b/pyglossary/plugins/ebook_mobi.py @@ -1,19 +1,15 @@ # -*- coding: utf-8 -*- # The MIT License (MIT) - # Copyright © 2012-2016 Alberto Pettarin (alberto@albertopettarin.it) # Copyright © 2016-2022 Saeed Rasooli - # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: - # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,6 +17,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import annotations import os from collections.abc import Generator @@ -126,16 +123,16 @@ class GroupStateBySize: - def __init__(self, writer: "Writer") -> None: + def __init__(self, writer: Writer) -> None: self.writer = writer self.group_index = -1 self.reset() def reset(self) -> None: - self.group_contents: "list[str]" = [] + self.group_contents: list[str] = [] self.group_size = 0 - def add(self, entry: "EntryType") -> None: + def add(self, entry: EntryType) -> None: defi = entry.defi content = self.writer.format_group_content( entry.l_word[0], @@ -230,7 +227,7 @@ class Writer(EbookWriter): """ - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: import uuid EbookWriter.__init__( @@ -252,7 +249,7 @@ def get_prefix(self, word: str) -> str: def format_group_content( self, - word: "str", + word: str, defi: str, variants: "list[str] | None" = None, ) -> str: @@ -319,7 +316,7 @@ def get_opf_contents( ).encode("utf-8") def write_groups(self) -> "Generator[None, EntryType, None]": - def add_group(state: "GroupStateBySize") -> None: + def add_group(state: GroupStateBySize) -> None: if state.group_size <= 0: return state.group_index += 1 diff --git a/pyglossary/plugins/edict2/__init__.py b/pyglossary/plugins/edict2/__init__.py index 5adc0ab45..8e881bc55 100644 --- a/pyglossary/plugins/edict2/__init__.py +++ b/pyglossary/plugins/edict2/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections.abc import Iterator from typing import TYPE_CHECKING @@ -80,7 +82,7 @@ class Reader: _traditional_title: bool = False _colorize_tones: bool = True - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self.file: "io.TextIOBase" = nullTextIO self._fileSize = 0 diff --git a/pyglossary/plugins/edict2/conv.py b/pyglossary/plugins/edict2/conv.py index 4719526b4..691984996 100644 --- a/pyglossary/plugins/edict2/conv.py +++ b/pyglossary/plugins/edict2/conv.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re from typing import TYPE_CHECKING, cast @@ -34,7 +36,7 @@ def parse_line(line: str) -> "tuple[str, str, str, list[str]] | None": return trad, simp, pinyin, eng.split("/") -def make_entry( # noqa: PLR0913 +def make_entry( # noqa: PLR0913 trad: str, simp: str, pinyin: str, @@ -54,8 +56,8 @@ def make_entry( # noqa: PLR0913 def colorize( hf: "T_htmlfile", - syllables: "Sequence[str]", - tones: "Sequence[str]", + syllables: Sequence[str], + tones: Sequence[str], colorize_tones: bool, ) -> None: @@ -76,7 +78,7 @@ def colorize( hf.write(syllable) -def render_article( # noqa: PLR0913 +def render_article( # noqa: PLR0913 trad: str, simp: str, pinyin: str, diff --git a/pyglossary/plugins/edlin.py b/pyglossary/plugins/edlin.py index 41a6c99c9..940b147ea 100644 --- a/pyglossary/plugins/edlin.py +++ b/pyglossary/plugins/edlin.py @@ -89,7 +89,7 @@ def _clear(self) -> None: self._wordCount = None self._rootPath = None self._resDir = "" - self._resFileNames: "list[str]" = [] + self._resFileNames: list[str] = [] def open(self, filename: str) -> None: from pyglossary.json_utils import jsonToOrderedData @@ -207,7 +207,7 @@ def _clear(self) -> None: self._filename = "" self._resDir = "" self._encoding = "utf-8" - self._hashSet: "set[str]" = set() + self._hashSet: set[str] = set() # self._wordCount = None @staticmethod diff --git a/pyglossary/plugins/freedict.py b/pyglossary/plugins/freedict.py index e0bae8f1a..d47a272ad 100644 --- a/pyglossary/plugins/freedict.py +++ b/pyglossary/plugins/freedict.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import re from io import BytesIO, IOBase @@ -9,6 +10,7 @@ from collections.abc import Callable, Iterator from typing import Any + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element, T_htmlfile @@ -17,7 +19,6 @@ stdCompressions, ) from pyglossary.core import log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.html_utils import unescape_unicode from pyglossary.io_utils import nullBinaryIO from pyglossary.langs import langDict @@ -168,8 +169,8 @@ class Reader: @staticmethod def makeList( # noqa: PLR0913 hf: "T_htmlfile", - input_objects: "list[Any]", - processor: "Callable", + input_objects: list[Any], + processor: Callable, single_prefix: str = "", skip_single: bool = True, ordered: bool = True, @@ -204,7 +205,7 @@ def getTitleTag(sample: str) -> str: def writeRef( # noqa: PLR6301 self, hf: "T_htmlfile", - ref: "Element", + ref: Element, ) -> None: target = ref.get("target") attrib: "dict[str, str]" = {} @@ -219,14 +220,14 @@ def writeRef( # noqa: PLR6301 def writeQuote( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, ) -> None: self.writeWithDirection(hf, elem, "div") def writeTransCit( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, ) -> None: from lxml import etree as ET @@ -272,7 +273,7 @@ def writeTransCit( def writeDef( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, ) -> None: # sep = None # if self._cif_newline: @@ -308,7 +309,7 @@ def writeChild(item: "str | Element", depth: int) -> None: def writeWithDirection( self, hf: "T_htmlfile", - child: "Element", + child: Element, tag: str, ) -> None: attrib = dict(child.attrib) @@ -338,7 +339,7 @@ def writeWithDirection( def writeRichText( self, hf: "T_htmlfile", - el: "Element", + el: Element, ) -> None: from lxml import etree as ET @@ -365,7 +366,7 @@ def writeRichText( self.writeRichText(hf, child) - def getLangDesc(self, elem: "Element") -> "str | None": + def getLangDesc(self, elem: Element) -> "str | None": lang = elem.attrib.get(self.xmlLang) if lang: langObj = langDict[lang] @@ -384,7 +385,7 @@ def getLangDesc(self, elem: "Element") -> "str | None": def writeLangTag( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, ) -> None: langDesc = self.getLangDesc(elem) if not langDesc: @@ -398,7 +399,7 @@ def writeLangTag( def writeNote( self, hf: "T_htmlfile", - note: "Element", + note: Element, ) -> None: self.writeRichText(hf, note) @@ -407,7 +408,7 @@ def writeNote( def writeSenseSense( # noqa: PLR0912 self, hf: "T_htmlfile", - sense: "Element", + sense: Element, ) -> int: # this element can be 1st-level (directly under ) # or 2nd-level @@ -562,7 +563,7 @@ def getCommaSep(self, sample: str) -> str: def writeGramGroups( self, hf: "T_htmlfile", - gramGrpList: "list[Element]", + gramGrpList: list[Element], ) -> None: from lxml import etree as ET @@ -593,14 +594,14 @@ def writeGramGroups( def writeSenseGrams( self, hf: "T_htmlfile", - sense: "Element", + sense: Element, ) -> None: self.writeGramGroups(hf, sense.findall("gramGrp", self.ns)) def writeSense( self, hf: "T_htmlfile", - sense: "Element", + sense: Element, ) -> None: # this element is 1st-level (directly under ) self.writeSenseGrams(hf, sense) @@ -612,7 +613,7 @@ def writeSense( ) self.writeSenseSense(hf, sense) - def getDirection(self, elem: "Element") -> str: + def getDirection(self, elem: Element) -> str: lang = elem.get(self.xmlLang) if lang is None: return "" @@ -627,7 +628,7 @@ def getDirection(self, elem: "Element") -> str: def writeSenseList( self, hf: "T_htmlfile", - senseList: "list[Element]", + senseList: list[Element], ) -> None: # these elements are 1st-level (directly under ) if not senseList: @@ -650,7 +651,7 @@ def writeSenseList( # list_type="A", ) - def normalizeGramGrpChild(self, elem: "Element") -> str: # noqa: PLR0912 + def normalizeGramGrpChild(self, elem: Element) -> str: # noqa: PLR0912 # child can be "pos" or "gen" tag = elem.tag text = elem.text @@ -695,8 +696,8 @@ def normalizeGramGrpChild(self, elem: "Element") -> str: # noqa: PLR0912 def getEntryByElem( # noqa: PLR0912 self, - entry: "Element", - ) -> "EntryType": + entry: Element, + ) -> EntryType: from lxml import etree as ET glos = self._glos @@ -709,7 +710,7 @@ def getEntryByElem( # noqa: PLR0912 if elem.tag not in self.supportedTags: self._discoveredTags[elem.tag] = elem - def br() -> "Element": + def br() -> Element: return ET.Element("br") inflectedKeywords = [] @@ -771,7 +772,7 @@ def br() -> "Element": byteProgress=(_file.tell(), self._fileSize), ) - def setWordCount(self, header: "Element") -> None: + def setWordCount(self, header: Element) -> None: extent_elem = header.find(".//extent", self.ns) if extent_elem is None: log.warning( @@ -788,7 +789,7 @@ def setWordCount(self, header: "Element") -> None: log.exception(f"unexpected {extent=}") @staticmethod - def tostring(elem: "Element") -> str: + def tostring(elem: Element) -> str: from lxml import etree as ET return ( @@ -801,14 +802,14 @@ def tostring(elem: "Element") -> str: .strip() ) - def stripParag(self, elem: "Element") -> str: + def stripParag(self, elem: Element) -> str: text = self.tostring(elem) text = self._p_pattern.sub("\\2", text) return text # noqa: RET504 def stripParagList( self, - elems: "list[Element]", + elems: list[Element], ) -> str: lines = [] for elem in elems: @@ -822,7 +823,7 @@ def stripParagList( def setGlosInfo(self, key: str, value: str) -> None: self._glos.setInfo(key, unescape_unicode(value)) - def setCopyright(self, header: "Element") -> None: + def setCopyright(self, header: Element) -> None: elems = header.findall(".//availability//p", self.ns) if not elems: log.warning("did not find copyright") @@ -832,14 +833,14 @@ def setCopyright(self, header: "Element") -> None: self.setGlosInfo("copyright", _copyright) log.debug(f"Copyright: {_copyright!r}") - def setPublisher(self, header: "Element") -> None: + def setPublisher(self, header: Element) -> None: elem = header.find(".//publisher", self.ns) if elem is None or not elem.text: log.warning("did not find publisher") return self.setGlosInfo("publisher", elem.text) - def setCreationTime(self, header: "Element") -> None: + def setCreationTime(self, header: Element) -> None: elem = header.find(".//publicationStmt/date", self.ns) if elem is None or not elem.text: return @@ -848,7 +849,7 @@ def setCreationTime(self, header: "Element") -> None: def replaceRefLink(self, text: str) -> str: return self._ref_pattern.sub('\\2', text) - def setDescription(self, header: "Element") -> None: + def setDescription(self, header: Element) -> None: elems = [] for tag in ("sourceDesc", "projectDesc"): elems += header.findall(f".//{tag}//p", self.ns) @@ -875,7 +876,7 @@ def setDescription(self, header: "Element") -> None: "--------------------------------------", ) - def setMetadata(self, header: "Element") -> None: + def setMetadata(self, header: Element) -> None: self.setWordCount(header) title = header.find(".//title", self.ns) if title is not None and title.text: @@ -894,7 +895,7 @@ def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._dirname = "" - self._file: "IOBase" = nullBinaryIO + self._file: IOBase = nullBinaryIO self._fileSize = 0 self._wordCount = 0 self._discoveredTags: "dict[str, Element]" = {} @@ -957,7 +958,7 @@ def open( cfile.close() - def loadInclude(self, elem: "Element") -> "Reader | None": + def loadInclude(self, elem: Element) -> "Reader | None": href = elem.attrib.get("href") if not href: log.error(f"empty href in {elem}") diff --git a/pyglossary/plugins/gettext_po.py b/pyglossary/plugins/gettext_po.py index f60b7961b..c735b2b5c 100644 --- a/pyglossary/plugins/gettext_po.py +++ b/pyglossary/plugins/gettext_po.py @@ -64,7 +64,7 @@ def clear(self) -> None: self._file: "io.TextIOBase" = nullTextIO self._wordCount: "int | None" = None self._resDir = "" - self._resFileNames: "list[str]" = [] + self._resFileNames: list[str] = [] def open(self, filename: str) -> None: self._filename = filename diff --git a/pyglossary/plugins/html_dir.py b/pyglossary/plugins/html_dir.py index d3de09041..5f84c4362 100644 --- a/pyglossary/plugins/html_dir.py +++ b/pyglossary/plugins/html_dir.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import html import os @@ -12,11 +13,12 @@ if TYPE_CHECKING: import io + from pyglossary.glossary_types import ( + EntryType, + GlossaryType, + ) + from pyglossary.core import log -from pyglossary.glossary_types import ( - EntryType, - GlossaryType, -) from pyglossary.option import ( BoolOption, EncodingOption, @@ -111,7 +113,7 @@ class Writer: _word_title: bool = True @staticmethod - def stripFullHtmlError(entry: "EntryType", error: str) -> None: + def stripFullHtmlError(entry: EntryType, error: str) -> None: log.error(f"error in stripFullHtml: {error}, words={entry.l_word!r}") def __init__(self, glos: GlossaryType) -> None: @@ -121,7 +123,7 @@ def __init__(self, glos: GlossaryType) -> None: self._encoding = "utf-8" self._filename_format = "{n:05d}.html" self._tail = "" - self._filenameList: "list[str]" = [] + self._filenameList: list[str] = [] glos.stripFullHtml(errorHandler=self.stripFullHtmlError) self._resSrcPattern = re.compile(' src="([^"]*)"') @@ -344,7 +346,7 @@ def write(self) -> "Generator[None, EntryType, None]": # noqa: PLR0912 entry_url_fmt = glos.getInfo("entry_url") - def getEntryWebLink(entry: "EntryType") -> str: + def getEntryWebLink(entry: EntryType) -> str: if not entry_url_fmt: return "" url = entry_url_fmt.format(word=html.escape(entry.l_word[0])) diff --git a/pyglossary/plugins/iupac_goldbook.py b/pyglossary/plugins/iupac_goldbook.py index 5d58a9a3e..c9f1d71b3 100644 --- a/pyglossary/plugins/iupac_goldbook.py +++ b/pyglossary/plugins/iupac_goldbook.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # mypy: ignore-errors +from __future__ import annotations from collections.abc import Iterator from io import BytesIO @@ -53,7 +54,7 @@ class Reader: "lxml": "lxml", } - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._file = None @@ -117,7 +118,7 @@ def setGlosInfo(self, key: str, value: str) -> None: return self._glos.setInfo(key, unescape_unicode(value)) - def setMetadata(self, header: "Element") -> None: + def setMetadata(self, header: Element) -> None: if header is None: return @@ -143,7 +144,7 @@ def setMetadata(self, header: "Element") -> None: @staticmethod def tostring( - elem: "Element", + elem: Element, ) -> str: from lxml import etree as ET @@ -158,7 +159,7 @@ def tostring( ) @staticmethod - def innerXML(elem: "Element") -> str: + def innerXML(elem: Element) -> str: from lxml import etree as ET elemName = elem.xpath("name(/*)") @@ -171,7 +172,7 @@ def innerXML(elem: "Element") -> str: return resultStr - def getTerm(self, termE: "Element") -> str: # noqa: PLR6301 + def getTerm(self, termE: Element) -> str: # noqa: PLR6301 from lxml import etree as ET term = ( diff --git a/pyglossary/plugins/jmdict.py b/pyglossary/plugins/jmdict.py index 181b09545..82c172127 100644 --- a/pyglossary/plugins/jmdict.py +++ b/pyglossary/plugins/jmdict.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import os import re @@ -10,6 +11,10 @@ import io from collections.abc import Callable, Iterator + from pyglossary.glossary_types import ( + EntryType, + GlossaryType, + ) from pyglossary.lxml_types import Element, T_htmlfile from pyglossary.compression import ( @@ -17,10 +22,6 @@ stdCompressions, ) from pyglossary.core import pip -from pyglossary.glossary_types import ( - EntryType, - GlossaryType, -) from pyglossary.io_utils import nullBinaryIO from pyglossary.option import ( BoolOption, @@ -100,8 +101,8 @@ class Reader: @staticmethod def makeList( hf: "T_htmlfile", - input_objects: "list[Element]", - processor: "Callable", + input_objects: list[Element], + processor: Callable, single_prefix: str = "", skip_single: bool = True, ) -> None: @@ -124,11 +125,11 @@ def makeList( def writeSense( # noqa: PLR0912 self, hf: "T_htmlfile", - sense: "Element", + sense: Element, ) -> None: from lxml import etree as ET - def br() -> "Element": + def br() -> Element: return ET.Element("br") for elem in sense.findall("pos"): @@ -233,7 +234,7 @@ def br() -> "Element": if not textElem.text: continue text = textElem.text - sentList: "list[str]" = [] + sentList: list[str] = [] for sentElem in elem.findall("ex_sent"): if not sentElem.text: continue @@ -251,8 +252,8 @@ def br() -> "Element": # TODO: break it down def getEntryByElem( # noqa: PLR0912 self, - entry: "Element", - ) -> "EntryType": + entry: Element, + ) -> EntryType: from lxml import etree as ET glos = self._glos @@ -260,13 +261,13 @@ def getEntryByElem( # noqa: PLR0912 f = BytesIO() translit = self._translitation - def br() -> "Element": + def br() -> Element: return ET.Element("br") with ET.htmlfile(f, encoding="utf-8") as hf: # noqa: PLR1702 - kebList: "list[str]" = [] - rebList: "list[str]" = [] - kebDisplayList: "list[str]" = [] + kebList: list[str] = [] + rebList: list[str] = [] + kebDisplayList: list[str] = [] rebDisplayList: "list[tuple[str, list[str]]]" = [] with hf.element("div"): for k_ele in entry.findall("k_ele"): @@ -372,7 +373,7 @@ def br() -> "Element": ) @staticmethod - def tostring(elem: "Element") -> str: + def tostring(elem: Element) -> str: from lxml import etree as ET return ( diff --git a/pyglossary/plugins/jmnedict.py b/pyglossary/plugins/jmnedict.py index 8868141a0..a3927f85f 100644 --- a/pyglossary/plugins/jmnedict.py +++ b/pyglossary/plugins/jmnedict.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import annotations + import os import re from io import BytesIO @@ -8,6 +10,10 @@ import io from collections.abc import Callable, Iterator + from pyglossary.glossary_types import ( + EntryType, + GlossaryType, + ) from pyglossary.lxml_types import Element, T_htmlfile from pyglossary.option import Option @@ -16,10 +22,6 @@ stdCompressions, ) from pyglossary.core import pip -from pyglossary.glossary_types import ( - EntryType, - GlossaryType, -) from pyglossary.io_utils import nullBinaryIO __all__ = [ @@ -78,8 +80,8 @@ class Reader: @staticmethod def makeList( hf: "T_htmlfile", - input_objects: "list[Element]", - processor: "Callable", + input_objects: list[Element], + processor: Callable, single_prefix: str = "", skip_single: bool = True, ) -> None: @@ -100,11 +102,11 @@ def makeList( def writeTrans( self, hf: "T_htmlfile", - trans: "Element", + trans: Element, ) -> None: from lxml import etree as ET - def br() -> "Element": + def br() -> Element: return ET.Element("br") for elem in trans.findall("name_type"): @@ -142,19 +144,19 @@ def br() -> "Element": def getEntryByElem( # noqa: PLR0912 self, - entry: "Element", - ) -> "EntryType": + entry: Element, + ) -> EntryType: from lxml import etree as ET glos = self._glos keywords = [] f = BytesIO() - def br() -> "Element": + def br() -> Element: return ET.Element("br") with ET.htmlfile(f, encoding="utf-8") as hf: # noqa: PLR1702 - kebList: "list[str]" = [] + kebList: list[str] = [] rebList: "list[tuple[str, list[str]]]" = [] with hf.element("div"): for k_ele in entry.findall("k_ele"): @@ -237,7 +239,7 @@ def br() -> "Element": ) @staticmethod - def tostring(elem: "Element") -> str: + def tostring(elem: Element) -> str: from lxml import etree as ET return ( diff --git a/pyglossary/plugins/lingoes_ldf.py b/pyglossary/plugins/lingoes_ldf.py index b6c580667..788a60dc8 100644 --- a/pyglossary/plugins/lingoes_ldf.py +++ b/pyglossary/plugins/lingoes_ldf.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- +from __future__ import annotations from collections.abc import Generator +from typing import TYPE_CHECKING from pyglossary.compression import ( # compressionOpen, @@ -8,7 +10,6 @@ ) from pyglossary.core import log from pyglossary.file_utils import fileCountLines -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.option import ( BoolOption, EncodingOption, @@ -18,6 +19,9 @@ from pyglossary.text_reader import TextGlossaryReader, nextBlockResultType from pyglossary.text_utils import splitByBar +if TYPE_CHECKING: + from pyglossary.glossary_types import EntryType, GlossaryType + __all__ = [ "Reader", "Writer", @@ -83,7 +87,7 @@ def fixInfoWord(cls, word: str) -> str: return word - def nextBlock(self) -> "nextBlockResultType": + def nextBlock(self) -> nextBlockResultType: if not self._file: raise StopIteration entryLines = [] diff --git a/pyglossary/plugins/octopus_mdict_new/__init__.py b/pyglossary/plugins/octopus_mdict_new/__init__.py index 25249a0db..cd2fafa56 100644 --- a/pyglossary/plugins/octopus_mdict_new/__init__.py +++ b/pyglossary/plugins/octopus_mdict_new/__init__.py @@ -15,6 +15,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import gc import os @@ -93,7 +94,7 @@ class Reader: _same_dir_data_files: bool = False _audio: bool = False - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self.clear() self._re_internal_link = re.compile("href=([\"'])(entry://|[dx]:)") @@ -104,7 +105,7 @@ def __init__(self, glos: "GlossaryType") -> None: def clear(self) -> None: self._filename = "" self._mdx: "MDX | None" = None - self._mdd: "list[MDD]" = [] + self._mdd: list[MDD] = [] self._wordCount = 0 self._dataEntryCount = 0 diff --git a/pyglossary/plugins/quickdic6.py b/pyglossary/plugins/quickdic6.py index c36f6f7e7..6190152b2 100644 --- a/pyglossary/plugins/quickdic6.py +++ b/pyglossary/plugins/quickdic6.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import datetime as dt import functools @@ -15,9 +16,10 @@ from collections.abc import Callable from typing import Any, Literal + from pyglossary.glossary_types import EntryType, GlossaryType + from pyglossary.core import log from pyglossary.flags import NEVER -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.langs import langDict from pyglossary.option import ( Option, @@ -546,7 +548,7 @@ def __init__( # noqa: PLR0913 self.created = dt.datetime.now() if created is None else created @classmethod - def from_path(cls: "type[QuickDic]", path_str: str) -> "QuickDic": + def from_path(cls: type[QuickDic], path_str: str) -> QuickDic: path = pathlib.Path(path_str) if path.suffix != ".zip": with open(path, "rb") as fp: @@ -557,7 +559,7 @@ def from_path(cls: "type[QuickDic]", path_str: str) -> "QuickDic": return cls.from_fp(fp) @classmethod - def from_fp(cls: "type[QuickDic]", fp: IO[bytes]) -> "QuickDic": + def from_fp(cls: type[QuickDic], fp: IO[bytes]) -> QuickDic: version = read_int(fp) created = dt.datetime.fromtimestamp(float(read_long(fp)) / 1000.0) # noqa: DTZ006 name = read_string(fp) diff --git a/pyglossary/plugins/sql.py b/pyglossary/plugins/sql.py index 3dba55739..124e8ad30 100644 --- a/pyglossary/plugins/sql.py +++ b/pyglossary/plugins/sql.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations from collections.abc import Generator from typing import TYPE_CHECKING @@ -57,7 +58,7 @@ class Writer: _newline: str = "
" _transaction: bool = False - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._file: "io.IOBase | None" = None @@ -126,7 +127,7 @@ def _writeInfo(self) -> None: f"'{key2}', '{value2}');\n", ) - def _getInfoKeys(self) -> "list[str]": + def _getInfoKeys(self) -> list[str]: info_keys = self._info_keys if info_keys: return info_keys diff --git a/pyglossary/plugins/stardict.py b/pyglossary/plugins/stardict.py index 81166945b..0df897918 100644 --- a/pyglossary/plugins/stardict.py +++ b/pyglossary/plugins/stardict.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import gzip import os @@ -30,12 +31,12 @@ import io import sqlite3 + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.langs import Lang from pyglossary.core import log from pyglossary.flags import ALWAYS, DEFAULT_YES -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.glossary_utils import Error from pyglossary.option import ( BoolOption, @@ -180,7 +181,7 @@ def sort(self) -> None: ... class MemSdList: def __init__(self) -> None: - self._l: "list[Any]" = [] + self._l: list[Any] = [] def append(self, x: Any) -> None: self._l.append(x) @@ -248,7 +249,7 @@ def getExtraColumns(cls) -> "list[tuple[str, str]]": def __len__(self) -> int: return self._len - def append(self, item: "Sequence") -> None: + def append(self, item: Sequence) -> None: if self._cur is None or self._con is None: raise RuntimeError("db is closed") self._len += 1 @@ -334,7 +335,7 @@ def __init__(self, glos: GlossaryType) -> None: a dict { entryIndex -> altList } """ - def xdxf_setup(self) -> "XdxfTransformerType": + def xdxf_setup(self) -> XdxfTransformerType: if self._xsl: from pyglossary.xdxf.xsl_transform import XslXdxfTransformer @@ -360,7 +361,7 @@ def clear(self) -> None: self._synDict: "dict[int, list[str]]" = {} self._sametypesequence = "" self._resDir = "" - self._resFileNames: "list[str]" = [] + self._resFileNames: list[str] = [] self._wordCount: "int | None" = None def open(self, filename: str) -> None: diff --git a/pyglossary/plugins/stardict_textual.py b/pyglossary/plugins/stardict_textual.py index 644860ae4..380a08ff8 100644 --- a/pyglossary/plugins/stardict_textual.py +++ b/pyglossary/plugins/stardict_textual.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import annotations + import os from os.path import dirname, isdir, join from typing import TYPE_CHECKING, cast @@ -9,6 +11,7 @@ from lxml import builder + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element from pyglossary.xdxf.transform import XdxfTransformer @@ -18,7 +21,6 @@ stdCompressions, ) from pyglossary.core import log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.html_utils import unescape_unicode from pyglossary.io_utils import nullBinaryIO from pyglossary.option import ( @@ -75,14 +77,14 @@ class Reader: "lxml": "lxml", } - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._file: "io.IOBase" = nullBinaryIO self._fileSize = 0 self._xdxfTr: "XdxfTransformer | None" = None - def xdxf_setup(self) -> "XdxfTransformer": + def xdxf_setup(self) -> XdxfTransformer: from pyglossary.xdxf.transform import XdxfTransformer self._xdxfTr = tr = XdxfTransformer(encoding="utf-8") @@ -137,7 +139,7 @@ def setGlosInfo(self, key: str, value: str) -> None: return self._glos.setInfo(key, unescape_unicode(value)) - def setMetadata(self, header: "Element") -> None: + def setMetadata(self, header: Element) -> None: if (elem := header.find("./bookname")) is not None and elem.text: self.setGlosInfo("name", elem.text) @@ -329,7 +331,7 @@ def writeInfo( def writeDataEntry( self, maker: "builder.ElementMaker", # noqa: ARG002 - entry: "EntryType", + entry: EntryType, ) -> None: entry.save(self._resDir) # TODO: create article tag with "definition-r" in it? diff --git a/pyglossary/plugins/testformat.py b/pyglossary/plugins/testformat.py index d1af7cf36..4dbb53bc9 100644 --- a/pyglossary/plugins/testformat.py +++ b/pyglossary/plugins/testformat.py @@ -1,4 +1,6 @@ + +from __future__ import annotations import typing # -*- coding: utf-8 -*- @@ -39,7 +41,7 @@ class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._wordCount = 0 @@ -88,7 +90,7 @@ def __iter__(self) -> "Iterator[EntryType]": class Writer: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" diff --git a/pyglossary/plugins/wiktextract.py b/pyglossary/plugins/wiktextract.py index 0d6f0253a..a5d656140 100644 --- a/pyglossary/plugins/wiktextract.py +++ b/pyglossary/plugins/wiktextract.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import collections from io import BytesIO, IOBase @@ -9,6 +10,7 @@ from collections.abc import Callable, Iterator from typing import Any + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element, T_htmlfile @@ -17,7 +19,6 @@ stdCompressions, ) from pyglossary.core import log, pip -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.io_utils import nullBinaryIO from pyglossary.option import ( BoolOption, @@ -94,7 +95,7 @@ class Reader: _audio: bool = True - _audio_formats: "list[str]" = ["ogg", "mp3"] + _audio_formats: list[str] = ["ogg", "mp3"] topicStyle = ( "color:white;" @@ -108,7 +109,7 @@ class Reader: def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" - self._file: "IOBase" = nullBinaryIO + self._file: IOBase = nullBinaryIO self._fileSize = 0 self._wordCount = 0 @@ -165,13 +166,13 @@ def __iter__(self) -> "Iterator[EntryType]": def warning(self, msg): self._warnings[msg] += 1 - def makeEntry(self, data: "dict[str, Any]") -> "EntryType": + def makeEntry(self, data: "dict[str, Any]") -> EntryType: from lxml import etree as ET glos = self._glos f = BytesIO() - def br() -> "Element": + def br() -> Element: return ET.Element("br") keywords = [] @@ -223,7 +224,7 @@ def br() -> "Element": # TODO: data.get("translations") # list[dict[str, str]] - # dict keys: "code", "lang", "sense", "word" + # dict keys: code, "lang", "sense", "word" etymology: str = data.get("etymology_text", "") if etymology: @@ -367,7 +368,7 @@ def writeSenseExample( # noqa: PLR6301, PLR0912 hf: "T_htmlfile", example: "dict[str, str]", ) -> None: - # example keys: "text", "english", "ref", "type" + # example keys: text, "english", "ref", "type" textList: list[tuple[str, str]] = [] _text = example.pop("example", "") if _text: @@ -533,7 +534,7 @@ def writeSynonyms( # "word": "str", # "sense": "str", # "_dis1": "str", - # "tags": "list[str]" + # "tags": list[str] # "extra": "str", # "english": "str" @@ -554,7 +555,7 @@ def writeAntonyms( ) -> None: if not antonyms: return - # dict keys: "word" + # dict keys: word with hf.element("div"): hf.write("Antonyms: ") for i, item in enumerate(antonyms): @@ -572,7 +573,7 @@ def writeRelated( ) -> None: if not relatedList: return - # dict keys: "sense", "word", "english" + # dict keys: sense, "word", "english" with hf.element("div"): hf.write("Related: ") for i, item in enumerate(relatedList): @@ -673,8 +674,8 @@ def writeSense( @staticmethod def makeList( # noqa: PLR0913 hf: "T_htmlfile", - input_objects: "list[Any]", - processor: "Callable", + input_objects: list[Any], + processor: Callable, single_prefix: str = "", skip_single: bool = True, ordered: bool = True, diff --git a/pyglossary/plugins/wordnet.py b/pyglossary/plugins/wordnet.py index 877545a1e..e4ebc8ecc 100644 --- a/pyglossary/plugins/wordnet.py +++ b/pyglossary/plugins/wordnet.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 # as published by the Free Software Foundation. @@ -14,6 +13,7 @@ # Copyright (C) 2015 Igor Tkach # # This plugin is based on https://github.com/itkach/wordnet2slob +from __future__ import annotations import io import os @@ -335,7 +335,7 @@ def process(self) -> "Iterator[tuple[str, str]]": class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" self._wordCount = 0 diff --git a/pyglossary/plugins/xdxf/__init__.py b/pyglossary/plugins/xdxf/__init__.py index 0c8bc266d..d60c80299 100644 --- a/pyglossary/plugins/xdxf/__init__.py +++ b/pyglossary/plugins/xdxf/__init__.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # xdxf/__init__.py +from __future__ import annotations + """xdxf file format reader and utils to convert xdxf to html.""" # # Copyright © 2023 Saeed Rasooli @@ -29,6 +31,7 @@ if TYPE_CHECKING: import io + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element from lxml import etree as ET @@ -38,7 +41,6 @@ stdCompressions, ) from pyglossary.core import log -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.io_utils import nullBinaryIO from pyglossary.option import ( BoolOption, @@ -114,7 +116,7 @@ if TYPE_CHECKING: class TransformerType(typing.Protocol): - def transform(self, article: "Element") -> str: ... + def transform(self, article: Element) -> str: ... class Reader: @@ -259,7 +261,7 @@ def close(self) -> None: @staticmethod def tostring( - elem: "Element", + elem: Element, ) -> str: return ( ET.tostring( @@ -271,14 +273,14 @@ def tostring( .strip() ) - def titles(self, article: "Element") -> "list[str]": + def titles(self, article: Element) -> list[str]: """ :param article: tag :return: (title (str) | None, alternative titles (set)) """ from itertools import combinations - titles: "list[str]" = [] + titles: list[str] = [] for title_element in article.findall("k"): if title_element.text is None: # TODO: look for tag? @@ -286,9 +288,11 @@ def titles(self, article: "Element") -> "list[str]": continue n_opts = len([c for c in title_element if c.tag == "opt"]) if n_opts: - for j in range(n_opts + 1): - for comb in combinations(list(range(n_opts)), j): - titles.append(self._mktitle(title_element, comb)) + titles += [ + self._mktitle(title_element, comb) + for j in range(n_opts + 1) + for comb in combinations(list(range(n_opts)), j) + ] else: titles.append(self._mktitle(title_element)) @@ -296,7 +300,7 @@ def titles(self, article: "Element") -> "list[str]": def _mktitle( # noqa: PLR6301 self, - title_element: "Element", + title_element: Element, include_opts: "Sequence | None" = None, ) -> str: if include_opts is None: diff --git a/pyglossary/plugins/xdxf_css/__init__.py b/pyglossary/plugins/xdxf_css/__init__.py index bf54f171f..e30b0e862 100644 --- a/pyglossary/plugins/xdxf_css/__init__.py +++ b/pyglossary/plugins/xdxf_css/__init__.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # xdxf/__init__.py +from __future__ import annotations + """xdxf file format reader and utils to convert xdxf to html.""" # # Copyright © 2023 Saeed Rasooli @@ -32,6 +34,7 @@ if TYPE_CHECKING: import io + from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.lxml_types import Element from pyglossary.option import Option @@ -43,7 +46,6 @@ stdCompressions, ) from pyglossary.core import log, rootDir -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.io_utils import nullBinaryIO from pyglossary.text_utils import toStr @@ -112,7 +114,7 @@ if TYPE_CHECKING: class TransformerType(typing.Protocol): - def transform(self, article: "Element") -> str: ... + def transform(self, article: Element) -> str: ... class Reader: @@ -286,7 +288,7 @@ def generate_abbr_js(self, abbr_defs: list["Element"]) -> bytes: @staticmethod def tostring( - elem: "Element", + elem: Element, ) -> str: return ( ET.tostring( @@ -298,14 +300,14 @@ def tostring( .strip() ) - def titles(self, article: "Element") -> "list[str]": + def titles(self, article: Element) -> list[str]: """ :param article: tag :return: (title (str) | None, alternative titles (set)) """ from itertools import combinations - titles: "list[str]" = [] + titles: list[str] = [] for title_element in article.findall("k"): if title_element.text is None: # TODO: look for tag? @@ -313,9 +315,11 @@ def titles(self, article: "Element") -> "list[str]": continue n_opts = len([c for c in title_element if c.tag == "opt"]) if n_opts: - for j in range(n_opts + 1): - for comb in combinations(list(range(n_opts)), j): - titles.append(self._mktitle(title_element, comb)) + titles += [ + self._mktitle(title_element, comb) + for j in range(n_opts + 1) + for comb in combinations(list(range(n_opts)), j) + ] else: titles.append(self._mktitle(title_element)) @@ -323,7 +327,7 @@ def titles(self, article: "Element") -> "list[str]": def _mktitle( # noqa: PLR6301 self, - title_element: "Element", + title_element: Element, include_opts: "Sequence | None" = None, ) -> str: if include_opts is None: diff --git a/pyglossary/plugins/xdxf_lax.py b/pyglossary/plugins/xdxf_lax.py index d041551ec..610e20003 100644 --- a/pyglossary/plugins/xdxf_lax.py +++ b/pyglossary/plugins/xdxf_lax.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- # +from __future__ import annotations + """Lax implementation of xdxf reader.""" # # Copyright © 2023 Saeed Rasooli @@ -31,12 +33,13 @@ from lxml.html import HtmlElement as Element + from pyglossary.glossary_types import EntryType, GlossaryType + from pyglossary.compression import ( compressionOpen, stdCompressions, ) from pyglossary.core import log -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.io_utils import nullBinaryIO from pyglossary.option import ( BoolOption, @@ -85,7 +88,7 @@ if TYPE_CHECKING: class TransformerType(typing.Protocol): - def transform(self, article: "Element") -> str: ... + def transform(self, article: Element) -> str: ... class Reader: @@ -220,7 +223,7 @@ def close(self) -> None: @staticmethod def tostring( - elem: "Element", + elem: Element, ) -> str: from lxml.html import tostring @@ -234,14 +237,14 @@ def tostring( .strip() ) - def titles(self, article: "Element") -> "list[str]": + def titles(self, article: Element) -> list[str]: """ :param article: tag :return: (title (str) | None, alternative titles (set)) """ from itertools import combinations - titles: "list[str]" = [] + titles: list[str] = [] for title_element in article.findall("k"): if title_element.text is None: # TODO: look for tag? @@ -249,9 +252,11 @@ def titles(self, article: "Element") -> "list[str]": continue n_opts = len([c for c in title_element if c.tag == "opt"]) if n_opts: - for j in range(n_opts + 1): - for comb in combinations(list(range(n_opts)), j): - titles.append(self._mktitle(title_element, comb)) + titles += [ + self._mktitle(title_element, comb) + for j in range(n_opts + 1) + for comb in combinations(list(range(n_opts)), j) + ] else: titles.append(self._mktitle(title_element)) @@ -259,7 +264,7 @@ def titles(self, article: "Element") -> "list[str]": def _mktitle( # noqa: PLR6301 self, - title_element: "Element", + title_element: Element, include_opts: "Sequence | None" = None, ) -> str: if include_opts is None: diff --git a/pyglossary/plugins/yomichan.py b/pyglossary/plugins/yomichan.py index 75058a7ee..310ec58c4 100644 --- a/pyglossary/plugins/yomichan.py +++ b/pyglossary/plugins/yomichan.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- +from __future__ import annotations import json import re from collections.abc import Generator, Sequence -from typing import Any +from typing import TYPE_CHECKING, Any from pyglossary import os_utils from pyglossary.flags import ALWAYS -from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.option import ( BoolOption, IntOption, @@ -15,6 +15,9 @@ StrOption, ) +if TYPE_CHECKING: + from pyglossary.glossary_types import EntryType, GlossaryType + __all__ = [ "Writer", "description", @@ -201,7 +204,7 @@ def _isKanji(char: str) -> bool: ) -def _uniqueList(lst: "Sequence") -> "list[Any]": +def _uniqueList(lst: Sequence) -> "list[Any]": seen = set() result = [] for elem in lst: @@ -230,7 +233,7 @@ class Writer: _rule_vk_defi_pattern = "" _rule_adji_defi_pattern = "" - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._filename = "" # Yomichan technically supports "structured content" that renders to @@ -276,7 +279,7 @@ def _compileRegex(self) -> None: def _getExpressionsAndReadingFromEntry( self, - entry: "EntryType", + entry: EntryType, ) -> "tuple[list[str], str]": term_expressions = list(entry.l_word) if self._alternates_from_word_pattern: @@ -342,7 +345,7 @@ def _getRuleIdentifiersFromEntry(self, entry: EntryType) -> list[str]: def _getTermsFromEntry( self, - entry: "EntryType", + entry: EntryType, sequenceNumber: int, ) -> "list[list[Any]]": termExpressions, reading = self._getExpressionsAndReadingFromEntry(entry) @@ -379,7 +382,7 @@ def write(self) -> "Generator[None, EntryType, None]": entryCount = 0 termBankIndex = 0 - terms: "list[list[Any]]" = [] + terms: list[list[Any]] = [] def flushTerms() -> None: nonlocal termBankIndex diff --git a/pyglossary/reverse.py b/pyglossary/reverse.py index ee94fcdc6..849b47181 100644 --- a/pyglossary/reverse.py +++ b/pyglossary/reverse.py @@ -1,3 +1,5 @@ + +from __future__ import annotations import logging import re from operator import itemgetter @@ -12,7 +14,7 @@ def reverseGlossary( - glos: "GlossaryExtendedType", + glos: GlossaryExtendedType, savePath: str = "", words: "list[str] | None" = None, includeDefs: bool = False, @@ -41,7 +43,7 @@ def reverseGlossary( minWordLen = 3 includeDefs = False showRel = "None" - allowed values: "None", "Percent", "Percent At First" + allowed values: None, "Percent", "Percent At First" """ if not savePath: savePath = glos.getInfo("name") + ".txt" @@ -49,7 +51,7 @@ def reverseGlossary( if saveStep < 2: raise ValueError("saveStep must be more than 1") - entries: "list[EntryType]" = list(glos) + entries: list[EntryType] = list(glos) log.info(f"loaded {len(entries)} entries into memory") if words: @@ -97,10 +99,10 @@ def reverseGlossary( def takeOutputWords( - glos: "GlossaryExtendedType", - entryIter: "Iterable[EntryType]", + glos: GlossaryExtendedType, + entryIter: Iterable[EntryType], minWordLen: int = 3, -) -> "list[str]": +) -> list[str]: # fr"[\w]{{{minWordLen},}}" wordPattern = re.compile(r"[\w]{%d,}" % minWordLen, re.UNICODE) words = set() @@ -114,7 +116,7 @@ def takeOutputWords( def searchWordInDef( - entryIter: "Iterable[EntryType]", + entryIter: Iterable[EntryType], st: str, matchWord: bool = True, sepChars: str = ".,،", @@ -123,7 +125,7 @@ def searchWordInDef( minWordLen: int = 3, includeDefs: bool = False, showRel: str = "Percent", # "Percent" | "Percent At First" | "" -) -> "list[str]": +) -> list[str]: # searches word "st" in definitions of the glossary splitPattern = re.compile( "|".join(re.escape(x) for x in sepChars), diff --git a/pyglossary/sdsqlite.py b/pyglossary/sdsqlite.py index 04e61b29c..e7d9d1f69 100644 --- a/pyglossary/sdsqlite.py +++ b/pyglossary/sdsqlite.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import annotations + from os.path import isfile from typing import TYPE_CHECKING @@ -17,7 +19,7 @@ class Writer: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() @@ -94,7 +96,7 @@ def finish(self) -> None: class Reader: - def __init__(self, glos: "GlossaryType") -> None: + def __init__(self, glos: GlossaryType) -> None: self._glos = glos self._clear() diff --git a/pyglossary/slob.py b/pyglossary/slob.py index 7a11084ff..bee618b57 100644 --- a/pyglossary/slob.py +++ b/pyglossary/slob.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import encodings import io @@ -228,7 +229,7 @@ class IncorrectFileSize(FileFormatException): def sortkey( strength: int, maxlength: "int | None" = None, -) -> "Callable": +) -> Callable: # pass empty locale to use root locale # if you pass no arg, it will use system locale c: "T_Collator" = Collator.createInstance(Locale("")) @@ -247,7 +248,7 @@ def __init__( self, *args: str, ) -> None: - filenames: "list[str]" = list(args) + filenames: list[str] = list(args) files = [] ranges = [] offset = 0 @@ -263,7 +264,7 @@ def __init__( self._offset = -1 self.seek(0) - def __enter__(self) -> "MultiFileReader": + def __enter__(self) -> MultiFileReader: return self def __exit__( @@ -387,7 +388,7 @@ def __init__( # noqa: PLR0913 key: str, fragment: str, read_content_type_func: "Callable[[], str]", - read_func: "Callable", + read_func: Callable, ) -> None: # print(f"read_func is {type(read_func)}") # read_func is @@ -424,7 +425,7 @@ def __repr__(self) -> str: return f"<{self.__class__.__module__}.{self.__class__.__name__} {self.key}>" -def read_byte_string(f: "IOBase", len_spec: str) -> bytes: +def read_byte_string(f: IOBase, len_spec: str) -> bytes: length = unpack(len_spec, f.read(calcsize(len_spec)))[0] return f.read(length) @@ -432,7 +433,7 @@ def read_byte_string(f: "IOBase", len_spec: str) -> bytes: class StructReader: def __init__( self, - _file: "IOBase", + _file: IOBase, encoding: "str | None" = None, ) -> None: self._file = _file @@ -575,7 +576,7 @@ def write(self, data: bytes) -> int: return self._file.write(data) -def read_header(_file: "MultiFileReader") -> Header: +def read_header(_file: MultiFileReader) -> Header: _file.seek(0) magic = _file.read(len(MAGIC)) @@ -599,7 +600,7 @@ def read_tags() -> "dict[str, str]": tags = read_tags() def read_content_types() -> "Sequence[str]": - content_types: "list[str]" = [] + content_types: list[str] = [] count = reader.read_byte() for _ in range(count): content_type = reader.read_text() @@ -669,7 +670,7 @@ def __init__( self._header.content_types, ) - def __enter__(self) -> "Slob": + def __enter__(self) -> Slob: return self def __exit__( @@ -719,11 +720,11 @@ def count(self) -> int: # just to comply with Sequence and make type checker happy raise NotImplementedError - def index(self, x: "Blob") -> int: + def index(self, x: Blob) -> int: # just to comply with Sequence and make type checker happy raise NotImplementedError - def getBlobByIndex(self, i: int) -> "Blob": + def getBlobByIndex(self, i: int) -> Blob: ref = self._refs[i] def read_func() -> bytes: @@ -750,7 +751,7 @@ def get(self, blob_id: int) -> "tuple[str, bytes]": @cache def as_dict( - self: "Slob", + self: Slob, strength: int = TERTIARY, maxlength: "int | None" = None, ) -> KeydItemDict: @@ -771,9 +772,9 @@ def open(*filenames: str) -> Slob: class BinMemWriter: def __init__(self) -> None: - self.content_type_ids: "list[int]" = [] - self.item_dir: "list[bytes]" = [] - self.items: "list[bytes]" = [] + self.content_type_ids: list[int] = [] + self.item_dir: list[bytes] = [] + self.items: list[bytes] = [] self.current_offset = 0 def add(self, content_type_id: int, blob_bytes: bytes) -> None: @@ -788,7 +789,7 @@ def __len__(self) -> int: def finalize( self, - fout: "BufferedIOBase", + fout: BufferedIOBase, compress: "Callable[[bytes], bytes]", ) -> None: count = len(self) @@ -810,7 +811,7 @@ def finalize( class ItemList(Generic[ItemT]): def __init__( self, - reader: "StructReader", + reader: StructReader, offset: int, count_or_spec: "str | int", pos_spec: str, @@ -858,7 +859,7 @@ def __getitem__(self, i: int) -> ItemT: class RefList(ItemList[Ref]): def __init__( self, - f: "IOBase", + f: IOBase, encoding: str, offset: int = 0, count: "int | None" = None, @@ -874,12 +875,12 @@ def __init__( def __getitem__( self, i: int, - ) -> "Ref": + ) -> Ref: if i >= len(self) or i < 0: raise IndexError("index out of range") return cast(Ref, self.read(self.pos(i))) - def _read_item(self) -> "Ref": + def _read_item(self) -> Ref: key = self.reader.read_text() bin_index = self.reader.read_int() item_index = self.reader.read_short() @@ -893,7 +894,7 @@ def _read_item(self) -> "Ref": @cache def as_dict( - self: "RefList", + self: RefList, strength: int = TERTIARY, maxlength: "int | None" = None, ) -> KeydItemDict: @@ -930,10 +931,10 @@ class StoreItem(NamedTuple): class Store(ItemList[StoreItem]): def __init__( self, - _file: "IOBase", + _file: IOBase, offset: int, decompress: "Callable[[bytes], bytes]", - content_types: "Sequence[str]", + content_types: Sequence[str], ) -> None: super().__init__( reader=StructReader(_file), @@ -948,12 +949,12 @@ def __init__( def __getitem__( self, i: int, - ) -> "StoreItem": + ) -> StoreItem: if i >= len(self) or i < 0: raise IndexError("index out of range") return cast(StoreItem, self.read(self.pos(i))) - def _read_item(self) -> "StoreItem": + def _read_item(self) -> StoreItem: bin_item_count = self.reader.read_int() packed_content_type_ids = self.reader.read(bin_item_count * U_CHAR_SIZE) content_type_ids = [] @@ -1242,7 +1243,7 @@ def _resolve_aliases(self) -> None: # noqa: PLR0912 self._fire_event("begin_resolve_aliases") self.f_aliases.finalize() - def read_key_frag(item: "Blob", default_fragment: str) -> "tuple[str, str]": + def read_key_frag(item: Blob, default_fragment: str) -> "tuple[str, str]": key_frag = pickle.loads(item.content) if isinstance(key_frag, str): return key_frag, default_fragment @@ -1344,7 +1345,7 @@ def finalize(self) -> None: buf_size = 10 * 1024 * 1024 - def write_tags(tags: "MappingProxyType[str, Any]", f: "StructWriter") -> None: + def write_tags(tags: "MappingProxyType[str, Any]", f: StructWriter) -> None: f.write(pack(U_CHAR, len(tags))) for key, value in tags.items(): f.write_tiny_text(key) @@ -1361,7 +1362,7 @@ def write_tags(tags: "MappingProxyType[str, Any]", f: "StructWriter") -> None: def write_content_types( content_types: "dict[str, int]", - f: "StructWriter", + f: StructWriter, ) -> None: count = len(content_types) f.write(pack(U_CHAR, count)) @@ -1391,7 +1392,7 @@ def write_content_types( file_size += sum(os.stat(f.name).st_size for f in files) out.write_long(file_size) - def mv(src: "StructWriter", out: "StructWriter") -> None: + def mv(src: StructWriter, out: StructWriter) -> None: fname = src.name self._fire_event("begin_move", fname) with fopen(fname, mode="rb") as f: @@ -1462,7 +1463,7 @@ def size_data(self) -> int: ) return sum(os.stat(f.name).st_size for f in files) - def __enter__(self) -> "Slob": + def __enter__(self) -> Slob: return cast("Slob", self) def close(self) -> None: diff --git a/pyglossary/sort_keys.py b/pyglossary/sort_keys.py index 4cef8a14e..9b08056be 100644 --- a/pyglossary/sort_keys.py +++ b/pyglossary/sort_keys.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING, Any, NamedTuple @@ -24,7 +25,7 @@ from collections.abc import Callable from .icu_types import T_Collator, T_Locale - from .sort_keys_type import sortKeyType, sqliteSortKeyType + from .sort_keys_type import SortKeyType, SQLiteSortKeyType __all__ = [ "LocaleNamedSortKey", @@ -40,8 +41,8 @@ class NamedSortKey(NamedTuple): name: str desc: str - normal: "sortKeyType" - sqlite: "sqliteSortKeyType" + normal: SortKeyType + sqlite: SQLiteSortKeyType @dataclass(slots=True) # not frozen because of mod @@ -62,19 +63,19 @@ def module(self): # noqa: ANN201 return mod @property - def normal(self) -> "sortKeyType": + def normal(self) -> SortKeyType: return self.module.normal @property - def sqlite(self) -> "sqliteSortKeyType": + def sqlite(self) -> SQLiteSortKeyType: return self.module.sqlite @property - def locale(self) -> "sortKeyType | None": + def locale(self) -> "SortKeyType | None": return getattr(self.module, "locale", None) @property - def sqlite_locale(self) -> "Callable[..., sqliteSortKeyType] | None": + def sqlite_locale(self) -> "Callable[..., SQLiteSortKeyType] | None": return getattr(self.module, "sqlite_locale", None) diff --git a/pyglossary/sort_keys_types.py b/pyglossary/sort_keys_types.py index ae2696b70..7708d7ca7 100644 --- a/pyglossary/sort_keys_types.py +++ b/pyglossary/sort_keys_types.py @@ -1,14 +1,16 @@ +from __future__ import annotations + from collections.abc import Callable from typing import Any, TypeAlias -sortKeyType: "TypeAlias" = Callable[ +SortKeyType: TypeAlias = Callable[ [list[str]], Any, ] -sqliteSortKeyType: "TypeAlias" = list[tuple[str, str, sortKeyType]] +SQLiteSortKeyType: TypeAlias = list[tuple[str, str, SortKeyType]] __all__ = [ - "sortKeyType", - "sqliteSortKeyType", + "SQLiteSortKeyType", + "SortKeyType", ] diff --git a/pyglossary/sort_modules/dicformids.py b/pyglossary/sort_modules/dicformids.py index 147b6702a..8ce016253 100644 --- a/pyglossary/sort_modules/dicformids.py +++ b/pyglossary/sort_modules/dicformids.py @@ -1,21 +1,23 @@ +from __future__ import annotations + import re from typing import TYPE_CHECKING if TYPE_CHECKING: - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "DictionaryForMIDs" -def normal(**_options) -> "sortKeyType": +def normal(**_options) -> SortKeyType: re_punc = re.compile( r"""[!"$§%&/()=?´`\\{}\[\]^°+*~#'\-_.:,;<>@|]*""", # noqa: RUF001 ) re_spaces = re.compile(" +") re_tabs = re.compile("\t+") - def sortKey(words: "list[str]") -> str: + def sortKey(words: list[str]) -> str: word = words[0] word = word.strip() word = re_punc.sub("", word) @@ -27,7 +29,7 @@ def sortKey(words: "list[str]") -> str: return sortKey -def sqlite(**options) -> "sqliteSortKeyType": +def sqlite(**options) -> SQLiteSortKeyType: return [ ( "headword_norm", diff --git a/pyglossary/sort_modules/ebook.py b/pyglossary/sort_modules/ebook.py index 815c18f38..c21edaaad 100644 --- a/pyglossary/sort_modules/ebook.py +++ b/pyglossary/sort_modules/ebook.py @@ -1,7 +1,9 @@ +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType __all__ = ["normal", "sqlite"] @@ -12,11 +14,11 @@ def normal( sortEncoding: str = "utf-8", # noqa: ARG001 **options, -) -> "sortKeyType": +) -> SortKeyType: length = options.get("group_by_prefix_length", 2) # FIXME: return bytes - def sortKey(words: "list[str]") -> "tuple[str, str]": + def sortKey(words: list[str]) -> "tuple[str, str]": word = words[0] if not word: return "", "" @@ -28,10 +30,10 @@ def sortKey(words: "list[str]") -> "tuple[str, str]": return sortKey -def sqlite(sortEncoding: str = "utf-8", **options) -> "sqliteSortKeyType": +def sqlite(sortEncoding: str = "utf-8", **options) -> SQLiteSortKeyType: length = options.get("group_by_prefix_length", 2) - def getPrefix(words: "list[str]") -> str: + def getPrefix(words: list[str]) -> str: word = words[0] if not word: return "" @@ -40,7 +42,7 @@ def getPrefix(words: "list[str]") -> str: return "SPECIAL" return prefix - def headword(words: "list[str]") -> bytes: + def headword(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace") _type = "TEXT" if sortEncoding == "utf-8" else "BLOB" diff --git a/pyglossary/sort_modules/ebook_length3.py b/pyglossary/sort_modules/ebook_length3.py index efdf9719d..2f1048686 100644 --- a/pyglossary/sort_modules/ebook_length3.py +++ b/pyglossary/sort_modules/ebook_length3.py @@ -1,15 +1,17 @@ +from __future__ import annotations + from typing import TYPE_CHECKING from pyglossary.sort_modules import ebook if TYPE_CHECKING: - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "E-Book (prefix length: 3)" -def normal(sortEncoding: str = "utf-8", **_options) -> "sortKeyType": +def normal(sortEncoding: str = "utf-8", **_options) -> SortKeyType: return ebook.normal( sortEncoding=sortEncoding, group_by_prefix_length=3, @@ -19,7 +21,7 @@ def normal(sortEncoding: str = "utf-8", **_options) -> "sortKeyType": def sqlite( sortEncoding: str = "utf-8", **_options, -) -> "sqliteSortKeyType": +) -> SQLiteSortKeyType: return ebook.sqlite( sortEncoding=sortEncoding, group_by_prefix_length=3, diff --git a/pyglossary/sort_modules/headword.py b/pyglossary/sort_modules/headword.py index eb82c8dd4..05f4287d3 100644 --- a/pyglossary/sort_modules/headword.py +++ b/pyglossary/sort_modules/headword.py @@ -1,17 +1,19 @@ +from __future__ import annotations + from collections.abc import Callable from typing import TYPE_CHECKING if TYPE_CHECKING: from pyglossary.icu_types import T_Collator - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "Headword" -def normal(sortEncoding: str = "utf-8", **_options) -> "sortKeyType": - def sortKey(words: "list[str]") -> bytes: +def normal(sortEncoding: str = "utf-8", **_options) -> SortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace") return sortKey @@ -19,17 +21,17 @@ def sortKey(words: "list[str]") -> bytes: def locale( collator: "T_Collator", # noqa: F821 -) -> "sortKeyType": +) -> SortKeyType: cSortKey = collator.getSortKey - def sortKey(words: "list[str]") -> bytes: + def sortKey(words: list[str]) -> bytes: return cSortKey(words[0]) return lambda **_options: sortKey -def sqlite(sortEncoding: str = "utf-8", **_options) -> "sqliteSortKeyType": - def sortKey(words: "list[str]") -> bytes: +def sqlite(sortEncoding: str = "utf-8", **_options) -> SQLiteSortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace") return [ @@ -43,10 +45,10 @@ def sortKey(words: "list[str]") -> bytes: def sqlite_locale( collator: "T_Collator", # noqa: F821 -) -> "Callable[..., sqliteSortKeyType]": +) -> "Callable[..., SQLiteSortKeyType]": cSortKey = collator.getSortKey - def sortKey(words: "list[str]") -> bytes: + def sortKey(words: list[str]) -> bytes: return cSortKey(words[0]) return lambda **_options: [("sortkey", "BLOB", sortKey)] diff --git a/pyglossary/sort_modules/headword_bytes_lower.py b/pyglossary/sort_modules/headword_bytes_lower.py index daf1af10c..dbf0e34b8 100644 --- a/pyglossary/sort_modules/headword_bytes_lower.py +++ b/pyglossary/sort_modules/headword_bytes_lower.py @@ -1,7 +1,9 @@ +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "ASCII-Lowercase Headword" @@ -10,8 +12,8 @@ def normal( sortEncoding: str = "utf-8", **_options, -) -> "sortKeyType": - def sortKey(words: "list[str]") -> bytes: +) -> SortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace").lower() return sortKey @@ -19,12 +21,12 @@ def sortKey(words: "list[str]") -> bytes: # def locale( # collator: "T_Collator", # noqa: F821 -# ) -> "sortKeyType": +# ) -> SortKeyType: # raise NotImplementedError("") -def sqlite(sortEncoding: str = "utf-8", **_options) -> "sqliteSortKeyType": - def sortKey(words: "list[str]") -> bytes: +def sqlite(sortEncoding: str = "utf-8", **_options) -> SQLiteSortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace").lower() return [ diff --git a/pyglossary/sort_modules/headword_lower.py b/pyglossary/sort_modules/headword_lower.py index 8653d99ca..19f0a9cf4 100644 --- a/pyglossary/sort_modules/headword_lower.py +++ b/pyglossary/sort_modules/headword_lower.py @@ -1,17 +1,19 @@ +from __future__ import annotations + from collections.abc import Callable from typing import TYPE_CHECKING if TYPE_CHECKING: from pyglossary.icu_types import T_Collator - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "Lowercase Headword" -def normal(sortEncoding: str = "utf-8", **_options) -> "sortKeyType": - def sortKey(words: "list[str]") -> bytes: +def normal(sortEncoding: str = "utf-8", **_options) -> SortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].lower().encode(sortEncoding, errors="replace") return sortKey @@ -19,10 +21,10 @@ def sortKey(words: "list[str]") -> bytes: def locale( collator: "T_Collator", # noqa: F821 -) -> "sortKeyType": +) -> SortKeyType: cSortKey = collator.getSortKey - def sortKey(words: "list[str]") -> bytes: + def sortKey(words: list[str]) -> bytes: return cSortKey(words[0].lower()) return lambda **_options: sortKey @@ -31,8 +33,8 @@ def sortKey(words: "list[str]") -> bytes: def sqlite( sortEncoding: str = "utf-8", **_options, -) -> "sqliteSortKeyType": - def sortKey(words: "list[str]") -> bytes: +) -> SQLiteSortKeyType: + def sortKey(words: list[str]) -> bytes: return words[0].lower().encode(sortEncoding, errors="replace") return [ @@ -46,10 +48,10 @@ def sortKey(words: "list[str]") -> bytes: def sqlite_locale( collator: "T_Collator", # noqa: F821 -) -> "Callable[..., sqliteSortKeyType]": +) -> "Callable[..., SQLiteSortKeyType]": cSortKey = collator.getSortKey - def sortKey(words: "list[str]") -> bytes: + def sortKey(words: list[str]) -> bytes: return cSortKey(words[0].lower()) return lambda **_options: [("sortkey", "BLOB", sortKey)] diff --git a/pyglossary/sort_modules/random.py b/pyglossary/sort_modules/random.py index 9f5d16ec6..886380ba3 100644 --- a/pyglossary/sort_modules/random.py +++ b/pyglossary/sort_modules/random.py @@ -1,16 +1,18 @@ +from __future__ import annotations + from collections.abc import Callable from typing import TYPE_CHECKING if TYPE_CHECKING: from pyglossary.icu_types import T_Collator - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "Random" -def normal(**_options) -> "sortKeyType": +def normal(**_options) -> SortKeyType: from random import random return lambda _words: random() @@ -18,13 +20,13 @@ def normal(**_options) -> "sortKeyType": def locale( _collator: "T_Collator", # noqa: F821 -) -> "sortKeyType": +) -> SortKeyType: from random import random return lambda **_options: lambda _words: random() -def sqlite(**_options) -> "sqliteSortKeyType": +def sqlite(**_options) -> SQLiteSortKeyType: from random import random return [ @@ -39,7 +41,7 @@ def sqlite(**_options) -> "sqliteSortKeyType": def sqlite_locale( _collator: "T_Collator", # noqa: F821 **_options, -) -> "Callable[..., sqliteSortKeyType]": +) -> "Callable[..., SQLiteSortKeyType]": from random import random return lambda **_opt: [ diff --git a/pyglossary/sort_modules/stardict.py b/pyglossary/sort_modules/stardict.py index f8348c7b9..ee88155be 100644 --- a/pyglossary/sort_modules/stardict.py +++ b/pyglossary/sort_modules/stardict.py @@ -1,25 +1,27 @@ +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: - from .sort_keys_types import sortKeyType, sqliteSortKeyType + from .sort_keys_types import SortKeyType, SQLiteSortKeyType desc = "StarDict" -def normal(sortEncoding: str = "utf-8", **_options) -> "sortKeyType": - def sortKey(words: "list[str]") -> "tuple[bytes, bytes]": +def normal(sortEncoding: str = "utf-8", **_options) -> SortKeyType: + def sortKey(words: list[str]) -> "tuple[bytes, bytes]": b_word = words[0].encode(sortEncoding, errors="replace") return (b_word.lower(), b_word) return sortKey -def sqlite(sortEncoding: str = "utf-8", **_options) -> "sqliteSortKeyType": - def headword_lower(words: "list[str]") -> bytes: +def sqlite(sortEncoding: str = "utf-8", **_options) -> SQLiteSortKeyType: + def headword_lower(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace").lower() - def headword(words: "list[str]") -> bytes: + def headword(words: list[str]) -> bytes: return words[0].encode(sortEncoding, errors="replace") _type = "TEXT" if sortEncoding == "utf-8" else "BLOB" diff --git a/pyglossary/sq_entry_list.py b/pyglossary/sq_entry_list.py index 34335a07e..8f45452d1 100644 --- a/pyglossary/sq_entry_list.py +++ b/pyglossary/sq_entry_list.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import logging import os @@ -96,7 +97,7 @@ def rawEntryCompress(self, enable: bool) -> None: def setSortKey( self, - namedSortKey: "NamedSortKey", + namedSortKey: NamedSortKey, sortEncoding: "str | None", writeOptions: "dict[str, Any]", ) -> None: @@ -135,7 +136,7 @@ def setSortKey( def __len__(self) -> int: return self._len - def append(self, entry: "EntryType") -> None: + def append(self, entry: EntryType) -> None: if self._sqliteSortKey is None: raise RuntimeError("self._sqliteSortKey is None") rawEntry = self._entryToRaw(entry) @@ -159,7 +160,7 @@ def append(self, entry: "EntryType") -> None: if self._len % 1000 == 0: self._con.commit() - def __iadd__(self, other: "Iterable"): # -> Self + def __iadd__(self, other: Iterable): # -> Self for item in other: self.append(item) return self diff --git a/pyglossary/text_reader.py b/pyglossary/text_reader.py index 8981c8937..beb4cba3e 100644 --- a/pyglossary/text_reader.py +++ b/pyglossary/text_reader.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import logging import os @@ -8,6 +10,7 @@ if TYPE_CHECKING: from collections.abc import Generator, Iterator + from pyglossary.entry_base import MultiStr from pyglossary.glossary_types import EntryType, GlossaryType from pyglossary.compression import ( @@ -15,7 +18,6 @@ stdCompressions, ) from pyglossary.entry import DataEntry -from pyglossary.entry_base import MultiStr from pyglossary.io_utils import nullTextIO __all__ = ["TextFilePosWrapper", "TextGlossaryReader", "nextBlockResultType"] @@ -58,12 +60,12 @@ class TextGlossaryReader: compressions = stdCompressions - def __init__(self, glos: "GlossaryType", hasInfo: bool = True) -> None: + def __init__(self, glos: GlossaryType, hasInfo: bool = True) -> None: self._glos = glos self._filename = "" self._file: "io.TextIOBase" = nullTextIO self._hasInfo = hasInfo - self._pendingEntries: "list[EntryType]" = [] + self._pendingEntries: list[EntryType] = [] self._wordCount = 0 self._fileSize = 0 self._pos = -1 @@ -71,7 +73,7 @@ def __init__(self, glos: "GlossaryType", hasInfo: bool = True) -> None: self._fileIndex = -1 self._bufferLine = "" self._resDir = "" - self._resFileNames: "list[str]" = [] + self._resFileNames: list[str] = [] def _setResDir(self, resDir: str) -> bool: if isdir(resDir): @@ -166,7 +168,7 @@ def close(self) -> None: log.exception(f"error while closing file {self._filename!r}") self._file = nullTextIO - def newEntry(self, word: MultiStr, defi: str) -> "EntryType": + def newEntry(self, word: MultiStr, defi: str) -> EntryType: byteProgress: "tuple[int, int] | None" = None if self._fileSize and self._file is not None: byteProgress = (self._file.tell(), self._fileSize) @@ -230,7 +232,7 @@ def _genDataEntries( ) def __iter__(self) -> "Iterator[EntryType | None]": - resPathSet: "set[str]" = set() + resPathSet: set[str] = set() while True: self._pos += 1 if self._pendingEntries: diff --git a/pyglossary/text_utils.py b/pyglossary/text_utils.py index 15e983f17..816dbc44e 100644 --- a/pyglossary/text_utils.py +++ b/pyglossary/text_utils.py @@ -15,6 +15,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import binascii import logging @@ -48,13 +49,13 @@ endFormat = "\x1b[0;0;0m" # len=8 -def toStr(s: "AnyStr") -> str: +def toStr(s: AnyStr) -> str: if isinstance(s, bytes): return str(s, "utf-8") return str(s) -def fixUtf8(st: "AnyStr") -> str: +def fixUtf8(st: AnyStr) -> str: if isinstance(st, str): st = bytes(st, "utf-8") return st.replace(b"\x00", b"").decode("utf-8", "replace") @@ -87,7 +88,7 @@ def unescapeNTB(st: str, bar: bool = False) -> str: return st # noqa: RET504 -def splitByBarUnescapeNTB(st: str) -> "list[str]": +def splitByBarUnescapeNTB(st: str) -> list[str]: r""" Split by "|" (and not "\\|") then unescapes Newline (\\n), Tab (\\t), Baskslash (\\) and Bar (\\|) in each part @@ -107,7 +108,7 @@ def unescapeBar(st: str) -> str: return pattern_bar_us.sub(r"\1|", st).replace("\\\\", "\\") -def splitByBar(st: str) -> "list[str]": +def splitByBar(st: str) -> list[str]: r""" Split by "|" (and not "\\|") then unescapes Baskslash (\\) and Bar (\\|) in each part. @@ -115,7 +116,7 @@ def splitByBar(st: str) -> "list[str]": return [unescapeBar(part) for part in pattern_bar_sp.split(st)] -def joinByBar(parts: "list[str]") -> "str": +def joinByBar(parts: list[str]) -> str: return "|".join(escapeBar(part) for part in parts) diff --git a/pyglossary/text_writer.py b/pyglossary/text_writer.py index 41704cde4..6738adf51 100644 --- a/pyglossary/text_writer.py +++ b/pyglossary/text_writer.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from os.path import ( @@ -36,7 +38,7 @@ class TextGlossaryWriter: def __init__( self, - glos: "GlossaryType", + glos: GlossaryType, entryFmt: str = "", # contain {word} and {defi} writeInfo: bool = True, outInfoKeysAliasDict: "dict[str, str] | None" = None, @@ -212,7 +214,7 @@ def finish(self) -> None: def writeTxt( # noqa: PLR0913 - glos: "GlossaryType", + glos: GlossaryType, entryFmt: str = "", # contain {word} and {defi} filename: str = "", writeInfo: bool = True, diff --git a/pyglossary/ui/argparse_utils.py b/pyglossary/ui/argparse_utils.py index 33a02c6a7..6ba94caef 100644 --- a/pyglossary/ui/argparse_utils.py +++ b/pyglossary/ui/argparse_utils.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import argparse class StoreConstAction(argparse.Action): def __init__( self, - option_strings: "list[str]", + option_strings: list[str], same_dest: str = "", const_value: "bool | None" = None, nargs: int = 0, @@ -29,7 +31,7 @@ def __call__( # noqa: PLR0913 option_strings: list[str] | None = None, # noqa: ARG002 required: bool = False, # noqa: ARG002 dest: str | None = None, - ) -> "StoreConstAction": + ) -> StoreConstAction: if not parser: return self dest = self.dest diff --git a/pyglossary/ui/dependency.py b/pyglossary/ui/dependency.py index 190525549..a6a7dcafc 100644 --- a/pyglossary/ui/dependency.py +++ b/pyglossary/ui/dependency.py @@ -25,7 +25,7 @@ __all__ = ["checkDepends"] -def checkDepends(depends: "dict[str, str]") -> "list[str]": +def checkDepends(depends: "dict[str, str]") -> list[str]: """Return the list of non-installed dependencies.""" if not depends: return [] diff --git a/pyglossary/ui/option_ui.py b/pyglossary/ui/option_ui.py index 0d0b2585b..4162486d0 100644 --- a/pyglossary/ui/option_ui.py +++ b/pyglossary/ui/option_ui.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import argparse import json from typing import TYPE_CHECKING, Any @@ -11,7 +13,7 @@ def registerConfigOption( parser: "argparse.ArgumentParser", key: str, - option: "Option", + option: Option, ) -> None: if not option.hasFlag: return diff --git a/pyglossary/ui/tools/diff_glossary.py b/pyglossary/ui/tools/diff_glossary.py index 23947cfa8..0399ece3a 100755 --- a/pyglossary/ui/tools/diff_glossary.py +++ b/pyglossary/ui/tools/diff_glossary.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # mypy: ignore-errors +from __future__ import annotations import atexit import difflib @@ -120,7 +121,7 @@ def nextEntry2() -> None: entry2 = next(iter2) index2 += 1 - def printEntry(color: str, prefix: str, index: int, entry: "EntryType") -> None: + def printEntry(color: str, prefix: str, index: int, entry: EntryType) -> None: formatted = ( f"{color}{prefix}#{index} " + formatEntry(entry).replace("\n", "\n" + color) diff --git a/pyglossary/ui/tools/format_entry.py b/pyglossary/ui/tools/format_entry.py index af43a6f6c..45c9b17a1 100644 --- a/pyglossary/ui/tools/format_entry.py +++ b/pyglossary/ui/tools/format_entry.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -6,7 +8,7 @@ __all__ = ["formatEntry"] -def formatEntry(entry: "EntryType") -> str: +def formatEntry(entry: EntryType) -> str: words = entry.l_word headword = "" if words: diff --git a/pyglossary/ui/tools/view_glossary.py b/pyglossary/ui/tools/view_glossary.py index d26bb5877..dce5e5c00 100755 --- a/pyglossary/ui/tools/view_glossary.py +++ b/pyglossary/ui/tools/view_glossary.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # mypy: ignore-errors +from __future__ import annotations import os.path import shlex @@ -41,7 +42,7 @@ def getEntryHighlighter() -> "Callable[[EntryType], None] | None": h_lexer = HtmlLexer() x_lexer = XmlLexer() - def highlightEntry(entry: "EntryType") -> None: + def highlightEntry(entry: EntryType) -> None: entry.detectDefiFormat() if entry.defiFormat == "h": entry._defi = highlight(entry.defi, h_lexer, formatter) @@ -77,7 +78,7 @@ def viewGlossary( entrySep = "_" * 50 - def handleEntry(entry: "EntryType") -> None: + def handleEntry(entry: EntryType) -> None: nonlocal index if highlightEntry: highlightEntry(entry) diff --git a/pyglossary/ui/tools/word_diff.py b/pyglossary/ui/tools/word_diff.py index 545536631..3befd3158 100644 --- a/pyglossary/ui/tools/word_diff.py +++ b/pyglossary/ui/tools/word_diff.py @@ -14,11 +14,11 @@ ) -def plainWordSplit(text: str) -> "list[str]": +def plainWordSplit(text: str) -> list[str]: return [word for word in wordRE.split(text) if word] -def xmlWordSplit(text: str) -> "list[str]": +def xmlWordSplit(text: str) -> list[str]: pos = 0 words = [] diff --git a/pyglossary/ui/ui_cmd.py b/pyglossary/ui/ui_cmd.py index 5c5f89b7b..d17291ba7 100644 --- a/pyglossary/ui/ui_cmd.py +++ b/pyglossary/ui/ui_cmd.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License along # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations import os import sys @@ -55,11 +56,11 @@ def wc_ljust(text: str, length: int, padding: str = " ") -> str: COMMAND = "pyglossary" -def getColWidth(subject: str, strings: "list[str]") -> int: +def getColWidth(subject: str, strings: list[str]) -> int: return max(len(x) for x in [subject] + strings) -def getFormatsTable(names: "list[str]", header: str) -> str: +def getFormatsTable(names: list[str], header: str) -> str: descriptions = [Glossary.plugins[name].description for name in names] extensions = [" ".join(Glossary.plugins[name].extensions) for name in names] @@ -142,19 +143,19 @@ def parseFormatOptionsStr(st: str) -> "dict[str, Any] | None": class NullObj: - def __getattr__(self, attr: str) -> "NullObj": + def __getattr__(self, attr: str) -> NullObj: return self - def __setattr__(self, attr: str, value: "Any") -> None: + def __setattr__(self, attr: str, value: Any) -> None: pass - def __setitem__(self, key: str, value: "Any") -> None: + def __setitem__(self, key: str, value: Any) -> None: pass def __call__( self, - *args: "tuple[Any]", - **kwargs: "Mapping[Any]", + *args: tuple[Any], + **kwargs: Mapping[Any], ) -> None: pass @@ -173,7 +174,7 @@ def __init__( def onSigInt( self, - *_args: "tuple[Any]", + *_args: tuple[Any], ) -> None: log.info("") if self._toPause: @@ -228,8 +229,8 @@ def progressEnd(self) -> None: def reverseLoop( self, - *_args: "tuple[Any]", - **kwargs: "Mapping[Any]", + *_args: tuple[Any], + **kwargs: Mapping[Any], ) -> None: from pyglossary.reverse import reverseGlossary diff --git a/pyglossary/ui/ui_cmd_interactive.py b/pyglossary/ui/ui_cmd_interactive.py index 9cf835a7a..5cb293588 100644 --- a/pyglossary/ui/ui_cmd_interactive.py +++ b/pyglossary/ui/ui_cmd_interactive.py @@ -19,6 +19,8 @@ # with this program. Or on Debian systems, from /usr/share/common-licenses/GPL # If not, see . +from __future__ import annotations + """ To use this user interface: sudo pip3 install prompt_toolkit. @@ -327,7 +329,7 @@ def __init__( ) @staticmethod - def fs_pwd(args: "list[str]"): + def fs_pwd(args: list[str]): if args: print(f"extra arguments: {args}") print(os.getcwd()) @@ -362,7 +364,7 @@ def get_ls_l( details.append(f"-> {os.readlink(argPath)}") return " ".join(details) - def fs_ls(self, args: "list[str]"): + def fs_ls(self, args: list[str]): opts, args = self.ls_parser.parse_known_args(args=args) if opts.help: @@ -408,7 +410,7 @@ def fs_ls(self, args: "list[str]"): ) @staticmethod - def fs_cd_parent(args: "list[str]"): + def fs_cd_parent(args: list[str]): if args: log.error("This command does not take arguments") return @@ -417,7 +419,7 @@ def fs_cd_parent(args: "list[str]"): print(f"Changed current directory to: {newDir}") @staticmethod - def fs_cd(args: "list[str]"): + def fs_cd(args: list[str]): if len(args) != 1: log.error("This command takes exactly one argument") return @@ -583,14 +585,14 @@ def finish(self): # TODO: how to handle \r and \n in NewlineOption.values? @staticmethod - def getOptionValueSuggestValues(option: "Option"): + def getOptionValueSuggestValues(option: Option): if option.values: return [str(x) for x in option.values] if option.typ == "bool": return ["True", "False"] return None - def getOptionValueCompleter(self, option: "Option"): + def getOptionValueCompleter(self, option: Option): values = self.getOptionValueSuggestValues(option) if values: return WordCompleter( @@ -932,7 +934,7 @@ def askFinalOptions(self) -> "bool | Literal['back']": return True # convert - def getRunKeywordArgs(self) -> "dict": + def getRunKeywordArgs(self) -> dict: return { "inputFilename": self._inputFilename, "outputFilename": self._outputFilename, diff --git a/pyglossary/ui/ui_gtk.py b/pyglossary/ui/ui_gtk.py index 02b64dc2d..f85d57e46 100644 --- a/pyglossary/ui/ui_gtk.py +++ b/pyglossary/ui/ui_gtk.py @@ -118,7 +118,7 @@ def buffer_get_text(b): class FormatDialog(gtk.Dialog): def __init__( self, - descList: "list[str]", + descList: list[str], parent=None, **kwargs, ) -> None: @@ -264,7 +264,7 @@ class FormatButton(gtk.Button): noneLabel = "[Select Format]" dialogTitle = "Select Format" - def __init__(self, descList: "list[str]", parent=None) -> None: + def __init__(self, descList: list[str], parent=None) -> None: gtk.Button.__init__(self) self.set_label(self.noneLabel) ### @@ -313,7 +313,7 @@ class FormatOptionsDialog(gtk.Dialog): def __init__( self, formatName: str, - options: "list[str]", + options: list[str], optionsValues: "dict[str, Any]", parent=None, ) -> None: @@ -583,7 +583,7 @@ def getOptionsValues(self): class FormatBox(FormatButton): - def __init__(self, descList: "list[str]", parent=None) -> None: + def __init__(self, descList: list[str], parent=None) -> None: FormatButton.__init__(self, descList, parent=parent) self.optionsValues = {} diff --git a/pyglossary/ui/ui_gtk4.py b/pyglossary/ui/ui_gtk4.py index 7b9d4a95e..73fed012c 100644 --- a/pyglossary/ui/ui_gtk4.py +++ b/pyglossary/ui/ui_gtk4.py @@ -105,7 +105,7 @@ def buffer_get_text(b): class FormatDialog(gtk.Dialog): def __init__( self, - descList: "list[str]", + descList: list[str], parent=None, **kwargs, ) -> None: @@ -253,7 +253,7 @@ class FormatButton(gtk.Button): noneLabel = "[Select Format]" dialogTitle = "Select Format" - def __init__(self, descList: "list[str]", parent=None) -> None: + def __init__(self, descList: list[str], parent=None) -> None: gtk.Button.__init__(self) self.set_label(self.noneLabel) ### @@ -308,7 +308,7 @@ def __init__( self, app, formatName: str, - options: "list[str]", + options: list[str], optionsValues: "dict[str, Any]", **kwargs, ) -> None: @@ -626,7 +626,7 @@ def getOptionsValues(self): class FormatBox(FormatButton): - def __init__(self, app, descList: "list[str]", parent=None) -> None: + def __init__(self, app, descList: list[str], parent=None) -> None: self.app = app FormatButton.__init__(self, descList, parent=parent) diff --git a/pyglossary/ui/ui_tk.py b/pyglossary/ui/ui_tk.py index 8b8b783d1..e94529c22 100644 --- a/pyglossary/ui/ui_tk.py +++ b/pyglossary/ui/ui_tk.py @@ -16,6 +16,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from __future__ import annotations import logging import os @@ -140,7 +141,7 @@ def newReadOnlyText( widget.insert(1.0, text) widget.pack() - # widget.bind("", lambda e: "break") + # widget.bind("", lambda e: break) widget.configure(state="disabled") # if tkinter is 8.5 or above you'll want the selection background @@ -315,10 +316,10 @@ def update(self, event=None, labelText=""): # noqa: ARG002 class FormatDialog(tix.Toplevel): def __init__( # noqa: PLR0913 self, - descList: "list[str]", + descList: list[str], title: str, - onOk: "Callable", - button: "FormatButton", + onOk: Callable, + button: FormatButton, activeDesc: str = "", ) -> None: tix.Toplevel.__init__(self) @@ -505,9 +506,9 @@ class FormatButton(tk.Button): def __init__( self, - descList: "list[str]", + descList: list[str], dialogTitle: str, - onChange: "Callable", + onChange: Callable, master=None, ) -> None: self.var = tk.StringVar() @@ -867,8 +868,8 @@ class FormatOptionsButton(tk.Button): def __init__( self, kind: "Literal['Read', 'Write']", - values: "dict", - formatInput: "FormatButton", + values: dict, + formatInput: FormatButton, master=None, ) -> None: tk.Button.__init__( diff --git a/pyglossary/xdxf/css_js_transform.py b/pyglossary/xdxf/css_js_transform.py index 08dc8b530..1aa734c4c 100644 --- a/pyglossary/xdxf/css_js_transform.py +++ b/pyglossary/xdxf/css_js_transform.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import sys from io import BytesIO @@ -54,7 +56,7 @@ def __init__(self, encoding: str = "utf-8") -> None: } @staticmethod - def tostring(elem: "Element") -> str: + def tostring(elem: Element) -> str: from lxml import etree as ET return ( @@ -98,7 +100,7 @@ def writeString( # noqa: PLR0913 self, hf: "T_htmlfile", child: str, - parent: "Element", + parent: Element, prev: "None | str | Element", stringSep: "str | None" = None, ) -> None: @@ -136,7 +138,7 @@ def addSep() -> None: if trail: addSep() - def _write_example(self, hf: "T_htmlfile", elem: "Element") -> None: + def _write_example(self, hf: "T_htmlfile", elem: Element) -> None: prev = None stringSep = " " with hf.element( # noqa: PLR1702 @@ -174,17 +176,17 @@ def _write_example(self, hf: "T_htmlfile", elem: "Element") -> None: self.writeChild(hf, child, elem, prev, stringSep=stringSep) prev = child - def _write_ex_orig(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_ex_orig(self, hf: "T_htmlfile", child: Element) -> None: # TODO NOT REACHABLE sys.exit("NOT REACHABLE") with hf.element("i"): self.writeChildrenOf(hf, child) - def _write_ex_transl(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_ex_transl(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_iref(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_iref(self, hf: "T_htmlfile", child: Element) -> None: iref_url = child.attrib.get("href", "") if iref_url.endswith((".mp3", ".wav", ".aac", ".ogg")): # with hf.element("audio", src=iref_url): @@ -207,11 +209,11 @@ def _write_iref(self, hf: "T_htmlfile", child: "Element") -> None: ): self.writeChildrenOf(hf, child, stringSep=" ") - def _write_blockquote(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_blockquote(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": "m"}): self.writeChildrenOf(hf, child) - def _write_tr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_tr(self, hf: "T_htmlfile", child: Element) -> None: from lxml import etree as ET hf.write("[") @@ -219,7 +221,7 @@ def _write_tr(self, hf: "T_htmlfile", child: "Element") -> None: hf.write("]") hf.write(ET.Element("br")) - def _write_k(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_k(self, hf: "T_htmlfile", child: Element) -> None: self.logging_enabled = child.text == "iść" index = child.getparent().index(child) @@ -234,13 +236,13 @@ def _write_k(self, hf: "T_htmlfile", child: "Element") -> None: # hf.write(str(index)) # self.writeChildrenOf(hf, child) - def _write_mrkd(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_mrkd(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 if not child.text: return with hf.element("span", attrib={"class": child.tag}): hf.write(child.text) - def _write_kref(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_kref(self, hf: "T_htmlfile", child: Element) -> None: if not child.text: log.warning(f"kref with no text: {self.tostring(child)}") return @@ -253,54 +255,54 @@ def _write_kref(self, hf: "T_htmlfile", child: "Element") -> None: ): hf.write(child.text) - def _write_sr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_sr(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_pos(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_pos(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_abr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_abr(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": "abbr"}): self.writeChildrenOf(hf, child) - def _write_abbr(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_abbr(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 with hf.element("span", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_dtrn(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_dtrn(self, hf: "T_htmlfile", child: Element) -> None: self.writeChildrenOf(hf, child, sep=" ") - def _write_co(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_co(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): hf.write("(") self.writeChildrenOf(hf, child, sep=" ") hf.write(")") - def _write_basic_format(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_basic_format(self, hf: "T_htmlfile", child: Element) -> None: with hf.element(child.tag): self.writeChildrenOf(hf, child) # if child.text is not None: # hf.write(child.text.strip("\n")) - def _write_br(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_br(self, hf: "T_htmlfile", child: Element) -> None: from lxml import etree as ET hf.write(ET.Element("br")) self.writeChildrenOf(hf, child) - def _write_c(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_c(self, hf: "T_htmlfile", child: Element) -> None: color = child.attrib.get("c", "green") with hf.element("font", color=color): self.writeChildrenOf(hf, child) - def _write_rref(self, _hf: "T_htmlfile", child: "Element") -> None: + def _write_rref(self, _hf: "T_htmlfile", child: Element) -> None: if not child.text: log.warning(f"rref with no text: {self.tostring(child)}") return - def _write_def(self, hf: "T_htmlfile", elem: "Element") -> None: + def _write_def(self, hf: "T_htmlfile", elem: Element) -> None: has_nested_def = False has_deftext = False for child in elem.iterchildren(): @@ -327,33 +329,33 @@ def _write_def(self, hf: "T_htmlfile", elem: "Element") -> None: with hf.element("li"): self.writeChildrenOf(hf, elem) - def _write_deftext(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_deftext(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): self.writeChildrenOf(hf, child, stringSep=" ", sep=" ") - def _write_span(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_span(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span"): self.writeChildrenOf(hf, child) - def _write_gr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_gr(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_categ(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_categ(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", style="background-color: green;"): self.writeChildrenOf(hf, child, stringSep=" ") - def _write_opt(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_opt(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 if child.text: hf.write(" (") hf.write(child.text) hf.write(")") - def _write_img(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_img(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 with hf.element("img", attrib=dict(child.attrib)): pass - def _write_etm(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_etm(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 # Etymology (history and origin) # TODO: formatting? hf.write(f"{child.text}") @@ -361,8 +363,8 @@ def _write_etm(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR63 def writeChildElem( # noqa: PLR0913 self, hf: "T_htmlfile", - child: "Element", - parent: "Element", # noqa: ARG002 + child: Element, + parent: Element, # noqa: ARG002 prev: "None | str | Element", stringSep: "str | None" = None, # noqa: ARG002 ) -> None: @@ -387,7 +389,7 @@ def writeChild( # noqa: PLR0913 self, hf: "T_htmlfile", child: "str | Element", - parent: "Element", + parent: Element, prev: "None | str | Element", stringSep: "str | None" = None, ) -> None: @@ -423,7 +425,7 @@ def shouldAddSep( # noqa: PLR6301 def writeChildrenOf( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, sep: "str | None" = None, stringSep: "str | None" = None, ) -> None: @@ -435,7 +437,7 @@ def writeChildrenOf( prev = child @staticmethod - def stringify_children(elem: "Element") -> str: + def stringify_children(elem: Element) -> str: from itertools import chain from lxml.etree import tostring @@ -462,7 +464,7 @@ def stringify_children(elem: "Element") -> str: normalized_children += chunk.decode(encoding="utf-8") return normalized_children - def transform(self, article: "Element") -> str: + def transform(self, article: Element) -> str: from lxml import etree as ET # encoding = self._encoding diff --git a/pyglossary/xdxf/transform.py b/pyglossary/xdxf/transform.py index 81c424805..9bdf4afd6 100644 --- a/pyglossary/xdxf/transform.py +++ b/pyglossary/xdxf/transform.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from io import BytesIO from typing import TYPE_CHECKING, cast @@ -56,7 +58,7 @@ def __init__(self, encoding: str = "utf-8") -> None: } @staticmethod - def tostring(elem: "Element") -> str: + def tostring(elem: Element) -> str: from lxml import etree as ET return ( @@ -100,7 +102,7 @@ def writeString( # noqa: PLR0913 self, hf: "T_htmlfile", child: str, - parent: "Element", + parent: Element, prev: "None | str | Element", stringSep: "str | None" = None, ) -> None: @@ -139,7 +141,7 @@ def addSep() -> None: if trail: addSep() - def _write_example(self, hf: "T_htmlfile", elem: "Element") -> None: + def _write_example(self, hf: "T_htmlfile", elem: Element) -> None: prev = None stringSep = " " with hf.element( @@ -167,7 +169,7 @@ def _write_example(self, hf: "T_htmlfile", elem: "Element") -> None: self.writeChild(hf, child, elem, prev, stringSep=stringSep) prev = child - def _write_iref(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_iref(self, hf: "T_htmlfile", child: Element) -> None: iref_url = child.attrib.get("href", "") if iref_url.endswith((".mp3", ".wav", ".aac", ".ogg")): # with hf.element("audio", src=iref_url): @@ -190,11 +192,11 @@ def _write_iref(self, hf: "T_htmlfile", child: "Element") -> None: ): self.writeChildrenOf(hf, child, stringSep=" ") - def _write_blockquote(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_blockquote(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": "m"}): self.writeChildrenOf(hf, child) - def _write_tr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_tr(self, hf: "T_htmlfile", child: Element) -> None: from lxml import etree as ET hf.write("[") @@ -202,21 +204,21 @@ def _write_tr(self, hf: "T_htmlfile", child: "Element") -> None: hf.write("]") hf.write(ET.Element("br")) - def _write_k(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_k(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": child.tag}): # with hf.element(glos.titleTag(child.text)): # ^ no glos object here! with hf.element("b"): self.writeChildrenOf(hf, child) - def _write_mrkd(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_mrkd(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 if not child.text: return with hf.element("span", attrib={"class": child.tag}): with hf.element("b"): hf.write(child.text) - def _write_kref(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_kref(self, hf: "T_htmlfile", child: Element) -> None: if not child.text: log.warning(f"kref with no text: {self.tostring(child)}") return @@ -229,103 +231,103 @@ def _write_kref(self, hf: "T_htmlfile", child: "Element") -> None: ): hf.write(child.text) - def _write_sr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_sr(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("div", attrib={"class": child.tag}): self.writeChildrenOf(hf, child) - def _write_pos(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_pos(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): with hf.element("font", color="green"): with hf.element("i"): self.writeChildrenOf(hf, child) # NESTED 5 - def _write_abr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_abr(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", attrib={"class": child.tag}): with hf.element("font", color="green"): with hf.element("i"): self.writeChildrenOf(hf, child) # NESTED 5 - def _write_dtrn(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_dtrn(self, hf: "T_htmlfile", child: Element) -> None: self.writeChildrenOf(hf, child, sep=" ") - def _write_co(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_co(self, hf: "T_htmlfile", child: Element) -> None: self.writeChildrenOf(hf, child, sep=" ") - def _write_basic_format(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_basic_format(self, hf: "T_htmlfile", child: Element) -> None: with hf.element(child.tag): self.writeChildrenOf(hf, child) # if child.text is not None: # hf.write(child.text.strip("\n")) - def _write_br(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_br(self, hf: "T_htmlfile", child: Element) -> None: from lxml import etree as ET hf.write(ET.Element("br")) self.writeChildrenOf(hf, child) - def _write_c(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_c(self, hf: "T_htmlfile", child: Element) -> None: color = child.attrib.get("c", "green") with hf.element("font", color=color): self.writeChildrenOf(hf, child) - def _write_rref(self, _hf: "T_htmlfile", child: "Element") -> None: + def _write_rref(self, _hf: "T_htmlfile", child: Element) -> None: if not child.text: log.warning(f"rref with no text: {self.tostring(child)}") return - def _write_def(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_def(self, hf: "T_htmlfile", child: Element) -> None: # TODO: create a list (ol / ul) unless it has one item only # like FreeDict reader with hf.element("div"): self.writeChildrenOf(hf, child) - def _write_deftext(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_deftext(self, hf: "T_htmlfile", child: Element) -> None: self.writeChildrenOf(hf, child, stringSep=" ", sep=" ") - def _write_span(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_span(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span"): self.writeChildrenOf(hf, child) - def _write_abbr_def(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_abbr_def(self, hf: "T_htmlfile", child: Element) -> None: # _type = child.attrib.get("type", "") # {"": "", "grm": "grammatical", "stl": "stylistical", # "knl": "area/field of knowledge", "aux": "subsidiary" # "oth": "others"}[_type] self.writeChildrenOf(hf, child) - def _write_gr(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_gr(self, hf: "T_htmlfile", child: Element) -> None: from lxml import etree as ET with hf.element("font", color=self._gram_color): hf.write(child.text or "") hf.write(ET.Element("br")) - def _write_ex_orig(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_ex_orig(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("i"): self.writeChildrenOf(hf, child) - # def _write_ex_transl(self, hf: "T_htmlfile", child: "Element") -> None: + # def _write_ex_transl(self, hf: "T_htmlfile", child: Element) -> None: - def _write_categ(self, hf: "T_htmlfile", child: "Element") -> None: + def _write_categ(self, hf: "T_htmlfile", child: Element) -> None: with hf.element("span", style="background-color: green;"): self.writeChildrenOf(hf, child, stringSep=" ") - def _write_opt(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_opt(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 if child.text: hf.write(" (") hf.write(child.text) hf.write(")") - def _write_img(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_img(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 with hf.element("img", attrib=dict(child.attrib)): pass - def _write_abbr(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_abbr(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 # FIXME: may need an space or newline before it with hf.element("i"): hf.write(f"{child.text}") - def _write_etm(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR6301 + def _write_etm(self, hf: "T_htmlfile", child: Element) -> None: # noqa: PLR6301 # Etymology (history and origin) # TODO: formatting? hf.write(f"{child.text}") @@ -333,8 +335,8 @@ def _write_etm(self, hf: "T_htmlfile", child: "Element") -> None: # noqa: PLR63 def writeChildElem( # noqa: PLR0913 self, hf: "T_htmlfile", - child: "Element", - parent: "Element", # noqa: ARG002 + child: Element, + parent: Element, # noqa: ARG002 prev: "None | str | Element", stringSep: "str | None" = None, # noqa: ARG002 ) -> None: @@ -359,7 +361,7 @@ def writeChild( # noqa: PLR0913 self, hf: "T_htmlfile", child: "str | Element", - parent: "Element", + parent: Element, prev: "None | str | Element", stringSep: "str | None" = None, ) -> None: @@ -397,7 +399,7 @@ def shouldAddSep( # noqa: PLR6301 def writeChildrenOf( self, hf: "T_htmlfile", - elem: "Element", + elem: Element, sep: "str | None" = None, stringSep: "str | None" = None, ) -> None: @@ -408,7 +410,7 @@ def writeChildrenOf( self.writeChild(hf, child, elem, prev, stringSep=stringSep) prev = child - def transform(self, article: "Element") -> str: + def transform(self, article: Element) -> str: from lxml import etree as ET # encoding = self._encoding diff --git a/pyglossary/xdxf/xsl_transform.py b/pyglossary/xdxf/xsl_transform.py index 01915caaf..9ba3cb406 100644 --- a/pyglossary/xdxf/xsl_transform.py +++ b/pyglossary/xdxf/xsl_transform.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from os.path import join from typing import TYPE_CHECKING @@ -53,7 +55,7 @@ def tostring(elem: "_XSLTResultTree | Element") -> str: .strip() ) - def transform(self, article: "Element") -> str: + def transform(self, article: Element) -> str: result_tree = self._transform(article) text = self.tostring(result_tree) text = text.replace("
", "
") diff --git a/scripts/wiktextract/extract-schema.py b/scripts/wiktextract/extract-schema.py index 7bc821f8d..41cabea4c 100644 --- a/scripts/wiktextract/extract-schema.py +++ b/scripts/wiktextract/extract-schema.py @@ -44,7 +44,7 @@ def __dict__(self): valueSet: "dict[str, set]" = {} -def addToValueSet(value: "str | int | float | bool", path: "list[str]"): +def addToValueSet(value: "str | int | float | bool", path: list[str]): if isinstance(value, str) and "://" in value: return pathStr = ".".join(path) @@ -54,7 +54,7 @@ def addToValueSet(value: "str | int | float | bool", path: "list[str]"): valueSet[pathStr] = {value} -def getSchemaNode(path: "list[str]"): +def getSchemaNode(path: list[str]): node = schema for name in path: if name == "[]": @@ -76,7 +76,7 @@ def getSchemaNode(path: "list[str]"): return node -def updateSchema(_type: str, path: "list[str]"): +def updateSchema(_type: str, path: list[str]): node = getSchemaNode(path) prevType = node.Type if prevType and prevType != _type: @@ -86,7 +86,7 @@ def updateSchema(_type: str, path: "list[str]"): node.Type = _type -def parseList(data: "list[Any]", path: "list[str]", node: Node): +def parseList(data: list[Any], path: list[str], node: Node): node.Type = "list" if not node.ListOf: node.ListOf = Node() @@ -109,7 +109,7 @@ def parseList(data: "list[Any]", path: "list[str]", node: Node): updateSchema(itemTypesStr, path + ["[]"]) -def parseDict(data: "dict[str, Any]", path: "list[str]", node: Node): +def parseDict(data: "dict[str, Any]", path: list[str], node: Node): if not node.Dict: node.Dict = {} node.KeyScore = Counter() diff --git a/tests/g_appledict_bin_test.py b/tests/g_appledict_bin_test.py index cbcaa56ed..b795adb48 100644 --- a/tests/g_appledict_bin_test.py +++ b/tests/g_appledict_bin_test.py @@ -53,7 +53,7 @@ def __init__(self, *args, **kwargs): def convert_appledict_binary_to_txt( self, baseName: str, - files: "list[str]", + files: list[str], html_full: bool = False, resFiles: "dict[str, str] | None" = None, ): diff --git a/tests/glossary_test.py b/tests/glossary_test.py index 57e22ad40..b2ae329d6 100644 --- a/tests/glossary_test.py +++ b/tests/glossary_test.py @@ -119,7 +119,7 @@ def downloadFile(self, filename): _file.write(data) return fpath - def downloadDir(self, dirName: str, files: "list[str]") -> str: + def downloadDir(self, dirName: str, files: list[str]) -> str: dirPath = join(testCacheDir, self.fixDownloadFilename(dirName)) for fileRelPath in files: newFilePath = join(dirPath, fileRelPath) diff --git a/tests/glossary_v2_test.py b/tests/glossary_v2_test.py index ddb4b8b47..652977beb 100644 --- a/tests/glossary_v2_test.py +++ b/tests/glossary_v2_test.py @@ -120,7 +120,7 @@ def downloadFile(self, filename): _file.write(data) return fpath - def downloadDir(self, dirName: str, files: "list[str]") -> str: + def downloadDir(self, dirName: str, files: list[str]) -> str: dirPath = join(testCacheDir, self.fixDownloadFilename(dirName)) for fileRelPath in files: newFilePath = join(dirPath, fileRelPath) diff --git a/tests/option_test.py b/tests/option_test.py index 11bf748d5..8f4be4150 100644 --- a/tests/option_test.py +++ b/tests/option_test.py @@ -112,7 +112,7 @@ def test_float_failed(self): class TestOptionValidateStr(unittest.TestCase): - def newTester(self, customValue: bool, values: "list[str]"): + def newTester(self, customValue: bool, values: list[str]): def test(raw: str, valid: bool): opt = StrOption(customValue=customValue, values=values) valueActual, evalOkActual = opt.evaluate(raw) diff --git a/whitelist.py b/whitelist.py index 3a691a738..d9538a688 100644 --- a/whitelist.py +++ b/whitelist.py @@ -22,24 +22,24 @@ RawEntryType # unused import (pyglossary/entry.py:25) RawEntryType # unused import (pyglossary/glossary_v2.py:65) RawEntryType # unused import (pyglossary/sq_entry_list.py:30) -sortKeyType # unused import (pyglossary/sort_keys.py:27) -sortKeyType # unused import (pyglossary/sort_modules/dicformids.py:5) -sortKeyType # unused import (pyglossary/sort_modules/ebook_length3.py:6) -sortKeyType # unused import (pyglossary/sort_modules/ebook.py:4) -sortKeyType # unused import (pyglossary/sort_modules/headword_bytes_lower.py:4) -sortKeyType # unused import (pyglossary/sort_modules/headword_lower.py:7) -sortKeyType # unused import (pyglossary/sort_modules/headword.py:7) -sortKeyType # unused import (pyglossary/sort_modules/random.py:7) -sortKeyType # unused import (pyglossary/sort_modules/stardict.py:4) -sqliteSortKeyType # unused import (pyglossary/sort_keys.py:27) -sqliteSortKeyType # unused import (pyglossary/sort_modules/dicformids.py:5) -sqliteSortKeyType # unused import (pyglossary/sort_modules/ebook_length3.py:6) -sqliteSortKeyType # unused import (pyglossary/sort_modules/ebook.py:4) -sqliteSortKeyType # unused import (pyglossary/sort_modules/headword_bytes_lower.py:4) -sqliteSortKeyType # unused import (pyglossary/sort_modules/headword_lower.py:7) -sqliteSortKeyType # unused import (pyglossary/sort_modules/headword.py:7) -sqliteSortKeyType # unused import (pyglossary/sort_modules/random.py:7) -sqliteSortKeyType # unused import (pyglossary/sort_modules/stardict.py:4) +SortKeyType # unused import (pyglossary/sort_keys.py:27) +SortKeyType # unused import (pyglossary/sort_modules/dicformids.py:5) +SortKeyType # unused import (pyglossary/sort_modules/ebook_length3.py:6) +SortKeyType # unused import (pyglossary/sort_modules/ebook.py:4) +SortKeyType # unused import (pyglossary/sort_modules/headword_bytes_lower.py:4) +SortKeyType # unused import (pyglossary/sort_modules/headword_lower.py:7) +SortKeyType # unused import (pyglossary/sort_modules/headword.py:7) +SortKeyType # unused import (pyglossary/sort_modules/random.py:7) +SortKeyType # unused import (pyglossary/sort_modules/stardict.py:4) +SQLiteSortKeyType # unused import (pyglossary/sort_keys.py:27) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/dicformids.py:5) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/ebook_length3.py:6) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/ebook.py:4) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/headword_bytes_lower.py:4) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/headword_lower.py:7) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/headword.py:7) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/random.py:7) +SQLiteSortKeyType # unused import (pyglossary/sort_modules/stardict.py:4) T_Collator # unused import (pyglossary/slob.py:54) T_Collator # unused import (pyglossary/sort_keys.py:26) T_Collator # unused import (pyglossary/sort_modules/headword_lower.py:5)