csvpath 0.0.514__tar.gz → 0.0.516__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 (336) hide show
  1. {csvpath-0.0.514 → csvpath-0.0.516}/PKG-INFO +37 -33
  2. {csvpath-0.0.514 → csvpath-0.0.516}/README.md +36 -32
  3. {csvpath-0.0.514 → csvpath-0.0.516}/config/config.ini +15 -2
  4. csvpath-0.0.516/csvpath/cli/asker.py +90 -0
  5. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/cli/cli.py +68 -22
  6. csvpath-0.0.516/csvpath/cli/debug_config.py +94 -0
  7. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/cli/drill_down.py +14 -10
  8. csvpath-0.0.514/csvpath/cli/select.py → csvpath-0.0.516/csvpath/cli/selecter.py +24 -8
  9. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/csvpath.py +3 -0
  10. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/errors/error_comms.py +19 -2
  11. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/errors/error_manager.py +40 -4
  12. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/files/file_manager.py +17 -5
  13. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/files/file_metadata.py +3 -3
  14. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/files/file_registrar.py +7 -1
  15. csvpath-0.0.516/csvpath/managers/files/files_listener.py +84 -0
  16. csvpath-0.0.516/csvpath/managers/paths/paths_listener.py +72 -0
  17. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/paths/paths_manager.py +25 -40
  18. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/paths/paths_metadata.py +2 -2
  19. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/paths/paths_registrar.py +2 -0
  20. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/function_factory.py +6 -0
  21. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/append.py +1 -1
  22. csvpath-0.0.516/csvpath/matching/functions/math/odd.py +31 -0
  23. csvpath-0.0.516/csvpath/matching/functions/strings/caps.py +40 -0
  24. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/qualified.py +20 -2
  25. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/variable.py +7 -0
  26. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/error_mode.py +1 -0
  27. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/config.py +19 -1
  28. csvpath-0.0.516/docs/functions/odd.md +15 -0
  29. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/string_functions.md +5 -1
  30. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions.md +2 -0
  31. csvpath-0.0.516/docs/images/sftpplus-logo2.png +0 -0
  32. csvpath-0.0.516/docs/images/sftpplus-logo3.png +0 -0
  33. {csvpath-0.0.514 → csvpath-0.0.516}/docs/qualifiers.md +9 -1
  34. {csvpath-0.0.514 → csvpath-0.0.516}/pyproject.toml +1 -1
  35. csvpath-0.0.514/csvpath/cli/error.py +0 -61
  36. {csvpath-0.0.514 → csvpath-0.0.516}/LICENSE +0 -0
  37. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/__init__.py +0 -0
  38. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/cli/__init__.py +0 -0
  39. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/csvpaths.py +0 -0
  40. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/__init__.py +0 -0
  41. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/errors/error.py +0 -0
  42. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/errors/error_collector.py +0 -0
  43. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/files/file_cacher.py +0 -0
  44. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ckan/ckan.py +0 -0
  45. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ckan/ckan_listener.py +0 -0
  46. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ckan/datafile.py +0 -0
  47. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ckan/dataset.py +0 -0
  48. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/event.py +0 -0
  49. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/event_result.py +0 -0
  50. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/file_listener_ol.py +0 -0
  51. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/job.py +0 -0
  52. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/ol_listener.py +0 -0
  53. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/paths_listener_ol.py +0 -0
  54. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/result_listener_ol.py +0 -0
  55. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/results_listener_ol.py +0 -0
  56. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/run.py +0 -0
  57. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/run_listener_ol.py +0 -0
  58. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/run_state.py +0 -0
  59. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/ol/sender.py +0 -0
  60. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/error_metrics.py +0 -0
  61. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/metrics.py +0 -0
  62. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/otlp_error_listener.py +0 -0
  63. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/otlp_listener.py +0 -0
  64. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/otlp_result_listener.py +0 -0
  65. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/otlp/otlp_results_listener.py +0 -0
  66. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftp/sftp_sender.py +0 -0
  67. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftpplus/arrival_handler.py +0 -0
  68. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftpplus/rpc.py +0 -0
  69. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftpplus/sftpplus_listener.py +0 -0
  70. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftpplus/transfer_creator.py +0 -0
  71. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/sftpplus/transfers.py +0 -0
  72. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/slack/event.py +0 -0
  73. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/integrations/slack/sender.py +0 -0
  74. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/listener.py +0 -0
  75. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/metadata.py +0 -0
  76. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/registrar.py +0 -0
  77. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/readers/file_errors_reader.py +0 -0
  78. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/readers/file_lines_reader.py +0 -0
  79. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/readers/file_printouts_reader.py +0 -0
  80. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/readers/file_unmatched_reader.py +0 -0
  81. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/readers/readers.py +0 -0
  82. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/result.py +0 -0
  83. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/result_file_reader.py +0 -0
  84. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/result_metadata.py +0 -0
  85. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/result_registrar.py +0 -0
  86. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/result_serializer.py +0 -0
  87. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/results_manager.py +0 -0
  88. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/results_metadata.py +0 -0
  89. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/results/results_registrar.py +0 -0
  90. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/run/run_listener_stdout.py +0 -0
  91. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/run/run_metadata.py +0 -0
  92. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/managers/run/run_registrar.py +0 -0
  93. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/__init__.py +0 -0
  94. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/__init__.py +0 -0
  95. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/args.py +0 -0
  96. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/args_helper.py +0 -0
  97. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/all.py +0 -0
  98. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/andf.py +0 -0
  99. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/any.py +0 -0
  100. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/between.py +0 -0
  101. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/empty.py +0 -0
  102. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/exists.py +0 -0
  103. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/inf.py +0 -0
  104. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/no.py +0 -0
  105. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/notf.py +0 -0
  106. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/orf.py +0 -0
  107. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/boolean/yes.py +0 -0
  108. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/count.py +0 -0
  109. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/count_bytes.py +0 -0
  110. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/count_headers.py +0 -0
  111. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/count_lines.py +0 -0
  112. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/count_scans.py +0 -0
  113. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/counter.py +0 -0
  114. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/every.py +0 -0
  115. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/has_matches.py +0 -0
  116. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/increment.py +0 -0
  117. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/tally.py +0 -0
  118. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/counting/total_lines.py +0 -0
  119. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/dates/now.py +0 -0
  120. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/function.py +0 -0
  121. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/function_finder.py +0 -0
  122. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/function_focus.py +0 -0
  123. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/collect.py +0 -0
  124. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/empty_stack.py +0 -0
  125. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/end.py +0 -0
  126. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/header_name.py +0 -0
  127. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/header_names_mismatch.py +0 -0
  128. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/headers.py +0 -0
  129. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/mismatch.py +0 -0
  130. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/replace.py +0 -0
  131. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/headers/reset_headers.py +0 -0
  132. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/advance.py +0 -0
  133. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/after_blank.py +0 -0
  134. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/dups.py +0 -0
  135. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/first.py +0 -0
  136. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/first_line.py +0 -0
  137. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/last.py +0 -0
  138. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/lines/stop.py +0 -0
  139. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/above.py +0 -0
  140. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/add.py +0 -0
  141. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/divide.py +0 -0
  142. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/equals.py +0 -0
  143. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/intf.py +0 -0
  144. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/mod.py +0 -0
  145. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/multiply.py +0 -0
  146. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/round.py +0 -0
  147. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/subtotal.py +0 -0
  148. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/subtract.py +0 -0
  149. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/math/sum.py +0 -0
  150. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/misc/fingerprint.py +0 -0
  151. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/misc/importf.py +0 -0
  152. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/misc/random.py +0 -0
  153. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/print/jinjaf.py +0 -0
  154. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/print/print_line.py +0 -0
  155. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/print/print_queue.py +0 -0
  156. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/print/printf.py +0 -0
  157. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/print/table.py +0 -0
  158. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/stats/minf.py +0 -0
  159. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/stats/percent.py +0 -0
  160. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/stats/percent_unique.py +0 -0
  161. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/stats/stdev.py +0 -0
  162. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/concat.py +0 -0
  163. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/contains.py +0 -0
  164. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/length.py +0 -0
  165. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/lower.py +0 -0
  166. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/metaphone.py +0 -0
  167. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/regex.py +0 -0
  168. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/starts_with.py +0 -0
  169. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/strip.py +0 -0
  170. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/substring.py +0 -0
  171. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/strings/upper.py +0 -0
  172. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/testing/debug.py +0 -0
  173. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/__init__.py +0 -0
  174. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/boolean.py +0 -0
  175. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/datef.py +0 -0
  176. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/decimal.py +0 -0
  177. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/email.py +0 -0
  178. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/nonef.py +0 -0
  179. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/string.py +0 -0
  180. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/type.py +0 -0
  181. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/types/url.py +0 -0
  182. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/validity/fail.py +0 -0
  183. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/validity/failed.py +0 -0
  184. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/validity/line.py +0 -0
  185. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/variables/get.py +0 -0
  186. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/variables/pushpop.py +0 -0
  187. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/variables/put.py +0 -0
  188. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/variables/track.py +0 -0
  189. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/functions/variables/variables.py +0 -0
  190. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/lark_parser.py +0 -0
  191. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/lark_transformer.py +0 -0
  192. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/matcher.py +0 -0
  193. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/__init__.py +0 -0
  194. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/equality.py +0 -0
  195. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/expression.py +0 -0
  196. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/header.py +0 -0
  197. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/matchable.py +0 -0
  198. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/reference.py +0 -0
  199. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/productions/term.py +0 -0
  200. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/exceptions.py +0 -0
  201. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/expression_encoder.py +0 -0
  202. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/expression_utility.py +0 -0
  203. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/lark_print_parser.py +0 -0
  204. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/print_parser.py +0 -0
  205. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/matching/util/runtime_data_collector.py +0 -0
  206. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/explain_mode.py +0 -0
  207. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/files_mode.py +0 -0
  208. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/logic_mode.py +0 -0
  209. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/mode_controller.py +0 -0
  210. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/print_mode.py +0 -0
  211. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/return_mode.py +0 -0
  212. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/run_mode.py +0 -0
  213. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/source_mode.py +0 -0
  214. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/transfer_mode.py +0 -0
  215. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/unmatched_mode.py +0 -0
  216. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/modes/validation_mode.py +0 -0
  217. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/__init__.py +0 -0
  218. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/exceptions.py +0 -0
  219. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/parser.out +0 -0
  220. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/parsetab.py +0 -0
  221. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/scanner.py +0 -0
  222. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/scanning/scanning_lexer.py +0 -0
  223. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/box.py +0 -0
  224. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/cache.py +0 -0
  225. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/class_loader.py +0 -0
  226. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/config_exception.py +0 -0
  227. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/exceptions.py +0 -0
  228. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/file_info.py +0 -0
  229. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/file_readers.py +0 -0
  230. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/file_writers.py +0 -0
  231. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/hasher.py +0 -0
  232. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/last_line_stats.py +0 -0
  233. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/line_counter.py +0 -0
  234. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/line_monitor.py +0 -0
  235. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/line_spooler.py +0 -0
  236. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/log_utility.py +0 -0
  237. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/metadata_parser.py +0 -0
  238. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/nos.py +0 -0
  239. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/pandas_data_reader.py +0 -0
  240. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/printer.py +0 -0
  241. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/reference_parser.py +0 -0
  242. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/s3/s3_data_reader.py +0 -0
  243. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/s3/s3_data_writer.py +0 -0
  244. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/s3/s3_fingerprinter.py +0 -0
  245. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/s3/s3_utils.py +0 -0
  246. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/s3/s3_xlsx_data_reader.py +0 -0
  247. {csvpath-0.0.514 → csvpath-0.0.516}/csvpath/util/var_utility.py +0 -0
  248. {csvpath-0.0.514 → csvpath-0.0.516}/docs/asbool.md +0 -0
  249. {csvpath-0.0.514 → csvpath-0.0.516}/docs/assignment.md +0 -0
  250. {csvpath-0.0.514 → csvpath-0.0.516}/docs/comments.md +0 -0
  251. {csvpath-0.0.514 → csvpath-0.0.516}/docs/config.md +0 -0
  252. {csvpath-0.0.514 → csvpath-0.0.516}/docs/examples.md +0 -0
  253. {csvpath-0.0.514 → csvpath-0.0.516}/docs/files.md +0 -0
  254. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/above.md +0 -0
  255. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/advance.md +0 -0
  256. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/after_blank.md +0 -0
  257. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/all.md +0 -0
  258. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/andor.md +0 -0
  259. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/any.md +0 -0
  260. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/average.md +0 -0
  261. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/between.md +0 -0
  262. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/collect.md +0 -0
  263. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/contains.md +0 -0
  264. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/correlate.md +0 -0
  265. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/count.md +0 -0
  266. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/count_bytes.md +0 -0
  267. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/count_headers.md +0 -0
  268. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/counter.md +0 -0
  269. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/date.md +0 -0
  270. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/email.md +0 -0
  271. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/empty.md +0 -0
  272. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/empty_stack.md +0 -0
  273. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/end.md +0 -0
  274. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/equal.md +0 -0
  275. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/every.md +0 -0
  276. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/fail.md +0 -0
  277. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/fingerprint.md +0 -0
  278. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/first.md +0 -0
  279. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/get.md +0 -0
  280. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/has_dups.md +0 -0
  281. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/has_matches.md +0 -0
  282. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/header.md +0 -0
  283. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/header_name.md +0 -0
  284. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/header_names_mismatch.md +0 -0
  285. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/implementing_functions.md +0 -0
  286. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/import.md +0 -0
  287. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/in.md +0 -0
  288. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/increment.md +0 -0
  289. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/intf.md +0 -0
  290. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/jinja.md +0 -0
  291. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/last.md +0 -0
  292. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/line.md +0 -0
  293. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/line_number.md +0 -0
  294. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/max.md +0 -0
  295. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/metaphone.md +0 -0
  296. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/mismatch.md +0 -0
  297. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/no.md +0 -0
  298. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/not.md +0 -0
  299. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/now.md +0 -0
  300. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/percent_unique.md +0 -0
  301. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/pop.md +0 -0
  302. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/print.md +0 -0
  303. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/print_line.md +0 -0
  304. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/print_queue.md +0 -0
  305. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/random.md +0 -0
  306. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/regex.md +0 -0
  307. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/replace.md +0 -0
  308. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/reset_headers.md +0 -0
  309. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/stdev.md +0 -0
  310. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/stop.md +0 -0
  311. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/subtotal.md +0 -0
  312. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/subtract.md +0 -0
  313. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/sum.md +0 -0
  314. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/tally.md +0 -0
  315. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/total_lines.md +0 -0
  316. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/track.md +0 -0
  317. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/types.md +0 -0
  318. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/variables.md +0 -0
  319. {csvpath-0.0.514 → csvpath-0.0.516}/docs/functions/variables_and_headers.md +0 -0
  320. {csvpath-0.0.514 → csvpath-0.0.516}/docs/grammar.md +0 -0
  321. {csvpath-0.0.514 → csvpath-0.0.516}/docs/headers.md +0 -0
  322. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/ckan-logo-sm.png +0 -0
  323. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/csvpath-icon-sm.png +0 -0
  324. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/csvpath-logo-wordmark-tight-2.svg +0 -0
  325. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/logo-wordmark-3.svg +0 -0
  326. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/logo-wordmark-4.svg +0 -0
  327. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
  328. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/logo-wordmark-white-trimmed.png +0 -0
  329. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/marquez-logo-sm.png +0 -0
  330. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/openlineage-logo-sm.png +0 -0
  331. {csvpath-0.0.514 → csvpath-0.0.516}/docs/images/opentelemetry.png +0 -0
  332. {csvpath-0.0.514 → csvpath-0.0.516}/docs/paths.md +0 -0
  333. {csvpath-0.0.514 → csvpath-0.0.516}/docs/printing.md +0 -0
  334. {csvpath-0.0.514 → csvpath-0.0.516}/docs/references.md +0 -0
  335. {csvpath-0.0.514 → csvpath-0.0.516}/docs/terms.md +0 -0
  336. {csvpath-0.0.514 → csvpath-0.0.516}/docs/variables.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csvpath
