toolslm 0.3.2__py3-none-any.whl → 0.3.7__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.
toolslm/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.2"
1
+ __version__ = "0.3.7"
toolslm/_modidx.py CHANGED
@@ -14,7 +14,7 @@ d = { 'settings': { 'branch': 'main',
14
14
  'toolslm.download.read_html': ('download.html#read_html', 'toolslm/download.py'),
15
15
  'toolslm.download.read_md': ('download.html#read_md', 'toolslm/download.py'),
16
16
  'toolslm.download.split_url': ('download.html#split_url', 'toolslm/download.py')},
17
- 'toolslm.funccall': { 'toolslm.funccall.PathArg': ('funccall.html#patharg', 'toolslm/funccall.py'),
17
+ 'toolslm.funccall': { 'toolslm.funccall._coerce_inputs': ('funccall.html#_coerce_inputs', 'toolslm/funccall.py'),
18
18
  'toolslm.funccall._copy_loc': ('funccall.html#_copy_loc', 'toolslm/funccall.py'),
19
19
  'toolslm.funccall._get_nested_schema': ('funccall.html#_get_nested_schema', 'toolslm/funccall.py'),
20
20
  'toolslm.funccall._handle_container': ('funccall.html#_handle_container', 'toolslm/funccall.py'),
@@ -29,16 +29,25 @@ d = { 'settings': { 'branch': 'main',
29
29
  'toolslm.funccall.call_func_async': ('funccall.html#call_func_async', 'toolslm/funccall.py'),
30
30
  'toolslm.funccall.get_schema': ('funccall.html#get_schema', 'toolslm/funccall.py'),
31
31
  'toolslm.funccall.mk_ns': ('funccall.html#mk_ns', 'toolslm/funccall.py'),
32
- 'toolslm.funccall.python': ('funccall.html#python', 'toolslm/funccall.py')},
32
+ 'toolslm.funccall.mk_param': ('funccall.html#mk_param', 'toolslm/funccall.py'),
33
+ 'toolslm.funccall.mk_tool': ('funccall.html#mk_tool', 'toolslm/funccall.py'),
34
+ 'toolslm.funccall.python': ('funccall.html#python', 'toolslm/funccall.py'),
35
+ 'toolslm.funccall.schema2sig': ('funccall.html#schema2sig', 'toolslm/funccall.py')},
33
36
  'toolslm.md_hier': {},
34
37
  'toolslm.shell': { 'toolslm.shell.TerminalInteractiveShell.run_cell': ( 'shell.html#terminalinteractiveshell.run_cell',
35
38
  'toolslm/shell.py'),
36
39
  'toolslm.shell.get_shell': ('shell.html#get_shell', 'toolslm/shell.py')},
37
40
  'toolslm.xml': { 'toolslm.xml._add_nls': ('xml.html#_add_nls', 'toolslm/xml.py'),
41
+ 'toolslm.xml.cell2out': ('xml.html#cell2out', 'toolslm/xml.py'),
42
+ 'toolslm.xml.cell2xml': ('xml.html#cell2xml', 'toolslm/xml.py'),
38
43
  'toolslm.xml.docs_xml': ('xml.html#docs_xml', 'toolslm/xml.py'),
39
44
  'toolslm.xml.files2ctx': ('xml.html#files2ctx', 'toolslm/xml.py'),
40
45
  'toolslm.xml.folder2ctx': ('xml.html#folder2ctx', 'toolslm/xml.py'),
41
46
  'toolslm.xml.folder2ctx_cli': ('xml.html#folder2ctx_cli', 'toolslm/xml.py'),
47
+ 'toolslm.xml.get_mime_text': ('xml.html#get_mime_text', 'toolslm/xml.py'),
42
48
  'toolslm.xml.json_to_xml': ('xml.html#json_to_xml', 'toolslm/xml.py'),
43
49
  'toolslm.xml.mk_doc': ('xml.html#mk_doc', 'toolslm/xml.py'),
44
- 'toolslm.xml.mk_doctype': ('xml.html#mk_doctype', 'toolslm/xml.py')}}}
50
+ 'toolslm.xml.mk_doctype': ('xml.html#mk_doctype', 'toolslm/xml.py'),
51
+ 'toolslm.xml.nb2xml': ('xml.html#nb2xml', 'toolslm/xml.py'),
52
+ 'toolslm.xml.read_file': ('xml.html#read_file', 'toolslm/xml.py'),
53
+ 'toolslm.xml.repo2ctx': ('xml.html#repo2ctx', 'toolslm/xml.py')}}}
toolslm/funccall.py CHANGED
@@ -1,15 +1,20 @@
1
1
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../01_funccall.ipynb.
2
2
 
