polyfile-weave 0.5.5__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.

Potentially problematic release.


This version of polyfile-weave might be problematic. Click here for more details.

Files changed (585) hide show
  1. polyfile/__init__.py +15 -0
  2. polyfile/__main__.py +394 -0
  3. polyfile/arithmetic.py +27 -0
  4. polyfile/ast.py +114 -0
  5. polyfile/debugger.py +1039 -0
  6. polyfile/expressions.py +346 -0
  7. polyfile/fileutils.py +343 -0
  8. polyfile/html.py +135 -0
  9. polyfile/http/__init__.py +1 -0
  10. polyfile/http/defacto.py +37 -0
  11. polyfile/http/deprecated.py +51 -0
  12. polyfile/http/experimental.py +67 -0
  13. polyfile/http/http_11.py +548 -0
  14. polyfile/http/matcher.py +37 -0
  15. polyfile/http/structured_headers.py +48 -0
  16. polyfile/iterators.py +72 -0
  17. polyfile/jpeg.py +24 -0
  18. polyfile/kaitai/__init__.py +0 -0
  19. polyfile/kaitai/compiler.py +156 -0
  20. polyfile/kaitai/parser.py +312 -0
  21. polyfile/kaitai/parsers/__init__.py +0 -0
  22. polyfile/kaitai/parsers/aix_utmp.py +116 -0
  23. polyfile/kaitai/parsers/allegro_dat.py +367 -0
  24. polyfile/kaitai/parsers/andes_firmware.py +64 -0
  25. polyfile/kaitai/parsers/android_bootldr_asus.py +105 -0
  26. polyfile/kaitai/parsers/android_bootldr_huawei.py +181 -0
  27. polyfile/kaitai/parsers/android_bootldr_qcom.py +217 -0
  28. polyfile/kaitai/parsers/android_dto.py +138 -0
  29. polyfile/kaitai/parsers/android_img.py +319 -0
  30. polyfile/kaitai/parsers/android_nanoapp_header.py +83 -0
  31. polyfile/kaitai/parsers/android_opengl_shaders_cache.py +151 -0
  32. polyfile/kaitai/parsers/android_sparse.py +237 -0
  33. polyfile/kaitai/parsers/android_super.py +401 -0
  34. polyfile/kaitai/parsers/apm_partition_table.py +196 -0
  35. polyfile/kaitai/parsers/apple_single_double.py +180 -0
  36. polyfile/kaitai/parsers/asn1_der.py +235 -0
  37. polyfile/kaitai/parsers/au.py +138 -0
  38. polyfile/kaitai/parsers/avantes_roh60.py +112 -0
  39. polyfile/kaitai/parsers/avi.py +296 -0
  40. polyfile/kaitai/parsers/bcd.py +111 -0
  41. polyfile/kaitai/parsers/bitcoin_transaction.py +210 -0
  42. polyfile/kaitai/parsers/blender_blend.py +334 -0
  43. polyfile/kaitai/parsers/bmp.py +780 -0
  44. polyfile/kaitai/parsers/bson.py +411 -0
  45. polyfile/kaitai/parsers/btrfs_stream.py +318 -0
  46. polyfile/kaitai/parsers/bytes_with_io.py +27 -0
  47. polyfile/kaitai/parsers/chrome_pak.py +194 -0
  48. polyfile/kaitai/parsers/code_6502.py +456 -0
  49. polyfile/kaitai/parsers/compressed_resource.py +217 -0
  50. polyfile/kaitai/parsers/cpio_old_le.py +154 -0
  51. polyfile/kaitai/parsers/cramfs.py +344 -0
  52. polyfile/kaitai/parsers/creative_voice_file.py +342 -0
  53. polyfile/kaitai/parsers/dbf.py +274 -0
  54. polyfile/kaitai/parsers/dcmp_0.py +664 -0
  55. polyfile/kaitai/parsers/dcmp_1.py +422 -0
  56. polyfile/kaitai/parsers/dcmp_2.py +312 -0
  57. polyfile/kaitai/parsers/dcmp_variable_length_integer.py +66 -0
  58. polyfile/kaitai/parsers/dex.py +1086 -0
  59. polyfile/kaitai/parsers/dicom.py +4370 -0
  60. polyfile/kaitai/parsers/dime_message.py +201 -0
  61. polyfile/kaitai/parsers/dns_packet.py +569 -0
  62. polyfile/kaitai/parsers/doom_wad.py +654 -0
  63. polyfile/kaitai/parsers/dos_datetime.py +191 -0
  64. polyfile/kaitai/parsers/dos_mz.py +172 -0
  65. polyfile/kaitai/parsers/ds_store.py +513 -0
  66. polyfile/kaitai/parsers/dtb.py +310 -0
  67. polyfile/kaitai/parsers/dune_2_pak.py +126 -0
  68. polyfile/kaitai/parsers/edid.py +472 -0
  69. polyfile/kaitai/parsers/efivar_signature_list.py +331 -0
  70. polyfile/kaitai/parsers/elf.py +2482 -0
  71. polyfile/kaitai/parsers/ethernet_frame.py +114 -0
  72. polyfile/kaitai/parsers/exif.py +723 -0
  73. polyfile/kaitai/parsers/ext2.py +537 -0
  74. polyfile/kaitai/parsers/fallout2_dat.py +187 -0
  75. polyfile/kaitai/parsers/fallout_dat.py +156 -0
  76. polyfile/kaitai/parsers/fasttracker_xm_module.py +558 -0
  77. polyfile/kaitai/parsers/ftl_dat.py +90 -0
  78. polyfile/kaitai/parsers/genmidi_op2.py +161 -0
  79. polyfile/kaitai/parsers/gettext_mo.py +541 -0
  80. polyfile/kaitai/parsers/gif.py +492 -0
  81. polyfile/kaitai/parsers/gimp_brush.py +244 -0
  82. polyfile/kaitai/parsers/glibc_utmp.py +114 -0
  83. polyfile/kaitai/parsers/gltf_binary.py +132 -0
  84. polyfile/kaitai/parsers/google_protobuf.py +151 -0
  85. polyfile/kaitai/parsers/gpt_partition_table.py +175 -0
  86. polyfile/kaitai/parsers/gran_turismo_vol.py +140 -0
  87. polyfile/kaitai/parsers/grub2_font.py +337 -0
  88. polyfile/kaitai/parsers/gzip.py +232 -0
  89. polyfile/kaitai/parsers/hashcat_restore.py +60 -0
  90. polyfile/kaitai/parsers/hccap.py +111 -0
  91. polyfile/kaitai/parsers/hccapx.py +103 -0
  92. polyfile/kaitai/parsers/heaps_pak.py +177 -0
  93. polyfile/kaitai/parsers/heroes_of_might_and_magic_agg.py +116 -0
  94. polyfile/kaitai/parsers/heroes_of_might_and_magic_bmp.py +34 -0
  95. polyfile/kaitai/parsers/icmp_packet.py +136 -0
  96. polyfile/kaitai/parsers/ico.py +129 -0
  97. polyfile/kaitai/parsers/id3v1_1.py +220 -0
  98. polyfile/kaitai/parsers/id3v2_3.py +324 -0
  99. polyfile/kaitai/parsers/id3v2_4.py +423 -0
  100. polyfile/kaitai/parsers/ines.py +282 -0
  101. polyfile/kaitai/parsers/ipv4_packet.py +158 -0
  102. polyfile/kaitai/parsers/ipv6_packet.py +55 -0
  103. polyfile/kaitai/parsers/iso9660.py +544 -0
  104. polyfile/kaitai/parsers/java_class.py +1113 -0
  105. polyfile/kaitai/parsers/jpeg.py +361 -0
  106. polyfile/kaitai/parsers/luks.py +149 -0
  107. polyfile/kaitai/parsers/lzh.py +165 -0
  108. polyfile/kaitai/parsers/mac_os_resource_snd.py +493 -0
  109. polyfile/kaitai/parsers/mach_o.py +3033 -0
  110. polyfile/kaitai/parsers/mach_o_fat.py +92 -0
  111. polyfile/kaitai/parsers/magicavoxel_vox.py +391 -0
  112. polyfile/kaitai/parsers/manifest.json +1 -0
  113. polyfile/kaitai/parsers/mbr_partition_table.py +119 -0
  114. polyfile/kaitai/parsers/mcap.py +1015 -0
  115. polyfile/kaitai/parsers/microsoft_cfb.py +293 -0
  116. polyfile/kaitai/parsers/microsoft_network_monitor_v2.py +309 -0
  117. polyfile/kaitai/parsers/microsoft_pe.py +765 -0
  118. polyfile/kaitai/parsers/mifare_classic.py +706 -0
  119. polyfile/kaitai/parsers/minecraft_nbt.py +449 -0
  120. polyfile/kaitai/parsers/monomakh_sapr_chg.py +69 -0
  121. polyfile/kaitai/parsers/mozilla_mar.py +239 -0
  122. polyfile/kaitai/parsers/mp4.py +333 -0
  123. polyfile/kaitai/parsers/msgpack.py +467 -0
  124. polyfile/kaitai/parsers/nitf.py +1189 -0
  125. polyfile/kaitai/parsers/nt_mdt_pal.py +155 -0
  126. polyfile/kaitai/parsers/ogg.py +118 -0
  127. polyfile/kaitai/parsers/openpgp_message.py +993 -0
  128. polyfile/kaitai/parsers/packet_ppi.py +515 -0
  129. polyfile/kaitai/parsers/pcap.py +344 -0
  130. polyfile/kaitai/parsers/pcf_font.py +506 -0
  131. polyfile/kaitai/parsers/pcx.py +195 -0
  132. polyfile/kaitai/parsers/pcx_dcx.py +79 -0
  133. polyfile/kaitai/parsers/phar_without_stub.py +399 -0
  134. polyfile/kaitai/parsers/php_serialized_value.py +505 -0
  135. polyfile/kaitai/parsers/png.py +721 -0
  136. polyfile/kaitai/parsers/protocol_body.py +260 -0
  137. polyfile/kaitai/parsers/psx_tim.py +104 -0
  138. polyfile/kaitai/parsers/python_pickle.py +718 -0
  139. polyfile/kaitai/parsers/python_pyc_27.py +510 -0
  140. polyfile/kaitai/parsers/quake_mdl.py +441 -0
  141. polyfile/kaitai/parsers/quake_pak.py +112 -0
  142. polyfile/kaitai/parsers/quicktime_mov.py +634 -0
  143. polyfile/kaitai/parsers/rar.py +265 -0
  144. polyfile/kaitai/parsers/regf.py +569 -0
  145. polyfile/kaitai/parsers/renderware_binary_stream.py +877 -0
  146. polyfile/kaitai/parsers/resource_fork.py +611 -0
  147. polyfile/kaitai/parsers/respack.py +57 -0
  148. polyfile/kaitai/parsers/riff.py +409 -0
  149. polyfile/kaitai/parsers/rpm.py +964 -0
  150. polyfile/kaitai/parsers/rtcp_payload.py +579 -0
  151. polyfile/kaitai/parsers/rtp_packet.py +150 -0
  152. polyfile/kaitai/parsers/rtpdump.py +115 -0
  153. polyfile/kaitai/parsers/ruby_marshal.py +423 -0
  154. polyfile/kaitai/parsers/s3m.py +493 -0
  155. polyfile/kaitai/parsers/saints_row_2_vpp_pc.py +254 -0
  156. polyfile/kaitai/parsers/shapefile_index.py +174 -0
  157. polyfile/kaitai/parsers/shapefile_main.py +893 -0
  158. polyfile/kaitai/parsers/some_ip.py +209 -0
  159. polyfile/kaitai/parsers/some_ip_container.py +37 -0
  160. polyfile/kaitai/parsers/some_ip_sd.py +86 -0
  161. polyfile/kaitai/parsers/some_ip_sd_entries.py +160 -0
  162. polyfile/kaitai/parsers/some_ip_sd_options.py +374 -0
  163. polyfile/kaitai/parsers/specpr.py +404 -0
  164. polyfile/kaitai/parsers/sqlite3.py +472 -0
  165. polyfile/kaitai/parsers/ssh_public_key.py +252 -0
  166. polyfile/kaitai/parsers/standard_midi_file.py +390 -0
  167. polyfile/kaitai/parsers/stl.py +111 -0
  168. polyfile/kaitai/parsers/sudoers_ts.py +201 -0
  169. polyfile/kaitai/parsers/swf.py +406 -0
  170. polyfile/kaitai/parsers/systemd_journal.py +361 -0
  171. polyfile/kaitai/parsers/tcp_segment.py +57 -0
  172. polyfile/kaitai/parsers/tga.py +213 -0
  173. polyfile/kaitai/parsers/tls_client_hello.py +293 -0
  174. polyfile/kaitai/parsers/tr_dos_image.py +322 -0
  175. polyfile/kaitai/parsers/tsm.py +198 -0
  176. polyfile/kaitai/parsers/ttf.py +1847 -0
  177. polyfile/kaitai/parsers/udp_datagram.py +42 -0
  178. polyfile/kaitai/parsers/uefi_te.py +236 -0
  179. polyfile/kaitai/parsers/uimage.py +198 -0
  180. polyfile/kaitai/parsers/utf8_string.py +137 -0
  181. polyfile/kaitai/parsers/vfat.py +410 -0
  182. polyfile/kaitai/parsers/vlq_base128_be.py +104 -0
  183. polyfile/kaitai/parsers/vlq_base128_le.py +129 -0
  184. polyfile/kaitai/parsers/vmware_vmdk.py +167 -0
  185. polyfile/kaitai/parsers/vp8_ivf.py +112 -0
  186. polyfile/kaitai/parsers/warcraft_2_pud.py +423 -0
  187. polyfile/kaitai/parsers/wav.py +1014 -0
  188. polyfile/kaitai/parsers/websocket.py +167 -0
  189. polyfile/kaitai/parsers/windows_evt_log.py +304 -0
  190. polyfile/kaitai/parsers/windows_lnk_file.py +467 -0
  191. polyfile/kaitai/parsers/windows_minidump.py +575 -0
  192. polyfile/kaitai/parsers/windows_resource_file.py +243 -0
  193. polyfile/kaitai/parsers/windows_shell_items.py +190 -0
  194. polyfile/kaitai/parsers/windows_systemtime.py +52 -0
  195. polyfile/kaitai/parsers/wmf.py +502 -0
  196. polyfile/kaitai/parsers/xar.py +181 -0
  197. polyfile/kaitai/parsers/xwd.py +189 -0
  198. polyfile/kaitai/parsers/zip.py +685 -0
  199. polyfile/kaitai/parsers/zisofs.py +158 -0
  200. polyfile/kaitai/parsers/zx_spectrum_tap.py +184 -0
  201. polyfile/kaitaimatcher.py +113 -0
  202. polyfile/languagematcher.py +217 -0
  203. polyfile/logger.py +135 -0
  204. polyfile/magic.py +2983 -0
  205. polyfile/magic_defs/COPYING +29 -0
  206. polyfile/magic_defs/__init__.py +0 -0
  207. polyfile/magic_defs/acorn +102 -0
  208. polyfile/magic_defs/adi +13 -0
  209. polyfile/magic_defs/adventure +122 -0
  210. polyfile/magic_defs/aes +29 -0
  211. polyfile/magic_defs/algol68 +35 -0
  212. polyfile/magic_defs/allegro +9 -0
  213. polyfile/magic_defs/alliant +18 -0
  214. polyfile/magic_defs/alpha +32 -0
  215. polyfile/magic_defs/amanda +12 -0
  216. polyfile/magic_defs/amigaos +218 -0
  217. polyfile/magic_defs/android +259 -0
  218. polyfile/magic_defs/animation +1197 -0
  219. polyfile/magic_defs/aout +46 -0
  220. polyfile/magic_defs/apache +28 -0
  221. polyfile/magic_defs/apl +7 -0
  222. polyfile/magic_defs/apple +773 -0
  223. polyfile/magic_defs/application +7 -0
  224. polyfile/magic_defs/applix +13 -0
  225. polyfile/magic_defs/apt +52 -0
  226. polyfile/magic_defs/archive +2586 -0
  227. polyfile/magic_defs/aria +38 -0
  228. polyfile/magic_defs/arm +50 -0
  229. polyfile/magic_defs/asf +132 -0
  230. polyfile/magic_defs/assembler +18 -0
  231. polyfile/magic_defs/asterix +18 -0
  232. polyfile/magic_defs/att3b +41 -0
  233. polyfile/magic_defs/audio +1291 -0
  234. polyfile/magic_defs/avm +33 -0
  235. polyfile/magic_defs/basis +18 -0
  236. polyfile/magic_defs/beetle +7 -0
  237. polyfile/magic_defs/ber +65 -0
  238. polyfile/magic_defs/bflt +14 -0
  239. polyfile/magic_defs/bhl +10 -0
  240. polyfile/magic_defs/bioinformatics +178 -0
  241. polyfile/magic_defs/biosig +154 -0
  242. polyfile/magic_defs/blackberry +8 -0
  243. polyfile/magic_defs/blcr +25 -0
  244. polyfile/magic_defs/blender +50 -0
  245. polyfile/magic_defs/blit +24 -0
  246. polyfile/magic_defs/bm +10 -0
  247. polyfile/magic_defs/bout +11 -0
  248. polyfile/magic_defs/bsdi +33 -0
  249. polyfile/magic_defs/bsi +10 -0
  250. polyfile/magic_defs/btsnoop +13 -0
  251. polyfile/magic_defs/burp +7 -0
  252. polyfile/magic_defs/bytecode +41 -0
  253. polyfile/magic_defs/c-lang +110 -0
  254. polyfile/magic_defs/c64 +531 -0
  255. polyfile/magic_defs/cad +437 -0
  256. polyfile/magic_defs/cafebabe +107 -0
  257. polyfile/magic_defs/cbor +21 -0
  258. polyfile/magic_defs/ccf +14 -0
  259. polyfile/magic_defs/cddb +12 -0
  260. polyfile/magic_defs/chord +15 -0
  261. polyfile/magic_defs/cisco +12 -0
  262. polyfile/magic_defs/citrus +12 -0
  263. polyfile/magic_defs/clarion +27 -0
  264. polyfile/magic_defs/claris +48 -0
  265. polyfile/magic_defs/clipper +65 -0
  266. polyfile/magic_defs/clojure +30 -0
  267. polyfile/magic_defs/coff +98 -0
  268. polyfile/magic_defs/commands +201 -0
  269. polyfile/magic_defs/communications +22 -0
  270. polyfile/magic_defs/compress +461 -0
  271. polyfile/magic_defs/console +1213 -0
  272. polyfile/magic_defs/convex +69 -0
  273. polyfile/magic_defs/coverage +91 -0
  274. polyfile/magic_defs/cracklib +14 -0
  275. polyfile/magic_defs/crypto +31 -0
  276. polyfile/magic_defs/csv +8 -0
  277. polyfile/magic_defs/ctags +6 -0
  278. polyfile/magic_defs/ctf +23 -0
  279. polyfile/magic_defs/cubemap +8 -0
  280. polyfile/magic_defs/cups +56 -0
  281. polyfile/magic_defs/dact +11 -0
  282. polyfile/magic_defs/database +886 -0
  283. polyfile/magic_defs/dataone +47 -0
  284. polyfile/magic_defs/dbpf +15 -0
  285. polyfile/magic_defs/der +146 -0
  286. polyfile/magic_defs/diamond +12 -0
  287. polyfile/magic_defs/dif +33 -0
  288. polyfile/magic_defs/diff +41 -0
  289. polyfile/magic_defs/digital +59 -0
  290. polyfile/magic_defs/dolby +69 -0
  291. polyfile/magic_defs/dsf +25 -0
  292. polyfile/magic_defs/dump +96 -0
  293. polyfile/magic_defs/dwarfs +45 -0
  294. polyfile/magic_defs/dyadic +61 -0
  295. polyfile/magic_defs/ebml +8 -0
  296. polyfile/magic_defs/edid +11 -0
  297. polyfile/magic_defs/editors +43 -0
  298. polyfile/magic_defs/efi +15 -0
  299. polyfile/magic_defs/elf +379 -0
  300. polyfile/magic_defs/encore +22 -0
  301. polyfile/magic_defs/epoc +62 -0
  302. polyfile/magic_defs/erlang +21 -0
  303. polyfile/magic_defs/espressif +57 -0
  304. polyfile/magic_defs/esri +28 -0
  305. polyfile/magic_defs/etf +33 -0
  306. polyfile/magic_defs/fcs +9 -0
  307. polyfile/magic_defs/filesystems +2694 -0
  308. polyfile/magic_defs/finger +16 -0
  309. polyfile/magic_defs/firmware +133 -0
  310. polyfile/magic_defs/flash +62 -0
  311. polyfile/magic_defs/flif +36 -0
  312. polyfile/magic_defs/fonts +449 -0
  313. polyfile/magic_defs/forth +82 -0
  314. polyfile/magic_defs/fortran +9 -0
  315. polyfile/magic_defs/frame +62 -0
  316. polyfile/magic_defs/freebsd +164 -0
  317. polyfile/magic_defs/fsav +128 -0
  318. polyfile/magic_defs/fusecompress +12 -0
  319. polyfile/magic_defs/games +696 -0
  320. polyfile/magic_defs/gcc +17 -0
  321. polyfile/magic_defs/gconv +10 -0
  322. polyfile/magic_defs/gentoo +85 -0
  323. polyfile/magic_defs/geo +166 -0
  324. polyfile/magic_defs/geos +20 -0
  325. polyfile/magic_defs/gimp +77 -0
  326. polyfile/magic_defs/git +13 -0
  327. polyfile/magic_defs/glibc +21 -0
  328. polyfile/magic_defs/gnome +59 -0
  329. polyfile/magic_defs/gnu +173 -0
  330. polyfile/magic_defs/gnumeric +8 -0
  331. polyfile/magic_defs/gpt +240 -0
  332. polyfile/magic_defs/gpu +28 -0
  333. polyfile/magic_defs/grace +21 -0
  334. polyfile/magic_defs/graphviz +12 -0
  335. polyfile/magic_defs/gringotts +48 -0
  336. polyfile/magic_defs/guile +13 -0
  337. polyfile/magic_defs/hardware +12 -0
  338. polyfile/magic_defs/hitachi-sh +30 -0
  339. polyfile/magic_defs/hp +433 -0
  340. polyfile/magic_defs/human68k +26 -0
  341. polyfile/magic_defs/ibm370 +52 -0
  342. polyfile/magic_defs/ibm6000 +35 -0
  343. polyfile/magic_defs/icc +214 -0
  344. polyfile/magic_defs/iff +80 -0
  345. polyfile/magic_defs/images +4210 -0
  346. polyfile/magic_defs/inform +9 -0
  347. polyfile/magic_defs/intel +310 -0
  348. polyfile/magic_defs/interleaf +9 -0
  349. polyfile/magic_defs/island +10 -0
  350. polyfile/magic_defs/ispell +63 -0
  351. polyfile/magic_defs/isz +15 -0
  352. polyfile/magic_defs/java +52 -0
  353. polyfile/magic_defs/javascript +171 -0
  354. polyfile/magic_defs/jpeg +252 -0
  355. polyfile/magic_defs/json +8 -0
  356. polyfile/magic_defs/karma +9 -0
  357. polyfile/magic_defs/kde +11 -0
  358. polyfile/magic_defs/keepass +20 -0
  359. polyfile/magic_defs/kerberos +45 -0
  360. polyfile/magic_defs/kicad +85 -0
  361. polyfile/magic_defs/kml +34 -0
  362. polyfile/magic_defs/lammps +64 -0
  363. polyfile/magic_defs/lecter +6 -0
  364. polyfile/magic_defs/lex +12 -0
  365. polyfile/magic_defs/lif +50 -0
  366. polyfile/magic_defs/linux +557 -0
  367. polyfile/magic_defs/lisp +78 -0
  368. polyfile/magic_defs/llvm +22 -0
  369. polyfile/magic_defs/locoscript +12 -0
  370. polyfile/magic_defs/lua +31 -0
  371. polyfile/magic_defs/luks +126 -0
  372. polyfile/magic_defs/m4 +11 -0
  373. polyfile/magic_defs/mach +303 -0
  374. polyfile/magic_defs/macintosh +505 -0
  375. polyfile/magic_defs/macos +7 -0
  376. polyfile/magic_defs/magic +10 -0
  377. polyfile/magic_defs/magic.mgc +0 -0
  378. polyfile/magic_defs/mail.news +132 -0
  379. polyfile/magic_defs/make +21 -0
  380. polyfile/magic_defs/map +413 -0
  381. polyfile/magic_defs/maple +109 -0
  382. polyfile/magic_defs/marc21 +30 -0
  383. polyfile/magic_defs/mathcad +8 -0
  384. polyfile/magic_defs/mathematica +188 -0
  385. polyfile/magic_defs/matroska +17 -0
  386. polyfile/magic_defs/mcrypt +52 -0
  387. polyfile/magic_defs/measure +44 -0
  388. polyfile/magic_defs/mercurial +13 -0
  389. polyfile/magic_defs/metastore +8 -0
  390. polyfile/magic_defs/meteorological +53 -0
  391. polyfile/magic_defs/microfocus +21 -0
  392. polyfile/magic_defs/mime +9 -0
  393. polyfile/magic_defs/mips +120 -0
  394. polyfile/magic_defs/mirage +8 -0
  395. polyfile/magic_defs/misctools +140 -0
  396. polyfile/magic_defs/mkid +11 -0
  397. polyfile/magic_defs/mlssa +8 -0
  398. polyfile/magic_defs/mmdf +6 -0
  399. polyfile/magic_defs/modem +92 -0
  400. polyfile/magic_defs/modulefile +9 -0
  401. polyfile/magic_defs/motorola +71 -0
  402. polyfile/magic_defs/mozilla +37 -0
  403. polyfile/magic_defs/msdos +2304 -0
  404. polyfile/magic_defs/msooxml +68 -0
  405. polyfile/magic_defs/msvc +222 -0
  406. polyfile/magic_defs/msx +309 -0
  407. polyfile/magic_defs/mup +24 -0
  408. polyfile/magic_defs/music +17 -0
  409. polyfile/magic_defs/nasa +7 -0
  410. polyfile/magic_defs/natinst +24 -0
  411. polyfile/magic_defs/ncr +49 -0
  412. polyfile/magic_defs/neko +12 -0
  413. polyfile/magic_defs/netbsd +251 -0
  414. polyfile/magic_defs/netscape +26 -0
  415. polyfile/magic_defs/netware +11 -0
  416. polyfile/magic_defs/news +13 -0
  417. polyfile/magic_defs/nifty +202 -0
  418. polyfile/magic_defs/nim-lang +29 -0
  419. polyfile/magic_defs/nitpicker +14 -0
  420. polyfile/magic_defs/numpy +9 -0
  421. polyfile/magic_defs/oasis +12 -0
  422. polyfile/magic_defs/ocaml +14 -0
  423. polyfile/magic_defs/octave +6 -0
  424. polyfile/magic_defs/ole2compounddocs +760 -0
  425. polyfile/magic_defs/olf +98 -0
  426. polyfile/magic_defs/openfst +17 -0
  427. polyfile/magic_defs/opentimestamps +16 -0
  428. polyfile/magic_defs/oric +16 -0
  429. polyfile/magic_defs/os2 +186 -0
  430. polyfile/magic_defs/os400 +39 -0
  431. polyfile/magic_defs/os9 +80 -0
  432. polyfile/magic_defs/osf1 +10 -0
  433. polyfile/magic_defs/palm +156 -0
  434. polyfile/magic_defs/parix +13 -0
  435. polyfile/magic_defs/parrot +22 -0
  436. polyfile/magic_defs/pascal +39 -0
  437. polyfile/magic_defs/pbf +11 -0
  438. polyfile/magic_defs/pbm +8 -0
  439. polyfile/magic_defs/pc88 +24 -0
  440. polyfile/magic_defs/pc98 +77 -0
  441. polyfile/magic_defs/pci_ids +116 -0
  442. polyfile/magic_defs/pcjr +8 -0
  443. polyfile/magic_defs/pdf +51 -0
  444. polyfile/magic_defs/pdp +42 -0
  445. polyfile/magic_defs/perl +100 -0
  446. polyfile/magic_defs/pgf +52 -0
  447. polyfile/magic_defs/pgp +581 -0
  448. polyfile/magic_defs/pgp-binary-keys +388 -0
  449. polyfile/magic_defs/pkgadd +7 -0
  450. polyfile/magic_defs/plan9 +25 -0
  451. polyfile/magic_defs/playdate +57 -0
  452. polyfile/magic_defs/plus5 +18 -0
  453. polyfile/magic_defs/pmem +46 -0
  454. polyfile/magic_defs/polyfile_zip +5 -0
  455. polyfile/magic_defs/polyml +23 -0
  456. polyfile/magic_defs/printer +269 -0
  457. polyfile/magic_defs/project +10 -0
  458. polyfile/magic_defs/psdbms +14 -0
  459. polyfile/magic_defs/psl +14 -0
  460. polyfile/magic_defs/pulsar +13 -0
  461. polyfile/magic_defs/puzzle +17 -0
  462. polyfile/magic_defs/pwsafe +14 -0
  463. polyfile/magic_defs/pyramid +12 -0
  464. polyfile/magic_defs/python +305 -0
  465. polyfile/magic_defs/qt +30 -0
  466. polyfile/magic_defs/revision +66 -0
  467. polyfile/magic_defs/riff +840 -0
  468. polyfile/magic_defs/rinex +44 -0
  469. polyfile/magic_defs/ringdove +45 -0
  470. polyfile/magic_defs/rpi +52 -0
  471. polyfile/magic_defs/rpm +45 -0
  472. polyfile/magic_defs/rpmsg +7 -0
  473. polyfile/magic_defs/rst +11 -0
  474. polyfile/magic_defs/rtf +94 -0
  475. polyfile/magic_defs/ruby +55 -0
  476. polyfile/magic_defs/rust +21 -0
  477. polyfile/magic_defs/sc +7 -0
  478. polyfile/magic_defs/sccs +24 -0
  479. polyfile/magic_defs/scientific +144 -0
  480. polyfile/magic_defs/securitycerts +6 -0
  481. polyfile/magic_defs/selinux +24 -0
  482. polyfile/magic_defs/sendmail +37 -0
  483. polyfile/magic_defs/sequent +42 -0
  484. polyfile/magic_defs/sereal +35 -0
  485. polyfile/magic_defs/sgi +144 -0
  486. polyfile/magic_defs/sgml +161 -0
  487. polyfile/magic_defs/sharc +23 -0
  488. polyfile/magic_defs/sinclair +40 -0
  489. polyfile/magic_defs/sisu +18 -0
  490. polyfile/magic_defs/sketch +6 -0
  491. polyfile/magic_defs/smalltalk +25 -0
  492. polyfile/magic_defs/smile +34 -0
  493. polyfile/magic_defs/sniffer +482 -0
  494. polyfile/magic_defs/softquad +40 -0
  495. polyfile/magic_defs/sosi +40 -0
  496. polyfile/magic_defs/spec +21 -0
  497. polyfile/magic_defs/spectrum +184 -0
  498. polyfile/magic_defs/sql +288 -0
  499. polyfile/magic_defs/ssh +39 -0
  500. polyfile/magic_defs/ssl +20 -0
  501. polyfile/magic_defs/statistics +45 -0
  502. polyfile/magic_defs/subtitle +38 -0
  503. polyfile/magic_defs/sun +141 -0
  504. polyfile/magic_defs/svf +5 -0
  505. polyfile/magic_defs/sylk +36 -0
  506. polyfile/magic_defs/symbos +42 -0
  507. polyfile/magic_defs/sysex +429 -0
  508. polyfile/magic_defs/tcl +29 -0
  509. polyfile/magic_defs/teapot +6 -0
  510. polyfile/magic_defs/terminfo +63 -0
  511. polyfile/magic_defs/tex +141 -0
  512. polyfile/magic_defs/tgif +7 -0
  513. polyfile/magic_defs/ti-8x +239 -0
  514. polyfile/magic_defs/timezone +42 -0
  515. polyfile/magic_defs/tplink +95 -0
  516. polyfile/magic_defs/troff +38 -0
  517. polyfile/magic_defs/tuxedo +8 -0
  518. polyfile/magic_defs/typeset +8 -0
  519. polyfile/magic_defs/uf2 +72 -0
  520. polyfile/magic_defs/unicode +15 -0
  521. polyfile/magic_defs/unisig +12 -0
  522. polyfile/magic_defs/unknown +34 -0
  523. polyfile/magic_defs/usd +21 -0
  524. polyfile/magic_defs/uterus +16 -0
  525. polyfile/magic_defs/uuencode +28 -0
  526. polyfile/magic_defs/vacuum-cleaner +54 -0
  527. polyfile/magic_defs/varied.out +46 -0
  528. polyfile/magic_defs/varied.script +21 -0
  529. polyfile/magic_defs/vax +32 -0
  530. polyfile/magic_defs/vicar +17 -0
  531. polyfile/magic_defs/virtual +307 -0
  532. polyfile/magic_defs/virtutech +12 -0
  533. polyfile/magic_defs/visx +32 -0
  534. polyfile/magic_defs/vms +30 -0
  535. polyfile/magic_defs/vmware +6 -0
  536. polyfile/magic_defs/vorbis +155 -0
  537. polyfile/magic_defs/vxl +14 -0
  538. polyfile/magic_defs/warc +16 -0
  539. polyfile/magic_defs/weak +16 -0
  540. polyfile/magic_defs/web +18 -0
  541. polyfile/magic_defs/webassembly +17 -0
  542. polyfile/magic_defs/windows +1811 -0
  543. polyfile/magic_defs/wireless +7 -0
  544. polyfile/magic_defs/wordprocessors +630 -0
  545. polyfile/magic_defs/wsdl +23 -0
  546. polyfile/magic_defs/x68000 +25 -0
  547. polyfile/magic_defs/xdelta +13 -0
  548. polyfile/magic_defs/xenix +106 -0
  549. polyfile/magic_defs/xilinx +58 -0
  550. polyfile/magic_defs/xo65 +37 -0
  551. polyfile/magic_defs/xwindows +43 -0
  552. polyfile/magic_defs/yara +17 -0
  553. polyfile/magic_defs/zfs +96 -0
  554. polyfile/magic_defs/zilog +12 -0
  555. polyfile/magic_defs/zip +126 -0
  556. polyfile/magic_defs/zyxel +17 -0
  557. polyfile/nes.py +144 -0
  558. polyfile/nitf.py +15 -0
  559. polyfile/pdf.py +1264 -0
  560. polyfile/pickles.py +45 -0
  561. polyfile/polyfile.py +409 -0
  562. polyfile/profiling.py +115 -0
  563. polyfile/repl.py +624 -0
  564. polyfile/search.py +310 -0
  565. polyfile/serialization.py +323 -0
  566. polyfile/structmatcher.py +46 -0
  567. polyfile/structs.py +281 -0
  568. polyfile/templates/download.js +162 -0
  569. polyfile/templates/hexdump.css +268 -0
  570. polyfile/templates/hexdump.js +756 -0
  571. polyfile/templates/jquery-3.4.1.min.js +2 -0
  572. polyfile/templates/template.html +119 -0
  573. polyfile/wildcards.py +62 -0
  574. polyfile/zipmatcher.py +183 -0
  575. polyfile_weave-0.5.5.dist-info/METADATA +173 -0
  576. polyfile_weave-0.5.5.dist-info/RECORD +585 -0
  577. polyfile_weave-0.5.5.dist-info/WHEEL +5 -0
  578. polyfile_weave-0.5.5.dist-info/entry_points.txt +2 -0
  579. polyfile_weave-0.5.5.dist-info/licenses/LICENSE +202 -0
  580. polyfile_weave-0.5.5.dist-info/top_level.txt +2 -0
  581. polymerge/__init__.py +1 -0
  582. polymerge/__main__.py +296 -0
  583. polymerge/cfg.py +127 -0
  584. polymerge/polymerge.py +227 -0
  585. polymerge/polytracker.py +190 -0
