GJDutils 0.2.2__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.
Files changed (49) hide show
  1. gjdutils/__init__.py +12 -0
  2. gjdutils/audios.py +39 -0
  3. gjdutils/cacheing.py +237 -0
  4. gjdutils/cmd.py +149 -0
  5. gjdutils/colab.py +39 -0
  6. gjdutils/collections.py +36 -0
  7. gjdutils/decorators.py +34 -0
  8. gjdutils/dicts.py +216 -0
  9. gjdutils/dsci.py +202 -0
  10. gjdutils/dt.py +296 -0
  11. gjdutils/env.py +64 -0
  12. gjdutils/errors.py +12 -0
  13. gjdutils/files.py +140 -0
  14. gjdutils/functions.py +6 -0
  15. gjdutils/google_translate.py +80 -0
  16. gjdutils/hashing.py +32 -0
  17. gjdutils/html.py +87 -0
  18. gjdutils/indexing.py +97 -0
  19. gjdutils/iterfunc.py +99 -0
  20. gjdutils/jsons.py +70 -0
  21. gjdutils/lists.py +13 -0
  22. gjdutils/llm_utils.py +167 -0
  23. gjdutils/llms_claude.py +131 -0
  24. gjdutils/llms_openai.py +299 -0
  25. gjdutils/misc.py +30 -0
  26. gjdutils/num.py +77 -0
  27. gjdutils/obsolete/google_text_to_speech.py +46 -0
  28. gjdutils/obsolete/llms_obsolete.py +298 -0
  29. gjdutils/outloud_text_to_speech.py +230 -0
  30. gjdutils/prompt_templates.py +20 -0
  31. gjdutils/pypi_build.py +112 -0
  32. gjdutils/pytest_utils.py +24 -0
  33. gjdutils/rand.py +65 -0
  34. gjdutils/regex.py +78 -0
  35. gjdutils/requirements_dev.txt +2 -0
  36. gjdutils/runtime.py +19 -0
  37. gjdutils/sets.py +5 -0
  38. gjdutils/shell.py +69 -0
  39. gjdutils/sorteddict.py +34 -0
  40. gjdutils/stopwatch.py +79 -0
  41. gjdutils/strings.py +218 -0
  42. gjdutils/todo/convert_parquet.py +28 -0
  43. gjdutils/typ.py +37 -0
  44. gjdutils/voice_speechrecognition.py +29 -0
  45. gjdutils/web.py +68 -0
  46. gjdutils-0.2.2.dist-info/METADATA +101 -0
  47. gjdutils-0.2.2.dist-info/RECORD +49 -0
  48. gjdutils-0.2.2.dist-info/WHEEL +4 -0
  49. gjdutils-0.2.2.dist-info/licenses/LICENSE +21 -0
