bs-python-utils 0.0.1__py3-none-any.whl

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,100 @@
1
+ """ Utilities for logging
2
+ """
3
+ import functools
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Callable
7
+
8
+
9
+ def init_logger(
10
+ logger_name: str,
11
+ log_level_for_console: str = "info",
12
+ log_level_for_file: str = "debug",
13
+ save_dir: str = None,
14
+ ) -> logging.Logger:
15
+ """
16
+ Initialize a logger
17
+
18
+ Args:
19
+ logger_name: name for the logger
20
+ log_level_for_console: minimum level of messages logged to the console logging
21
+ log_level_for_file:
22
+ save_dir:
23
+
24
+ Returns:
25
+ the logger
26
+
27
+ Example:
28
+ logger_dir = "logs"
29
+ logger_name = "check_log"
30
+ logger = init_logger(logger_name, save_dir=logger_dir)
31
+ logger = get_logger(logger_name)
32
+
33
+ will create two logs:
34
+
35
+ * one printed to console where we run the code (the `StreamHandler`),
36
+ * and one that will be saved to file `save_dir/logger_name.txt` (the `FileHandler`).
37
+
38
+ `'logger.propagate = False'` makes sure that the logs sent to file will not be printed to console.
39
+
40
+ We use the `Formatter` class to define the format of the logs.
41
+ Here:
42
+ * The time of the log in a human-readable format, `asctime`
43
+ * `levelname` is the level of the log, one out of `INFO, DEBUG, WARNING, ERROR, CRITICAL`.
44
+ * The name of the file, `filename`, from which the log was generated,
45
+ and the line number, `lineno`.
46
+ * Lastly, the message itself — `message`.
47
+
48
+ The default has only `INFO` logs and above (i.e., also `WARNING, ERROR` and `CRITICAL`)
49
+ displayed in the console; the file will also include `DEBUG` logs.
50
+ """
51
+ logger = logging.getLogger()
52
+ logger.setLevel(level=logging.DEBUG)
53
+ logger.propagate = False
54
+
55
+ formatter = logging.Formatter(
56
+ "%(asctime)s [%(levelname)s] %(filename)s %(lineno)d - %(message)s",
57
+ "%Y-%m-%d %H:%M:%S",
58
+ )
59
+
60
+ ch = logging.StreamHandler()
61
+ ch.setLevel(log_level_for_console.upper())
62
+ ch.setFormatter(formatter)
63
+ logger.addHandler(ch)
64
+
65
+ if save_dir is not None:
66
+ Path(save_dir).mkdir(exist_ok=True, parents=True)
67
+ fh = logging.FileHandler(save_dir + f"/{logger_name}.txt")
68
+ fh.setLevel(log_level_for_file.upper())
69
+ fh.setFormatter(formatter)
70
+ logger.addHandler(fh)
71
+
72
+ return logger
73
+
74
+
75
+ def log_execution(func: Callable) -> Callable:
76
+ """Decorator to log the execution of a function
77
+ Only records entry to and exit from the function, to the console
78
+ """
79
+ loglevel = logging.info
80
+
81
+ @functools.wraps(func)
82
+ def wrapper(*args, **kwargs):
83
+ loglevel(f"Executing {func.__name__}")
84
+ result = func(*args, **kwargs)
85
+ loglevel(f"Finished executing {func.__name__}")
86
+ return result
87
+
88
+ return wrapper
89
+
90
+
91
+ def get_logger(logger_name: str) -> logging.Logger:
92
+ """Get a logger
93
+
94
+ Args:
95
+ logger_name: name you want for the logger
96
+
97
+ Returns:
98
+ the logger
99
+ """
100
+ return logging.getLogger(logger_name)
@@ -0,0 +1,130 @@
1
+ """Some useful strings for math formulae.
2
+
3
+ Attributes:
4
+ str_beta0 (str): the LaTeX string `\\beta_0`
5
+
6
+ str_beta1 (str): the LaTeX string `\\beta_1`
7
+
8
+ str_pi (str): the LaTeX string `\\pi`
9
+
10
+ str_sigma (str): the LaTeX string `\\sigma`
11
+
12
+ str_sigma2 (str): the LaTeX string `\\sigma^2`
13
+
14
+ uni_beta0 (str): the Unicode string `\\beta_0`
15
+
16
+ uni_beta1 (str): the Unicode string `\\beta_1`
17
+
18
+ uni_pi (str): the Unicode string `\\pi`
19
+
20
+ uni_sigma (str): the Unicode string `\\sigma`
21
+
22
+ uni_sigma2 (str): the Unicode string `\\sigma^2`
23
+
24
+ uni_s2 (str): the Unicode string `s^2`
25
+
26
+ uni_R2 (str): the Unicode string `R^2`
27
+
28
+ sub_sub_scripts (dict): a dictionary of Unicodes for subscripts and superscripts;
29
+ e.g `a^b` would be `"a" + sub_sup_scripts['b'][0]`
30
+ """
31
+
32
+ # LaTeX strings
33
+ str_beta0 = r"$\beta_0$"
34
+ str_beta1 = r"$\beta_1$"
35
+ str_pi = r"$\pi$"
36
+ str_sigma = r"$\sigma$"
37
+ str_sigma2 = r"$\sigma^2$"
38
+
39
+ # Unicode
40
+ uni_beta0 = "\N{GREEK SMALL LETTER BETA}\N{SUBSCRIPT ZERO}"
41
+ uni_beta1 = "\N{GREEK SMALL LETTER BETA}\N{SUBSCRIPT ONE}"
42
+ uni_pi = "\N{GREEK SMALL LETTER PI}"
43
+ uni_sigma = "\N{GREEK SMALL LETTER SIGMA}"
44
+ uni_sigma2 = "\N{GREEK SMALL LETTER SIGMA}\N{SUPERSCRIPT TWO}"
45
+ uni_s2 = "s\N{SUPERSCRIPT TWO}"
46
+ uni_R2 = "R\N{SUPERSCRIPT TWO}"
47
+
48
+
49
+ sub_sup_scripts = {
50
+ # superscript subscript
51
+ "0": ("\u2070", "\u2080"),
52
+ "1": ("\u00B9", "\u2081"),
53
+ "2": ("\u00B2", "\u2082"),
54
+ "3": ("\u00B3", "\u2083"),
55
+ "4": ("\u2074", "\u2084"),
56
+ "5": ("\u2075", "\u2085"),
57
+ "6": ("\u2076", "\u2086"),
58
+ "7": ("\u2077", "\u2087"),
59
+ "8": ("\u2078", "\u2088"),
60
+ "9": ("\u2079", "\u2089"),
61
+ "a": ("\u1d43", "\u2090"),
62
+ "b": ("\u1d47", "?"),
63
+ "c": ("\u1d9c", "?"),
64
+ "d": ("\u1d48", "?"),
65
+ "e": ("\u1d49", "\u2091"),
66
+ "f": ("\u1da0", "?"),
67
+ "g": ("\u1d4d", "?"),
68
+ "h": ("\u02b0", "\u2095"),
69
+ "i": ("\u2071", "\u1d62"),
70
+ "j": ("\u02b2", "\u2c7c"),
71
+ "k": ("\u1d4f", "\u2096"),
72
+ "l": ("\u02e1", "\u2097"),
73
+ "m": ("\u1d50", "\u2098"),
74
+ "n": ("\u207f", "\u2099"),
75
+ "o": ("\u1d52", "\u2092"),
76
+ "p": ("\u1d56", "\u209a"),
77
+ "q": ("?", "?"),
78
+ "r": ("\u02b3", "\u1d63"),
79
+ "s": ("\u02e2", "\u209b"),
80
+ "t": ("\u1d57", "\u209c"),
81
+ "u": ("\u1d58", "\u1d64"),
82
+ "v": ("\u1d5b", "\u1d65"),
83
+ "w": ("\u02b7", "?"),
84
+ "x": ("\u02e3", "\u2093"),
85
+ "y": ("\u02b8", "?"),
86
+ "z": ("?", "?"),
87
+ "A": ("\u1d2c", "?"),
88
+ "B": ("\u1d2e", "?"),
89
+ "C": ("?", "?"),
90
+ "D": ("\u1d30", "?"),
91
+ "E": ("\u1d31", "?"),
92
+ "F": ("?", "?"),
93
+ "G": ("\u1d33", "?"),
94
+ "H": ("\u1d34", "?"),
95
+ "I": ("\u1d35", "?"),
96
+ "J": ("\u1d36", "?"),
97
+ "K": ("\u1d37", "?"),
98
+ "L": ("\u1d38", "?"),
99
+ "M": ("\u1d39", "?"),
100
+ "N": ("\u1d3a", "?"),
101
+ "O": ("\u1d3c", "?"),
102
+ "P": ("\u1d3e", "?"),
103
+ "Q": ("?", "?"),
104
+ "R": ("\u1d3f", "?"),
105
+ "S": ("?", "?"),
106
+ "T": ("\u1d40", "?"),
107
+ "U": ("\u1d41", "?"),
108
+ "V": ("\u2c7d", "?"),
109
+ "W": ("\u1d42", "?"),
110
+ "X": ("?", "?"),
111
+ "Y": ("?", "?"),
112
+ "Z": ("?", "?"),
113
+ "+": ("\u207A", "\u208A"),
114
+ "-": ("\u207B", "\u208B"),
115
+ "=": ("\u207C", "\u208C"),
116
+ "(": ("\u207D", "\u208D"),
117
+ ")": ("\u207E", "\u208E"),
118
+ ":alpha": ("\u1d45", "?"),
119
+ ":beta": ("\u1d5d", "\u1d66"),
120
+ ":gamma": ("\u1d5e", "\u1d67"),
121
+ ":delta": ("\u1d5f", "?"),
122
+ ":epsilon": ("\u1d4b", "?"),
123
+ ":theta": ("\u1dbf", "?"),
124
+ ":iota": ("\u1da5", "?"),
125
+ ":pho": ("?", "\u1d68"),
126
+ ":phi": ("\u1db2", "?"),
127
+ ":psi": ("\u1d60", "\u1d69"),
128
+ ":chi": ("\u1d61", "\u1d6a"),
129
+ ":coffee": ("\u2615", "\u2615"),
130
+ }
@@ -0,0 +1,148 @@
1
+ import linecache
2
+ import sys
3
+ import tracemalloc
4
+
5
+ import pandas as pd
6
+
7
+ from bs_python_utils.bsutils import print_stars
8
+
9
+
10
+ def _obj_size_fmt(num: int) -> str:
11
+ """
12
+ format sizes from bytes to appropriate strings depending on size
13
+
14
+ Args:
15
+ num: size of object in bytes
16
+
17
+ Returns:
18
+ its formatted size
19
+ """
20
+ if num < 10**3:
21
+ return "{:.2f}{}".format(num, "B")
22
+ elif (num >= 10**3) & (num < 10**6):
23
+ return "{:.2f}{}".format(num / (1.024 * 10**3), "KB")
24
+ elif (num >= 10**6) & (num < 10**9):
25
+ return "{:.2f}{}".format(num / (1.024 * 10**6), "MB")
26
+ else:
27
+ return "{:.2f}{}".format(num / (1.024 * 10**9), "GB")
28
+
29
+
30
+ def memory_usage(n: int | None = 10) -> None:
31
+ """
32
+ dataframe of the top `n` largest global items in memory
33
+
34
+ Args:
35
+ n: we report the size of the largest `n` global items
36
+
37
+ Returns:
38
+ nothing
39
+ """
40
+ memory_usage_by_variable = pd.DataFrame(
41
+ {k: sys.getsizeof(v) for (k, v) in globals().items()}, index=["Size"]
42
+ )
43
+ memory_usage_by_variable = memory_usage_by_variable.T
44
+ total_usage = _obj_size_fmt(memory_usage_by_variable["Size"].sum())
45
+ memory_usage_by_variable = memory_usage_by_variable.sort_values(
46
+ by="Size", ascending=False
47
+ ).head(n)
48
+ memory_usage_by_variable["Size"] = memory_usage_by_variable["Size"].apply(
49
+ lambda x: _obj_size_fmt(x)
50
+ )
51
+ print_stars(
52
+ f"Currently used memory = {total_usage}\n\t\t\t\t Top {n} global objects:"
53
+ )
54
+ print(memory_usage_by_variable)
55
+
56
+ return
57
+
58
+
59
+ def memory_display_top(
60
+ snapshot: tracemalloc.Snapshot, key_type: str = "lineno", limit: int | None = 5
61
+ ) -> None:
62
+ """
63
+ prints out the lines with the top `limit` allocations of memory since tracemalloc.start()
64
+
65
+ Args:
66
+ snapshot: obtained from tracemalloc.take_snapshot()
67
+ key_type: 'lineno' gives file and line number; 'traceback' gives all
68
+ limit: how many top allocations we want
69
+
70
+ Returns:
71
+ just prints
72
+
73
+ Example:
74
+ tracemalloc.start()
75
+ .... execute ...
76
+ snapshot = tracemalloc.take_snapshot()
77
+ memory_display_top(snapshot)
78
+ """
79
+
80
+ top_stats = snapshot.statistics(key_type)
81
+
82
+ print_stars(f"Top {limit} memory allocations")
83
+ for index, stat in enumerate(top_stats[:limit], 1):
84
+ frame = stat.traceback[0]
85
+ print(
86
+ "#%s: %s:%s: %.1f KiB"
87
+ % (index, frame.filename, frame.lineno, stat.size / 1024)
88
+ )
89
+ line = linecache.getline(frame.filename, frame.lineno).strip()
90
+ if line:
91
+ print(" %s" % line)
92
+
93
+ other = top_stats[limit:]
94
+ if other:
95
+ size = sum(stat.size for stat in other)
96
+ print(f"{len(other)} other: {size / 1024:.1f} KiB")
97
+ total = sum(stat.size for stat in top_stats)
98
+ print("Total allocated size: %.1f KiB" % (total / 1024))
99
+
100
+
101
+ def memory_display_top_diffs(
102
+ snapshot1: tracemalloc.Snapshot,
103
+ snapshot2: tracemalloc.Snapshot,
104
+ key_type: str = "lineno",
105
+ limit: int = 5,
106
+ ) -> None:
107
+ """
108
+ prints out the lines with the top `limit` allocations \
109
+ between the two snapshots
110
+
111
+ Args:
112
+ snapshot1: previous snapshot
113
+ snapshot2: new snapshot
114
+ key_type: 'lineno' gives file and line number; 'traceback' gives all
115
+ limit: how many top allocations we want
116
+
117
+ Returns:
118
+ just prints
119
+
120
+ Example:
121
+ tracemalloc.start()
122
+ .... execute ...
123
+ snapshot1 = tracemalloc.take_snapshot()
124
+ .... execute ...
125
+ snapshot2 = tracemalloc.take_snapshot()
126
+ memory_display_top_diffs(snapshot1, snapshot2)
127
+
128
+ """
129
+
130
+ top_stats = snapshot2.compare_to(snapshot1, key_type)
131
+
132
+ print_stars(f"Top {limit} new memory allocations")
133
+ for index, stat in enumerate(top_stats[:limit], 1):
134
+ frame = stat.traceback[0]
135
+ print(
136
+ "#%s: %s:%s: %.1f KiB"
137
+ % (index, frame.filename, frame.lineno, stat.size / 1024)
138
+ )
139
+ line = linecache.getline(frame.filename, frame.lineno).strip()
140
+ if line:
141
+ print(" %s" % line)
142
+
143
+ other = top_stats[limit:]
144
+ if other:
145
+ size = sum(stat.size for stat in other)
146
+ print(f"{len(other)} other: {size / 1024:.1f} KiB")
147
+ total = sum(stat.size for stat in top_stats)
148
+ print("Total allocated size: %.1f KiB" % (total / 1024))