csvpath 0.0.485__tar.gz → 0.0.487__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. {csvpath-0.0.485 → csvpath-0.0.487}/PKG-INFO +13 -7
  2. {csvpath-0.0.485 → csvpath-0.0.487}/README.md +11 -6
  3. {csvpath-0.0.485 → csvpath-0.0.487}/config/config.ini +3 -0
  4. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/csvpath.py +13 -0
  5. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/csvpaths.py +55 -6
  6. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/managers/result.py +45 -1
  7. csvpath-0.0.487/csvpath/managers/result_serializer.py +165 -0
  8. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/managers/results_manager.py +26 -1
  9. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/args.py +3 -10
  10. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/inf.py +4 -0
  11. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/function.py +9 -1
  12. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/dups.py +9 -5
  13. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/print/printf.py +10 -2
  14. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/types/boolean.py +9 -12
  15. csvpath-0.0.487/csvpath/matching/functions/types/datef.py +130 -0
  16. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/types/decimal.py +39 -34
  17. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/types/nonef.py +24 -1
  18. csvpath-0.0.487/csvpath/matching/functions/types/string.py +65 -0
  19. csvpath-0.0.487/csvpath/matching/functions/types/type.py +14 -0
  20. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/validity/line.py +50 -23
  21. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/matcher.py +24 -20
  22. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/matchable.py +5 -2
  23. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/qualified.py +11 -0
  24. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/util/exceptions.py +4 -0
  25. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/util/expression_utility.py +37 -2
  26. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/util/print_parser.py +7 -4
  27. csvpath-0.0.487/csvpath/matching/util/runtime_data_collector.py +108 -0
  28. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/scanning_lexer.py +1 -1
  29. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/config.py +13 -0
  30. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/error.py +1 -1
  31. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/file_readers.py +16 -0
  32. {csvpath-0.0.485 → csvpath-0.0.487}/pyproject.toml +2 -1
  33. csvpath-0.0.485/csvpath/matching/functions/types/datef.py +0 -130
  34. csvpath-0.0.485/csvpath/matching/functions/types/string.py +0 -45
  35. {csvpath-0.0.485 → csvpath-0.0.487}/LICENSE +0 -0
  36. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/__init__.py +0 -0
  37. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/managers/__init__.py +0 -0
  38. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/managers/csvpaths_manager.py +0 -0
  39. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/managers/file_manager.py +0 -0
  40. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/__init__.py +0 -0
  41. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/__init__.py +0 -0
  42. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/all.py +0 -0
  43. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/andf.py +0 -0
  44. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/any.py +0 -0
  45. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/between.py +0 -0
  46. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/empty.py +0 -0
  47. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/exists.py +0 -0
  48. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/no.py +0 -0
  49. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/notf.py +0 -0
  50. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/orf.py +0 -0
  51. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/boolean/yes.py +0 -0
  52. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/count.py +0 -0
  53. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/count_headers.py +0 -0
  54. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/count_lines.py +0 -0
  55. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/count_scans.py +0 -0
  56. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/counter.py +0 -0
  57. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/every.py +0 -0
  58. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/has_matches.py +0 -0
  59. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/increment.py +0 -0
  60. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/tally.py +0 -0
  61. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/counting/total_lines.py +0 -0
  62. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/dates/now.py +0 -0
  63. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/function_factory.py +0 -0
  64. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/function_finder.py +0 -0
  65. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/function_focus.py +0 -0
  66. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/append.py +0 -0
  67. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/collect.py +0 -0
  68. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/empty_stack.py +0 -0
  69. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/end.py +0 -0
  70. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/header_name.py +0 -0
  71. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/header_names_mismatch.py +0 -0
  72. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/headers.py +0 -0
  73. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/mismatch.py +0 -0
  74. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/replace.py +0 -0
  75. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/headers/reset_headers.py +0 -0
  76. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/advance.py +0 -0
  77. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/after_blank.py +0 -0
  78. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/first.py +0 -0
  79. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/first_line.py +0 -0
  80. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/last.py +0 -0
  81. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/lines/stop.py +0 -0
  82. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/above.py +0 -0
  83. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/add.py +0 -0
  84. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/divide.py +0 -0
  85. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/equals.py +0 -0
  86. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/intf.py +0 -0
  87. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/mod.py +0 -0
  88. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/multiply.py +0 -0
  89. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/round.py +0 -0
  90. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/subtotal.py +0 -0
  91. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/subtract.py +0 -0
  92. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/math/sum.py +0 -0
  93. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/misc/importf.py +0 -0
  94. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/misc/random.py +0 -0
  95. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/print/jinjaf.py +0 -0
  96. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/print/print_line.py +0 -0
  97. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/print/print_queue.py +0 -0
  98. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/print/table.py +0 -0
  99. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/stats/minf.py +0 -0
  100. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/stats/percent.py +0 -0
  101. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/stats/percent_unique.py +0 -0
  102. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/stats/stdev.py +0 -0
  103. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/concat.py +0 -0
  104. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/length.py +0 -0
  105. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/lower.py +0 -0
  106. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/metaphone.py +0 -0
  107. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/regex.py +0 -0
  108. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/starts_with.py +0 -0
  109. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/strip.py +0 -0
  110. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/substring.py +0 -0
  111. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/strings/upper.py +0 -0
  112. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/testing/debug.py +0 -0
  113. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/types/__init__.py +0 -0
  114. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/validity/fail.py +0 -0
  115. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/validity/failed.py +0 -0
  116. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/variables/get.py +0 -0
  117. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/variables/pushpop.py +0 -0
  118. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/variables/put.py +0 -0
  119. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/variables/track.py +0 -0
  120. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/functions/variables/variables.py +0 -0
  121. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/lark_parser.py +0 -0
  122. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/lark_transformer.py +0 -0
  123. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/__init__.py +0 -0
  124. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/equality.py +0 -0
  125. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/expression.py +0 -0
  126. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/header.py +0 -0
  127. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/reference.py +0 -0
  128. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/term.py +0 -0
  129. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/productions/variable.py +0 -0
  130. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/util/expression_encoder.py +0 -0
  131. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/matching/util/lark_print_parser.py +0 -0
  132. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/__init__.py +0 -0
  133. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/exceptions.py +0 -0
  134. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/parser.out +0 -0
  135. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/parsetab.py +0 -0
  136. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/scanning/scanner.py +0 -0
  137. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/cache.py +0 -0
  138. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/config_exception.py +0 -0
  139. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/exceptions.py +0 -0
  140. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/last_line_stats.py +0 -0
  141. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/line_counter.py +0 -0
  142. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/line_monitor.py +0 -0
  143. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/log_utility.py +0 -0
  144. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/metadata_parser.py +0 -0
  145. {csvpath-0.0.485 → csvpath-0.0.487}/csvpath/util/printer.py +0 -0
  146. {csvpath-0.0.485 → csvpath-0.0.487}/docs/asbool.md +0 -0
  147. {csvpath-0.0.485 → csvpath-0.0.487}/docs/assignment.md +0 -0
  148. {csvpath-0.0.485 → csvpath-0.0.487}/docs/comments.md +0 -0
  149. {csvpath-0.0.485 → csvpath-0.0.487}/docs/config.md +0 -0
  150. {csvpath-0.0.485 → csvpath-0.0.487}/docs/examples.md +0 -0
  151. {csvpath-0.0.485 → csvpath-0.0.487}/docs/files.md +0 -0
  152. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/above.md +0 -0
  153. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/advance.md +0 -0
  154. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/after_blank.md +0 -0
  155. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/all.md +0 -0
  156. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/andor.md +0 -0
  157. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/any.md +0 -0
  158. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/average.md +0 -0
  159. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/between.md +0 -0
  160. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/collect.md +0 -0
  161. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/correlate.md +0 -0
  162. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/count.md +0 -0
  163. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/count_headers.md +0 -0
  164. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/counter.md +0 -0
  165. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/date.md +0 -0
  166. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/empty.md +0 -0
  167. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/empty_stack.md +0 -0
  168. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/end.md +0 -0
  169. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/every.md +0 -0
  170. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/fail.md +0 -0
  171. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/first.md +0 -0
  172. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/get.md +0 -0
  173. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/has_dups.md +0 -0
  174. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/has_matches.md +0 -0
  175. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/header.md +0 -0
  176. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/header_name.md +0 -0
  177. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/header_names_mismatch.md +0 -0
  178. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/implementing_functions.md +0 -0
  179. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/import.md +0 -0
  180. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/in.md +0 -0
  181. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/increment.md +0 -0
  182. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/intf.md +0 -0
  183. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/jinja.md +0 -0
  184. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/last.md +0 -0
  185. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/line.md +0 -0
  186. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/line_number.md +0 -0
  187. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/max.md +0 -0
  188. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/metaphone.md +0 -0
  189. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/mismatch.md +0 -0
  190. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/no.md +0 -0
  191. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/not.md +0 -0
  192. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/now.md +0 -0
  193. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/percent_unique.md +0 -0
  194. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/pop.md +0 -0
  195. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/print.md +0 -0
  196. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/print_line.md +0 -0
  197. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/print_queue.md +0 -0
  198. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/random.md +0 -0
  199. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/regex.md +0 -0
  200. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/replace.md +0 -0
  201. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/reset_headers.md +0 -0
  202. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/stdev.md +0 -0
  203. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/stop.md +0 -0
  204. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/string_functions.md +0 -0
  205. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/subtotal.md +0 -0
  206. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/subtract.md +0 -0
  207. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/sum.md +0 -0
  208. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/tally.md +0 -0
  209. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/total_lines.md +0 -0
  210. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/track.md +0 -0
  211. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/types.md +0 -0
  212. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/variables.md +0 -0
  213. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions/variables_and_headers.md +0 -0
  214. {csvpath-0.0.485 → csvpath-0.0.487}/docs/functions.md +0 -0
  215. {csvpath-0.0.485 → csvpath-0.0.487}/docs/grammar.md +0 -0
  216. {csvpath-0.0.485 → csvpath-0.0.487}/docs/headers.md +0 -0
  217. {csvpath-0.0.485 → csvpath-0.0.487}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
  218. {csvpath-0.0.485 → csvpath-0.0.487}/docs/images/logo-wordmark-white-trimmed.png +0 -0
  219. {csvpath-0.0.485 → csvpath-0.0.487}/docs/paths.md +0 -0
  220. {csvpath-0.0.485 → csvpath-0.0.487}/docs/printing.md +0 -0
  221. {csvpath-0.0.485 → csvpath-0.0.487}/docs/qualifiers.md +0 -0
  222. {csvpath-0.0.485 → csvpath-0.0.487}/docs/references.md +0 -0
  223. {csvpath-0.0.485 → csvpath-0.0.487}/docs/terms.md +0 -0
  224. {csvpath-0.0.485 → csvpath-0.0.487}/docs/variables.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csvpath
