Metadata-Version: 2.4
Name: tldm
Version: 1.1.1
Summary: A Python progress bar for a new era
Project-URL: repository, https://github.com/eliotwrobson/tldm
Maintainer-email: "Eliot W. Robson" <eliot.robson24@gmail.com>
License: # Mozilla Public License Version 2.0
        
        1. Definitions
        
        ---
        
        1.1. "Contributor"
        means each individual or legal entity that creates, contributes to
        the creation of, or owns Covered Software.
        
        1.2. "Contributor Version"
        means the combination of the Contributions of others (if any) used
        by a Contributor and that particular Contributor's Contribution.
        
        1.3. "Contribution"
        means Covered Software of a particular Contributor.
        
        1.4. "Covered Software"
        means Source Code Form to which the initial Contributor has attached
        the notice in Exhibit A, the Executable Form of such Source Code
        Form, and Modifications of such Source Code Form, in each case
        including portions thereof.
        
        1.5. "Incompatible With Secondary Licenses"
        means
        
            (a) that the initial Contributor has attached the notice described
                in Exhibit B to the Covered Software; or
        
            (b) that the Covered Software was made available under the terms of
                version 1.1 or earlier of the License, but not also under the
                terms of a Secondary License.
        
        1.6. "Executable Form"
        means any form of the work other than Source Code Form.
        
        1.7. "Larger Work"
        means a work that combines Covered Software with other material, in
        a separate file or files, that is not Covered Software.
        
        1.8. "License"
        means this document.
        
        1.9. "Licensable"
        means having the right to grant, to the maximum extent possible,
        whether at the time of the initial grant or subsequently, any and
        all of the rights conveyed by this License.
        
        1.10. "Modifications"
        means any of the following:
        
            (a) any file in Source Code Form that results from an addition to,
                deletion from, or modification of the contents of Covered
                Software; or
        
            (b) any new file in Source Code Form that contains any Covered
                Software.
        
        1.11. "Patent Claims" of a Contributor
        means any patent claim(s), including without limitation, method,
        process, and apparatus claims, in any patent Licensable by such
        Contributor that would be infringed, but for the grant of the
        License, by the making, using, selling, offering for sale, having
        made, import, or transfer of either its Contributions or its
        Contributor Version.
        
        1.12. "Secondary License"
        means either the GNU General Public License, Version 2.0, the GNU
        Lesser General Public License, Version 2.1, the GNU Affero General
        Public License, Version 3.0, or any later versions of those
        licenses.
        
        1.13. "Source Code Form"
        means the form of the work preferred for making modifications.
        
        1.14. "You" (or "Your")
        means an individual or a legal entity exercising rights under this
        License. For legal entities, "You" includes any entity that
        controls, is controlled by, or is under common control with You. For
        purposes of this definition, "control" means (a) the power, direct
        or indirect, to cause the direction or management of such entity,
        whether by contract or otherwise, or (b) ownership of more than
        fifty percent (50%) of the outstanding shares or beneficial
        ownership of such entity.
        
        2. License Grants and Conditions
        
        ---
        
        2.1. Grants
        
        Each Contributor hereby grants You a world-wide, royalty-free,
        non-exclusive license:
        
        (a) under intellectual property rights (other than patent or trademark)
        Licensable by such Contributor to use, reproduce, make available,
        modify, display, perform, distribute, and otherwise exploit its
        Contributions, either on an unmodified basis, with Modifications, or
        as part of a Larger Work; and
        
        (b) under Patent Claims of such Contributor to make, use, sell, offer
        for sale, have made, import, and otherwise transfer either its
        Contributions or its Contributor Version.
        
        2.2. Effective Date
        
        The licenses granted in Section 2.1 with respect to any Contribution
        become effective for each Contribution on the date the Contributor first
        distributes such Contribution.
        
        2.3. Limitations on Grant Scope
        
        The licenses granted in this Section 2 are the only rights granted under
        this License. No additional rights or licenses will be implied from the
        distribution or licensing of Covered Software under this License.
        Notwithstanding Section 2.1(b) above, no patent license is granted by a
        Contributor:
        
        (a) for any code that a Contributor has removed from Covered Software;
        or
        
        (b) for infringements caused by: (i) Your and any other third party's
        modifications of Covered Software, or (ii) the combination of its
        Contributions with other software (except as part of its Contributor
        Version); or
        
        (c) under Patent Claims infringed by Covered Software in the absence of
        its Contributions.
        
        This License does not grant any rights in the trademarks, service marks,
        or logos of any Contributor (except as may be necessary to comply with
        the notice requirements in Section 3.4).
        
        2.4. Subsequent Licenses
        
        No Contributor makes additional grants as a result of Your choice to
        distribute the Covered Software under a subsequent version of this
        License (see Section 10.2) or under the terms of a Secondary License (if
        permitted under the terms of Section 3.3).
        
        2.5. Representation
        
        Each Contributor represents that the Contributor believes its
        Contributions are its original creation(s) or it has sufficient rights
        to grant the rights to its Contributions conveyed by this License.
        
        2.6. Fair Use
        
        This License is not intended to limit any rights You have under
        applicable copyright doctrines of fair use, fair dealing, or other
        equivalents.
        
        2.7. Conditions
        
        Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
        in Section 2.1.
        
        3. Responsibilities
        
        ---
        
        3.1. Distribution of Source Form
        
        All distribution of Covered Software in Source Code Form, including any
        Modifications that You create or to which You contribute, must be under
        the terms of this License. You must inform recipients that the Source
        Code Form of the Covered Software is governed by the terms of this
        License, and how they can obtain a copy of this License. You may not
        attempt to alter or restrict the recipients' rights in the Source Code
        Form.
        
        3.2. Distribution of Executable Form
        
        If You distribute Covered Software in Executable Form then:
        
        (a) such Covered Software must also be made available in Source Code
        Form, as described in Section 3.1, and You must inform recipients of
        the Executable Form how they can obtain a copy of such Source Code
        Form by reasonable means in a timely manner, at a charge no more
        than the cost of distribution to the recipient; and
        
        (b) You may distribute such Executable Form under the terms of this
        License, or sublicense it under different terms, provided that the
        license for the Executable Form does not attempt to limit or alter
        the recipients' rights in the Source Code Form under this License.
        
        3.3. Distribution of a Larger Work
        
        You may create and distribute a Larger Work under terms of Your choice,
        provided that You also comply with the requirements of this License for
        the Covered Software. If the Larger Work is a combination of Covered
        Software with a work governed by one or more Secondary Licenses, and the
        Covered Software is not Incompatible With Secondary Licenses, this
        License permits You to additionally distribute such Covered Software
        under the terms of such Secondary License(s), so that the recipient of
        the Larger Work may, at their option, further distribute the Covered
        Software under the terms of either this License or such Secondary
        License(s).
        
        3.4. Notices
        
        You may not remove or alter the substance of any license notices
        (including copyright notices, patent notices, disclaimers of warranty,
        or limitations of liability) contained within the Source Code Form of
        the Covered Software, except that You may alter any license notices to
        the extent required to remedy known factual inaccuracies.
        
        3.5. Application of Additional Terms
        
        You may choose to offer, and to charge a fee for, warranty, support,
        indemnity or liability obligations to one or more recipients of Covered
        Software. However, You may do so only on Your own behalf, and not on
        behalf of any Contributor. You must make it absolutely clear that any
        such warranty, support, indemnity, or liability obligation is offered by
        You alone, and You hereby agree to indemnify every Contributor for any
        liability incurred by such Contributor as a result of warranty, support,
        indemnity or liability terms You offer. You may include additional
        disclaimers of warranty and limitations of liability specific to any
        jurisdiction.
        
        4. Inability to Comply Due to Statute or Regulation
        
        ---
        
        If it is impossible for You to comply with any of the terms of this
        License with respect to some or all of the Covered Software due to
        statute, judicial order, or regulation then You must: (a) comply with
        the terms of this License to the maximum extent possible; and (b)
        describe the limitations and the code they affect. Such description must
        be placed in a text file included with all distributions of the Covered
        Software under this License. Except to the extent prohibited by statute
        or regulation, such description must be sufficiently detailed for a
        recipient of ordinary skill to be able to understand it.
        
        5. Termination
        
        ---
        
        5.1. The rights granted under this License will terminate automatically
        if You fail to comply with any of its terms. However, if You become
        compliant, then the rights granted under this License from a particular
        Contributor are reinstated (a) provisionally, unless and until such
        Contributor explicitly and finally terminates Your grants, and (b) on an
        ongoing basis, if such Contributor fails to notify You of the
        non-compliance by some reasonable means prior to 60 days after You have
        come back into compliance. Moreover, Your grants from a particular
        Contributor are reinstated on an ongoing basis if such Contributor
        notifies You of the non-compliance by some reasonable means, this is the
        first time You have received notice of non-compliance with this License
        from such Contributor, and You become compliant prior to 30 days after
        Your receipt of the notice.
        
        5.2. If You initiate litigation against any entity by asserting a patent
        infringement claim (excluding declaratory judgment actions,
        counter-claims, and cross-claims) alleging that a Contributor Version
        directly or indirectly infringes any patent, then the rights granted to
        You by any and all Contributors for the Covered Software under Section
        2.1 of this License shall terminate.
        
        5.3. In the event of termination under Sections 5.1 or 5.2 above, all
        end user license agreements (excluding distributors and resellers) which
        have been validly granted by You or Your distributors under this License
        prior to termination shall survive termination.
        
        ---
        
        -                                                                      *
        - 6.  Disclaimer of Warranty \*
        - ------------------------- \*
        -                                                                      *
        - Covered Software is provided under this License on an "as is" \*
        - basis, without warranty of any kind, either expressed, implied, or \*
        - statutory, including, without limitation, warranties that the \*
        - Covered Software is free of defects, merchantable, fit for a \*
        - particular purpose or non-infringing. The entire risk as to the \*
        - quality and performance of the Covered Software is with You. \*
        - Should any Covered Software prove defective in any respect, You \*
        - (not any Contributor) assume the cost of any necessary servicing, \*
        - repair, or correction. This disclaimer of warranty constitutes an \*
        - essential part of this License. No use of any Covered Software is \*
        - authorized under this License except under this disclaimer. \*
        -                                                                      *
        
        ---
        
        ---
        
        -                                                                      *
        - 7.  Limitation of Liability \*
        - -------------------------- \*
        -                                                                      *
        - Under no circumstances and under no legal theory, whether tort \*
        - (including negligence), contract, or otherwise, shall any \*
        - Contributor, or anyone who distributes Covered Software as \*
        - permitted above, be liable to You for any direct, indirect, \*
        - special, incidental, or consequential damages of any character \*
        - including, without limitation, damages for lost profits, loss of \*
        - goodwill, work stoppage, computer failure or malfunction, or any \*
        - and all other commercial damages or losses, even if such party \*
        - shall have been informed of the possibility of such damages. This \*
        - limitation of liability shall not apply to liability for death or \*
        - personal injury resulting from such party's negligence to the \*
        - extent applicable law prohibits such limitation. Some \*
        - jurisdictions do not allow the exclusion or limitation of \*
        - incidental or consequential damages, so this exclusion and \*
        - limitation may not apply to You. \*
        -                                                                      *
        
        ---
        
        8. Litigation
        
        ---
        
        Any litigation relating to this License may be brought only in the
        courts of a jurisdiction where the defendant maintains its principal
        place of business and such litigation shall be governed by laws of that
        jurisdiction, without reference to its conflict-of-law provisions.
        Nothing in this Section shall prevent a party's ability to bring
        cross-claims or counter-claims.
        
        9. Miscellaneous
        
        ---
        
        This License represents the complete agreement concerning the subject
        matter hereof. If any provision of this License is held to be
        unenforceable, such provision shall be reformed only to the extent
        necessary to make it enforceable. Any law or regulation which provides
        that the language of a contract shall be construed against the drafter
        shall not be used to construe this License against a Contributor.
        
        10. Versions of the License
        
        ---
        
        10.1. New Versions
        
        Mozilla Foundation is the license steward. Except as provided in Section
        10.3, no one other than the license steward has the right to modify or
        publish new versions of this License. Each version will be given a
        distinguishing version number.
        
        10.2. Effect of New Versions
        
        You may distribute the Covered Software under the terms of the version
        of the License under which You originally received the Covered Software,
        or under the terms of any subsequent version published by the license
        steward.
        
        10.3. Modified Versions
        
        If you create software not governed by this License, and you want to
        create a new license for such software, you may create and use a
        modified version of this License if you rename the license and remove
        any references to the name of the license steward (except to note that
        such modified license differs from this License).
        
        10.4. Distributing Source Code Form that is Incompatible With Secondary
        Licenses
        
        If You choose to distribute Source Code Form that is Incompatible With
        Secondary Licenses under the terms of this version of the License, the
        notice described in Exhibit B of this License must be attached.
        
        ## Exhibit A - Source Code Form License Notice
        
        This Source Code Form is subject to the terms of the Mozilla Public
        License, v. 2.0. If a copy of the MPL was not distributed with this
        file, You can obtain one at http://mozilla.org/MPL/2.0/.
        
        If it is not possible or desirable to put the notice in a particular
        file, then You may include the notice in a location (such as a LICENSE
        file in a relevant directory) where a recipient would be likely to look
        for such a notice.
        
        You may add additional accurate notices of copyright ownership.
        
        ## Exhibit B - "Incompatible With Secondary Licenses" Notice
        
        This Source Code Form is "Incompatible With Secondary Licenses", as
        defined by the Mozilla Public License, v. 2.0.
