synapse 2.223.0__py311-none-any.whl → 2.225.0__py311-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.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (135) hide show
  1. synapse/axon.py +10 -5
  2. synapse/common.py +2 -2
  3. synapse/cortex.py +52 -3
  4. synapse/datamodel.py +1 -1
  5. synapse/lib/cell.py +1 -1
  6. synapse/lib/const.py +4 -0
  7. synapse/lib/layer.py +6 -6
  8. synapse/lib/multislabseqn.py +36 -1
  9. synapse/lib/nexus.py +67 -8
  10. synapse/lib/queue.py +4 -1
  11. synapse/lib/rstorm.py +2 -2
  12. synapse/lib/schemas.py +13 -1
  13. synapse/lib/slabseqn.py +28 -0
  14. synapse/lib/storm.py +40 -2
  15. synapse/lib/stormhttp.py +7 -1
  16. synapse/lib/stormlib/imap.py +12 -4
  17. synapse/lib/stormlib/task.py +0 -1
  18. synapse/lib/stormtypes.py +19 -1
  19. synapse/lib/version.py +2 -2
  20. synapse/models/inet.py +29 -0
  21. synapse/models/media.py +4 -0
  22. synapse/models/proj.py +3 -0
  23. synapse/models/risk.py +9 -0
  24. synapse/models/syn.py +8 -0
  25. synapse/tests/test_common.py +4 -0
  26. synapse/tests/test_cortex.py +53 -2
  27. synapse/tests/test_lib_agenda.py +1 -1
  28. synapse/tests/test_lib_cell.py +1 -1
  29. synapse/tests/test_lib_certdir.py +1 -1
  30. synapse/tests/test_lib_httpapi.py +1 -1
  31. synapse/tests/test_lib_layer.py +1 -1
  32. synapse/tests/test_lib_multislabseqn.py +22 -0
  33. synapse/tests/test_lib_nexus.py +42 -1
  34. synapse/tests/test_lib_slabseqn.py +30 -1
  35. synapse/tests/test_lib_storm.py +156 -1
  36. synapse/tests/test_lib_stormhttp.py +16 -0
  37. synapse/tests/test_lib_stormlib_imap.py +14 -0
  38. synapse/tests/test_lib_stormlib_oauth.py +1 -1
  39. synapse/tests/test_lib_stormsvc.py +1 -1
  40. synapse/tests/test_lib_stormtypes.py +12 -0
  41. synapse/tests/test_lib_trigger.py +1 -1
  42. synapse/tests/test_model_inet.py +29 -0
  43. synapse/tests/test_model_media.py +4 -1
  44. synapse/tests/test_model_proj.py +3 -1
  45. synapse/tests/test_model_risk.py +12 -0
  46. synapse/tests/test_model_syn.py +54 -2
  47. synapse/tests/{test_tools_axon2axon.py → test_tools_axon_copy.py} +4 -4
  48. synapse/tests/{test_tools_pullfile.py → test_tools_axon_get.py} +4 -4
  49. synapse/tests/{test_tools_pushfile.py → test_tools_axon_put.py} +7 -7
  50. synapse/tests/{test_tools_csvtool.py → test_tools_cortex_csv.py} +12 -3
  51. synapse/tests/{test_tools_feed.py → test_tools_cortex_feed.py} +2 -2
  52. synapse/tests/{test_tools_apikey.py → test_tools_service_apikey.py} +1 -4
  53. synapse/tests/{test_tools_backup.py → test_tools_service_backup.py} +5 -5
  54. synapse/tests/{test_tools_demote.py → test_tools_service_demote.py} +1 -1
  55. synapse/tests/{test_tools_healthcheck.py → test_tools_service_healthcheck.py} +1 -1
  56. synapse/tests/{test_tools_livebackup.py → test_tools_service_livebackup.py} +1 -1
  57. synapse/tests/{test_tools_modrole.py → test_tools_service_modrole.py} +1 -1
  58. synapse/tests/{test_tools_moduser.py → test_tools_service_moduser.py} +1 -1
  59. synapse/tests/{test_tools_promote.py → test_tools_service_promote.py} +1 -1
  60. synapse/tests/{test_tools_reload.py → test_tools_service_reload.py} +1 -1
  61. synapse/tests/{test_tools_shutdown.py → test_tools_service_shutdown.py} +1 -1
  62. synapse/tests/{test_tools_snapshot.py → test_tools_service_snapshot.py} +1 -1
  63. synapse/tests/{test_tools_storm.py → test_tools_storm_cli.py} +1 -1
  64. synapse/tests/{test_tools_pkgs_gendocs.py → test_tools_storm_pkg_doc.py} +12 -3
  65. synapse/tests/{test_tools_genpkg.py → test_tools_storm_pkg_gen.py} +1 -1
  66. synapse/tests/{test_tools_autodoc.py → test_tools_utils_autodoc.py} +1 -1
  67. synapse/tests/test_tools_utils_changelog.py +454 -0
  68. synapse/tests/{test_tools_easycert.py → test_tools_utils_easycert.py} +48 -46
  69. synapse/tests/{test_tools_guid.py → test_tools_utils_guid.py} +3 -3
  70. synapse/tests/{test_tools_json2mpk.py → test_tools_utils_json2mpk.py} +3 -3
  71. synapse/tests/{test_tools_rstorm.py → test_tools_utils_rstorm.py} +6 -1
  72. synapse/tests/utils.py +3 -1
  73. synapse/tools/apikey.py +4 -83
  74. synapse/tools/autodoc.py +3 -1031
  75. synapse/tools/axon/copy.py +44 -0
  76. synapse/tools/axon/get.py +64 -0
  77. synapse/tools/axon/put.py +122 -0
  78. synapse/tools/axon2axon.py +3 -36
  79. synapse/tools/backup.py +6 -176
  80. synapse/tools/changelog.py +3 -1098
  81. synapse/tools/cortex/csv.py +236 -0
  82. synapse/tools/cortex/feed.py +151 -0
  83. synapse/tools/csvtool.py +3 -227
  84. synapse/tools/demote.py +4 -40
  85. synapse/tools/docker/validate.py +3 -3
  86. synapse/tools/easycert.py +4 -129
  87. synapse/tools/feed.py +3 -140
  88. synapse/tools/genpkg.py +3 -307
  89. synapse/tools/guid.py +7 -6
  90. synapse/tools/healthcheck.py +3 -101
  91. synapse/tools/json2mpk.py +6 -38
  92. synapse/tools/livebackup.py +4 -27
  93. synapse/tools/modrole.py +3 -108
  94. synapse/tools/moduser.py +3 -179
  95. synapse/tools/pkgs/gendocs.py +3 -164
  96. synapse/tools/promote.py +4 -41
  97. synapse/tools/pullfile.py +3 -56
  98. synapse/tools/pushfile.py +3 -114
  99. synapse/tools/reload.py +4 -61
  100. synapse/tools/rstorm.py +3 -26
  101. synapse/tools/service/__init__.py +0 -0
  102. synapse/tools/service/apikey.py +90 -0
  103. synapse/tools/service/backup.py +181 -0
  104. synapse/tools/service/demote.py +47 -0
  105. synapse/tools/service/healthcheck.py +109 -0
  106. synapse/tools/service/livebackup.py +34 -0
  107. synapse/tools/service/modrole.py +116 -0
  108. synapse/tools/service/moduser.py +184 -0
  109. synapse/tools/service/promote.py +48 -0
  110. synapse/tools/service/reload.py +68 -0
  111. synapse/tools/service/shutdown.py +51 -0
  112. synapse/tools/service/snapshot.py +64 -0
  113. synapse/tools/shutdown.py +5 -45
  114. synapse/tools/snapshot.py +4 -57
  115. synapse/tools/storm/__init__.py +0 -0
  116. synapse/tools/storm/__main__.py +5 -0
  117. synapse/tools/{storm.py → storm/_cli.py} +0 -3
  118. synapse/tools/storm/pkg/__init__.py +0 -0
  119. synapse/tools/{pkgs/pandoc_filter.py → storm/pkg/_pandoc_filter.py} +1 -1
  120. synapse/tools/storm/pkg/doc.py +176 -0
  121. synapse/tools/storm/pkg/gen.py +315 -0
  122. synapse/tools/utils/__init__.py +0 -0
  123. synapse/tools/utils/autodoc.py +1040 -0
  124. synapse/tools/utils/changelog.py +1124 -0
  125. synapse/tools/utils/easycert.py +136 -0
  126. synapse/tools/utils/guid.py +11 -0
  127. synapse/tools/utils/json2mpk.py +46 -0
  128. synapse/tools/utils/rstorm.py +35 -0
  129. {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/METADATA +1 -1
  130. {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/RECORD +134 -105
  131. synapse/tests/test_tools_changelog.py +0 -196
  132. /synapse/tests/{test_tools_axon.py → test_tools_axon_dump_load.py} +0 -0
  133. {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/WHEEL +0 -0
  134. {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/licenses/LICENSE +0 -0
  135. {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/top_level.txt +0 -0
synapse/tools/autodoc.py CHANGED
@@ -1,1039 +1,11 @@
1
- import copy
2
- import logging
3
- import collections
4
-
5
- from typing import List, Tuple, Dict, Union
6
-
7
- import synapse.exc as s_exc
8
1
  import synapse.common as s_common
9
- import synapse.cortex as s_cortex
10
- import synapse.telepath as s_telepath
11
2
 
12
3
  import synapse.lib.cmd as s_cmd
13
- import synapse.lib.json as s_json
14
- import synapse.lib.storm as s_storm
15
- import synapse.lib.config as s_config
16
- import synapse.lib.output as s_output
17
- import synapse.lib.autodoc as s_autodoc
18
- import synapse.lib.dyndeps as s_dyndeps
19
- import synapse.lib.version as s_version
20
- import synapse.lib.stormsvc as s_stormsvc
21
- import synapse.lib.stormtypes as s_stormtypes
22
-
23
- import synapse.tools.genpkg as s_genpkg
24
-
25
- logger = logging.getLogger(__name__)
26
-
27
- # src / name / target
28
- EdgeDef = Tuple[Union[str, None], str, Union[str, None]]
29
- EdgeDict = Dict[str, str]
30
- Edge = Tuple[EdgeDef, EdgeDict]
31
- Edges = List[Edge]
32
-
33
- poptsToWords = {
34
- 'ex': 'Example',
35
- 'ro': 'Read Only',
36
- 'deprecated': 'Deprecated',
37
- 'disp': 'Display',
38
- }
39
-
40
- info_ignores = (
41
- 'stortype',
42
- 'bases',
43
- 'custom',
44
- 'template',
45
- 'display',
46
- 'deprecated',
47
- )
48
-
49
- raw_back_slash_colon = r'\:'
50
-
51
-
52
- class DocHelp:
53
- '''
54
- Helper to pre-compute all doc strings hierarchically
55
- '''
56
-
57
- def __init__(self, ctors, types, forms, props, univs):
58
- self.ctors = {c[0]: c[3].get('doc', 'BaseType has no doc string.') for c in ctors}
59
- self.types = {t[0]: t[2].get('doc', self.ctors.get(t[1][0])) for t in types}
60
- self.forms = {f[0]: f[1].get('doc', self.types.get(f[0], self.ctors.get(f[0]))) for f in forms}
61
- self.univs = {}
62
- for unam, utyp, unfo in univs:
63
- tn = utyp[0]
64
- doc = unfo.get('doc', self.forms.get(tn, self.types.get(tn, self.ctors.get(tn))))
65
- self.univs[unam] = doc
66
- self.props = {}
67
- for form, props in props.items():
68
- for prop in props:
69
- tn = prop[1][0]
70
- doc = prop[2].get('doc', self.forms.get(tn, self.types.get(tn, self.ctors.get(tn))))
71
- self.props[(form, prop[0])] = doc
72
- typed = {t[0]: t for t in types}
73
- ctord = {c[0]: c for c in ctors}
74
- self.formhelp = {} # form name -> ex string for a given type
75
- for form in forms:
76
- formname = form[0]
77
- tnfo = typed.get(formname)
78
- ctor = ctord.get(formname)
79
- if tnfo:
80
- tnfo = tnfo[2]
81
- example = tnfo.get('ex')
82
- self.formhelp[formname] = example
83
- elif ctor:
84
- ctor = ctor[3]
85
- example = ctor.get('ex')
86
- self.formhelp[formname] = example
87
- else: # pragma: no cover
88
- logger.warning(f'No ctor/type available for [{formname}]')
89
-
90
-
91
- def processCtors(rst, dochelp, ctors):
92
- '''
93
-
94
- Args:
95
- rst (RstHelp):
96
- dochelp (DocHelp):
97
- ctors (list):
98
-
99
- Returns:
100
- None
101
- '''
102
- rst.addHead('Base Types', lvl=1, link='.. _dm-base-types:')
103
- rst.addLines('',
104
- 'Base types are defined via Python classes.',
105
- '')
106
-
107
- for name, ctor, opts, info in ctors:
108
-
109
- doc = dochelp.ctors.get(name)
110
- if not doc.endswith('.'):
111
- logger.warning(f'Docstring for ctor {name} does not end with a period.]')
112
- doc = doc + '.'
113
-
114
- # Break implicit links to nowhere
115
- hname = name
116
- if ':' in name:
117
- hname = name.replace(':', raw_back_slash_colon)
118
-
119
- link = f'.. _dm-type-{name.replace(":", "-")}:'
120
- rst.addHead(hname, lvl=2, link=link)
121
-
122
- rst.addLines(doc, f'It is implemented by the following class{raw_back_slash_colon} ``{ctor}``.')
123
- _ = info.pop('doc', None)
124
- ex = info.pop('ex', None)
125
- if ex:
126
- rst.addLines('',
127
- f'An example of ``{name}``{raw_back_slash_colon}',
128
- '',
129
- f' * ``{ex}``',
130
- )
131
-
132
- if opts:
133
- rst.addLines('',
134
- f'The base type ``{name}`` has the following default options set:',
135
- ''
136
- )
137
- for k, v in opts.items():
138
- rst.addLines(f' * {k}: ``{v}``')
139
-
140
- for key in info_ignores:
141
- info.pop(key, None)
142
-
143
- if info:
144
- logger.warning(f'Base type {name} has unhandled info: {info}')
145
-
146
- def processTypes(rst, dochelp, types):
147
- '''
148
-
149
- Args:
150
- rst (RstHelp):
151
- dochelp (DocHelp):
152
- ctors (list):
153
-
154
- Returns:
155
- None
156
- '''
157
- rst.addHead('Types', lvl=1, link='.. _dm-types:')
158
-
159
- rst.addLines('',
160
- 'Regular types are derived from BaseTypes.',
161
- '')
162
-
163
- for name, (ttyp, topt), info in types:
164
-
165
- doc = dochelp.types.get(name)
166
- if not doc.endswith('.'):
167
- logger.warning(f'Docstring for type {name} does not end with a period.]')
168
- doc = doc + '.'
169
-
170
- # Break implicit links to nowhere
171
- hname = name
172
- if ':' in name:
173
- hname = name.replace(':', raw_back_slash_colon)
174
-
175
- link = f'.. _dm-type-{name.replace(":", "-")}:'
176
- rst.addHead(hname, lvl=2, link=link)
177
-
178
- rst.addLines(doc,
179
- f'The ``{name}`` type is derived from the base type: ``{ttyp}``.')
180
-
181
- ifaces = info.pop('interfaces', None)
182
- if ifaces:
183
- rst.addLines('', 'This type implements the following interfaces:', '')
184
- for iface in ifaces:
185
- rst.addLines(f' * ``{iface}``')
186
-
187
- _ = info.pop('doc', None)
188
- ex = info.pop('ex', None)
189
- if ex:
190
- rst.addLines('',
191
- f'An example of ``{name}``{raw_back_slash_colon}',
192
- '',
193
- f' * ``{ex}``',
194
- )
195
-
196
- if topt:
197
- rst.addLines('',
198
- f'This type has the following options set:',
199
- ''
200
- )
201
-
202
- for key, valu in sorted(topt.items(), key=lambda x: x[0]):
203
- if key == 'enums':
204
- if valu is None:
205
- continue
206
- lines = [f' * {key}:\n']
207
- elines = []
208
- if isinstance(valu, str):
209
- # handle str
210
- enums = valu.split(',')
211
- header = 'valu'
212
- maxa = max((len(enum) for enum in enums))
213
- maxa = max(maxa, len(header))
214
-
215
- seprline = f'+{"-" * maxa}+'
216
- elines.append(seprline)
217
- line = f'+{header}{" " * (maxa - len(header))}+'
218
- elines.append(line)
219
- line = f'+{"=" * maxa}+'
220
- elines.append(line)
221
- for enum in enums:
222
- line = f'+{enum}{" " * (maxa - len(enum))}+'
223
- elines.append(line)
224
- elines.append(seprline)
225
-
226
- elif isinstance(valu, (list, tuple)):
227
- # handle enum list
228
- valu = sorted(valu, key=lambda x: x[0])
229
-
230
- maxa, maxb = len('int'), len('valu')
231
- for (a, b) in valu:
232
- maxa = max(len(str(a)), maxa)
233
- maxb = max(len(b), maxb)
234
- line = f'{"=" * maxa} {"=" * maxb}'
235
- elines.append(line)
236
- line = f'int{" " * (maxa - 3)} valu{" " * (maxb - 4)}'
237
- elines.append(line)
238
- line = f'{"=" * maxa} {"=" * maxb}'
239
- elines.append(line)
240
-
241
- for (a, b) in valu:
242
- line = f'{a}{" " * (maxa - len(str(a)))} {b}{" " * (maxb - len(b))}'
243
- elines.append(line)
244
-
245
- line = f'{"=" * maxa} {"=" * maxb}'
246
- elines.append(line)
247
-
248
- else: # pragma: no cover
249
- raise ValueError(f'Unknown enum type {type(valu)} for {name}')
250
-
251
- elines = [' ' + line for line in elines]
252
- lines.extend(elines)
253
- lines.append('\n')
254
- rst.addLines(*lines)
255
-
256
- elif key in ('fields',
257
- 'schema',
258
- ):
259
- if len(str(valu)) < 80:
260
- rst.addLines(f' * {key}: ``{valu}``')
261
- continue
262
- lines = [f' * {key}:\n', ' ::\n\n']
263
- json_lines = s_json.dumps(valu, indent=True, sort_keys=True).decode()
264
- json_lines = [' ' + line for line in json_lines.split('\n')]
265
- lines.extend(json_lines)
266
- lines.append('\n')
267
- rst.addLines(*lines)
268
- else:
269
- rst.addLines(f' * {key}: ``{valu}``')
270
-
271
- for key in info_ignores:
272
- info.pop(key, None)
273
-
274
- if info:
275
- logger.warning(f'Type {name} has unhandled info: {info}')
276
-
277
- def has_popts_data(props):
278
- # Props contain "doc" which we pop out
279
- # Check if a list of props has any keys
280
- # which are not 'doc'
281
- for _, _, popts in props:
282
- keys = set(popts.keys())
283
- if 'doc' in keys:
284
- keys.remove('doc')
285
- if keys:
286
- return True
287
-
288
- return False
289
-
290
- def processFormsProps(rst, dochelp, forms, univ_names, alledges):
291
- rst.addHead('Forms', lvl=1, link='.. _dm-forms:')
292
- rst.addLines('',
293
- 'Forms are derived from types, or base types. Forms represent node types in the graph.'
294
- '')
295
-
296
- for name, info, props in forms:
297
-
298
- formedges = lookupedgesforform(name, alledges)
299
-
300
- doc = dochelp.forms.get(name)
301
- if not doc.endswith('.'):
302
- logger.warning(f'Docstring for form {name} does not end with a period.]')
303
- doc = doc + '.'
304
-
305
- hname = name
306
- if ':' in name:
307
- hname = name.replace(':', raw_back_slash_colon)
308
- link = f'.. _dm-form-{name.replace(":", "-")}:'
309
- rst.addHead(hname, lvl=2, link=link)
310
-
311
- baseline = f'The base type for the form can be found at :ref:`dm-type-{name.replace(":", "-")}`.'
312
- rst.addLines(doc,
313
- '',
314
- baseline,
315
- '')
316
-
317
- ex = dochelp.formhelp.get(name)
318
- if ex:
319
- rst.addLines('',
320
- f'An example of ``{name}``{raw_back_slash_colon}',
321
- '',
322
- f' * ``{ex}``',
323
- ''
324
- )
325
-
326
- props = [blob for blob in props if blob[0] not in univ_names]
327
-
328
- if props:
329
-
330
- has_popts = has_popts_data(props)
331
-
332
- rst.addLines('', '', f' Properties:', )
333
- rst.addLines(' .. list-table::',
334
- ' :header-rows: 1',
335
- ' :widths: auto',
336
- ' :class: tight-table',
337
- '')
338
- header = (' * - name',
339
- ' - type',
340
- ' - doc',
341
- )
342
- if has_popts:
343
- header = header + (' - opts',)
344
- rst.addLines(*header)
345
-
346
- for pname, (ptname, ptopts), popts in props:
347
-
348
- _ = popts.pop('doc', None)
349
- doc = dochelp.props.get((name, pname))
350
- if not doc.endswith('.'):
351
- logger.warning(f'Docstring for prop ({name}, {pname}) does not end with a period.]')
352
- doc = doc + '.'
353
-
354
- hptlink = f'dm-type-{ptname.replace(":", "-")}'
355
-
356
- rst.addLines(f' * - ``:{pname}``',)
357
- if ptopts:
358
-
359
- rst.addLines(f' - | :ref:`{hptlink}`', )
360
- for k, v in ptopts.items():
361
- if ptname == 'array' and k == 'type':
362
- tlink = f'dm-type-{v.replace(":", "-")}'
363
- rst.addLines(f' | {k}: :ref:`{tlink}`', )
364
- else:
365
- rst.addLines(f' | {k}: ``{v}``', )
366
-
367
- else:
368
- rst.addLines(f' - :ref:`{hptlink}`',)
369
-
370
- rst.addLines(f' - {doc}',)
371
-
372
- if has_popts:
373
- if popts:
374
- if len(popts) == 1:
375
- for k, v in popts.items():
376
- k = poptsToWords.get(k, k.replace(':', raw_back_slash_colon))
377
- rst.addLines(f' - {k}: ``{v}``')
378
- else:
379
- for i, (k, v) in enumerate(popts.items()):
380
- k = poptsToWords.get(k, k.replace(':', raw_back_slash_colon))
381
- if i == 0:
382
- rst.addLines(f' - | {k}: ``{v}``')
383
- else:
384
- rst.addLines(f' | {k}: ``{v}``')
385
- else:
386
- rst.addLines(f' - ')
387
-
388
- if formedges:
389
-
390
- source_edges = formedges.pop('source', None)
391
- dst_edges = formedges.pop('target', None)
392
- generic_edges = formedges.pop('generic', None)
393
-
394
- if source_edges:
395
- if generic_edges:
396
- source_edges.extend(generic_edges)
397
-
398
- rst.addLines(f' Source Edges:',)
399
- rst.addLines(' .. list-table::',
400
- ' :header-rows: 1',
401
- ' :widths: auto',
402
- ' :class: tight-table',
403
- '',
404
- ' * - source',
405
- ' - verb',
406
- ' - target',
407
- ' - doc',
408
- )
409
-
410
- _edges = []
411
- for (edef, enfo) in source_edges:
412
- src, enam, dst = edef
413
- doc = enfo.pop('doc', None)
414
-
415
- if src is None:
416
- src = '*'
417
- if dst is None:
418
- dst = '*'
419
-
420
- for key in info_ignores:
421
- enfo.pop(key, None)
422
-
423
- if enfo:
424
- logger.warning(f'{name} => Light edge {enam} has unhandled info: {enfo}')
425
- _edges.append((src, enam, dst, doc))
426
- _edges.sort(key=lambda x: x[:2])
427
-
428
- for src, enam, dst, doc in _edges:
429
- rst.addLines(f' * - ``{src}``',
430
- f' - ``-({enam})>``',
431
- f' - ``{dst}``',
432
- f' - {doc}',
433
- )
434
-
435
- if dst_edges:
436
- if generic_edges:
437
- dst_edges.extend(generic_edges)
438
-
439
- rst.addLines(f' Target Edges:', )
440
- rst.addLines(' .. list-table::',
441
- ' :header-rows: 1',
442
- ' :widths: auto',
443
- ' :class: tight-table',
444
- '',
445
- ' * - source',
446
- ' - verb',
447
- ' - target',
448
- ' - doc',
449
- )
450
-
451
- _edges = []
452
- for (edef, enfo) in dst_edges:
453
- src, enam, dst = edef
454
- doc = enfo.pop('doc', None)
455
- if src is None:
456
- src = '*'
457
- if dst is None:
458
- dst = '*'
459
-
460
- for key in info_ignores:
461
- enfo.pop(key, None)
462
-
463
- if enfo:
464
- logger.warning(f'{name} => Light edge {enam} has unhandled info: {enfo}')
465
-
466
- _edges.append((src, enam, dst, doc))
467
- _edges.sort(key=lambda x: x[:2])
468
-
469
- for src, enam, dst, doc in _edges:
470
- rst.addLines(f' * - ``{src}``',
471
- f' - ``-({enam})>``',
472
- f' - ``{dst}``',
473
- f' - {doc}',
474
- )
475
-
476
- rst.addLines('', '')
477
-
478
- if formedges:
479
- logger.warning(f'{name} has unhandled light edges: {formedges}')
480
-
481
- def processUnivs(rst, dochelp, univs):
482
- rst.addHead('Universal Properties', lvl=1, link='.. _dm-universal-props:')
483
-
484
- rst.addLines('',
485
- 'Universal props are system level properties which may be present on every node.',
486
- '',
487
- 'These properties are not specific to a particular form and exist outside of a particular',
488
- 'namespace.',
489
- '')
490
-
491
- for name, (utyp, uopt), info in univs:
492
-
493
- _ = info.pop('doc', None)
494
- doc = dochelp.univs.get(name)
495
- if not doc.endswith('.'):
496
- logger.warning(f'Docstring for form {name} does not end with a period.]')
497
- doc = doc + '.'
498
-
499
- hname = name
500
- if ':' in name:
501
- hname = name.replace(':', raw_back_slash_colon)
502
-
503
- rst.addHead(hname, lvl=2, link=f'.. _dm-univ-{name.replace(":", "-")}:')
504
-
505
- rst.addLines('',
506
- doc,
507
- )
508
-
509
- if info:
510
- rst.addLines('It has the following property options set:',
511
- ''
512
- )
513
- for k, v in info.items():
514
- k = poptsToWords.get(k, k.replace(':', raw_back_slash_colon))
515
- rst.addLines(' ' + f'* {k}: ``{v}``')
516
-
517
- hptlink = f'dm-type-{utyp.replace(":", "-")}'
518
- tdoc = f'The universal property type is :ref:`{hptlink}`.'
519
-
520
- rst.addLines('',
521
- tdoc,
522
- )
523
- if uopt:
524
- rst.addLines("Its type has the following options set:",
525
- '')
526
- for k, v in uopt.items():
527
- rst.addLines(' ' + f'* {k}: ``{v}``')
528
-
529
- async def processStormCmds(rst, pkgname, commands):
530
- '''
531
-
532
- Args:
533
- rst (RstHelp):
534
- pkgname (str):
535
- commands (list):
536
-
537
- Returns:
538
- None
539
- '''
540
- rst.addHead('Storm Commands', lvl=2)
541
-
542
- rst.addLines(f'This package implements the following Storm Commands.\n')
543
-
544
- commands = sorted(commands, key=lambda x: x.get('name'))
545
-
546
- for cdef in commands:
547
-
548
- cname = cdef.get('name')
549
- cdesc = cdef.get('descr')
550
- cargs = cdef.get('cmdargs')
551
-
552
- # command names cannot have colons in them thankfully
553
- cref = f'.. _stormcmd-{pkgname.replace(":", "-")}-{cname.replace(".", "-")}:'
554
- rst.addHead(cname, lvl=3, link=cref)
555
-
556
- # Form the description
557
- lines = ['::\n']
558
-
559
- # Generate help from args
560
- pars = s_storm.Parser(prog=cname, descr=cdesc)
561
- if cargs:
562
- for (argname, arginfo) in cargs:
563
- pars.add_argument(argname, **arginfo)
564
- pars.help()
565
-
566
- for line in pars.mesgs:
567
- if '\n' in line:
568
- for subl in line.split('\n'):
569
- lines.append(f' {subl}')
570
- else:
571
- lines.append(f' {line}')
572
-
573
- lines.append('\n')
574
-
575
- forms = cdef.get('forms', {})
576
- iforms = forms.get('input')
577
- oforms = forms.get('output')
578
- nodedata = forms.get('nodedata')
579
-
580
- if iforms:
581
- line = 'The command is aware of how to automatically handle the following forms as input nodes:\n'
582
- lines.append(line)
583
- for form in iforms:
584
- lines.append(f'- ``{form}``')
585
- lines.append('\n')
586
-
587
- if oforms:
588
- line = 'The command may make the following types of nodes in the graph as a result of its execution:\n'
589
- lines.append(line)
590
- for form in oforms:
591
- lines.append(f'- ``{form}``')
592
- lines.append('\n')
593
-
594
- if nodedata:
595
- line = 'The command may add nodedata with the following keys to the corresponding forms:\n'
596
- lines.append(line)
597
- for key, form in nodedata:
598
- lines.append(f'- ``{key}`` on ``{form}``')
599
- lines.append('\n')
600
-
601
- rst.addLines(*lines)
602
-
603
- async def processStormModules(rst, pkgname, modules):
604
-
605
- rst.addHead('Storm Modules', lvl=2)
606
-
607
- hasapi = False
608
- modules = sorted(modules, key=lambda x: x.get('name'))
609
-
610
- for mdef in modules:
611
-
612
- apidefs = mdef.get('apidefs')
613
- if not apidefs:
614
- continue
615
-
616
- if not hasapi:
617
- rst.addLines('This package implements the following Storm Modules.\n')
618
- hasapi = True
619
-
620
- mname = mdef['name']
621
-
622
- mref = f'.. _stormmod-{pkgname.replace(":", "-")}-{mname.replace(".", "-")}:'
623
- rst.addHead(mname, lvl=3, link=mref)
624
-
625
- for apidef in apidefs:
626
-
627
- apiname = apidef['name']
628
- apidesc = apidef['desc']
629
- apitype = apidef['type']
630
-
631
- callsig = s_autodoc.genCallsig(apitype)
632
- rst.addHead(f'{apiname}{callsig}', lvl=4)
633
- if depr := apidef.get('deprecated'):
634
- rst.addLines(*s_autodoc.genDeprecationWarning(apiname, depr, True))
635
- rst.addLines(*s_autodoc.prepareRstLines(apidesc))
636
- rst.addLines(*s_autodoc.getArgLines(apitype))
637
- rst.addLines(*s_autodoc.getReturnLines(apitype))
638
-
639
- if not hasapi:
640
- rst.addLines('This package does not export any Storm APIs.\n')
641
-
642
- def lookupedgesforform(form: str, edges: Edges) -> Dict[str, Edges]:
643
- ret = collections.defaultdict(list)
644
-
645
- for edge in edges:
646
- src, name, dst = edge[0]
647
-
648
- # src and dst may be None, form==name, or form!=name.
649
- # This gives us 9 possible states to consider.
650
- # src | dst | -> ret
651
- # ===================================
652
- # none | none | -> generic
653
- # none | != | -> source
654
- # none | = | -> target
655
- # != | none | -> target
656
- # = | none | -> source
657
- # != | = | -> target
658
- # = | != | -> source
659
- # != | != | -> no-op
660
- # = | = | -> source, target
661
-
662
- if src is None and dst is None:
663
- ret['generic'].append(edge)
664
- continue
665
- if src is None and dst != form:
666
- ret['source'].append(edge)
667
- continue
668
- if src is None and dst == form:
669
- ret['target'].append(edge)
670
- continue
671
- if src != form and dst is None:
672
- ret['target'].append(edge)
673
- continue
674
- if src == form and dst is None:
675
- ret['source'].append(edge)
676
- continue
677
- if src != form and dst == form:
678
- ret['target'].append(edge)
679
- continue
680
- if src == form and dst != form:
681
- ret['source'].append(edge)
682
- continue
683
- if src != form and dst != form:
684
- # no-op
685
- continue
686
- if src == form and dst == form:
687
- ret['source'].append(edge)
688
- ret['target'].append(edge)
689
-
690
- return copy.deepcopy(dict(ret))
691
-
692
- async def docModel(outp,
693
- core):
694
- coreinfo = await core.getCoreInfo()
695
- _, model = coreinfo.get('modeldef')[0]
696
-
697
- ctors = model.get('ctors')
698
- types = model.get('types')
699
- forms = model.get('forms')
700
- univs = model.get('univs')
701
- edges = model.get('edges')
702
- props = collections.defaultdict(list)
703
-
704
- ctors = sorted(ctors, key=lambda x: x[0])
705
- univs = sorted(univs, key=lambda x: x[0])
706
- types = sorted(types, key=lambda x: x[0])
707
- forms = sorted(forms, key=lambda x: x[0])
708
- univ_names = {univ[0] for univ in univs}
709
-
710
- for fname, fnfo, fprops in forms:
711
- for prop in fprops:
712
- props[fname].append(prop)
713
-
714
- [v.sort() for k, v in props.items()]
715
-
716
- dochelp = DocHelp(ctors, types, forms, props, univs)
717
-
718
- # Validate examples
719
- for form, example in dochelp.formhelp.items():
720
- if example is None:
721
- continue
722
- if example.startswith('('):
723
- q = f"[{form}={example}]"
724
- else:
725
- q = f"[{form}='{example}']"
726
- node = False
727
- async for (mtyp, mnfo) in core.storm(q, {'editformat': 'none'}):
728
- if mtyp in ('init', 'fini'):
729
- continue
730
- if mtyp == 'err': # pragma: no cover
731
- raise s_exc.SynErr(mesg='Invalid example', form=form, example=example, info=mnfo)
732
- if mtyp == 'node':
733
- node = True
734
- if not node: # pragma: no cover
735
- raise s_exc.SynErr(mesg='Unable to make a node from example.', form=form, example=example)
736
-
737
- rst = s_autodoc.RstHelp()
738
- rst.addHead('Synapse Data Model - Types', lvl=0)
739
-
740
- processCtors(rst, dochelp, ctors)
741
- processTypes(rst, dochelp, types)
742
-
743
- rst2 = s_autodoc.RstHelp()
744
- rst2.addHead('Synapse Data Model - Forms', lvl=0)
745
-
746
- processFormsProps(rst2, dochelp, forms, univ_names, edges)
747
- processUnivs(rst2, dochelp, univs)
748
-
749
- return rst, rst2
750
-
751
- async def docConfdefs(ctor):
752
- cls = s_dyndeps.tryDynLocal(ctor)
753
-
754
- if not hasattr(cls, 'confdefs'):
755
- raise Exception('ctor must have a confdefs attr')
756
-
757
- rst = s_autodoc.RstHelp()
758
-
759
- clsname = cls.__name__
760
-
761
- conf = cls.initCellConf() # type: s_config.Config
762
-
763
- # access raw config data
764
-
765
- # Get envar and argparse mapping
766
- name2envar = conf.getEnvarMapping()
767
-
768
- schema = conf.json_schema.get('properties', {})
769
-
770
- for name, conf in sorted(schema.items(), key=lambda x: x[0]):
771
-
772
- if conf.get('hideconf'):
773
- continue
774
-
775
- if conf.get('hidedocs'):
776
- continue
777
-
778
- nodesc = f'No description available for ``{name}``.'
779
-
780
- desc = conf.get('description', nodesc)
781
- if not desc.endswith('.'): # pragma: no cover
782
- logger.warning(f'Description for [{name}] is missing a period.')
783
-
784
- hname = name.replace(':', raw_back_slash_colon)
785
- lines = []
786
- lines.append(hname)
787
- lines.append('~' * len(hname))
788
- lines.append('')
789
- lines.append(desc)
790
-
791
- extended_description = conf.get('extended_description')
792
- if extended_description:
793
- lines.append('\n')
794
- lines.append(extended_description)
795
-
796
- # Type/additional information
797
-
798
- lines.append('\n')
799
-
800
- ctyp = conf.get('type')
801
- lines.append('Type')
802
- lines.append(f' ``{ctyp}``\n')
803
-
804
- if ctyp == 'object':
805
- if conf.get('properties'):
806
- lines.append('Properties')
807
- lines.append(' The object expects the following properties:')
808
- data = {k: v for k, v in conf.items() if k not in (
809
- 'description', 'default', 'type', 'hideconf', 'hidecmdl',
810
- )}
811
- parts = s_json.dumps(data, sort_keys=True, indent=True).decode().split('\n')
812
- lines.append(' ::')
813
- lines.append('\n')
814
- lines.extend([f' {p}' for p in parts])
815
- lines.append('\n')
816
-
817
- defval = conf.get('default', s_common.novalu)
818
- if defval is not s_common.novalu:
819
- lines.append('Default Value')
820
- lines.append(f' ``{repr(defval)}``\n')
821
-
822
- envar = name2envar.get(name)
823
- if envar:
824
- lines.append('Environment Variable')
825
- lines.append(f' ``{envar}``\n')
826
-
827
- rst.addLines(*lines)
828
-
829
- return rst, clsname
830
-
831
- async def docStormsvc(ctor):
832
- cls = s_dyndeps.tryDynLocal(ctor)
833
-
834
- if not hasattr(cls, 'cellapi'):
835
- raise Exception('ctor must have a cellapi attr')
836
-
837
- clsname = cls.__name__
838
-
839
- cellapi = cls.cellapi
840
-
841
- if not issubclass(cellapi, s_stormsvc.StormSvc):
842
- raise Exception('cellapi must be a StormSvc implementation')
843
-
844
- # Make a dummy object
845
- class MockSess:
846
- def __init__(self):
847
- self.user = None
848
-
849
- class DummyLink:
850
- def __init__(self):
851
- self.info = {'sess': MockSess()}
852
-
853
- def get(self, key):
854
- return self.info.get(key)
855
-
856
- async with await cellapi.anit(s_common.novalu, DummyLink(), s_common.novalu) as obj:
857
- svcinfo = await obj.getStormSvcInfo()
858
-
859
- rst = s_autodoc.RstHelp()
860
-
861
- # Disable default python highlighting
862
- rst.addLines('.. highlight:: none\n')
863
-
864
- rst.addHead(f'{clsname} Storm Service')
865
- lines = ['The following Storm Packages and Commands are available from this service.',
866
- f'This documentation is generated for version '
867
- f'{s_version.fmtVersion(*svcinfo.get("vers"))} of the service.',
868
- f'The Storm Service name is ``{svcinfo.get("name")}``.',
869
- ]
870
- rst.addLines(*lines)
871
-
872
- for pkg in svcinfo.get('pkgs'):
873
- pname = pkg.get('name')
874
- pver = pkg.get('version')
875
- commands = pkg.get('commands')
876
-
877
- hname = pname
878
- if ':' in pname:
879
- hname = pname.replace(':', raw_back_slash_colon)
880
-
881
- rst.addHead(f'Storm Package\\: {hname}', lvl=1)
882
-
883
- rst.addLines(f'This documentation for {pname} is generated for version {s_version.fmtVersion(*pver)}')
884
-
885
- if commands:
886
- await processStormCmds(rst, pname, commands)
887
-
888
- if modules := pkg.get('modules'):
889
- await processStormModules(rst, pname, modules)
890
-
891
- return rst, clsname
892
-
893
- async def docStormpkg(pkgpath):
894
- pkgdef = s_genpkg.loadPkgProto(pkgpath)
895
- pkgname = pkgdef.get('name')
896
-
897
- rst = s_autodoc.RstHelp()
898
-
899
- # Disable default python highlighting
900
- rst.addLines('.. highlight:: none\n')
901
-
902
- hname = pkgname
903
- if ':' in pkgname:
904
- hname = pkgname.replace(':', raw_back_slash_colon)
905
-
906
- rst.addHead(f'Storm Package\\: {hname}')
907
- lines = ['The following Commands are available from this package.',
908
- f'This documentation is generated for version '
909
- f'{s_version.fmtVersion(pkgdef.get("version"))} of the package.',
910
- ]
911
- rst.addLines(*lines)
912
-
913
- commands = pkgdef.get('commands')
914
- if commands:
915
- await processStormCmds(rst, pkgname, commands)
916
-
917
- if modules := pkgdef.get('modules'):
918
- await processStormModules(rst, pkgname, modules)
919
-
920
- return rst, pkgname
921
-
922
- async def docStormTypes():
923
- registry = s_stormtypes.registry
924
-
925
- libsinfo = registry.getLibDocs()
926
-
927
- libspage = s_autodoc.RstHelp()
928
-
929
- libspage.addHead('Storm Libraries', lvl=0, link='.. _stormtypes-libs-header:')
930
-
931
- lines = (
932
- '',
933
- 'Storm Libraries represent powerful tools available inside of the Storm query language.',
934
- ''
935
- )
936
- libspage.addLines(*lines)
937
-
938
- # This value is appended to the end of the ref to the first level header of a type.
939
- # This prevents accidental cross linking between parts of the docs; which can happen
940
- # when secondary properties of a type may overlap with the main name of the type.
941
- types_suffix = 'f527'
942
-
943
- s_autodoc.docStormTypes(libspage, libsinfo, linkprefix='stormlibs', islib=True,
944
- known_types=registry.known_types, types_prefix='stormprims', types_suffix=types_suffix)
945
-
946
- priminfo = registry.getTypeDocs()
947
- typespage = s_autodoc.RstHelp()
948
-
949
- typespage.addHead('Storm Types', lvl=0, link='.. _stormtypes-prim-header:')
950
-
951
- lines = (
952
- '',
953
- 'Storm Objects are used as view objects for manipulating data in the Storm Runtime and in the Cortex itself.'
954
- ''
955
- )
956
- typespage.addLines(*lines)
957
- s_autodoc.docStormTypes(typespage, priminfo, linkprefix='stormprims', known_types=registry.known_types,
958
- types_prefix='stormprims', types_suffix=types_suffix)
959
-
960
- return libspage, typespage
961
-
962
- async def main(argv, outp=s_output.stdout):
963
- pars = getArgParser(outp)
964
- opts = pars.parse_args(argv)
965
-
966
- if opts.doc_model:
967
-
968
- if opts.cortex:
969
- async with s_telepath.withTeleEnv():
970
- async with await s_telepath.openurl(opts.cortex) as core:
971
- rsttypes, rstforms = await docModel(outp, core)
972
-
973
- else:
974
- async with s_cortex.getTempCortex() as core:
975
- rsttypes, rstforms = await docModel(outp, core)
976
-
977
- if opts.savedir:
978
- with open(s_common.genpath(opts.savedir, 'datamodel_types.rst'), 'wb') as fd:
979
- fd.write(rsttypes.getRstText().encode())
980
- with open(s_common.genpath(opts.savedir, 'datamodel_forms.rst'), 'wb') as fd:
981
- fd.write(rstforms.getRstText().encode())
982
-
983
- if opts.doc_conf:
984
- confdocs, cname = await docConfdefs(opts.doc_conf)
985
-
986
- if opts.savedir:
987
- with open(s_common.genpath(opts.savedir, f'conf_{cname.lower()}.rst'), 'wb') as fd:
988
- fd.write(confdocs.getRstText().encode())
989
-
990
- if opts.doc_storm:
991
- confdocs, svcname = await docStormsvc(opts.doc_storm)
992
-
993
- if opts.savedir:
994
- with open(s_common.genpath(opts.savedir, f'stormsvc_{svcname.lower()}.rst'), 'wb') as fd:
995
- fd.write(confdocs.getRstText().encode())
996
-
997
- if opts.doc_stormpkg:
998
- pkgdocs, pkgname = await docStormpkg(opts.doc_stormpkg)
999
-
1000
- if opts.savedir:
1001
- with open(s_common.genpath(opts.savedir, f'stormpkg_{pkgname.lower()}.rst'), 'wb') as fd:
1002
- fd.write(pkgdocs.getRstText().encode())
1003
-
1004
- if opts.doc_stormtypes:
1005
- libdocs, typedocs = await docStormTypes()
1006
- if opts.savedir:
1007
- with open(s_common.genpath(opts.savedir, f'stormtypes_libs.rst'), 'wb') as fd:
1008
- fd.write(libdocs.getRstText().encode())
1009
- with open(s_common.genpath(opts.savedir, f'stormtypes_prims.rst'), 'wb') as fd:
1010
- fd.write(typedocs.getRstText().encode())
1011
-
1012
- return 0
1013
-
1014
- def getArgParser(outp):
1015
- desc = 'Command line tool to generate various synapse documentation.'
1016
- pars = s_cmd.Parser(prog='synapse.tools.autodoc', outp=outp, description=desc)
1017
-
1018
- pars.add_argument('--cortex', '-c', default=None,
1019
- help='Cortex URL for model inspection')
1020
- pars.add_argument('--savedir', default=None,
1021
- help='Save output to the given directory')
1022
- doc_type = pars.add_mutually_exclusive_group()
1023
- doc_type.add_argument('--doc-model', action='store_true', default=False,
1024
- help='Generate RST docs for the DataModel within a cortex')
1025
- doc_type.add_argument('--doc-conf', default=None,
1026
- help='Generate RST docs for the Confdefs for a given Cell ctor')
1027
- doc_type.add_argument('--doc-storm', default=None,
1028
- help='Generate RST docs for a stormssvc implemented by a given Cell')
1029
-
1030
- doc_type.add_argument('--doc-stormpkg', default=None,
1031
- help='Generate RST docs for the specified Storm package YAML file.')
1032
4
 
1033
- doc_type.add_argument('--doc-stormtypes', default=None, action='store_true',
1034
- help='Generate RST docs for StormTypes')
5
+ from synapse.tools.utils.autodoc import logger, main, docStormpkg
1035
6
 
1036
- return pars
7
+ s_common.deprecated('synapse.tools.autodoc is deprecated. Please use synapse.tools.utils.autodoc instead.',
8
+ curv='v2.225.0')
1037
9
 
1038
10
  if __name__ == '__main__': # pragma: no cover
1039
11
  s_common.setlogging(logger, 'DEBUG')