3
- Version: 0.0.485
3
+ Version: 0.0.487
4
4
  Summary: A declarative language for validation of CSV files
5
5
  Author: David Kershaw
6
6
  Author-email: dk107dk@hotmail.com
@@ -28,6 +28,7 @@ Requires-Dist: metaphone (>=0.6,<0.7)
28
28
  Requires-Dist: ply (>=3.11,<4.0)
29
29
  Requires-Dist: pylightxl (>=1.61,<2.0)
30
30
  Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
31
+ Requires-Dist: smart-open[s3] (>=7.0.5,<8.0.0)
31
32
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
32
33
  Project-URL: Csvpath.org, https://www.csvpath.org
33
34
  Project-URL: Github, https://github.com/csvpath/csvpath.git
@@ -36,23 +37,25 @@ Description-Content-Type: text/markdown
36
37
 
37
38
  # <img src='https://www.csvpath.org/~gitbook/image?url=https%3A%2F%2F3739708663-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Forganizations%252FMXTJeGvaEsqwNG39F37h%252Fsites%252Fsite_SPBqJ%252Ficon%252FMCSxo7k6rXWnqoPE204u%252Fcsvpath-icon.png%3Falt%3Dmedia%26token%3D28869fdd-d54e-400e-8917-b8097f935f42&width=32&dpr=2&quality=100&sign=71ca9f3e&sv=1'/> About CsvPath
