Skip to content

tftp:// URLs

tftp.TFTPURL(host, port, filename, mode='octet', options=dict()) dataclass

The tftp:// URL of one file: host, port, filename, mode, options.

Immutable and hashable. The constructor validates and normalises: the host is lower-cased (an IPv6 literal in its compressed form, its zone kept), the port is an int in 1..65535 (0 is the default, 69), the mode is "octet" or "netascii" and the file name is non-empty text without a NUL. options is a mapping of option name to text, kept as a read-only mapping with lower-case names: blksize, windowsize, timeout, tsize and rollover must be readable as the TFTPClient keyword of that name, any other name is a wire option, and mode is not an option. A host given as an ipaddress address or interface or a netimps.Host is reduced to its text. str(url) is the URL, in the ; spelling, and TFTPURL.parse(str(url)) == url.

:raises TypeError: an argument of the wrong type. :raises TFTPValueError: a value no URL can carry.

parse(text) classmethod

The URL text spells; the inverse of str(url).

:raises TypeError: text is not a str. :raises TFTPValueError: not a tftp:// URL, or one with a fragment, userinfo, both parameter delimiters unencoded, a repeated name, an empty or malformed pair, an option value its name cannot read, or a mode other than octet and netascii.

Source code in src/tftp/_uri.py
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
@classmethod
def parse(cls, text: str) -> "TFTPURL":
    """The URL ``text`` spells; the inverse of ``str(url)``.

    :raises TypeError: ``text`` is not a ``str``.
    :raises TFTPValueError: not a ``tftp://`` URL, or one with a
        fragment, userinfo, both parameter delimiters unencoded, a
        repeated name, an empty or malformed pair, an option value its
        name cannot read, or a mode other than ``octet`` and ``netascii``.
    """
    if not isinstance(text, str):
        raise TypeError("a URL is parsed from text, not %s" % type(text).__name__)
    if text[:7].lower() != "tftp://":
        raise TFTPValueError("not a tftp:// URL: %r" % text)
    if _has_control(text):
        raise TFTPValueError("a control character in the URL: %r" % text)
    rest = text[7:]
    if "#" in rest:
        raise TFTPValueError(
            "a tftp:// URL has no fragment (write %%23 in a file name or value): %r" % text
        )
    authority, slash, path = rest.partition("/")
    if not slash:
        raise TFTPValueError("tftp:// URL without a file name: %r" % text)
    if "@" in authority:
        raise TFTPValueError("a tftp:// URL has no userinfo: %r" % text)
    if authority.startswith("["):
        end = authority.find("]")
        if end < 0:
            raise TFTPValueError("unclosed '[' in %r" % text)
        host, tail = authority[1:end], authority[end + 1 :]
        if tail and not tail.startswith(":"):
            raise TFTPValueError("bad authority in %r" % text)
        port_text = tail[1:]
        if ":" not in host:
            raise TFTPValueError("brackets hold an IPv6 address only: %r" % text)
        address, percent, zone = host.partition("%")
        if percent:
            # RFC 6874 writes the zone delimiter as "%25"; a bare "%" is read too.
            zone = unquote(zone[2:] if zone.startswith("25") and len(zone) > 2 else zone)
            host = address + "%" + zone
    else:
        host, colon, port_text = authority.partition(":")
        if ":" in port_text:
            raise TFTPValueError("bad authority in %r (an IPv6 address needs brackets)" % text)
    if not host:
        raise TFTPValueError("tftp:// URL without a host: %r" % text)
    port = 69
    if port_text:
        if not (port_text.isascii() and port_text.isdigit()):
            raise TFTPValueError("bad port in %r" % text)
        port = int(port_text)
        if not 0 <= port <= 65535:
            raise TFTPValueError("bad port in %r" % text)
    quoted, mode, options = _split_parameters(path)
    return cls(host, port, _decode(quoted), "octet" if mode is None else mode, options)

try_parse(text, default=None) classmethod

:meth:parse, or default for text that is not a tftp:// URL.

Still raises :class:TypeError when text is not a str.

Source code in src/tftp/_uri.py
419
420
421
422
423
424
425
426
427
428
@classmethod
def try_parse(cls, text: str, default: Optional["TFTPURL"] = None) -> Optional["TFTPURL"]:
    """:meth:`parse`, or ``default`` for text that is not a ``tftp://`` URL.

    Still raises :class:`TypeError` when ``text`` is not a ``str``.
    """
    try:
        return cls.parse(text)
    except TFTPValueError:
        return default

tftp.download_url(url, dst, /, *, progress=None, **client_options)

Download the file a tftp:// URL names; client_options go to TFTPClient.

The URL's options become TFTPClient keywords; a keyword in client_options wins over the URL's option of the same name, and extra_options merge name by name.

Source code in src/tftp/_uri.py
444
445
446
447
448
449
450
451
452
453
454
455
456
def download_url(
    url: str, dst: SinkLike, /, *, progress: Optional[ProgressFunction] = None, **client_options: Any
) -> TransferResult:
    """Download the file a ``tftp://`` URL names; ``client_options`` go to ``TFTPClient``.

    The URL's options become ``TFTPClient`` keywords; a keyword in
    ``client_options`` wins over the URL's option of the same name, and
    ``extra_options`` merge name by name.
    """
    target = TFTPURL.parse(url)
    return TFTPClient(
        target.host, target.port, **_client_keywords_over(target.options, client_options)
    ).download(target.filename, dst, mode=target.mode, progress=progress)

tftp.upload_url(url, src, /, *, progress=None, **client_options)

Upload to the file a tftp:// URL names; client_options go to TFTPClient.

The URL's options and the precedence are as for :func:download_url.

Source code in src/tftp/_uri.py
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
def upload_url(
    url: str,
    src: SourceLike,
    /,
    *,
    progress: Optional[ProgressFunction] = None,
    **client_options: Any,
) -> TransferResult:
    """Upload to the file a ``tftp://`` URL names; ``client_options`` go to ``TFTPClient``.

    The URL's options and the precedence are as for :func:`download_url`.
    """
    target = TFTPURL.parse(url)
    return TFTPClient(
        target.host, target.port, **_client_keywords_over(target.options, client_options)
    ).upload(target.filename, src, mode=target.mode, progress=progress)