synapse 2.212.0__py311-none-any.whl → 2.214.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 (75) hide show
  1. synapse/cortex.py +37 -6
  2. synapse/daemon.py +6 -6
  3. synapse/exc.py +13 -1
  4. synapse/lib/aha.py +5 -0
  5. synapse/lib/ast.py +2 -6
  6. synapse/lib/boss.py +47 -2
  7. synapse/lib/cell.py +199 -6
  8. synapse/lib/certdir.py +44 -1
  9. synapse/lib/cmd.py +24 -0
  10. synapse/lib/coro.py +8 -2
  11. synapse/lib/drive.py +7 -2
  12. synapse/lib/link.py +11 -3
  13. synapse/lib/schemas.py +1 -1
  14. synapse/lib/scrape.py +3 -1
  15. synapse/lib/snap.py +89 -80
  16. synapse/lib/storm.py +2 -1
  17. synapse/lib/stormlib/imap.py +3 -2
  18. synapse/lib/stormlib/spooled.py +4 -0
  19. synapse/lib/stormtypes.py +18 -0
  20. synapse/lib/task.py +1 -0
  21. synapse/lib/types.py +36 -8
  22. synapse/lib/version.py +2 -2
  23. synapse/models/inet.py +5 -0
  24. synapse/telepath.py +4 -2
  25. synapse/tests/files/testpkg_build_docs/docs/bar.rst +15 -0
  26. synapse/tests/files/testpkg_build_docs/docs/foo.rst +4 -0
  27. synapse/tests/files/testpkg_build_docs/storm/commands/testcmd.storm +0 -0
  28. synapse/tests/files/testpkg_build_docs/storm/modules/apimod.storm +0 -0
  29. synapse/tests/files/testpkg_build_docs/storm/modules/testmod.storm +0 -0
  30. synapse/tests/files/testpkg_build_docs/storm/testcmd.storm +5 -0
  31. synapse/tests/files/testpkg_build_docs/testpkg.yaml +69 -0
  32. synapse/tests/test_cortex.py +20 -1
  33. synapse/tests/test_daemon.py +1 -1
  34. synapse/tests/test_exc.py +6 -0
  35. synapse/tests/test_lib_ast.py +69 -14
  36. synapse/tests/test_lib_boss.py +8 -0
  37. synapse/tests/test_lib_cell.py +119 -8
  38. synapse/tests/test_lib_certdir.py +8 -0
  39. synapse/tests/test_lib_coro.py +5 -0
  40. synapse/tests/test_lib_httpapi.py +10 -2
  41. synapse/tests/test_lib_link.py +1 -1
  42. synapse/tests/test_lib_scrape.py +6 -0
  43. synapse/tests/test_lib_storm.py +123 -1
  44. synapse/tests/test_lib_stormlib_spooled.py +31 -0
  45. synapse/tests/test_lib_stormtypes.py +11 -0
  46. synapse/tests/test_lib_types.py +137 -45
  47. synapse/tests/test_model_crypto.py +8 -0
  48. synapse/tests/test_model_inet.py +7 -0
  49. synapse/tests/test_telepath.py +50 -5
  50. synapse/tests/test_tools_axon.py +304 -0
  51. synapse/tests/test_tools_cortex_layer.py +419 -0
  52. synapse/tests/test_tools_demote.py +114 -0
  53. synapse/tests/test_tools_pkgs_gendocs.py +100 -0
  54. synapse/tests/test_tools_shutdown.py +95 -0
  55. synapse/tests/test_utils.py +22 -1
  56. synapse/tests/utils.py +44 -29
  57. synapse/tools/aha/easycert.py +2 -0
  58. synapse/tools/aha/enroll.py +3 -0
  59. synapse/tools/axon/__init__.py +0 -0
  60. synapse/tools/axon/dump.py +155 -0
  61. synapse/tools/axon/load.py +89 -0
  62. synapse/tools/cortex/__init__.py +0 -0
  63. synapse/tools/cortex/layer/__init__.py +0 -0
  64. synapse/tools/cortex/layer/dump.py +184 -0
  65. synapse/tools/cortex/layer/load.py +129 -0
  66. synapse/tools/demote.py +52 -0
  67. synapse/tools/healthcheck.py +1 -1
  68. synapse/tools/pkgs/gendocs.py +176 -0
  69. synapse/tools/pkgs/pandoc_filter.py +79 -0
  70. synapse/tools/shutdown.py +52 -0
  71. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/METADATA +1 -1
  72. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/RECORD +75 -52
  73. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/WHEEL +0 -0
  74. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/licenses/LICENSE +0 -0
  75. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/top_level.txt +0 -0
