rra-tools 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, James Collins <collijk@uw.edu>
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,428 @@
1
+ Metadata-Version: 2.1
2
+ Name: rra-tools
3
+ Version: 1.0.0
4
+ Summary: Common utilities for IHME Rapid Response team pipelines.
5
+ Home-page: https://collijk.github.io/rra-tools
6
+ License: BSD-3-Clause
7
+ Author: James Collins
8
+ Author-email: collijk@uw.edu
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: click
22
+ Requires-Dist: deep-translator (>=1.11.4,<2.0.0)
23
+ Requires-Dist: loguru (>=0.7.2,<0.8.0)
24
+ Requires-Dist: pandas (>=2.2.2,<3.0.0)
25
+ Requires-Dist: pathos (>=0.3.2,<0.4.0)
26
+ Requires-Dist: tqdm (>=4.66.4,<5.0.0)
27
+ Project-URL: Documentation, https://collijk.github.io/rra-tools
28
+ Project-URL: Repository, https://github.com/collijk/rra-tools
29
+ Description-Content-Type: text/markdown
30
+
31
+ # RRA Tools
32
+
33
+ [![PyPI](https://img.shields.io/pypi/v/rra-tools?style=flat-square)](https://pypi.python.org/pypi/rra-tools/)
34
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/rra-tools?style=flat-square)](https://pypi.python.org/pypi/rra-tools/)
35
+ [![PyPI - License](https://img.shields.io/pypi/l/rra-tools?style=flat-square)](https://pypi.python.org/pypi/rra-tools/)
36
+
37
+ ---
38
+
39
+ **Documentation**: [https://collijk.github.io/rra-tools](https://collijk.github.io/rra-tools)
40
+
41
+ **Source Code**: [https://github.com/collijk/rra-tools](https://github.com/collijk/rra-tools)
42
+
43
+ **PyPI**: [https://pypi.org/project/rra-tools/](https://pypi.org/project/rra-tools/)
44
+
45
+ ---
46
+
47
+ Common utilities for IHME Rapid Response team pipelines.
48
+
49
+ ### CLI Tools
50
+
51
+ The provided cli tools break down into three categories.
52
+
53
+ #### Exception handling
54
+
55
+ The `cli_tools` subpackage provides a `handle_exceptions` wrapper that can be used to
56
+ wrap functions that may raise exceptions and drop into a pdb shell when an exception is
57
+ raised. This is useful for debugging functions that are failing in a pipeline.
58
+
59
+ ```python
60
+ from rra_tools.cli_tools import handle_exceptions
61
+ from loguru import logger
62
+
63
+ def my_task():
64
+ ...
65
+
66
+
67
+ if __name__ == "__main__":
68
+ runner = handle_exceptions(my_task, logger, with_debugger=True)
69
+ runner()
70
+ ```
71
+
72
+ #### Dynamic module import
73
+
74
+ The `cli_tools` subpackage provides a `load_module_from_info` function that can be used
75
+ to dynamically import a module. This is a useful pattern for creating a CLI that can
76
+ dynamically add new commands and subcommands based on subpackage structure.
77
+
78
+ TODO: Usage example
79
+
80
+ #### Click options
81
+
82
+ The `cli_tools` subpackage provides several click options that can be used to create
83
+ CLI commands with common options.
84
+
85
+ - `with_verbose` - Add a `--verbose`, `-v` option to the command to increase the log
86
+ verbosity
87
+ - `with_debugger` - Add a `--pdb` option to the command to drop into a pdb shell on
88
+ exception
89
+ - `with_input_directory` - Add a parameterizeable `--{dir-name}-dir` option to the
90
+ command to specify an input directory.
91
+ - `with_output_directory` - Add a `--output-dir`, `-o` option to the command to
92
+ specify an output directory.
93
+ - `with_num_cores` - Add a `--num-cores`, `-c` option to the command to specify the
94
+ number of cores to use for parallel processing.
95
+ - `with_queue` - Add a `--queue`, `-q` option to the command to specify a slurm queue
96
+ to run jobs on.
97
+ - `with_progress_bar` - Add a `--progress-bar`, `--pb` option to the command to
98
+ display a progress bar for long-running tasks.
99
+ - `with_dry_run` - Add a `--dry-run`, `-n` option to the command to run the command
100
+ without actually executing the task.
101
+
102
+ These options do not provide implementations for the options, but rather provide a
103
+ standard interface for adding these options to a command to ensure consistency across
104
+ commands. Several of these options are meant to be used with other tools provided
105
+ in this package like exception handling, logging, and parallel processing.
106
+
107
+ ```python
108
+ import click
109
+ from loguru import logger
110
+
111
+ from rra_tools.cli_tools import (
112
+ with_verbose,
113
+ with_debugger,
114
+ with_output_directory,
115
+ handle_exceptions,
116
+ )
117
+ from rra_tools.logging import configure_logging_to_terminal
118
+
119
+ def my_task_main(output_dir: str):
120
+ ...
121
+
122
+
123
+ @click.command()
124
+ @with_output_directory("/path/to/default/output")
125
+ @with_verbose()
126
+ @with_debugger()
127
+ def my_task(output_dir: str, verbose: int, debugger: bool):
128
+ configure_logging_to_terminal(verbose)
129
+ runner = handle_exceptions(my_task_main, logger, with_debugger=debugger)
130
+ runner(output_dir)
131
+
132
+ ```
133
+
134
+ ### Logging
135
+
136
+ The `logging` subpackage provides a number of utilities for configuring loggers
137
+ and for performance logging. This package is built on top of the `loguru` package,
138
+ which provides a more flexible and powerful logging interface than the standard
139
+ library `logging` package.
140
+
141
+ #### Configuration
142
+
143
+ There are three main functions for configuring loggers:
144
+
145
+ - `configure_logging_to_terminal` - Configure a logger to log to the terminal
146
+ - `configure_logging_to_file` - Configure a logger to log to a file
147
+ - `add_logging_sink` - A generic function to add a sink to a logger
148
+
149
+ The first two options are convenience functions that set up a logger with a standard
150
+ configuration. The third option is more flexible and can be used to add additional
151
+ sinks to a logger.
152
+
153
+ #### Performance Logging
154
+
155
+ The `logging` subpackage provides a `task_performance_logger` that is a drop-in
156
+ replacement for the `loguru` logger that logs the time taken to run a task.
157
+
158
+ ```python
159
+ from rra_tools.logging import task_performance_logger as logger
160
+
161
+ def my_task():
162
+ logger.debug("Loading training data", context="load_data")
163
+ # Load the data
164
+
165
+ logger.debug("Training model", context="train_model")
166
+ # Train the model
167
+
168
+ # Using the same context for logging will accumulate time
169
+ # spent in that context across both usages.
170
+ logger.debub("Loading inference_data", context="load_data")
171
+ # Load the inference data
172
+
173
+ logger.debug("Evaluating model", context="evaluate_model")
174
+ # Evaluate the model
175
+
176
+ logger.debug("Saving results", context="save_results")
177
+ # Save the results
178
+
179
+ logger.report() # Prints out the time spent in each logging context
180
+
181
+ ```
182
+
183
+ Additionally, `rra_tools` provides a command line tool `parse_logs` that can be used to
184
+ summarize the performance logs generated by the `task_performance_logger`. This is
185
+ useful when trying to understand the runtime characteristics of a pipeline that may
186
+ run hundreds or thousands of tasks.
187
+
188
+ ```sh
189
+ parse_logs path/to/output/log/directory/
190
+ ```
191
+
192
+ ### Shell Tools
193
+
194
+ The `shell_tools` module provides a few functions to run common shell commands.
195
+
196
+ 1. `wget` - Download a file from a URL
197
+
198
+ ```python
199
+ from rra_tools.shell_tools import wget
200
+
201
+ wget("https://example.com/file.txt", "path/to/output.txt")
202
+ ```
203
+
204
+ 2. `unzip_and_delete_archive` - Unzip a file and delete the archive
205
+
206
+ ```python
207
+ from rra_tools.shell_tools import unzip_and_delete_archive
208
+
209
+ unzip_and_delete_archive("path/to/archive.zip", "path/to/output")
210
+ ```
211
+
212
+ Note: you may need to install `unzip` on your system to use this function. You
213
+ can do so with `conda install -c conda-forge unzip`.
214
+
215
+ 3. `mkdir` - Create a directory with correct permissions.
216
+ The default operation of mkdir via the `os` module or `pathlib` translates uses
217
+ the umask of the user running the script along with the permissions set. This
218
+ often results in unexpected permissions on the created directory. This function
219
+ allows you to specify the permissions of the directory without relying on the
220
+ umask.
221
+
222
+ ```python
223
+ from rra_tools.shell_tools import mkdir
224
+
225
+ mkdir("path/to/directory", mode=0o755)
226
+ # Can also make parents
227
+ mkdir("path/to/other/directory", parents=True, mode=0o775)
228
+ # Can also do a no-op if the directory already exists
229
+ mkdir("path/to/other/directory", mode=0o775)
230
+ ```
231
+
232
+ 4. `touch` - Create a file with the correct permissions.
233
+ Like `mkdir`, `touch` allows you to specify the permissions of the file without
234
+ relying on the umask.
235
+
236
+ ```python
237
+ from rra_tools.shell_tools import touch
238
+
239
+ touch("path/to/file.txt", mode=0o664)
240
+ ```
241
+
242
+ ### Parallel Processing with Multiprocessing
243
+
244
+ The `parallel` module provides a utility to run a function of a single argument in
245
+ parallel across a list of inputs using multiprocessing.
246
+
247
+ ```python
248
+ from rra_tools.parallel import run_parallel
249
+
250
+ # Trivial example
251
+ def my_runner(x):
252
+ return x ** 2
253
+
254
+ inputs = list(range(1000))
255
+
256
+ results = run_parallel(
257
+ my_runner,
258
+ inputs,
259
+ num_cores=3, # By default, num_cores is set to 1 and will run sequentially
260
+ )
261
+ ```
262
+
263
+ In practice, the function we want to parallelize will be significantly more complex
264
+ than the trivial example above. Generally, you want to set things up so that:
265
+
266
+ 1. The function you want to parallelize is self-contained and does not rely on any
267
+ global state.
268
+ 2. The function you want to parallelize is relatively expensive to run. If the
269
+ function is cheap to run, the overhead of parallelization can outweigh the benefits
270
+ of parallelization.
271
+ 3. The input argument to the function is relatively small in memory. Multiprocessing
272
+ needs to copy the input data to each worker process, so if the input data is large,
273
+ the overhead of copying the data can outweigh the benefits of parallelization.
274
+ A common way to overcome this limitation is to pass the path to the input data
275
+ instead of the data itself and then have the function read the data from the path.
276
+ 4. The function is not *too* complex. If your `runner` function is complicated, you may
277
+ end up with resource contention between the worker processes that is hard to
278
+ understand (e.g. you may run out of memory because each worker process is trying
279
+ is loading a big dataset at the same time). There's no hard and fast rule here,
280
+ but once functions get to be more than a few dozen lines long, you should start
281
+ thinking about whether process-based parallelization is the right choice, and maybe
282
+ opt for a different parallelization strategy (like `jobmon` described below).
283
+
284
+
285
+ ### Jobmon Integration
286
+
287
+ The `jobmon` module provides a set of utilities to run more complicated parallel
288
+ jobs by interfacing with a job scheduler like `slurm`. See
289
+ [Jobmon documentation](https://jobmon.readthedocs.io/en/latest/index.html) for more
290
+ information.
291
+
292
+ #### Installation
293
+
294
+ Jobmon is not installed by default with `rra-tools` and is only available to download
295
+ and install on the IHME cluster. To install jobmon, you must have
296
+ a conf file in your home directory at `~/.pip/pip.conf` with the following contents:
297
+
298
+ ```
299
+ [global]
300
+ extra-index-url = https://artifactory.ihme.washington.edu/artifactory/api/pypi/pypi-shared/simple
301
+ trusted-host = artifactory.ihme.washington.edu/artifactory/api/pypi/pypi-shared
302
+ ```
303
+
304
+ Then you can install jobmon with:
305
+
306
+ ```sh
307
+ pip install jobmon[ihme]
308
+ ```
309
+
310
+ #### Usage
311
+
312
+ TBD
313
+
314
+
315
+ ### Translation
316
+
317
+ The `translate` module provides functions to translate text files from one
318
+ language to another.
319
+
320
+
321
+ ```python
322
+ from rra_tools.translate import translate_text_file
323
+
324
+ translate_text_file("path/to/input.txt", "path/to/output.txt")
325
+ ```
326
+
327
+ By default, it will attempt to autodetect the language in the input file and produce
328
+ outputs in English, but you can specify the source and target languages:
329
+
330
+ ```python
331
+ from rra_tools.translate import translate_text_file
332
+ # Translate from German to Spanish
333
+ translate_text_file(
334
+ "path/to/input.txt",
335
+ "path/to/output.txt",
336
+ source_language="de",
337
+ target_language="es",
338
+ )
339
+ ```
340
+
341
+ The `translate` subpackage can also translate dataframe columns
342
+
343
+ ```python
344
+ import pandas as pd
345
+ from rra_tools.translate import translate_dataframe
346
+
347
+ df = pd.DataFrame({"text": ["hola", "mundo"]})
348
+ translated_df = translate_dataframe(df, columns=["text"])
349
+ ```
350
+
351
+
352
+ ---
353
+
354
+ ## Installation
355
+
356
+ ```sh
357
+ pip install rra-tools
358
+ ```
359
+
360
+ ## Development
361
+
362
+ Instructions using conda:
363
+
364
+ 1. Clone this repository.
365
+
366
+ Over ssh:
367
+ ```sh
368
+ git clone git@github.com:ihmeuw/climate-downscale.git
369
+ ```
370
+
371
+ Over https:
372
+ ```sh
373
+ git clone https://github.com/ihmeuw/climate-downscale.git
374
+ ```
375
+
376
+ 2. Create a new conda environment.
377
+
378
+ ```sh
379
+ conda create -n climate-downscale python=3.10
380
+ conda activate climate-downscale
381
+ ```
382
+
383
+ 3. Install `poetry` and the project dependencies.
384
+
385
+ ```sh
386
+ conda install poetry
387
+ poetry install
388
+ ```
389
+
390
+ ### Documentation
391
+
392
+ The documentation is automatically generated from the content of the `docs` directory and from the docstrings
393
+ of the public signatures of the source code. The documentation is updated and published as a [Github project page
394
+ ](https://pages.github.com/) automatically as part each release.
395
+
396
+ ### Releasing
397
+
398
+ Trigger the [Draft release workflow](https://github.com/collijk/rra-tools/actions/workflows/draft_release.yml)
399
+ (press _Run workflow_). This will update the changelog & version and create a GitHub release which is in _Draft_ state.
400
+
401
+ Find the draft release from the
402
+ [GitHub releases](https://github.com/collijk/rra-tools/releases) and publish it. When
403
+ a release is published, it'll trigger [release](https://github.com/collijk/rra-tools/blob/master/.github/workflows/release.yml) workflow which creates PyPI
404
+ release and deploys updated documentation.
405
+
406
+ ### Pre-commit
407
+
408
+ Pre-commit hooks run all the auto-formatting (`ruff format`), linters (e.g. `ruff` and `mypy`), and other quality
409
+ checks to make sure the changeset is in good shape before a commit/push happens.
410
+
411
+ You can install the hooks with (runs for each commit):
412
+
413
+ ```sh
414
+ pre-commit install
415
+ ```
416
+
417
+ Or if you want them to run only for each push:
418
+
419
+ ```sh
420
+ pre-commit install -t pre-push
421
+ ```
422
+
423
+ Or if you want e.g. want to run all checks manually for all files:
424
+
425
+ ```sh
426
+ poetry run pre-commit run --all-files
427
+ ```
428
+