mod: add etpgrf typograph
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from cadpoint.settings import *
|
||||
from bs4 import BeautifulSoup
|
||||
from html import unescape
|
||||
import pytils
|
||||
import re
|
||||
|
||||
|
||||
@@ -12,42 +14,56 @@ def check_cookies(request) -> bool:
|
||||
|
||||
|
||||
def safe_html_special_symbols(s: str) -> str:
|
||||
""" Очистка строки от HTML-разметки типографа
|
||||
"""Преобразует HTML-фрагмент в чистый текст.
|
||||
|
||||
:param s: строка которую надо очистить
|
||||
:return: str:
|
||||
Удаляет все HTML-теги и декодирует HTML-сущности в Unicode.
|
||||
|
||||
:param s: строка, которую надо очистить
|
||||
:return: str: чистый текст без HTML-разметки
|
||||
"""
|
||||
# очистка строки от некоторых спец-символов HTML
|
||||
result = s.replace('­', '')
|
||||
result = result.replace('<span class="laquo">', '')
|
||||
result = result.replace('<span style="margin-right:0.44em;">', '')
|
||||
result = result.replace('<span style="margin-left:-0.44em;">', '')
|
||||
result = result.replace('<span class="raquo">', '')
|
||||
result = result.replace('<span class="point">', '')
|
||||
result = result.replace('<span class="thinsp">', ' ')
|
||||
result = result.replace('<span class="ensp">', '')
|
||||
result = result.replace('</span>', '')
|
||||
result = result.replace(' ', ' ')
|
||||
result = result.replace('«', '«')
|
||||
result = result.replace('»', '»')
|
||||
result = result.replace('…', '…')
|
||||
result = result.replace('<nobr>', '')
|
||||
result = result.replace('</nobr>', '')
|
||||
result = result.replace('—', '—')
|
||||
result = result.replace('№', '№')
|
||||
result = result.replace('<br />', ' ')
|
||||
result = result.replace('<br>', ' ')
|
||||
return result
|
||||
if not s:
|
||||
return ""
|
||||
|
||||
soup = BeautifulSoup(s, "html.parser")
|
||||
|
||||
# Скрипты и стили в чистый текст не нужны — выкидываем их целиком.
|
||||
for tag in soup(["script", "style", "noscript", "code", "kbd", "pre"]):
|
||||
tag.decompose()
|
||||
|
||||
result = soup.get_text()
|
||||
result = unescape(result).replace("\xa0", " ")
|
||||
# Убираем мягкие переносы и другие невидимые символы, которые не нужны
|
||||
# ни для slug, ни для человекочитаемого текста.
|
||||
result = result.translate({
|
||||
ord("\xad"): None, # символ мягкого переноса
|
||||
ord("\u200b"): None, # символ нулевой ширины (zero-width space)
|
||||
ord("\u200c"): None, # символ нулевой ширины (zero-width non-joiner)
|
||||
ord("\u200d"): None, # символ Zero Width Joiner (ZWJ)
|
||||
ord("\u2060"): None, # символ Word Joiner (WJ)
|
||||
ord("\ufeff"): None, # символ Zero Width No-Break Space (BOM)
|
||||
})
|
||||
return " ".join(result.split())
|
||||
|
||||
|
||||
def post_processing_html(s: str) -> str:
|
||||
s = re.sub(r"\s+", " ", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r">\s+|> ", "> ", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"\n|\r|<p[^>]*>\s*</p>|<p> </p>", "", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"</p>\s*<br[^>]*>", "</p>", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"<br[^>]*>\s*<p>|<p[^>]*>\s*<p[^>]*>", "<p>", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"</p>\s*</p>", "</p>", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"<br[^>]*>\s*<br[^>]*>", "<br />", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"<p><blockquote>", "<blockquote>", s, flags=re.IGNORECASE)
|
||||
s = re.sub(r"</blockquote></p>", "</blockquote>", s, flags=re.IGNORECASE)
|
||||
return s
|
||||
def clean_text_to_slug(s: str, default: str = "content") -> str:
|
||||
"""Готовит чистый slug из HTML/Unicode текста."""
|
||||
slug = pytils.translit.slugify(safe_html_special_symbols(s).lower())
|
||||
slug = re.sub(r"-+", "-", slug).strip("-")
|
||||
return slug or default
|
||||
|
||||
|
||||
# Удалить: HTML-постобработка была нужна только для старого типографа Муравьёва.
|
||||
# После перехода на `etpgrf` можно будет убрать и этот закомментированный блок,
|
||||
# и сам импорт `re`, если он больше нигде не понадобится.
|
||||
#
|
||||
# def post_processing_html(s: str) -> str:
|
||||
# s = re.sub(r"\s+", " ", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r">\s+|> ", "> ", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"\n|\r|<p[^>]*>\s*</p>|<p> </p>", "", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"</p>\s*<br[^>]*>", "</p>", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"<br[^>]*>\s*<p>|<p[^>]*>\s*<p[^>]*>", "<p>", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"</p>\s*</p>", "</p>", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"<br[^>]*>\s*<br[^>]*>", "<br />", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"<p><blockquote>", "<blockquote>", s, flags=re.IGNORECASE)
|
||||
# s = re.sub(r"</blockquote></p>", "</blockquote>", s, flags=re.IGNORECASE)
|
||||
# return s
|
||||
|
||||
@@ -1,15 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from etpgrf import Hyphenator, Typographer
|
||||
from filer.fields.image import FilerFileField
|
||||
from taggit.managers import TaggableManager
|
||||
from taggit.models import Tag, TaggedItem
|
||||
from web.add_function import safe_html_special_symbols, post_processing_html
|
||||
import urllib3
|
||||
from web.add_function import clean_text_to_slug, safe_html_special_symbols
|
||||
import pytils
|
||||
import random
|
||||
import datetime
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Типограф настраиваем один раз на модуль: в save() он только обрабатывает строку,
|
||||
# а не пересоздаётся на каждый объект контента.
|
||||
_TYPOGRAPHER_LANGS = 'ru+en'
|
||||
_TYPOGRAPHER_MAX_UNHYPHENATED_LEN = 14
|
||||
|
||||
def _build_typographer(hanging_punctuation=None) -> Typographer:
|
||||
"""Собирает `etpgrf` с едиными настройками для заголовка и текста."""
|
||||
return Typographer(
|
||||
langs=_TYPOGRAPHER_LANGS,
|
||||
process_html=True,
|
||||
hyphenation=Hyphenator(
|
||||
langs=_TYPOGRAPHER_LANGS,
|
||||
max_unhyphenated_len=_TYPOGRAPHER_MAX_UNHYPHENATED_LEN,
|
||||
),
|
||||
hanging_punctuation=hanging_punctuation,
|
||||
)
|
||||
|
||||
_TYPOGRAPHER_HEAD = _build_typographer(hanging_punctuation='left')
|
||||
_TYPOGRAPHER_TEXT = _build_typographer()
|
||||
|
||||
def _typograph_text(text: str, typographer: Typographer) -> str:
|
||||
"""Применяет `etpgrf` к HTML-фрагменту и не валит save при сбое библиотеки."""
|
||||
if not text:
|
||||
return text
|
||||
try:
|
||||
return typographer.process(text)
|
||||
except Exception:
|
||||
logger.exception("etpgrf не смог обработать текст, сохраняем исходный вариант")
|
||||
return text
|
||||
|
||||
|
||||
# класс для транслитерации русскоязычных slug
|
||||
@@ -113,13 +147,13 @@ class TbContent(models.Model):
|
||||
)
|
||||
bTypograf = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Типограф Стандарт",
|
||||
help_text="Обработать через <a href=\"https://www.typograf.ru\""
|
||||
" target=\"_blank\">Типограф 2.0</a><br />"
|
||||
"<small><b>НОРМАЛЬНЫЙ ТИПОГРАФ, ХОРОШИЙ HTML, РЕКОМЕНДУЕМ</b> "
|
||||
"«приклеивает» союзы, поддерживает неразрывные конструкции, "
|
||||
verbose_name="Типограф etpgrf",
|
||||
help_text="Обработать через <a href=\"https://typograph.cube2.ru/\""
|
||||
" target=\"_blank\">Типограф ETPRGF</a><br />"
|
||||
"<small><b>СТАБИЛЬНЫЙ И СОВРЕМЕННЫЙ ТИПОГРАФ, РЕКОМЕНДУЕМ</b> "
|
||||
"«приклеивает» союзы и предлоги, поддерживает неразрывные конструкции, "
|
||||
"замена тире, кавычек и дефисов, расстановка «мягких переносов» "
|
||||
"в словах длиннее 12 символов, убирает «вдовы» «сироты» (кроме "
|
||||
"в словах длиннее 14 символов, убирает «вдовы» «сироты» (кроме "
|
||||
"заголовков), расставляет абзацы (кроме заголовков), расшифровывает "
|
||||
"аббревиатуры (те, что знает и кроме заголовков), висячая "
|
||||
"пунктуация (только в заголовках) и т.п.</small>"
|
||||
@@ -158,87 +192,24 @@ class TbContent(models.Model):
|
||||
return u"%03d: %s" % (self.id, result[:50] + "…" if len(result) > 50 else result)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# переопределяем метод save() чтобы "проверуть" тексты через типографы...
|
||||
# Переопределяем save(), чтобы автоматически типографировать контент перед сохранением.
|
||||
if self.szContentSlug is None or self.szContentSlug == "" or " " in self.szContentSlug:
|
||||
# print("ку-ку", self.szContentHead)
|
||||
result_slug = pytils.translit.slugify(
|
||||
safe_html_special_symbols(self.szContentHead)).lower()
|
||||
while TbContent.objects.filter(szContentSlug=result_slug).count() != 0:
|
||||
result_slug = "%s-%x" % (result_slug[0: -3], int(random.uniform(0, 255)))
|
||||
base_slug = clean_text_to_slug(self.szContentHead)
|
||||
result_slug = base_slug
|
||||
suffix = 1
|
||||
while TbContent.objects.filter(szContentSlug=result_slug).exists():
|
||||
result_slug = f"{base_slug}-{suffix}"
|
||||
suffix += 1
|
||||
self.szContentSlug = result_slug
|
||||
if self.bTypograf:
|
||||
# Используем типограф Eugene Spearance (https://www.typograf.ru) через API
|
||||
# Настройки стиля типографики см. тут: https://www.typograf.ru/webservice/about/
|
||||
try:
|
||||
http = urllib3.PoolManager()
|
||||
resp = http.request("POST", "https://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szContentHead.encode('cp1251'),
|
||||
'xml': '<?xml version="1.0" encoding="windows-1251" ?>'
|
||||
'<preferences>'
|
||||
' <!-- Абзацы НЕ СТАВИМ-->'
|
||||
' <paragraph insert="0" />'
|
||||
' <!-- Переводы строк НЕ СТАВИМ -->'
|
||||
' <newline insert="0" />'
|
||||
' <!-- Неразрывные конструкции ДА -->'
|
||||
' <hanging-punct insert="1" />'
|
||||
' <!-- Переносы слов длиннее 12 знаков -->'
|
||||
' <hyphen insert="1" length="12" />'
|
||||
'</preferences>'.encode('cp1251')})
|
||||
result = resp.data.decode('cp1251')
|
||||
if len(result) <= 512:
|
||||
self.szContentHead = result
|
||||
resp = http.request("POST", "https://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szContentIntro.encode('cp1251'),
|
||||
'xml': '<?xml version="1.0" encoding="windows-1251" ?>'
|
||||
'<preferences>'
|
||||
' <!-- Висячая пунктуация УДАЛЯЕТСЯ -->'
|
||||
' <hanging-punct insert="1" />'
|
||||
' <!-- Висячие слова УДАЛЯЕМ -->'
|
||||
' <hanging-line delete="1" />'
|
||||
' <!-- Переносы слов длиннее 12 знаков -->'
|
||||
' <hyphen insert="1" length="12" />'
|
||||
' <!-- Параметры ссылок -->'
|
||||
' <link target="_blank" />'
|
||||
'</preferences>'.encode('cp1251')})
|
||||
self.szContentIntro = resp.data.decode('cp1251')
|
||||
resp = http.request("POST", "https://www.typograf.ru/webservice/",
|
||||
fields={"text": self.szContentBody.encode('cp1251'),
|
||||
'xml': '<?xml version="1.0" encoding="windows-1251" ?>'
|
||||
'<preferences>'
|
||||
' <!-- Висячая пунктуация УДАЛЯЕТСЯ -->'
|
||||
' <hanging-punct insert="1" />'
|
||||
' <!-- Висячие слова УДАЛЯЕМ -->'
|
||||
' <hanging-line delete="1" />'
|
||||
' <!-- Переносы слов длиннее 10 знаков -->'
|
||||
' <hyphen insert="1" length="12" />'
|
||||
' <!-- Параметры ссылок -->'
|
||||
' <link target="_blank" />'
|
||||
'</preferences>'.encode('cp1251')})
|
||||
self.szContentBody = resp.data.decode('cp1251')
|
||||
except:
|
||||
# если API типографа не доступен, то подключаем локальный типограф Муравьева
|
||||
import web.EMT as EMT
|
||||
emt_header = EMT.EMTypograph()
|
||||
emt_header.setup({'Text.paragraphs': 'off'})
|
||||
emt_header.set_text(self.szContentHead)
|
||||
self.szContentHead = emt_header.apply()
|
||||
emt_intro = EMT.EMTypograph()
|
||||
# print("==================================== self.szContentBody\n", self.szContentIntro)
|
||||
# print("-----------------")
|
||||
emt_intro.set_text(self.szContentIntro)
|
||||
# emt_intro.set_tag_layout(layout=EMT.LAYOUT_CLASS)
|
||||
self.szContentIntro = emt_intro.apply()
|
||||
self.szContentIntro = post_processing_html(self.szContentIntro)
|
||||
# print(self.szContentIntro)
|
||||
emt_body = EMT.EMTypograph()
|
||||
# print("==================================== self.szContentBody")
|
||||
# print(self.szContentBody)
|
||||
# print("-----------------")
|
||||
emt_body.set_text(self.szContentBody)
|
||||
# emt_body.set_tag_layout(layout=EMT.LAYOUT_CLASS)
|
||||
self.szContentBody = emt_body.apply()
|
||||
self.szContentBody = post_processing_html(self.szContentBody)
|
||||
# print(self.szContentBody)
|
||||
# `etpgrf` уже умеет HTML-режим и висячую пунктуацию, поэтому здесь
|
||||
# не нужен старый локальный fallback.
|
||||
# Для заголовка включаем левую висячую пунктуацию, а для анонса и
|
||||
# тела текста оставляем обычную обработку без hanging punctuation.
|
||||
self.szContentHead = _typograph_text(self.szContentHead, _TYPOGRAPHER_HEAD)
|
||||
self.szContentIntro = _typograph_text(self.szContentIntro, _TYPOGRAPHER_TEXT)
|
||||
self.szContentBody = _typograph_text(self.szContentBody, _TYPOGRAPHER_TEXT)
|
||||
self.bTypograf = False
|
||||
if self.dtContentCreate is None:
|
||||
self.dtContentCreate = datetime.datetime.now()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django import template
|
||||
from web.add_function import safe_html_special_symbols
|
||||
import pytils
|
||||
from web.add_function import clean_text_to_slug, safe_html_special_symbols
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -16,9 +15,9 @@ def slug_ru(value: str, arg: int) -> str:
|
||||
"""
|
||||
try:
|
||||
arg = int(arg)
|
||||
return pytils.translit.slugify(str(value).lower())[:int(arg)]
|
||||
return clean_text_to_slug(str(value))[:int(arg)]
|
||||
except ValueError:
|
||||
return pytils.translit.slugify(str(value).lower())
|
||||
return clean_text_to_slug(str(value))
|
||||
|
||||
|
||||
@register.filter
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import SimpleTestCase, TestCase
|
||||
from django.urls import reverse
|
||||
from taggit.models import Tag
|
||||
|
||||
from web.add_function import clean_text_to_slug, safe_html_special_symbols
|
||||
from web.legacy_links import build_canonical_url, replace_legacy_links
|
||||
from web.models import TbContent
|
||||
|
||||
|
||||
class LegacyLinksTests(SimpleTestCase):
|
||||
@@ -35,6 +39,23 @@ class LegacyLinksTests(SimpleTestCase):
|
||||
self.assertEqual(len(matches), 1)
|
||||
|
||||
|
||||
class SafeHtmlSpecialSymbolsTests(SimpleTestCase):
|
||||
def test_strips_html_tags_and_decodes_entities(self):
|
||||
text = '<p>«Привет <b>мир</b>» ­<script>alert(1)</script><style>p{}</style></p>'
|
||||
|
||||
|
||||
self.assertEqual(safe_html_special_symbols(text), '«Привет мир»')
|
||||
|
||||
def test_clean_text_to_slug_normalizes_non_latin_symbols(self):
|
||||
self.assertEqual(clean_text_to_slug('αβγ ΔΩ'), 'content')
|
||||
self.assertEqual(clean_text_to_slug('₽ € $ ₴ ₿'), 'content')
|
||||
|
||||
def test_tbcontent_str_uses_clean_text(self):
|
||||
item = TbContent(id=7, szContentHead='<b>«Привет мир»</b>')
|
||||
|
||||
self.assertEqual(str(item), '007: «Привет мир»')
|
||||
|
||||
|
||||
class TagAutocompleteTests(TestCase):
|
||||
def setUp(self):
|
||||
user_model = get_user_model()
|
||||
@@ -85,3 +106,40 @@ class TagAutocompleteTests(TestCase):
|
||||
self.assertEqual(payload['pagination']['more'], False)
|
||||
|
||||
|
||||
class TypographTests(TestCase):
|
||||
def test_save_generates_slug_from_clean_text(self):
|
||||
item = TbContent(szContentHead='<b>Привет мир</b>')
|
||||
|
||||
item.save()
|
||||
|
||||
self.assertEqual(item.szContentSlug, 'privet-mir')
|
||||
|
||||
def test_save_normalizes_non_latin_slug_to_default(self):
|
||||
item = TbContent(szContentHead='αβγ ΔΩ')
|
||||
|
||||
item.save()
|
||||
|
||||
self.assertEqual(item.szContentSlug, 'content')
|
||||
|
||||
def test_save_uses_etpgrf_and_clears_flag(self):
|
||||
item = TbContent(
|
||||
szContentHead='«Привет»',
|
||||
szContentIntro='<p>Абзац</p>',
|
||||
szContentBody='<p>Тело</p>',
|
||||
bTypograf=True,
|
||||
)
|
||||
|
||||
with patch('web.models._TYPOGRAPHER_HEAD.process') as head_process_mock, \
|
||||
patch('web.models._TYPOGRAPHER_TEXT.process') as text_process_mock:
|
||||
head_process_mock.side_effect = lambda text: f'HEAD[{text}]'
|
||||
text_process_mock.side_effect = lambda text: f'TEXT[{text}]'
|
||||
item.save()
|
||||
|
||||
self.assertEqual(head_process_mock.call_count, 1)
|
||||
self.assertEqual(text_process_mock.call_count, 2)
|
||||
self.assertEqual(item.szContentHead, 'HEAD[«Привет»]')
|
||||
self.assertEqual(item.szContentIntro, 'TEXT[<p>Абзац</p>]')
|
||||
self.assertEqual(item.szContentBody, 'TEXT[<p>Тело</p>]')
|
||||
self.assertFalse(item.bTypograf)
|
||||
|
||||
|
||||
|
||||
174
poetry.lock
generated
174
poetry.lock
generated
@@ -14,6 +14,28 @@ files = [
|
||||
[package.extras]
|
||||
tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"]
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.14.3"
|
||||
description = "Screen-scraping library"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"},
|
||||
{file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = ">=1.6.1"
|
||||
typing-extensions = ">=4.0.0"
|
||||
|
||||
[package.extras]
|
||||
cchardet = ["cchardet"]
|
||||
chardet = ["chardet"]
|
||||
charset-normalizer = ["charset-normalizer"]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.7"
|
||||
@@ -354,6 +376,22 @@ svglib = {version = "*", optional = true, markers = "extra == \"svg\""}
|
||||
[package.extras]
|
||||
svg = ["reportlab", "svglib"]
|
||||
|
||||
[[package]]
|
||||
name = "etpgrf"
|
||||
version = "0.1.6.post1"
|
||||
description = "Electro-Typographer: Python library for advanced web typography (non-breaking spaces, hyphenation, hanging punctuation and ."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "etpgrf-0.1.6.post1-py3-none-any.whl", hash = "sha256:0863b14385bdacdd405f137ca2ce6bdb6f683f0189e8c927196a1eee754366be"},
|
||||
{file = "etpgrf-0.1.6.post1.tar.gz", hash = "sha256:984d201cff232a58c05b6f4455a50f822162520df829ad4d543bfe0b7fd962a9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
beautifulsoup4 = ">=4.10.0"
|
||||
lxml = ">=4.9.0"
|
||||
regex = ">=2022.1.18"
|
||||
|
||||
[[package]]
|
||||
name = "lxml"
|
||||
version = "6.0.2"
|
||||
@@ -628,6 +666,129 @@ files = [
|
||||
{file = "pytils-0.4.4.tar.gz", hash = "sha256:9992a96caad57daa211584df1da4fd825f11e836d3ed93011785f1d02ab6f0ca"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2026.4.4"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win32.whl", hash = "sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee"},
|
||||
{file = "regex-2026.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win32.whl", hash = "sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735"},
|
||||
{file = "regex-2026.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b"},
|
||||
{file = "regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81"},
|
||||
{file = "regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0"},
|
||||
{file = "regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4"},
|
||||
{file = "regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45"},
|
||||
{file = "regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d"},
|
||||
{file = "regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reportlab"
|
||||
version = "4.4.10"
|
||||
@@ -670,6 +831,17 @@ enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.18.*)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.8.3"
|
||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95"},
|
||||
{file = "soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.5"
|
||||
@@ -772,4 +944,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.12,<3.13"
|
||||
content-hash = "8115895b95f66275813106c93c64640813e5939a984e76ad53a17032f0788de0"
|
||||
content-hash = "8284fc2ef5f2a06d27b41da40cc2067920b8fd5fed8f23621b777a15d8ca4559"
|
||||
|
||||
@@ -404,3 +404,20 @@ nav > .pagination > .page-item > .page-link {
|
||||
nav > .pagination > .page-item > .page-link:hover {background: #008DD2; transition: 0.6s;}
|
||||
nav > .pagination > .page-item > .page-link:not(hover) {transition: 1.2s;}
|
||||
nav > .pagination > .page-item.disabled > .page-link {color: silver; background: gray}
|
||||
|
||||
/* -----------------------------------------
|
||||
СТИЛИ ДЛЯ ВИСЯЧЕЙ ПУНКТУАЦИИ ТИПОГРАФА ETPGRF
|
||||
Значения отступов (padding) для компенсирующих пробелов и полей (margin) для самих символов висячей
|
||||
пунктуации приведены для шрифта Times New Roman и должны быть скорректированы в зависимости
|
||||
от выбранного вами шрифта.
|
||||
------------------------------------------ */
|
||||
/* --- ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ --- */
|
||||
.etp-laquo { margin-left: -0.49em; } /* « */
|
||||
.etp-ldquo, .etp-bdquo { margin-left: -0.4em; } /* “ “ */
|
||||
.etp-lsquo { margin-left: -0.22em; } /* ’ */
|
||||
.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.23em; } /* ( [ { */
|
||||
/* компенсирующие пробелы для левых висячих символов */
|
||||
.etp-sp-laquo { padding-right: 0.49em; }
|
||||
.etp-sp-ldquo, .etp-sp-bdquo { padding-right: 0.4em; }
|
||||
.etp-sp-lsquo { padding-right: 0.22em; }
|
||||
.etp-sp-lpar, .etp-sp-lsqb, .etp-sp-lcub { padding-right: 0.35em; }
|
||||
|
||||
@@ -23,6 +23,7 @@ django-environ = "^0.13"
|
||||
django-mptt = "^0.18.0"
|
||||
pytils = "^0.4.4"
|
||||
django-select2 = "^8.4.8"
|
||||
etpgrf = "^0.1.6"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
django-debug-toolbar = "^6.3"
|
||||
|
||||
Reference in New Issue
Block a user