@@ -110,7 +110,7 @@ class Foo:
110
110
 
111
111
  def raze(self):
112
112
  # test that SynErr makes it through
113
- raise s_exc.NoSuchMeth(name='haha')
113
+ raise s_exc.SynErr(mesg='hehe', key='valu')
114
114
 
115
115
  async def corovalu(self, x, y):
116
116
  return x * 2 + y
@@ -305,17 +305,48 @@ class TeleTest(s_t_utils.SynTest):
305
305
  genr = prox.agenrboom()
306
306
  await self.asyncraises(s_exc.SynErr, genr.list())
307
307
 
308
- await self.asyncraises(s_exc.NoSuchMeth, prox.raze())
308
+ with self.raises(s_exc.SynErr) as cm:
309
+ await prox.raze()
310
+ self.eq(cm.exception.get('mesg'), 'hehe')
311
+ self.eq(cm.exception.get('key'), 'valu')
309
312
 
310
- await self.asyncraises(s_exc.NoSuchMeth, prox.fake())
313
+ with self.raises(s_exc.NoSuchMeth) as cm:
314
+ await prox.fake()
315
+ self.eq(cm.exception.get('mesg'), 'Foo has no method: fake.')
316
+ self.eq(cm.exception.get('name'), 'fake')
311
317
 
312
- await self.asyncraises(s_exc.SynErr, prox.boom())
318
+ with self.raises(s_exc.NoSuchMeth) as cm:
319
+ await prox._fake()
320
+ self.eq(cm.exception.get('mesg'), 'Foo has no method: _fake.')
321
+ self.eq(cm.exception.get('name'), '_fake')
322
+
323
+ with self.raises(s_exc.NotMsgpackSafe) as cm:
324
+ await prox.boom()
325
+ self.isin("can not serialize 'Boom' object", cm.exception.get('mesg'))
313
326
 
314
327
  # Fini'ing a daemon fini's proxies connected to it.
315
328
  self.true(await s_coro.event_wait(evt, 2))
316
329
  self.true(prox.isfini)
317
330
  await self.asyncraises(s_exc.IsFini, prox.bar((10, 20)))
318
331
 
332
+ async def test_telepath_openinfo_cell(self):
333
+ with self.getTestDir() as dirn:
334
+ async with self.getTestCore(dirn=dirn) as core:
335
+
336
+ layr00 = core.getView().layers[0]
337
+
338
+ async with await s_telepath.openurl(f'cell://root@{dirn}:*', name=f'*/layer/{layr00.iden}') as layer:
339
+ self.eq(layr00.iden, await layer.getIden())
340
+
341
+ async def test_telepath_openinfo_unix(self):
342
+ with self.getTestDir() as dirn:
343
+ async with self.getTestCore(dirn=dirn) as core:
344
+
345
+ layr00 = core.getView().layers[0]
346
+
347
+ async with await s_telepath.openurl(f'unix://root@{dirn}/sock:*', name=f'*/layer/{layr00.iden}') as layer:
348
+ self.eq(layr00.iden, await layer.getIden())
349
+
319
350
  async def test_telepath_sync_genr(self):
320
351
 
321
352
  foo = Foo()
@@ -377,10 +408,12 @@ class TeleTest(s_t_utils.SynTest):
377
408
  genr = prox.corogenr(3)
378
409
  self.eq((0, 1, 2), await s_t_utils.alist(genr))
