Files
simple-nanoshare/my_modules/textview/render.py
T
daniel156161 73fdeacbfe
Build and Push Docker Container / build-and-push (push) Successful in 2m25s
feat!: adopt Python 3.14 and 3.15 language features
- 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+.
2026-10-26 14:47:29 +01:00

68 lines
1.9 KiB
Python

from collections.abc import Awaitable, Callable
from .linkify import tokenize
from .own_links import parse_share_link
# Each resolved link costs one backend lookup, so a single document stays bounded.
MAX_LOOKUPS = 20
def _media_kind(content_type: str) -> str:
value = (content_type or '').split(';')[0].strip().lower()
for prefix in ('image', 'video', 'audio'):
if value.startswith(prefix + '/'):
return prefix
if 'pdf' in value:
return 'pdf'
return 'other'
async def build_segments(
text: str,
host: str,
root_path: str,
lookup: Callable[[str], Awaitable[dict | None]] | None = None,
max_lookups: int = MAX_LOOKUPS,
) -> list[dict]:
"""Turn raw text into template segments: plain text, links and resolved own files."""
segments: list[dict] = []
resolved: dict[str, dict | None] = {}
for kind, value in tokenize(text):
if kind == 'text':
segments.append({'kind': 'text', 'text': value})
continue
file_id = parse_share_link(value, host=host, root_path=root_path) if lookup else None
if file_id is None:
segments.append({'kind': 'link', 'url': value})
continue
if file_id not in resolved:
if len(resolved) >= max_lookups:
segments.append({'kind': 'link', 'url': value})
continue
resolved[file_id] = await lookup(file_id)
file_data = resolved[file_id]
if not file_data:
segments.append({'kind': 'link', 'url': value, 'state': 'missing'})
continue
if file_data.get('expired'):
segments.append({
'kind': 'link',
'url': value,
'state': 'expired',
'label': file_data.get('file_name') or file_id,
})
continue
segments.append({
'kind': 'file',
'url': value,
'state': 'ok',
'file_id': file_id,
'label': file_data.get('file_name') or file_id,
'media': _media_kind(file_data.get('content_type') or ''),
})
return segments