tqdm/tqdm · error · AttributeError

`tk_parent` required when using `tkinter.NoDefaultRoot()`

Error message

`tk_parent` required when using `tkinter.NoDefaultRoot()`

What it means

tqdm.utils.CallbackIoContext (the object returned by file-like wrappers such as tqdm.wrapattr used on unknown methods) only knows how to wrap read or write; asking it to wrap another method name raises KeyError('Can only wrap read/write methods').

Source

Thrown at tqdm/tk.py:65

        """
        kwargs = kwargs.copy()
        kwargs['gui'] = True
        # convert disable = None to False
        kwargs['disable'] = bool(kwargs.get('disable', False))
        self._warn_leave = 'leave' in kwargs
        grab = kwargs.pop('grab', False)
        tk_parent = kwargs.pop('tk_parent', None)
        self._cancel_callback = kwargs.pop('cancel_callback', None)
        super().__init__(*args, **kwargs)

        if self.disable:
            return

        if tk_parent is None:  # Discover parent widget
            try:
                tk_parent = tkinter._default_root
            except AttributeError:
                raise AttributeError(
                    "`tk_parent` required when using `tkinter.NoDefaultRoot()`")
            if tk_parent is None:  # use new default root window as display
                self._tk_window = tkinter.Tk()
            else:  # some other windows already exist
                self._tk_window = tkinter.Toplevel()
        else:
            self._tk_window = tkinter.Toplevel(tk_parent)

        warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2)
        self._tk_dispatching = self._tk_dispatching_helper()

        self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel)
        self._tk_window.wm_title(self.desc)
        self._tk_window.wm_attributes("-topmost", 1)
        self._tk_window.after(0, lambda: self._tk_window.wm_attributes("-topmost", 0))
        self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0)
        self._tk_text_var = tkinter.StringVar(self._tk_window)
        pbar_frame = ttk.Frame(self._tk_window, padding=5)

View on GitHub (pinned to 96f2e60e45)

Solutions

  1. Wrap 'read' or 'write' only
  2. For other methods, count manually via update() inside your own loop
  3. Wrap the underlying buffered stream's read/write instead of the exotic method

Example fix

# before
t = tqdm.wrapattr(sock, 'send', total=n)
# after
t = tqdm(total=n)
# ... in send loop:
t.update(len(chunk))
Defensive patterns

Strategy: validation

Validate before calling

method = 'readline'
assert method in ('read', 'write'), f'cannot wrap {method!r}'

Type guard

def is_wrappable(method: str) -> bool:
    return method in ('read', 'write')

Try / catch

try:
    with tqdm.wrapattr(f, 'read', total=n) as _:
        data = _.read()
except KeyError:
    data = manual_read_with_progress(f)

Prevention

When it happens

Trigger: tqdm.wrapattr(f, 'readline', ...) or Tqdm.wrapattr(stream, 'send', ...) — any method name other than 'read' or 'write'.

Common situations: Trying to wrap socket.send, file.readline, or ws.send to count bytes progressed.

Related errors


AI-assisted analysis of tqdm/tqdm@96f2e60e45 (2026-08-28). Data as JSON: /api/errors/25045b766ed2766c. Report an issue: GitHub.