Initial commit: Linux Transcriber app with multi-language and auto-detection support
This commit is contained in:
128
venv/lib/python3.12/site-packages/idna/cli.py
Normal file
128
venv/lib/python3.12/site-packages/idna/cli.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""Command-line interface for the :mod:`idna` package.
|
||||
|
||||
Invoked via ``python -m idna``. See :func:`main` for the entry point.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
from itertools import chain
|
||||
from typing import IO, Optional
|
||||
|
||||
from . import IDNAError, decode, encode
|
||||
from .core import _alabel_prefix, _unicode_dots_re
|
||||
from .package_data import __version__
|
||||
|
||||
|
||||
def _looks_like_alabel(s: str) -> bool:
|
||||
"""Return True if any label in ``s`` carries the ``xn--`` ACE prefix."""
|
||||
prefix = _alabel_prefix.decode("ascii")
|
||||
return any(label.lower().startswith(prefix) for label in _unicode_dots_re.split(s))
|
||||
|
||||
|
||||
def _build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="python -m idna",
|
||||
description=(
|
||||
"Convert a domain name between its Unicode (U-label) and "
|
||||
"ASCII-compatible (A-label) forms. With no mode flag, the "
|
||||
"direction is chosen from the first input — if it contains "
|
||||
"an xn-- label the stream is decoded, otherwise it is "
|
||||
"encoded — and the same mode is applied to every remaining "
|
||||
"input. UTS #46 mapping is applied by default; pass "
|
||||
"--strict to disable it. When no domains are given on the "
|
||||
"command line and stdin is piped, one domain per line is "
|
||||
"read from stdin."
|
||||
),
|
||||
)
|
||||
mode = parser.add_mutually_exclusive_group()
|
||||
mode.add_argument(
|
||||
"-e",
|
||||
"--encode",
|
||||
dest="mode",
|
||||
action="store_const",
|
||||
const="encode",
|
||||
help="Encode the input to its ASCII A-label form.",
|
||||
)
|
||||
mode.add_argument(
|
||||
"-d",
|
||||
"--decode",
|
||||
dest="mode",
|
||||
action="store_const",
|
||||
const="decode",
|
||||
help="Decode the input from its ASCII A-label form.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
action="store_true",
|
||||
help="Disable the default UTS #46 mapping and apply IDNA 2008 rules verbatim.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"idna {__version__}",
|
||||
)
|
||||
parser.add_argument(
|
||||
"domain",
|
||||
nargs="*",
|
||||
help="One or more domain names to convert. Omit to read from stdin.",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def _iter_stdin(stream: IO[str]) -> Iterable[str]:
|
||||
"""Yield non-empty stripped lines from ``stream``, ignoring blanks."""
|
||||
for line in stream:
|
||||
stripped = line.strip()
|
||||
if stripped:
|
||||
yield stripped
|
||||
|
||||
|
||||
def _convert_one(domain: str, mode: str, uts46: bool) -> bool:
|
||||
"""Convert ``domain`` and write the result; return ``False`` on failure."""
|
||||
try:
|
||||
if mode == "decode":
|
||||
print(decode(domain, uts46=uts46))
|
||||
else:
|
||||
print(encode(domain, uts46=uts46).decode("ascii"))
|
||||
except IDNAError as err:
|
||||
print(f"idna: {mode} failed for {domain!r}: {err}", file=sys.stderr)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def main(argv: Optional[list[str]] = None) -> int:
|
||||
"""Entry point for ``python -m idna``.
|
||||
|
||||
When more than one domain is supplied (via positional arguments or
|
||||
piped stdin) and no mode flag is given, the first input determines
|
||||
the direction and that mode is applied uniformly to the rest.
|
||||
|
||||
:param argv: Argument list excluding the program name. Defaults to
|
||||
:data:`sys.argv` when ``None``.
|
||||
:returns: ``0`` on success, ``1`` if any conversion fails.
|
||||
"""
|
||||
parser = _build_parser()
|
||||
args = parser.parse_args(argv)
|
||||
uts46 = not args.strict
|
||||
|
||||
if args.domain:
|
||||
domains: Iterable[str] = args.domain
|
||||
elif not sys.stdin.isatty():
|
||||
domains = _iter_stdin(sys.stdin)
|
||||
else:
|
||||
parser.error("a domain argument is required when stdin is a terminal")
|
||||
|
||||
iterator = iter(domains)
|
||||
first = next(iterator, None)
|
||||
if first is None:
|
||||
return 0
|
||||
|
||||
mode = args.mode or ("decode" if _looks_like_alabel(first) else "encode")
|
||||
|
||||
results = [_convert_one(domain, mode, uts46) for domain in chain([first], iterator)]
|
||||
return 0 if all(results) else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user