diff --git a/README.md b/README.md index 3536b68..65b57e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ | in progress // в процессе разработки | |--------------------------------------| -| ----3 | +| -----4 | # Типограф для Web diff --git a/etpgrf/__init__.py b/etpgrf/__init__.py index 4dcabfb..c6e4c8c 100644 --- a/etpgrf/__init__.py +++ b/etpgrf/__init__.py @@ -12,4 +12,4 @@ __version__ = "0.1.0" from etpgrf.typograph import Typographer from etpgrf.hyphenation import Hyphenator -import etpgrf.config \ No newline at end of file +import etpgrf.defaults \ No newline at end of file diff --git a/etpgrf/comutil.py b/etpgrf/comutil.py index 6fb0e94..c81312f 100644 --- a/etpgrf/comutil.py +++ b/etpgrf/comutil.py @@ -1,5 +1,6 @@ # Общие функции для типографа etpgrf -from etpgrf.config import MODE_UNICODE, MODE_MNEMONIC, MODE_MIXED, DEFAULT_MODE, DEFAULT_LANGS, SUPPORTED_LANGS +from etpgrf.config import MODE_UNICODE, MODE_MNEMONIC, MODE_MIXED, SUPPORTED_LANGS +from etpgrf.defaults import etpgrf_settings import os import regex @@ -18,7 +19,7 @@ def parse_and_validate_mode( """ if mode_input is None: # Если mode_input не предоставлен явно, используем режим по умолчанию - _mode_input = DEFAULT_MODE + _mode_input = etpgrf_settings.MODE else: _mode_input = str(mode_input).lower() @@ -31,44 +32,44 @@ def parse_and_validate_mode( def parse_and_validate_langs( - langs_input: str | list[str] | tuple[str, ...] | frozenset[str] | None = None, + langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None, ) -> frozenset[str]: """ Обрабатывает и валидирует входной параметр языков. Если langs_input не предоставлен (None), используются языки по умолчанию (сначала из переменной окружения ETPGRF_DEFAULT_LANGS, затем внутренний дефолт). - :param langs_input: Язык(и) для обработки. Может быть строкой (например, "ru+en"), + :param langs: Язык(и) для обработки. Может быть строкой (например, "ru+en"), списком, кортежем или frozenset. :return: Frozenset валидированных кодов языков в нижнем регистре. :raises TypeError: Если langs_input имеет неожиданный тип. :raises ValueError: Если langs_input пуст после обработки или содержит неподдерживаемые коды. """ - _langs_input = langs_input + _langs = langs - if _langs_input is None: - # Если langs_input не предоставлен явно, будем выкручиваться и искать в разных местах + if _langs is None: + # Если langs не предоставлен явно, будем выкручиваться и искать в разных местах # 1. Попытка получить языки из переменной окружения системы env_default_langs = os.environ.get('ETPGRF_DEFAULT_LANGS') if env_default_langs: # Нашли язык для библиотеки в переменных окружения - _langs_input = env_default_langs + _langs = env_default_langs # print(f"Using ETPGRF_DEFAULT_LANGS from environment: {env_default_langs}") # Для отладки else: # Если в переменной окружения нет, используем то что есть в конфиге `etpgrf/config.py` - _langs_input = DEFAULT_LANGS + _langs = etpgrf_settings.DEFAULT_LANGS # print(f"Using library internal default langs: {DEFAULT_LANGS}") # Для отладки - if isinstance(_langs_input, str): + if isinstance(_langs, str): # Разделяем строку по любым небуквенным символам, приводим к нижнему регистру # и фильтруем пустые строки - parsed_lang_codes_list = [lang.lower() for lang in regex.split(r'[^a-zA-Z]+', _langs_input) if lang] - elif isinstance(_langs_input, (list, tuple, frozenset)): # frozenset тоже итерируемый + parsed_lang_codes_list = [lang.lower() for lang in regex.split(r'[^a-zA-Z]+', _langs) if lang] + elif isinstance(_langs, (list, tuple, frozenset)): # frozenset тоже итерируемый # Приводим к строке, нижнему регистру и проверяем, что строка не пустая - parsed_lang_codes_list = [str(lang).lower() for lang in _langs_input if str(lang).strip()] + parsed_lang_codes_list = [str(lang).lower() for lang in _langs if str(lang).strip()] else: raise TypeError( - f"etpgrf: параметр 'langs' должен быть строкой, списком, кортежем или frozenset. Получен: {type(_langs_input)}" + f"etpgrf: параметр 'langs' должен быть строкой, списком, кортежем или frozenset. Получен: {type(_langs)}" ) if not parsed_lang_codes_list: @@ -84,7 +85,7 @@ def parse_and_validate_langs( ) validated_langs_set.add(code) - # Эта проверка на случай, если parsed_lang_codes_list был не пуст, но все коды оказались невалидными + # Эта проверка на случай если parsed_lang_codes_list был не пуст, но все коды оказались невалидными # (хотя предыдущее исключение должно было сработать раньше для каждого невалидного кода). if not validated_langs_set: raise ValueError("etpgrf: не предоставлено ни одного валидного кода языка.") diff --git a/etpgrf/config.py b/etpgrf/config.py index 1fedc97..53f5bf4 100644 --- a/etpgrf/config.py +++ b/etpgrf/config.py @@ -1,23 +1,22 @@ # etpgrf/conf.py # Настройки по умолчанию для типографа etpgrf -from email.header import SPACE # Режимы "отдачи" результатов обработки MODE_UNICODE = "unicode" MODE_MNEMONIC = "mnemonic" MODE_MIXED = "mixed" -DEFAULT_MODE = MODE_MIXED +# DEFAULT_MODE = MODE_MIXED # Языки, поддерживаемые библиотекой LANG_RU = 'ru' # Русский LANG_EN = 'en' # Английский SUPPORTED_LANGS = frozenset([LANG_RU, LANG_EN]) # Язык(и) по умолчанию, если не указаны пользователем и не заданы через ETPGRF_DEFAULT_LANGS_MODULE -DEFAULT_LANGS = LANG_RU +# DEFAULT_LANGS = LANG_RU # Значения по умолчанию для параметров Hyphenator -DEFAULT_HYP_MAX_LEN = 10 # Максимальная длина слова без переносов -DEFAULT_HYP_MIN_LEN = 3 # Минимальный "хвост" слова для переноса +# DEFAULT_HYP_MAX_LEN = 10 # Максимальная длина слова без переносов +# DEFAULT_HYP_MIN_LEN = 3 # Минимальный "хвост" слова для переноса # ----------------- соответствия `unicode` и `mnemonic` для типографа diff --git a/etpgrf/defaults.py b/etpgrf/defaults.py new file mode 100644 index 0000000..64ec46f --- /dev/null +++ b/etpgrf/defaults.py @@ -0,0 +1,22 @@ +# etpgrf/defaults.py -- Настройки по умолчанию для типографа etpgrf +from etpgrf.config import LANG_RU, MODE_MIXED + +class HyphenationDefaults: + """ + Настройки по умолчанию для Hyphenator etpgrf. + """ + MAX_UNHYPHENATED_LEN: int = 14 + MIN_TAIL_LEN: int = 3 + + +class EtpgrfDefaultSettings: + """ + Общие настройки по умолчанию для всех модулей типографа etpgrf. + """ + def __init__(self): + self.LANGS: list[str] | str = LANG_RU + self.MODE: str = MODE_MIXED + self.hyphenation = HyphenationDefaults() + # self.quotes = EtpgrfQuoteDefaults() + +etpgrf_settings = EtpgrfDefaultSettings() \ No newline at end of file diff --git a/etpgrf/hyphenation.py b/etpgrf/hyphenation.py index 83c07b9..09a86e2 100755 --- a/etpgrf/hyphenation.py +++ b/etpgrf/hyphenation.py @@ -1,11 +1,17 @@ import regex -from etpgrf.config import LANG_RU, LANG_EN, SHY_ENTITIES, MODE_UNICODE, DEFAULT_HYP_MAX_LEN, DEFAULT_HYP_MIN_LEN +from etpgrf.config import LANG_RU, LANG_EN, SHY_ENTITIES, MODE_UNICODE +from etpgrf.defaults import etpgrf_settings from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs _RU_VOWELS_UPPER = frozenset(['А', 'О', 'И', 'Е', 'Ё', 'Э', 'Ы', 'У', 'Ю', 'Я']) _RU_CONSONANTS_UPPER = frozenset(['Б', 'В', 'Г', 'Д', 'Ж', 'З', 'К', 'Л', 'М', 'Н', 'П', 'Р', 'С', 'Т', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ']) _RU_J_SOUND_UPPER = frozenset(['Й']) _RU_SIGNS_UPPER = frozenset(['Ь', 'Ъ']) +_RU_OLD_I_DESYAT = frozenset(['І']) # И-десятеричное +_RU_OLD_YAT = frozenset(['Ѣ']) # Ять +_RU_OLD_FITA = frozenset(['Ѳ']) # Фита +_RU_OLD_IZHITSA = frozenset(['Ѵ']) # Ижица (может быть и гласной, и согласной - сложный случай!) + _EN_VOWELS_UPPER = frozenset(['A', 'E', 'I', 'O', 'U', 'Æ', 'Œ']) _EN_CONSONANTS_UPPER = frozenset(['B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z']) @@ -17,12 +23,12 @@ class Hyphenator: def __init__(self, langs: str | list[str] | tuple[str, ...] | frozenset[str] | None = None, mode: str = None, # Режим обработки текста - max_unhyphenated_len: int = DEFAULT_HYP_MAX_LEN, # Максимальная длина непереносимой группы - min_chars_per_part: int = DEFAULT_HYP_MIN_LEN): # Минимальная длина после переноса (хвост, который разрешено переносить) + max_unhyphenated_len: int | None = None, # Максимальная длина непереносимой группы + min_tail_len: int | None = None): # Минимальная длина после переноса (хвост, который разрешено переносить) self.langs: frozenset[str] = parse_and_validate_langs(langs) self.mode: str = parse_and_validate_mode(mode) - self.max_unhyphenated_len = max_unhyphenated_len - self.min_chars_per_part = min_chars_per_part + self.max_unhyphenated_len = etpgrf_settings.hyphenation.MAX_UNHYPHENATED_LEN if max_unhyphenated_len is None else max_unhyphenated_len + self.min_chars_per_part = etpgrf_settings.hyphenation.MIN_TAIL_LEN if min_tail_len is None else min_tail_len # Внутренние языковые ресурсы, если нужны специфично для переносов self._vowels: frozenset = frozenset() diff --git a/etpgrf/typograph.py b/etpgrf/typograph.py index 521df34..89b015f 100644 --- a/etpgrf/typograph.py +++ b/etpgrf/typograph.py @@ -1,4 +1,5 @@ from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs + from etpgrf.hyphenation import Hyphenator diff --git a/main.py b/main.py index 98168a4..a7817eb 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ if __name__ == '__main__': # --- Пример использования --- print("\n--- Пример использования класса---\n") - etpgrf.config.DEFAULT_HYP_MAX_LEN = 6 + etpgrf.defaults.etpgrf_settings.hyphenation.MAX_UNHYPHENATED_LEN = 8 # Определяем пользовательские правила переносов hyphen_settings = etpgrf.Hyphenator(langs='ru', max_unhyphenated_len=8)