3
- Version: 0.0.514
3
+ Version: 0.0.516
4
4
  Summary: An edge governance framework for managing and validating CSV, Excel, and other tabular data files
5
5
  Author: David Kershaw
6
6
  Author-email: dk107dk@hotmail.com
@@ -49,22 +49,22 @@ Description-Content-Type: text/markdown
49
49
 
50
50
  # <a href='https://www.csvpath.org/'><img src='https://github.com/csvpath/csvpath/blob/main/docs/images/logo-wordmark-4.svg'/></a>
51
51
 
52
- The CsvPath language defines a declarative syntax for inspecting and validating CSV and Excel files, and other tabular data.
52
+ The CsvPath Language defines a declarative syntax for inspecting and validating CSV and Excel files, and other tabular data.
53
53
 
54
- The CsvPath library's goal is to make it easy to setup a <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> flat-file landing zone that:
55
- - Registers files
56
- - Validates that the data matches expectations
57
- - Reports on content validity and other metadata
58
- - Shapes files for consistency using copy-on-write
59
- - And stages the results for loading into a data lake
54
+ The CsvPath Framework makes it easy to setup a <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> flat-file landing zone that:
55
+ - Registers files for **clear and durable identity**
56
+ - **Validates the data** against expectations
57
+ - Provides **operations and validity reports**
58
+ - Shapes files with **copy-on-write canonicalization**
59
+ - And **stages data for consistent loading** to a data lake or applications
60
60
 