License-File: LICENCE
Keywords: bar,console,eta,meter,progress,progressbar,progressmeter,rate,terminal,time
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Environment :: MacOS X
Classifier: Environment :: Other Environment
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications
Classifier: Framework :: IPython
Classifier: Framework :: Jupyter
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: End Users/Desktop
Classifier: Intended Audience :: Other Audience
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
Classifier: Operating System :: MacOS
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft
Classifier: Operating System :: Microsoft :: MS-DOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Operating System :: POSIX :: BSD
Classifier: Operating System :: POSIX :: BSD :: FreeBSD
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: POSIX :: SunOS/Solaris
Classifier: Operating System :: Unix
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Desktop Environment
Classifier: Topic :: Office/Business
Classifier: Topic :: Other/Nonlisted Topic
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Pre-processors
Classifier: Topic :: Software Development :: User Interfaces
Classifier: Topic :: System :: Installation/Setup
Classifier: Topic :: System :: Logging
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Shells
Classifier: Topic :: Terminals
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: colorama
Requires-Dist: wcwidth
Description-Content-Type: text/markdown

# TL;DM

[![PyPI version](https://badge.fury.io/py/tldm.svg)](https://badge.fury.io/py/tldm)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tldm)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
[![tests](https://github.com/eliotwrobson/tldm/actions/workflows/test.yml/badge.svg)](https://github.com/eliotwrobson/tldm/actions/workflows/test.yml)
[![lint](https://github.com/eliotwrobson/tldm/actions/workflows/check.yml/badge.svg)](https://github.com/eliotwrobson/tldm/actions/workflows/check.yml)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)

**TL;DM** (too long; didn't monitor) is a fast, extensible progress bar for Python, forked from [tqdm](https://github.com/tqdm/tqdm). This fork was created to provide continued maintenance and development as the original tqdm project has become unmaintained.

Instantly make your loops show a smart progress meter - just wrap any iterable with `tldm(iterable)`, and you're done!

```python
from tldm import tldm
for i in tldm(range(10000)):
    ...
```

`76%|████████████████████████        | 7568/10000 [00:33<00:10, 229.00it/s]`

`trange(N)` can also be used as a convenient shortcut for `tldm(range(N))`.

A progress bar in Python with a focus on simplicity and ease of use. This library makes your loops display with a smart progress meter, offering predictive statistics and minimal overhead.

Works across all major platforms (Linux, Windows, macOS) and in all major environments (terminal, Jupyter notebooks, IPython, etc.).

---

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
  - [Iterable-based](#iterable-based)
  - [Manual Control](#manual-control)
- [Examples](#examples)
- [Parameters](#parameters)
- [Methods](#methods)
- [Convenience Functions](#convenience-functions)
- [Extensions](#extensions)
  - [Asyncio](#asyncio)
  - [Pandas Integration](#pandas-integration)
  - [Rich Integration](#rich-integration)
  - [Concurrent Processing](#concurrent-processing)
  - [Logging Integration](#logging-integration)
- [Advanced Usage](#advanced-usage)
  - [Description and Postfix](#description-and-postfix)
  - [Nested Progress Bars](#nested-progress-bars)
  - [Hooks and Callbacks](#hooks-and-callbacks)
  - [Writing Messages](#writing-messages)
- [FAQ and Known Issues](#faq-and-known-issues)
- [Contributing](#contributing)
- [License](#license)

---

## Installation

You can install `tldm` via pip:

```bash
pip install tldm
```

Latest development release:

```bash
pip install "git+https://github.com/eliotwrobson/tldm.git@devel#egg=tldm"
```

---

## Usage

`tldm` is very versatile and can be used in a number of ways. The two main ones are given below.

### Iterable-based

Wrap `tldm()` around any iterable:

```python
from tldm import tldm
from time import sleep

text = ""
for char in tldm(["a", "b", "c", "d"]):
    sleep(0.25)
    text = text + char
```

`trange(i)` is a special optimised instance of `tldm(range(i))`:

```python
from tldm import trange

for i in trange(100):
    sleep(0.01)
```

Instantiation outside of the loop allows for manual control over `tldm()`:

```python
pbar = tldm(["a", "b", "c", "d"])
for char in pbar:
    sleep(0.25)
    pbar.set_description("Processing %s" % char)
```

### Manual Control

Manual control of `tldm()` updates using a `with` statement:

```python
from tldm import tldm
from time import sleep

with tldm(total=100) as pbar:
    for i in range(10):
        sleep(0.1)
        pbar.update(10)
```

If the optional variable `total` (or an iterable with `len()`) is provided, predictive stats are displayed.

`with` is also optional (you can just assign `tldm()` to a variable, but in this case don't forget to `del` or `close()` at the end):

```python
pbar = tldm(total=100)
for i in range(10):
    sleep(0.1)
    pbar.update(10)
pbar.close()
```

---

## Examples

### Simple Loop with Progress Bar

```python
from tldm import trange
from time import sleep

for i in trange(16, leave=True):
    sleep(0.1)
```

### Nested Progress Bars

```python
from tldm import trange
from time import sleep

for i in trange(10, desc='1st loop'):
    for j in trange(5, desc='2nd loop'):
        for k in trange(50, desc='3rd loop', leave=False):
            sleep(0.01)
```

### Parallel Processing with Thread Pool

```python
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from random import random
from time import sleep
from tldm import tldm

def worker(n):
    interval = random() * 0.01
    total = 100
    for _ in tldm(range(total), desc=f"Task #{n}", position=n):
        sleep(interval)
    return n + 1

if __name__ == '__main__':
    with ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(worker, range(4)))
```

### Custom Prefix and Units

```python
from tldm import tldm
from time import sleep

# Custom unit and description
for i in tldm(range(100), desc="Processing", unit="files"):
    sleep(0.01)

# Custom unit with scaling
for i in tldm(range(1000000), unit="B", unit_scale=True, unit_divisor=1024):
    pass  # This will show KB, MB, etc.
```

---

## Parameters

### Core Parameters

- **iterable** : iterable, optional
  Iterable to decorate with a progressbar. Leave blank to manually manage the updates.

- **desc** : str, optional
  Prefix for the progressbar.

- **total** : int or float, optional
  The number of expected iterations. If unspecified, `len(iterable)` is used if possible. If `float("inf")` or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar).

- **leave** : bool, optional
  If [default: True], keeps all traces of the progressbar upon termination of iteration. If `None`, will leave only if `position` is `0`.

- **file** : `io.TextIOWrapper` or `io.StringIO`, optional
  Specifies where to output the progress messages (default: sys.stderr).

- **ncols** : int, optional
  The width of the entire output message. If specified, dynamically resizes the progressbar to stay within this bound.

- **mininterval** : float, optional
  Minimum progress display update interval [default: 0.1] seconds.

- **maxinterval** : float, optional
  Maximum progress display update interval [default: 10] seconds.

- **miniters** : int or float, optional
  Minimum progress display update interval, in iterations. If 0 and `dynamic_miniters`, will automatically adjust to equal `mininterval`.

- **ascii** : bool or str, optional
  If unspecified or False, use unicode (smooth blocks) to fill the meter. The fallback is to use ASCII characters.

- **disable** : bool, optional
  Whether to disable the entire progressbar wrapper [default: False]. If set to None, disable on non-TTY.

- **unit** : str, optional
  String that will be used to define the unit of each iteration [default: it].

- **unit_scale** : bool or int or float, optional
  If 1 or True, the number of iterations will be reduced/scaled automatically and a metric prefix following the International System of Units standard will be added (kilo, mega, etc.) [default: False].

- **dynamic_ncols** : bool, optional
  If set, constantly alters `ncols` to the environment (allowing for window resizes) [default: False].

- **smoothing** : float, optional
  Exponential moving average smoothing factor for speed estimates (ignored in GUI mode). Ranges from 0 (average speed) to 1 (current/instantaneous speed) [default: 0.3].

- **bar_format** : str, optional
  Specify a custom bar string format. May impact performance.

- **initial** : int or float, optional
  The initial counter value. Useful when restarting a progress bar [default: 0].

- **complete_bar_on_early_finish** : bool, optional
  If True, complete the bar when closing early without errors and a total is known [default: False].

- **position** : int, optional
  Specify the line offset to print this bar (starting from 0). Useful to manage multiple bars at once (eg, from threads).

- **postfix** : dict or `*`, optional
  Specify additional stats to display at the end of the bar.

- **unit_divisor** : float, optional
  [default: 1000], ignored unless `unit_scale` is True.

- **write_bytes** : bool, optional
  If (default: None) and `file` is unspecified, bytes will be written in Python 2.

- **lock_args** : tuple, optional
  Passed to `refresh` for intermediate output (initialisation, iterating, and updating).

- **nrows** : int, optional
  The screen height. If specified, hides nested bars outside this bound.

- **colour** : str, optional
  Bar colour (e.g. 'green', '#00ff00').

- **delay** : float, optional
  Don't display until [default: 0] seconds have elapsed.

---

## Methods

### `update(n=1)`

Manually update the progress bar, useful for streams such as reading files.

```python
with tldm(total=100) as pbar:
    for i in range(10):
        # do something
        pbar.update(10)
```

### `close()`

Cleanup and (if leave=False) remove the progressbar.

### `clear(nomove=False)`

Clear current bar display.

### `refresh()`

Force refresh the display of this bar.

### `set_description(desc=None, refresh=True)`

Set/modify description of the progress bar.

```python
pbar = tldm(range(10))
for i in pbar:
    pbar.set_description(f"Processing {i}")
```

### `set_postfix(ordered_dict=None, refresh=True, **kwargs)`

Set/modify postfix (additional stats) with automatic formatting based on datatype.

```python
from tldm import trange
from random import random
from time import sleep

with trange(10) as t:
    for i in t:
        t.set_postfix(loss=random(), accuracy=random())
        sleep(0.1)
```

### `print(*values, file=sys.stdout, sep=" ", end="\n", flush=False)`

Print messages via tldm without overlapping with the progress bar. This works like the builtin `print()` function and is the recommended way to print messages. Supports all standard print arguments including `flush`.

```python
from tldm import tldm
from time import sleep

for i in tldm(range(10)):
    if i == 5:
        tldm.print("Half way there!")
        tldm.print("Progress:", i, "out of", 10)
    sleep(0.1)
```

### `write(s, file=sys.stdout, end="\n", flush=False)`

Print a single string via tldm without overlapping with the progress bar. For most use cases, `tldm.print()` is more convenient.

```python
from tldm import tldm
from time import sleep

for i in tldm(range(10)):
    if i == 5:
        tldm.write("Half way there!")
    sleep(0.1)
```

### `reset(total=None)`

Reset the progress bar to 0 iterations for repeated use.

---

## Convenience Functions

All convenience functions use automatic environment detection by default, displaying notebook widgets in Jupyter/IPython environments and standard terminal output otherwise.

### `auto_tldm`

An alias that automatically selects between `tldm.notebook.tldm` (for Jupyter/IPython) and the standard `tldm.std.tldm` (for terminals). This is used internally by all convenience functions.

```python
from tldm import auto_tldm

# Works seamlessly in both notebooks and terminals
for i in auto_tldm(range(100)):
    pass
```

### `trange(*args, **kwargs)`

Shortcut for `auto_tldm(range(*args), **kwargs)`.

```python
from tldm import trange

for i in trange(100):
    pass
```

### `tenumerate(iterable, start=0, total=None, tldm_class=None, **tldm_kwargs)`

Equivalent of builtin `enumerate` with a progress bar.

**Note:** By default, `tldm_class` is automatically detected (`auto_tldm`) and will use notebook widgets in Jupyter/IPython or standard terminal output otherwise.

```python
from tldm import tenumerate

for i, item in tenumerate(['a', 'b', 'c']):
    print(f"{i}: {item}")
```

### `tzip(iter1, *iter2plus, **tldm_kwargs)`

Equivalent of builtin `zip` with a progress bar. Accepts optional `tldm_class` in kwargs (defaults to `auto_tldm`).

```python
from tldm import tzip

for a, b in tzip(range(100), range(100, 200)):
    pass
```

### `tmap(function, *sequences, **tldm_kwargs)`

Equivalent of builtin `map` with a progress bar. Accepts optional `tldm_class` in kwargs (defaults to `auto_tldm`).

```python
from tldm import tmap

results = list(tmap(lambda x: x**2, range(100)))
```

### `tproduct(*iterables, **tldm_kwargs)`

Equivalent of `itertools.product` with a progress bar. Accepts optional `tldm_class` in kwargs (defaults to `auto_tldm`).

```python
from tldm import tproduct

for combo in tproduct(range(10), range(10)):
    pass
```

### `tbatched(iterable, n, *, strict=False, total=None, tldm_class=None, **tldm_kwargs)`

Equivalent of `itertools.batched` (Python 3.12+) with a progress bar. Yields successive batches of size `n` from the iterable.

**Note:** Requires Python 3.12 or later (when `itertools.batched` was added).

```python
from tldm import tbatched

# Process items in batches of 3 with progress bar
for batch in tbatched(range(10), 3):
    print(batch)  # [0, 1, 2], [3, 4, 5], [6, 7, 8], [9]
```

---

## Extensions

> **Note:** Extension support is still a work in progress. The core library focuses on command-line use cases.

This tldm implementation includes several extension modules located in `tldm.extensions`:

### Asyncio

Asynchronous-friendly version of tldm for use with `async`/`await`:

```python
from tldm.extensions.asyncio import tldm_asyncio
import asyncio

async def main():
    async for i in tldm_asyncio(range(100)):
        await asyncio.sleep(0.01)

asyncio.run(main())
```

**Note**: When using `break` with async iterators, either call `pbar.close()` manually or use the context manager syntax to ensure proper cleanup:

```python
from tldm.extensions.asyncio import tldm_asyncio

with tldm_asyncio(range(100)) as pbar:
    async for i in pbar:
        if i == 50:
            break
```

### Pandas Integration

Apply tldm to pandas operations. There are multiple ways to register the pandas integration:

**Using the syntactic sugar (recommended):**

```python
import pandas as pd
import numpy as np
from tldm import tldm

# Register pandas integration - simple and clean!
tldm.pandas(desc="Processing")

df = pd.DataFrame(np.random.randint(0, 100, (1000, 6)))

# Now you can use progress_apply instead of apply
df.progress_apply(lambda x: x**2)

# Also works with groupby
df.groupby(0).progress_apply(lambda x: x**2)
```

**Alternative import style:**

```python
from tldm import pandas

# Register with default settings
pandas()

# Or with custom parameters
pandas(desc="Processing", ncols=80)
```

**Traditional import (also supported):**

```python
from tldm.extensions.pandas import tldm_pandas

# Register pandas integration
tldm_pandas(desc="Processing")
```

The pandas integration automatically uses the appropriate progress bar for your environment (terminal or Jupyter notebook).

### Rich Integration

Integration with the `rich` library for enhanced terminal output:

```python
from tldm.extensions.rich import tldm

for i in tldm(range(100)):
    pass
```

### Concurrent Processing

Convenient wrappers for concurrent futures:

```python
from tldm.extensions.concurrent import thread_map, process_map

# Thread-based parallel processing with progress bar
results = thread_map(lambda x: x**2, range(100), max_workers=4)

# Process-based parallel processing with progress bar
results = process_map(lambda x: x**2, range(100), max_workers=4)
```

### Logging Integration

Redirect console logging output to work seamlessly with tldm progress bars. This prevents log messages from interfering with progress bar display:

```python
import logging
from tldm import trange
from tldm.logging import logging_redirect_tldm

LOG = logging.getLogger(__name__)

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    with logging_redirect_tldm():
        for i in trange(9):
            if i == 4:
                LOG.info("console logging redirected to `tldm.write()`")
    # logging restored
```

The `logging_redirect_tldm()` context manager redirects console logging to `tldm.write()`, leaving other logging handlers (e.g., log files) unaffected. It automatically:

- Removes console handlers (stdout/stderr) from loggers
- Adds a `TldmLoggingHandler` that writes via `tldm.write()`
- Preserves formatters and log levels from the original console handlers
- Restores original handlers when exiting the context

You can also combine progress bars with logging redirection using `tldm_logging_redirect()`:

```python
import logging
from tldm.logging import tldm_logging_redirect

LOG = logging.getLogger(__name__)

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    with tldm_logging_redirect(total=10) as pbar:
        for i in range(10):
            LOG.info(f"Processing item {i}")
            pbar.update(1)
```

**Parameters:**

- `loggers`: List of loggers to redirect (default: `[logging.root]`)
- `tldm_class`: Progress bar class to use (default: `tldm.std.tldm`)

---

## Advanced Usage

### Description and Postfix

Custom information can be displayed and updated dynamically on `tldm` bars:

```python
from tldm import tldm, trange
from random import random, randint
from time import sleep

with trange(10) as t:
    for i in t:
        # Description will be displayed on the left
        t.set_description(f'GEN {i}')
        # Postfix will be displayed on the right,
        # formatted automatically based on argument's datatype
        t.set_postfix(loss=random(), gen=randint(1, 999), str='h', lst=[1, 2])
        sleep(0.1)
```

You can also use a custom `bar_format`:

```python
from tldm import tldm

with tldm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}",
          postfix=["Batch", {"value": 0}]) as t:
    for i in range(10):
        t.postfix[1]["value"] = i / 2
        t.update()
```

### Nested Progress Bars

`tldm` supports nested progress bars. Here's an example:

```python
from tldm import trange
from time import sleep

for i in trange(4, desc='1st loop'):
    for j in trange(5, desc='2nd loop'):
        for k in trange(50, desc='3rd loop', leave=False):
            sleep(0.01)
```

For manual control over positioning (e.g., for multi-processing), you may specify `position=n` where `n=0` for the outermost bar, `n=1` for the next, and so on:

```python
from time import sleep
from tldm import trange, tldm
from multiprocessing import Pool, RLock, freeze_support

L = list(range(9))

def progresser(n):
    interval = 0.001 / (n + 2)
    total = 5000
    text = f"#{n}, est. {interval * total:<04.2}s"
    for _ in trange(total, desc=text, position=n):
        sleep(interval)

if __name__ == '__main__':
    freeze_support()  # for Windows support
    tldm.set_lock(RLock())  # for managing output contention
    p = Pool(initializer=tldm.set_lock, initargs=(tldm.get_lock(),))
    p.map(progresser, L)
```

Note that `tldm.write` is thread-safe:

```python
from time import sleep
from tldm import tldm, trange
from concurrent.futures import ThreadPoolExecutor

L = list(range(9))

def progresser(n):
    interval = 0.001 / (n + 2)
    total = 5000
    text = f"#{n}, est. {interval * total:<04.2}s"
    for _ in trange(total, desc=text):
        sleep(interval)
    if n == 6:
        tldm.write("n == 6 completed.")
        tldm.write("`tldm.write()` is thread-safe!")

if __name__ == '__main__':
    with ThreadPoolExecutor() as p:
        p.map(progresser, L)
```

### Hooks and Callbacks

`tldm` can easily support callbacks/hooks and manual updates. Here's an example with `urllib`:

```python
import urllib.request
import os
from tldm import tldm

class TldmUpTo(tldm):
    """Provides `update_to(n)` which uses `tldm.update(delta_n)`."""
    def update_to(self, b=1, bsize=1, tsize=None):
        """
        b  : int, optional
            Number of blocks transferred so far [default: 1].
        bsize  : int, optional
            Size of each block (in tldm units) [default: 1].
        tsize  : int, optional
            Total size (in tldm units). If [default: None] remains unchanged.
        """
        if tsize is not None:
            self.total = tsize
        return self.update(b * bsize - self.n)

eg_link = "https://example.com/file.zip"
with TldmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1,
              desc=eg_link.split('/')[-1]) as t:
    urllib.request.urlretrieve(eg_link, filename=os.devnull,
                               reporthook=t.update_to, data=None)
    t.total = t.n
```

Alternatively, use the `wrapattr` convenience function:

```python
import urllib.request
import os
from tldm import tldm

eg_link = "https://example.com/file.zip"
response = urllib.request.urlopen(eg_link)
with tldm.wrapattr(open(os.devnull, "wb"), "write",
                   miniters=1, desc=eg_link.split('/')[-1],
                   total=getattr(response, 'length', None)) as fout:
    for chunk in response:
        fout.write(chunk)
```

The `requests` equivalent is nearly identical:

```python
import requests
import os
from tldm import tldm

eg_link = "https://example.com/file.zip"
response = requests.get(eg_link, stream=True)
with tldm.wrapattr(open(os.devnull, "wb"), "write",
                   miniters=1, desc=eg_link.split('/')[-1],
                   total=int(response.headers.get('content-length', 0))) as fout:
    for chunk in response.iter_content(chunk_size=4096):
        fout.write(chunk)
```

**Working with Zip Files**

You can also wrap file operations within zipfiles to show progress during compression/decompression:

```python
import zipfile
from tldm import tldm

class ZipFile(zipfile.ZipFile):
    """ZipFile subclass with progress bars for read/write operations."""

    def open(self, name, mode="r", pwd=None, *, force_zip64=False):
        f = super().open(name, mode, pwd=pwd, force_zip64=force_zip64)

        if mode == "r":
            if not isinstance(name, zipfile.ZipInfo):
                name = self.getinfo(name)
            return tldm.wrapattr(
                f, "read",
                total=name.compress_size,
                desc=f"Decompressing {name.filename}"
            )
        elif mode == "w":
            if isinstance(name, zipfile.ZipInfo):
                return tldm.wrapattr(
                    f, "write",
                    total=name.file_size,
                    desc=f"Compressing {name.filename}"
                )
            return f
        else:
            raise ValueError('open() requires mode "r" or "w"')

# Usage example
with ZipFile('archive.zip', 'r') as zf:
    # Reading with progress bar
    data = zf.open('largefile.txt').read()

    # Extracting with progress bar
    zf.extract('largefile.txt', 'output_dir/')
```

### Writing Messages

Since `tldm` uses a simple printing mechanism to display progress bars, you should not write any message in the terminal using the builtin `print()` function while a progressbar is open.

To write messages in the terminal without any collision with `tldm` bar display, use the `tldm.print()` method (recommended) or the `tldm.write()` method:

**Using `tldm.print()` (recommended):**

```python
from tldm import tldm, trange
from time import sleep

bar = trange(10)
for i in bar:
    sleep(0.1)
    if not (i % 3):
        tldm.print(f"Done task {i}")
```

The `tldm.print()` function works just like the builtin `print()`, accepting multiple values and standard keyword arguments like `sep`, `end`, `file`, and `flush`. This makes it a drop-in replacement for Python's builtin `print()`.

**Using `tldm.write()` (alternative):**

```python
from tldm import tldm, trange
from time import sleep

bar = trange(10)
for i in bar:
    sleep(0.1)
    if not (i % 3):
        tldm.write(f"Done task {i}")
```

Both methods will print to standard output `sys.stdout` by default, but you can specify any file-like object using the `file` argument. Both also support the `flush` argument to force flushing of the output buffer.

---

## FAQ and Known Issues

The most common issues relate to excessive output on multiple lines, instead of a neat one-line progress bar.

### Console Issues

- **Consoles in general**: require support for carriage return (`CR`, `\r`).
  - Some cloud logging consoles which don't support `\r` properly (cloudwatch, K8s) may benefit from `export TLDM_POSITION=-1`.

### Nested Progress Bars

- Consoles in general require support for moving cursors up to the previous line. IDLE, ConEmu, and PyCharm lack full support.
- Windows may require the `colorama` module to ensure nested bars stay within their respective lines.

### Unicode

- Environments which report that they support unicode will have solid smooth progressbars. The fallback is an ASCII-only bar.
- Windows consoles often only partially support unicode and may require explicit `ascii=True`.

### Wrapping Generators

Generator wrapper functions tend to hide the length of iterables. `tldm` does not.

- Replace `tldm(enumerate(...))` with `enumerate(tldm(...))` or `tldm(enumerate(x), total=len(x), ...)`.
  - The same applies to `numpy.ndenumerate`.
- Replace `tldm(zip(a, b))` with `zip(tldm(a), b)` or even `zip(tldm(a), tldm(b))`.
- The same applies to `itertools`.
- Useful convenience functions: `tenumerate`, `tzip`, `tmap`, `tproduct` are available in this package.

### No intermediate output in docker-compose

Use `docker-compose run` instead of `docker-compose up` and `tty: true`.

### Monitoring thread, intervals and miniters

`tldm` implements a few tricks to increase efficiency and reduce overhead:

- Avoid unnecessary frequent bar refreshing: `mininterval` defines how long to wait between each refresh.
- Reduce number of calls to check system clock/time.
- `mininterval` is more intuitive to configure than `miniters`. A clever adjustment system `dynamic_miniters` will automatically adjust `miniters` to the amount of iterations that fit into time `mininterval`.

However, consider a case with a combination of fast and slow iterations. After a few fast iterations, `dynamic_miniters` will set `miniters` to a large number. When iteration rate subsequently slows, `miniters` will remain large and thus reduce display update frequency. To address this:

- `maxinterval` defines the maximum time between display refreshes. A concurrent monitoring thread checks for overdue updates and forces one where necessary.

The monitoring thread should not have a noticeable overhead, and guarantees updates at least every 10 seconds by default. This value can be directly changed by setting the `monitor_interval` of any `tldm` instance (i.e., `t = tldm(...); t.monitor_interval = 2`). The monitor thread may be disabled application-wide by setting `tldm.monitor_interval = 0` before instantiation of any `tldm` bar.

---

## Contributing

All source code is hosted on [GitHub](https://github.com/eliotwrobson/tldm). Contributions are welcome.

See the [CONTRIBUTING](CONTRIBUTING.md) file for more information.

### Acknowledgments

TL;DM is forked from [tqdm](https://github.com/tqdm/tqdm), created by [Noam Yorav-Raphael](https://github.com/noamraph). We gratefully acknowledge the contributions of all tqdm contributors, especially (in no particular order):

- [**Casper da Costa-Luis**](https://github.com/casperdcl)
- [**Stephen Larroque**](https://github.com/lrq3000)
- [**Kyle Altendorf**](https://github.com/altendky)
- [**Hadrien Mary**](https://github.com/hadim)
- [**Richard Sheridan**](https://github.com/richardsheridan)
- [**Ivan Ivanov**](https://github.com/obiwanus)
- [**Mikhail Korobov**](https://github.com/kmike)

And all other contributors to the original tqdm project.

---

## License

This project is licensed under the MPL-2.0 license. See the [LICENCE](LICENCE) file for details.
