csvpath 0.0.508__tar.gz → 0.0.510__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 (309) hide show
  1. {csvpath-0.0.508 → csvpath-0.0.510}/PKG-INFO +2 -1
  2. {csvpath-0.0.508 → csvpath-0.0.510}/config/config.ini +4 -1
  3. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/csvpaths.py +10 -0
  4. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_manager.py +31 -1
  5. csvpath-0.0.510/csvpath/managers/integrations/otlp/metrics.py +52 -0
  6. csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_listener.py +38 -0
  7. csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_result_listener.py +46 -0
  8. csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_results_listener.py +32 -0
  9. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftp/sftp_sender.py +17 -2
  10. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/metadata.py +15 -0
  11. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_serializer.py +3 -0
  12. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_metadata.py +0 -1
  13. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_registrar.py +5 -4
  14. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/box.py +1 -1
  15. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/config.py +4 -3
  16. csvpath-0.0.510/csvpath/util/file_info.py +49 -0
  17. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/nos.py +6 -0
  18. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_data_reader.py +4 -1
  19. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_data_writer.py +9 -3
  20. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_utils.py +14 -9
  21. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_xlsx_data_reader.py +3 -6
  22. {csvpath-0.0.508 → csvpath-0.0.510}/pyproject.toml +3 -1
  23. csvpath-0.0.508/csvpath/util/file_info.py +0 -29
  24. {csvpath-0.0.508 → csvpath-0.0.510}/LICENSE +0 -0
  25. {csvpath-0.0.508 → csvpath-0.0.510}/README.md +0 -0
  26. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/__init__.py +0 -0
  27. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/__init__.py +0 -0
  28. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/cli.py +0 -0
  29. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/drill_down.py +0 -0
  30. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/csvpath.py +0 -0
  31. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/__init__.py +0 -0
  32. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_cacher.py +0 -0
  33. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_metadata.py +0 -0
  34. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_registrar.py +0 -0
  35. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/ckan.py +0 -0
  36. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/ckan_listener.py +0 -0
  37. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/datafile.py +0 -0
  38. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/dataset.py +0 -0
  39. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/event.py +0 -0
  40. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/event_result.py +0 -0
  41. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/file_listener_ol.py +0 -0
  42. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/job.py +0 -0
  43. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/ol_listener.py +0 -0
  44. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/paths_listener_ol.py +0 -0
  45. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/result_listener_ol.py +0 -0
  46. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/results_listener_ol.py +0 -0
  47. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run.py +0 -0
  48. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run_listener_ol.py +0 -0
  49. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run_state.py +0 -0
  50. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/sender.py +0 -0
  51. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/arrival_handler.py +0 -0
  52. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/sftpplus_listener.py +0 -0
  53. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/transfer_creator.py +0 -0
  54. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/slack/event.py +0 -0
  55. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/slack/sender.py +0 -0
  56. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/listener.py +0 -0
  57. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_manager.py +0 -0
  58. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_metadata.py +0 -0
  59. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_registrar.py +0 -0
  60. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/registrar.py +0 -0
  61. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_errors_reader.py +0 -0
  62. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_lines_reader.py +0 -0
  63. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_printouts_reader.py +0 -0
  64. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_unmatched_reader.py +0 -0
  65. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/readers.py +0 -0
  66. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result.py +0 -0
  67. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_file_reader.py +0 -0
  68. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_metadata.py +0 -0
  69. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_registrar.py +0 -0
  70. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_manager.py +0 -0
  71. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_listener_stdout.py +0 -0
  72. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_metadata.py +0 -0
  73. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_registrar.py +0 -0
  74. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/__init__.py +0 -0
  75. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/__init__.py +0 -0
  76. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/args.py +0 -0
  77. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/all.py +0 -0
  78. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/andf.py +0 -0
  79. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/any.py +0 -0
  80. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/between.py +0 -0
  81. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/empty.py +0 -0
  82. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/exists.py +0 -0
  83. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/inf.py +0 -0
  84. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/no.py +0 -0
  85. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/notf.py +0 -0
  86. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/orf.py +0 -0
  87. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/yes.py +0 -0
  88. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count.py +0 -0
  89. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_bytes.py +0 -0
  90. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_headers.py +0 -0
  91. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_lines.py +0 -0
  92. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_scans.py +0 -0
  93. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/counter.py +0 -0
  94. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/every.py +0 -0
  95. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/has_matches.py +0 -0
  96. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/increment.py +0 -0
  97. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/tally.py +0 -0
  98. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/total_lines.py +0 -0
  99. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/dates/now.py +0 -0
  100. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function.py +0 -0
  101. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_factory.py +0 -0
  102. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_finder.py +0 -0
  103. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_focus.py +0 -0
  104. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/append.py +0 -0
  105. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/collect.py +0 -0
  106. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/empty_stack.py +0 -0
  107. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/end.py +0 -0
  108. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/header_name.py +0 -0
  109. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/header_names_mismatch.py +0 -0
  110. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/headers.py +0 -0
  111. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/mismatch.py +0 -0
  112. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/replace.py +0 -0
  113. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/reset_headers.py +0 -0
  114. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/advance.py +0 -0
  115. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/after_blank.py +0 -0
  116. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/dups.py +0 -0
  117. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/first.py +0 -0
  118. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/first_line.py +0 -0
  119. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/last.py +0 -0
  120. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/stop.py +0 -0
  121. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/above.py +0 -0
  122. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/add.py +0 -0
  123. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/divide.py +0 -0
  124. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/equals.py +0 -0
  125. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/intf.py +0 -0
  126. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/mod.py +0 -0
  127. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/multiply.py +0 -0
  128. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/round.py +0 -0
  129. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/subtotal.py +0 -0
  130. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/subtract.py +0 -0
  131. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/sum.py +0 -0
  132. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/fingerprint.py +0 -0
  133. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/importf.py +0 -0
  134. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/random.py +0 -0
  135. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/jinjaf.py +0 -0
  136. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/print_line.py +0 -0
  137. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/print_queue.py +0 -0
  138. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/printf.py +0 -0
  139. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/table.py +0 -0
  140. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/minf.py +0 -0
  141. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/percent.py +0 -0
  142. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/percent_unique.py +0 -0
  143. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/stdev.py +0 -0
  144. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/concat.py +0 -0
  145. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/length.py +0 -0
  146. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/lower.py +0 -0
  147. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/metaphone.py +0 -0
  148. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/regex.py +0 -0
  149. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/starts_with.py +0 -0
  150. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/strip.py +0 -0
  151. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/substring.py +0 -0
  152. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/upper.py +0 -0
  153. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/testing/debug.py +0 -0
  154. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/__init__.py +0 -0
  155. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/boolean.py +0 -0
  156. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/datef.py +0 -0
  157. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/decimal.py +0 -0
  158. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/nonef.py +0 -0
  159. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/string.py +0 -0
  160. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/type.py +0 -0
  161. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/fail.py +0 -0
  162. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/failed.py +0 -0
  163. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/line.py +0 -0
  164. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/get.py +0 -0
  165. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/pushpop.py +0 -0
  166. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/put.py +0 -0
  167. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/track.py +0 -0
  168. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/variables.py +0 -0
  169. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/lark_parser.py +0 -0
  170. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/lark_transformer.py +0 -0
  171. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/matcher.py +0 -0
  172. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/__init__.py +0 -0
  173. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/equality.py +0 -0
  174. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/expression.py +0 -0
  175. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/header.py +0 -0
  176. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/matchable.py +0 -0
  177. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/qualified.py +0 -0
  178. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/reference.py +0 -0
  179. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/term.py +0 -0
  180. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/variable.py +0 -0
  181. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/exceptions.py +0 -0
  182. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/expression_encoder.py +0 -0
  183. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/expression_utility.py +0 -0
  184. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/lark_print_parser.py +0 -0
  185. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/print_parser.py +0 -0
  186. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/runtime_data_collector.py +0 -0
  187. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/explain_mode.py +0 -0
  188. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/files_mode.py +0 -0
  189. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/logic_mode.py +0 -0
  190. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/mode_controller.py +0 -0
  191. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/print_mode.py +0 -0
  192. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/return_mode.py +0 -0
  193. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/run_mode.py +0 -0
  194. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/source_mode.py +0 -0
  195. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/transfer_mode.py +0 -0
  196. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/unmatched_mode.py +0 -0
  197. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/validation_mode.py +0 -0
  198. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/__init__.py +0 -0
  199. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/exceptions.py +0 -0
  200. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/parser.out +0 -0
  201. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/parsetab.py +0 -0
  202. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/scanner.py +0 -0
  203. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/scanning_lexer.py +0 -0
  204. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/cache.py +0 -0
  205. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/class_loader.py +0 -0
  206. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/config_exception.py +0 -0
  207. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/error.py +0 -0
  208. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/exceptions.py +0 -0
  209. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/file_readers.py +0 -0
  210. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/file_writers.py +0 -0
  211. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/last_line_stats.py +0 -0
  212. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_counter.py +0 -0
  213. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_monitor.py +0 -0
  214. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_spooler.py +0 -0
  215. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/log_utility.py +0 -0
  216. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/metadata_parser.py +0 -0
  217. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/pandas_data_reader.py +0 -0
  218. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/printer.py +0 -0
  219. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/reference_parser.py +0 -0
  220. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_fingerprinter.py +0 -0
  221. {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/var_utility.py +0 -0
  222. {csvpath-0.0.508 → csvpath-0.0.510}/docs/asbool.md +0 -0
  223. {csvpath-0.0.508 → csvpath-0.0.510}/docs/assignment.md +0 -0
  224. {csvpath-0.0.508 → csvpath-0.0.510}/docs/comments.md +0 -0
  225. {csvpath-0.0.508 → csvpath-0.0.510}/docs/config.md +0 -0
  226. {csvpath-0.0.508 → csvpath-0.0.510}/docs/examples.md +0 -0
  227. {csvpath-0.0.508 → csvpath-0.0.510}/docs/files.md +0 -0
  228. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/above.md +0 -0
  229. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/advance.md +0 -0
  230. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/after_blank.md +0 -0
  231. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/all.md +0 -0
  232. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/andor.md +0 -0
  233. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/any.md +0 -0
  234. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/average.md +0 -0
  235. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/between.md +0 -0
  236. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/collect.md +0 -0
  237. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/correlate.md +0 -0
  238. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count.md +0 -0
  239. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count_bytes.md +0 -0
  240. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count_headers.md +0 -0
  241. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/counter.md +0 -0
  242. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/date.md +0 -0
  243. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/empty.md +0 -0
  244. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/empty_stack.md +0 -0
  245. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/end.md +0 -0
  246. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/every.md +0 -0
  247. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/fail.md +0 -0
  248. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/fingerprint.md +0 -0
  249. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/first.md +0 -0
  250. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/get.md +0 -0
  251. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/has_dups.md +0 -0
  252. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/has_matches.md +0 -0
  253. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header.md +0 -0
  254. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header_name.md +0 -0
  255. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header_names_mismatch.md +0 -0
  256. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/implementing_functions.md +0 -0
  257. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/import.md +0 -0
  258. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/in.md +0 -0
  259. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/increment.md +0 -0
  260. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/intf.md +0 -0
  261. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/jinja.md +0 -0
  262. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/last.md +0 -0
  263. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/line.md +0 -0
  264. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/line_number.md +0 -0
  265. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/max.md +0 -0
  266. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/metaphone.md +0 -0
  267. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/mismatch.md +0 -0
  268. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/no.md +0 -0
  269. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/not.md +0 -0
  270. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/now.md +0 -0
  271. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/percent_unique.md +0 -0
  272. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/pop.md +0 -0
  273. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print.md +0 -0
  274. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print_line.md +0 -0
  275. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print_queue.md +0 -0
  276. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/random.md +0 -0
  277. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/regex.md +0 -0
  278. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/replace.md +0 -0
  279. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/reset_headers.md +0 -0
  280. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/stdev.md +0 -0
  281. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/stop.md +0 -0
  282. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/string_functions.md +0 -0
  283. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/subtotal.md +0 -0
  284. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/subtract.md +0 -0
  285. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/sum.md +0 -0
  286. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/tally.md +0 -0
  287. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/total_lines.md +0 -0
  288. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/track.md +0 -0
  289. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/types.md +0 -0
  290. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/variables.md +0 -0
  291. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/variables_and_headers.md +0 -0
  292. {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions.md +0 -0
  293. {csvpath-0.0.508 → csvpath-0.0.510}/docs/grammar.md +0 -0
  294. {csvpath-0.0.508 → csvpath-0.0.510}/docs/headers.md +0 -0
  295. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/ckan-logo-sm.png +0 -0
  296. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/csvpath-icon-sm.png +0 -0
  297. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/csvpath-logo-wordmark-tight-2.svg +0 -0
  298. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-3.svg +0 -0
  299. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-4.svg +0 -0
  300. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
  301. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-white-trimmed.png +0 -0
  302. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/marquez-logo-sm.png +0 -0
  303. {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/openlineage-logo-sm.png +0 -0
  304. {csvpath-0.0.508 → csvpath-0.0.510}/docs/paths.md +0 -0
  305. {csvpath-0.0.508 → csvpath-0.0.510}/docs/printing.md +0 -0
  306. {csvpath-0.0.508 → csvpath-0.0.510}/docs/qualifiers.md +0 -0
  307. {csvpath-0.0.508 → csvpath-0.0.510}/docs/references.md +0 -0
  308. {csvpath-0.0.508 → csvpath-0.0.510}/docs/terms.md +0 -0
  309. {csvpath-0.0.508 → csvpath-0.0.510}/docs/variables.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csvpath
3
- Version: 0.0.508
3
+ Version: 0.0.510
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
@@ -32,6 +32,7 @@ Requires-Dist: lark (>=1.2.2,<2.0.0)
32
32
  Requires-Dist: marquez-python (>=0.50.0,<0.51.0)
33
33
  Requires-Dist: metaphone (>=0.6,<0.7)
34
34
  Requires-Dist: openlineage-python (>=1.25.0,<2.0.0)
35
+ Requires-Dist: opentelemetry-distro[otlp] (>=0.50b0,<0.51)
35
36
  Requires-Dist: paramiko (>=3.5.0,<4.0.0)
36
37
  Requires-Dist: ply (>=3.11,<4.0)
37
38
  Requires-Dist: pylightxl (>=1.61,<2.0)
@@ -26,7 +26,10 @@ imports = config/functions.imports
26
26
 
27
27
  [listeners]
28
28
  groups =
29
- #slack, marquez, ckan, sftp, sftpplus
29
+ #slack, marquez, ckan, sftp, sftpplus, otlp
30
+
31
+ otlp.result = from csvpath.managers.integrations.otlp.otlp_result_listener import OpenTelemetryResultListener
32
+ otlp.results = from csvpath.managers.integrations.otlp.otlp_results_listener import OpenTelemetryResultsListener
30
33
 
31
34
  # add sftpplus to the list of groups above to automate registration and named-paths group runs on file arrival at an SFTPPlus server
32
35
  sftpplus.paths = from csvpath.managers.integrations.sftpplus.sftpplus_listener import SftpPlusListener
@@ -148,6 +148,16 @@ class CsvPaths(CsvPathsPublic, CsvPathsCoordinator, ErrorCollector):
148
148
  self._advance_all = 0
149
149
  self._current_run_time = None
150
150
  self._run_time_str = None
151
+ #
152
+ # metrics is for OTLP OpenTelemetry. it should only
153
+ # be used by the OTLP listener. it is here because
154
+ # the integration may need a long-lived presence. if
155
+ # needed, the first OTLP listener will set it up
156
+ # before spinning up a thread. any other OTLP
157
+ # listener threads that need to use a long-lived metric
158
+ # will work with this property.
159
+ #
160
+ self.metrics = None
151
161
 
152
162
  def run_time_str(self, pathsname=None) -> str:
153
163
  """adds the stringified current run time to the named-paths
@@ -36,7 +36,37 @@ class FileManager:
36
36
  # named-file homes are a dir like: inputs/named_files/March-2024/March-2024.csv
37
37
  #
38
38
  def named_file_home(self, name: str) -> str:
39
- home = os.path.join(self.named_files_dir, name)
39
+ #
40
+ # not a named-file name
41
+ #
42
+ if name.find("/") > -1:
43
+ #
44
+ # this is definitely not what we should be returning. but it is what
45
+ # works in the new world of remote and fully-qualified local paths.
46
+ # for now, going with it. the previous implementation was wonky too,
47
+ # in a different and not visible way, but not good, so this is a step
48
+ # up in multiple ways.
49
+ #
50
+ return ""
51
+ #
52
+ # added
53
+ #
54
+ home = None
55
+ if name.startswith("/"):
56
+ home = name
57
+ else:
58
+ #
59
+ # done add
60
+ #
61
+ home = os.path.join(self.named_files_dir, name)
62
+ #
63
+ # added
64
+ #
65
+ if Nos(home).isfile():
66
+ home = home[0 : home.rfind(Nos(home).sep)]
67
+ #
68
+ # done add
69
+ #
40
70
  return home
41
71
 
42
72
  def assure_named_file_home(self, name: str) -> str:
@@ -0,0 +1,52 @@
1
+ from opentelemetry import metrics
2
+ from opentelemetry.sdk.metrics import MeterProvider
3
+ from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
4
+ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
5
+ from opentelemetry.sdk.metrics.export import InMemoryMetricReader
6
+ from csvpath.managers.listener import Listener
7
+
8
+
9
+ class Metrics:
10
+ def __init__(self, listener: Listener, exporting=True):
11
+ self.listener = listener
12
+ self.reader = None
13
+ self.provider = None
14
+ self.meter = None
15
+ if exporting:
16
+ self.reader = PeriodicExportingMetricReader(OTLPMetricExporter())
17
+ self.provider = MeterProvider(metric_readers=[self.reader])
18
+ metrics.set_meter_provider(self.provider)
19
+ else:
20
+ self.reader = InMemoryMetricReader()
21
+ self.provider = MeterProvider(metric_readers=[self.reader])
22
+ metrics.set_meter_provider(self.provider)
23
+
24
+ self.meter = metrics.get_meter("csvpath")
25
+ # Create metrics
26
+ self.runs_started = self.meter.create_counter(
27
+ "runs_started", description="Number of named-files started"
28
+ )
29
+ self.runs_completed = self.meter.create_counter(
30
+ "runs_completed", description="Number of named-files completed"
31
+ )
32
+ self.errors = self.meter.create_counter(
33
+ "errors", description="Number of errors found at the end of a csvpath's run"
34
+ )
35
+ self.files = self.meter.create_counter(
36
+ "files_generated",
37
+ description="Number of result files generated by an instance",
38
+ )
39
+ self.valid = self.meter.create_counter(
40
+ "valid_files", description="Number of named-files considered valid"
41
+ )
42
+ self.files_expected = self.meter.create_counter(
43
+ "files_expected",
44
+ description="Were all the files expected actually produced?",
45
+ )
46
+ self.size = self.meter.create_histogram(
47
+ "named_file_size_distribution",
48
+ description="Distribution of processed file sizes",
49
+ )
50
+ self.bytes = self.meter.create_counter(
51
+ "bytes", description="Number of bytes in the named-files"
52
+ )
@@ -0,0 +1,38 @@
1
+ from csvpath.managers.listener import Listener
2
+ from csvpath.managers.metadata import Metadata
3
+ from csvpath.managers.results.results_registrar import ResultsRegistrar
4
+ from .metrics import Metrics
5
+
6
+
7
+ class OtlpListener(Listener):
8
+ def __init__(self, config=None):
9
+ super().__init__(config=config)
10
+ self.csvpaths = None
11
+ self.result = None
12
+
13
+ def core_meta(self, mdata: Metadata) -> dict:
14
+ cmeta = {}
15
+ if mdata.named_file_name:
16
+ cmeta["file"] = mdata.named_file_name
17
+ if mdata.named_results_name:
18
+ cmeta["paths"] = mdata.named_results_name
19
+ if mdata.archive_name:
20
+ cmeta["archive"] = mdata.archive_name
21
+ if mdata.archive_path:
22
+ cmeta["archive_path"] = mdata.archive_path
23
+ if mdata.named_files_root:
24
+ cmeta["named_files_root"] = mdata.named_files_root
25
+ if mdata.named_paths_root:
26
+ cmeta["named_paths_root"] = mdata.named_paths_root
27
+ if mdata.uuid_string:
28
+ cmeta["uuid"] = mdata.uuid_string
29
+ if mdata.run_home:
30
+ cmeta["run_home"] = mdata.run_home
31
+ if mdata.hostname:
32
+ cmeta["hostname"] = mdata.hostname
33
+ if mdata.username:
34
+ cmeta["username"] = mdata.username
35
+ if mdata.ip_address:
36
+ cmeta["ip_address"] = mdata.ip_address
37
+
38
+ return cmeta
@@ -0,0 +1,46 @@
1
+ from csvpath.managers.listener import Listener
2
+ from csvpath.managers.metadata import Metadata
3
+ from csvpath.managers.results.results_registrar import ResultsRegistrar
4
+ from .otlp_listener import OtlpListener
5
+ from .metrics import Metrics
6
+
7
+
8
+ class OpenTelemetryResultListener(OtlpListener):
9
+ def __init__(self, config=None):
10
+ super().__init__(config=config)
11
+ self.csvpaths = None
12
+ self.result = None
13
+
14
+ def metadata_update(self, mdata: Metadata) -> None:
15
+ if self.csvpaths and self.csvpaths.metrics is None:
16
+ self.csvpaths.metrics = Metrics(self)
17
+ self.csvpaths.metrics.errors.add(mdata.error_count, self.core_meta(mdata))
18
+ if mdata.error_count:
19
+ self.csvpaths.metrics.errors.add(mdata.error_count, self.core_meta(mdata))
20
+ if mdata.valid:
21
+ v = 0
22
+ if mdata.valid is True:
23
+ v = 1
24
+ self.csvpaths.metrics.valid.add(v, self.core_meta(mdata))
25
+ if mdata.file_count:
26
+ self.csvpaths.metrics.files.add(mdata.file_count, self.core_meta(mdata))
27
+
28
+ yn = mdata.files_expected
29
+ if yn and not isinstance(yn, bool):
30
+ raise ValueError("you must convert")
31
+ if yn is True:
32
+ yn = 1
33
+ else:
34
+ yn = 0
35
+ self.csvpaths.metrics.files_expected.add(yn, self.core_meta(mdata))
36
+
37
+ def core_meta(self, mdata):
38
+ cmeta = super().core_meta(mdata)
39
+ if mdata.instance_identity:
40
+ cmeta["instance"] = mdata.instance_identity
41
+ #
42
+ # named_paths_uuid_string should be on results as well
43
+ #
44
+ if mdata.named_paths_uuid_string:
45
+ cmeta["named_paths_uuid_string"] = mdata.named_paths_uuid_string
46
+ return cmeta
@@ -0,0 +1,32 @@
1
+ from csvpath.managers.listener import Listener
2
+ from csvpath.managers.metadata import Metadata
3
+ from csvpath.managers.results.results_registrar import ResultsRegistrar
4
+ from .otlp_listener import OtlpListener
5
+ from .metrics import Metrics
6
+
7
+
8
+ class OpenTelemetryResultsListener(OtlpListener):
9
+ def __init__(self, config=None):
10
+ super().__init__(config=config)
11
+ self.csvpaths = None
12
+
13
+ def metadata_update(self, mdata: Metadata) -> None:
14
+
15
+ if self.csvpaths and self.csvpaths.metrics is None:
16
+ self.csvpaths.metrics = Metrics(self)
17
+
18
+ if mdata.status == ResultsRegistrar.COMPLETE:
19
+ print("completing!")
20
+ self.csvpaths.metrics.runs_completed.add(1, self.core_meta(mdata))
21
+ else:
22
+ print("starting")
23
+ self.csvpaths.metrics.runs_started.add(1, self.core_meta(mdata))
24
+ size = mdata.named_file_size
25
+ if size:
26
+ try:
27
+ size = int(size)
28
+ except ValueError:
29
+ size = None
30
+ if size is not None:
31
+ self.csvpaths.metrics.bytes.add(size, self.core_meta(mdata))
32
+ self.csvpaths.metrics.size.record(size, self.core_meta(mdata))
@@ -7,6 +7,7 @@ from csvpath.managers.results.result import Result
7
7
  from csvpath.managers.listener import Listener
8
8
  from csvpath.util.nos import Nos
9
9
  from csvpath.util.var_utility import VarUtility
10
+ from csvpath.util.file_readers import DataFileReader
10
11
 
11
12
 
12
13
  #
@@ -105,10 +106,24 @@ class SftpSender(Listener, threading.Thread):
105
106
  raise ValueError("File name {file} is not in {files}")
106
107
  if to is None:
107
108
  raise ValueError("File name {file} has no destination")
108
- path = f"/Users/davidkershaw/dev/csvpath/{self.metadata.run_home}{sep}{self.result.identity_or_index}{sep}{file}"
109
+ path = f"{self.metadata.run_home}{sep}{self.result.identity_or_index}{sep}{file}"
109
110
  remote_path = f"{self._target_path}/{to}"
110
111
  self.csvpaths.logger.info("Putting %s to %s", path, remote_path)
111
- sftp.put(path, remote_path)
112
+ #
113
+ # need to use data reader and stream. if we're
114
+ # sftping from s3 or something this is going to
115
+ # be slow
116
+ #
117
+ # sftp.put(path, remote_path)
118
+ try:
119
+ sftp.stat(self._target_path)
120
+ except FileNotFoundError:
121
+ sftp.mkdir(self._target_path)
122
+ with DataFileReader(path) as reader:
123
+ flo = reader.source
124
+ f = sftp.open(remote_path, "wb")
125
+ f.write(flo.read())
126
+ f.close()
112
127
  #
113
128
  # send the original file if we need to. this will always be the normative
114
129
  # original, w/o regard to source-mode or by_line chaining or to rewind.
@@ -3,6 +3,8 @@ import os
3
3
  from abc import ABC
4
4
  from datetime import datetime, timezone
5
5
  from dateutil import parser
6
+ import getpass
7
+ import socket
6
8
 
7
9
 
8
10
  class Metadata(ABC):
@@ -18,6 +20,19 @@ class Metadata(ABC):
18
20
  self._base_path = None
19
21
  self._named_files_root: str = None
20
22
  self._named_paths_root: str = None
23
+ self.username = None
24
+ try:
25
+ self.username = getpass.getuser()
26
+ except Exception:
27
+ ...
28
+ self.hostname = None
29
+ self.ip_address = None
30
+ try:
31
+ self.hostname = socket.gethostname()
32
+ self.ip_address = socket.gethostbyname(self.hostname)
33
+ except Exception:
34
+ ...
35
+
21
36
  #
22
37
  # find base dir so we can add file:// refs, if needed
23
38
  #
@@ -24,6 +24,9 @@ class ResultSerializer:
24
24
  def save_result(self, result) -> None:
25
25
  self.result = result
26
26
  runtime_data = {}
27
+ result.csvpath.csvpaths.logger.debug(
28
+ "Saving result of %s.%s", result.paths_name, result.identity_or_index
29
+ )
27
30
  RuntimeDataCollector.collect(result.csvpath, runtime_data, local=True)
28
31
  runtime_data["run_index"] = result.run_index
29
32
  es = []
@@ -1,5 +1,4 @@
1
1
  from csvpath.managers.metadata import Metadata
2
-
3
2
  from datetime import datetime
4
3
 
5
4
 
@@ -106,6 +106,9 @@ class ResultsRegistrar(Registrar, Listener):
106
106
  m["named_file_last_change"] = mdata.named_file_last_change
107
107
  m["named_file_fingerprint"] = mdata.named_file_fingerprint
108
108
  m["named_file_fingerprint_on_file"] = mdata.named_file_fingerprint_on_file
109
+ m["hostname"] = mdata.hostname
110
+ m["username"] = mdata.username
111
+ m["ip_address"] = mdata.ip_address
109
112
  mp = mdata.manifest_path
110
113
  m["manifest_path"] = mp
111
114
  with DataFileWriter(path=mp) as file:
@@ -119,16 +122,14 @@ class ResultsRegistrar(Registrar, Listener):
119
122
  def _size(self, path) -> str:
120
123
  try:
121
124
  fi = FileInfo.info(path)
122
- return fi["bytes"] if fi and "bytes" in fi else -1
125
+ return fi["bytes"]
123
126
  except FileNotFoundError:
124
127
  return 0
125
128
 
126
129
  def _last_change(self, path) -> str:
127
130
  try:
128
131
  fi = FileInfo.info(path)
129
- if fi and "last_mod" in fi:
130
- return fi["last_mod"]
131
- return None
132
+ return fi["last_mod"]
132
133
  except FileNotFoundError:
133
134
  return -1
134
135
 
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # just a box to put things in. initial use is
3
- # sharing a boto3 client under "boto_client".
3
+ # sharing a boto3 client under "boto_s3_client".
4
4
  #
5
5
  class Box:
6
6
  STUFF = {}
@@ -175,10 +175,11 @@ path =
175
175
  [listeners]
176
176
  # add listener group names to send events to the channel they represent
177
177
  groups =
178
- #slack, marquez, ckan, sftp, sftpplus
178
+ #slack, marquez, ckan, sftp, sftpplus, otlp
179
179
 
180
- # add sftpplus to the list of groups above to automate registration and named-paths group runs on file arrival at an SFTPPlus server
181
- sftpplus.paths = from csvpath.managers.integrations.sftpplus.sftpplus_listener import SftpPlusListener
180
+ # add otlp to the list of groups above to push observability metrics to an OpenTelemetry endpoint
181
+ otlp.result = from csvpath.managers.integrations.otlp.otlp_result_listener import OpenTelemetryResultListener
182
+ otlp.results = from csvpath.managers.integrations.otlp.otlp_results_listener import OpenTelemetryResultsListener
182
183
 
183
184
  # add sftp to the list of groups above to push content and metadata to an SFTP account
184
185
  sftp.results = from csvpath.managers.integrations.sftp.sftp_listener import SftpListener
@@ -0,0 +1,49 @@
1
+ import os
2
+
3
+
4
+ class FileInfo:
5
+ @classmethod
6
+ def info(cls, path) -> dict[str, str | int | float]:
7
+ if path is None:
8
+ raise ValueError("Path cannot be None")
9
+ try:
10
+ if path.find("://") > -1:
11
+ return cls._remote(path)
12
+ return cls._local(path)
13
+ except Exception:
14
+ # this shouldn't happen, but it also shouldn't be the
15
+ # main source of friction. If we have a wrong path or
16
+ # an asset we don't know how to get info on, we'll know it
17
+ # above and can handle it there in a more meaningful
18
+ # context.
19
+ return cls._empty()
20
+
21
+ @classmethod
22
+ def _remote(cls, path):
23
+ return cls._empty()
24
+
25
+ @classmethod
26
+ def _empty(cls) -> dict:
27
+ return {
28
+ "mode": "",
29
+ "device": "",
30
+ "bytes": -1,
31
+ "created": None,
32
+ "last_read": None,
33
+ "last_mod": None,
34
+ "flags": None,
35
+ }
36
+
37
+ @classmethod
38
+ def _local(cls, path):
39
+ s = os.stat(path)
40
+ meta = {
41
+ "mode": s.st_mode,
42
+ "device": s.st_dev,
43
+ "bytes": s.st_size,
44
+ "created": s.st_ctime,
45
+ "last_read": s.st_atime,
46
+ "last_mod": s.st_mtime,
47
+ "flags": s.st_flags,
48
+ }
49
+ return meta
@@ -139,6 +139,12 @@ class S3Do:
139
139
  def isfile(self) -> bool:
140
140
  bucket, key = S3Utils.path_to_parts(self.path)
141
141
  client = boto3.client("s3")
142
+ #
143
+ # boto3 uses a deprecated feature. pytest doesn't like it. this is a quick fix.
144
+ #
145
+ import warnings
146
+
147
+ warnings.filterwarnings(action="ignore", message=r"datetime.datetime.utcnow")
142
148
  try:
143
149
  client.head_object(Bucket=bucket, Key=key)
144
150
  except ClientError as e:
@@ -16,13 +16,16 @@ from csvpath.util.box import Box
16
16
  class S3DataReader(CsvDataReader):
17
17
  def load_if(self) -> None:
18
18
  if self.source is None:
19
- client = Box.STUFF.get("boto_client")
19
+ client = Box.STUFF.get("boto_s3_client")
20
20
  if client is None:
21
+ client = S3Utils.make_client()
22
+ """
21
23
  session = boto3.Session(
22
24
  aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
23
25
  aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
24
26
  )
25
27
  client = session.client("s3")
28
+ """
26
29
  try:
27
30
  self.source = open(self._path, "r", transport_params={"client": client})
28
31
  except DeprecationWarning:
@@ -5,23 +5,26 @@ import boto3
5
5
  from smart_open import open
6
6
  from ..file_writers import DataFileWriter
7
7
  from csvpath.util.box import Box
8
+ from csvpath.util.s3.s3_utils import S3Utils
8
9
 
9
10
 
10
11
  class S3DataWriter(DataFileWriter):
11
12
  def load_if(self) -> None:
12
13
  if self.sink is None:
13
- client = Box.STUFF.get("boto_client")
14
+ client = Box.STUFF.get("boto_s3_client")
14
15
  if client is None:
16
+ client = S3Utils.make_client()
17
+ """
15
18
  session = boto3.Session(
16
19
  aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
17
20
  aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
18
21
  )
19
22
  import warnings
20
-
21
23
  warnings.filterwarnings(
22
24
  action="ignore", message=r"datetime.datetime.utcnow"
23
25
  )
24
26
  client = session.client("s3")
27
+ """
25
28
  self.sink = open(
26
29
  self._path,
27
30
  self._mode,
@@ -33,13 +36,16 @@ class S3DataWriter(DataFileWriter):
33
36
  as a context manager for this method. for multiple write
34
37
  calls to the same file handle use append().
35
38
  """
36
- client = Box.STUFF.get("boto_client")
39
+ client = Box.STUFF.get("boto_s3_client")
37
40
  if client is None:
41
+ client = S3Utils.make_client()
42
+ """
38
43
  session = boto3.Session(
39
44
  aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
40
45
  aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
41
46
  )
42
47
  client = session.client("s3")
48
+ """
43
49
  with open(self._path, "wb", transport_params={"client": client}) as file:
44
50
  file.write(data.encode("utf-8"))
45
51
 
@@ -1,9 +1,23 @@
1
+ import os
1
2
  import boto3
2
3
  import uuid
3
4
  from botocore.exceptions import ClientError
5
+ from csvpath.util.box import Box
4
6
 
5
7
 
6
8
  class S3Utils:
9
+ @classmethod
10
+ def make_client(cls):
11
+ import warnings
12
+
13
+ warnings.filterwarnings(action="ignore", message=r"datetime.datetime.utcnow")
14
+ session = boto3.Session(
15
+ aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
16
+ aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
17
+ )
18
+ client = session.client("s3")
19
+ return client
20
+
7
21
  @classmethod
8
22
  def path_to_parts(self, path) -> tuple[str, str]:
9
23
  if path.startswith("s3://"):
@@ -53,14 +67,5 @@ class S3Utils:
53
67
 
54
68
  @classmethod
55
69
  def rename(self, bucket: str, key: str, new_key: str) -> None:
56
- """
57
- client = boto3.client("s3")
58
- r = client.copy_object(
59
- Bucket=bucket,
60
- CopySource={'Bucket': bucket, 'Key': key},
61
- Key=new_key,
62
- ChecksumAlgorithm='SHA256'
63
- )
64
- """
65
70
  S3Utils.copy(bucket, key, bucket, new_key)
66
71
  S3Utils.remove(bucket, key)
@@ -6,6 +6,7 @@ from smart_open import open
6
6
  from ..file_readers import XlsxDataReader
7
7
  from .s3_fingerprinter import S3Fingerprinter
8
8
  from csvpath.util.box import Box
9
+ from csvpath.util.s3.s3_utils import S3Utils
9
10
 
10
11
 
11
12
  class S3XlsxDataReader(XlsxDataReader):
@@ -19,13 +20,9 @@ class S3XlsxDataReader(XlsxDataReader):
19
20
 
20
21
  def load_if(self) -> None:
21
22
  if self.source is None:
22
- client = Box.STUFF.get("boto_client")
23
+ client = Box.STUFF.get("boto_s3_client")
23
24
  if client is None:
24
- session = boto3.Session(
25
- aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
26
- aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
27
- )
28
- client = session.client("s3")
25
+ client = S3Utils.make_client()
29
26
  try:
30
27
  self.source = open(
31
28
  self._path, "rb", transport_params={"client": client}
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "csvpath"
3
- version = "0.0.508"
3
+ version = "0.0.510"
4
4
  description = "An edge governance framework for managing and validating CSV, Excel, and other tabular data files"
5
5
  authors = ["David Kershaw <dk107dk@hotmail.com>"]
6
6
  readme = "README.md"
@@ -49,6 +49,7 @@ ckanapi = "^4.8"
49
49
  smart-open = "^7.1.0"
50
50
  paramiko = "^3.5.0"
51
51
  boto3 = "^1.35.91"
52
+ opentelemetry-distro = {extras = ["otlp"], version = "^0.50b0"}
52
53
 
53
54
  [tool.poetry.extras]
54
55
  pandas = ["pandas"]
@@ -62,6 +63,7 @@ pydeps = "^1.12.20"
62
63
  pylint = "^3.2.7"
63
64
  coverage = "^7.6.1"
64
65
  pytest = "^8.2.2"
66
+ pytest-profiling = "^1.8.1"
65
67
 
66
68
  [build-system]
67
69
  requires = ["poetry-core"]
@@ -1,29 +0,0 @@
1
- import os
2
-
3
-
4
- class FileInfo:
5
- @classmethod
6
- def info(cls, path) -> dict[str, str | int | float]:
7
- if path is None:
8
- raise ValueError("Path cannot be None")
9
- if path.find("://") > -1:
10
- return cls._remote(path)
11
- return cls._local(path)
12
-
13
- @classmethod
14
- def _remote(cls, path):
15
- return {}
16
-
17
- @classmethod
18
- def _local(cls, path):
19
- s = os.stat(path)
20
- meta = {
21
- "mode": s.st_mode,
22
- "device": s.st_dev,
23
- "bytes": s.st_size,
24
- "created": s.st_ctime,
25
- "last_read": s.st_atime,
26
- "last_mod": s.st_mtime,
27
- "flags": s.st_flags,
28
- }
29
- return meta
File without changes
File without changes
File without changes
File without changes
File without changes