61
61
  And does it all in an automation-friendly way.
62
62
 
63
- CsvPath's validation is inspired by:
63
+ CsvPath Language validation is inspired by:
64
64
  - XPath for XML files
65
65
  - The ISO standard <a href='https://schematron.com/'>Schematron validation</a>
66
66
 
67
- CsvPath is intended to fit tightly with other DataOps and data quality tools. Files are streamed. The interface is simple. Metadata is plentiful. New functions and listeners are easy to create.
67
+ The CsvPath Framework is intended to fit tightly with other DataOps and data quality tools. Files are streamed. The interface is simple. Metadata is plentiful. New functions and listeners are easy to create.
68
68
 
69
69
  CsvPath can stream lineage events to an OpenLineage server, such as the open source Marquez server. Read about <a href="https://www.csvpath.org/getting-started/getting-started-with-csvpath-+-openlineage" target="_blank">CsvPath and OpenLineage here</a>.
70
70
  <br/><a href='https://openlineage.io' >
@@ -75,6 +75,10 @@ CsvPath can stream lineage events to an OpenLineage server, such as the open sou
75
75
  DataOps demands observability! Pipe CsvPath events through OpenTelemetry to your APM or observability platform. Read about <a href='https://www.csvpath.org/getting-started/integrations/getting-started-with-csvpath-+-opentelemetry' target="_blank">how to get started here</a>, with an example using Grafana.