38
39
 
39
- CsvPath defines a declarative syntax for inspecting and validating CSV files.
40
+ CsvPath defines a declarative syntax for inspecting and validating CSV and Excel files.
40
41
 
41
42
  CsvPath' goal is to make it easy to:
42
- - Analyze the content and structure of a CSV
43
+ - Analyze the content and structure of a CSV or Excel file
43
44
  - Validate that the file matches expectations
44
45
  - Report on the content or validity
45
46
  - Create new derived CSV files
46
47
 
47
48
  And do it all in an automation-friendly way.
48
49
 
49
- Though much simpler, it is inspired by:
50
- - XPath. CsvPath is to CSV files like XPath is to XML files.
51
- - Validation of XML using <a href='https://schematron.com/'>Schematron rules</a>
50
+ CsvPath is inspired by:
51
+ - XPath for XML files
52
+ - The ISO standard <a href='https://schematron.com/'>Schematron validation</a>
52
53
 
53
54
  CsvPath is intended to fit with other DataOps and data quality tools. Files are streamed. The interface is simple. New functions are easy to create.
54
55
 
55
- Read more about CsvPath and see realistic CSV validation examples at <a href='https://www.csvpath.org'>https://www.csvpath.org</a>.
56
+ Read more about CsvPath and see realistic CSV and Excel validation examples at <a href='https://www.csvpath.org'>https://www.csvpath.org</a>.
57
+
58
+ If you need help, use the <a href='https://www.csvpath.org/getting-started/get-help'>contact form</a> or the <a href='https://github.com/csvpath/csvpath/issues'>issue tracker</a> or talk to one of our [sponsors](#sponsors).
56
59
 
57
60
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/csvpath?logoColor=green&color=green) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/dk107dk/csvpath) ![PyPI - Version](https://img.shields.io/pypi/v/csvpath)
58
61
 
@@ -80,6 +83,7 @@ Read more about CsvPath and see realistic CSV validation examples at <a href='ht
80
83
  - [Error Handling](#errors)
81
84
  - [More Examples](#examples)
82
85
  - [Grammar](#grammar)
86
+ - [Sponsors](#sponsors)
83
87
 
84
88
  <a name="motivation"></a>
85
89
  # Motivation
@@ -451,10 +455,12 @@ To create example CsvPaths from your own data, try <a href='https://autogen.csvp
451
455
  Read <a href='https://github.com/dk107dk/csvpath/blob/main/docs/grammar.md'>more about the CsvPath grammar definition here</a>.
452
456
 
453
457
 
458
+ <a name="more-info"></a>
454
459
  # More Info
455
460
 
456
461
  Visit <a href="https://www.csvpath.org">https://www.csvpath.org</a>
457
462
 
463
+ <a name="sponsors"></a>
458
464
  # Sponsors
459
465
 
460
466
  <a href='https://www.atestaanalytics.com/' >
@@ -1,23 +1,25 @@
1
1
 
2
2
  # <img src='https://www.csvpath.org/~gitbook/image?url=https%3A%2F%2F3739708663-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Forganizations%252FMXTJeGvaEsqwNG39F37h%252Fsites%252Fsite_SPBqJ%252Ficon%252FMCSxo7k6rXWnqoPE204u%252Fcsvpath-icon.png%3Falt%3Dmedia%26token%3D28869fdd-d54e-400e-8917-b8097f935f42&width=32&dpr=2&quality=100&sign=71ca9f3e&sv=1'/> About CsvPath
3
3
 
4
- CsvPath defines a declarative syntax for inspecting and validating CSV files.
4
+ CsvPath defines a declarative syntax for inspecting and validating CSV and Excel files.
5
5
 
6
6
  CsvPath' goal is to make it easy to:
7
- - Analyze the content and structure of a CSV
7
+ - Analyze the content and structure of a CSV or Excel file
8
8
  - Validate that the file matches expectations
9
9
  - Report on the content or validity
10
10
  - Create new derived CSV files
11
11
 
12
12
  And do it all in an automation-friendly way.
13
13
 
14
- Though much simpler, it is inspired by:
15
- - XPath. CsvPath is to CSV files like XPath is to XML files.
16
- - Validation of XML using <a href='https://schematron.com/'>Schematron rules</a>
14
+ CsvPath is inspired by:
15
+ - XPath for XML files
16
+ - The ISO standard <a href='https://schematron.com/'>Schematron validation</a>
17
17
 
18
18
  CsvPath is intended to fit with other DataOps and data quality tools. Files are streamed. The interface is simple. New functions are easy to create.
19
19
 
20
- Read more about CsvPath and see realistic CSV validation examples at <a href='https://www.csvpath.org'>https://www.csvpath.org</a>.
20
+ Read more about CsvPath and see realistic CSV and Excel validation examples at <a href='https://www.csvpath.org'>https://www.csvpath.org</a>.
21
+
22
+ If you need help, use the <a href='https://www.csvpath.org/getting-started/get-help'>contact form</a> or the <a href='https://github.com/csvpath/csvpath/issues'>issue tracker</a> or talk to one of our [sponsors](#sponsors).
21
23
 
22
24
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/csvpath?logoColor=green&color=green) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/dk107dk/csvpath) ![PyPI - Version](https://img.shields.io/pypi/v/csvpath)
23
25
 
@@ -45,6 +47,7 @@ Read more about CsvPath and see realistic CSV validation examples at <a href='ht
45
47
  - [Error Handling](#errors)
46
48
  - [More Examples](#examples)
47
49
  - [Grammar](#grammar)
50
+ - [Sponsors](#sponsors)
48
51
 
49
52
  <a name="motivation"></a>
50
53
  # Motivation
@@ -416,10 +419,12 @@ To create example CsvPaths from your own data, try <a href='https://autogen.csvp
416
419
  Read <a href='https://github.com/dk107dk/csvpath/blob/main/docs/grammar.md'>more about the CsvPath grammar definition here</a>.
417
420
 
418
421
 
422
+ <a name="more-info"></a>
419
423
  # More Info
420
424
 
421
425
  Visit <a href="https://www.csvpath.org">https://www.csvpath.org</a>
422
426
 
427
+ <a name="sponsors"></a>
423
428
  # Sponsors
424
429
 
425
430
  <a href='https://www.atestaanalytics.com/' >
@@ -24,3 +24,6 @@ path = cache
24
24
  [functions]
25
25
  imports = config/functions.imports
26
26
 
27
+ [results]
28
+ archive = archive
29
+
@@ -5,6 +5,7 @@ import csv
5
5
  import time
6
6
  import os
7
7
  import hashlib
8
+ from datetime import datetime
8
9
  from typing import List, Dict, Any
9
10
  from collections.abc import Iterator
10
11
  from abc import ABC, abstractmethod
@@ -286,6 +287,16 @@ class CsvPath(CsvPathPublic, ErrorCollector, Printer): # pylint: disable=R0902,
286
287
  self._ecoms = ErrorCommsManager(csvpath=self)
287
288
  self._function_times_match = {}
288
289
  self._function_times_value = {}
290
+ self._created_at = datetime.now()
291
+ self._run_started_at = None
292
+
293
+ @property
294
+ def created_at(self) -> datetime:
295
+ return self._created_at
296
+
297
+ @property
298
+ def run_started_at(self) -> datetime:
299
+ return self._run_started_at
289
300
 
290
301
  @property
291
302
  def run_mode(self) -> bool:
@@ -1071,6 +1082,8 @@ class CsvPath(CsvPathPublic, ErrorCollector, Printer): # pylint: disable=R0902,
1071
1082
  if self.matcher:
1072
1083
  last_line = self.matcher.line
1073
1084
  self.line_monitor.next_line(last_line=last_line, data=line)
1085
+ if self.line_monitor.physical_line_number == 0:
1086
+ self._run_started_at = datetime.now()
1074
1087
 
1075
1088
  def _consider_line(self, line): # pylint: disable=R0912, R0911
1076
1089
  # re: R0912: this method has already been refactored but maybe
@@ -5,6 +5,7 @@ from abc import ABC, abstractmethod
5
5
  from typing import List, Any
6
6
  import csv
7
7
  import traceback
8
+ from datetime import datetime
8
9
  from .util.error import ErrorHandler, ErrorCollector, Error
9
10
  from .util.config import Config
10
11
  from .util.log_utility import LogUtility
@@ -146,6 +147,13 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
146
147
  self._fail_all = False
147
148
  self._skip_all = False
148
149
  self._advance_all = 0
150
+ self._current_run_time = None
151
+
152
+ @property
153
+ def current_run_time(self) -> datetime:
154
+ if self._current_run_time is None:
155
+ self._current_run_time = datetime.now()
156
+ return self._current_run_time
149
157
 
150
158
  def clear_run_coordination(self) -> None:
151
159
  """run coordination is the set of signals that csvpaths send to affect
@@ -154,6 +162,7 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
154
162
  self._fail_all = False
155
163
  self._skip_all = False
156
164
  self._advance_all = 0
165
+ self._current_run_time = None
157
166
  self.logger.debug("Cleared run coordination")
158
167
 
159
168
  def csvpath(self) -> CsvPath:
@@ -224,9 +233,17 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
224
233
  self.logger.info(
225
234
  "Beginning collect_paths %s with %s paths", pathsname, len(paths)
226
235
  )
227
- for path in paths:
236
+ crt = self.results_manager.get_run_time_str(pathsname, self.current_run_time)
237
+ for i, path in enumerate(paths):
228
238
  csvpath = self.csvpath()
229
- result = Result(csvpath=csvpath, file_name=filename, paths_name=pathsname)
239
+ result = Result(
240
+ csvpath=csvpath,
241
+ file_name=filename,
242
+ paths_name=pathsname,
243
+ run_index=i,
244
+ run_time=self.current_run_time,
245
+ run_dir=crt,
246
+ )
230
247
  # casting a broad net because if "raise" not in the error policy we
231
248
  # want to never fail during a run
232
249
  try:
@@ -249,7 +266,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
249
266
  except Exception as ex: # pylint: disable=W0718
250
267
  ex.trace = traceback.format_exc()
251
268
  ex.source = self
269
+ self.results_manager.save(result)
252
270
  ErrorHandler(csvpaths=self, error_collector=result).handle_error(ex)
271
+ self.results_manager.save(result)
253
272
  self.clear_run_coordination()
254
273
  self.logger.info(
255
274
  "Completed collect_paths %s with %s paths", pathsname, len(paths)
@@ -280,10 +299,18 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
280
299
  len(paths),
281
300
  filename,
282
301
  )
302
+ crt = self.results_manager.get_run_time_str(pathsname, self.current_run_time)
283
303
  for i, path in enumerate(paths):
284
304
  csvpath = self.csvpath()
285
305
  self.logger.debug("Beginning to FF CsvPath instance: %s", csvpath)
286
- result = Result(csvpath=csvpath, file_name=filename, paths_name=pathsname)
306
+ result = Result(
307
+ csvpath=csvpath,
308
+ file_name=filename,
309
+ paths_name=pathsname,
310
+ run_index=i,
311
+ run_time=self.current_run_time,
312
+ run_dir=crt,
313
+ )
287
314
  try:
288
315
  self.results_manager.add_named_result(result)
289
316
  self._load_csvpath(csvpath, path=path, file=file)
@@ -299,7 +326,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
299
326
  except Exception as ex: # pylint: disable=W0718
300
327
  ex.trace = traceback.format_exc()
301
328
  ex.source = self
329
+ self.results_manager.save(result)
302
330
  ErrorHandler(csvpaths=self, error_collector=result).handle_error(ex)
331
+ self.results_manager.save(result)
303
332
  self.clear_run_coordination()
304
333
  self.logger.info(
305
334
  "Completed fast_forward_paths %s with %s paths", pathsname, len(paths)
@@ -315,7 +344,8 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
315
344
  self.logger.info("Cleaning out any %s and %s results", filename, pathsname)
316
345
  self.clean(paths=pathsname)
317
346
  self.logger.info("Beginning next_paths with %s paths", len(paths))
318
- for path in paths:
347
+ crt = self.results_manager.get_run_time_str(pathsname, self.current_run_time)
348
+ for i, path in enumerate(paths):
319
349
  if self._skip_all:
320
350
  skip_err = "Found the skip-all signal set. skip_all() is"
321
351
  skip_err = f"{skip_err} only for breadth-first runs using the"
@@ -334,7 +364,14 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
334
364
  advance_err = f"{advance_err} serial run like this one."
335
365
  self.logger.error(advance_err)
336
366
  csvpath = self.csvpath()
337
- result = Result(csvpath=csvpath, file_name=filename, paths_name=pathsname)
367
+ result = Result(
368
+ csvpath=csvpath,
369
+ file_name=filename,
370
+ paths_name=pathsname,
371
+ run_index=i,
372
+ run_time=self.current_run_time,
373
+ run_dir=crt,
374
+ )
338
375
  if self._fail_all:
339
376
  self.logger.warning(
340
377
  "Fail-all set. Failing all remaining CsvPath instances in the run."
@@ -351,7 +388,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
351
388
  except Exception as ex: # pylint: disable=W0718
352
389
  ex.trace = traceback.format_exc()
353
390
  ex.source = self
391
+ self.results_manager.save(result)
354
392
  ErrorHandler(csvpaths=self, error_collector=result).handle_error(ex)
393
+ self.results_manager.save(result)
355
394
  self.clear_run_coordination()
356
395
 
357
396
  # =============== breadth first processing ================
@@ -541,6 +580,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
541
580
  except Exception as ex: # pylint: disable=W0718
542
581
  ex.trace = traceback.format_exc()
543
582
  ex.source = self
583
+ for r in csvpath_objects:
584
+ r = r[1]
585
+ self.results_manager.save(r)
544
586
  ErrorHandler(
545
587
  csvpaths=self, error_collector=self.current_matcher
546
588
  ).handle_error(ex)
@@ -553,6 +595,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
553
595
  yield line
554
596
  if sum(stopped_count) == len(csvpath_objects):
555
597
  break
598
+ for r in csvpath_objects:
599
+ r = r[1]
600
+ self.results_manager.save(r)
556
601
  self.clear_run_coordination()
557
602
 
558
603
  def _load_csvpath_objects(
@@ -577,7 +622,8 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
577
622
  return csvpath_objects
578
623
 
579
624
  def _prep_csvpath_results(self, *, csvpath_objects, filename, pathsname):
580
- for csvpath in csvpath_objects:
625
+ crt = self.results_manager.get_run_time_str(pathsname, self.current_run_time)
626
+ for i, csvpath in enumerate(csvpath_objects):
581
627
  try:
582
628
  #
583
629
  # Result will set itself into its CsvPath as error collector
@@ -588,6 +634,9 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
588
634
  file_name=filename,
589
635
  paths_name=pathsname,
590
636
  lines=csvpath[1],
637
+ run_index=i,
638
+ run_time=self.current_run_time,
639
+ run_dir=crt,
591
640
  )
592
641
  csvpath[1] = result
593
642
  self.results_manager.add_named_result(result)
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=C0114
2
+ from datetime import datetime
2
3
  from typing import Dict, List, Any
3
4
  from ..util.error import Error, ErrorCollector
4
5
  from ..util.printer import Printer
@@ -20,9 +21,14 @@ class Result(ErrorCollector, Printer): # pylint: disable=R0902
20
21
  csvpath: CsvPath,
21
22
  file_name: str,
22
23
  paths_name: str,
24
+ run_index: int,
25
+ run_time: datetime,
26
+ run_dir: str,
27
+ runtime_data: dict = None,
23
28
  ):
24
29
  self._lines: List[List[Any]] = None
25
30
  self._csvpath = None
31
+ self._runtime_data = runtime_data
26
32
  self._paths_name = paths_name
27
33
  self._file_name = file_name
28
34
  self._errors = []
@@ -32,6 +38,28 @@ class Result(ErrorCollector, Printer): # pylint: disable=R0902
32
38
  # use the properties so error_collector, etc. is set correctly
33
39
  self.csvpath = csvpath
34
40
  self.lines = lines
41
+ self.run_index = f"{run_index}"
42
+ self._run_time = run_time
43
+ self._run_dir = run_dir
44
+
45
+ @property
46
+ def run_time(self) -> datetime:
47
+ return self._run_time
48
+
49
+ @property
50
+ def run_dir(self) -> str:
51
+ return self._run_dir
52
+
53
+ @run_dir.setter
54
+ def run_dir(self, d: str) -> None:
55
+ self._run_dir = d
56
+
57
+ @property
58
+ def identity_or_index(self) -> str:
59
+ s = self._csvpath.identity
60
+ if f"{s}".strip() == "":
61
+ s = self.run_index
62
+ return s
35
63
 
36
64
  @property
37
65
  def metadata(self) -> Dict[str, Any]: # pylint: disable=C0116
@@ -99,6 +127,10 @@ class Result(ErrorCollector, Printer): # pylint: disable=R0902
99
127
  def errors(self) -> List[Error]: # pylint: disable=C0116
100
128
  return self._errors
101
129
 
130
+ @errors.setter
131
+ def errors(self, errors: List[Error]) -> None:
132
+ self._errors = errors
133
+
102
134
  @property
103
135
  def errors_count(self) -> int: # pylint: disable=C0116
104
136
  return len(self._errors)
@@ -112,8 +144,12 @@ class Result(ErrorCollector, Printer): # pylint: disable=R0902
112
144
 
113
145
  @property
114
146
  def is_valid(self) -> bool: # pylint: disable=C0116
115
- if self._csvpath:
147
+ # if the csvpath has not been run -- e.g. because it represents results that were
148
+ # saved to disk and reloaded -- it won't have a run started time.
149
+ if self._csvpath and self._csvpath.run_started_at is not None:
116
150
  return self._csvpath.is_valid
151
+ elif self._runtime_data and "valid" in self._runtime_data:
152
+ return self._runtime_data["valid"]
117
153
  return False
118
154
 
119
155
  @property
@@ -124,6 +160,14 @@ class Result(ErrorCollector, Printer): # pylint: disable=R0902
124
160
  self._printouts = []
125
161
  return self._printouts["default"] if "default" in self._printouts else []
126
162
 
163
+ def get_printouts(self) -> dict[str, list[str]]:
164
+ return self._printouts
165
+
166
+ def set_printouts(self, name: str, lines: List[str]) -> None:
167
+ if self._printouts is None:
168
+ self._printouts = {}
169
+ self._printouts[name] = lines
170
+
127
171
  def get_printout_by_name(self, name: str) -> List[str]: # pylint: disable=C0116
128
172
  if self._printouts is None:
129
173
  self._printouts = []
@@ -0,0 +1,165 @@
1
+ import os
2
+ import json
3
+ import csv
4
+ from typing import NewType, List, Dict, Optional, Union
5
+ from datetime import datetime
6
+ from .result import Result
7
+ from ..matching.util.runtime_data_collector import RuntimeDataCollector
8
+ from csvpath import CsvPath
9
+
10
+ Simpledata = NewType("Simpledata", Union[None | str | int | float | bool])
11
+ Listdata = NewType("Listdata", list[None | str | int | float | bool])
12
+ Csvdata = NewType("Csvdata", list[List[str]])
13
+ Metadata = NewType("Metadata", Dict[str, Simpledata])
14
+
15
+
16
+ class ResultSerializer:
17
+ def __init__(self, base_dir: str):
18
+ self.base_dir = base_dir
19
+
20
+ def save_result(self, result: Result) -> None:
21
+ runtime_data = {}
22
+ RuntimeDataCollector.collect(result.csvpath, runtime_data, local=True)
23
+ runtime_data["run_index"] = result.run_index
24
+ self._save(
25
+ metadata=result.csvpath.metadata,
26
+ errors=result.errors,
27
+ variables=result.variables,
28
+ lines=result.lines,
29
+ printouts=result.get_printouts(),
30
+ runtime_data=runtime_data,
31
+ paths_name=result.paths_name,
32
+ file_name=result.file_name,
33
+ identity=result.identity_or_index,
34
+ run_time=result.run_time,
35
+ run_dir=result.run_dir,
36
+ run_index=result.run_index,
37
+ )
38
+
39
+ def _save(
40
+ self,
41
+ *,
42
+ metadata: Metadata,
43
+ runtime_data: Metadata,
44
+ errors: List[Metadata],
45
+ variables: dict[str, Simpledata | Listdata | Metadata],
46
+ lines: Csvdata,
47
+ printouts: dict[str, list[str]],
48
+ paths_name: str,
49
+ file_name: str,
50
+ identity: str,
51
+ run_time: datetime,
52
+ run_dir: str,
53
+ run_index: int,
54
+ ) -> None:
55
+ """Save a single Result object to basedir/paths_name/run_time/identity_or_index."""
56
+ meta = {
57
+ "paths_name": paths_name,
58
+ "file_name": file_name,
59
+ "run_time": f"{run_time}",
60
+ "run_index": run_index,
61
+ "identity": identity,
62
+ "metadata": metadata,
63
+ "runtime_data": runtime_data,
64
+ }
65
+ print(f"\nresult_serializer: meta: {meta}")
66
+ run_dir = self.get_instance_dir(run_dir=run_dir, identity=identity)
67
+ # Save the JSON files
68
+ with open(os.path.join(run_dir, "meta.json"), "w") as f:
69
+ json.dump(meta, f, indent=2)
70
+ with open(os.path.join(run_dir, "errors.json"), "w") as f:
71
+ json.dump(errors, f, indent=2)
72
+ with open(os.path.join(run_dir, "vars.json"), "w") as f:
73
+ json.dump(variables, f, indent=2)
74
+
75
+ # Save lines returned as a CSV file
76
+ if lines is None:
77
+ lines = []
78
+ with open(os.path.join(run_dir, "data.csv"), "w") as f:
79
+ writer = csv.writer(f)
80
+ writer.writerows(lines)
81
+
82
+ # Save the printout lines
83
+ with open(os.path.join(run_dir, "printouts.txt"), "w") as f:
84
+ for k, v in printouts.items():
85
+ f.write(f"---- PRINTOUT: {k}\n")
86
+ for _ in v:
87
+ f.write(f"{_}\n")
88
+
89
+ def get_run_dir(self, *, paths_name, run_time):
90
+ run_dir = os.path.join(self.base_dir, paths_name)
91
+ if not isinstance(run_time, str):
92
+ run_time = run_time.strftime("%Y-%m-%d_%I-%M-%S")
93
+ run_dir = os.path.join(run_dir, f"{run_time}")
94
+ # the path existing for a different named-paths run in progress
95
+ # or having completed less than 1000ms ago is expected to be
96
+ # uncommon in real world usage. CsvPaths are single user instances
97
+ # atm. a server process would namespace each CsvPaths instance
98
+ # to prevent conflicts. if there is a conflict the two runs would
99
+ # overwrite each other. this prevents that.
100
+ if os.path.exists(run_dir):
101
+ i = 0
102
+ adir = f"{run_dir}.{i}"
103
+ while os.path.exists(adir):
104
+ i += 1
105
+ adir = f"{run_dir}.{i}"
106
+ run_dir = adir
107
+ return run_dir
108
+
109
+ def get_instance_dir(self, run_dir, identity) -> str:
110
+ run_dir = os.path.join(run_dir, identity)
111
+ os.makedirs(run_dir, exist_ok=True)
112
+ return run_dir
113
+
114
+ def load_result(
115
+ self, paths_name: str, run_time: str, identity: str
116
+ ) -> Optional[Result]:
117
+ """Load a single Result object from the base directory."""
118
+ run_dir = self._run_dir(
119
+ paths_name=paths_name, run_time=run_time, identity=identity
120
+ )
121
+ if not os.path.exists(run_dir):
122
+ return None
123
+ return self._load_result(run_dir)
124
+
125
+ def _load_result(self, run_dir: str) -> Optional[Result]:
126
+ if not os.path.exists(run_dir):
127
+ return None
128
+ try:
129
+ meta = None
130
+ variables = None
131
+ errors = None
132
+ data = None
133
+ printouts = None
134
+
135
+ with open(os.path.join(run_dir, "meta.json"), "r") as f:
136
+ meta = json.load(f)
137
+ with open(os.path.join(run_dir, "vars.json"), "r") as f:
138
+ variables = json.load(f)
139
+ with open(os.path.join(run_dir, "errors.json"), "r") as f:
140
+ errors = json.load(f)
141
+ with open(os.path.join(run_dir, "data.csv"), "r") as f:
142
+ reader = csv.reader(f)
143
+ data = [",".join(row) for row in reader]
144
+ with open(os.path.join(run_dir, "printouts.txt"), "r") as f:
145
+ printouts = f.readlines()
146
+
147
+ c = CsvPath()
148
+ c.variables = variables
149
+ c.metadata = meta["metadata"]
150
+ c.identity = meta["identity"]
151
+ result = Result(
152
+ lines=data,
153
+ csvpath=c,
154
+ file_name=meta["file_name"],
155
+ paths_name=meta["paths_name"],
156
+ run_index=meta["run_index"],
157
+ run_time=meta["run_time"],
158
+ runtime_data=meta["runtime_data"],
159
+ )
160
+ result.errors = errors
161
+ result.set_printouts("all", printouts)
162
+
163
+ return result
164
+ except (FileNotFoundError, ValueError, IOError):
165
+ return None
@@ -3,7 +3,8 @@ from __future__ import annotations
3
3
  from typing import Dict, List, Any
4
4
  from abc import ABC, abstractmethod
5
5
  from .result import Result
6
- from ..util.exceptions import InputException
6
+ from ..util.exceptions import InputException, CsvPathsException
7
+ from .result_serializer import ResultSerializer
7
8
 
8
9
 
9
10
  class CsvPathsResultsManager(ABC):
@@ -15,6 +16,14 @@ class CsvPathsResultsManager(ABC):
15
16
  CsvPaths clears the named results from the ResultsManager.
16
17
  """
17
18
 
19
+ #
20
+ # - printout lines
21
+ # - lines of captured data
22
+ # - variables
23
+ # - csvpath.metadata
24
+ # - csvpath.csvpath data
25
+ #
26
+
18
27
  @abstractmethod
19
28
  def get_variables(self, name: str) -> bool:
20
29
  """gets all the variables from all csvpaths in one dict. variables may
@@ -191,6 +200,19 @@ class ResultsManager(CsvPathsResultsManager): # pylint: disable=C0115
191
200
  for r in results:
192
201
  self.add_named_result(r)
193
202
 
203
+ def save(self, result: Result) -> None:
204
+ if self._csvpaths is None:
205
+ raise CsvPathsException(
206
+ "Cannot save result because there is no CsvPaths instance"
207
+ )
208
+ rs = ResultSerializer(self._csvpaths.config.archive_path)
209
+ rs.save_result(result)
210
+
211
+ def get_run_time_str(self, name, run_time) -> str:
212
+ rs = ResultSerializer(self._csvpaths.config.archive_path)
213
+ t = rs.get_run_dir(paths_name=name, run_time=run_time)
214
+ return t
215
+
194
216
  def remove_named_results(self, name: str) -> None:
195
217
  if name in self.named_results:
196
218
  del self.named_results[name]
@@ -210,6 +232,9 @@ class ResultsManager(CsvPathsResultsManager): # pylint: disable=C0115
210
232
  def clean_named_results(self, name: str) -> None:
211
233
  if name in self.named_results:
212
234
  self.remove_named_results(name)
235
+ #
236
+ # clean from filesystem too?
237
+ #
213
238
 
214
239
  def get_named_results(self, name) -> List[List[Any]]:
215
240
  if name in self.named_results:
@@ -456,16 +456,9 @@ class Args:
456
456
  def handle_errors_if(self, mismatch_count, mismatches):
457
457
  if mismatch_count == len(self._argsets):
458
458
  self._args_match = False
459
- pm = f"mismatch in {self.matchable.my_chain}: {mismatches}"
460
- # when would we not have a csvpath?
461
- # pln = (
462
- # self._csvpath.line_monitor.physical_line_number if self._csvpath else 0
463
- # )
464
- # csvpathid = f"{self._csvpath_id()} " if self._csvpath_id() else ""
465
- # ei = ExpressionUtility.get_my_expressions_index(self._matchable)
466
- # pm = f"{csvpathid}Wrong value in match component {ei} at line {pln}: {pm}"
467
- # raise ChildrenValidationException(pm)
468
- #
459
+ pm = f"mismatch in {self.matchable.my_chain}"
469
460
  ei = ExpressionUtility.get_my_expressions_index(self._matchable)
470
461
  pm = f"Wrong value in match component {ei}: {pm}"
462
+ lpm = f"{pm}: {mismatches}"
463
+ self._matchable.matcher.csvpath.logger.error(lpm)
471
464
  self._matchable.raiseChildrenException(pm)
@@ -31,6 +31,10 @@ class In(MatchDecider):
31
31
  v = f"{v}".strip()
32
32
  nvs = [_.strip() for _ in v.split("|")]
33
33
  inf += nvs
34
+ # elif isinstance(s, Reference) and s.is_header():
35
+ #
36
+ # do lookup here
37
+ #
34
38
  else:
35
39
  # tuple would mean vars were frozen. this would not be
36
40
  # surprising from a reference