3
3
  # %% auto 0
4
- __all__ = ['empty', 'custom_types', 'get_schema', 'PathArg', 'python', 'mk_ns', 'call_func', 'call_func_async']
4
+ __all__ = ['empty', 'custom_types', 'get_schema', 'python', 'mk_ns', 'call_func', 'call_func_async', 'mk_param', 'schema2sig',
5
+ 'mk_tool']
5
6
 
6
7
  # %% ../01_funccall.ipynb
7
- import inspect
8
+ import inspect, json
8
9
  from collections import abc
9
10
  from fastcore.utils import *
10
11
  from fastcore.docments import docments
11
- from typing import get_origin, get_args, Dict, List, Optional, Tuple, Union
12
+ from typing import get_origin, get_args, Dict, List, Optional, Tuple, Union, Any
12
13
  from types import UnionType
14
+ from typing import get_type_hints
15
+ from inspect import Parameter, Signature
16
+ from decimal import Decimal
17
+ from uuid import UUID
13
18
 
14
19
  # %% ../01_funccall.ipynb
15
20
  empty = inspect.Parameter.empty
@@ -41,7 +46,7 @@ def _param(name, info):
41
46
  return pschema
42
47
 
43
48
  # %% ../01_funccall.ipynb
44
- custom_types = {Path}
49
+ custom_types = {Path, bytes, Decimal, UUID}
45
50
 
46
51
  def _handle_type(t, defs):
47
52
  "Handle a single type, creating nested schemas if necessary"
@@ -107,7 +112,9 @@ def _get_nested_schema(obj):
107
112
  if n != 'return' and n != 'self':
108
113
  _process_property(n, o, props, req, defs)
109
114
 
110
- schema = dict(type='object', properties=props, title=obj.__name__ if isinstance(obj, type) else None)
115
+ tkw = {}
116
+ if isinstance(obj, type): tkw['title']=obj.__name__
117
+ schema = dict(type='object', properties=props, **tkw)
111
118
  if req: schema['required'] = list(req)
112
119
  if defs: schema['$defs'] = defs
113
120
  return schema
@@ -121,14 +128,9 @@ def get_schema(f:Union[callable,dict], pname='input_schema')->dict:
121
128
  assert desc, "Docstring missing!"
122
129
  d = docments(f, full=True)
123
130
  ret = d.pop('return')
124
- if ret.anno is not empty: desc += f'\n\nReturns:\n- type: {_types(ret.anno)[0]}'
131
+ if (ret.anno is not empty) and (ret.anno is not None): desc += f'\n\nReturns:\n- type: {_types(ret.anno)[0]}'
125
132
  return {"name": f.__name__, "description": desc, pname: schema}
126
133
 
127
- # %% ../01_funccall.ipynb
128
- def PathArg(
129
- path: str # A filesystem path
130
- ): return Path(path)
131
-
132
134
  # %% ../01_funccall.ipynb
133
135
  import ast, time, signal, traceback
134
136
  from fastcore.utils import *
@@ -191,14 +193,26 @@ def mk_ns(fs):
191
193
  elif callable(o) and hasattr(o, '__name__'): merged |= {o.__name__: o}
192
194
  return merged
193
195
 
196
+ # %% ../01_funccall.ipynb
197
+ def _coerce_inputs(func, inputs):
198
+ "Coerce inputs based on function type annotations"
199
+ hints = get_type_hints(func) if hasattr(func, '__annotations__') else {}
200
+ res = {}
201
+ for k,v in inputs.items():
202
+ ann = hints.get(k)
203
+ if ann in custom_types: res[k] = ann(v)
204
+ elif isinstance(v, dict) and callable(ann): res[k] = ann(**v)
205
+ else: res[k] = v
206
+ return res
207
+
194
208
  # %% ../01_funccall.ipynb
195
209
  def call_func(fc_name, fc_inputs, ns, raise_on_err=True):
196
210
  "Call the function `fc_name` with the given `fc_inputs` using namespace `ns`."