76
76
  <br/><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/opentelemetry.png" alt="OpenTelemetry Logo"/>
77
77
 
78
+ CsvPath has multiple MFT options including SFTPPlus. <a href="https://www.csvpath.org/getting-started/dataops-integrations/getting-started-with-csvpath-+-sftpplus" target="_blank">See how SFTPPlus + CsvPath improves data onboarding</a>.
79
+ <a href="https://sftpplus.com/" target="_blank"><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/sftpplus-logo3.png" alt="MFT with SFTPPlus"/></a>
80
+
81
+
78
82
  Need to publish validated datasets to a CKAN data portal? <a href="https://www.csvpath.org/getting-started/getting-started-with-csvpath-+-ckan" target="_blank">Read about how CsvPath is integrated with CKAN</a>.
79
83
  <a href="https://ckan.org/" target="_blank"><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/ckan-logo-sm.png" alt="CKAN Data Portal"/></a>
80
84
 
@@ -118,9 +122,13 @@ CSV files are everywhere!
118
122
 
119
123
  The majority of companies depend on file processing for significant revenue operations. Research organizations and archives are awash in CSVs. And everyone's favorite issue tracker, database GUI, spreadsheet, APM platform, and most any other type of tool we use spits out CSV or Excel files for sharing. Delimited and tabular files are the lowest of common dominators. Many are invalid or broken in some way. Often times a lot of manual effort goes into finding problems and fixing them.
120
124
 
121
- CsvPath is first and foremost a validation language. It describes tabular data in simple declarative rules that define what valid means for that data. CsvPath can also extract and shape data and create reports.
125
+ This project tackles two needs:
126
+ - A more robust validation language for delimited and tabular data
127
+ - A systems integration framework for flat-file landing and staging
128
+
129
+ CsvPath Language is first and foremost a validation language. It supports both schema definitions and rules-based validation. CsvPath Language describes data so you can easily tell if a file is valid. CsvPath can also extract and shape data and create reports. Overall the goal is to automate human judgement out of the processing loop and instead move it to the process definition stage.
122
130
 
123
- The CsvPath library implements the CsvPath language, but goes far beyond it to provide a full <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> framework for landing flat files, registering them, validating them, shaping them to a consistent and comparable form, and staging them for a data lake. In that way, CsvPath fills the gap commonly found between an organization's MFT (managed file transfer) and a typical data lake architecture.
131
+ The CsvPath Framework implements CsvPath Language, but goes far beyond it to provide a full <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> framework for landing flat files, registering them, validating them, shaping them to a consistent and comparable form, and staging them for a data lake. In that way, CsvPath fills the gap commonly found between an organization's MFT (managed file transfer) and a typical data lake architecture.
124
132
 
125
133
  CsvPath's goal is to make simple validations almost trivial and more complex situations more manageable. It is a library and framework, not a system, so it relies on being easy to integrate with other DataOps tools.
126
134
 
@@ -128,36 +136,27 @@ CsvPath's goal is to make simple validations almost trivial and more complex sit
128
136
  <a name="install"></a>
129
137
  # Install
130
138
 
131
- <a href='https://pypi.org/project/csvpath/'>CsvPath is available on PyPi</a>. It has been tested on 3.10, 3.11 and 3.13. Install with
139
+ <a href='https://pypi.org/project/csvpath/'>CsvPath is available on PyPi</a>. It has been tested on 3.10, 3.11 and 3.13.
140
+
141
+ The CsvPath Framework project uses Poetry. You can also install it with:
132
142
  ```
133
143
  pip install csvpath
134
144
  ```
135
145
 
136
- CsvPath has two optional dependencies:
137
-
138
- - <a target='_blank' href='https://pypi.org/project/pandas/'>Pandas</a>
139
- - <a target='_blank' href='https://pypi.org/project/smart-open/'>Smart-open</a>
140
-
141
- Pandas data frames can be used as a data source, much like Excel or CSV files. Install CsvPath with the Pandas option:
146
+ CsvPath has an optional dependency on Pandas. Pandas data frames can be used as a data source, much like Excel or CSV files. To install CsvPath with the Pandas option do:
142
147
  ```
143
148
  pip install csvpath[pandas]
144
149
  ```
145
150
 
146
- Smart-open is an option for loading data files directly from S3. Install the Smart Open extra with:
147
- ```
148
- pip install csvpath[smart-open]
149
- ```
150
-
151
- Both of these optional dependencies can make it harder to use CsvPath in certain specific use cases. For e.g., using Pandas in an AWS Lambda layer may be less straightforward. If you need the capabilities, they are easy to install, but if you don't CsvPath is lighter weight without.
152
-
151
+ Pandas and its dependencies can make it harder to use CsvPath in certain specific MFT use cases. For e.g., using Pandas in an AWS Lambda layer may be less straightforward. If you need the capability, though, it is easy to install.
153
152
 
154
153
  # Description
155
154
  <a name="description"></a>
156
155
 
157
- CsvPath paths have three parts:
156
+ CsvPath Language is for creating "paths" that walk line-by-line through tabular data. They have three parts:
158
157
  - a "root" file name
159
- - a scanning part
160
- - a matching part
158
+ - a scanning part that says what lines to validate
159
+ - a matching part that decides if a line is valid
161
160
 
162
161
  The root of a csvpath starts with `$`. The match and scan parts are enclosed by brackets. Newlines are ignored.
163
162
 
@@ -167,7 +166,12 @@ A very simple csvpath might look like this:
167
166
  $filename[*][yes()]
