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