Skip to content

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 24 Jul 08:13

First published release with real content. 0.0.0 was a placeholder; everything
below accumulated since and is new to PyPI.

Added

  • Modern auth.login_ex login with 2FA. client.login(login_ex=True) uses
    the middleware's auth.login_ex mechanism (PASSWORD_PLAIN/API_KEY_PLAIN/
    TOKEN_PLAIN) instead of the legacy auth.login/login_with_*. It handles an
    OTP_REQUIRED challenge by continuing with auth.login_ex_continue — the OTP
    comes from the credential's otp_token or an otp_provider callback — and
    raises auth.AuthenticationError on AUTH_ERR/DENIED/etc. login_options
    overrides the server defaults. The legacy path remains the default and
    unchanged; a credential with no login_ex form (local-socket auth) falls back
    automatically. Validated live against TrueNAS 26.0.
  • Client convenience wrappers client.me() (auth.me), client.logout()
    (auth.logout), and client.ping() (core.ping).
  • Event subscriptions. Subscribe to middleware collection events over the
    existing websocket: client.subscribe("alert.list") (or
    client.api.alert.list.subscribe()) returns a Subscription. Consume events
    by iterating sub.events(timeout=...) — a bounded queue drained on the
    caller's thread, so backpressure is visible; a full queue drops the oldest
    event and counts it in sub.dropped rather than blocking. An optional
    callback is invoked inline on the reader thread (keep it fast; a raising
    callback is logged and contained). Each event is an Event(collection, msg, fields, id). Close with sub.unsubscribe() or a with block; closing the
    client ends every events() iterator cleanly. A subscription is bound to the
    current connection and does not survive a reconnect — the events()
    iterator ending is the signal to re-subscribe. Validated live against
    TrueNAS 26.0.
  • RunPath step directories. Adopt duho.runpath (requires duho>=0.4.0),
    wired into the per-target fan-out: a directory of numbered NN-name.py steps
    (no __init__.py), placed among the command sources (PYTRUENAS_PATH /
    --cmdspath / config commandspath, or nested one level inside a source
    directory), becomes a subcommand that runs the whole step sequence once per
    target
    , each target getting its own connected TrueNASClient — restoring
    the private predecessor's per-target RunPathCmd behavior the current
    duho-based pytruenas never had. A directory's optional __main__.py
    init(cmd, logger) builds the per-target client (reuse
    pytruenas.utils.runpath.default_init); steps are main(cmd, ctx) /
    main(cmd); -O/--rcopts and filename !/!strict/!enable tokens select
    steps. The step signature is duho's native main(cmd, ctx) rather than the
    predecessor's run(client, args, logger) (the logger travels on cmd, the
    client is ctx) — capability parity, not signature parity. The
    filename-modifier / --rcopts grammar follows the predecessor's intent with
    two of its original bugs fixed (the :!enable/.enabled attribute mismatch,
    and the Extend() nested-list double-collection), not reproduced.

Changed

  • Local network-adapter discovery uses netimps instead of ifaddr.
    pytruenas.ops.host.is_local_ip / find_adapter_in_network now delegate to
    netimps (a core dependency), so the optional host extra is removed
    those helpers work out of the box, no pip install pytruenas[host] needed.
    find_adapter_in_network now returns a netimps.Interface (was an ifaddr
    adapter). Requires netimps>=0.0.2, which also supplies the ws/wss default
    ports built-in, so utils/target.py no longer registers them at import.
  • The trailing TARGET... positionals are registered centrally. Every
    command's register hook previously had to call
    pytruenas.utils.cmd.register_targets(parser) last or silently lose the
    target grammar. pytruenas.main now wraps each command's hook and adds the
    positional after it, so targets stay trailing whatever positionals a command
    adds — including for a command with no register hook at all, and for
    third-party commands supplied via --cmdspath/PYTRUENAS_PATH, which now get
    the <command> [args...] [TARGET ...] grammar for free.
  • Commands declare their CLI fields via an Args class. call, query and
    generate-typings declare arguments on their Args class rather than adding
    them imperatively in register() (which they no longer define). Previously the
    Args class was inert — duho ignored it, register() did the real work, and
    the two were hand-synced. register() remains supported as the escape hatch
    for what declarations can't express. The CLI surface is unchanged.
  • Dependency floor duho>=0.4.0duho>=0.4.1 for the two behaviors the
    above depends on: a module command may declare its own Args class (added to
    the subparser before register runs), and the register hook is gated and
    introspected on the object actually called, so wrapping it app-wide works even
    for a command that defines no hook of its own.
  • Dependency floor duho>=0.3.2duho>=0.4.0. 0.4.0 carries the RunPath
    register(base=...) shared-root method inheritance, the __main__.py
    lifecycle, the corrected enable/!enable token spelling, and the Extend()
    nested-list fix that now flattens --cmdspath a:b to ['a', 'b'] (previously
    silently mis-collected as [['a', 'b']] for multi-value input).

Security

  • Passwords in a target connection string are redacted from logs. A target
    like wss://root:secret@nas passed as a positional was logged verbatim
    (Started: …/Finished: …, at INFO) and, worse, embedded in the --logto
    filename on disk. The password is now masked (wss://root:***@nas) at every
    such point via pytruenas.utils.target.redact — the username is kept, the
    real target still builds the client. The auth.Credentials "not supported"
    ValueError no longer carries the raw password/token/api_key kwargs
    (which an exc_info=True log would have surfaced). Command text logged by
    client.run is unchanged: that logging is intentional, opt-in via loglevel
    (default TRACE, off unless enabled), and suppressible with loglevel=0.

Fixed

  • Connection-string reassembly preserves reserved characters. Target.uri
    now percent-encodes userinfo and path, so a credential or path containing
    @ : / # round-trips instead of reassembling into a URL that reparses to a
    different host/port/path.
  • ops reads files as UTF-8, and narrower exception handling in auth
    (ValueError/TypeError rather than bare except Exception) so a genuine
    error surfaces instead of being swallowed behind a generic message.

Full Changelog: v0.0.0...v0.1.0