168
167
  ```
169
168
 
170
- This csvpath says open the file named `filename`, scan all the lines, and match every line scanned.
169
+ This csvpath says:
170
+ - Open the file: `filename`
171
+ - Scan all the lines: `*`
172
+ - And match every line scanned: `yes()`
173
+
174
+ In this case a match is considered a valid line. Treating matches as valid is a simple approach. There are <a href='https://www.csvpath.org/topics/validation' target='_blank'>many possible validation strategies</a> when its time to be more ambitious in your validation.
171
175
 
172
176
  A slightly more functional csvpath could look like this:
173
177
 
@@ -274,14 +278,14 @@ There are a small number of configuration options. Read <a href='https://github.
274
278
 
275
279
  Before we get into the details of scanning and matching, let's look at what CsvPath can print. The `print` function has several important uses, including:
276
280
 
277
- - Validating CSV files
281
+ - Validating CSV and Excel files
278
282
  - Debugging csvpaths
279
283
  - Creating new CSV files based on an existing file
280
284
 
281
285
  You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/printing.md'>read more about the mechanics of printing here</a>.
282
286
 
283
287
  <a name="validating"></a>
284
- ### Validating CSV
288
+ ### Validating CSV and Excel
285
289
 
286
290
  CsvPath paths can be used for rules based validation. Rules based validation checks a file against content and structure rules but does not validate the file's structure against a schema. This validation approach is similar to XML's Schematron validation, where XPath rules are applied to XML.
287
291
 
@@ -312,7 +316,7 @@ CsvPaths have file scanning instructions, match components, and comments. Commen
312
316
 
313
317
  As well as documentation, comments outside the csvpath can:
314
318
  - Contribute to a collection of metadata fields associated with a csvpath
315
- - Switch on/off certain validation settings
319
+ - Switch on/off certain validation and DataOps tool integration settings
316
320
  - Set the identity of a csvpath within a group of csvpaths
317
321
 
318
322
  A comment starts and ends with a `~` character. Within the comment, any word that has a colon after it is considered a metadata key. The metadata value is anything following the key up till a new metadata key word is seen or the comment ends.
@@ -1,22 +1,22 @@
1
1
 
2
2
  # <a href='https://www.csvpath.org/'><img src='https://github.com/csvpath/csvpath/blob/main/docs/images/logo-wordmark-4.svg'/></a>
3
3
 
4
- The CsvPath language defines a declarative syntax for inspecting and validating CSV and Excel files, and other tabular data.
4
+ The CsvPath Language defines a declarative syntax for inspecting and validating CSV and Excel files, and other tabular data.
5
5
 
6
- The CsvPath library's goal is to make it easy to setup a <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> flat-file landing zone that:
7
- - Registers files
8
- - Validates that the data matches expectations
9
- - Reports on content validity and other metadata
10
- - Shapes files for consistency using copy-on-write
11
- - And stages the results for loading into a data lake
6
+ The CsvPath Framework makes it easy to setup a <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> flat-file landing zone that:
7
+ - Registers files for **clear and durable identity**
8
+ - **Validates the data** against expectations
9
+ - Provides **operations and validity reports**
10
+ - Shapes files with **copy-on-write canonicalization**
11
+ - And **stages data for consistent loading** to a data lake or applications
12
12
 
13
13
  And does it all in an automation-friendly way.
14
14
 
15
- CsvPath's validation is inspired by:
15
+ CsvPath Language validation is inspired by:
16
16
  - XPath for XML files
17
17
  - The ISO standard <a href='https://schematron.com/'>Schematron validation</a>
18
18
 
19
- CsvPath is intended to fit tightly with other DataOps and data quality tools. Files are streamed. The interface is simple. Metadata is plentiful. New functions and listeners are easy to create.
19
+ The CsvPath Framework is intended to fit tightly with other DataOps and data quality tools. Files are streamed. The interface is simple. Metadata is plentiful. New functions and listeners are easy to create.
20
20
 
21
21
  CsvPath can stream lineage events to an OpenLineage server, such as the open source Marquez server. Read about <a href="https://www.csvpath.org/getting-started/getting-started-with-csvpath-+-openlineage" target="_blank">CsvPath and OpenLineage here</a>.
22
22
  <br/><a href='https://openlineage.io' >
@@ -27,6 +27,10 @@ CsvPath can stream lineage events to an OpenLineage server, such as the open sou
27
27
  DataOps demands observability! Pipe CsvPath events through OpenTelemetry to your APM or observability platform. Read about <a href='https://www.csvpath.org/getting-started/integrations/getting-started-with-csvpath-+-opentelemetry' target="_blank">how to get started here</a>, with an example using Grafana.
28
28
  <br/><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/opentelemetry.png" alt="OpenTelemetry Logo"/>
29
29
 
30
+ CsvPath has multiple MFT options including SFTPPlus. <a href="https://www.csvpath.org/getting-started/dataops-integrations/getting-started-with-csvpath-+-sftpplus" target="_blank">See how SFTPPlus + CsvPath improves data onboarding</a>.
31
+ <a href="https://sftpplus.com/" target="_blank"><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/sftpplus-logo3.png" alt="MFT with SFTPPlus"/></a>
32
+
33
+
30
34
  Need to publish validated datasets to a CKAN data portal? <a href="https://www.csvpath.org/getting-started/getting-started-with-csvpath-+-ckan" target="_blank">Read about how CsvPath is integrated with CKAN</a>.
31
35
  <a href="https://ckan.org/" target="_blank"><img target='_blank' src="https://github.com/csvpath/csvpath/blob/main/docs/images/ckan-logo-sm.png" alt="CKAN Data Portal"/></a>
32
36
 
@@ -70,9 +74,13 @@ CSV files are everywhere!
70
74
 
71
75
  The majority of companies depend on file processing for significant revenue operations. Research organizations and archives are awash in CSVs. And everyone's favorite issue tracker, database GUI, spreadsheet, APM platform, and most any other type of tool we use spits out CSV or Excel files for sharing. Delimited and tabular files are the lowest of common dominators. Many are invalid or broken in some way. Often times a lot of manual effort goes into finding problems and fixing them.
72
76
 
73
- CsvPath is first and foremost a validation language. It describes tabular data in simple declarative rules that define what valid means for that data. CsvPath can also extract and shape data and create reports.
77
+ This project tackles two needs:
78
+ - A more robust validation language for delimited and tabular data
79
+ - A systems integration framework for flat-file landing and staging
80
+
81
+ CsvPath Language is first and foremost a validation language. It supports both schema definitions and rules-based validation. CsvPath Language describes data so you can easily tell if a file is valid. CsvPath can also extract and shape data and create reports. Overall the goal is to automate human judgement out of the processing loop and instead move it to the process definition stage.
74
82
 
75
- The CsvPath library implements the CsvPath language, but goes far beyond it to provide a full <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> framework for landing flat files, registering them, validating them, shaping them to a consistent and comparable form, and staging them for a data lake. In that way, CsvPath fills the gap commonly found between an organization's MFT (managed file transfer) and a typical data lake architecture.
83
+ The CsvPath Framework implements CsvPath Language, but goes far beyond it to provide a full <a href='https://www.atestaanalytics.com/s/The-Collect-Store-Validate-Pattern-Atesta-Analytics.pdf'>Collect, Store, Validate Pattern</a> framework for landing flat files, registering them, validating them, shaping them to a consistent and comparable form, and staging them for a data lake. In that way, CsvPath fills the gap commonly found between an organization's MFT (managed file transfer) and a typical data lake architecture.
76
84
 
77
85
  CsvPath's goal is to make simple validations almost trivial and more complex situations more manageable. It is a library and framework, not a system, so it relies on being easy to integrate with other DataOps tools.
78
86
 
@@ -80,36 +88,27 @@ CsvPath's goal is to make simple validations almost trivial and more complex sit
80
88
  <a name="install"></a>
81
89
  # Install
82
90
 
83
- <a href='https://pypi.org/project/csvpath/'>CsvPath is available on PyPi</a>. It has been tested on 3.10, 3.11 and 3.13. Install with
91
+ <a href='https://pypi.org/project/csvpath/'>CsvPath is available on PyPi</a>. It has been tested on 3.10, 3.11 and 3.13.
92
+
93
+ The CsvPath Framework project uses Poetry. You can also install it with:
84
94
  ```