379
410
 
380
- await self.asyncraises(s_exc.NoSuchMeth, prox.raze())
411
+ await self.asyncraises(s_exc.SynErr, prox.raze())
381
412
 
382
413
  await self.asyncraises(s_exc.NoSuchMeth, prox.fake())
383
414
 
415
+ await self.asyncraises(s_exc.NoSuchMeth, prox._fake())
416
+
384
417
  await self.asyncraises(s_exc.SynErr, prox.boom())
385
418
 
386
419
  # Fini'ing a daemon fini's proxies connected to it.
@@ -816,6 +849,18 @@ class TeleTest(s_t_utils.SynTest):
816
849
  {'family': 'unix',
817
850
  'addr': sockpath})
818
851
 
852
+ async def test_url_bad_cell_path(self):
853
+ with self.getTestDir() as dirn:
854
+ # Touch the sock path
855
+ s_common.genfile(dirn, 'newp', 'sock').close()
856
+ cell_url = f'cell://{dirn}/newp'
857
+ with self.raises(s_exc.LinkErr) as cm:
858
+ await s_telepath.openurl(cell_url)
859
+ self.isin('Cell path is not listening', cm.exception.get('mesg'))
860
+ with self.raises(s_exc.NoSuchPath) as cm:
861
+ await s_telepath.openurl(cell_url)
862
+ self.isin('Cell path does not exist', cm.exception.get('mesg'))
863
+
819
864
  async def test_ipv6(self):
820
865
  if s_common.envbool('CIRCLECI'):
821
866
  self.skip('ipv6 listener is not supported in circleci')