197
211
  if not isinstance(ns, abc.Mapping): ns = mk_ns(ns)
198
212
  func = ns[fc_name]
199
- # Clean up bad param names
200
213
  inps = {re.sub(r'\W', '', k):v for k,v in fc_inputs.items()}
201
- try: return func(**fc_inputs)
214
+ inps = _coerce_inputs(func, inps)
215
+ try: return func(**inps)
202
216
  except Exception as e:
203
217
  if raise_on_err: raise e from None
204
218
  else: return traceback.format_exc()
@@ -213,3 +227,35 @@ async def call_func_async(fc_name, fc_inputs, ns, raise_on_err=True):
213
227
  if raise_on_err: raise e from None
214
228
  else: return traceback.format_exc()
215
229
  return res
230
+
231
+ # %% ../01_funccall.ipynb
232
+ def mk_param(nm, props, req):
233
+ "Create a `Parameter` for `nm` with schema `props`"
234
+ kind = Parameter.POSITIONAL_OR_KEYWORD if nm in req else Parameter.KEYWORD_ONLY
235
+ default = Parameter.empty if nm in req else props.get('default')
236
+ if props.get('type') == 'array' and 'items' in props:
237
+ item_type = type_map.get(props['items'].get('type'), Any)
238
+ anno = list[item_type]
239
+ else: anno = type_map.get(props.get('type'), Any)
240
+ return Parameter(nm, kind, default=default, annotation=anno)
241
+
242
+ # %% ../01_funccall.ipynb
243
+ def schema2sig(tool):
244
+ "Convert json schema `tool` to a `Signature`"
245
+ props, req = tool.inputSchema['properties'], tool.inputSchema.get('required', [])
246
+ params = sorted([mk_param(k, v, req) for k, v in props.items()], key=lambda p: p.kind)
247
+ return Signature(params)
248
+
249
+ # %% ../01_funccall.ipynb
250
+ def mk_tool(dispfn, tool):
251
+ "Create a callable function from a JSON schema tool definition"
252
+ sig = schema2sig(tool)
253
+ props = tool.inputSchema['properties']
254
+ def fn(*args, **kwargs):
255
+ bound = sig.bind(*args, **kwargs)
256
+ return dispfn(tool.name, **bound.arguments)
257
+ fn.__doc__ = tool.description
258
+ fn.__signature__ = sig
259
+ fn.__name__ = fn.__qualname__ = tool.name
260
+ fn.__annotations__ = {k: p.annotation for k, p in sig.parameters.items()}
261
+ return fn
toolslm/md_hier.py CHANGED
@@ -14,20 +14,32 @@ def create_heading_dict(text, rm_fenced=True):
14
14
  original_text = text
15
15
  original_lines = text.splitlines()
16
16
 
17
- # Use fenced-removed text only for finding headings
18
- text_for_headings = text
19
- if rm_fenced: text_for_headings = re.sub(r'```[\s\S]*?```', '', text)
17
+ # Build line mapping when removing fenced blocks
18
+ if rm_fenced:
19
+ filtered_lines,original_line_map,in_fenced = [],{},False
20
+ for i, line in enumerate(original_lines):
21
+ if line.strip().startswith('```'):
22
+ in_fenced = not in_fenced
23
+ continue
24
+ if not in_fenced:
25
+ original_line_map[len(filtered_lines)] = i
26
+ filtered_lines.append(line)
27
+
28
+ lines_for_headings = filtered_lines
29
+ else:
30
+ lines_for_headings = original_lines
31
+ original_line_map = {i: i for i in range(len(original_lines))}
20
32
 
21
- lines_for_headings = text_for_headings.splitlines()
22
33
  headings = []
23
34
 
24
- # Parse headings with their levels and line numbers
35
+ # Parse headings with their levels and original line numbers
25
36
  for idx, line in enumerate(lines_for_headings):
26
37
  match = re.match(r'^(#{1,6})\s+\S.*', line)
27
38
  if match:
28
39
  level = len(match.group(1))
29
40
  title = line.strip('#').strip()
30
- headings.append({'level': level, 'title': title, 'line': idx})
41
+ original_line_idx = original_line_map[idx]
42
+ headings.append({'level': level, 'title': title, 'line': original_line_idx})
31
43
 