85
95
  pip install csvpath
86
96
  ```
87
97
 
88
- CsvPath has two optional dependencies:
89
-
90
- - <a target='_blank' href='https://pypi.org/project/pandas/'>Pandas</a>
91
- - <a target='_blank' href='https://pypi.org/project/smart-open/'>Smart-open</a>
92
-
93
- Pandas data frames can be used as a data source, much like Excel or CSV files. Install CsvPath with the Pandas option:
98
+ CsvPath has an optional dependency on Pandas. Pandas data frames can be used as a data source, much like Excel or CSV files. To install CsvPath with the Pandas option do:
94
99
  ```
95
100
  pip install csvpath[pandas]
96
101
  ```
97
102
 
98
- Smart-open is an option for loading data files directly from S3. Install the Smart Open extra with:
99
- ```
100
- pip install csvpath[smart-open]
101
- ```
102
-
103
- Both of these optional dependencies can make it harder to use CsvPath in certain specific use cases. For e.g., using Pandas in an AWS Lambda layer may be less straightforward. If you need the capabilities, they are easy to install, but if you don't CsvPath is lighter weight without.
104
-
103
+ Pandas and its dependencies can make it harder to use CsvPath in certain specific MFT use cases. For e.g., using Pandas in an AWS Lambda layer may be less straightforward. If you need the capability, though, it is easy to install.
105
104
 
106
105
  # Description
107
106
  <a name="description"></a>
108
107
 
109
- CsvPath paths have three parts:
108
+ CsvPath Language is for creating "paths" that walk line-by-line through tabular data. They have three parts:
110
109
  - a "root" file name
111
- - a scanning part
112
- - a matching part
110
+ - a scanning part that says what lines to validate
111
+ - a matching part that decides if a line is valid
113
112
 
114
113
  The root of a csvpath starts with `$`. The match and scan parts are enclosed by brackets. Newlines are ignored.
115
114
 
@@ -119,7 +118,12 @@ A very simple csvpath might look like this:
119
118
  $filename[*][yes()]
120
119
  ```
121
120
 
122
- This csvpath says open the file named `filename`, scan all the lines, and match every line scanned.
121
+ This csvpath says:
122
+ - Open the file: `filename`
123
+ - Scan all the lines: `*`
124
+ - And match every line scanned: `yes()`
125
+
126
+ In this case a match is considered a valid line. Treating matches as valid is a simple approach. There are <a href='https://www.csvpath.org/topics/validation' target='_blank'>many possible validation strategies</a> when its time to be more ambitious in your validation.
123
127
 
124
128
  A slightly more functional csvpath could look like this:
125
129
 
@@ -226,14 +230,14 @@ There are a small number of configuration options. Read <a href='https://github.
226
230
 
227
231
  Before we get into the details of scanning and matching, let's look at what CsvPath can print. The `print` function has several important uses, including:
228
232
 
229
- - Validating CSV files
233
+ - Validating CSV and Excel files
230
234
  - Debugging csvpaths
231
235
  - Creating new CSV files based on an existing file
232
236
 
233
237
  You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/printing.md'>read more about the mechanics of printing here</a>.
234
238
 
235
239
  <a name="validating"></a>
236
- ### Validating CSV
240
+ ### Validating CSV and Excel
237
241
 
238
242
  CsvPath paths can be used for rules based validation. Rules based validation checks a file against content and structure rules but does not validate the file's structure against a schema. This validation approach is similar to XML's Schematron validation, where XPath rules are applied to XML.
239
243
 
@@ -264,7 +268,7 @@ CsvPaths have file scanning instructions, match components, and comments. Commen
264
268
 
265
269
  As well as documentation, comments outside the csvpath can:
266
270
  - Contribute to a collection of metadata fields associated with a csvpath
267
- - Switch on/off certain validation settings
271
+ - Switch on/off certain validation and DataOps tool integration settings
268
272
  - Set the identity of a csvpath within a group of csvpaths
269
273
 
270
274
  A comment starts and ends with a `~` character. Within the comment, any word that has a colon after it is considered a metadata key. The metadata value is anything following the key up till a new metadata key word is seen or the comment ends.
@@ -7,6 +7,8 @@ extensions = txt, csv, tsv, dat, tab, psv, ssv
7
7
  [errors]
8
8
  csvpath = raise, collect, stop, fail, print
