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.
- {csvpath-0.0.508 → csvpath-0.0.510}/PKG-INFO +2 -1
- {csvpath-0.0.508 → csvpath-0.0.510}/config/config.ini +4 -1
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/csvpaths.py +10 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_manager.py +31 -1
- csvpath-0.0.510/csvpath/managers/integrations/otlp/metrics.py +52 -0
- csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_listener.py +38 -0
- csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_result_listener.py +46 -0
- csvpath-0.0.510/csvpath/managers/integrations/otlp/otlp_results_listener.py +32 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftp/sftp_sender.py +17 -2
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/metadata.py +15 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_serializer.py +3 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_metadata.py +0 -1
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_registrar.py +5 -4
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/box.py +1 -1
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/config.py +4 -3
- csvpath-0.0.510/csvpath/util/file_info.py +49 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/nos.py +6 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_data_reader.py +4 -1
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_data_writer.py +9 -3
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_utils.py +14 -9
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_xlsx_data_reader.py +3 -6
- {csvpath-0.0.508 → csvpath-0.0.510}/pyproject.toml +3 -1
- csvpath-0.0.508/csvpath/util/file_info.py +0 -29
- {csvpath-0.0.508 → csvpath-0.0.510}/LICENSE +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/README.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/cli.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/cli/drill_down.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/csvpath.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_cacher.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_metadata.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/files/file_registrar.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/ckan.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/ckan_listener.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/datafile.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ckan/dataset.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/event.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/event_result.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/file_listener_ol.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/job.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/ol_listener.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/paths_listener_ol.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/result_listener_ol.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/results_listener_ol.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run_listener_ol.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/run_state.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/ol/sender.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/arrival_handler.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/sftpplus_listener.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/sftpplus/transfer_creator.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/slack/event.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/integrations/slack/sender.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/listener.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_manager.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_metadata.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/paths/paths_registrar.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/registrar.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_errors_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_lines_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_printouts_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/file_unmatched_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/readers/readers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_file_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_metadata.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/result_registrar.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/results/results_manager.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_listener_stdout.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_metadata.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/managers/run/run_registrar.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/args.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/all.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/andf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/any.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/between.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/empty.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/exists.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/inf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/no.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/notf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/orf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/boolean/yes.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_bytes.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_headers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_lines.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/count_scans.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/counter.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/every.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/has_matches.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/increment.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/tally.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/counting/total_lines.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/dates/now.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_factory.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_finder.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/function_focus.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/append.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/collect.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/empty_stack.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/end.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/header_name.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/header_names_mismatch.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/headers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/mismatch.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/replace.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/headers/reset_headers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/advance.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/after_blank.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/dups.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/first.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/first_line.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/last.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/lines/stop.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/above.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/add.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/divide.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/equals.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/intf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/mod.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/multiply.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/round.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/subtotal.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/subtract.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/math/sum.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/fingerprint.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/importf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/misc/random.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/jinjaf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/print_line.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/print_queue.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/printf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/print/table.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/minf.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/percent.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/percent_unique.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/stats/stdev.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/concat.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/length.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/lower.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/metaphone.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/regex.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/starts_with.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/strip.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/substring.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/strings/upper.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/testing/debug.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/boolean.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/datef.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/decimal.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/nonef.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/string.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/types/type.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/fail.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/failed.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/validity/line.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/get.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/pushpop.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/put.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/track.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/functions/variables/variables.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/lark_parser.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/lark_transformer.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/matcher.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/equality.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/expression.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/header.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/matchable.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/qualified.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/reference.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/term.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/productions/variable.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/exceptions.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/expression_encoder.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/expression_utility.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/lark_print_parser.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/print_parser.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/matching/util/runtime_data_collector.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/explain_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/files_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/logic_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/mode_controller.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/print_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/return_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/run_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/source_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/transfer_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/unmatched_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/modes/validation_mode.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/__init__.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/exceptions.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/parser.out +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/parsetab.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/scanner.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/scanning/scanning_lexer.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/cache.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/class_loader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/config_exception.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/error.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/exceptions.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/file_readers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/file_writers.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/last_line_stats.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_counter.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_monitor.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/line_spooler.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/log_utility.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/metadata_parser.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/pandas_data_reader.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/printer.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/reference_parser.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/s3/s3_fingerprinter.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/csvpath/util/var_utility.py +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/asbool.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/assignment.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/comments.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/config.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/examples.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/files.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/above.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/advance.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/after_blank.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/all.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/andor.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/any.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/average.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/between.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/collect.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/correlate.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count_bytes.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/count_headers.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/counter.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/date.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/empty.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/empty_stack.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/end.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/every.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/fail.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/fingerprint.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/first.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/get.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/has_dups.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/has_matches.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header_name.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/header_names_mismatch.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/implementing_functions.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/import.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/in.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/increment.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/intf.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/jinja.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/last.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/line.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/line_number.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/max.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/metaphone.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/mismatch.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/no.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/not.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/now.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/percent_unique.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/pop.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print_line.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/print_queue.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/random.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/regex.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/replace.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/reset_headers.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/stdev.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/stop.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/string_functions.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/subtotal.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/subtract.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/sum.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/tally.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/total_lines.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/track.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/types.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/variables.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions/variables_and_headers.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/functions.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/grammar.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/headers.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/ckan-logo-sm.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/csvpath-icon-sm.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/csvpath-logo-wordmark-tight-2.svg +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-3.svg +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-4.svg +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/logo-wordmark-white-trimmed.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/marquez-logo-sm.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/images/openlineage-logo-sm.png +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/paths.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/printing.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/qualifiers.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/references.md +0 -0
- {csvpath-0.0.508 → csvpath-0.0.510}/docs/terms.md +0 -0
- {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.
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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 = []
|
|
@@ -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"]
|
|
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
|
-
|
|
130
|
-
return fi["last_mod"]
|
|
131
|
-
return None
|
|
132
|
+
return fi["last_mod"]
|
|
132
133
|
except FileNotFoundError:
|
|
133
134
|
return -1
|
|
134
135
|
|
|
@@ -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
|
|
181
|
-
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
23
|
+
client = Box.STUFF.get("boto_s3_client")
|
|
23
24
|
if client is None:
|
|
24
|
-
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|