32
44
  # Assign text content to each heading using original lines
33
45
  for i, h in enumerate(headings):
@@ -270,9 +282,26 @@ More content after code."""
270
282
  assert '💻 Computer' in result['🚀 Rocket Heading']
271
283
  assert result.text == md_content
272
284
 
285
+ def test_fenced_blocks_between_headings():
286
+ md_content = """# First Header
287
+ Content here.
288
+
289
+ ```python
290
+ # A comment that should be ignored for structure
291
+ def example():
292
+ pass
293
+ ```
294
+
295
+ # Second Header
296
+ This content belongs to Second Header."""
297
+ result = create_heading_dict(md_content)
298
+ assert "def example():" not in result['Second Header'].text
299
+ assert result['Second Header'].text == "# Second Header\nThis content belongs to Second Header."
300
+
273
301
  test_empty_input()
274
302
  test_whitespace_only()
275
303
  test_malformed_headings()
276
304
  test_unicode_and_emojis()
305
+ test_fenced_blocks_between_headings()
277
306
  print('tests passed')
278
307
 
toolslm/xml.py CHANGED
@@ -1,19 +1,19 @@
1
1
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../00_xml.ipynb.
2
2
 
3
3
  # %% auto 0
4
- __all__ = ['doctype', 'json_to_xml', 'mk_doctype', 'mk_doc', 'docs_xml', 'files2ctx', 'folder2ctx', 'folder2ctx_cli']
4
+ __all__ = ['doctype', 'json_to_xml', 'get_mime_text', 'cell2out', 'cell2xml', 'nb2xml', 'mk_doctype', 'mk_doc', 'docs_xml',
5
+ 'read_file', 'files2ctx', 'folder2ctx', 'repo2ctx', 'folder2ctx_cli']
5
6
 
6
7
  # %% ../00_xml.ipynb
7
8
  import hashlib,xml.etree.ElementTree as ET
8
9
  from collections import namedtuple
10
+ from ghapi.all import GhApi
9
11
 
10
12
  from fastcore.utils import *
11
13
  from fastcore.meta import delegates
12
14
  from fastcore.xtras import hl_md
13
- from fastcore.xml import to_xml, Document, Documents, Document_content, Src
15
+ from fastcore.xml import to_xml, Document, Documents, Document_content, Src, Source,Out,Outs,Cell,Notebook,Md,Code
14
16
  from fastcore.script import call_parse
15
- try: from IPython import display
16
- except: display=None
17
17
 
18
18
  # %% ../00_xml.ipynb
19
19
  def json_to_xml(d:dict, # JSON dictionary to convert
@@ -31,6 +31,42 @@ def json_to_xml(d:dict, # JSON dictionary to convert
31
31
  ET.indent(root)
32
32
  return ET.tostring(root, encoding='unicode')
33
33
 
34
+ # %% ../00_xml.ipynb
35
+ def get_mime_text(data):
36
+ "Get text from MIME bundle, preferring markdown over plain"
37
+ if 'text/markdown' in data: return ''.join(list(data['text/markdown']))
38
+ if 'text/plain' in data: return ''.join(list(data['text/plain']))
39
+
40
+ # %% ../00_xml.ipynb
41
+ def cell2out(o):
42
+ "Convert single notebook output to XML format"
43
+ if hasattr(o, 'data'):
44
+ txt = get_mime_text(o.data)
45
+ if txt: return Out(txt, mime='markdown' if 'text/markdown' in o.data else 'plain')
46
+ if hasattr(o, 'text'):
47
+ txt = o.text if isinstance(o.text, str) else ''.join(o.text)
48
+ return Out(txt, type='stream', name=o.get('name', 'stdout'))
49
+ if hasattr(o, 'ename'): return Out(f"{o.ename}: {o.evalue}", type='error')
50
+
51
+ # %% ../00_xml.ipynb
52
+ def cell2xml(cell, out=True):
53
+ "Convert notebook cell to concise XML format"
54
+ src = ''.join(getattr(cell, 'source', ''))
55
+ f = Code if cell.cell_type=='code' else Md
56
+ if not out: return f(src)
57
+ parts = [Source(src)]
58
+ out_items = L(getattr(cell,'outputs',[])).map(cell2out).filter()
59
+ if out_items: parts.append(Outs(*out_items))
60
+ return f(*parts)
61
+
62
+ # %% ../00_xml.ipynb
63
+ def nb2xml(fname=None, nb=None, out=True):
64
+ "Convert notebook to XML format"
65
+ assert bool(fname)^bool(nb), "Pass either `fname` or `nb`"
66
+ if not nb: nb = dict2obj(fname.read_json())
67
+ cells_xml = [to_xml(cell2xml(c, out=out), do_escape=False) for c in nb.cells if c.cell_type in ('code','markdown')]
68
+ return to_xml(Notebook(*cells_xml), do_escape=False)
69
+
34
70
  # %% ../00_xml.ipynb
35
71
  doctype = namedtuple('doctype', ['src', 'content'])
36
72
 
@@ -73,32 +109,68 @@ def docs_xml(docs:list[str], # The content of each document
73
109
  if srcs is None: srcs = [None]*len(docs)
74
110
  if details is None: details = [{}]*len(docs)
75
111
  docs = (mk_doc(i+1, d, s, **kw) for i,(d,s,kw) in enumerate(zip(docs,srcs,details)))
76
- return pre + to_xml(Documents(docs))
112
+ return pre + to_xml(Documents(docs), do_escape=False)
113
+
114
+ # %% ../00_xml.ipynb
115
+ def read_file(fname, out=True):
116
+ "Read file content, converting notebooks to XML if needed"
117
+ fname = Path(fname)
118
+ if fname.suffix == '.ipynb': return nb2xml(fname, out=out)
119
+ return fname.read_text()
77
120
 
78
121
  # %% ../00_xml.ipynb
79
122
  def files2ctx(
80
123
  fnames:list[Union[str,Path]], # List of file names to add to context
81
- prefix:bool=True # Include Anthropic's suggested prose intro?
124
+ prefix:bool=True, # Include Anthropic's suggested prose intro?
125
+ out:bool=True, # Include notebook cell outputs?
126
+ srcs:Optional[list]=None # Use the labels instead of `fnames`
82
127
  )->str: # XML for LM context
128
+ "Convert files to XML context, handling notebooks"
83
129
  fnames = [Path(o) for o in fnames]
84
- contents = [o.read_text() for o in fnames]
85
- return docs_xml(contents, fnames, prefix=prefix)
130
+ contents = [read_file(o, out=out) for o in fnames]
131
+ return docs_xml(contents, srcs or fnames, prefix=prefix)
86
132
 
87
133
  # %% ../00_xml.ipynb
88
134
  @delegates(globtastic)
89
135
  def folder2ctx(
90
- folder:Union[str,Path], # Folder name containing files to add to context
91
- prefix:bool=True, # Include Anthropic's suggested prose intro?
92
- **kwargs # Passed to `globtastic`
93
- )->str: # XML for Claude context
136
+ folder:Union[str,Path],
137
+ prefix:bool=True,
138
+ out:bool=True,
139
+ include_base:bool=True,
140
+ **kwargs
141
+ )->str:
142
+ "Convert folder contents to XML context, handling notebooks"
143
+ folder = Path(folder)
94
144
  fnames = globtastic(folder, **kwargs)
95
- return files2ctx(fnames, prefix=prefix)
145
+ srcs = fnames if include_base else [Path(f).relative_to(folder) for f in fnames]
146
+ return files2ctx(fnames, prefix=prefix, out=out, srcs=srcs)
147
+
148
+ # %% ../00_xml.ipynb
149
+ @delegates(folder2ctx)
150
+ def repo2ctx(
151
+ owner:str, # GitHub repo owner
152
+ repo:str, # GitHub repo name
153
+ ref:str=None, # Git ref (branch/tag/sha); defaults to repo's default branch
154
+ **kwargs # Passed to `folder2ctx`
155
+ )->str: # XML for LM context
156
+ "Convert GitHub repo to XML context without cloning"
157
+ import tempfile, tarfile, io
158
+ api = GhApi()
159
+ if ref is None: ref = api.repos.get(owner, repo).default_branch
160
+ data = api.repos.download_tarball_archive(owner, repo, ref)
161
+ tf = tarfile.open(fileobj=io.BytesIO(data))
162
+ with tempfile.TemporaryDirectory() as tmp:
163
+ tf.extractall(tmp, filter='data')
164
+ subdir = Path(tmp) / tf.getmembers()[0].name.split('/')[0]
165
+ return folder2ctx(subdir, include_base=False, **kwargs)
96
166
 
97
167
  # %% ../00_xml.ipynb
98
168
  @call_parse
99
169
  @delegates(folder2ctx)
100
170
  def folder2ctx_cli(
101
171
  folder:str, # Folder name containing files to add to context
172
+ out:bool=True, # Include notebook cell outputs?
102
173
  **kwargs # Passed to `folder2ctx`
103
174
  )->str: # XML for Claude context
104
- print(folder2ctx(folder, **kwargs))
175
+ "CLI to convert folder contents to XML context, handling notebooks"
176
+ print(folder2ctx(folder, out=out, **kwargs))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toolslm
3
- Version: 0.3.2
3
+ Version: 0.3.7
4
4
  Summary: Tools to make language models a bit easier to use
5
5
  Home-page: https://github.com/AnswerDotAI/toolslm
6
6
  Author: Jeremy Howard
@@ -16,7 +16,7 @@ Classifier: License :: OSI Approved :: Apache Software License
16
16
  Requires-Python: >=3.9
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: fastcore>=1.5.47
19
+ Requires-Dist: fastcore>=1.9.6
20
20
  Requires-Dist: httpx
21
21
  Provides-Extra: dev
22
22
  Dynamic: author
@@ -0,0 +1,13 @@
1
+ toolslm/__init__.py,sha256=J0I0c7-a50EOnWXMryTu_E6xhXSYFBPjVpeYP_a3vRI,22
2
+ toolslm/_modidx.py,sha256=kpgsDpj-Tvn90wezrHaMttyzhNcyNVgw_dQgK10qotI,5308
3
+ toolslm/download.py,sha256=g3BxUSxylC_575M7RFSJ1GI3Co3EwPDdEeWzxaf2Czk,4451
4
+ toolslm/funccall.py,sha256=0OBrx6KzI0KK13L-5Hn69yah9oZhgTsKchmMenCoT0A,10421
5
+ toolslm/md_hier.py,sha256=r_NPezhgfxjRmSYFlu_ND42hXt1qSbaPWHTcjbviOn4,11010
6
+ toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
7
+ toolslm/xml.py,sha256=TO3i6QD1g_ya8B7Wxwib2ZWv7pwVpfyaAalw1qrKb74,7148
8
+ toolslm-0.3.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
9
+ toolslm-0.3.7.dist-info/METADATA,sha256=IaRtHzIR_YzPP-XOAcO5EdSKWChZwgpSOz5HCdgGqGc,2403
10
+ toolslm-0.3.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ toolslm-0.3.7.dist-info/entry_points.txt,sha256=xFz0Eymlo5X7BGpaO6DI9gMxvN5A7faebzrlr8ctp5I,95
12
+ toolslm-0.3.7.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
13
+ toolslm-0.3.7.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- toolslm/__init__.py,sha256=vNiWJ14r_cw5t_7UDqDQIVZvladKFGyHH2avsLpN7Vg,22
2
- toolslm/_modidx.py,sha256=-D-B5o30VGs11gBKf96lpADVXnZhdiVEshJpLzmUnDs,4378
3
- toolslm/download.py,sha256=g3BxUSxylC_575M7RFSJ1GI3Co3EwPDdEeWzxaf2Czk,4451
4
- toolslm/funccall.py,sha256=7nPfbcvDRMWiVKBKMLlCOMInoUJgDs5e38ef2T7QBHY,8485
5
- toolslm/md_hier.py,sha256=Havk9Hf0t2Xt67n_r7ZxCsS0pciR85iLcE5quShvkTg,10032
6
- toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
7
- toolslm/xml.py,sha256=D665Nk7NzyZlXyXrpnIRqfK2xQ-6Gf0bCSgocjF7zik,4061
8
- toolslm-0.3.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
9
- toolslm-0.3.2.dist-info/METADATA,sha256=5lWEv7BWTwdd5cvXgGsQXqr0j6tk8UIcGpRTlcjV3V4,2404
10
- toolslm-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- toolslm-0.3.2.dist-info/entry_points.txt,sha256=xFz0Eymlo5X7BGpaO6DI9gMxvN5A7faebzrlr8ctp5I,95
12
- toolslm-0.3.2.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
13
- toolslm-0.3.2.dist-info/RECORD,,