9
9
  csvpaths = raise, collect
10
+ use_format = full
11
+ pattern = {time}:{file}:{line}:{paths}:{instance}:{chain}: {message}
10
12
 
11
13
  [logging]
12
14
  csvpath = info
@@ -26,8 +28,19 @@ imports = config/functions.imports
26
28
 
27
29
  [listeners]
28
30
  groups =
29
- #slack, marquez, ckan, sftp, sftpplus, otlp
30
-
31
+ #slack, marquez, ckan, sftp, sftpplus, otlp, default
32
+
33
+ # these simple default listeners track all files staged and all paths loaded in
34
+ # central manifests. unlike other manifest writers, if they have multiple
35
+ # concurrent users they have potential a race condition. users that do not
36
+ # share inputs directories do not need to worry about this. you should vet
37
+ # using them against your use case; potential utility vs. the modest but non-0
38
+ # risk of lost updates. they can be disabled with little loss of function.
39
+ # alternatively a database-backed version may be available.
40
+ default.file = from csvpath.managers.files.files_listener import FilesListener
41
+ default.paths = from csvpath.managers.paths.paths_listener import PathsListener
42
+
43
+ # add otlp to support any OpenTelemetry backend
31
44
  otlp.result = from csvpath.managers.integrations.otlp.otlp_result_listener import OpenTelemetryResultListener
32
45
  otlp.results = from csvpath.managers.integrations.otlp.otlp_results_listener import OpenTelemetryResultsListener
33
46
  otlp.errors = from csvpath.managers.integrations.otlp.otlp_error_listener import OpenTelemetryErrorListener
@@ -0,0 +1,90 @@
1
+ from prompt_toolkit import Application
2
+ from prompt_toolkit.buffer import Buffer
3
+ from prompt_toolkit.layout.containers import (
4
+ HSplit,
5
+ VSplit,
6
+ Window,
7
+ ConditionalContainer,
8
+ )
9
+ from prompt_toolkit.layout.menus import CompletionsMenu
10
+ from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
11
+ from prompt_toolkit.layout.layout import Layout
12
+ from prompt_toolkit.key_binding import KeyBindings
13
+ from prompt_toolkit.completion import WordCompleter
14
+
15
+
16
+ class Asker:
17
+ def __init__(self, cli, *, name_type) -> None:
18
+ self._cli = cli
19
+ names = None
20
+ if name_type == "files":
21
+ names = self._cli.csvpaths.file_manager.named_file_names
22
+ names.sort()
23
+ elif name_type == "paths":
24
+ names = self._cli.csvpaths.file_manager.named_file_names
25
+ names.sort()
26
+ else:
27
+ raise ValueError("Name type must be files or paths")
28
+ self.completer = WordCompleter(names, ignore_case=True)
29
+ self.result = None
30
+
31
+ def create_prompt_application(self, prompt_text="Named-file name? "):
32
+ # Create a buffer to store input
33
+ buffer = Buffer(completer=self.completer, complete_while_typing=True)
34
+
35
+ # Create key bindings
36
+ kb = KeyBindings()
37
+
38
+ @kb.add("enter")
39
+ def _(event):
40
+ self.result = buffer.text
41
+ event.app.exit()
42
+
43
+ @kb.add("c-c")
44
+ @kb.add("c-q")
45
+ def _(event):
46
+ event.app.exit()
47
+
48
+ # Create the layout
49
+ layout = Layout(
50
+ HSplit(
51
+ [
52
+ # The actual prompt layout
53
+ VSplit(
54
+ [
55
+ # Prompt text
56
+ Window(
57
+ content=FormattedTextControl(prompt_text),
58
+ width=len(prompt_text),
59
+ dont_extend_width=True,
60
+ ),
61
+ # Input field with completions menu wrapped in a Window
62
+ Window(content=BufferControl(buffer=buffer)),
63
+ ]
64
+ )
65
+ ]
66
+ # , align_right=False
67
+ )
68
+ )
69
+
70
+ # Create and return the application
71
+ return Application(
72
+ layout=layout,
73
+ key_bindings=kb,
74
+ mouse_support=True,
75
+ full_screen=False,
76
+ erase_when_done=True,
77
+ # complete_while_typing=True
78
+ )
79
+
80
+ def ask(self) -> str:
81
+ try:
82
+ app = self.create_prompt_application()
83
+ app.run()
84
+ return self.result
85
+ except Exception as e:
86
+ print(f"Error: {e}")
87
+ import traceback
88
+
89
+ print(traceback.format_exc())
90
+ return None
@@ -4,13 +4,29 @@ import time
4
4
  import traceback
5
5
  from csvpath import CsvPaths
6
6
  from .drill_down import DrillDown
7
- from .select import Select
8
- from .error import Error
7
+ from .selecter import Selecter
8
+ from .debug_config import DebugConfig
9
9
 
10
10
 
11
11
  class Cli:
12
12
  def __init__(self):
13
13
  self.csvpaths = CsvPaths()
14
+ self.clear()
15
+ splash = """
16
+ *** * ****** ** **
17
+ *** ** * ** ** **** **
18
+ ** ** ** * ** *** ***** ** *****
19
+ ** * **** **** ***** *** ** ** ** **
20
+ ** **** ****** ** ** ** *** ** ** **
21
+ *** **** * ** *** ** **** **
22
+ ***************************
23
+ CsvPath Command Line Interface
24
+ Try tab completion and menu-by-key.
25
+ For help see https://www.csvpath.org
26
+ """
27
+ print(splash)
28
+ self._return_to_cont()
29
+ self.clear()
14
30
 
15
31
  def clear(self):
16
32
  print(chr(27) + "[2J")
@@ -28,6 +44,7 @@ class Cli:
28
44
  STOP_HERE2 = "👍 pick this dir"
29
45
  CANCEL = f"{SIDEBAR_COLOR}{ITALIC}... cancel{REVERT}"
30
46
  CANCEL2 = "← cancel"
47
+ QUIT = "← quit"
31
48
 
32
49
  def _return_to_cont(self):