polyfile/debugger.py ADDED
@@ -0,0 +1,1039 @@
1
+ from abc import ABC, abstractmethod
2
+ import atexit
3
+ from enum import Enum
4
+ from pathlib import Path
5
+ from pdb import Pdb
6
+ import sys
7
+ from typing import Any, Callable, Dict, Generic, Iterable, Iterator, List, Optional, Type, TypeVar, Union
8
+
9
+ from .polyfile import __copyright__, __license__, __version__, PARSERS, Match, Parser, ParserFunction, Submatch
10
+ from .magic import (
11
+ AbsoluteOffset, FailedTest, InvalidOffsetError, MagicMatcher, MagicTest, Offset, TestResult, TEST_TYPES
12
+ )
13
+ from .profiling import Profiler, Unprofiled, unprofiled
14
+ from .repl import ANSIColor, ANSIWriter, arg_completer, command, ExitREPL, log, REPL, SetCompleter, string_escape
15
+ from .wildcards import Wildcard
16
+
17
+
18
+ B = TypeVar("B", bound="Breakpoint")
19
+ T = TypeVar("T")
20
+
21
+
22
+ BREAKPOINT_TYPES: List[Type["Breakpoint"]] = []
23
+
24
+
25
+ class Breakpoint(ABC):
26
+ def __init_subclass__(cls, **kwargs):
27
+ super().__init_subclass__(**kwargs)
28
+ if cls.__name__ not in ("FailedBreakpoint", "MatchedBreakpoint"):
29
+ BREAKPOINT_TYPES.append(cls)
30
+
31
+ @abstractmethod
32
+ def should_break(
33
+ self,
34
+ test: MagicTest,
35
+ data: bytes,
36
+ absolute_offset: int,
37
+ parent_match: Optional[TestResult],
38
+ result: Optional[TestResult]
39
+ ) -> bool:
40
+ raise NotImplementedError()
41
+
42
+ @classmethod
43
+ @abstractmethod
44
+ def parse(cls: Type[B], command: str) -> Optional[B]:
45
+ raise NotImplementedError()
46
+
47
+ @staticmethod
48
+ def from_str(command: str) -> Optional["Breakpoint"]:
49
+ if command.startswith("!"):
50
+ return FailedBreakpoint.parse(command)
51
+ elif command.startswith("="):
52
+ return MatchedBreakpoint.parse(command)
53
+ for b_type in BREAKPOINT_TYPES:
54
+ parsed = b_type.parse(command)
55
+ if parsed is not None:
56
+ return parsed
57
+ return None
58
+
59
+ @classmethod
60
+ @abstractmethod
61
+ def print_usage(cls, debugger: "Debugger"):
62
+ raise NotImplementedError()
63
+
64
+ @abstractmethod
65
+ def __str__(self):
66
+ raise NotImplementedError()
67
+
68
+
69
+ class FailedBreakpoint(Breakpoint):
70
+ def __init__(self, parent: Breakpoint):
71
+ self.parent: Breakpoint = parent
72
+
73
+ def should_break(
74
+ self,
75
+ test: MagicTest,
76
+ data: bytes,
77
+ absolute_offset: int,
78
+ parent_match: Optional[TestResult],
79
+ result: Optional[TestResult]
80
+ ) -> bool:
81
+ return (result is None or isinstance(result, FailedTest)) and self.parent.should_break(
82
+ test, data, absolute_offset, parent_match, result
83
+ )
84
+
85
+ @classmethod
86
+ def parse(cls: B, command: str) -> Optional[B]:
87
+ if not command.startswith("!"):
88
+ return None
89
+ parent = Breakpoint.from_str(command[1:])
90
+ if parent is not None:
91
+ return FailedBreakpoint(parent)
92
+ else:
93
+ return None
94
+
95
+ @classmethod
96
+ def print_usage(cls, debugger: "Debugger"):
97
+ pass
98
+
99
+ def __str__(self):
100
+ return f"[FAILED] {self.parent!s}"
101
+
102
+
103
+ class MatchedBreakpoint(Breakpoint):
104
+ def __init__(self, parent: Breakpoint):
105
+ self.parent: Breakpoint = parent
106
+
107
+ def should_break(
108
+ self,
109
+ test: MagicTest,
110
+ data: bytes,
111
+ absolute_offset: int,
112
+ parent_match: Optional[TestResult],
113
+ result: Optional[TestResult]
114
+ ) -> bool:
115
+ return result is not None and not isinstance(result, FailedTest) and self.parent.should_break(
116
+ test, data, absolute_offset, parent_match, result
117
+ )
118
+
119
+ @classmethod
120
+ def parse(cls: B, command: str) -> Optional[B]:
121
+ if not command.startswith("="):
122
+ return None
123
+ parent = Breakpoint.from_str(command[1:])
124
+ if parent is not None:
125
+ return MatchedBreakpoint(parent)
126
+ else:
127
+ return None
128
+
129
+ @classmethod
130
+ def print_usage(cls, debugger: "Debugger"):
131
+ pass
132
+
133
+ def __str__(self):
134
+ return f"[MATCHED] {self.parent!s}"
135
+
136
+
137
+ class MimeBreakpoint(Breakpoint):
138
+ def __init__(self, mimetype: str):
139
+ self.mimetype: str = mimetype
140
+ self.pattern: Wildcard = Wildcard.parse(mimetype)
141
+
142
+ def should_break(
143
+ self,
144
+ test: MagicTest,
145
+ data: bytes,
146
+ absolute_offset: int,
147
+ parent_match: Optional[TestResult],
148
+ result: Optional[TestResult]
149
+ ) -> bool:
150
+ return self.pattern.is_contained_in(test.mimetypes())
151
+
152
+ @classmethod
153
+ def parse(cls: Type[B], command: str) -> Optional[B]:
154
+ if command.lower().startswith("mime:"):
155
+ return MimeBreakpoint(command[len("mime:"):])
156
+ return None
157
+
158
+ @classmethod
159
+ def print_usage(cls, debugger: "Debugger"):
160
+ debugger.write("b MIME:MIMETYPE", color=ANSIColor.MAGENTA)
161
+ debugger.write(" to break when a test is capable of matching that mimetype.\nThe ")
162
+ debugger.write("MIMETYPE", color=ANSIColor.MAGENTA)
163
+ debugger.write(" can include the ")
164
+ debugger.write("*", color=ANSIColor.MAGENTA)
165
+ debugger.write(" and ")
166
+ debugger.write("?", color=ANSIColor.MAGENTA)
167
+ debugger.write(" wildcards.\nFor example:\n")
168
+ debugger.write(" b MIME:application/pdf\n b MIME:*pdf\n", color=ANSIColor.MAGENTA)
169
+
170
+ def __str__(self):
171
+ return f"Breakpoint: Matching for MIME {self.mimetype}"
172
+
173
+
174
+ class ExtensionBreakpoint(Breakpoint):
175
+ def __init__(self, ext: str):
176
+ self.ext: str = ext
177
+
178
+ def should_break(
179
+ self,
180
+ test: MagicTest,
181
+ data: bytes,
182
+ absolute_offset: int,
183
+ parent_match: Optional[TestResult],
184
+ result: Optional[TestResult]
185
+ ) -> bool:
186
+ return self.ext in test.all_extensions()
187
+
188
+ @classmethod
189
+ def parse(cls: Type[B], command: str) -> Optional[B]:
190
+ if command.lower().startswith("ext:"):
191
+ return ExtensionBreakpoint(command[len("ext:"):])
192
+ return None
193
+
194
+ @classmethod
195
+ def print_usage(cls, debugger: "Debugger") -> str:
196
+ debugger.write("b EXT:EXTENSION", color=ANSIColor.MAGENTA)
197
+ debugger.write(" to break when a test is capable of matching that extension.\nFor example:\n")
198
+ debugger.write(" b EXT:pdf\n", color=ANSIColor.MAGENTA)
199
+
200
+ def __str__(self):
201
+ return f"Breakpoint: Matching for extension {self.ext}"
202
+
203
+
204
+ class FileBreakpoint(Breakpoint):
205
+ def __init__(self, filename: str, line: int):
206
+ self.filename: str = filename
207
+ self.line: int = line
208
+
209
+ def should_break(
210
+ self,
211
+ test: MagicTest,
212
+ data: bytes,
213
+ absolute_offset: int,
214
+ parent_match: Optional[TestResult],
215
+ result: TestResult
216
+ ) -> bool:
217
+ if test.source_info is None or test.source_info.line != self.line:
218
+ return False
219
+ if "/" in self.filename:
220
+ # it is a file path
221
+ return str(test.source_info.path) == self.filename
222
+ else:
223
+ # treat it like a filename
224
+ return test.source_info.path.name == self.filename
225
+
226
+ @classmethod
227
+ def parse(cls: Type[B], command: str) -> Optional[B]:
228
+ filename, *remainder = command.split(":")
229
+ if not remainder:
230
+ return None
231
+ try:
232
+ line = int("".join(remainder))
233
+ except ValueError:
234
+ return None
235
+ if line <= 0:
236
+ return None
237
+ return FileBreakpoint(filename, line)
238
+
239
+ @classmethod
240
+ def print_usage(cls, debugger: "Debugger"):
241
+ debugger.write("b FILENAME:LINE_NO", color=ANSIColor.MAGENTA)
242
+ debugger.write(" to break when the line of the given magic file is reached.\nFor example:\n")
243
+ debugger.write(" b archive:525\n", color=ANSIColor.MAGENTA)
244
+ debugger.write("will break on the test at line 525 of the archive DSL file.\n")
245
+
246
+ def __str__(self):
247
+ return f"Breakpoint: {self.filename} line {self.line}"
248
+
249
+
250
+ class InstrumentedTest:
251
+ def __init__(self, test: Type[MagicTest], debugger: "Debugger"):
252
+ self.test: Type[MagicTest] = test
253
+ self.debugger: Debugger = debugger
254
+ if "test" in test.__dict__:
255
+ self.original_test: Optional[Callable[[...], Optional[TestResult]]] = test.test
256
+
257
+ def wrapper(test_instance, *args, **kwargs) -> Optional[TestResult]:
258
+ # if self.original_test is None:
259
+ # # this is a NOOP
260
+ # return self.test.test(test_instance, *args, **kwargs)
261
+ return self.debugger.debug(self, test_instance, *args, **kwargs)
262
+
263
+ test.test = wrapper
264
+ else:
265
+ self.original_test = None
266
+
267
+ @property
268
+ def enabled(self) -> bool:
269
+ return self.original_test is not None
270
+
271
+ def uninstrument(self):
272
+ if self.original_test is not None:
273
+ # we are still assigned to the test function, so reset it
274
+ self.test.test = self.original_test
275
+ self.original_test = None
276
+
277
+
278
+ class InstrumentedParser:
279
+ def __init__(self, parser: Parser, debugger: "Debugger"):
280
+ self.parser: Type[Parser] = parser
281
+ self.debugger: Debugger = debugger
282
+ self.original_parser: Optional[ParserFunction] = parser.parse
283
+
284
+ def wrapper(parser_instance, *args, **kwargs) -> Iterator[Submatch]:
285
+ yield from self.debugger.debug_parse(self, parser_instance, *args, **kwargs)
286
+
287
+ parser.parse = wrapper
288
+
289
+ @property
290
+ def enalbed(self) -> bool:
291
+ return self.original_parser is not None
292
+
293
+ def uninstrument(self):
294
+ if self.original_parser is not None:
295
+ self.parser.parse = self.original_parser
296
+ self.original_parser = None
297
+
298
+
299
+ class StepMode(Enum):
300
+ RUNNING = 0
301
+ SINGLE_STEPPING = 1
302
+ NEXT = 2
303
+
304
+
305
+ T = TypeVar("T")
306
+
307
+
308
+ class Variable(Generic[T]):
309
+ def __init__(self, possibilities: Iterable[T], value: T):
310
+ self.possibilities: List[T] = list(possibilities)
311
+ self._value: T = value
312
+ self.value = value
313
+
314
+ @property
315
+ def value(self) -> T:
316
+ return self._value
317
+
318
+ @value.setter
319
+ def value(self, new_value: T):
320
+ if new_value not in self.possibilities:
321
+ raise ValueError(f"invalid value {new_value!r}; must be one of {self.possibilities!r}")
322
+ self._value = new_value
323
+
324
+ def parse(self, value: str) -> T:
325
+ value = value.strip().lower()
326
+ for p in self.possibilities:
327
+ if str(p).lower() == value:
328
+ return p
329
+ raise ValueError(f"Invalid value {value!r}; must be one of {', '.join(map(str, self.possibilities))}")
330
+
331
+ def __str__(self):
332
+ return str(self.value)
333
+
334
+ def __repr__(self):
335
+ return f"{self.__class__.__name__}()"
336
+
337
+
338
+ class BooleanVariable(Variable[bool]):
339
+ def __init__(self, value: bool):
340
+ super().__init__((True, False), value)
341
+
342
+ def parse(self, value: str) -> T:
343
+ try:
344
+ return super().parse(value)
345
+ except ValueError:
346
+ pass
347
+ value = value.strip().lower()
348
+ if value == "0" or value == "f":
349
+ return False
350
+ return bool(value)
351
+
352
+ def __bool__(self):
353
+ return self.value
354
+
355
+
356
+ class BreakOnSubmatching(BooleanVariable):
357
+ def __init__(self, value: bool, debugger: "Debugger"):
358
+ self.debugger: Debugger = debugger
359
+ super().__init__(value)
360
+
361
+ @Variable.value.setter
362
+ def value(self, new_value):
363
+ Variable.value.fset(self, new_value)
364
+ if self.debugger.enabled:
365
+ # disable and re-enable the debugger to update the instrumentation
366
+ self.debugger._uninstrument()
367
+ self.debugger._instrument()
368
+
369
+
370
+ class Profile(BooleanVariable):
371
+ def __init__(self, value: bool, debugger: "Debugger"):
372
+ self.debugger: Debugger = debugger
373
+ self._registered_callback = False
374
+ super().__init__(value)
375
+
376
+ @Variable.value.setter
377
+ def value(self, new_value):
378
+ Variable.value.fset(self, new_value)
379
+ if new_value and not self._registered_callback:
380
+ self._registered_callback = True
381
+ atexit.register(Debugger.profile_command, self.debugger, "")
382
+
383
+
384
+ class Debugger(REPL):
385
+ def __init__(self, break_on_parsing: bool = True):
386
+ super().__init__(name="the debugger")
387
+ self.instrumented_tests: List[InstrumentedTest] = []
388
+ self.breakpoints: List[Breakpoint] = []
389
+ self._entries: int = 0
390
+ self.step_mode: StepMode = StepMode.RUNNING
391
+ self.last_command: Optional[str] = None
392
+ self.last_test: Optional[MagicTest] = None
393
+ self.last_parent_match: Optional[MagicTest] = None
394
+ self.data: bytes = b""
395
+ self.last_offset: int = 0
396
+ self.last_result: Optional[TestResult] = None
397
+ self.repl_test: Optional[MagicTest] = None
398
+ self.instrumented_parsers: List[InstrumentedParser] = []
399
+ self.break_on_submatching: BreakOnSubmatching = BreakOnSubmatching(break_on_parsing, self)
400
+ self.profile: Profile = Profile(False, self)
401
+ self.variables_by_name: Dict[str, Variable] = {
402
+ "break_on_parsing": self.break_on_submatching,
403
+ "profile": self.profile
404
+ }
405
+ self.variable_descriptions: Dict[str, str] = {
406
+ "break_on_parsing": "Break when a PolyFile parser is about to be invoked and debug using PDB (default=True;"
407
+ " disable from the command line with `--no-debug-python`)",
408
+ "profile": "Profile the performance of each magic test that is run (default=False)"
409
+ }
410
+ self.profile_results: Dict[Union[MagicTest, Type[Parser]], float] = {}
411
+ self._pdb: Optional[Pdb] = None
412
+ self._debug_next: bool = False
413
+
414
+ def save_context(self):
415
+ class DebugContext:
416
+ def __init__(self, debugger: Debugger):
417
+ self.debugger: Debugger = debugger
418
+ self.__saved_state = {}
419
+
420
+ def __enter__(self) -> Debugger:
421
+ self.__saved_state = dict(self.debugger.__dict__)
422
+ return self.debugger
423
+
424
+ def __exit__(self, exc_type, exc_val, exc_tb):
425
+ self.debugger.__dict__ = self.__saved_state
426
+
427
+ return DebugContext(self)
428
+
429
+ @property
430
+ def enabled(self) -> bool:
431
+ return any(t.enabled for t in self.instrumented_tests)
432
+
433
+ def _uninstrument(self):
434
+ # Uninstrument any existing instrumentation:
435
+ for t in self.instrumented_tests:
436
+ t.uninstrument()
437
+ self.instrumented_tests = []
438
+ for m in self.instrumented_parsers:
439
+ m.uninstrument()
440
+ self.instrumented_parsers = []
441
+
442
+ def _instrument(self):
443
+ # Instrument all of the MagicTest.test functions:
444
+ for test in TEST_TYPES:
445
+ if "test" in test.__dict__:
446
+ # this class actually implements the test() function
447
+ self.instrumented_tests.append(InstrumentedTest(test, self))
448
+ if self.break_on_submatching.value:
449
+ for parsers in PARSERS.values():
450
+ for parser in parsers:
451
+ self.instrumented_parsers.append(InstrumentedParser(parser, self))
452
+
453
+ @enabled.setter
454
+ def enabled(self, is_enabled: bool):
455
+ was_enabled = self.enabled
456
+ self._uninstrument()
457
+ if is_enabled:
458
+ self._instrument()
459
+ self.load_history()
460
+ self.write(f"PolyFile {__version__}\n", color=ANSIColor.MAGENTA, bold=True)
461
+ self.write(f"{__copyright__}\n{__license__}\n\nFor help, type \"help\".\n")
462
+ self.repl()
463
+ elif was_enabled:
464
+ # we are now disabled, so store our history
465
+ self.store_history()
466
+
467
+ def __enter__(self) -> "Debugger":
468
+ self._entries += 1
469
+ if self._entries == 1:
470
+ self.enabled = True
471
+ return self
472
+
473
+ def __exit__(self, exc_type, exc_val, exc_tb):
474
+ self._entries -= 1
475
+ if self._entries == 0:
476
+ self.enabled = False
477
+
478
+ def should_break(self) -> bool:
479
+ return self.step_mode == StepMode.SINGLE_STEPPING or (
480
+ self.step_mode == StepMode.NEXT and self.last_result
481
+ ) or any(
482
+ b.should_break(self.last_test, self.data, self.last_offset, self.last_parent_match, self.last_result)
483
+ for b in self.breakpoints
484
+ )
485
+
486
+ def write_test(self, test: MagicTest, is_current_test: bool = False):
487
+ writer = ANSIWriter(use_ansi=sys.stdout.isatty())
488
+ if self.profile.value and test in self.profile_results:
489
+ pre_mime_text = f"\t⏱ {int(self.profile_results[test] + 0.5)}ms"
490
+ else:
491
+ pre_mime_text = ""
492
+ test.write(writer, is_current_test=is_current_test, pre_mime_text=pre_mime_text)
493
+ super().write(str(writer))
494
+
495
+ def write(self, message: Any, bold: bool = False, dim: bool = False, color: Optional[ANSIColor] = None):
496
+ if sys.stdout.isatty() and isinstance(message, MagicTest):
497
+ self.write_test(message)
498
+ else:
499
+ super().write(message=message, bold=bold, dim=dim, color=color)
500
+
501
+ def print_context(self, data: bytes, offset: int, context_bytes: int = 32, num_bytes: int = 1):
502
+ writer = ANSIWriter(use_ansi=sys.stdout.isatty())
503
+ writer.write_context(data, offset, context_bytes, num_bytes)
504
+ super().write(message=str(writer))
505
+
506
+ def _debug(self, func: Callable[[Any], T], *args, **kwargs) -> T:
507
+ if self.profile.value:
508
+ self.write("Warning:", bold=True, color=ANSIColor.RED)
509
+ self.write(" Profiling will be disabled for this test while debugging!\n")
510
+ with Unprofiled():
511
+ # check if there is already a debugger attached, most likely an IDE like PyCharm;
512
+ # if so, use that debugger instead of creating our own instance of pdb
513
+ external_debugger = sys.gettrace() is not None or self._pdb is not None
514
+ if not external_debugger:
515
+ self._pdb = Pdb(skip=["polyfile.magic_debugger", "polyfile.magic"])
516
+ if sys.stderr.isatty():
517
+ self._pdb.prompt = "\001\u001b[1m\002(polyfile-Pdb)\001\u001b[0m\002 "
518
+ else:
519
+ self._pdb.prompt = "(polyfile-Pdb) "
520
+ try:
521
+ result = self._pdb.runcall(func, *args, **kwargs)
522
+ finally:
523
+ self._pdb = None
524
+ else:
525
+ breakpoint()
526
+ # This next line will invoke the test:
527
+ result = func(*args, **kwargs)
528
+ return result
529
+
530
+ def debug(
531
+ self,
532
+ instrumented_test: InstrumentedTest,
533
+ test: MagicTest,
534
+ data: bytes,
535
+ absolute_offset: int,
536
+ parent_match: Optional[TestResult]
537
+ ) -> Optional[TestResult]:
538
+ profiler = Profiler()
539
+ if instrumented_test.original_test is None:
540
+ test_func = instrumented_test.test.test
541
+ else:
542
+ test_func = instrumented_test.original_test
543
+ if self._debug_next:
544
+ self._debug_next = False
545
+ result: TestResult = self._debug(test_func, test, data, absolute_offset, parent_match)
546
+ else:
547
+ with profiler:
548
+ result = test_func(test, data, absolute_offset, parent_match)
549
+ if self.profile.value:
550
+ self.profile_results[test] = profiler.elapsed_ms
551
+ if self.repl_test is test:
552
+ # this is a one-off test run from the REPL, so do not save its results
553
+ return result
554
+ self.last_result = result
555
+ self.last_test = test
556
+ self.data = data
557
+ self.last_offset = absolute_offset
558
+ self.last_parent_match = parent_match
559
+ if self.should_break():
560
+ self.repl()
561
+ return self.last_result
562
+
563
+ @unprofiled
564
+ def print_where(
565
+ self,
566
+ test: Optional[MagicTest] = None,
567
+ offset: Optional[int] = None,
568
+ parent_match: Optional[TestResult] = None,
569
+ result: Optional[TestResult] = None
570
+ ):
571
+ if test is None:
572
+ test = self.last_test
573
+ if test is None:
574
+ self.write("The first test has not yet been run.\n", color=ANSIColor.RED)
575
+ self.write("Use `step`, `next`, or `run` to start testing.\n")
576
+ return
577
+ if offset is None:
578
+ offset = self.last_offset
579
+ if parent_match is None:
580
+ parent_match = self.last_parent_match
581
+ if result is None:
582
+ result = self.last_result
583
+ wrote_breakpoints = False
584
+ for b in self.breakpoints:
585
+ if b.should_break(test, self.data, offset, parent_match, result):
586
+ self.write(b, color=ANSIColor.MAGENTA)
587
+ self.write("\n")
588
+ wrote_breakpoints = True
589
+ if wrote_breakpoints:
590
+ self.write("\n")
591
+ test_stack = [test]
592
+ while test_stack[-1].parent is not None:
593
+ test_stack.append(test_stack[-1].parent)
594
+ for i, t in enumerate(reversed(test_stack)):
595
+ if i == len(test_stack) - 1:
596
+ self.write_test(t, is_current_test=True)
597
+ else:
598
+ self.write_test(t)
599
+ test_stack = list(reversed(test.children))
600
+ descendants = []
601
+ while test_stack:
602
+ descendant = test_stack.pop()
603
+ if descendant.can_match_mime:
604
+ descendants.append(descendant)
605
+ test_stack.extend(reversed(descendant.children))
606
+ for t in descendants:
607
+ self.write_test(t)
608
+ self.write("\n")
609
+ data_offset = offset
610
+ if not isinstance(test.offset, AbsoluteOffset):
611
+ try:
612
+ data_offset = test.offset.to_absolute(self.data, parent_match)
613
+ self.write(str(test.offset), color=ANSIColor.BLUE)
614
+ self.write(" = byte offset ", dim=True)
615
+ self.write(f"{data_offset!s}\n", bold=True)
616
+ except InvalidOffsetError as e:
617
+ self.write(f"{e!s}\n", color=ANSIColor.RED)
618
+ if result is not None and hasattr(result, "length"):
619
+ context_bytes = result.length
620
+ else:
621
+ context_bytes = 1
622
+ self.print_context(self.data, data_offset, num_bytes=context_bytes)
623
+ if result is not None:
624
+ if not result:
625
+ self.write("Test failed.\n", color=ANSIColor.RED)
626
+ if isinstance(result, FailedTest):
627
+ self.write(result.message)
628
+ self.write("\n")
629
+ else:
630
+ self.write("Test succeeded.\n", color=ANSIColor.GREEN)
631
+
632
+ def print_match(self, match: Match):
633
+ obj = match.to_obj()
634
+ self.write("{\n", bold=True)
635
+ for key, value in obj.items():
636
+ if isinstance(value, list):
637
+ # TODO: Maybe implement list printing later.
638
+ # I don't think there will be lists here currently, thouh.
639
+ continue
640
+ self.write(f" {key!r}", color=ANSIColor.BLUE)
641
+ self.write(": ", bold=True)
642
+ if isinstance(value, int) or isinstance(value, float):
643
+ self.write(str(value))
644
+ else:
645
+ self.write(repr(value), color=ANSIColor.GREEN)
646
+ self.write(",\n", bold=True)
647
+ self.write("}\n", bold=True)
648
+
649
+ def debug_parse(self, instrumented_parser: InstrumentedParser, file_stream, match: Match) -> Iterator[Submatch]:
650
+ log.clear_status()
651
+
652
+ if instrumented_parser.original_parser is None:
653
+ parse = instrumented_parser.parser.parse
654
+ else:
655
+ parse = instrumented_parser.original_parser
656
+
657
+ def print_location():
658
+ self.write(f"{file_stream.name}", dim=True, color=ANSIColor.CYAN)
659
+ self.write(":", dim=True)
660
+ self.write(f"{file_stream.tell()} ", dim=True, color=ANSIColor.CYAN)
661
+
662
+ if self._pdb is not None:
663
+ # We are already debugging!
664
+ print_location()
665
+ self.write(f"Parsing for submatches using {instrumented_parser.parser!s}.\n")
666
+ profiler = Profiler()
667
+ with profiler:
668
+ yield from parse(file_stream, match)
669
+ if self.profile.value:
670
+ self.profile_results[instrumented_parser.parser] = profiler.elapsed_ms
671
+ return
672
+ with Unprofiled():
673
+ self.print_match(match)
674
+ print_location()
675
+ self.write(f"About to parse for submatches using {instrumented_parser.parser!s}.\n")
676
+ buffer = ANSIWriter(use_ansi=sys.stderr.isatty(), escape_for_readline=True)
677
+ buffer.write("Debug using PDB? ")
678
+ buffer.write("(disable this prompt with `", dim=True)
679
+ buffer.write("set ", color=ANSIColor.BLUE)
680
+ buffer.write("break_on_parsing ", color=ANSIColor.GREEN)
681
+ buffer.write("False", color=ANSIColor.CYAN)
682
+ buffer.write("`)", dim=True)
683
+ if not self.prompt(str(buffer), default=False):
684
+ with Profiler() as p:
685
+ yield from parse(file_stream, match)
686
+ if self.profile.value:
687
+ self.profile_results[instrumented_parser.parser] = p.elapsed_ms
688
+ return
689
+ try:
690
+ if self.profile.value:
691
+ self.write("Warning:", bold=True, color=ANSIColor.RED)
692
+ self.write(" Profiling will be disabled for this parser while debugging!\n")
693
+ with Unprofiled():
694
+ # check if there is already a debugger attached, most likely an IDE like PyCharm;
695
+ # if so, use that debugger instead of creating our own instance of pdb
696
+ external_debugger = sys.gettrace() is not None
697
+ if not external_debugger:
698
+ self._pdb = Pdb(skip=["polyfile.magic_debugger", "polyfile.magic"])
699
+ if sys.stderr.isatty():
700
+ self._pdb.prompt = "\001\u001b[1m\002(polyfile-Pdb)\001\u001b[0m\002 "
701
+ else:
702
+ self._pdb.prompt = "(polyfile-Pdb) "
703
+ generator = parse(file_stream, match)
704
+ while True:
705
+ try:
706
+ if external_debugger:
707
+ breakpoint()
708
+ # This next line will invoke the parser:
709
+ result = next(generator)
710
+ else:
711
+ result = self._pdb.runcall(next, generator)
712
+ self.write(f"Got a submatch:\n", dim=True)
713
+ self.print_match(result)
714
+ yield result
715
+ except StopIteration:
716
+ self.write(f"Yielded all submatches from {match.__class__.__name__} at offset {match.offset}."
717
+ f"\n")
718
+ break
719
+ print_location()
720
+ if not self.prompt("Continue debugging the next submatch?", default=True):
721
+ if self.prompt("Print the remaining submatches?", default=False):
722
+ for result in generator:
723
+ self.print_match(result)
724
+ yield result
725
+ else:
726
+ yield from generator
727
+ break
728
+ finally:
729
+ self._pdb = None
730
+
731
+ def before_prompt(self):
732
+ if self.last_test is not None:
733
+ self.print_where()
734
+
735
+ @command(name="continue", allows_abbreviation=True, help="continue execution until the next breakpoint is hit",
736
+ aliases=("run",))
737
+ def cont(self, arguments: str):
738
+ self.step_mode = StepMode.RUNNING
739
+ self.last_command = command
740
+ raise ExitREPL()
741
+
742
+ @command(allows_abbreviation=True, help="step through a single magic test")
743
+ def step(self, arguments: str):
744
+ self.step_mode = StepMode.SINGLE_STEPPING
745
+ self.last_command = command
746
+ raise ExitREPL()
747
+
748
+ @command(allows_abbreviation=False, name="debug_and_rerun", help="re-run the last test and debug in PDB")
749
+ def debug_and_rerun(self, arguments: str):
750
+ self.last_command = command
751
+ if self.last_test is None:
752
+ self.write("Error: A test has not yet been run\n", color=ANSIColor.RED)
753
+ return
754
+ test = self.last_test.__class__.test
755
+ # Is the test instrumented? If so, find the original version!
756
+ for instrumented in self.instrumented_tests:
757
+ if instrumented.test.test is test:
758
+ test = instrumented.original_test
759
+ break
760
+ self._debug(test, self.last_test, self.data, self.last_offset, self.last_parent_match)
761
+
762
+ @command(allows_abbreviation=False, name="debug_and_continue", aliases=("debug", "debug_and_cont"),
763
+ help="continue while debugging in PDB")
764
+ def debug_and_continue(self, arguments: str):
765
+ if sys.gettrace() is None and self._pdb is None:
766
+ self.write("Error: ", color=ANSIColor.RED)
767
+ self.write("`", dim=True)
768
+ self.write("debug_and_continue", color=ANSIColor.BLUE)
769
+ self.write("`", dim=True)
770
+ self.write(" can only be called when running from an external debugger like PDB or PyCharm",
771
+ color=ANSIColor.RED)
772
+ return
773
+ self.step_mode = StepMode.RUNNING
774
+ self.last_command = command
775
+ try:
776
+ raise ExitREPL()
777
+ finally:
778
+ breakpoint()
779
+
780
+ @command(allows_abbreviation=False, help="step into the next magic test and debug in PDB")
781
+ def debug_and_step(self, arguments: str):
782
+ self.step_mode = StepMode.SINGLE_STEPPING
783
+ self.last_command = command
784
+ self._debug_next = True
785
+ raise ExitREPL()
786
+
787
+ @command(allows_abbreviation=True, help="continue execution until the next test that matches")
788
+ def next(self, arguments: str):
789
+ self.step_mode = StepMode.NEXT
790
+ self.last_command = command
791
+ raise ExitREPL()
792
+
793
+ @command(allows_abbreviation=True,
794
+ help="print the context of the current magic test",
795
+ aliases=("info stack", "backtrace"))
796
+ def where(self, arguments: str):
797
+ self.print_where()
798
+
799
+ @command(allows_abbreviation=False, name="profile", help="print current profiling results (to enable profiling, "
800
+ "use `set profile True`)", )
801
+ def profile_command(self, arguments: str):
802
+ if not self.profile_results:
803
+ if not self.profile.value:
804
+ self.write("Profiling is disabled.\n", color=ANSIColor.RED)
805
+ self.write("Enable it by running `set profile True`.\n")
806
+ else:
807
+ self.write("No profiling data yet.\n")
808
+ return
809
+ self.write("Profile Results:\n", bold=True)
810
+ tests = sorted([(runtime, test) for test, runtime in self.profile_results.items()], reverse=True,
811
+ key=lambda x: x[0])
812
+ max_text_width = 0
813
+ for runtime, test in tests:
814
+ if isinstance(test, MagicTest):
815
+ if test.source_info is not None and test.source_info.original_line is not None:
816
+ max_text_width = max(max_text_width,
817
+ len(test.source_info.path.name) + 1 + len(str(test.source_info.line)))
818
+ else:
819
+ max_text_width = max(max_text_width, test.level + len(str(test.offset)))
820
+ else:
821
+ max_text_width = max(max_text_width, len(str(test)))
822
+ for runtime, test in tests:
823
+ if isinstance(test, MagicTest):
824
+ self.write("🪄 ")
825
+ if test.source_info is not None and test.source_info.original_line is not None:
826
+ self.write(test.source_info.path.name, dim=True, color=ANSIColor.CYAN)
827
+ self.write(":", dim=True)
828
+ self.write(test.source_info.line, dim=True, color=ANSIColor.CYAN)
829
+ padding = max_text_width - (len(test.source_info.path.name) + 1 + len(str(test.source_info.line)))
830
+ else:
831
+ self.write(f"{'>' * test.level}{test.offset!s}", color=ANSIColor.BLUE)
832
+ padding = max_text_width - test.level - len(str(test.offset))
833
+ else:
834
+ self.write("🖥 ")
835
+ self.write(str(test), color=ANSIColor.BLUE)
836
+ padding = max_text_width - len(str(test))
837
+ self.write(" " * padding)
838
+ if runtime >= 1.0:
839
+ self.write(f" ⏱ {int(runtime + 0.5)}ms\n")
840
+ else:
841
+ self.write(f" ⏱ {runtime:.2f}ms\n")
842
+
843
+ @command(allows_abbreviation=True, help="test the following libmagic DSL test at the current position")
844
+ def test(self, args: str):
845
+ if args:
846
+ if self.last_test is None:
847
+ self.write("The first test has not yet been run.\n", color=ANSIColor.RED)
848
+ self.write("Use `step`, `next`, or `run` to start testing.\n")
849
+ return
850
+ try:
851
+ test = MagicMatcher.parse_test(args, Path("STDIN"), 1, parent=self.last_test)
852
+ if test is None:
853
+ self.write("Error parsing test\n", color=ANSIColor.RED)
854
+ return
855
+ try:
856
+ with self.save_context():
857
+ self.repl_test = test
858
+ if test.parent is None:
859
+ self.last_result = None
860
+ self.last_offset = 0
861
+ result = test.test(self.data, self.last_offset, parent_match=self.last_result)
862
+ finally:
863
+ if test.parent is not None:
864
+ test.parent.children.remove(test)
865
+ self.print_where(
866
+ test=test, offset=self.last_offset, parent_match=self.last_result, result=result
867
+ )
868
+ except ValueError as e:
869
+ self.write(f"{e!s}\n", color=ANSIColor.RED)
870
+ else:
871
+ self.write("Usage: ", dim=True)
872
+ self.write("test", bold=True, color=ANSIColor.BLUE)
873
+ self.write(" LIBMAGIC DSL TEST\n", bold=True)
874
+ self.write("Attempt to run the given test.\n\nExample:\n")
875
+ self.write("test", bold=True, color=ANSIColor.BLUE)
876
+ self.write(" 0 search \\x50\\x4b\\x05\\x06 ZIP EOCD record\n", bold=True)
877
+
878
+ @command(allows_abbreviation=True, help="print the computed absolute offset of the following libmagic DSL offset")
879
+ def print(self, args: str):
880
+ if args:
881
+ if self.last_test is None:
882
+ self.write("The first test has not yet been run.\n", color=ANSIColor.RED)
883
+ self.write("Use `step`, `next`, or `run` to start testing.\n")
884
+ return
885
+ try:
886
+ dsl_offset = Offset.parse(args)
887
+ except ValueError as e:
888
+ self.write(f"{e!s}\n", color=ANSIColor.RED)
889
+ return
890
+ try:
891
+ absolute = dsl_offset.to_absolute(self.data, self.last_result)
892
+ self.write(f"{absolute}\n", bold=True)
893
+ self.print_context(self.data, absolute)
894
+ except InvalidOffsetError as e:
895
+ self.write(f"{e!s}\n", color=ANSIColor.RED)
896
+ return
897
+ else:
898
+ self.write("Usage: ", dim=True)
899
+ self.write("print", bold=True, color=ANSIColor.BLUE)
900
+ self.write(" LIBMAGIC DSL OFFSET\n", bold=True)
901
+ self.write("Calculate the absolute offset for the given DSL offset.\n\nExample:\n")
902
+ self.write("print", bold=True, color=ANSIColor.BLUE)
903
+ self.write(" (&0x7c.l+0x26)\n", bold=True)
904
+
905
+ @command(allows_abbreviation=True, help="list the current breakpoints or add a new one")
906
+ def breakpoint(self, args: str):
907
+ if args:
908
+ parsed = Breakpoint.from_str(args)
909
+ if parsed is None:
910
+ self.write("Error: Invalid breakpoint pattern\n", color=ANSIColor.RED)
911
+ else:
912
+ self.write(parsed, color=ANSIColor.MAGENTA)
913
+ self.write("\n")
914
+ self.breakpoints.append(parsed)
915
+ else:
916
+ if self.breakpoints:
917
+ for i, b in enumerate(self.breakpoints):
918
+ self.write(f"{i}:\t", dim=True)
919
+ self.write(b, color=ANSIColor.MAGENTA)
920
+ self.write("\n")
921
+ else:
922
+ self.write("No breakpoints set.\n", color=ANSIColor.RED)
923
+ for b_type in BREAKPOINT_TYPES:
924
+ b_type.print_usage(self)
925
+ self.write("\n")
926
+ self.write("\nBy default, breakpoints will trigger whenever a matching test is run.\n\n"
927
+ "Prepend a breakpoint with ")
928
+ self.write("!", bold=True)
929
+ self.write(" to only trigger the breakpoint when the test fails.\nFor Example:\n")
930
+ self.write(" b !MIME:application/zip\n", color=ANSIColor.MAGENTA)
931
+ self.write("will only trigger if a test that could match a ZIP file failed.\n\n"
932
+ "Prepend a breakpoint with ")
933
+ self.write("=", bold=True)
934
+ self.write(" to only trigger the breakpoint when the test passes.\n For example:\n")
935
+ self.write(" b =archive:1337\n", color=ANSIColor.MAGENTA)
936
+ self.write("will only trigger if the test on line 1337 of the archive DSL matched.\n\n")
937
+
938
+ @arg_completer(for_command="delete")
939
+ def delete_completer(self, index: int, arg: str, prev_arguments: Iterable[str]):
940
+ if index == 0:
941
+ return SetCompleter(lambda: map(str, range(len(self.breakpoints))))(arg)
942
+ else:
943
+ return []
944
+
945
+ @command(allows_abbreviation=True, help="delete a breakpoint")
946
+ def delete(self, args: str):
947
+ if args:
948
+ try:
949
+ breakpoint_num = int(args)
950
+ except ValueError:
951
+ breakpoint_num = -1
952
+ if not (0 <= breakpoint_num < len(self.breakpoints)):
953
+ print(f"Error: Invalid breakpoint \"{args}\"")
954
+ else:
955
+ b = self.breakpoints[breakpoint_num]
956
+ self.breakpoints = self.breakpoints[:breakpoint_num] + self.breakpoints[breakpoint_num + 1:]
957
+ self.write(f"Deleted {b!s}\n")
958
+
959
+ @arg_completer(for_command="set")
960
+ def set_completer(self, index: int, arg: str, prev_arguments: Iterable[str]):
961
+ if index == 0:
962
+ return SetCompleter(lambda: self.variables_by_name.keys())(arg)
963
+ elif index == 1:
964
+ if prev_arguments[-1] in self.variables_by_name:
965
+ var = self.variables_by_name[prev_arguments[-1]]
966
+ return SetCompleter(lambda: map(str, var.possibilities))(arg)
967
+ return []
968
+
969
+ @command(help="modifies part of the debugger environment")
970
+ def set(self, arguments: str):
971
+ parsed = arguments.strip().split()
972
+ if len(parsed) == 3 and parsed[1].strip() == "=":
973
+ parsed = [parsed[0], parsed[1]]
974
+ if len(parsed) != 2:
975
+ self.write("Usage: ", dim=True)
976
+ self.write("set", bold=True, color=ANSIColor.BLUE)
977
+ self.write(" VARIABLE ", bold=True, color=ANSIColor.GREEN)
978
+ self.write("VALUE\n\n", bold=True, color=ANSIColor.CYAN)
979
+ self.write("Options:\n\n", bold=True)
980
+ for name, var in self.variables_by_name.items():
981
+ self.write(f" {name} ", bold=True, color=ANSIColor.GREEN)
982
+ self.write("[", dim=True)
983
+ for i, value in enumerate(var.possibilities):
984
+ if i > 0:
985
+ self.write("|", dim=True)
986
+ self.write(str(value), bold=True, color=ANSIColor.CYAN)
987
+ self.write("]\n ", dim=True)
988
+ self.write(self.variable_descriptions[name])
989
+ self.write("\n\n")
990
+ elif parsed[0] not in self.variables_by_name:
991
+ self.write("Error: Unknown variable ", bold=True, color=ANSIColor.RED)
992
+ self.write(parsed[0], bold=True)
993
+ self.write("\n")
994
+ else:
995
+ try:
996
+ var = self.variables_by_name[parsed[0]]
997
+ var.value = var.parse(parsed[1])
998
+ except ValueError as e:
999
+ self.write(f"{e!s}\n", bold=True, color=ANSIColor.RED)
1000
+
1001
+ @arg_completer(for_command="show")
1002
+ def show_completer(self, index: int, arg: str, prev_arguments: Iterable[str]):
1003
+ if index == 0:
1004
+ return SetCompleter(lambda: self.variables_by_name.keys())(arg)
1005
+ else:
1006
+ return []
1007
+
1008
+ @command(help="prints part of the debugger environment")
1009
+ def show(self, arguments: str):
1010
+ parsed = arguments.strip().split()
1011
+ if len(parsed) > 2:
1012
+ self.write("Usage: ", dim=True)
1013
+ self.write("show", bold=True, color=ANSIColor.BLUE)
1014
+ self.write(" VARIABLE\n\n", bold=True, color=ANSIColor.GREEN)
1015
+ self.write("Options:\n", bold=True)
1016
+ for name, var in self.variables_by_name.items():
1017
+ self.write(f"\n {name}\n ", bold=True, color=ANSIColor.GREEN)
1018
+ self.write(self.variable_descriptions[name])
1019
+ self.write("\n")
1020
+ elif not parsed:
1021
+ for i, (name, var) in enumerate(self.variables_by_name.items()):
1022
+ if i > 0:
1023
+ self.write("\n")
1024
+ self.write(name, bold=True, color=ANSIColor.GREEN)
1025
+ self.write(" = ", dim=True)
1026
+ self.write(str(var.value), bold=True, color=ANSIColor.CYAN)
1027
+ self.write("\n")
1028
+ self.write(self.variable_descriptions[name])
1029
+ self.write("\n")
1030
+ elif parsed[0] not in self.variables_by_name:
1031
+ self.write("Error: Unknown variable ", bold=True, color=ANSIColor.RED)
1032
+ self.write(parsed[0], bold=True)
1033
+ self.write("\n")
1034
+ else:
1035
+ self.write(parsed[0], bold=True, color=ANSIColor.GREEN)
1036
+ self.write(" = ", dim=True)
1037
+ self.write(str(self.variables_by_name[parsed[0]].value), bold=True, color=ANSIColor.CYAN)
1038
+ self.write("\n")
1039
+ self.write(self.variable_descriptions[parsed[0]])