Build and Push Docker Container / build-and-push (push) Successful in 2m25s
- Require Python 3.15 for the server and Python 3.14 for the CLI. - Match the server requirement to quart_common's Python 3.15 features. - Replace future annotations with native deferred evaluation. - Defer CLI command dependencies and QR imports on Python 3.15. - Enable argparse suggestions for mistyped CLI commands. - Remove the obsolete tomllib import fallback. - Update my_helpers and quart_common submodule references. - Lock test dependencies and exclude them from Docker installations. - Add regression coverage for lazy imports and deferred annotations. - Document runtime requirements and standalone CLI validation. - Validate 276 tests on Python 3.15 and 18 CLI tests on Python 3.14. BREAKING CHANGE: The server now requires Python 3.15+ and the standalone CLI requires Python 3.14+.
44 lines
1.2 KiB
Python
44 lines
1.2 KiB
Python
import re
|
|
|
|
# Only http(s) is linkified; everything else stays inert plain text.
|
|
_URL_PATTERN = re.compile(r'https?://[^\s<>"\'`]+', re.IGNORECASE)
|
|
|
|
# Prose punctuation that is almost never part of the URL itself.
|
|
_TRAILING_PUNCTUATION = '.,;:!?\'"'
|
|
_BRACKET_PAIRS = {')': '(', ']': '[', '}': '{'}
|
|
|
|
def _trim_trailing(url: str) -> str:
|
|
"""Give punctuation that belongs to the surrounding sentence back to the text."""
|
|
while url:
|
|
last = url[-1]
|
|
if last in _TRAILING_PUNCTUATION:
|
|
url = url[:-1]
|
|
continue
|
|
opening = _BRACKET_PAIRS.get(last)
|
|
if opening is not None and url.count(opening) < url.count(last):
|
|
url = url[:-1]
|
|
continue
|
|
break
|
|
return url
|
|
|
|
def tokenize(text: str) -> list[tuple[str, str]]:
|
|
"""Split text into ('text', value) and ('url', value) tokens, order preserved."""
|
|
tokens: list[tuple[str, str]] = []
|
|
cursor = 0
|
|
|
|
for match in _URL_PATTERN.finditer(text):
|
|
url = _trim_trailing(match.group(0))
|
|
if not url:
|
|
continue
|
|
|
|
start = match.start()
|
|
end = start + len(url)
|
|
if start > cursor:
|
|
tokens.append(('text', text[cursor:start]))
|
|
tokens.append(('url', url))
|
|
cursor = end
|
|
|
|
if cursor < len(text):
|
|
tokens.append(('text', text[cursor:]))
|
|
return tokens
|