33
50
  print(
@@ -52,14 +69,18 @@ class Cli:
52
69
  def end(self) -> None:
53
70
  print(chr(27) + "[2J")
54
71
 
55
- def ask(self, choices: list[str]) -> str:
72
+ def ask(self, choices: list[str], q=None) -> str:
56
73
  self.clear()
74
+ if len(choices) == 0:
75
+ return
76
+ if q is not None:
77
+ print(q)
57
78
  if choices[len(choices) - 1] == Cli.CANCEL:
58
79
  choices[len(choices) - 1] = Cli.CANCEL2
59
80
  if choices[len(choices) - 2] == Cli.STOP_HERE:
60
81
  choices[len(choices) - 2] = Cli.STOP_HERE2
61
82
  cs = [(s, s) for s in choices]
62
- t = Select().ask(title="", values=cs, cancel_value="CANCEL")
83
+ t = Selecter().ask(title="", values=cs, cancel_value="CANCEL")
63
84
  self.clear()
64
85
  return t
65
86
 
@@ -70,22 +91,22 @@ class Cli:
70
91
  choices = [
71
92
  "named-files",
72
93
  "named-paths",
73
- "named-results",
94
+ "archive",
74
95
  "run",
75
96
  "config",
76
- "quit",
97
+ self.QUIT,
77
98
  ]
78
99
  t = self.ask(choices)
79
100
  except KeyboardInterrupt:
80
101
  self.end()
81
102
  return
82
103
  t = self._do(t)
83
- if t == "quit":
104
+ if t == Cli.QUIT:
84
105
  self.end()
85
106
  return
86
107
 
87
108
  def _do(self, t: str) -> str | None:
88
- if t == "quit":
109
+ if t == Cli.QUIT:
89
110
  return t
90
111
  try:
91
112
  if t == "run":
@@ -94,21 +115,21 @@ class Cli:
94
115
  self._files()
95
116
  if t == "named-paths":
96
117
  self._paths()
97
- if t == "named-results":
118
+ if t == "archive":
98
119
  self._results()
99
120
  if t == "config":
100
121
  self._config()
101
122
  except KeyboardInterrupt:
102
- return "quit"
123
+ return Cli.QUIT
103
124
  except Exception:
104
125
  print(traceback.format_exc())
105
126
  self._return_to_cont()
106
127
 
107
128
  def _config(self) -> None:
108
- Error(self).show()
129
+ DebugConfig(self).show()
109
130
 
110
131
  def _files(self) -> None:
111
- choices = ["add named-file", "list named-files", "cancel"]
132
+ choices = ["add named-file", "list named-files", self.CANCEL2]
112
133
  t = self.ask(choices)
113
134
  if t == "add named-file":
114
135
  DrillDown(self).name_file()
@@ -116,7 +137,7 @@ class Cli:
116
137
  self.list_named_files()
117
138
 
118
139
  def _paths(self) -> None:
119
- choices = ["add named-paths", "list named-paths", "cancel"]
140
+ choices = ["add named-paths", "list named-paths", self.CANCEL2]
120
141
  t = self.ask(choices)
121
142
  if t == "add named-paths":
122
143
  DrillDown(self).name_paths()
@@ -124,7 +145,7 @@ class Cli:
124
145
  self.list_named_paths()
125
146
 
126
147
  def _results(self) -> None:
127
- choices = ["open named-result", "list named-results", "cancel"]
148
+ choices = ["open named-result", "list named-results", self.CANCEL2]
128
149
  t = self.ask(choices)
129
150
  if t == "open named-result":
130
151
  self.open_named_result()
@@ -136,6 +157,8 @@ class Cli:
136
157
  names = self.csvpaths.results_manager.list_named_results()
137
158
  print(f"{len(names)} named-results names:")
138
159
  for n in names:
160
+ if n.find(".") > -1:
161
+ continue
139
162
  self._response(f" {n}")
140
163
  self._return_to_cont()
141
164
 
@@ -143,6 +166,7 @@ class Cli:
143
166
  self.clear()
144
167
  try:
145
168
  names = self.csvpaths.results_manager.list_named_results()
169
+ names = [n for n in names if n.find(".") == -1]
146
170
  print(f"{len(names)} named-results names:")
147
171
  names.append(self.CANCEL)
148
172
  t = self.ask(names)
@@ -176,18 +200,25 @@ class Cli:
176
200
 
177
201
  def run(self):
178
202
  self.clear()
179
- print("What named-file? ")
180
203
  files = self.csvpaths.file_manager.named_file_names
181
- file = self.ask(files)
204
+ if len(files) == 0:
205
+ input("You must add a named-file. Press any key to continue.")
206
+ return
207
+ files.sort()
208
+ file = self.ask(files, q="What named-file? ")
182
209
  self.clear()
183
- print("What named-paths? ")
184
210
  allpaths = self.csvpaths.paths_manager.named_paths_names
185
- paths = self.ask(allpaths)
211
+ if len(allpaths) == 0:
212
+ input("You must add a named-paths file. Press any key to continue.")
213
+ return
214
+ allpaths.sort()
215
+ paths = self.ask(allpaths, q="What named-paths? ")
186
216
  self.clear()
187
- choices = ["collect", "fast-forward"]
188
- print("What method? ")
189
- method = self.ask(choices)
217
+ choices = ["collect", "fast-forward", Cli.CANCEL2]
218
+ method = self.ask(choices, q="What method? ")
190
219
  self.clear()
220
+ if method == Cli.CANCEL2:
221
+ return
191
222
  self.action(f"Running {paths} against {file} using {method}\n")
192
223
  self.pause()
193
224
  try:
@@ -196,7 +227,22 @@ class Cli:
196
227
  else:
197
228
  self.csvpaths.fast_forward_paths(filename=file, pathsname=paths)
198
229
  except Exception:
199
- print(traceback.format_exc())
230
+ cfg = None
231
+ while cfg in [None, "c", "e"]:
232
+ print("\nThere was an error.")
233
+ print("Click 'e' and return to print the stack trace. ")
234
+ print("Click 'c' and return to change config options. ")
235
+ print("Click return to continue. ")
236
+ cfg = input("")
237
+ if cfg == "c":
238
+ DebugConfig(self).show()
239
+ elif cfg == "e":
240
+ self.clear()
241
+ print(traceback.format_exc())
242
+ input("\n\nClick return to continue")
243
+ else:
244
+ return
245
+ self.clear()
200
246
  self._return_to_cont()
201
247
 
202
248