{ "info": { "author": "exactEarth Ltd.", "author_email": "open-source@exactearth.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "================\nprogress_tracker\n================\n\n``progress_tracker`` is an easy and flexible way to print custom progress messages while processing streams of events on the CLI.\n\nIt was originally developed at `exactEarth Ltd`_ . See `this presentation`_ to `DevHouse Waterloo`_ for the original motivation.\n\n.. _exactEarth Ltd: https://exactearth.com/\n\nBuilt and tested with Python 3.6+\n\n.. contents:: Contents\n\nQuick Start\n-----------\n\n.. code:: bash\n\n % pip install progress_tracker\n\n.. code:: python\n\n >>> from progress_tracker import track_progress\n >>> for _ in track_progress(list(range(1000)), every_n_records=100):\n ... continue\n ...\n 100/1000 (10.0%) in 0:00:00.000114 (Time left: 0:00:00.001026)\n 200/1000 (20.0%) in 0:00:00.000274 (Time left: 0:00:00.001096)\n 300/1000 (30.0%) in 0:00:00.000374 (Time left: 0:00:00.000873)\n 400/1000 (40.0%) in 0:00:00.000473 (Time left: 0:00:00.000710)\n 500/1000 (50.0%) in 0:00:00.000572 (Time left: 0:00:00.000572)\n 600/1000 (60.0%) in 0:00:00.000671 (Time left: 0:00:00.000447)\n 700/1000 (70.0%) in 0:00:00.000770 (Time left: 0:00:00.000330)\n 800/1000 (80.0%) in 0:00:00.000868 (Time left: 0:00:00.000217)\n 900/1000 (90.0%) in 0:00:00.000979 (Time left: 0:00:00.000109)\n 1000 in 0:00:00.001086\n\nUsage\n-----\n\n``progress_tracker`` is very customizable to fit your desires, but tries to have sensible defaults.\n\nThe core of ``progress_tracker`` is a method called ``track_progress``.\nBy changing the parameters passed to ``track_progress``, you can customize how frequently (and with what messages) the tracker will report.\n\n.. code:: python\n\n def track_progress( \n iterable: Iterable[T], # The iterable to iterate over\n total: Optional[int] = None, # Override for the total message count, defaults to len(iterable)\n callback: Callable[[str], Any] = print, # A function (f(str) -> None) that gets called each time a condition matches\n format_callback: Callable[[Dict[str, Any], Set[str]], str] = default_format_callback, # A function (f(str) -> str) that formats the progress values into a string.\n every_n_percent: Optional[float] = None, # Reports after every n percent\n every_n_records: Optional[int] = None, # Reports every n records\n every_n_seconds: Optional[float] = None, # Reports every n seconds\n every_n_seconds_idle: Optional[float] = None, # Report if there has not been a record processed in the past n seconds. Useful for infinite streams.\n every_n_seconds_since_report: Optional[float] = None, # Report if there hasn\u2019t been any report in the past n seconds.\n report_first_record: bool = False, # Report after the first record\n report_last_record: bool = False # Report after the last record\n ) -> None\n\nExamples\n^^^^^^^^\n\nPrint after every n records are processed\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe ``every_n_records`` parameter will trigger a report after every nth record is processed. \n\n.. code:: python\n\n >>> from progress_tracker import track_progress\n >>>\n >>> for _ in track_progress(list(range(1000)), every_n_records=100):\n ... continue\n ...\n 100/1000 (10.0%) in 0:00:00.000114 (Time left: 0:00:00.001026)\n 200/1000 (20.0%) in 0:00:00.000274 (Time left: 0:00:00.001096)\n 300/1000 (30.0%) in 0:00:00.000374 (Time left: 0:00:00.000873)\n 400/1000 (40.0%) in 0:00:00.000473 (Time left: 0:00:00.000710)\n 500/1000 (50.0%) in 0:00:00.000572 (Time left: 0:00:00.000572)\n 600/1000 (60.0%) in 0:00:00.000671 (Time left: 0:00:00.000447)\n 700/1000 (70.0%) in 0:00:00.000770 (Time left: 0:00:00.000330)\n 800/1000 (80.0%) in 0:00:00.000868 (Time left: 0:00:00.000217)\n 900/1000 (90.0%) in 0:00:00.000979 (Time left: 0:00:00.000109)\n 1000 in 0:00:00.001086\n\nPrint after every x percent of records are processed\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe ``every_n_percent`` parameter will trigger a report after every nth percent of records are processed. \n\n.. code:: python\n\n >>> from progress_tracker import track_progress\n >>> for _ in track_progress(list(range(1000)), every_n_percent=10):\n ... continue\n ...\n 100/1000 (10.0%) in 0:00:00.000114 (Time left: 0:00:00.001026)\n 200/1000 (20.0%) in 0:00:00.000274 (Time left: 0:00:00.001096)\n 300/1000 (30.0%) in 0:00:00.000374 (Time left: 0:00:00.000873)\n 400/1000 (40.0%) in 0:00:00.000473 (Time left: 0:00:00.000710)\n 500/1000 (50.0%) in 0:00:00.000572 (Time left: 0:00:00.000572)\n 600/1000 (60.0%) in 0:00:00.000671 (Time left: 0:00:00.000447)\n 700/1000 (70.0%) in 0:00:00.000770 (Time left: 0:00:00.000330)\n 800/1000 (80.0%) in 0:00:00.000868 (Time left: 0:00:00.000217)\n 900/1000 (90.0%) in 0:00:00.000979 (Time left: 0:00:00.000109)\n 1000 in 0:00:00.001086\n\n``every_n_percent`` only works for bounded iterables. For unbounded iterables (ex. streams), using ``every_n_percent`` will report a ``RuntimeWarning``.\n\nAt most a single report is generated per processed record. Even if processing of a single record would meet the conditions multiple times \n(ex. if ``every_n_percent=10``, but there are only 2 records, then processing each record causes 50%, or 5 * 10%, progress), only a single report is created (containing the latest values).\n\nPrint every n records OR every n seconds during processing\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is especially useful when you have highly variable processing times (ex. most records take 2 seconds to process, but some take 20 seconds to process).\nYou can use the ``every_n_seconds`` parameter to get reports between the expensive records.\n\n.. code:: python\n\n import time\n from progress_tracker import track_progress\n\n def simulated_processing(item):\n if item == 'hard':\n time.sleep(10)\n\n variable_stream_simulation = (['easy'] * 15) + (['hard'] * 5) + (['easy'] * 15)\n\n for item in track_progress(variable_stream_simulation, every_n_records=5, every_n_seconds=10):\n simulated_processing(item)\n\n ...\n 5/35 (14.285714285714285%) in 0:00:00.000014 (Time left: 0:00:00.000084)\n 10/35 (28.57142857142857%) in 0:00:00.000095 (Time left: 0:00:00.000238)\n 15/35 (42.857142857142854%) in 0:00:00.000120 (Time left: 0:00:00.000160)\n 16/35 (45.714285714285715%) in 0:00:10.011364 (Time left: 0:00:11.888495)\n 17/35 (48.57142857142857%) in 0:00:20.022107 (Time left: 0:00:21.199878)\n 18/35 (51.42857142857142%) in 0:00:30.031801 (Time left: 0:00:28.363368)\n 19/35 (54.285714285714285%) in 0:00:40.041754 (Time left: 0:00:33.719372)\n 20/35 (57.14285714285714%) in 0:00:50.073991 (Time left: 0:00:37.555493)\n 25/35 (71.42857142857143%) in 0:00:50.074246 (Time left: 0:00:20.029698)\n 30/35 (85.71428571428571%) in 0:00:50.074286 (Time left: 0:00:08.345714)\n 35 in 0:00:50.074319\n\nDuring the processing of the slow records, ``track_progress`` reported after every record.\n\nNote: Because the default \"Time left\" calculation is just a simple linear extrapolation, it is not as useful in the face of such variability in processing times.\n\nCombining trigger conditions\n----------------------------\n\nAs seen in the previous example, you can combine multiple conditions together to dictate when a report is created.\n\nEach of the conditions are combined using an OR operator, meaning that if any condition is met, a report is created.\n\nEven if multiple conditions are met simultaneously, only a single report will be created.\n\nReport Creation Invariants\n--------------------------\n\nReport creation observes two invariants:\n\n1. At most a single report is created per processed record.\n2. Reports are only created in response to a record being processed.\n\nCustomizing the report formatting / Internationalization\n--------------------------------------------------------\n\nBy default, ``progress_tracker`` formats the report into an English language string.\nThis can be overriden by supplying a different function as the ``format_callback`` parameter to ``track_progress``.\n\nThis can be used to perform advanced formatting, or to add internationalization/localization.\n\n.. code:: python\n\n def format_en_francais(report: Dict[str, Any], reasons: Set[str]):\n i = report[\"i\"]\n total = report[\"total\"]\n if total is None or i == total:\n format_string = \"{i} messages trait\u00e9s en {time_taken}\"\n else:\n format_string = \"{i}/{total} messages trait\u00e9s en {time_taken} (temps restant: {estimated_time_remaining})\"\n return format_string.format(**report)\n\n for poste in track_progress(postes, every_n_records=100, format_callback=format_en_francais):\n trait\u00e9(poste)\n\n(Veuillez excuser toute erreur en fran\u00e7ais. C'est le r\u00e9sultat de Google Translate.)\n\nSimple cases can also be done using a lambda:\n\n.. code:: python\n\n >>> from progress_tracker import track_progress\n >>>\n >>> for _ in track_progress(list(range(5)), every_n_records=1, format_callback=lambda **kwargs: \"Got one!\"):\n ... continue\n ...\n Got one!\n Got one!\n Got one!\n Got one!\n Got one!\n\nReport values available\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe following values are available in every report for use in the ``format_callback``:\n\n.. table::\n :widths: auto\n\n ============================== =================== =======================================================================================================================================\n Value Type Meaning\n ============================== =================== =======================================================================================================================================\n ``{records_seen}`` int The number of records processed so far.\n ``{total}`` Optional[int] The total of records in the iterable, if known. Else ``None``\n ``{percent_complete}`` Optional[float] The percentage of records processed so far. ``None`` if ``{total}`` is ``None`` or ``records_seen`` = 0\n ``{time_taken}`` timedelta The amount of time that processing has taken thus far.\n ``{estimated_time_remaining}`` Optional[timedelta] The estimated amount of time needed in order to process the rest of the records (simple linear estimate). ``None`` if total is ``None``\n ``{items_per_second}`` Optional[float] The number of records processed so far / the number of seconds elapsed. ``None`` if no time have elapsed.\n ``{idle_time}`` timedelta The amount of idle time between the previous record's processing and this record's arrival.\n ============================== =================== =======================================================================================================================================\n\nCustomizing the print behaviour\n-------------------------------\n\nBy default, ``progress_tracker`` calls Python's `print`_ function with the formatted report.\nThis can be overriden by supplying a different function as the ``callback`` parameter to ``track_progress``.\n\n.. _`print`: https://docs.python.org/3/library/functions.html#print\n\n``every_n_seconds_idle``\n------------------------\n\n``every_n_seconds_idle`` allows you to trigger a report if there is ever more than ``n`` seconds when no records were processed.\n\nNote: If processing of a single record takes longer than ``every_n_seconds_idle``, then it will be triggered after every record.\n\nDifference between ``every_n_seconds`` and ``every_n_seconds_idle``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n* ``every_n_seconds`` triggers a report anytime it has been more than n seconds since ``every_n_seconds`` last triggered a report.\n* ``every_n_seconds_idle`` triggers a report anytime there has not been a record processed in the past n seconds (ie. the processing has been idle).\n\nFor example:\n\n.. table::\n :widths: auto\n\n ========== ================================== ============================= ================================================================ ======================\n After # of records processed in interval Cummulative records processed every_n_seconds=3 every_n_seconds_idle=3\n ========== ================================== ============================= ================================================================ ======================\n 0 seconds 0 0 \n 1 second 1 1 \n 2 seconds 1 2 \n 3 seconds 1 3 Triggered, since it is the first record T >= 3s (T >= 0s + 3s)\n 4 seconds 1 4 \n 5 seconds 1 5 \n 6 seconds 1 6 Triggered, since it is the first record T >= 6s (T >= 3s + 3s) \n 7 seconds 1 6 \n 8 seconds 0 6 \n 9 seconds 0 6 \n 10 seconds 0 6 \n 11 seconds 1 7 Triggered, since it is the first record T >= 9s (T >= 6s + 3s) Triggered, since it is the first record processed in the past 3 seconds (T >= 6s + 3s) \n 12 seconds 1 8 \n 13 seconds 1 9 \n 14 seconds 1 10 Triggered, since it is the first record T >= 14s (T >= 11s + 3s) \n 15 seconds 1 11 \n ========== ================================== ============================= ================================================================ ======================\n\nNote that ``every_n_seconds`` reports at 3 seconds and 6 seconds, as one would expect. Then it reports at 11 seconds, since that is the first time a record was processed after the 9 seconds mark.\nThen note that instead of next reporting at 12 seconds (9s + 3s), it reports next at 14 seconds (11s + 3s).\n\n``every_n_seconds_idle`` only reported at 11 seconds, since that was the only time that a record was processed without other records being processed during the previous 3 seconds.\n\nAccessing tracker after processing\n----------------------------------\n\nBy default, ``track_progress`` hides the internal ``ProgressTracker`` object underneath. However, in some cases you might want to be able to access the internals of the object after iteration.\nIn these cases, you can use ``track_progress`` an explicit context manager:\n\n.. code:: python\n\n with track_progress(range(0, 101), every_n_percent=5) as tracker:\n for item in tracker:\n process(item)\n final_report = tracker.create_report()\n print(f\"Processing took {final_report['time_taken']} and processed {final_report['records_seen']} records.\")\n\n\nOther Resources\n---------------\n\n- `This presentation`_ to `DevHouse Waterloo`_.\n\n.. _This presentation: https://www.slideshare.net/MichaelOvermeyer/progress-tracker-a-handy-progress-printout-pattern\n.. _DevHouse Waterloo: https://www.meetup.com/DevHouse-Waterloo/events/247071801/\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "progress-tracker", "package_url": "https://pypi.org/project/progress-tracker/", "platform": "", "project_url": "https://pypi.org/project/progress-tracker/", "project_urls": null, "release_url": "https://pypi.org/project/progress-tracker/1.0.0/", "requires_dist": null, "requires_python": ">=3.6", "summary": "A utility that wraps an Iterable and regularly prints out progress on the processing of that Iterable", "version": "1.0.0" }, "last_serial": 4297887, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "884c534018da706c63c297155b4bdec2", "sha256": "3a68fb904c47b24ab19e1f1e7435e7b75839848ea969786d2d5604ffbf845067" }, "downloads": -1, "filename": "progress_tracker-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "884c534018da706c63c297155b4bdec2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 8688, "upload_time": "2018-09-21T20:03:16", "url": "https://files.pythonhosted.org/packages/d6/aa/643a50dedefb21f4c34112d97afd7a575f50e968f177dacb57fa6a3889ba/progress_tracker-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e94839ac82cd9cc9238b9026fb5e4cc7", "sha256": "0bcf39c9f3f0c2b541c512dea44baf4ce745f48ec4f377c664952233edbed8ea" }, "downloads": -1, "filename": "progress_tracker-1.0.0.tar.gz", "has_sig": false, "md5_digest": "e94839ac82cd9cc9238b9026fb5e4cc7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 10248, "upload_time": "2018-09-21T20:03:17", "url": "https://files.pythonhosted.org/packages/42/8b/ecfd4128f997af7f8f0d250fa71734e78875108f35f197b802d74514fb67/progress_tracker-1.0.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "884c534018da706c63c297155b4bdec2", "sha256": "3a68fb904c47b24ab19e1f1e7435e7b75839848ea969786d2d5604ffbf845067" }, "downloads": -1, "filename": "progress_tracker-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "884c534018da706c63c297155b4bdec2", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 8688, "upload_time": "2018-09-21T20:03:16", "url": "https://files.pythonhosted.org/packages/d6/aa/643a50dedefb21f4c34112d97afd7a575f50e968f177dacb57fa6a3889ba/progress_tracker-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e94839ac82cd9cc9238b9026fb5e4cc7", "sha256": "0bcf39c9f3f0c2b541c512dea44baf4ce745f48ec4f377c664952233edbed8ea" }, "downloads": -1, "filename": "progress_tracker-1.0.0.tar.gz", "has_sig": false, "md5_digest": "e94839ac82cd9cc9238b9026fb5e4cc7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 10248, "upload_time": "2018-09-21T20:03:17", "url": "https://files.pythonhosted.org/packages/42/8b/ecfd4128f997af7f8f0d250fa71734e78875108f35f197b802d74514fb67/progress_tracker-1.0.0.tar.gz" } ] }