csvpath 0.0.476__tar.gz → 0.0.478__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.476 → csvpath-0.0.478}/PKG-INFO +37 -35
- {csvpath-0.0.476 → csvpath-0.0.478}/README.md +32 -33
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/csvpath.py +198 -15
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/csvpaths.py +22 -25
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/managers/csvpaths_manager.py +14 -4
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/managers/file_manager.py +8 -6
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/managers/result.py +10 -6
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/managers/results_manager.py +1 -10
- csvpath-0.0.478/csvpath/matching/functions/args.py +390 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/all.py +18 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/andf.py +12 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/any.py +16 -8
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/between.py +50 -5
- csvpath-0.0.478/csvpath/matching/functions/boolean/exists.py +27 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/inf.py +10 -3
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/no.py +5 -4
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/notf.py +10 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/orf.py +12 -1
- csvpath-0.0.478/csvpath/matching/functions/boolean/yes.py +18 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/count.py +1 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/count_headers.py +3 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/count_lines.py +5 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/count_scans.py +3 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/counter.py +6 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/every.py +8 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/has_matches.py +3 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/increment.py +8 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/tally.py +9 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/counting/total_lines.py +3 -1
- csvpath-0.0.478/csvpath/matching/functions/dates/datef.py +40 -0
- csvpath-0.0.478/csvpath/matching/functions/dates/now.py +47 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/function.py +50 -18
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/function_factory.py +16 -7
- csvpath-0.0.478/csvpath/matching/functions/headers/append.py +31 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/collect.py +11 -6
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/empty_stack.py +6 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/end.py +6 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/header_name.py +8 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/header_names_mismatch.py +9 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/headers.py +11 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/mismatch.py +8 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/replace.py +13 -7
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/headers/reset_headers.py +9 -5
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/advance.py +20 -14
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/after_blank.py +3 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/dups.py +16 -5
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/first.py +6 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/first_line.py +5 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/last.py +13 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/lines/stop.py +22 -11
- csvpath-0.0.478/csvpath/matching/functions/math/above.py +98 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/add.py +15 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/divide.py +9 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/equals.py +9 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/mod.py +10 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/multiply.py +12 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/round.py +9 -3
- csvpath-0.0.478/csvpath/matching/functions/math/subtotal.py +32 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/subtract.py +8 -3
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/math/sum.py +8 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/misc/importf.py +17 -5
- csvpath-0.0.478/csvpath/matching/functions/misc/intf.py +75 -0
- csvpath-0.0.478/csvpath/matching/functions/misc/nonef.py +21 -0
- csvpath-0.0.478/csvpath/matching/functions/misc/random.py +70 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/print/jinjaf.py +22 -17
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/print/print_line.py +12 -5
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/print/print_queue.py +4 -2
- csvpath-0.0.478/csvpath/matching/functions/print/printf.py +55 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/print/table.py +21 -8
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/stats/minf.py +18 -8
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/stats/percent.py +7 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/stats/percent_unique.py +5 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/stats/stdev.py +8 -2
- csvpath-0.0.478/csvpath/matching/functions/strings/concat.py +35 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/length.py +13 -4
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/lower.py +7 -3
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/metaphone.py +9 -3
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/regex.py +12 -17
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/starts_with.py +8 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/strip.py +8 -2
- csvpath-0.0.478/csvpath/matching/functions/strings/substring.py +38 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/strings/upper.py +7 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/testing/debug.py +42 -17
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/validity/fail.py +7 -4
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/validity/failed.py +3 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/variables/get.py +6 -5
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/variables/pushpop.py +36 -18
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/variables/put.py +17 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/variables/track.py +15 -6
- csvpath-0.0.478/csvpath/matching/functions/variables/variables.py +29 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/matcher.py +20 -15
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/equality.py +8 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/expression.py +5 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/header.py +2 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/matchable.py +41 -7
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/qualified.py +16 -2
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/reference.py +0 -44
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/util/expression_utility.py +178 -21
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/util/print_parser.py +69 -56
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/line_monitor.py +1 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/log_utility.py +8 -6
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/metadata_parser.py +1 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/printer.py +14 -4
- csvpath-0.0.478/docs/comments.md +121 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/between.md +2 -2
- csvpath-0.0.478/docs/functions/in.md +43 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/print.md +24 -2
- csvpath-0.0.478/docs/functions/random.md +24 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/regex.md +2 -2
- csvpath-0.0.478/docs/functions/replace.md +51 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/string_functions.md +2 -2
- csvpath-0.0.478/docs/functions/subtotal.md +30 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions.md +41 -37
- csvpath-0.0.478/docs/printing.md +101 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/qualifiers.md +4 -4
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/terms.md +1 -1
- {csvpath-0.0.476 → csvpath-0.0.478}/pyproject.toml +2 -2
- csvpath-0.0.476/csvpath/matching/functions/boolean/exists.py +0 -31
- csvpath-0.0.476/csvpath/matching/functions/boolean/yes.py +0 -17
- csvpath-0.0.476/csvpath/matching/functions/dates/datef.py +0 -25
- csvpath-0.0.476/csvpath/matching/functions/dates/now.py +0 -27
- csvpath-0.0.476/csvpath/matching/functions/math/above.py +0 -64
- csvpath-0.0.476/csvpath/matching/functions/misc/intf.py +0 -20
- csvpath-0.0.476/csvpath/matching/functions/misc/nonef.py +0 -17
- csvpath-0.0.476/csvpath/matching/functions/misc/random.py +0 -28
- csvpath-0.0.476/csvpath/matching/functions/print/printf.py +0 -45
- csvpath-0.0.476/csvpath/matching/functions/strings/concat.py +0 -22
- csvpath-0.0.476/csvpath/matching/functions/strings/num.py +0 -28
- csvpath-0.0.476/csvpath/matching/functions/strings/substring.py +0 -27
- csvpath-0.0.476/csvpath/matching/functions/validation.py +0 -293
- csvpath-0.0.476/csvpath/matching/functions/variables/variables.py +0 -17
- csvpath-0.0.476/docs/functions/in.md +0 -25
- csvpath-0.0.476/docs/functions/replace.md +0 -23
- {csvpath-0.0.476 → csvpath-0.0.478}/LICENSE +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/config/config.ini +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/__init__.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/managers/__init__.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/__init__.py +0 -0
- {csvpath-0.0.476/csvpath/scanning → csvpath-0.0.478/csvpath/matching/functions}/__init__.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/boolean/empty.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/function_finder.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/functions/function_focus.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/lark_parser.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/lark_transformer.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/__init__.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/term.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/productions/variable.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/util/exceptions.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/util/expression_encoder.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/matching/util/lark_print_parser.py +0 -0
- {csvpath-0.0.476/csvpath/matching/functions → csvpath-0.0.478/csvpath/scanning}/__init__.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/scanning/exceptions.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/scanning/parser.out +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/scanning/parsetab.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/scanning/scanner.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/scanning/scanning_lexer.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/cache.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/config.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/config_exception.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/error.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/exceptions.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/last_line_stats.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/csvpath/util/line_counter.py +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/asbool.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/assignment.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/config.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/examples.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/files.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/above.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/advance.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/after_blank.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/all.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/andor.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/any.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/average.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/collect.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/correlate.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/count.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/count_headers.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/counter.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/date.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/empty.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/empty_stack.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/end.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/every.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/fail.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/first.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/get.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/has_dups.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/has_matches.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/header.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/header_name.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/header_names_mismatch.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/implementing_functions.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/import.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/increment.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/jinja.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/last.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/line_number.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/max.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/metaphone.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/mismatch.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/no.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/not.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/now.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/percent_unique.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/pop.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/print_line.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/print_queue.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/reset_headers.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/stdev.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/stop.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/subtract.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/sum.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/tally.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/total_lines.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/track.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/variables.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/functions/variables_and_headers.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/grammar.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/headers.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/images/logo-wordmark-white-trimmed.png +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/paths.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/references.md +0 -0
- {csvpath-0.0.476 → csvpath-0.0.478}/docs/variables.md +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: csvpath
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.478
|
|
4
4
|
Summary: A declarative language for data extraction and validation of CSV files
|
|
5
5
|
Author: David Kershaw
|
|
6
6
|
Author-email: dk107dk@hotmail.com
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.9,<4.0
|
|
8
8
|
Classifier: Development Status :: 3 - Alpha
|
|
9
9
|
Classifier: Environment :: Console
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
17
|
Classifier: Topic :: File Formats
|
|
15
18
|
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
@@ -177,12 +180,11 @@ This is a very basic programmatic use of CsvPath.
|
|
|
177
180
|
```python
|
|
178
181
|
path = CsvPath()
|
|
179
182
|
path.parse("""
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
]
|
|
183
|
+
$test.csv[5-25][
|
|
184
|
+
#firstname == "Frog"
|
|
185
|
+
@lastname.onmatch = "Bat"
|
|
186
|
+
count() == 2
|
|
187
|
+
]
|
|
186
188
|
""")
|
|
187
189
|
|
|
188
190
|
for i, line in enumerate( path.next() ):
|
|
@@ -193,17 +195,16 @@ This is a very basic programmatic use of CsvPath.
|
|
|
193
195
|
The csvpath says:
|
|
194
196
|
- Open test.csv
|
|
195
197
|
- Scan lines 5 through 25
|
|
196
|
-
- Match the second time we see a line where the first header equals `Frog` and set the variable called `lastname` to "
|
|
198
|
+
- Match the second time we see a line where the first header equals `Frog` and set the variable called `lastname` to "Bat"
|
|
197
199
|
|
|
198
200
|
Another path that does the same thing a bit more simply might look like:
|
|
199
201
|
|
|
200
202
|
```bash
|
|
201
|
-
$test[5-25]
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
]
|
|
203
|
+
$test[5-25][
|
|
204
|
+
#firstname == "Frog"
|
|
205
|
+
@lastname.onmatch = "Bat"
|
|
206
|
+
count()==2 -> print( "$.csvpath.match_count: $.csvpath.line")
|
|
207
|
+
]
|
|
207
208
|
```
|
|
208
209
|
|
|
209
210
|
In this case, we're using the "when" operator, `->`, to determine when to print.
|
|
@@ -214,12 +215,14 @@ There are a small number of configuration options. Read <a href='https://github.
|
|
|
214
215
|
|
|
215
216
|
## The print function
|
|
216
217
|
|
|
217
|
-
Before we get into the details of
|
|
218
|
+
Before we get into the details of scanning and matching, let's look at what CsvPath can print. The `print` function has several important uses, including:
|
|
218
219
|
|
|
219
|
-
- Debugging csvpaths
|
|
220
220
|
- Validating CSV files
|
|
221
|
+
- Debugging csvpaths
|
|
221
222
|
- Creating new CSV files based on an existing file
|
|
222
223
|
|
|
224
|
+
You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/printing.md'>read more about the mechanics of printing here</a>.
|
|
225
|
+
|
|
223
226
|
<a name="validating"></a>
|
|
224
227
|
### Validating CSV
|
|
225
228
|
|
|
@@ -250,7 +253,12 @@ This csvpath reorders the headers of the test file at `tests/test_resources/test
|
|
|
250
253
|
# Comments
|
|
251
254
|
CsvPaths have file scanning instructions, match components, and comments. Comments exist at the top level, outside the CsvPath's brackets, as well as in the matching part of the path. Comments within the match part are covered below.
|
|
252
255
|
|
|
253
|
-
|
|
256
|
+
As well as documentation, comments outside the csvpath can:
|
|
257
|
+
- Contribute to a collection of metadata fields associated with a csvpath
|
|
258
|
+
- Switch on/off certain validation settings
|
|
259
|
+
- Set the identity of a csvpath within a group of csvpaths
|
|
260
|
+
|
|
261
|
+
A comment starts and ends with a `~` character. Within the comment, any word that has a colon after it is considered a metadata key. The metadata value is anything following the key up till a new metadata key word is seen or the comment ends.
|
|
254
262
|
|
|
255
263
|
For example, this comment says that the csvpath has the name `Order Validation`:
|
|
256
264
|
|
|
@@ -263,6 +271,7 @@ For example, this comment says that the csvpath has the name `Order Validation`:
|
|
|
263
271
|
|
|
264
272
|
The name `Order Validation` is available in CsvPath's `metadata` property along with the developer's name. You can use any metadata keys you like. All the metadata is available during and after a run, giving you an easy way to name, describe, attribute, etc. your csvpaths.
|
|
265
273
|
|
|
274
|
+
You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/comments.md'>read more about comments and metadata here</a>.
|
|
266
275
|
|
|
267
276
|
<a name="scanning"></a>
|
|
268
277
|
# Scanning
|
|
@@ -427,23 +436,14 @@ Because of this nuanced approach to errors, the library will tend to raise data
|
|
|
427
436
|
<a name="examples"></a>
|
|
428
437
|
## More Examples
|
|
429
438
|
|
|
430
|
-
```bash
|
|
431
|
-
[ exists(#common_name) #0=="field" @tail.onmatch=end() not(in(@tail, 'short|medium')) ]
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
In the path above, the rules applied are:
|
|
435
|
-
- The exists test of `#common_name` checks if the column with the header "common_name" has a value. Headers are named for whatever values are found in the 0th row. They indicate a column in the row being checked for match.
|
|
436
|
-
- `#2` means the 3rd column, counting from 0
|
|
437
|
-
- Functions and column references are ANDed together
|
|
438
|
-
- `@tail` creates a variable named "tail" and sets it to the value of the last column if all else matches
|
|
439
|
-
- Functions can contain functions, equality tests, and/or terms.
|
|
440
|
-
|
|
441
439
|
There are more examples scattered throughout the documentation. Good places to look include:
|
|
442
440
|
|
|
441
|
+
- Here are a few <a href='https://github.com/dk107dk/csvpath/blob/main/docs/examples.md'>more real-looking examples</a>
|
|
442
|
+
- Try the Getting Started examples on <a href="https://www.csvpath.org">https://www.csvpath.org</a>
|
|
443
443
|
- The individual <a href='https://github.com/dk107dk/csvpath/blob/main/docs/functions.md'>function descriptions</a>
|
|
444
|
-
- The <a href='https://github.com/dk107dk/csvpath/tree/main/tests'>unit tests</a>
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
- The <a href='https://github.com/dk107dk/csvpath/tree/main/tests'>unit tests</a> and <a href='https://github.com/dk107dk/csvpath/tree/main/tests/grammar/match'>their match parts</a> are not realistic, but a good source of ideas.
|
|
445
|
+
|
|
446
|
+
To create example CsvPaths from your own data, try <a href='https://autogen.csvpath.org'>CsvPath AutoGen</a>. The huge caveat is that AutoGen uses AI so your results will not be perfect. You will need to adjust, polish, and test them.
|
|
447
447
|
|
|
448
448
|
<a name="grammar"></a>
|
|
449
449
|
## Grammar
|
|
@@ -457,9 +457,11 @@ Visit <a href="https://www.csvpath.org">https://www.csvpath.org</a>
|
|
|
457
457
|
|
|
458
458
|
# Sponsors
|
|
459
459
|
|
|
460
|
-
<a href='https://www.atestaanalytics.com/'
|
|
461
|
-
|
|
462
|
-
|
|
460
|
+
<a href='https://www.atestaanalytics.com/' >
|
|
461
|
+
<img width="25%" src="https://raw.githubusercontent.com/dk107dk/csvpath/main/docs/images/logo-wordmark-white-on-black-trimmed-padded.png" alt="Atesta Analytics"/></a>
|
|
462
|
+
<a href='https://www.datakitchen.io/'>
|
|
463
|
+
<img src="https://datakitchen.io/wp-content/uploads/2020/10/logo.svg"
|
|
464
|
+
style='width:160px; position:relative;bottom:-5px;left:15px' alt="DataKitchen" id="logo" data-height-percentage="45"></a>
|
|
463
465
|
|
|
464
466
|
|
|
465
467
|
|
|
@@ -146,12 +146,11 @@ This is a very basic programmatic use of CsvPath.
|
|
|
146
146
|
```python
|
|
147
147
|
path = CsvPath()
|
|
148
148
|
path.parse("""
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
]
|
|
149
|
+
$test.csv[5-25][
|
|
150
|
+
#firstname == "Frog"
|
|
151
|
+
@lastname.onmatch = "Bat"
|
|
152
|
+
count() == 2
|
|
153
|
+
]
|
|
155
154
|
""")
|
|
156
155
|
|
|
157
156
|
for i, line in enumerate( path.next() ):
|
|
@@ -162,17 +161,16 @@ This is a very basic programmatic use of CsvPath.
|
|
|
162
161
|
The csvpath says:
|
|
163
162
|
- Open test.csv
|
|
164
163
|
- Scan lines 5 through 25
|
|
165
|
-
- Match the second time we see a line where the first header equals `Frog` and set the variable called `lastname` to "
|
|
164
|
+
- Match the second time we see a line where the first header equals `Frog` and set the variable called `lastname` to "Bat"
|
|
166
165
|
|
|
167
166
|
Another path that does the same thing a bit more simply might look like:
|
|
168
167
|
|
|
169
168
|
```bash
|
|
170
|
-
$test[5-25]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
]
|
|
169
|
+
$test[5-25][
|
|
170
|
+
#firstname == "Frog"
|
|
171
|
+
@lastname.onmatch = "Bat"
|
|
172
|
+
count()==2 -> print( "$.csvpath.match_count: $.csvpath.line")
|
|
173
|
+
]
|
|
176
174
|
```
|
|
177
175
|
|
|
178
176
|
In this case, we're using the "when" operator, `->`, to determine when to print.
|
|
@@ -183,12 +181,14 @@ There are a small number of configuration options. Read <a href='https://github.
|
|
|
183
181
|
|
|
184
182
|
## The print function
|
|
185
183
|
|
|
186
|
-
Before we get into the details of
|
|
184
|
+
Before we get into the details of scanning and matching, let's look at what CsvPath can print. The `print` function has several important uses, including:
|
|
187
185
|
|
|
188
|
-
- Debugging csvpaths
|
|
189
186
|
- Validating CSV files
|
|
187
|
+
- Debugging csvpaths
|
|
190
188
|
- Creating new CSV files based on an existing file
|
|
191
189
|
|
|
190
|
+
You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/printing.md'>read more about the mechanics of printing here</a>.
|
|
191
|
+
|
|
192
192
|
<a name="validating"></a>
|
|
193
193
|
### Validating CSV
|
|
194
194
|
|
|
@@ -219,7 +219,12 @@ This csvpath reorders the headers of the test file at `tests/test_resources/test
|
|
|
219
219
|
# Comments
|
|
220
220
|
CsvPaths have file scanning instructions, match components, and comments. Comments exist at the top level, outside the CsvPath's brackets, as well as in the matching part of the path. Comments within the match part are covered below.
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
As well as documentation, comments outside the csvpath can:
|
|
223
|
+
- Contribute to a collection of metadata fields associated with a csvpath
|
|
224
|
+
- Switch on/off certain validation settings
|
|
225
|
+
- Set the identity of a csvpath within a group of csvpaths
|
|
226
|
+
|
|
227
|
+
A comment starts and ends with a `~` character. Within the comment, any word that has a colon after it is considered a metadata key. The metadata value is anything following the key up till a new metadata key word is seen or the comment ends.
|
|
223
228
|
|
|
224
229
|
For example, this comment says that the csvpath has the name `Order Validation`:
|
|
225
230
|
|
|
@@ -232,6 +237,7 @@ For example, this comment says that the csvpath has the name `Order Validation`:
|
|
|
232
237
|
|
|
233
238
|
The name `Order Validation` is available in CsvPath's `metadata` property along with the developer's name. You can use any metadata keys you like. All the metadata is available during and after a run, giving you an easy way to name, describe, attribute, etc. your csvpaths.
|
|
234
239
|
|
|
240
|
+
You can <a href='https://github.com/dk107dk/csvpath/blob/main/docs/comments.md'>read more about comments and metadata here</a>.
|
|
235
241
|
|
|
236
242
|
<a name="scanning"></a>
|
|
237
243
|
# Scanning
|
|
@@ -396,23 +402,14 @@ Because of this nuanced approach to errors, the library will tend to raise data
|
|
|
396
402
|
<a name="examples"></a>
|
|
397
403
|
## More Examples
|
|
398
404
|
|
|
399
|
-
```bash
|
|
400
|
-
[ exists(#common_name) #0=="field" @tail.onmatch=end() not(in(@tail, 'short|medium')) ]
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
In the path above, the rules applied are:
|
|
404
|
-
- The exists test of `#common_name` checks if the column with the header "common_name" has a value. Headers are named for whatever values are found in the 0th row. They indicate a column in the row being checked for match.
|
|
405
|
-
- `#2` means the 3rd column, counting from 0
|
|
406
|
-
- Functions and column references are ANDed together
|
|
407
|
-
- `@tail` creates a variable named "tail" and sets it to the value of the last column if all else matches
|
|
408
|
-
- Functions can contain functions, equality tests, and/or terms.
|
|
409
|
-
|
|
410
405
|
There are more examples scattered throughout the documentation. Good places to look include:
|
|
411
406
|
|
|
407
|
+
- Here are a few <a href='https://github.com/dk107dk/csvpath/blob/main/docs/examples.md'>more real-looking examples</a>
|
|
408
|
+
- Try the Getting Started examples on <a href="https://www.csvpath.org">https://www.csvpath.org</a>
|
|
412
409
|
- The individual <a href='https://github.com/dk107dk/csvpath/blob/main/docs/functions.md'>function descriptions</a>
|
|
413
|
-
- The <a href='https://github.com/dk107dk/csvpath/tree/main/tests'>unit tests</a>
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
- The <a href='https://github.com/dk107dk/csvpath/tree/main/tests'>unit tests</a> and <a href='https://github.com/dk107dk/csvpath/tree/main/tests/grammar/match'>their match parts</a> are not realistic, but a good source of ideas.
|
|
411
|
+
|
|
412
|
+
To create example CsvPaths from your own data, try <a href='https://autogen.csvpath.org'>CsvPath AutoGen</a>. The huge caveat is that AutoGen uses AI so your results will not be perfect. You will need to adjust, polish, and test them.
|
|
416
413
|
|
|
417
414
|
<a name="grammar"></a>
|
|
418
415
|
## Grammar
|
|
@@ -426,9 +423,11 @@ Visit <a href="https://www.csvpath.org">https://www.csvpath.org</a>
|
|
|
426
423
|
|
|
427
424
|
# Sponsors
|
|
428
425
|
|
|
429
|
-
<a href='https://www.atestaanalytics.com/'
|
|
430
|
-
|
|
431
|
-
|
|
426
|
+
<a href='https://www.atestaanalytics.com/' >
|
|
427
|
+
<img width="25%" src="https://raw.githubusercontent.com/dk107dk/csvpath/main/docs/images/logo-wordmark-white-on-black-trimmed-padded.png" alt="Atesta Analytics"/></a>
|
|
428
|
+
<a href='https://www.datakitchen.io/'>
|
|
429
|
+
<img src="https://datakitchen.io/wp-content/uploads/2020/10/logo.svg"
|
|
430
|
+
style='width:160px; position:relative;bottom:-5px;left:15px' alt="DataKitchen" id="logo" data-height-percentage="45"></a>
|
|
432
431
|
|
|
433
432
|
|
|
434
433
|
|
|
@@ -25,6 +25,7 @@ from .util.exceptions import (
|
|
|
25
25
|
CsvPathsException,
|
|
26
26
|
)
|
|
27
27
|
from .matching.util.exceptions import MatchException
|
|
28
|
+
from csvpath.util.printer import Printer
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class CsvPathPublic(ABC):
|
|
@@ -70,7 +71,7 @@ class CsvPathPublic(ABC):
|
|
|
70
71
|
matching rows"""
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
74
|
+
class CsvPath(CsvPathPublic, ErrorCollector, Printer): # pylint: disable=R0902, R0904
|
|
74
75
|
"""CsvPath represents a csvpath string that contains a reference to
|
|
75
76
|
a file, scanning instructions, and rules for matching lines.
|
|
76
77
|
"""
|
|
@@ -121,7 +122,7 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
121
122
|
# the middle of iterating a file using next(). probably not a
|
|
122
123
|
# good idea, tho.
|
|
123
124
|
#
|
|
124
|
-
self._AND = True
|
|
125
|
+
self._AND = True # pylint: disable=C0103
|
|
125
126
|
#
|
|
126
127
|
# when True the lines that do not match are returned from next()
|
|
127
128
|
# and collect(). this effectively switches CsvPath from being an
|
|
@@ -239,6 +240,14 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
239
240
|
# way.
|
|
240
241
|
self._config = config
|
|
241
242
|
#
|
|
243
|
+
# these settings determine how we report function args validation
|
|
244
|
+
# errors. e.g. if print(True) the validation check fails because
|
|
245
|
+
# print() expects a string.
|
|
246
|
+
#
|
|
247
|
+
self._log_validation_errors = False
|
|
248
|
+
self._print_validation_errors = True
|
|
249
|
+
self._raise_validation_errors = True
|
|
250
|
+
#
|
|
242
251
|
# there are two logger components one for CsvPath and one for CsvPaths.
|
|
243
252
|
# the default levels are set in config.ini. to change the levels pass LogUtility
|
|
244
253
|
# your component instance and the logging level. e.g.:
|
|
@@ -276,19 +285,19 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
276
285
|
self._line_monitor = lm
|
|
277
286
|
|
|
278
287
|
@property
|
|
279
|
-
def AND(self) -> bool:
|
|
288
|
+
def AND(self) -> bool: # pylint: disable=C0103
|
|
280
289
|
return self._AND
|
|
281
290
|
|
|
282
291
|
@AND.setter
|
|
283
|
-
def AND(self, a: bool) -> bool:
|
|
292
|
+
def AND(self, a: bool) -> bool: # pylint: disable=C0103
|
|
284
293
|
self._AND = a
|
|
285
294
|
|
|
286
295
|
@property
|
|
287
|
-
def OR(self) -> bool:
|
|
296
|
+
def OR(self) -> bool: # pylint: disable=C0103
|
|
288
297
|
return not self._AND
|
|
289
298
|
|
|
290
299
|
@OR.setter
|
|
291
|
-
def OR(self, a: bool) -> bool:
|
|
300
|
+
def OR(self, a: bool) -> bool: # pylint: disable=C0103
|
|
292
301
|
self._AND = not a
|
|
293
302
|
|
|
294
303
|
@property
|
|
@@ -345,18 +354,53 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
345
354
|
def error_collector(self, error_collector) -> None:
|
|
346
355
|
self._error_collector = error_collector
|
|
347
356
|
|
|
348
|
-
def collect_error(self,
|
|
357
|
+
def collect_error(self, error: Error) -> None: # pylint: disable=C0116
|
|
349
358
|
#
|
|
350
359
|
# errors must be built and handled in ErrorHandler.
|
|
351
360
|
# here we're just collecting them if collect is
|
|
352
361
|
# selected by our configuration
|
|
353
362
|
#
|
|
354
363
|
if self._error_collector is not None:
|
|
355
|
-
self._error_collector.collect_error(
|
|
364
|
+
self._error_collector.collect_error(error)
|
|
356
365
|
else:
|
|
357
366
|
if self._errors is None:
|
|
358
367
|
self._errors = []
|
|
359
|
-
self._errors.append(
|
|
368
|
+
self._errors.append(error)
|
|
369
|
+
|
|
370
|
+
def report_validation_errors(self, msg: str) -> None:
|
|
371
|
+
if self.print_validation_errors:
|
|
372
|
+
self.print(msg)
|
|
373
|
+
elif self.log_validation_errors:
|
|
374
|
+
self.logger.warning(msg)
|
|
375
|
+
#
|
|
376
|
+
# the args class will do the raise if it is told to by the prop
|
|
377
|
+
#
|
|
378
|
+
|
|
379
|
+
def set_validation_error_handling(self, veh) -> None:
|
|
380
|
+
if veh and veh.find("print") > -1:
|
|
381
|
+
self._print_validation_errors = True
|
|
382
|
+
else:
|
|
383
|
+
self._print_validation_errors = False
|
|
384
|
+
if veh and veh.find("log") > -1:
|
|
385
|
+
self._log_validation_errors = True
|
|
386
|
+
else:
|
|
387
|
+
self._log_validation_errors = False
|
|
388
|
+
if veh and veh.find("raise") > -1:
|
|
389
|
+
self._raise_validation_errors = True
|
|
390
|
+
else:
|
|
391
|
+
self._raise_validation_errors = False
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def print_validation_errors(self) -> bool:
|
|
395
|
+
return self._print_validation_errors
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def log_validation_errors(self) -> bool:
|
|
399
|
+
return self._log_validation_errors
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def raise_validation_errors(self) -> bool:
|
|
403
|
+
return self._raise_validation_errors
|
|
360
404
|
|
|
361
405
|
def add_printer(self, printer) -> None: # pylint: disable=C0116
|
|
362
406
|
if printer not in self.printers:
|
|
@@ -365,12 +409,40 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
365
409
|
def set_printers(self, printers: List) -> None: # pylint: disable=C0116
|
|
366
410
|
self.printers = printers
|
|
367
411
|
|
|
412
|
+
@property
|
|
413
|
+
def has_default_printer(self) -> bool:
|
|
414
|
+
if not self.printers:
|
|
415
|
+
self.printers = []
|
|
416
|
+
for i, p in enumerate(self.printers):
|
|
417
|
+
if isinstance(p, StdOutPrinter):
|
|
418
|
+
return True
|
|
419
|
+
return False
|
|
420
|
+
|
|
368
421
|
def print(self, string: str) -> None: # pylint: disable=C0116
|
|
369
422
|
for p in self.printers:
|
|
370
423
|
p.print(string)
|
|
371
424
|
|
|
425
|
+
def print_to(self, name: str, string: str) -> None:
|
|
426
|
+
for p in self.printers:
|
|
427
|
+
p.print_to(name, string)
|
|
428
|
+
|
|
429
|
+
@property
|
|
430
|
+
def last_line(self):
|
|
431
|
+
if not self.printers or len(self.printers) == 0:
|
|
432
|
+
return None
|
|
433
|
+
return self.printers[0].last_line
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def lines_printed(self) -> int:
|
|
437
|
+
if not self.printers or len(self.printers) == 0:
|
|
438
|
+
return -1
|
|
439
|
+
self.printers[0].lines_printed
|
|
440
|
+
|
|
372
441
|
@property
|
|
373
442
|
def is_frozen(self) -> bool:
|
|
443
|
+
"""True if the instance is matching on its last row only to
|
|
444
|
+
allow last()s to run; in which case, no variable updates
|
|
445
|
+
are allowed, along with other limitations."""
|
|
374
446
|
return self._freeze_path
|
|
375
447
|
|
|
376
448
|
@is_frozen.setter
|
|
@@ -404,6 +476,7 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
404
476
|
# the comments so we won't find them again
|
|
405
477
|
#
|
|
406
478
|
csvpath = MetadataParser(self).extract_metadata(instance=self, csvpath=csvpath)
|
|
479
|
+
self.update_settings_from_metadata()
|
|
407
480
|
#
|
|
408
481
|
#
|
|
409
482
|
#
|
|
@@ -425,10 +498,10 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
425
498
|
# atm, tho, just create a dry-run copy. in some possible
|
|
426
499
|
# unit tests we may not have a parsable match part.
|
|
427
500
|
#
|
|
428
|
-
matcher = None
|
|
429
|
-
if mat:
|
|
430
|
-
matcher = Matcher(csvpath=self, data=mat, line=None, headers=None)
|
|
431
501
|
if disposably:
|
|
502
|
+
matcher = None
|
|
503
|
+
if mat:
|
|
504
|
+
matcher = Matcher(csvpath=self, data=mat, line=None, headers=None)
|
|
432
505
|
#
|
|
433
506
|
# if the matcher was requested for some reason beyond our own needs
|
|
434
507
|
# we just return it and forget it existed.
|
|
@@ -437,7 +510,83 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
437
510
|
if self.scanner.filename is None:
|
|
438
511
|
raise FileException("Cannot proceed without a filename")
|
|
439
512
|
self.get_total_lines_and_headers()
|
|
440
|
-
return self
|
|
513
|
+
return self
|
|
514
|
+
|
|
515
|
+
def update_settings_from_metadata(self) -> None:
|
|
516
|
+
#
|
|
517
|
+
# settings:
|
|
518
|
+
# - logic-mode: AND | OR
|
|
519
|
+
# - match-mode: matches | no-matches
|
|
520
|
+
# - print-mode: default-off | default-on
|
|
521
|
+
# - arg-validation-mode: print | log | raise | quiet
|
|
522
|
+
#
|
|
523
|
+
self.update_logic_mode_if()
|
|
524
|
+
self.update_match_mode_if()
|
|
525
|
+
self.update_print_mode_if()
|
|
526
|
+
self.update_arg_validation_mode_if()
|
|
527
|
+
|
|
528
|
+
def update_arg_validation_mode_if(self) -> None:
|
|
529
|
+
if self.metadata and "arg-validation-mode" in self.metadata:
|
|
530
|
+
# sets arg validation reporting. one or more or none of:
|
|
531
|
+
# - print
|
|
532
|
+
# - log
|
|
533
|
+
# - raise
|
|
534
|
+
#
|
|
535
|
+
validation_mode = f"{self.metadata['arg-validation-mode']}".strip()
|
|
536
|
+
if validation_mode:
|
|
537
|
+
self.set_validation_error_handling(validation_mode)
|
|
538
|
+
self.logger.info(
|
|
539
|
+
"Setting 'arg-validation-mode': %s",
|
|
540
|
+
self.metadata["arg-validation-mode"],
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
def update_logic_mode_if(self) -> None:
|
|
544
|
+
if self.metadata and "logic-mode" in self.metadata:
|
|
545
|
+
if f"{self.metadata['logic-mode']}".strip() == "AND":
|
|
546
|
+
self.AND = True
|
|
547
|
+
elif f"{self.metadata['logic-mode']}".strip() == "OR":
|
|
548
|
+
self.AND = False
|
|
549
|
+
else:
|
|
550
|
+
self.logger.warning(
|
|
551
|
+
"Incorrect metadata field value 'logic-mode': %s",
|
|
552
|
+
self.metadata["logic-mode"],
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
def update_match_mode_if(self) -> None:
|
|
556
|
+
if "match-mode" in self.metadata:
|
|
557
|
+
if f"{self.metadata['match-mode']}".strip() == "matches":
|
|
558
|
+
self.collect_when_not_matched = False
|
|
559
|
+
elif f"{self.metadata['match-mode']}".strip() == "no-matches":
|
|
560
|
+
self.collect_when_not_matched = True
|
|
561
|
+
else:
|
|
562
|
+
self.logger.warning(
|
|
563
|
+
"Incorrect metadata field value 'match-mode': %s",
|
|
564
|
+
self.metadata["match-mode"],
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
def update_print_mode_if(self) -> None:
|
|
568
|
+
if "print-mode" in self.metadata:
|
|
569
|
+
if f"{self.metadata['print-mode']}".strip() == "default-off":
|
|
570
|
+
remove = -1
|
|
571
|
+
for i, p in enumerate(self.printers):
|
|
572
|
+
if isinstance(p, StdOutPrinter):
|
|
573
|
+
remove = i
|
|
574
|
+
break
|
|
575
|
+
if remove >= 0:
|
|
576
|
+
del self.printers[remove]
|
|
577
|
+
elif f"{self.metadata['print-mode']}".strip() == "default-on":
|
|
578
|
+
done = False
|
|
579
|
+
for i, p in enumerate(self.printers):
|
|
580
|
+
if isinstance(p, StdOutPrinter):
|
|
581
|
+
done = True
|
|
582
|
+
break
|
|
583
|
+
if not done:
|
|
584
|
+
self.printers.append(StdOutPrinter())
|
|
585
|
+
else:
|
|
586
|
+
self.logger.warning(
|
|
587
|
+
"Incorrect metadata field value 'print-mode': %s",
|
|
588
|
+
self.metadata["print-mode"],
|
|
589
|
+
)
|
|
441
590
|
|
|
442
591
|
def parse_named_path(self, name, disposably=False):
|
|
443
592
|
"""disposably is True when a Matcher is needed for some purpose other than
|
|
@@ -653,7 +802,7 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
653
802
|
msg = "Line cannot be None"
|
|
654
803
|
self.logger.error(msg)
|
|
655
804
|
raise MatchException(msg)
|
|
656
|
-
|
|
805
|
+
if len(line) == 0:
|
|
657
806
|
msg = "Line cannot be len() == 0"
|
|
658
807
|
self.logger.error(msg)
|
|
659
808
|
raise MatchException(msg)
|
|
@@ -684,6 +833,9 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
684
833
|
# this exception will blow up a standalone CsvPath but should be
|
|
685
834
|
# caught and handled if there is a CsvPaths.
|
|
686
835
|
#
|
|
836
|
+
# but when would it happen? shouldn't we just let Python's exception
|
|
837
|
+
# handle it should it really occur?
|
|
838
|
+
#
|
|
687
839
|
if self.scanner.filename is None:
|
|
688
840
|
raise FileException("There is no filename")
|
|
689
841
|
with open(self.scanner.filename, "r", encoding="utf-8") as file:
|
|
@@ -695,6 +847,35 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
695
847
|
yield line
|
|
696
848
|
self.finalize()
|
|
697
849
|
|
|
850
|
+
"""
|
|
851
|
+
# potential replacement for method above
|
|
852
|
+
# this is a proposal for having the results of one csvpath feed into another
|
|
853
|
+
# in memory. the goal being to both shape the data chain-of-responsibility-style
|
|
854
|
+
# and also to narrow the data for performance gains.
|
|
855
|
+
#
|
|
856
|
+
# we would need:
|
|
857
|
+
# - csvpaths.chain_result_data
|
|
858
|
+
# - named-path added to csvpath metadata early-on
|
|
859
|
+
#
|
|
860
|
+
# caching this here for now. jury is out on if it should be added.
|
|
861
|
+
#
|
|
862
|
+
def _next_line_new(self) -> List[Any]:
|
|
863
|
+
self.logger.info("beginning to scan file: %s", self.scanner.filename)
|
|
864
|
+
if self.csvpath and self.csvpaths.chain_result_data:
|
|
865
|
+
rs = csvpath.result_manager.get_named_results(self.metadata["named-paths"])
|
|
866
|
+
for line in rs[len(rs)-1].lines:
|
|
867
|
+
yield line
|
|
868
|
+
elif:
|
|
869
|
+
with open(self.scanner.filename, "r", encoding="utf-8") as file:
|
|
870
|
+
reader = csv.reader(
|
|
871
|
+
file, delimiter=self.delimiter, quotechar=self.quotechar
|
|
872
|
+
)
|
|
873
|
+
for line in reader:
|
|
874
|
+
self.track_line(line=line)
|
|
875
|
+
yield line
|
|
876
|
+
self.finalize()
|
|
877
|
+
"""
|
|
878
|
+
|
|
698
879
|
def finalize(self) -> None:
|
|
699
880
|
"""clears caches, etc. this is an internal method, but not _ because
|
|
700
881
|
it is part of the lifecycle and we might find a reason to call it
|
|
@@ -788,6 +969,8 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
788
969
|
return False
|
|
789
970
|
|
|
790
971
|
def raise_match_count_if(self):
|
|
972
|
+
"""if the match count has already been raised earlier in the matching
|
|
973
|
+
process than the caller we don't raise it; otherwise, we raise."""
|
|
791
974
|
if self._current_match_count == self.match_count:
|
|
792
975
|
self.match_count += 1
|
|
793
976
|
else:
|
|
@@ -880,7 +1063,7 @@ class CsvPath(CsvPathPublic, ErrorCollector): # pylint: disable=R0902, R0904
|
|
|
880
1063
|
self.matcher = Matcher(
|
|
881
1064
|
csvpath=self, data=self.match, line=line, headers=self.headers, myid=h
|
|
882
1065
|
)
|
|
883
|
-
self.matcher.
|
|
1066
|
+
self.matcher.AND = self._AND
|
|
884
1067
|
else:
|
|
885
1068
|
self.logger.debug("Resetting and reloading matcher")
|
|
886
1069
|
self.matcher.reset()
|