@@ -0,0 +1,304 @@
1
+ import io
2
+ import os
3
+ import tarfile
4
+
5
+ from unittest import mock
6
+
7
+ import synapse.common as s_common
8
+
9
+ import synapse.tests.utils as s_t_utils
10
+
11
+ import synapse.tools.axon.dump as axon_dump
12
+ import synapse.tools.axon.load as axon_load
13
+
14
+ class AxonToolsTest(s_t_utils.SynTest):
15
+
16
+ async def test_axon_dump_and_load_url_handling(self):
17
+ with self.getTestDir() as testdir:
18
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', '--offset', '0', testdir]
19
+ outp = self.getTestOutp()
20
+ self.eq(1, await axon_dump.main(argv, outp=outp))
21
+ outp.expect('ERROR')
22
+ outp.expect('Cell path does not exist')
23
+
24
+ with self.getTestDir() as testdir:
25
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
26
+ tarfile = os.path.join(testdir, 'dummy.0-1.tar.gz')
27
+ with open(tarfile, 'wb') as fd:
28
+ fd.write(b'dummy')
29
+ url = f'cell://baduser:badpass@/{axon.dirn}'
30
+ argv = ['--url', url, tarfile]
31
+ outp = self.getTestOutp()
32
+ self.eq(1, await axon_load.main(argv, outp=outp))
33
+ outp.expect('No such user')
34
+
35
+ with self.getTestDir() as testdir:
36
+ tarfile = os.path.join(testdir, 'dummy.0-1.tar.gz')
37
+ with open(tarfile, 'wb') as fd:
38
+ fd.write(b'dummy')
39
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', tarfile]
40
+ outp = self.getTestOutp()
41
+ self.eq(1, await axon_load.main(argv, outp=outp))
42
+ outp.expect('ERROR')
43
+ outp.expect('Cell path does not exist')
44
+
45
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
46
+ dumpdir = os.path.join(testdir, 'dumpdir')
47
+ os.makedirs(dumpdir)
48
+ url = f'cell://baduser:badpass@/{axon.dirn}'
49
+ argv = ['--url', url, '--offset', '0', dumpdir]
50
+ outp = self.getTestOutp()
51
+ self.eq(1, await axon_dump.main(argv, outp=outp))
52
+ outp.expect('No such user')
53
+
54
+ async def test_axon_dump_and_load(self):
55
+ with self.getTestDir() as testdir:
56
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon0')) as axon0:
57
+ blobs = [b'foo', b'bar', b'baz' * 1000]
58
+ sha2s = []
59
+ for blob in blobs:
60
+ size, sha2 = await axon0.put(blob)
61
+ sha2s.append((size, sha2, blob))
62
+ dumpdir = os.path.join(testdir, 'dumpdir')
63
+ os.makedirs(dumpdir)
64
+ argv = [
65
+ '--url', f'cell:///{axon0.dirn}',
66
+ '--offset', '0',
67
+ dumpdir,
68
+ ]
69
+ self.eq(0, await axon_dump.main(argv))
70
+ files = os.listdir(dumpdir)
71
+ self.true(files, 'No dump file created')
72
+
73
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in files if f.endswith('.tar.gz')])
74
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon1')) as axon1:
75
+ argv = ['--url', f'cell:///{axon1.dirn}'] + tarfiles
76
+ self.eq(0, await axon_load.main(argv))
77
+ for size, sha2, blob in sha2s:
78
+ self.true(await axon1.has(sha2))
79
+ out = b''
80
+ async for byts in axon1.get(sha2):
81
+ out += byts
82
+ self.eq(out, blob)
83
+
84
+ async def test_dump_blob_size_mismatch(self):
85
+ with self.getTestDir() as testdir:
86
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
87
+ size, sha2 = await axon.put(b'hello world')
88
+ dumpdir = os.path.join(testdir, 'dumpdir')
89
+ os.makedirs(dumpdir)
90
+ orig_get = axon.get
91
+ async def bad_get(sha2arg, **kwargs):
92
+ if sha2arg == sha2:
93
+ yield b'hello' # corrupt it
94
+ else:
95
+ async for byts in orig_get(sha2arg):
96
+ yield byts
97
+ axon.get = bad_get
98
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', dumpdir]
99
+ outp = self.getTestOutp()
100
+ self.eq(1, await axon_dump.main(argv, outp=outp))
101
+ outp.expect('Blob size mismatch')
102
+
103
+ async def test_dump_not_axon_cell(self):
104
+ with self.getTestDir() as testdir:
105
+ async with self.getTestCore() as core:
106
+ curl = core.getLocalUrl()
107
+ argv = ['--url', curl, testdir]
108
+ outp = self.getTestOutp()
109
+ self.eq(1, await axon_dump.main(argv, outp=outp))
110
+ outp.expect('only works on axon')
111
+
112
+ async def test_dump_outdir_is_not_directory(self):
113
+ with self.getTestDir() as testdir:
114
+ outpath = os.path.join(testdir, 'notadir')
115
+ with open(outpath, 'w') as fd:
116
+ fd.write('not a directory')
117
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
118
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', outpath]
119
+ outp = self.getTestOutp()
120
+ self.eq(1, await axon_dump.main(argv, outp=outp))
121
+ outp.expect('exists but is not a directory')
122
+
123
+ async def test_dump_rotate_size_and_load(self):
124
+ with self.getTestDir() as testdir:
125
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
126
+ blobs = [os.urandom(1024) for _ in range(10)]
127
+ sha2s = []
128
+ for blob in blobs:
129
+ size, sha2 = await axon.put(blob)
130
+ sha2s.append((size, sha2, blob))
131
+ dumpdir = os.path.join(testdir, 'dumpdir')
132
+ os.makedirs(dumpdir)
133
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', '--rotate-size', '2048', dumpdir]
134
+ outp = self.getTestOutp()
135
+ self.eq(0, await axon_dump.main(argv, outp=outp))
136
+
137
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in os.listdir(dumpdir) if f.endswith('.tar.gz')])
138
+ self.true(len(tarfiles) > 1)
139
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon2')) as axon2:
140
+ outp = self.getTestOutp()
141
+ argv = ['--url', f'cell:///{axon2.dirn}'] + tarfiles
142
+ self.eq(0, await axon_load.main(argv, outp=outp))
143
+ for size, sha2, blob in sha2s:
144
+ self.true(await axon2.has(sha2))
145
+ out = b''
146
+ async for byts in axon2.get(sha2):
147
+ out += byts
148
+ self.eq(out, blob)
149
+
150
+ async def test_dump_tempfile_rollover(self):
151
+ with self.getTestDir() as testdir:
152
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
153
+ n = 128
154
+ blobs = [os.urandom(n) for n in range(n)]
155
+ sha2s = []
156
+ for blob in blobs:
157
+ size, sha2 = await axon.put(blob)
158
+ sha2s.append((size, sha2, blob))
159
+ dumpdir = os.path.join(testdir, 'dumpdir')
160
+ os.makedirs(dumpdir)
161
+ argv = ['--url', f'cell:///{axon.dirn}', dumpdir]
162
+ outp = self.getTestOutp()
163
+
164
+ # Patch the SppoledTemporaryFile rollover size while dumping files.
165
+ with mock.patch('synapse.tools.axon.dump.MAX_SPOOL_SIZE', n / 2):
166
+ self.eq(0, await axon_dump.main(argv, outp=outp))
167
+
168
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in os.listdir(dumpdir) if f.endswith('.tar.gz')])
169
+ self.len(1, tarfiles)
170
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon2')) as axon2:
171
+ outp = self.getTestOutp()
172
+ argv = ['--url', f'cell:///{axon2.dirn}'] + tarfiles
173
+ self.eq(0, await axon_load.main(argv, outp=outp))
174
+ for size, sha2, blob in sha2s:
175
+ self.true(await axon2.has(sha2))
176
+ out = b''
177
+ async for byts in axon2.get(sha2):
178
+ out += byts
179
+ self.eq(out, blob)
180
+
181
+ async def test_dump_statefile(self):
182
+ with self.getTestDir() as testdir:
183
+ async with self.getTestAxon(dirn=testdir) as axon:
184
+ blob_data = b'blobdata'
185
+ await axon.put(blob_data)
186
+ outdir = os.path.join(testdir, 'out')
187
+ os.makedirs(outdir)
188
+ statefile = os.path.join(testdir, 'state.yaml')
189
+
190
+ argv = ['--url', axon.getLocalUrl(), '--statefile', statefile, outdir]
191
+ outp = self.getTestOutp()
192
+ self.eq(0, await axon_dump.main(argv, outp=outp))
193
+ self.true(os.path.isfile(statefile))
194
+ state = s_common.yamlload(statefile)
195
+ self.isin('offset:next', state)
196
+
197
+ s_common.yamlsave({'offset:next': 123}, statefile)
198
+ outp = self.getTestOutp()
199
+ self.eq(0, await axon_dump.main(argv, outp=outp))
200
+ self.true(os.path.isfile(statefile))
201
+ state = s_common.yamlload(statefile)
202
+ self.isin('offset:next', state)
203
+ self.ne(state['offset:next'], 123)
204
+
205
+ statedir = os.path.join(testdir, 'statedir')
206
+ os.makedirs(statedir)
207
+ argv = ['--url', axon.getLocalUrl(), '--statefile', statedir, outdir]
208
+ outp = self.getTestOutp()
209
+ self.eq(0, await axon_dump.main(argv, outp=outp))
210
+ cellinfo = await axon.getCellInfo()
211
+ celliden = cellinfo['cell']['iden']
212
+ statefile_path = os.path.join(statedir, f'{celliden}.yaml')
213
+ self.true(os.path.isfile(statefile_path))
214
+ state = s_common.yamlload(statefile_path)
215
+ self.isin('offset:next', state)
216
+
217
+ async def test_load_blobs_path_does_not_exist(self):
218
+ with self.getTestDir() as testdir:
219
+ missing = os.path.join(testdir, 'doesnotexist')
220
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', missing]
221
+ outp = self.getTestOutp()
222
+ self.eq(1, await axon_load.main(argv, outp=outp))
223
+ outp.expect('ERROR: Cell path does not exist')
224
+
225
+ async def test_load_continue_and_invalid_blob(self):
226
+ with self.getTestDir() as testdir:
227
+ tarpath = os.path.join(testdir, 'test1.tar.gz')
228
+ with tarfile.open(tarpath, 'w:gz') as tar:
229
+ info = tarfile.TarInfo('notablob.txt')
230
+ data = b'not a blob'
231
+ info.size = len(data)
232
+ tar.addfile(info, io.BytesIO(data))
233
+ info2 = tarfile.TarInfo('invalidblobname.blob')
234
+ data2 = b'data'
235
+ info2.size = len(data2)
236
+ tar.addfile(info2, io.BytesIO(data2))
237
+
238
+ orig_uhex = s_common.uhex
239
+ def fake_uhex(val):
240
+ if val == 'invalidblobname':
241
+ raise Exception('bad hex')
242
+ return orig_uhex(val)
243
+ s_common.uhex = fake_uhex
244
+
245
+ outp = self.getTestOutp()
246
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
247
+ argv = ['--url', axon.getLocalUrl(), tarpath]
248
+ self.eq(0, await axon_load.main(argv, outp=outp))
249
+ s_common.uhex = orig_uhex
250
+ outp.expect('Skipping invalid blob filename')
251
+
252
+ async def test_load_failed_to_extract(self):
253
+ with self.getTestDir() as testdir:
254
+ tarpath = os.path.join(testdir, 'test3.tar.gz')
255
+ with tarfile.open(tarpath, 'w:gz') as tar:
256
+ info = tarfile.TarInfo('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.blob')
257
+ info.type = tarfile.DIRTYPE
258
+ tar.addfile(info)
259
+ outp = self.getTestOutp()
260
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
261
+ argv = ['--url', axon.getLocalUrl(), tarpath]
262
+ self.eq(0, await axon_load.main(argv, outp=outp))
263
+ outp.expect('Failed to extract')
264
+
265
+ async def test_load_not_axon_cell(self):
266
+ with self.getTestDir() as testdir:
267
+ async with self.getTestCore() as core:
268
+ curl = core.getLocalUrl()
269
+ dumpdir = os.path.join(testdir, 'dumpdir')
270
+ os.makedirs(dumpdir)
271
+ argv = ['--url', curl, dumpdir]
272
+ outp = self.getTestOutp()
273
+ self.eq(1, await axon_load.main(argv, outp=outp))
274
+ outp.expect('only works on axon')
275
+
276
+ async def test_load_oserror_extracting_blob(self):
277
+ with self.getTestDir() as testdir:
278
+ tarpath = os.path.join(testdir, 'test_oserror.tar.gz')
279
+ with tarfile.open(tarpath, 'w:gz') as tar:
280
+ info = tarfile.TarInfo('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.blob')
281
+ data = b'12345'
282
+ info.size = len(data)
283
+ tar.addfile(info, io.BytesIO(data))
284
+ outp = self.getTestOutp()
285
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
286
+ with mock.patch('tarfile.TarFile.extractfile', side_effect=OSError("simulated extraction error")):
287
+ argv = ['--url', axon.getLocalUrl(), tarpath]
288
+ self.eq(0, await axon_load.main(argv, outp=outp))
289
+ outp.expect('WARNING: Error extracting')
290
+
291
+ async def test_load_skipping_existing_blob(self):
292
+ with self.getTestDir() as testdir:
293
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
294
+ blob = b'foo'
295
+ size, sha2 = await axon.put(blob)
296
+ tarpath = os.path.join(testdir, 'test2.tar.gz')
297
+ with tarfile.open(tarpath, 'w:gz') as tar:
298
+ info = tarfile.TarInfo(f'{s_common.ehex(sha2)}.blob')
299
+ info.size = len(blob)
300
+ tar.addfile(info, io.BytesIO(blob))
301
+ outp = self.getTestOutp()
302
+ argv = ['--url', axon.getLocalUrl(), tarpath]
303
+ self.eq(0, await axon_load.main(argv, outp=outp))
304
+ outp.expect('Skipping existing blob')