gjdutils/strings.py ADDED
@@ -0,0 +1,218 @@
1
+ from pathlib import Path
2
+ from six import string_types
3
+ from string import punctuation
4
+ import textwrap
5
+ from typing import Optional, Sequence, Union
6
+
7
+ PathOrStr = Union[str, Path]
8
+
9
+
10
+ def is_string(x):
11
+ """
12
+ Based on https://stackoverflow.com/questions/11301138/how-to-check-if-variable-is-string-with-python-2-and-3-compatibility
13
+ """
14
+ return isinstance(x, string_types)
15
+
16
+
17
+ def remove_punctuation(txt: str) -> str:
18
+ """Removes characters that are in string.punctuation."""
19
+ return txt.translate(str.maketrans("", "", punctuation))
20
+
21
+
22
+ def remove_first_line(s):
23
+ return "\n".join(s.splitlines()[1:])
24
+
25
+
26
+ def truncate_chars(s: str, n: Optional[int] = None):
27
+ """
28
+ Truncate a string to N characters, appending '...' if truncated.
29
+
30
+ trunc('1234567890', 10) -> '1234567890'
31
+ trunc('12345678901', 10) -> '1234567890...'
32
+ """
33
+ if not s:
34
+ return s
35
+ if n is None:
36
+ return s
37
+ return s[:n] + "..." if len(s) > n else s
38
+
39
+
40
+ def truncate_words(txt: str, n: Optional[int] = None):
41
+ if n is None:
42
+ return txt
43
+ words = txt.split(" ")
44
+ return " ".join(words[:n])
45
+
46
+
47
+ def longest_substring_multi(strs: Sequence[str]) -> str:
48
+ """
49
+ Find the longest common substring for multiple strings in list DATA.
50
+
51
+ https://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
52
+ """
53
+ substr = ""
54
+ if len(strs) > 1 and len(strs[0]) > 0:
55
+ for i in range(len(strs[0])):
56
+ for j in range(len(strs[0]) - i + 1):
57
+ if j > len(substr) and all(strs[0][i : i + j] in x for x in strs):
58
+ substr = strs[0][i : i + j]
59
+ return substr
60
+
61
+
62
+ def calc_proportion_longest_common_substring(descriptions: Sequence[str]) -> float:
63
+ # find length of longest string
64
+ longest = max([len(description) for description in descriptions])
65
+ if longest == 0:
66
+ return 0.0
67
+
68
+ if len(descriptions) == 2:
69
+ # TODO try this out instead (both behaviour and speed)
70
+ # return fwfuzz.partial_ratio(descriptions[0], descriptions[1]) / 100
71
+ # this would be faster, but I can't install pylcs on my machine
72
+ # len_substring = pylcs.lcs2(descriptions[0], descriptions[1])
73
+ # so fall back on the original implementation
74
+ len_substring = len(longest_substring_multi(descriptions))
75
+ else:
76
+ len_substring = len(longest_substring_multi(descriptions))
77
+
78
+ if len_substring <= 1:
79
+ # decided to count a single letter as a 0
80
+ return 0.0
81
+ val = len_substring / longest
82
+ assert 0 <= val <= 1
83
+ return val
84
+
85
+
86
+ def jinja_get_template_variables(template: str) -> set[str]:
87
+ """
88
+ Extract all variables expected by a Jinja2 template.
89
+
90
+ Args:
91
+ template_string (str): The Jinja2 template string to analyze
92
+
93
+ Returns:
94
+ set: A set of variable names found in the template
95
+
96
+ Example:
97
+ >>> template = "Hello {{ name }}! Your age is {{ age }}"
98
+ >>> get_template_variables(template)
99
+ {'name', 'age'}
100
+ """
101
+ # https://claude.ai/chat/3a2e9e93-c9cd-4b19-8313-ef7640e5971f
102
+ from jinja2 import Environment, meta
103
+
104
+ env = Environment()
105
+ ast = env.parse(template)
106
+ variables = meta.find_undeclared_variables(ast)
107
+ return variables
108
+
109
+
110
+ def jinja_render(
111
+ prompt_template: str,
112
+ context: dict,
113
+ filesystem_loader: Optional[PathOrStr] = None,
114
+ check_surplus_context: bool = True,
115
+ strip=True,
116
+ ):
117
+ """
118
+ Render a Jinja template with the given dictionary, e.g.
119
+
120
+ jinja_render("{{name}} is {{age}} years old", {'name': 'Bob', 'age': 42}) -> "Bob is 42 years old"
121
+
122
+ Will raise an error if CONTEXT is missing any variables.
123
+ """
124
+ from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template
125
+
126
+ loader = None if filesystem_loader is None else FileSystemLoader(filesystem_loader)
127
+ env = Environment(loader=loader, undefined=StrictUndefined)
128
+ template = env.from_string(prompt_template)
129
+ rendered = template.render(context)
130
+ if strip:
131
+ rendered = rendered.strip()
132
+ if check_surplus_context:
133
+ # should it be an error if we have been provided more keys in the context
134
+ # than are used in the template? e.g. this is useful for noticing when the
135
+ # template has e.g. {myvar} with single instead of double braces
136
+ jinja_variables = jinja_get_template_variables(prompt_template)
137
+ surplus_context = set(context.keys()) - jinja_variables
138
+ if surplus_context:
139
+ raise ValueError(f"Surplus context: {surplus_context}")
140
+
141
+ return rendered
142
+
143
+
144
+ # probably better off using slugify from the slugify package
145
+ # import codecs
146
+ # import translitcodec
147
+ # def slugify(text, delim=u'-'):
148
+ # """
149
+ # Generates an ASCII-only slug
150
+
151
+ # Based on http://flask.pocoo.org/snippets/5/
152
+ # """
153
+ # if not text or not text.strip():
154
+ # return ''
155
+ # _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
156
+ # result = []
157
+ # for word in _punct_re.split(text.lower()):
158
+ # # https://pypi.org/project/translitcodec/
159
+ # word = codecs.encode(word, 'translit/long')
160
+ # if word:
161
+ # result.append(word)
162
+ # return str(delim.join(result))
163
+
164
+
165
+ def display_compare_strings(s1, s2):
166
+ if len(s2) > len(s1):
167
+ # e.g. s1='abc', s2='abcd', => s2[3:], i.e. 'd'
168
+ print("Truncated: %s" % s2[len(s1) :])
169
+ print("\n".join(["%s %s" % (pair[0], pair[1]) for pair in zip(s1, s2)]))
170
+
171
+
172
+ def wrap_indent(s: str, indent_level: int = 0, sep=" "):
173
+ spaces = sep * indent_level
174
+ return "\n".join(textwrap.wrap(s, initial_indent=spaces, subsequent_indent=spaces))
175
+
176
+
177
+ def indent_without_wrap(s: str, indent_txt: Optional[str] = None):
178
+ if indent_txt is None:
179
+ indent_txt = " "
180
+ return (
181
+ textwrap.fill(s, initial_indent=indent_txt, subsequent_indent=indent_txt)
182
+ if s and isinstance(s, str)
183
+ else str(s)
184
+ )
185
+
186
+
187
+ def append_fullstop(s: str):
188
+ """
189
+ Appends a fullstop if there isn't already punctuation at the end of S.
190
+ """
191
+ if not isinstance(s, str):
192
+ return ""
193
+ s_orig = s[:]
194
+ s = s.strip()
195
+ if not s:
196
+ return s
197
+ if s[-1] in punctuation:
198
+ return s
199
+ return f"{s}."
200
+
201
+
202
+ def str_from_num(x):
203
+ # todo, find a better way of doing this, e.g.
204
+ # if isinstance(Iterable), recur. otherwise try str(x)
205
+ if x is None:
206
+ return str(x)
207
+ elif isinstance(x, (bool, np.bool_)):
208
+ return str(x)
209
+ elif isinstance(x, str):
210
+ return x
211
+ elif isinstance(x, (int, float)):
212
+ # return f"{x:.2f}"
213
+ return str(round(x * 100))
214
+ elif isinstance(x, (list, tuple)):
215
+ # e.g. [0.20,0.10,-5.00]
216
+ return "[" + ",".join([str_from_num(item) for item in x]) + "]" # type: ignore
217
+ else:
218
+ raise Exception(f"Unknown NUM_STR type: {type(x)}, {x}")
@@ -0,0 +1,28 @@
1
+ # USAGE:
2
+ # python convert_parquet.py my_parquet_file.parquet
3
+ #
4
+ # based on https://chat.openai.com/c/ea3e9401-e7bb-4288-b270-83b0fb327abe
5
+ #
6
+ # pip install pandas openpyxl pyarrow
7
+
8
+ import sys
9
+ import pandas as pd
10
+
11
+ # Replace 'your_file.parquet' with the path to your Parquet file
12
+ # parquet_file = 'vary_amount_of_training_data__adult_sexual__aps.parquet'
13
+ parquet_file = sys.argv[1]
14
+
15
+ assert parquet_file.endswith(".parquet")
16
+
17
+ # Read the Parquet file
18
+ df = pd.read_parquet(parquet_file)
19
+
20
+ # Replace 'output_file.xlsx' with the desired output file name
21
+ output_file = parquet_file.replace('.parquet', '.xlsx')
22
+
23
+ # Write to an Excel file
24
+ df.to_excel(output_file, index=False)
25
+
26
+ print(f"Wrote to {output_file}")
27
+
28
+
gjdutils/typ.py ADDED
@@ -0,0 +1,37 @@
1
+ from inspect import isfunction
2
+
3
+ # these are occasionally useful for if statements,
4
+ # though probably better to rely on type-hinting wherever possible
5
+
6
+
7
+ def isint(f, tol=0.00000001):
8
+ """
9
+ Takes in a float F, and checks that it's within TOL of floor(f).
10
+ """
11
+ # we're casting to float before the comparison with TOL
12
+ # so that decimal Fs work
13
+ return abs(float(f) - int(f)) <= 0.00000001
14
+
15
+
16
+ def isnum(n):
17
+ try:
18
+ float(n)
19
+ return True
20
+ except:
21
+ return False
22
+
23
+
24
+ def is_same_sign(x1, x2):
25
+ if x1 > 0 and x2 > 0:
26
+ return True
27
+ if x1 < 0 and x2 < 0:
28
+ return True
29
+
30
+
31
+ def isiterable(x):
32
+ """
33
+ from http://stackoverflow.com/questions/1952464/in-python-how-do-i-determine-if-a-variable-is-iterable
34
+ """
35
+ import collections
36
+
37
+ return isinstance(x, collections.Iterable)
@@ -0,0 +1,29 @@
1
+ # from https://github.com/Uberi/speech_recognition/blob/master/examples/microphone_recognition.py
2
+
3
+ #!/usr/bin/env python3
4
+
5
+ # NOTE: this example requires PyAudio because it uses the Microphone class
6
+
7
+ from typing import Optional
8
+ import speech_recognition as sr
9
+ from .env import get_env_var
10
+
11
+
12
+ def recognise_speech(display: Optional[str], verbose: int = 0):
13
+ if display:
14
+ print(display, end="", flush=True)
15
+ # obtain audio from the microphone
16
+ r = sr.Recognizer()
17
+ with sr.Microphone() as source:
18
+ # print("Say something!")
19
+ audio = r.listen(source)
20
+ print("... PROCESSING")
21
+ openai_api_key = get_env_var("OPENAI_API_KEY")
22
+ text = r.recognize_whisper_api(audio, api_key=openai_api_key)
23
+ if verbose > 0:
24
+ print(text)
25
+ return text
26
+
27
+
28
+ if __name__ == "__main__":
29
+ recognise_speech("Say something!", verbose=1)
gjdutils/web.py ADDED
@@ -0,0 +1,68 @@
1
+ from pathlib import Path
2
+ from urllib import parse as urlparse
3
+ import webbrowser
4
+
5
+ from gjdutils.strings import PathOrStr
6
+
7
+
8
+ def webbrowser_open(filen: PathOrStr, browser=None):
9
+ """
10
+ For some reason, the default webbrowser.open() doesn't work for me, so you may want to set browser to 'chrome'
11
+ """
12
+ # I had an issue where it refused to open a non .html file
13
+ assert filen.endswith(".html"), "File must end with .html"
14
+ if browser:
15
+ browser = webbrowser.get(browser)
16
+ else:
17
+ browser = webbrowser
18
+ full_filen = f"file://{Path.cwd() / filen}"
19
+ return browser.open(full_filen)
20
+
21
+
22
+ def trunc_url(url):
23
+ """
24
+ e.g. 'http://www.guardian.co.uk/blah/ -> /blah
25
+
26
+ Based on dev/guardian/data/data/greg/sharedwisdom/sharedwisdom/models.py
27
+ """
28
+ # URLPARSE returns ParseResult(scheme='http',
29
+ # netloc='memrise.com',
30
+ # path='/blah.png',
31
+ # params='',
32
+ # query='q1=x&q2=y',
33
+ # fragment='')
34
+ scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
35
+ # ditch the SCHEME and NETLOC
36
+ # PARAMS are an arcane part that comes after a semi-colon
37
+ return path # + params
38
+
39
+
40
+ def validate_request_args(args, defaults):
41
+ """
42
+ DEFAULTS = dict of allowed query parameters, with the keys
43
+ being the allowed query-string-parameter-keys and values
44
+ as their defaults.
45
+ """
46
+ if args:
47
+ unexpecteds = set(args.keys()) - set(defaults.keys())
48
+ assert not unexpecteds, "Unexpected key(s): %s" % unexpecteds
49
+ # params = {key: args.get(key, default)
50
+ # for key, default in defaults.items()}
51
+ params = defaults | args
52
+ return params
53
+
54
+
55
+ def query_string_from_dict(d):
56
+ return "?" + "&".join(["%s=%s" % (k, v) for k, v in d.items()])
57
+
58
+
59
+ def params_from_request(request):
60
+ try:
61
+ if request.json:
62
+ params_in = request.json.get("params", {})
63
+ else:
64
+ params_in = dict(request.values)
65
+ return params_in
66
+ except:
67
+ print("Error in PARAMS_FROM_REQUEST")
68
+ return {}
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: GJDutils
3
+ Version: 0.2.2
4
+ Summary: A collection of useful utility functions (basics, data science/AI, web development, etc)
5
+ Project-URL: Homepage, https://github.com/gregdetre/gjdutils
6
+ Project-URL: Repository, https://github.com/gregdetre/gjdutils
7
+ Author-email: Greg Detre <greg@gregdetre.com>
8
+ License-File: LICENSE
9
+ Keywords: ai,data science,dates,llm,strings,utilities,web development
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Utilities
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: ipython
18
+ Requires-Dist: jinja2
19
+ Requires-Dist: pydantic
20
+ Requires-Dist: python-dotenv
21
+ Provides-Extra: all-no-dev
22
+ Requires-Dist: azure-cognitiveservices-speech; extra == 'all-no-dev'
23
+ Requires-Dist: bs4; extra == 'all-no-dev'
24
+ Requires-Dist: cachetools; extra == 'all-no-dev'
25
+ Requires-Dist: elevenlabs; extra == 'all-no-dev'
26
+ Requires-Dist: google-cloud-texttospeech; extra == 'all-no-dev'
27
+ Requires-Dist: google-cloud-translate; extra == 'all-no-dev'
28
+ Requires-Dist: humanize; extra == 'all-no-dev'
29
+ Requires-Dist: openai; extra == 'all-no-dev'
30
+ Requires-Dist: pendulum; extra == 'all-no-dev'
31
+ Requires-Dist: pillow; extra == 'all-no-dev'
32
+ Requires-Dist: playsound; extra == 'all-no-dev'
33
+ Requires-Dist: pygame; extra == 'all-no-dev'
34
+ Requires-Dist: python-vlc; extra == 'all-no-dev'
35
+ Requires-Dist: speechrecognition; extra == 'all-no-dev'
36
+ Provides-Extra: audio-lang
37
+ Requires-Dist: azure-cognitiveservices-speech; extra == 'audio-lang'
38
+ Requires-Dist: cachetools; extra == 'audio-lang'
39
+ Requires-Dist: elevenlabs; extra == 'audio-lang'
40
+ Requires-Dist: google-cloud-texttospeech; extra == 'audio-lang'
41
+ Requires-Dist: google-cloud-translate; extra == 'audio-lang'
42
+ Requires-Dist: playsound; extra == 'audio-lang'
43
+ Requires-Dist: pygame; extra == 'audio-lang'
44
+ Requires-Dist: python-vlc; extra == 'audio-lang'
45
+ Requires-Dist: speechrecognition; extra == 'audio-lang'
46
+ Provides-Extra: dev
47
+ Requires-Dist: black; extra == 'dev'
48
+ Requires-Dist: build; extra == 'dev'
49
+ Requires-Dist: pytest; extra == 'dev'
50
+ Requires-Dist: rich; extra == 'dev'
51
+ Requires-Dist: twine; extra == 'dev'
52
+ Requires-Dist: typer; extra == 'dev'
53
+ Requires-Dist: wheel; extra == 'dev'
54
+ Provides-Extra: dt
55
+ Requires-Dist: humanize; extra == 'dt'
56
+ Requires-Dist: pendulum; extra == 'dt'
57
+ Provides-Extra: html-web
58
+ Requires-Dist: bs4; extra == 'html-web'
59
+ Provides-Extra: llm
60
+ Requires-Dist: cachetools; extra == 'llm'
61
+ Requires-Dist: openai; extra == 'llm'
62
+ Requires-Dist: pillow; extra == 'llm'
63
+ Description-Content-Type: text/markdown
64
+
65
+ # gjdutils
66
+
67
+ A collection of useful utility functions (strings, dates, data science/AI, web development, types, etc).
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ pip install gjdutils
73
+ ```
74
+
75
+ For optional features:
76
+ ```bash
77
+ pip install "gjdutils[dt]" # Date/time utilities
78
+ pip install "gjdutils[llm]" # AI/LLM integrations
79
+ pip install "gjdutils[audio_lang]" # Speech/translation, language-related
80
+ pip install "gjdutils[html_web]" # Web scraping
81
+ pip install "gjdutils[dev]" # Development tools (for tweaking `gjdutils` itself, e.g. pytest)
82
+
83
+ # Install all optional dependencies at once (except `dev`, which is used for developing `gjdutils` itself)
84
+ pip install "gjdutils[all_no_dev]"
85
+ ```
86
+
87
+ ### Development Setup
88
+
89
+ If you're developing `gjdutils` itself:
90
+ ```bash
91
+ # From the gjdutils root directory
92
+ pip install -e ".[dev]" # Install in editable mode with development dependencies
93
+ pip install -e ".[all_no_dev]" # Install all optional dependencies (except dev)
94
+ ```
95
+
96
+ ### Adding to requirements.txt
97
+
98
+ To add to your `requirements.txt` in editable mode, e.g. to install all optional dependencies:
99
+ ```text
100
+ -e "git+https://github.com/gregdetre/gjdutils.git#egg=gjdutils[all_no_dev]"
101
+ ```
@@ -0,0 +1,49 @@
1
+ gjdutils/__init__.py,sha256=1pOy87RwksWpLHdzHBxDuDahPd6SLs4m3QnJydClhI4,312
2
+ gjdutils/audios.py,sha256=gEk_frpyyYHus2HzsU0l6RQmtmBMaBr2V7HfvT3AWSI,1286
3
+ gjdutils/cacheing.py,sha256=rnUrYffWhOyp5ezTOoKBaeuSTN2cNVhbX0f3piLLb0A,7434
4
+ gjdutils/cmd.py,sha256=FBQEP36_rKtIJzA78ftjEH2ooP5XxlfqRLNAWMcrJVQ,4890
5
+ gjdutils/colab.py,sha256=yZsC73qKPOjtMvztJvZAYZDLD9l3bmW0v9jKyS0fS2E,836
6
+ gjdutils/collections.py,sha256=XL0QQitMD3H2mtaDpnM8ffyBVS05sICfhz15GETNRCE,1264
7
+ gjdutils/decorators.py,sha256=x48h-kL8JCgcKaGRZlRAstzWSyPVW73B-_jBkU1Vzmg,933
8
+ gjdutils/dicts.py,sha256=GjBO5CeJEuPD063w-RrYPIRdX-zyw0lrPH34ol4SW_I,5563
9
+ gjdutils/dsci.py,sha256=XOFIbZLjyr0vmhJDspfjpTZC1-sFSmkzRfkXqGG3HlA,6201
10
+ gjdutils/dt.py,sha256=PIb_OvXcUDH-uHLuKC8yjF3QvYWMiTutUk75Uc0aEA4,7826
11
+ gjdutils/env.py,sha256=WfA-FeCdhuVdQmsfwxWN7Z8Z4tnHB59T384yGFqPrP8,1909
12
+ gjdutils/errors.py,sha256=JWuQ0dhTevekQWdwIQg50M_KZTCIdZzC3fvPbGu8KxQ,224
13
+ gjdutils/files.py,sha256=2ssTJo8iZj4WwXm7-wCpSqO32q6dn9mHKdYNcj-orGk,4193
14
+ gjdutils/functions.py,sha256=FbCc9wm9HgY7LCuivINCs5JHyaItisWl-99VuZoluSk,140
15
+ gjdutils/google_translate.py,sha256=b7wd9GdS_8_nlSo2sZJFuZcFIg_lY72lGt9ue3isQdg,2486
16
+ gjdutils/hashing.py,sha256=109RcUUkn3jmOZpcbV-4AFcoU_oAJn3IrFzrCb-5-ms,990
17
+ gjdutils/html.py,sha256=etDpcBpUt71O-kZfBaA9v4_ePj5bxAl6h_L-NXrX92c,2971
18
+ gjdutils/indexing.py,sha256=ZptiB1ranTjlJo7HsL56KmyAmRyjZwIS4pJx7D2cf8g,3444
19
+ gjdutils/iterfunc.py,sha256=4vVJZ72I5-UhRUX6-gUoqzvUE0jjPyRugNQQYJQA-r4,2519
20
+ gjdutils/jsons.py,sha256=Qrl382B6tl2q9PsFyXFCVGo4G5Q9CuMBW528sGK-Iwk,1971
21
+ gjdutils/lists.py,sha256=fGBNtld-3CmFwgATxTGltIEcmsz2gauCZC_11xOKDr0,438
22
+ gjdutils/llm_utils.py,sha256=HfGadqTr7q9H0uN4ffHQWgY6n3tp3SRsoSr1aFilg_8,5671
23
+ gjdutils/llms_claude.py,sha256=eUjK6koob5KKmaB471z3fatd1tEVOmLmB8aT6uNvqAI,4107
24
+ gjdutils/llms_openai.py,sha256=CiGdomHfldFugS6fe2vv6phQbpyP9d9qsOfHC-4CvBU,10401
25
+ gjdutils/misc.py,sha256=BpJiuf1_czKp0hLy681yV04FgyBMF6nTYlht3hxtxI4,592
26
+ gjdutils/num.py,sha256=x5PD3WKvUKrtZl0kGaMqX-3Ex5Ar_Fa54eMU1mseejU,2196
27
+ gjdutils/outloud_text_to_speech.py,sha256=1xhQjQUsgqSKM7U-oiDSvgUaNVLPP5qvYIDPQq6EoQo,8020
28
+ gjdutils/prompt_templates.py,sha256=WTIs-kVSDwiu6Ols0ZIWk5er4dnmL9DRZO6PjwNpgrU,634
29
+ gjdutils/pypi_build.py,sha256=V3g0RJucFi2mbKOuzG-6W6rjAcwJwVolczxH-ZGV7zw,3578
30
+ gjdutils/pytest_utils.py,sha256=qpTX6ehzNSOi4Y475JcR2YwbcMFsH71CmxczR9i5EMA,871
31
+ gjdutils/rand.py,sha256=_7OtrGIdDRpr7OLVwuX6b9Xz0eKK3NvyXsG9Nu-JLQA,1730
32
+ gjdutils/regex.py,sha256=pzQKPAcw72XXMZkdLqjzmjL3mqzcOUiGrqsIHgLeBNM,3139
33
+ gjdutils/requirements_dev.txt,sha256=foECC915zqqiIG2V4eohElBLF7jZ8ckpqiv5CAEOmYI,33
34
+ gjdutils/runtime.py,sha256=njKM7YvGm74r2joM7A4vvh6_jIHVfbvLv5sg82l5XHY,564
35
+ gjdutils/sets.py,sha256=749lZBOJeMweqyL0xW13xkjsshGJDB7AkYX0rkm2pq8,293
36
+ gjdutils/shell.py,sha256=BV6w6gavvTw-AXRUzZl5MRJp2BJtYb6Lv2WNLIVPLTs,1648
37
+ gjdutils/sorteddict.py,sha256=HVWHYl6L7E3T0X4c9G2lqpFVfKxAzgfpaTJ8xZTdMh4,1047
38
+ gjdutils/stopwatch.py,sha256=aJYbykOrPaa47D8zEaYEGEGqk4yIH0EqAhP7ucPfp0U,1905
39
+ gjdutils/strings.py,sha256=n6JM1ESUWAIvUxqWIedEDAw9IcELHWUJ6GpKca2W5gY,6830
40
+ gjdutils/typ.py,sha256=GCgaXSUWzQ4V2dwj_SUzYS-XXtzBs2AHkGv-XVTc80w,835
41
+ gjdutils/voice_speechrecognition.py,sha256=jWSl_vxkpBmJkf5m8v9Z1Q6Y30nPxQZ-4ezXaEJI0cI,859
42
+ gjdutils/web.py,sha256=bquklGMPe1syFMAxvPUEtGZiYeqiXl7xjGtI38Xd7I0,2171
43
+ gjdutils/obsolete/google_text_to_speech.py,sha256=PuCDf0z8oXqF2gA7ZshmF-TY9vUEmTjGJ80DeYFRC80,1974
44
+ gjdutils/obsolete/llms_obsolete.py,sha256=46mJ0HSV3kkvpuCN2xSA5QYJYA-xKc-S0SnLon1A6BM,10339
45
+ gjdutils/todo/convert_parquet.py,sha256=M1bq9cAU9lhOZLS4uwVBB5iyTyHnVGJCjn4zGqLI8zQ,699
46
+ gjdutils-0.2.2.dist-info/METADATA,sha256=1EogzH3PB3-pzPp1i0ZDwLoF1dS86PAHrBKhph0KUyM,3844
47
+ gjdutils-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
48
+ gjdutils-0.2.2.dist-info/licenses/LICENSE,sha256=WpqpqdB8Jg7s0BZwJaQBlLzhx1bFf-HvXEQLJYNqph4,1067
49
+ gjdutils-0.2.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Greg Detre
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.