diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 933df4967..6a35e8f38 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -21,7 +21,7 @@ assignees: '' ## Logs & Screenshots - ## Environment diff --git a/.github/ISSUE_TEMPLATE/false-positive.md b/.github/ISSUE_TEMPLATE/false-positive.md index 15bf86835..828ab1c5e 100644 --- a/.github/ISSUE_TEMPLATE/false-positive.md +++ b/.github/ISSUE_TEMPLATE/false-positive.md @@ -21,7 +21,7 @@ assignees: '' ## Logs & Screenshots - ## Environment diff --git a/.github/workflows/cd-pypi.yml b/.github/workflows/cd-pypi.yml index 60d1e7a7c..f7412d7a4 100644 --- a/.github/workflows/cd-pypi.yml +++ b/.github/workflows/cd-pypi.yml @@ -11,19 +11,19 @@ jobs: shell: bash strategy: matrix: - python: [3.9] + python: ["3.10"] steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: "[INIT] Install Python ${{ matrix.python }}" - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: "[INIT] Install Poetry" uses: snok/install-poetry@v1.3 - name: "[INIT] Restore dependency cache" id: cache-restore - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cache/pip diff --git a/.github/workflows/cd-site.yml b/.github/workflows/cd-site.yml index 0353a60bb..513ea6bda 100644 --- a/.github/workflows/cd-site.yml +++ b/.github/workflows/cd-site.yml @@ -11,17 +11,17 @@ jobs: shell: bash strategy: matrix: - python: [3.9] - ruby: [3.0.0] + python: ["3.10"] + ruby: ["3.0.0"] steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: "[INIT] Install Python ${{ matrix.python }}" - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: "[INIT] Install Ruby ${{ matrix.ruby }}" - uses: ruby/setup-ruby@v1.95.0 + uses: ruby/setup-ruby@v1.165.1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true diff --git a/.github/workflows/ci-danger.yml b/.github/workflows/ci-danger.yml index b4450e1c2..5fe191111 100644 --- a/.github/workflows/ci-danger.yml +++ b/.github/workflows/ci-danger.yml @@ -10,15 +10,15 @@ jobs: shell: bash strategy: matrix: - ruby: [3.0.0] + ruby: ["3.0.0"] steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: "[INIT] Install Ruby ${{ matrix.ruby }}" - uses: ruby/setup-ruby@v1.95.0 + uses: ruby/setup-ruby@v1.165.1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true diff --git a/.github/workflows/ci-label.yml b/.github/workflows/ci-label.yml index 38e83085b..22fcd1d8b 100644 --- a/.github/workflows/ci-label.yml +++ b/.github/workflows/ci-label.yml @@ -11,8 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: "[EXEC] Synchronize labels" - uses: crazy-max/ghaction-github-labeler@v3 + uses: crazy-max/ghaction-github-labeler@v5 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci-lint-test.yml b/.github/workflows/ci-lint-test.yml index 45ee291d6..b6b88ff26 100644 --- a/.github/workflows/ci-lint-test.yml +++ b/.github/workflows/ci-lint-test.yml @@ -13,9 +13,9 @@ jobs: python: ["3.10"] steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "[INIT] Install Python ${{ matrix.python }}" - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: "[INIT] Install Poetry" @@ -30,9 +30,12 @@ jobs: key: ${{ runner.os }}-py${{ matrix.python }}-${{ hashFiles('**/poetry.lock') }} - name: "[INIT] Install dependencies" if: ${{ !steps.cache-restore.outputs.cache-hit }} - run: pip install poetry && poetry install + run: poetry install - name: "[EXEC] Lint" - run: ./utils ci lint + uses: chartboost/ruff-action@v1 + # - name: Run Pre-Commit Tests 🧪 + # uses: pre-commit/action@v3.0.0 + test-cover: name: Test & Cover if: "!(contains(github.event.head_commit.message, '[skip_ci]'))" @@ -42,13 +45,14 @@ jobs: shell: bash strategy: matrix: - python: ["3.8", "3.9", "3.10", "pypy3.9", "pypy3.10"] + # 5 versions / years of coverage + python: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.9", "pypy3.10"] os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: "[INIT] Checkout repository" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "[INIT] Install Python ${{ matrix.python }}" - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: "[INIT] Install Poetry" @@ -64,10 +68,14 @@ jobs: - name: "[INIT] Install dependencies" if: ${{ !steps.cache-restore.outputs.cache-hit }} run: pip install poetry && poetry install - - name: "[EXEC] Test" - run: ./utils ci test --coverage - - name: "[EXEC] Upload coverage to Codecov" - uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true - flags: ${{ matrix.os }},py${{ matrix.python }} + - name: "[EXEC] Test build package" + run: poetry build + - name: "[EXEC] Unit test" + run: poetry run pytest +# - name: "[EXEC] Test" +# run: ./utils ci test --coverage +# - name: "[EXEC] Upload coverage to Codecov" +# uses: codecov/codecov-action@v3.1.4 # TODO: maybe not on every py-ver? +# with: +# fail_ci_if_error: true +# flags: ${{ matrix.os }},py${{ matrix.python }} diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 000000000..4feec1ac2 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,75 @@ + +#line-length = 90 +target-version = "py38" + +[lint] +select = [ + "A", # flake8-builtins +# "ANN", # flake8-annotations TODO activate +# "ARG", # flake8-unused-arguments + "B", # Bugbear + "C", + "COM", # flake8-commas +# "CPY", # flake8-copyright + "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez +# "D", # pydocstyle, TODO: activate + "E", # pycodestyle errors +# "ERA", # eradicate commented out code + "F", # pyflakes + "FA", # flake8-future-annotations +# "FBT", # boolean traps + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # incomplete isort + "INP", # flake8-no-pep420 + "LOG", # flake8-logging +# "N", # naming + "NPY", # NumPy-specific rules + "PD", # pandas-vet + "PERF", # Perflint + "PL", # Pylint + "PTH", # flake8-use-pathlib + "PYI", # flake8-pyi + "RET", # flake8-return + "RUF", # Ruff-specific rules + "S", # bandit, security + "SLF", # flake8-self + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "TCH", # flake8-type-checking + "T10", # flake8-print + "UP", # pyupgrade + "W", # pycodestyle warnings + "YTT", # flake8-2020 +] +ignore = [ + "PLR2004", # magic values + "COM812", # trailing comma -> done by formatter + # TODO: below is temporary + +] +preview = true + +[lint.per-file-ignores] +"tests/**" = ["S", "N8", "SLF001", "PLR2004"] +"proselint/checks/**" = ["RUF001", "RUF002", "RUF003", "TID252"] +"scripts/**" = ["E501", "DTZ005"] + +"plugins/**" = ["INP001", "RUF012"] +"scripts_web/**" = ["S1", "SIM", "RET", "TCH"] + +[lint.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 11 + +[lint.isort] +force-single-line = true + +[lint.pyupgrade] +keep-runtime-typing = true + +[format] +# per default like Black +#preview = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d46f482d..e8d691064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,131 @@ # Change Log +## [proselint@0.15.0](https://github.com/amperser/proselint/compare/0.13.0...0.15.0) + +### Biggest Changes + +- featureset of py38 +- cleaner and faster code +- fixed bugs, undefined behavior, broken legacy code +- some checks did not work as intended +- multiprocessed parallelization and other optimizations + +### Benchmark-Comparison + +- custom set of files, same hardware + +#### Windows + +Proselint-main (uncached & cached) + +- Found 104 lint-warnings in 47.579 s +- Found 104 lint-warnings in 0.878 s + +Proselint-modernized + +- Found 108 lint-warnings in 39.930 s (12 files, 617.45 kiByte) -> serialized +- Found 108 lint-warnings in 13.164 s (12 files, 617.45 kiByte) -> serial files, parallel checks +- Found 108 lint-warnings in 9.931 s (12 files, 617.45 kiByte) -> global check-executor +- Found 108 lint-warnings in 0.011 s (12 files, 617.45 kiByte) -> cached + +#### Linux / WSL + +Proselint-main (uncached & cached) + +- Found 104 lint-warnings in 37.041 s +- Found 104 lint-warnings in 0.771 s + +Proselint-modernized + +- Found 108 lint-warnings in 34.521 s (12 files, 617.45 kiByte) +- Found 108 lint-warnings in 8.248 s (12 files, 617.45 kiByte) +- Found 108 lint-warnings in 6.098 s (12 files, 617.45 kiByte) +- Found 108 lint-warnings in 0.044 s (12 files, 617.45 kiByte) + +### Breaking changes + +- cli arg `--time` -> `--benchmark`, `-b` +- cli arg `--debug` -> `--verbose`, `-v` +- exit code now returns number of errors +- cli arg `--output-format` controls old flags json, compact +- py >= 3.8 +- api-changes -> web_scripts & plugins untested +- config adjustments (1 test removed, 1 added) + - "misc.metaconcepts": False, # TODO: remove, was duplicate of scare_quotes + +### Whats broken / untested + +- web_scripts +- plugins +- Github action + + +### Detailed Changelog + +- featureset of py38 +- use pathlib instead of string based paths +- add type-hinting +- replaced memoizer, short-comings: + - shelves have a good interface but are slow + - cache-init and -closing was a mess + - clearing cache resulted in broken states + - memoize-wrapper was wasting resources (hash of text recalc for every check, ..) + - age of cache-entries was not considered + - every cached fn had its own file-cache + - cache migration was done on every memoize-decoration +- cache + - hash test beforehand only once + - consider age of items + - memoize lint() instead of checks +- fix bugs related to: + - overshadowing builtin names (list, ...) + - string based path-traversal + - mix of relative and absolute paths (also relics that deleted file somewhere) + - varius small bugs (ie. missing comma in element lists) + - cache was not cleaned up by fn (as it should) +- lots of modernization from old codebase + - latest python versions had trouble with proselint +- update deps +- reformat using black-style, done by ruff +- linted codebase with ruff +- add logger and refactor print-output + - linter informs about duration + - results include link to file + - inform about duration, files scanned and text-size +- refactor unittest + - remove classes / boilerplate + - move tests for checks into separate dir + - more helpful error-messages + - remove duplicates + - repair broken tests + - lower complexity of tests (each test should only test for one thing) +- add unittests + - detect missing check-flags in default-config + - find unavaible checks for default-config-flags + - don't fail on wrong config-flags + - check working cache, speed-test + - check working cache, same results +- minimal changes to outer api +- config + - correctness of default-config is tested + - don't fail on faulty config-flags +- checks + - remove duplicates + - fix padding for checks using regex +- existence_check() - join and padding -> cleared up + - join is always done + - padding can be selected py Enum, defaults to separate in text + - fix checks accordingly +- put root-scripts into sep dir +- exithandler for more graceful ctrl+c +- ppm-wrapper is now more forgiving with small sample-sizes +- add parallelization + - checks() are run multiprocessed + - one global executioner collects tasks for all files to analyze + - break up the slowest checks to balance the load +- optimizations were done with profiling and benchmarking +- overhaul controling output-format by cli + ## [proselint@0.13.0](https://github.com/amperser/proselint/compare/0.12.0...0.13.0) ### Bug Fixes diff --git a/LICENSE.md b/LICENSE.md index a984390db..402076869 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright © 2014–2015, Jordan Suchow, Michael Pacer, and Lara A. Ross +Copyright © 2014–2015, Jordan Suchow, Michael Pacer, and Lara A. Ross All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/MANIFEST.in b/MANIFEST.in index 2b65beec0..5c8c32fbe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.md LICENSE.md CHANGELOG.md tests/illegal-chars.txt tests/*.py tests/test_gmeu/*.py \ No newline at end of file +include README.md LICENSE.md CHANGELOG.md tests/illegal-chars.txt tests/*.py tests/test_gmeu/*.py diff --git a/Procfile b/Procfile index 9229dd983..6f4b0d789 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: gunicorn app:app --log-file=- -worker: python worker.py -clock: python clock.py +worker: python scripts/worker.py +clock: python scripts/clock.py diff --git a/plugins/sublime/SublimeLinter-contrib-proselint/linter.py b/plugins/sublime/SublimeLinter-contrib-proselint/linter.py index 21ff3778d..2abb67069 100644 --- a/plugins/sublime/SublimeLinter-contrib-proselint/linter.py +++ b/plugins/sublime/SublimeLinter-contrib-proselint/linter.py @@ -15,18 +15,16 @@ class Proselint(Linter): """Provides an interface to proselint.""" - syntax = ('html', 'markdown', 'plain text') - cmd = 'proselint' + syntax = ("html", "markdown", "plain text") + cmd = "proselint" executable = None - version_args = '--version' - version_re = r'(?P\d+\.\d+\.\d+)' - version_requirement = '>= 0.0.0' - regex = ( - r'^.+?:(?P\d+):(?P\d+): \S* (?P.+)' - ) + version_args = "--version" + version_re = r"(?P\d+\.\d+\.\d+)" + version_requirement = ">= 0.0.0" + regex = r"^.+?:(?P\d+):(?P\d+): \S* (?P.+)" multiline = True line_col_base = (1, 1) - tempfile_suffix = 'pltmp' + tempfile_suffix = "pltmp" selectors = {} word_re = None defaults = {} diff --git a/plugins/webeditor/cm-validator-remote.js b/plugins/webeditor/cm-validator-remote.js index afd9318d9..d8150232d 100755 --- a/plugins/webeditor/cm-validator-remote.js +++ b/plugins/webeditor/cm-validator-remote.js @@ -6,15 +6,15 @@ CodeMirror.remoteValidator = function(cm, updateLinting, options) { updateLinting(cm, []); return; } - + function result_cb(error_list) { var found = []; - + for(var i in error_list) { var error = error_list[i]; - + var start_line = error.line_no; var start_char; @@ -37,7 +37,7 @@ CodeMirror.remoteValidator = function(cm, updateLinting, options) { severity = error.severity; else severity = 'error'; - + found.push({ from: CodeMirror.Pos(start_line - 1, start_char), to: CodeMirror.Pos(end_line - 1, end_char), @@ -45,10 +45,9 @@ CodeMirror.remoteValidator = function(cm, updateLinting, options) { severity: severity // "error", "warning" }); } - + updateLinting(cm, found); } - + options.check_cb(text, result_cb) } - diff --git a/plugins/webeditor/codemirror/README.md b/plugins/webeditor/codemirror/README.md index 976584e3b..b87f7d5c0 100755 --- a/plugins/webeditor/codemirror/README.md +++ b/plugins/webeditor/codemirror/README.md @@ -4,6 +4,6 @@ CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding in, it will color your code, and optionally help with indentation. -The project page is http://codemirror.net -The manual is at http://codemirror.net/doc/manual.html +The project page is http://codemirror.net +The manual is at http://codemirror.net/doc/manual.html The contributing guidelines are in [CONTRIBUTING.md](https://github.com/marijnh/CodeMirror/blob/master/CONTRIBUTING.md) diff --git a/plugins/webeditor/codemirror/addon/edit/matchbrackets.js b/plugins/webeditor/codemirror/addon/edit/matchbrackets.js index f4925b725..72bca5709 100755 --- a/plugins/webeditor/codemirror/addon/edit/matchbrackets.js +++ b/plugins/webeditor/codemirror/addon/edit/matchbrackets.js @@ -3,7 +3,7 @@ (document.documentMode == null || document.documentMode < 8); var Pos = CodeMirror.Pos; - // Disable brace matching in long lines, since it'll cause hugely slow updates + // Disable brace matching in long lines, since it'll cause hugely slow updates var maxLineLen = 1000; var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; diff --git a/plugins/webeditor/codemirror/addon/hint/javascript-hint.js b/plugins/webeditor/codemirror/addon/hint/javascript-hint.js index fe2240af1..226fe6bb4 100755 --- a/plugins/webeditor/codemirror/addon/hint/javascript-hint.js +++ b/plugins/webeditor/codemirror/addon/hint/javascript-hint.js @@ -4,7 +4,7 @@ function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; diff --git a/plugins/webeditor/codemirror/addon/hint/pig-hint.js b/plugins/webeditor/codemirror/addon/hint/pig-hint.js index 9847996be..d831ccfe9 100755 --- a/plugins/webeditor/codemirror/addon/hint/pig-hint.js +++ b/plugins/webeditor/codemirror/addon/hint/pig-hint.js @@ -2,7 +2,7 @@ function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; @@ -25,11 +25,11 @@ token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, className: token.string == ":" ? "pig-type" : null}; } - + if (!context) var context = []; context.push(tprop); - - var completionList = getCompletions(token, context); + + var completionList = getCompletions(token, context); completionList = completionList.sort(); //prevent autocomplete for last word, instead show dropdown with one word if(completionList.length == 1) { @@ -40,24 +40,24 @@ from: CodeMirror.Pos(cur.line, token.start), to: CodeMirror.Pos(cur.line, token.end)}; } - + CodeMirror.pigHint = function(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); }; - + var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " - + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + "NEQ MATCHES TRUE FALSE"; var pigKeywordsU = pigKeywords.split(" "); var pigKeywordsL = pigKeywords.toLowerCase().split(" "); - + var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP"; var pigTypesU = pigTypes.split(" "); var pigTypesL = pigTypes.toLowerCase().split(" "); - - var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + + var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " @@ -66,9 +66,9 @@ + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " - + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; - var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); - var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; + var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); + var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " @@ -76,13 +76,13 @@ + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize " + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax " + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" "); - + function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); } - + function gatherCompletions(obj) { if(obj == ":") { forEach(pigTypesL, maybeAdd); @@ -103,11 +103,11 @@ // find in the current environment. var obj = context.pop(), base; - if (obj.type == "variable") + if (obj.type == "variable") base = obj.string; else if(obj.type == "variable-3") base = ":" + obj.string; - + while (base != null && context.length) base = base[context.pop().string]; if (base != null) gatherCompletions(base); diff --git a/plugins/webeditor/codemirror/addon/lint/lint.js b/plugins/webeditor/codemirror/addon/lint/lint.js index dd838e7ea..3f0b66860 100755 --- a/plugins/webeditor/codemirror/addon/lint/lint.js +++ b/plugins/webeditor/codemirror/addon/lint/lint.js @@ -95,7 +95,7 @@ CodeMirror.validate = (function() { else updateLinting(cm, options.getAnnotations(cm.getValue())); } - + function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); var state = cm._lintState, options = state.options; @@ -170,7 +170,7 @@ CodeMirror.validate = (function() { CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); delete cm._lintState; } - + if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; diff --git a/plugins/webeditor/codemirror/addon/mode/multiplex.js b/plugins/webeditor/codemirror/addon/mode/multiplex.js index e77ff2a9c..bf85b3926 100755 --- a/plugins/webeditor/codemirror/addon/mode/multiplex.js +++ b/plugins/webeditor/codemirror/addon/mode/multiplex.js @@ -61,7 +61,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return innerToken; } }, - + indent: function(state, textAfter) { var mode = state.innerActive ? state.innerActive.mode : outer; if (!mode.indent) return CodeMirror.Pass; diff --git a/plugins/webeditor/codemirror/addon/mode/overlay.js b/plugins/webeditor/codemirror/addon/mode/overlay.js index fba38987b..b7928a7bb 100755 --- a/plugins/webeditor/codemirror/addon/mode/overlay.js +++ b/plugins/webeditor/codemirror/addon/mode/overlay.js @@ -43,14 +43,14 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; else return state.overlayCur; }, - + indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, electricChars: base.electricChars, innerMode: function(state) { return {state: state.base, mode: base}; }, - + blankLine: function(state) { if (base.blankLine) base.blankLine(state.base); if (overlay.blankLine) overlay.blankLine(state.overlay); diff --git a/plugins/webeditor/codemirror/addon/search/match-highlighter.js b/plugins/webeditor/codemirror/addon/search/match-highlighter.js index c6e35cd97..14c1dab5b 100755 --- a/plugins/webeditor/codemirror/addon/search/match-highlighter.js +++ b/plugins/webeditor/codemirror/addon/search/match-highlighter.js @@ -14,7 +14,7 @@ (function() { var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; - + function State(options) { this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS; this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE; diff --git a/plugins/webeditor/codemirror/addon/selection/active-line.js b/plugins/webeditor/codemirror/addon/selection/active-line.js index 988a0ffbf..211de0fef 100755 --- a/plugins/webeditor/codemirror/addon/selection/active-line.js +++ b/plugins/webeditor/codemirror/addon/selection/active-line.js @@ -20,7 +20,7 @@ delete cm._activeLine; } }); - + function clearActiveLine(cm) { if ("_activeLine" in cm) { cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); diff --git a/plugins/webeditor/codemirror/bin/compress b/plugins/webeditor/codemirror/bin/compress index d059b618b..a53af7908 100755 --- a/plugins/webeditor/codemirror/bin/compress +++ b/plugins/webeditor/codemirror/bin/compress @@ -71,7 +71,7 @@ if (files.length) { files.map(function(a){return a.name;}).join(", ")); process.exit(1); } - + if (local) { require("child_process").spawn(local, args, {stdio: ["ignore", process.stdout, process.stderr]}); } else { diff --git a/plugins/webeditor/codemirror/demo/closetag.html b/plugins/webeditor/codemirror/demo/closetag.html index 87f4f019f..43805eb39 100755 --- a/plugins/webeditor/codemirror/demo/closetag.html +++ b/plugins/webeditor/codemirror/demo/closetag.html @@ -16,7 +16,7 @@ - +

Close-Tag Demo

  • Type an html tag. When you type '>' or '/', the tag will auto-close/complete. Block-level tags will indent.
  • diff --git a/plugins/webeditor/codemirror/demo/html5complete.html b/plugins/webeditor/codemirror/demo/html5complete.html index 5091354ae..4ffb51e3e 100755 --- a/plugins/webeditor/codemirror/demo/html5complete.html +++ b/plugins/webeditor/codemirror/demo/html5complete.html @@ -13,14 +13,14 @@ - + - +

    HTML5 code completation demo

    • Type an html tag. If you press Ctrl+Space a hint panel show the code suggest. You can type to autocomplete tags, attributes if your cursor are inner a tag or attribute values if your cursor are inner a attribute value.
    • @@ -37,11 +37,11 @@

      HTML5 code completation demo

      - + - +
      - +

      HTML5 Autocomplete

      - +

      W2S Cloud IDE

      - +
      - - + +
      - + - + @@ -81,7 +81,7 @@

      W2S Cloud IDE

      CodeMirror.commands.autocomplete = function(cm) { CodeMirror.showHint(cm, CodeMirror.htmlHint); } - + var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: 'text/html', autoCloseTags: true, diff --git a/plugins/webeditor/codemirror/demo/preview.html b/plugins/webeditor/codemirror/demo/preview.html index f70cdb009..7c839272c 100755 --- a/plugins/webeditor/codemirror/demo/preview.html +++ b/plugins/webeditor/codemirror/demo/preview.html @@ -62,7 +62,7 @@

      CodeMirror: HTML5 preview

      clearTimeout(delay); delay = setTimeout(updatePreview, 300); }); - + function updatePreview() { var previewFrame = document.getElementById('preview'); var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; diff --git a/plugins/webeditor/codemirror/demo/variableheight.html b/plugins/webeditor/codemirror/demo/variableheight.html index b00f7e454..71f4ace45 100755 --- a/plugins/webeditor/codemirror/demo/variableheight.html +++ b/plugins/webeditor/codemirror/demo/variableheight.html @@ -36,10 +36,10 @@

      CodeMirror: Variable Height Demo

      ### Header 3 > This is a blockquote. -> +> > This is the second paragraph in the blockquote. > -> ## This is an H2 in a blockquote +> ## This is an H2 in a blockquote diff --git a/plugins/webeditor/codemirror/mode/asterisk/asterisk.js b/plugins/webeditor/codemirror/mode/asterisk/asterisk.js index d491857e9..60b689d1d 100755 --- a/plugins/webeditor/codemirror/mode/asterisk/asterisk.js +++ b/plugins/webeditor/codemirror/mode/asterisk/asterisk.js @@ -8,7 +8,7 @@ * Created: 05/17/2012 09:20:25 PM * Revision: none * - * Author: Stas Kobzar (stas@modulis.ca), + * Author: Stas Kobzar (stas@modulis.ca), * Company: Modulis.ca Inc. * * ===================================================================================== @@ -99,9 +99,9 @@ CodeMirror.defineMode("asterisk", function() { state.extenStart = true; switch(cur) { case 'same': state.extenSame = true; break; - case 'include': - case 'switch': - case 'ignorepat': + case 'include': + case 'switch': + case 'ignorepat': state.extenInclude = true;break; default:break; } @@ -121,7 +121,7 @@ CodeMirror.defineMode("asterisk", function() { }; }, token: function(stream, state) { - + var cur = ''; var ch = ''; if(stream.eatSpace()) return null; @@ -174,7 +174,7 @@ CodeMirror.defineMode("asterisk", function() { } else{ return basicToken(stream,state); } - + return null; } }; diff --git a/plugins/webeditor/codemirror/mode/clike/clike.js b/plugins/webeditor/codemirror/mode/clike/clike.js index 1b350aeb8..c14d7b400 100755 --- a/plugins/webeditor/codemirror/mode/clike/clike.js +++ b/plugins/webeditor/codemirror/mode/clike/clike.js @@ -222,7 +222,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-java", { name: "clike", - keywords: words("abstract assert boolean break byte case catch char class const continue default " + + keywords: words("abstract assert boolean break byte case catch char class const continue default " + "do double else enum extends final finally float for goto if implements import " + "instanceof int interface long native new package private protected public " + "return short static strictfp super switch synchronized this throw throws transient " + @@ -238,12 +238,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-csharp", { name: "clike", - keywords: words("abstract as base break case catch checked class const continue" + - " default delegate do else enum event explicit extern finally fixed for" + - " foreach goto if implicit in interface internal is lock namespace new" + - " operator out override params private protected public readonly ref return sealed" + - " sizeof stackalloc static struct switch this throw try typeof unchecked" + - " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + + keywords: words("abstract as base break case catch checked class const continue" + + " default delegate do else enum event explicit extern finally fixed for" + + " foreach goto if implicit in interface internal is lock namespace new" + + " operator out override params private protected public readonly ref return sealed" + + " sizeof stackalloc static struct switch this throw try typeof unchecked" + + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + " global group into join let orderby partial remove select set value var yield"), blockKeywords: words("catch class do else finally for foreach if struct switch try while"), builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" + @@ -265,30 +265,30 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { CodeMirror.defineMIME("text/x-scala", { name: "clike", keywords: words( - + /* scala */ "abstract case catch class def do else extends false final finally for forSome if " + "implicit import lazy match new null object override package private protected return " + "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " + "<% >: # @ " + - + /* package scala */ "assert assume require print println printf readLine readBoolean readByte readShort " + "readChar readInt readLong readFloat readDouble " + - + "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " + "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " + - - /* package java.lang */ + + /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" - - + + ), blockKeywords: words("catch class do else finally for forSome if match switch try while"), atoms: words("true false null"), diff --git a/plugins/webeditor/codemirror/mode/clike/scala.html b/plugins/webeditor/codemirror/mode/clike/scala.html index f3c7eea49..60b7309b8 100755 --- a/plugins/webeditor/codemirror/mode/clike/scala.html +++ b/plugins/webeditor/codemirror/mode/clike/scala.html @@ -19,7 +19,7 @@ } html, form, .CodeMirror, .CodeMirror-scroll { - height: 100%; + height: 100%; } @@ -44,7 +44,7 @@ import parallel.ParIterable /** A template trait for traversable collections of type `Traversable[A]`. - * + * * $traversableInfo * @define mutability * @define traversableInfo @@ -54,15 +54,15 @@ * {{{ * def foreach[U](f: Elem => U): Unit * }}} - * Collection classes mixing in this trait provide a concrete + * Collection classes mixing in this trait provide a concrete * `foreach` method which traverses all the * elements contained in the collection, applying a given function to each. * They also need to provide a method `newBuilder` * which creates a builder for collections of the same kind. - * + * * A traversable class might or might not have two properties: strictness * and orderedness. Neither is represented as a type. - * + * * The instances of a strict collection class have all their elements * computed before they can be used as values. By contrast, instances of * a non-strict collection class may defer computation of some of their @@ -71,13 +71,13 @@ * * `scala.collection.immutable.Stream`. * A more general class of examples are `TraversableViews`. - * + * * If a collection is an instance of an ordered collection class, traversing * its elements with `foreach` will always visit elements in the * same order, even for different runs of the program. If the class is not * ordered, `foreach` can visit elements in different orders for * different runs (but it will keep the same order in the same run).' - * + * * A typical example of a collection class which is not ordered is a * `HashMap` of objects. The traversal order for hash maps will * depend on the hash codes of its elements, and these hash codes might @@ -94,7 +94,7 @@ * @define Coll Traversable * @define coll traversable collection */ - trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] + trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] with FilterMonadic[A, Repr] with TraversableOnce[A] with GenTraversableLike[A, Repr] @@ -131,15 +131,15 @@ protected[this] def parCombiner = ParIterable.newCombiner[A] /** Applies a function `f` to all elements of this $coll. - * + * * Note: this method underlies the implementation of most other bulk operations. * It's important to implement this method in an efficient way. - * + * * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. - * - * @tparam U the type parameter describing the result of function `f`. + * + * @tparam U the type parameter describing the result of function `f`. * This result will always be ignored. Typically `U` is `Unit`, * but this is not necessary. * @@ -167,7 +167,7 @@ * such as `Stream`, the predicate returns `true` if all elements have been computed. * It returns `false` if the stream is not yet evaluated to the end. * - * Note: many collection methods will not work on collections of infinite sizes. + * Note: many collection methods will not work on collections of infinite sizes. * * @return `true` if this collection is known to have finite size, `false` otherwise. */ @@ -188,16 +188,16 @@ /** Concatenates this $coll with the elements of a traversable collection. * It differs from ++ in that the right operand determines the type of the * resulting collection rather than the left one. - * + * * @param that the traversable to append. - * @tparam B the element type of the returned collection. + * @tparam B the element type of the returned collection. * @tparam That $thatinfo * @param bf $bfinfo * @return a new collection of type `That` which contains all elements * of this $coll followed by all elements of `that`. - * + * * @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B] - * + * * @return a new $coll which contains all elements of this $coll * followed by all elements of `that`. */ @@ -219,7 +219,7 @@ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) - b.sizeHint(this) + b.sizeHint(this) for (x <- this) b += f(x) b.result } @@ -238,7 +238,7 @@ */ def filter(p: A => Boolean): Repr = { val b = newBuilder - for (x <- this) + for (x <- this) if (p(x)) b += x b.result } @@ -269,14 +269,14 @@ * The order of the elements is preserved. * * @usecase def filterMap[B](f: A => Option[B]): $Coll[B] - * + * * @param pf the partial function which filters and maps the $coll. * @return a new $coll resulting from applying the given option-valued function * `f` to each element and collecting all defined results. * The order of the elements is preserved. def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) - for (x <- this) + for (x <- this) f(x) match { case Some(y) => b += y case _ => @@ -288,7 +288,7 @@ /** Partitions this $coll in two ${coll}s according to a predicate. * * @param p the predicate on which to partition. - * @return a pair of ${coll}s: the first $coll consists of all elements that + * @return a pair of ${coll}s: the first $coll consists of all elements that * satisfy the predicate `p` and the second $coll consists of all elements * that don't. The relative order of the elements in the resulting ${coll}s * is the same as in the original $coll. @@ -348,7 +348,7 @@ } /** Finds the first element of the $coll satisfying a predicate, if any. - * + * * $mayNotTerminateInf * $orderDependent * @@ -419,7 +419,7 @@ * @return a $coll consisting of all elements of this $coll * except the first one. * @throws `UnsupportedOperationException` if the $coll is empty. - */ + */ override def tail: Repr = { if (isEmpty) throw new UnsupportedOperationException("empty.tail") drop(1) @@ -465,7 +465,7 @@ def take(n: Int): Repr = slice(0, n) - def drop(n: Int): Repr = + def drop(n: Int): Repr = if (n <= 0) { val b = newBuilder b.sizeHint(this) @@ -501,7 +501,7 @@ val b = newBuilder if (until <= from) b.result else { - b.sizeHintBounded(until - from, this) + b.sizeHintBounded(until - from, this) sliceInternal(from, until, b) } } @@ -555,7 +555,7 @@ * * @return an iterator over all the tails of this $coll * @example `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)` - */ + */ def tails: Iterator[Repr] = iterateUntilEmpty(_.tail) /** Iterates over the inits of this $coll. The first value will be this @@ -574,12 +574,12 @@ * or the end of the array is reached, or `len` elements have been copied. * * $willNotTerminateInf - * + * * @param xs the array to fill. * @param start the starting index. * @param len the maximal number of elements to copy. - * @tparam B the type of the elements of the array. - * + * @tparam B the type of the elements of the array. + * * * @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit */ @@ -623,7 +623,7 @@ } /** Creates a non-strict view of this $coll. - * + * * @return a non-strict view of this $coll. */ def view = new TraversableView[A, Repr] { @@ -635,10 +635,10 @@ * * Note: the difference between `view` and `slice` is that `view` produces * a view of the current $coll, whereas `slice` produces a new $coll. - * + * * Note: `view(from, to)` is equivalent to `view.slice(from, to)` * $orderDependent - * + * * @param from the index of the first element of the view * @param until the index of the element following the view * @return a non-strict view of a slice of this $coll, starting at index `from` @@ -653,7 +653,7 @@ * restricts the domain of subsequent `map`, `flatMap`, `foreach`, * and `withFilter` operations. * $orderDependent - * + * * @param p the predicate used to test elements. * @return an object of class `WithFilter`, which supports * `map`, `flatMap`, `foreach`, and `withFilter` operations. @@ -678,22 +678,22 @@ * the given function `f` to each element of the outer $coll * that satisfies predicate `p` and collecting the results. * - * @usecase def map[B](f: A => B): $Coll[B] - * + * @usecase def map[B](f: A => B): $Coll[B] + * * @return a new $coll resulting from applying the given function * `f` to each element of the outer $coll that satisfies * predicate `p` and collecting the results. */ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) - for (x <- self) + for (x <- self) if (p(x)) b += f(x) b.result } /** Builds a new collection by applying a function to all elements of the * outer $coll containing this `WithFilter` instance that satisfy - * predicate `p` and concatenating the results. + * predicate `p` and concatenating the results. * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -705,13 +705,13 @@ * concatenating the results. * * @usecase def flatMap[B](f: A => TraversableOnce[B]): $Coll[B] - * + * * @return a new $coll resulting from applying the given collection-valued function * `f` to each element of the outer $coll that satisfies predicate `p` and concatenating the results. */ def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) - for (x <- self) + for (x <- self) if (p(x)) b ++= f(x).seq b.result } @@ -721,15 +721,15 @@ * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. - * - * @tparam U the type parameter describing the result of function `f`. + * + * @tparam U the type parameter describing the result of function `f`. * This result will always be ignored. Typically `U` is `Unit`, * but this is not necessary. * * @usecase def foreach(f: A => Unit): Unit - */ - def foreach[U](f: A => U): Unit = - for (x <- self) + */ + def foreach[U](f: A => U): Unit = + for (x <- self) if (p(x)) f(x) /** Further refines the filter for this $coll. @@ -740,7 +740,7 @@ * All these operations apply to those elements of this $coll which * satisfy the predicate `q` in addition to the predicate `p`. */ - def withFilter(q: A => Boolean): WithFilter = + def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) } diff --git a/plugins/webeditor/codemirror/mode/clojure/clojure.js b/plugins/webeditor/codemirror/mode/clojure/clojure.js index 658ff140e..775121263 100755 --- a/plugins/webeditor/codemirror/mode/clojure/clojure.js +++ b/plugins/webeditor/codemirror/mode/clojure/clojure.js @@ -14,7 +14,7 @@ CodeMirror.defineMode("clojure", function () { } var atoms = makeKeywords("true false nil"); - + var keywords = makeKeywords( "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); diff --git a/plugins/webeditor/codemirror/mode/coffeescript/LICENSE b/plugins/webeditor/codemirror/mode/coffeescript/LICENSE index 977e284e0..e79cca685 100755 --- a/plugins/webeditor/codemirror/mode/coffeescript/LICENSE +++ b/plugins/webeditor/codemirror/mode/coffeescript/LICENSE @@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/plugins/webeditor/codemirror/mode/coffeescript/coffeescript.js b/plugins/webeditor/codemirror/mode/coffeescript/coffeescript.js index 4d925037c..cf1cf4f24 100755 --- a/plugins/webeditor/codemirror/mode/coffeescript/coffeescript.js +++ b/plugins/webeditor/codemirror/mode/coffeescript/coffeescript.js @@ -158,7 +158,7 @@ CodeMirror.defineMode('coffeescript', function(conf) { if (stream.match(identifiers)) { return 'variable'; } - + if (stream.match(properties)) { return 'property'; } diff --git a/plugins/webeditor/codemirror/mode/coffeescript/index.html b/plugins/webeditor/codemirror/mode/coffeescript/index.html index ee72b8d2f..72eadf6e9 100755 --- a/plugins/webeditor/codemirror/mode/coffeescript/index.html +++ b/plugins/webeditor/codemirror/mode/coffeescript/index.html @@ -16,10 +16,10 @@

      CodeMirror: CoffeeScript mode

      # Copyright (c) 2011 Jeff Pickhardt, released under # the MIT License. # -# Modified from the Python CodeMirror mode, which also is +# Modified from the Python CodeMirror mode, which also is # under the MIT License Copyright (c) 2010 Timothy Farrell. # -# The following script, Underscore.coffee, is used to +# The following script, Underscore.coffee, is used to # demonstrate CoffeeScript mode for CodeMirror. # # To download CoffeeScript mode for CodeMirror, go to: diff --git a/plugins/webeditor/codemirror/mode/diff/index.html b/plugins/webeditor/codemirror/mode/diff/index.html index 556025204..125f388a2 100755 --- a/plugins/webeditor/codemirror/mode/diff/index.html +++ b/plugins/webeditor/codemirror/mode/diff/index.html @@ -38,21 +38,21 @@

      CodeMirror: Diff mode

      +++ b/lib/codemirror.js @@ -399,10 +399,16 @@ var CodeMirror = (function() { } - + function onMouseDown(e) { -- var start = posFromMouse(e), last = start; +- var start = posFromMouse(e), last = start; + var start = posFromMouse(e), last = start, target = e.target(); if (!start) return; setCursor(start.line, start.ch, false); if (e.button() != 1) return; -+ if (target.parentNode == gutter) { ++ if (target.parentNode == gutter) { + if (options.onGutterClick) + options.onGutterClick(indexOf(gutter.childNodes, target) + showingFrom); + return; + } + if (!focused) onFocus(); - + e.stop(); @@ -808,7 +814,7 @@ var CodeMirror = (function() { for (var i = showingFrom; i < showingTo; ++i) { @@ -78,7 +78,7 @@

      CodeMirror: Diff mode

      undo: operation(undo), @@ -1402,7 +1406,8 @@ var CodeMirror = (function() { replaceRange: operation(replaceRange), - + operation: function(f){return operation(f)();}, - refresh: function(){updateDisplay([{from: 0, to: lines.length}]);} + refresh: function(){updateDisplay([{from: 0, to: lines.length}]);}, diff --git a/plugins/webeditor/codemirror/mode/ecl/ecl.js b/plugins/webeditor/codemirror/mode/ecl/ecl.js index 3ee7a681d..b76220a4d 100755 --- a/plugins/webeditor/codemirror/mode/ecl/ecl.js +++ b/plugins/webeditor/codemirror/mode/ecl/ecl.js @@ -75,11 +75,11 @@ CodeMirror.defineMode("ecl", function(config) { } else if (builtin.propertyIsEnumerable(cur)) { if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; return "builtin"; - } else { //Data types are of from KEYWORD## + } else { //Data types are of from KEYWORD## var i = cur.length - 1; while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_')) --i; - + if (i > 0) { var cur2 = cur.substr(0, i + 1); if (variable_3.propertyIsEnumerable(cur2)) { diff --git a/plugins/webeditor/codemirror/mode/ecl/index.html b/plugins/webeditor/codemirror/mode/ecl/index.html index 0ba88c399..e8c629a32 100755 --- a/plugins/webeditor/codemirror/mode/ecl/index.html +++ b/plugins/webeditor/codemirror/mode/ecl/index.html @@ -1,4 +1,4 @@ - + CodeMirror: ECL mode @@ -19,7 +19,7 @@

      CodeMirror: ECL mode

      // this is a singleline comment! import ut; -r := +r := record string22 s1 := '123'; integer4 i1 := 123; diff --git a/plugins/webeditor/codemirror/mode/erlang/erlang.js b/plugins/webeditor/codemirror/mode/erlang/erlang.js index accf24e67..029b8e556 100755 --- a/plugins/webeditor/codemirror/mode/erlang/erlang.js +++ b/plugins/webeditor/codemirror/mode/erlang/erlang.js @@ -247,7 +247,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { return rval(state,stream,"function"); } } - return rval(state,stream,"atom"); + return rval(state,stream,"atom"); } // number diff --git a/plugins/webeditor/codemirror/mode/erlang/index.html b/plugins/webeditor/codemirror/mode/erlang/index.html index fd21521c8..5bb81f669 100755 --- a/plugins/webeditor/codemirror/mode/erlang/index.html +++ b/plugins/webeditor/codemirror/mode/erlang/index.html @@ -32,7 +32,7 @@

      CodeMirror: Erlang mode

      rec_info(demo) -> record_info(fields,demo). demo() -> expand_recs(?MODULE,#demo{a="A",b="BB"}). - + expand_recs(M,List) when is_list(List) -> [expand_recs(M,L)||L<-List]; expand_recs(M,Tup) when is_tuple(Tup) -> diff --git a/plugins/webeditor/codemirror/mode/gfm/index.html b/plugins/webeditor/codemirror/mode/gfm/index.html index 826a96d2d..0b02eb5b7 100755 --- a/plugins/webeditor/codemirror/mode/gfm/index.html +++ b/plugins/webeditor/codemirror/mode/gfm/index.html @@ -9,13 +9,13 @@ - + - + diff --git a/plugins/webeditor/codemirror/mode/haskell/haskell.js b/plugins/webeditor/codemirror/mode/haskell/haskell.js index 71235f4ee..faec08dc3 100755 --- a/plugins/webeditor/codemirror/mode/haskell/haskell.js +++ b/plugins/webeditor/codemirror/mode/haskell/haskell.js @@ -4,7 +4,7 @@ CodeMirror.defineMode("haskell", function() { setState(f); return f(source, setState); } - + // These should all be Unicode extended, as per the Haskell 2010 report var smallRE = /[a-z_]/; var largeRE = /[A-Z]/; @@ -15,12 +15,12 @@ CodeMirror.defineMode("haskell", function() { var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/; var specialRE = /[(),;[\]`{}]/; var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer - + function normal(source, setState) { if (source.eatWhile(whiteCharRE)) { return null; } - + var ch = source.next(); if (specialRE.test(ch)) { if (ch == '{' && source.eat('-')) { @@ -32,7 +32,7 @@ CodeMirror.defineMode("haskell", function() { } return null; } - + if (ch == '\'') { if (source.eat('\\')) { source.next(); // should handle other escapes here @@ -45,11 +45,11 @@ CodeMirror.defineMode("haskell", function() { } return "error"; } - + if (ch == '"') { return switchState(source, setState, stringLiteral); } - + if (largeRE.test(ch)) { source.eatWhile(idRE); if (source.eat('.')) { @@ -57,12 +57,12 @@ CodeMirror.defineMode("haskell", function() { } return "variable-2"; } - + if (smallRE.test(ch)) { source.eatWhile(idRE); return "variable"; } - + if (digitRE.test(ch)) { if (ch == '0') { if (source.eat(/[xX]/)) { @@ -87,7 +87,7 @@ CodeMirror.defineMode("haskell", function() { } return t; } - + if (symbolRE.test(ch)) { if (ch == '-' && source.eat(/-/)) { source.eatWhile(/-/); @@ -101,12 +101,12 @@ CodeMirror.defineMode("haskell", function() { t = "variable-2"; } source.eatWhile(symbolRE); - return t; + return t; } - + return "error"; } - + function ncomment(type, nest) { if (nest == 0) { return normal; @@ -130,7 +130,7 @@ CodeMirror.defineMode("haskell", function() { return type; }; } - + function stringLiteral(source, setState) { while (!source.eol()) { var ch = source.next(); @@ -153,7 +153,7 @@ CodeMirror.defineMode("haskell", function() { setState(normal); return "error"; } - + function stringGap(source, setState) { if (source.eat('\\')) { return switchState(source, setState, stringLiteral); @@ -162,8 +162,8 @@ CodeMirror.defineMode("haskell", function() { setState(normal); return "error"; } - - + + var wellKnownWords = (function() { var wkw = {}; function setType(t) { @@ -172,19 +172,19 @@ CodeMirror.defineMode("haskell", function() { wkw[arguments[i]] = t; }; } - + setType("keyword")( "case", "class", "data", "default", "deriving", "do", "else", "foreign", "if", "import", "in", "infix", "infixl", "infixr", "instance", "let", "module", "newtype", "of", "then", "type", "where", "_"); - + setType("keyword")( "\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>"); - + setType("builtin")( "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**"); - + setType("builtin")( "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT", @@ -192,7 +192,7 @@ CodeMirror.defineMode("haskell", function() { "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read", "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS", "String", "True"); - + setType("builtin")( "abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf", "asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling", @@ -220,16 +220,16 @@ CodeMirror.defineMode("haskell", function() { "toRational", "truncate", "uncurry", "undefined", "unlines", "until", "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip", "zip3", "zipWith", "zipWith3"); - + return wkw; })(); - - - + + + return { startState: function () { return { f: normal }; }, copyState: function (s) { return { f: s.f }; }, - + token: function(stream, state) { var t = state.f(stream, function(s) { state.f = s; }); var w = stream.current(); diff --git a/plugins/webeditor/codemirror/mode/haskell/index.html b/plugins/webeditor/codemirror/mode/haskell/index.html index 56307b8a9..78c027832 100755 --- a/plugins/webeditor/codemirror/mode/haskell/index.html +++ b/plugins/webeditor/codemirror/mode/haskell/index.html @@ -44,7 +44,7 @@

      CodeMirror: Haskell mode

      oneOfEach (an@(a,n):bs) = let bs' = if n == 1 then bs else (a,n-1):bs in (a,bs') : mapSnd (an:) (oneOfEach bs) - + apSnd f (a,b) = (a, f b) mapSnd = map . apSnd diff --git a/plugins/webeditor/codemirror/mode/haxe/haxe.js b/plugins/webeditor/codemirror/mode/haxe/haxe.js index 786fe92d0..28f9b00ec 100755 --- a/plugins/webeditor/codemirror/mode/haxe/haxe.js +++ b/plugins/webeditor/codemirror/mode/haxe/haxe.js @@ -1,6 +1,6 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { var indentUnit = config.indentUnit; - + // Tokenizer var keywords = function(){ @@ -12,10 +12,10 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { "if": A, "while": A, "else": B, "do": B, "try": B, "return": C, "break": C, "continue": C, "new": C, "throw": C, "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"), - "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"), + "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"), "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), - "in": operator, "never": kw("property_access"), "trace":kw("trace"), + "in": operator, "never": kw("property_access"), "trace":kw("trace"), "class": type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type, "true": atom, "false": atom, "null": atom }; @@ -55,14 +55,14 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); - } + } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); } else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) { nextUntilUnescaped(stream, "/"); - stream.eatWhile(/[gimsu]/); + stream.eatWhile(/[gimsu]/); return ret("regexp", "string-2"); } else if (ch == "/") { @@ -146,13 +146,13 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; } - + function parseHaxe(state, style, type, content, stream) { var cc = state.cc; // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - + if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; @@ -168,7 +168,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { } } } - + function imported(state, typename) { if (/[a-z]/.test(typename.charAt(0))) @@ -177,8 +177,8 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { for (var i = 0; iCodeMirror: Haxe mode { noFoo = 12; } - + public static inline function doFoo(obj:{k:Int, l:Float}):Int { for(i in 0...10) @@ -64,7 +64,7 @@

      CodeMirror: Haxe mode

      function getFoo():Int { return noFoo; } - + public var three:Int; } enum Color diff --git a/plugins/webeditor/codemirror/mode/htmlembedded/htmlembedded.js b/plugins/webeditor/codemirror/mode/htmlembedded/htmlembedded.js index e183d6746..ff6dfd2f9 100755 --- a/plugins/webeditor/codemirror/mode/htmlembedded/htmlembedded.js +++ b/plugins/webeditor/codemirror/mode/htmlembedded/htmlembedded.js @@ -1,12 +1,12 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { - + //config settings var scriptStartRegex = parserConfig.scriptStartRegex || /^<%/i, scriptEndRegex = parserConfig.scriptEndRegex || /^%>/i; - + //inner modes var scriptingMode, htmlMixedMode; - + //tokenizer when in html mode function htmlDispatch(stream, state) { if (stream.match(scriptStartRegex, false)) { @@ -32,7 +32,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { startState: function() { scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec); htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed"); - return { + return { token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, htmlState : CodeMirror.startState(htmlMixedMode), scriptState : CodeMirror.startState(scriptingMode) @@ -49,7 +49,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { else if (scriptingMode.indent) return scriptingMode.indent(state.scriptState, textAfter); }, - + copyState: function(state) { return { token : state.token, @@ -57,7 +57,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { scriptState : CodeMirror.copyState(scriptingMode, state.scriptState) }; }, - + electricChars: "/{}:", innerMode: function(state) { diff --git a/plugins/webeditor/codemirror/mode/htmlembedded/index.html b/plugins/webeditor/codemirror/mode/htmlembedded/index.html index 5a37dd637..d3f02d6cd 100755 --- a/plugins/webeditor/codemirror/mode/htmlembedded/index.html +++ b/plugins/webeditor/codemirror/mode/htmlembedded/index.html @@ -43,7 +43,7 @@

      CodeMirror: Html Embedded Scripts mode

      Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on JavaScript, CSS and XML.
      Other dependancies include those of the scriping language chosen.

      -

      MIME types defined: application/x-aspx (ASP.NET), +

      MIME types defined: application/x-aspx (ASP.NET), application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)

      diff --git a/plugins/webeditor/codemirror/mode/javascript/javascript.js b/plugins/webeditor/codemirror/mode/javascript/javascript.js index ff6bb5d80..c65021907 100755 --- a/plugins/webeditor/codemirror/mode/javascript/javascript.js +++ b/plugins/webeditor/codemirror/mode/javascript/javascript.js @@ -11,7 +11,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, @@ -87,7 +87,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); - } + } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); @@ -170,7 +170,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - + if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; @@ -282,7 +282,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expression); } - + function maybeoperator(type, value) { if (type == "operator") { if (/\+\+|--/.test(value)) return cont(maybeoperator); diff --git a/plugins/webeditor/codemirror/mode/javascript/typescript.html b/plugins/webeditor/codemirror/mode/javascript/typescript.html index 58315e7ac..ed4a5d58b 100755 --- a/plugins/webeditor/codemirror/mode/javascript/typescript.html +++ b/plugins/webeditor/codemirror/mode/javascript/typescript.html @@ -21,7 +21,7 @@

      CodeMirror: TypeScript mode

      greet() { return "Hello, " + this.greeting; } -} +} var greeter = new Greeter("world"); diff --git a/plugins/webeditor/codemirror/mode/jinja2/jinja2.js b/plugins/webeditor/codemirror/mode/jinja2/jinja2.js index 1472d398f..16b06c48e 100755 --- a/plugins/webeditor/codemirror/mode/jinja2/jinja2.js +++ b/plugins/webeditor/codemirror/mode/jinja2/jinja2.js @@ -1,5 +1,5 @@ CodeMirror.defineMode("jinja2", function() { - var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", + var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", "loop", "none", "self", "super", "if", "as", "not", "and", "else", "import", "with", "without", "context"]; keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); @@ -38,5 +38,5 @@ CodeMirror.defineMode("jinja2", function() { token: function (stream, state) { return state.tokenize(stream, state); } - }; + }; }); diff --git a/plugins/webeditor/codemirror/mode/less/index.html b/plugins/webeditor/codemirror/mode/less/index.html index 78c1e5307..bfaa78048 100755 --- a/plugins/webeditor/codemirror/mode/less/index.html +++ b/plugins/webeditor/codemirror/mode/less/index.html @@ -347,7 +347,7 @@

      CodeMirror: LESS mode

      border: 2px solid transparent; border-width: 0 2px; &:hover { - .dark-red; + .dark-red; text-decoration: none; } } @@ -400,7 +400,7 @@

      CodeMirror: LESS mode

      border-top-width: 0; background-color: darken(@medium, 10%); ul { - padding: 0px; + padding: 0px; } li { font-size: 14px; @@ -410,9 +410,9 @@

      CodeMirror: LESS mode

      border: 0; a { display: block; - padding: 0px 15px; + padding: 0px 15px; text-decoration: none; - color: white; + color: white; &:hover { background-color: darken(@medium, 15%); text-decoration: none; @@ -450,13 +450,13 @@

      CodeMirror: LESS mode

      #download img { display: inline-block} #download-info { code { - font-size: 13px; + font-size: 13px; } color: @blue + #333; display: inline; float: left; margin: 36px 0 0 15px } } h2 { span { - color: @medium; + color: @medium; } color: @blue; margin: 20px 0; @@ -526,7 +526,7 @@

      CodeMirror: LESS mode

      line-height: 26px; padding: 0 60px; code { - font-size: 16px; + font-size: 16px; } pre { border-width: 1px; @@ -599,18 +599,18 @@

      CodeMirror: LESS mode

      } td { - padding-right: 30px; + padding-right: 30px; } #synopsis { .box-shadow(0, 5px, 5px, 0.2); } #synopsis, #about { h2 { - font-size: 30px; + font-size: 30px; padding: 10px 0; } h1 + h2 { - margin-top: 15px; + margin-top: 15px; } h3 { font-size: 22px } @@ -631,7 +631,7 @@

      CodeMirror: LESS mode

      } .css-output { td { - border-left: 0; + border-left: 0; } } .less-example { diff --git a/plugins/webeditor/codemirror/mode/less/less.js b/plugins/webeditor/codemirror/mode/less/less.js index 70cd5c937..540290c69 100755 --- a/plugins/webeditor/codemirror/mode/less/less.js +++ b/plugins/webeditor/codemirror/mode/less/less.js @@ -9,16 +9,16 @@ CodeMirror.defineMode("less", function(config) { function ret(style, tp) {type = tp; return style;} //html tags var tags = "a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption cite code col colgroup command datalist dd del details dfn dir div dl dt em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins keygen kbd label legend li link map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr".split(' '); - + function inTagsArray(val){ for(var i=0; i"){ return ret("tag", "tag"); - }else if( /\(/.test(stream.peek()) ){ + }else if( /\(/.test(stream.peek()) ){ return ret(null, ch); }else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png) return ret("string", "string"); }else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign //commment out these 2 comment if you want the minus sign to be parsed as null -500px //stream.backUp(stream.current().length-1); - //return ret(null, ch); //console.log( stream.current() ); + //return ret(null, ch); //console.log( stream.current() ); return ret("number", "unit"); }else if( inTagsArray(stream.current().toLowerCase()) ){ // match html tags return ret("tag", "tag"); @@ -165,21 +165,21 @@ CodeMirror.defineMode("less", function(config) { return ret("tag", "tag"); } else stream.backUp(new_pos-(old_pos-1)); }else{ - stream.backUp(1); + stream.backUp(1); } if(t_v)return ret("tag", "tag"); else return ret("variable", "variable"); - }else{ - return ret("variable", "variable"); + }else{ + return ret("variable", "variable"); } - } + } } - + function tokenSComment(stream, state) { // SComment = Slash comment stream.skipToEnd(); state.tokenize = tokenBase; return ret("comment", "comment"); } - + function tokenCComment(stream, state) { var maybeEnd = false, ch; while ((ch = stream.next()) != null) { @@ -191,7 +191,7 @@ CodeMirror.defineMode("less", function(config) { } return ret("comment", "comment"); } - + function tokenSGMLComment(stream, state) { var dashes = 0, ch; while ((ch = stream.next()) != null) { @@ -203,7 +203,7 @@ CodeMirror.defineMode("less", function(config) { } return ret("comment", "comment"); } - + function tokenString(quote) { return function(stream, state) { var escaped = false, ch; @@ -216,18 +216,18 @@ CodeMirror.defineMode("less", function(config) { return ret("string", "string"); }; } - + return { - startState: function(base) { + startState: function(base) { return {tokenize: tokenBase, baseIndent: base || 0, stack: []}; }, - + token: function(stream, state) { if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); - + var context = state.stack[state.stack.length-1]; if (type == "hash" && context == "rule") style = "atom"; else if (style == "variable") { @@ -237,7 +237,7 @@ CodeMirror.defineMode("less", function(config) { /[\s,|\s\)|\s]/.test(stream.peek()) ? "tag" : type; } } - + if (context == "rule" && /^[\{\};]$/.test(type)) state.stack.pop(); if (type == "{") { @@ -249,18 +249,18 @@ CodeMirror.defineMode("less", function(config) { else if (context == "{" && type != "comment") state.stack.push("rule"); return style; }, - + indent: function(state, textAfter) { var n = state.stack.length; if (/^\}/.test(textAfter)) n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; return state.baseIndent + n * indentUnit; }, - + electricChars: "}" }; }); CodeMirror.defineMIME("text/x-less", "less"); if (!CodeMirror.mimeModes.hasOwnProperty("text/css")) - CodeMirror.defineMIME("text/css", "less"); \ No newline at end of file + CodeMirror.defineMIME("text/css", "less"); diff --git a/plugins/webeditor/codemirror/mode/lua/index.html b/plugins/webeditor/codemirror/mode/lua/index.html index a0a42d91c..c5891ab31 100755 --- a/plugins/webeditor/codemirror/mode/lua/index.html +++ b/plugins/webeditor/codemirror/mode/lua/index.html @@ -23,7 +23,7 @@

      CodeMirror: Lua mode

      local table = { "asd" = 123, - "x" = 0.34, + "x" = 0.34, } if x ~= 3 then print( x ) @@ -34,7 +34,7 @@

      CodeMirror: Lua mode

      end --single line comment - + end function blablabla3() diff --git a/plugins/webeditor/codemirror/mode/lua/lua.js b/plugins/webeditor/codemirror/mode/lua/lua.js index 97fb2c6f9..6c9c59d2d 100755 --- a/plugins/webeditor/codemirror/mode/lua/lua.js +++ b/plugins/webeditor/codemirror/mode/lua/lua.js @@ -1,7 +1,7 @@ // LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's // CodeMirror 1 mode. // highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting - + CodeMirror.defineMode("lua", function(config, parserConfig) { var indentUnit = config.indentUnit; @@ -12,7 +12,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return new RegExp("^(?:" + words.join("|") + ")$", "i"); } var specials = wordRE(parserConfig.specials || []); - + // long list of standard functions from lua manual var builtins = wordRE([ "_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load", @@ -47,7 +47,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { "table.concat","table.insert","table.maxn","table.remove","table.sort" ]); var keywords = wordRE(["and","break","elseif","false","nil","not","or","return", - "true","function", "end", "if", "then", "else", "do", + "true","function", "end", "if", "then", "else", "do", "while", "repeat", "until", "for", "in", "local" ]); var indentTokens = wordRE(["function", "if","repeat","do", "\\(", "{"]); @@ -68,7 +68,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state); stream.skipToEnd(); return "comment"; - } + } if (ch == "\"" || ch == "'") return (state.cur = string(ch))(stream, state); if (ch == "[" && /[\[=]/.test(stream.peek())) @@ -108,7 +108,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return "string"; }; } - + return { startState: function(basecol) { return {basecol: basecol || 0, indentDepth: 0, cur: normal}; diff --git a/plugins/webeditor/codemirror/mode/markdown/index.html b/plugins/webeditor/codemirror/mode/markdown/index.html index 6f97b10e7..2ef7605b3 100755 --- a/plugins/webeditor/codemirror/mode/markdown/index.html +++ b/plugins/webeditor/codemirror/mode/markdown/index.html @@ -71,7 +71,7 @@

      CodeMirror: Markdown mode

      A First Level Header ==================== - + A Second Level Header --------------------- @@ -81,11 +81,11 @@

      CodeMirror: Markdown mode

      The quick brown fox jumped over the lazy dog's back. - + ### Header 3 > This is a blockquote. - > + > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote @@ -94,23 +94,23 @@

      CodeMirror: Markdown mode

      Output: <h1>A First Level Header</h1> - + <h2>A Second Level Header</h2> - + <p>Now is the time for all good men to come to the aid of their country. This is just a regular paragraph.</p> - + <p>The quick brown fox jumped over the lazy dog's back.</p> - + <h3>Header 3</h3> - + <blockquote> <p>This is a blockquote.</p> - + <p>This is the second paragraph in the blockquote.</p> - + <h2>This is an H2 in a blockquote</h2> </blockquote> @@ -124,7 +124,7 @@

      CodeMirror: Markdown mode

      Some of these words *are emphasized*. Some of these words _are emphasized also_. - + Use two asterisks for **strong emphasis**. Or, if you prefer, __use two underscores instead__. @@ -132,10 +132,10 @@

      CodeMirror: Markdown mode

      <p>Some of these words <em>are emphasized</em>. Some of these words <em>are emphasized also</em>.</p> - + <p>Use two asterisks for <strong>strong emphasis</strong>. Or, if you prefer, <strong>use two underscores instead</strong>.</p> - + ## Lists ## @@ -188,7 +188,7 @@

      CodeMirror: Markdown mode

      the paragraphs by 4 spaces or 1 tab: * A list item. - + With multiple paragraphs. * Another item in the list. @@ -200,7 +200,7 @@

      CodeMirror: Markdown mode

      <p>With multiple paragraphs.</p></li> <li><p>Another item in the list.</p></li> </ul> - + ### Links ### @@ -295,7 +295,7 @@

      CodeMirror: Markdown mode

      <p>I strongly recommend against using any <code>&lt;blink&gt;</code> tags.</p> - + <p>I wish SmartyPants used named entities like <code>&amp;mdash;</code> instead of decimal-encoded entites like <code>&amp;#8212;</code>.</p> @@ -318,7 +318,7 @@

      CodeMirror: Markdown mode

      <p>If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:</p> - + <pre><code>&lt;blockquote&gt; &lt;p&gt;For example.&lt;/p&gt; &lt;/blockquote&gt; diff --git a/plugins/webeditor/codemirror/mode/markdown/markdown.js b/plugins/webeditor/codemirror/mode/markdown/markdown.js index be4308228..c0be49460 100755 --- a/plugins/webeditor/codemirror/mode/markdown/markdown.js +++ b/plugins/webeditor/codemirror/mode/markdown/markdown.js @@ -36,22 +36,22 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (aliases[a] in modes || aliases[a] in mimes) modes[a] = aliases[a]; } - + return function (lang) { return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null; }; }()); - + // Should underscores in words open/close em/strong? if (modeCfg.underscoresBreakWords === undefined) modeCfg.underscoresBreakWords = true; - + // Turn on fenced code blocks? ("```" to start/end) if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false; - + // Turn on task lists? ("- [ ] " and "- [x] ") if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; - + var codeDepth = 0; var header = 'header' @@ -109,7 +109,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function blockNormal(stream, state) { - + var prevLineIsList = (state.list !== false); if (state.list !== false && state.indentationDiff >= 0) { // Continued list if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block @@ -123,7 +123,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.list = false; state.listDepth = 0; } - + if (state.indentationDiff >= 4) { state.indentation -= 4; stream.skipToEnd(); @@ -158,7 +158,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { switchBlock(stream, state, local); return code; } - + return switchInline(stream, state, state.inline); } @@ -193,17 +193,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Inline function getType(state) { var styles = []; - + if (state.taskOpen) { return "meta"; } if (state.taskClosed) { return "property"; } - + if (state.strong) { styles.push(strong); } if (state.em) { styles.push(em); } - + if (state.linkText) { styles.push(linktext); } - + if (state.code) { styles.push(code); } - + if (state.header) { styles.push(header); } if (state.quote) { styles.push(state.quote % 2 ? quote1 : quote2); } if (state.list !== false) { @@ -224,19 +224,19 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (stream.match(textRE, true)) { return getType(state); } - return undefined; + return undefined; } function inlineNormal(stream, state) { var style = state.text(stream, state); if (typeof style !== 'undefined') return style; - + if (state.list) { // List marker (*, +, -, 1., etc) state.list = null; return getType(state); } - + if (state.taskList) { var taskOpen = stream.match(taskListRE, true)[1] !== "x"; if (taskOpen) state.taskOpen = true; @@ -244,17 +244,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.taskList = false; return getType(state); } - + state.taskOpen = false; state.taskClosed = false; - + var ch = stream.next(); - + if (ch === '\\') { stream.next(); return getType(state); } - + // Matches link titles present on next line if (state.linkTitle) { state.linkTitle = false; @@ -268,7 +268,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return linkhref; } } - + // If this block is changed, it may need to be updated in GFM mode if (ch === '`') { var t = getType(state); @@ -289,7 +289,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } else if (state.code) { return getType(state); } - + if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) { stream.match(/\[[^\]]*\]/); state.inline = state.f = linkHref; @@ -307,15 +307,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.inline = state.f = linkHref; return type; } - + if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) { return switchInline(stream, state, inlineElement(linkinline, '>')); } - + if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) { return switchInline(stream, state, inlineElement(linkemail, '>')); } - + if (ch === '<' && stream.match(/^\w/, false)) { if (stream.string.indexOf(">")!=-1) { var atts = stream.string.substring(1,stream.string.indexOf(">")); @@ -326,12 +326,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { stream.backUp(1); return switchBlock(stream, state, htmlBlock); } - + if (ch === '<' && stream.match(/^\/\w*?>/)) { state.md_inside = false; return "tag"; } - + var ignoreUnderscore = false; if (!modeCfg.underscoresBreakWords) { if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) { @@ -368,7 +368,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } } - + return getType(state); } @@ -414,7 +414,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (!savedInlineRE[endChar]) { // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741) endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); - // Match any non-endChar, escaped character, as well as the closing + // Match any non-endChar, escaped character, as well as the closing // endChar. savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')'); } @@ -434,17 +434,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { startState: function() { return { f: blockNormal, - + prevLineHasContent: false, thisLineHasContent: false, - + block: blockNormal, htmlState: CodeMirror.startState(htmlMode), indentation: 0, - + inline: inlineNormal, text: handleText, - + linkText: false, linkTitle: false, em: false, @@ -460,17 +460,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { copyState: function(s) { return { f: s.f, - + prevLineHasContent: s.prevLineHasContent, thisLineHasContent: s.thisLineHasContent, - + block: s.block, htmlState: CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, - + localMode: s.localMode, localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, - + inline: s.inline, text: s.text, linkTitle: s.linkTitle, @@ -497,10 +497,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Reset state.header state.header = false; - + // Reset state.taskList state.taskList = false; - + // Reset state.code state.code = false; diff --git a/plugins/webeditor/codemirror/mode/markdown/test.js b/plugins/webeditor/codemirror/mode/markdown/test.js index cfc229bc3..c451412fe 100755 --- a/plugins/webeditor/codemirror/mode/markdown/test.js +++ b/plugins/webeditor/codemirror/mode/markdown/test.js @@ -39,20 +39,20 @@ "[comment `]"); // Unclosed backticks - // Instead of simply marking as CODE, it would be nice to have an + // Instead of simply marking as CODE, it would be nice to have an // incomplete flag for CODE, that is styled slightly different. MT("unclosedBackticks", "foo [comment `bar]"); - // Per documentation: "To include a literal backtick character within a - // code span, you can use multiple backticks as the opening and closing + // Per documentation: "To include a literal backtick character within a + // code span, you can use multiple backticks as the opening and closing // delimiters" MT("doubleBackticks", "[comment ``foo ` bar``]"); // Tests based on Dingus // http://daringfireball.net/projects/markdown/dingus - // + // // Multiple backticks within an inline code block MT("consecutiveBackticks", "[comment `foo```bar`]"); @@ -98,10 +98,10 @@ // Setext headers - H1, H2 // Per documentation, "Any number of underlining =’s or -’s will work." // http://daringfireball.net/projects/markdown/syntax#header - // Ideally, the text would be marked as `header` as well, but this is - // not really feasible at the moment. So, instead, we're testing against + // Ideally, the text would be marked as `header` as well, but this is + // not really feasible at the moment. So, instead, we're testing against // what works today, to avoid any regressions. - // + // // Check if single underlining = works MT("setextH1", "foo", @@ -368,7 +368,7 @@ " [comment world]", " [comment foo]", " [variable-2 bar]"); - + // List nesting edge cases MT("listNested", "[variable-2 * foo]", @@ -548,7 +548,7 @@ MT("emInWordUnderscore", "foo[em _bar_]hello"); - // Per documentation: "...surround an * or _ with spaces, it’ll be + // Per documentation: "...surround an * or _ with spaces, it’ll be // treated as a literal asterisk or underscore." MT("emEscapedBySpaceIn", @@ -558,7 +558,7 @@ "foo _ bar[em _hello_]world"); // Unclosed emphasis characters - // Instead of simply marking as EM / STRONG, it would be nice to have an + // Instead of simply marking as EM / STRONG, it would be nice to have an // incomplete flag for EM and STRONG, that is styled slightly different. MT("emIncompleteAsterisk", "foo [em *bar]"); @@ -625,7 +625,7 @@ // Tests to make sure GFM-specific things aren't getting through - + MT("taskList", "[variable-2 * [ ]] bar]"); diff --git a/plugins/webeditor/codemirror/mode/ntriples/index.html b/plugins/webeditor/codemirror/mode/ntriples/index.html index 052a53d86..8d7b39469 100755 --- a/plugins/webeditor/codemirror/mode/ntriples/index.html +++ b/plugins/webeditor/codemirror/mode/ntriples/index.html @@ -11,12 +11,12 @@ .CodeMirror { border: 1px solid #eee; } - +

      CodeMirror: NTriples mode

      - diff --git a/plugins/webeditor/codemirror/mode/tiki/tiki.css b/plugins/webeditor/codemirror/mode/tiki/tiki.css index 0dbc3ea0e..6e970fb23 100755 --- a/plugins/webeditor/codemirror/mode/tiki/tiki.css +++ b/plugins/webeditor/codemirror/mode/tiki/tiki.css @@ -23,4 +23,4 @@ .cm-tw-underline { text-decoration: underline; -} \ No newline at end of file +} diff --git a/plugins/webeditor/codemirror/mode/tiki/tiki.js b/plugins/webeditor/codemirror/mode/tiki/tiki.js index 81e87ab21..bd9514017 100755 --- a/plugins/webeditor/codemirror/mode/tiki/tiki.js +++ b/plugins/webeditor/codemirror/mode/tiki/tiki.js @@ -8,13 +8,13 @@ CodeMirror.defineMode('tiki', function(config) { } stream.next(); } - + if (returnTokenizer) state.tokenize = returnTokenizer; - + return style; }; } - + function inLine(style) { return function(stream, state) { while(!stream.eol()) { @@ -24,16 +24,16 @@ CodeMirror.defineMode('tiki', function(config) { return style; }; } - + function inText(stream, state) { function chain(parser) { state.tokenize = parser; return parser(stream, state); } - + var sol = stream.sol(); var ch = stream.next(); - + //non start of line switch (ch) { //switch is generally much faster than if, so it is used here case "{": //plugin @@ -69,7 +69,7 @@ CodeMirror.defineMode('tiki', function(config) { return chain(inBlock("comment", "||")); } break; - case "-": + case "-": if (stream.eat("=")) {//titleBar return chain(inBlock("header string", "=-", inText)); } else if (stream.eat("-")) {//deleted @@ -95,7 +95,7 @@ CodeMirror.defineMode('tiki', function(config) { } break; } - + //start of line types if (sol) { switch (ch) { @@ -119,11 +119,11 @@ CodeMirror.defineMode('tiki', function(config) { break; } } - + //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki return null; } - + var indentUnit = config.indentUnit; // Return variables for tokenizers @@ -131,7 +131,7 @@ CodeMirror.defineMode('tiki', function(config) { function inPlugin(stream, state) { var ch = stream.next(); var peek = stream.peek(); - + if (ch == "}") { state.tokenize = inText; //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin @@ -140,18 +140,18 @@ CodeMirror.defineMode('tiki', function(config) { return "bracket"; } else if (ch == "=") { type = "equals"; - + if (peek == ">") { ch = stream.next(); peek = stream.peek(); } - + //here we detect values directly after equal character with no quotes if (!/[\'\"]/.test(peek)) { state.tokenize = inAttributeNoQuote(); } //end detect values - + return "operator"; } else if (/[\'\"]/.test(ch)) { state.tokenize = inAttribute(ch); @@ -173,7 +173,7 @@ CodeMirror.defineMode('tiki', function(config) { return "string"; }; } - + function inAttributeNoQuote() { return function(stream, state) { while (!stream.eol()) { @@ -192,7 +192,7 @@ CodeMirror.defineMode('tiki', function(config) { function pass() { for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); } - + function cont() { pass.apply(null, arguments); return true; @@ -208,7 +208,7 @@ CodeMirror.defineMode('tiki', function(config) { noIndent: noIndent }; } - + function popContext() { if (curState.context) curState.context = curState.context.prev; } @@ -233,7 +233,7 @@ CodeMirror.defineMode('tiki', function(config) { } else return cont(); } - + function endplugin(startOfLine) { return function(type) { if ( @@ -245,7 +245,7 @@ CodeMirror.defineMode('tiki', function(config) { return cont(); }; } - + function endcloseplugin(err) { return function(type) { if (err) setStyle = "error"; diff --git a/plugins/webeditor/codemirror/mode/turtle/index.html b/plugins/webeditor/codemirror/mode/turtle/index.html index 5e56e5755..1732331c3 100755 --- a/plugins/webeditor/codemirror/mode/turtle/index.html +++ b/plugins/webeditor/codemirror/mode/turtle/index.html @@ -16,7 +16,7 @@

      CodeMirror: Turtle mode

      @prefix geo: . @prefix rdf: . - + a foaf:Person; foaf:interest ; foaf:based_near [ diff --git a/plugins/webeditor/codemirror/mode/turtle/turtle.js b/plugins/webeditor/codemirror/mode/turtle/turtle.js index 5c7c28eb0..bd2ad2ecb 100755 --- a/plugins/webeditor/codemirror/mode/turtle/turtle.js +++ b/plugins/webeditor/codemirror/mode/turtle/turtle.js @@ -40,11 +40,11 @@ CodeMirror.defineMode("turtle", function(config) { return "variable-3"; } else { var word = stream.current(); - + if(keywords.test(word)) { return "meta"; } - + if(ch >= "A" && ch <= "Z") { return "comment"; } else { @@ -119,7 +119,7 @@ CodeMirror.defineMode("turtle", function(config) { state.context.col = stream.column(); } } - + return style; }, diff --git a/plugins/webeditor/codemirror/mode/vb/index.html b/plugins/webeditor/codemirror/mode/vb/index.html index 74dd5e816..2fc1a9d5e 100755 --- a/plugins/webeditor/codemirror/mode/vb/index.html +++ b/plugins/webeditor/codemirror/mode/vb/index.html @@ -11,7 +11,7 @@ .CodeMirror {border: 1px solid #aaa; height:210px; height: auto;} .CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;} .CodeMirror pre { font-family: Inconsolata; font-size: 14px} - + @@ -29,7 +29,7 @@

      CodeMirror: VB.NET mode

      } i++; } - CodeMirror.runMode(text, "text/x-vb",callback); + CodeMirror.runMode(text, "text/x-vb",callback); if (ok) return "Tests OK"; } @@ -74,7 +74,7 @@

      CodeMirror: VB.NET mode

      function runTest() { document.getElementById('testresult').innerHTML = testAll(); initText(editor); - + } diff --git a/plugins/webeditor/codemirror/mode/vb/vb.js b/plugins/webeditor/codemirror/mode/vb/vb.js index 764c07101..27b227195 100755 --- a/plugins/webeditor/codemirror/mode/vb/vb.js +++ b/plugins/webeditor/codemirror/mode/vb/vb.js @@ -1,10 +1,10 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var ERRORCLASS = 'error'; - + function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); } - + var singleOperators = new RegExp("^[\\+\\-\\*/%&\\\\|\\^~<>!]"); var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); var doubleOperators = new RegExp("^((==)|(<>)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); @@ -15,9 +15,9 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try']; var middleKeywords = ['else','elseif','case', 'catch']; var endKeywords = ['next','loop']; - + var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'in']); - var commonkeywords = ['as', 'dim', 'break', 'continue','optional', 'then', 'until', + var commonkeywords = ['as', 'dim', 'break', 'continue','optional', 'then', 'until', 'goto', 'byval','byref','new','handles','property', 'return', 'const','private', 'protected', 'friend', 'public', 'shared', 'static', 'true','false']; var commontypes = ['integer','string','double','decimal','boolean','short','char', 'float','single']; @@ -34,13 +34,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var indentInfo = null; - + function indent(_stream, state) { state.currentIndent++; } - + function dedent(_stream, state) { state.currentIndent--; } @@ -49,16 +49,16 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { if (stream.eatSpace()) { return null; } - + var ch = stream.peek(); - + // Handle Comments if (ch === "'") { stream.skipToEnd(); return 'comment'; } - - + + // Handle Number Literals if (stream.match(/^((&H)|(&O))?[0-9\.a-f]/i, false)) { var floatLiteral = false; @@ -66,7 +66,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { if (stream.match(/^\d*\.\d+F?/i)) { floatLiteral = true; } else if (stream.match(/^\d+\.\d*F?/)) { floatLiteral = true; } else if (stream.match(/^\.\d+F?/)) { floatLiteral = true; } - + if (floatLiteral) { // Float literals may be "imaginary" stream.eat(/J/i); @@ -93,13 +93,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return 'number'; } } - + // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } - + // Handle operators and Delimiters if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { return null; @@ -137,28 +137,28 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { dedent(stream,state); return 'keyword'; } - + if (stream.match(types)) { return 'keyword'; } - + if (stream.match(keywords)) { return 'keyword'; } - + if (stream.match(identifiers)) { return 'variable'; } - + // Handle non-detected items stream.next(); return ERRORCLASS; } - + function tokenStringFactory(delimiter) { var singleline = delimiter.length == 1; var OUTCLASS = 'string'; - + return function(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"]/); @@ -179,7 +179,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return OUTCLASS; }; } - + function tokenLexer(stream, state) { var style = state.tokenize(stream, state); @@ -195,8 +195,8 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return ERRORCLASS; } } - - + + var delimiter_index = '[({'.indexOf(current); if (delimiter_index !== -1) { indent(stream, state ); @@ -212,7 +212,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return ERRORCLASS; } } - + return style; } @@ -229,7 +229,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { }; }, - + token: function(stream, state) { if (stream.sol()) { state.currentIndent += state.nextLineIndent; @@ -237,24 +237,23 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { state.doInCurrentLine = 0; } var style = tokenLexer(stream, state); - + state.lastToken = {style:style, content: stream.current()}; - - - + + + return style; }, - + indent: function(state, textAfter) { var trueText = textAfter.replace(/^\s+|\s+$/g, '') ; if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1); if(state.currentIndent < 0) return 0; return state.currentIndent * conf.indentUnit; } - + }; return external; }); CodeMirror.defineMIME("text/x-vb", "vb"); - diff --git a/plugins/webeditor/codemirror/mode/vbscript/index.html b/plugins/webeditor/codemirror/mode/vbscript/index.html index 8c86f9ef9..58b65701a 100755 --- a/plugins/webeditor/codemirror/mode/vbscript/index.html +++ b/plugins/webeditor/codemirror/mode/vbscript/index.html @@ -39,4 +39,3 @@

      CodeMirror: VBScript mode

      MIME types defined: text/vbscript.

      - diff --git a/plugins/webeditor/codemirror/mode/verilog/index.html b/plugins/webeditor/codemirror/mode/verilog/index.html index f7c88c623..db514bafe 100755 --- a/plugins/webeditor/codemirror/mode/verilog/index.html +++ b/plugins/webeditor/codemirror/mode/verilog/index.html @@ -72,7 +72,7 @@

      CodeMirror: Verilog mode

      assign ya_im = xa_im_z + xbw_im; assign yb_re = xa_re_z - xbw_re; assign yb_im = xa_im_z - xbw_im; - + // Create the multiply module. multiply_complex #(WIDTH) multiply_complex_0 (.clk(clk), @@ -101,7 +101,7 @@

      CodeMirror: Verilog mode

      end end end - + endmodule diff --git a/plugins/webeditor/codemirror/mode/xml/xml.js b/plugins/webeditor/codemirror/mode/xml/xml.js index a68b03384..440343bbe 100755 --- a/plugins/webeditor/codemirror/mode/xml/xml.js +++ b/plugins/webeditor/codemirror/mode/xml/xml.js @@ -86,7 +86,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var ok; if (stream.eat("#")) { if (stream.eat("x")) { - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); } else { ok = stream.eatWhile(/[\d]/) && stream.eat(";"); } diff --git a/plugins/webeditor/codemirror/mode/xquery/LICENSE b/plugins/webeditor/codemirror/mode/xquery/LICENSE index 2a2d47be5..b3efbf40a 100755 --- a/plugins/webeditor/codemirror/mode/xquery/LICENSE +++ b/plugins/webeditor/codemirror/mode/xquery/LICENSE @@ -17,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/plugins/webeditor/codemirror/mode/xquery/index.html b/plugins/webeditor/codemirror/mode/xquery/index.html index 27acb8978..84a85da9e 100755 --- a/plugins/webeditor/codemirror/mode/xquery/index.html +++ b/plugins/webeditor/codemirror/mode/xquery/index.html @@ -1,5 +1,5 @@ - - + + - + - CodeMirror: XQuery mode - - - - - + CodeMirror: XQuery mode + + + + + - - -

      CodeMirror: XQuery mode

      - -
      - -
      - - - -

      MIME types defined: application/xquery.

      - -

      Development of the CodeMirror XQuery mode was sponsored by - MarkLogic and developed by + + +

      MIME types defined: application/xquery.

      + +

      Development of the CodeMirror XQuery mode was sponsored by + MarkLogic and developed by Mike Brevoort.

      - - + + diff --git a/plugins/webeditor/codemirror/mode/xquery/xquery.js b/plugins/webeditor/codemirror/mode/xquery/xquery.js index d5c859e00..95decc11b 100755 --- a/plugins/webeditor/codemirror/mode/xquery/xquery.js +++ b/plugins/webeditor/codemirror/mode/xquery/xquery.js @@ -35,18 +35,18 @@ CodeMirror.defineMode("xquery", function() { , atom = {type: "atom", style: "atom"} , punctuation = {type: "punctuation", style: null} , qualifier = {type: "axis_specifier", style: "qualifier"}; - + // kwObj is what is return from this function at the end var kwObj = { 'if': A, 'switch': A, 'while': A, 'for': A, 'else': B, 'then': B, 'try': B, 'finally': B, 'catch': B, - 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C, - 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C, + 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C, + 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C, ',': punctuation, 'null': atom, 'fn:false()': atom, 'fn:true()': atom }; - - // a list of 'basic' keywords. For each add a property to kwObj with the value of + + // a list of 'basic' keywords. For each add a property to kwObj with the value of // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"} var basic = ['after','ancestor','ancestor-or-self','and','as','ascending','assert','attribute','before', 'by','case','cast','child','comment','declare','default','define','descendant','descendant-or-self', @@ -57,20 +57,20 @@ CodeMirror.defineMode("xquery", function() { 'self','some','sortby','stable','text','then','to','treat','typeswitch','union','variable','version','where', 'xquery', 'empty-sequence']; for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);}; - - // a list of types. For each add a property to kwObj with the value of + + // a list of types. For each add a property to kwObj with the value of // {type: "atom", style: "atom"} - var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime', - 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary', + var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime', + 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary', 'xs:base64Binary', 'xs:anyURI', 'xs:QName', 'xs:byte','xs:boolean','xs:anyURI','xf:yearMonthDuration']; for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;}; - + // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"} var operators = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', ':=', '=', '>', '>=', '<', '<=', '.', '|', '?', 'and', 'or', 'div', 'idiv', 'mod', '*', '/', '+', '-']; for(var i=0, l=operators.length; i < l; i++) { kwObj[operators[i]] = operator;}; - + // each axis_specifiers will add a property to kwObj with value of {type: "axis_specifier", style: "qualifier"} - var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", + var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", "ancestor::", "ancestor-or-self::", "following::", "preceding::", "following-sibling::", "preceding-sibling::"]; for(var i=0, l=axis_specifiers.length; i < l; i++) { kwObj[axis_specifiers[i]] = qualifier; }; @@ -80,42 +80,42 @@ CodeMirror.defineMode("xquery", function() { // Used as scratch variables to communicate multiple values without // consing up tons of objects. var type, content; - + function ret(tp, style, cont) { type = tp; content = cont; return style; } - + function chain(stream, state, f) { state.tokenize = f; return f(stream, state); } - + // the primary mode tokenizer function tokenBase(stream, state) { - var ch = stream.next(), + var ch = stream.next(), mightBeFunction = false, isEQName = isEQNameAhead(stream); - + // an XML tag (if not in some sub, chained tokenizer) if (ch == "<") { if(stream.match("!--", true)) return chain(stream, state, tokenXMLComment); - + if(stream.match("![CDATA", false)) { state.tokenize = tokenCDATA; return ret("tag", "tag"); } - + if(stream.match("?", false)) { return chain(stream, state, tokenPreProcessing); } - + var isclose = stream.eat("/"); stream.eatSpace(); var tagName = "", c; while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; - + return chain(stream, state, tokenTag(tagName, isclose)); } // start code block @@ -136,7 +136,7 @@ CodeMirror.defineMode("xquery", function() { popStateStack(state); return ret("tag", "tag"); } - else + else return ret("word", "variable"); } // if a number @@ -186,13 +186,13 @@ CodeMirror.defineMode("xquery", function() { // if there's a EQName ahead, consume the rest of the string portion, it's likely a function if(isEQName && ch === '\"') while(stream.next() !== '"'){} if(isEQName && ch === '\'') while(stream.next() !== '\''){} - + // gobble up a word if the character is not known if(!known) stream.eatWhile(/[\w\$_-]/); - + // gobble a colon in the case that is a lib func type call fn:doc var foundColon = stream.eat(":"); - + // if there's not a second colon, gobble another word. Otherwise, it's probably an axis specifier // which should get matched as a keyword if(!stream.eat(":") && foundColon) { @@ -205,27 +205,27 @@ CodeMirror.defineMode("xquery", function() { // is the word a keyword? var word = stream.current(); known = keywords.propertyIsEnumerable(word) && keywords[word]; - - // if we think it's a function call but not yet known, + + // if we think it's a function call but not yet known, // set style to variable for now for lack of something better if(mightBeFunction && !known) known = {type: "function_call", style: "variable def"}; - + // if the previous word was element, attribute, axis specifier, this word should be the name of that if(isInXmlConstructor(state)) { popStateStack(state); return ret("word", "variable", word); } - // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and + // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and // push the stack so we know to look for it on the next word if(word == "element" || word == "attribute" || known.type == "axis_specifier") pushStateStack(state, {type: "xmlconstructor"}); - + // if the word is known, return the details of that else just call this a generic 'word' return known ? ret(known.type, known.style, word) : ret("word", "variable", word); } } - // handle comments, including nested + // handle comments, including nested function tokenComment(stream, state) { var maybeEnd = false, maybeNested = false, nestedCount = 0, ch; while (ch = stream.next()) { @@ -243,7 +243,7 @@ CodeMirror.defineMode("xquery", function() { maybeEnd = (ch == ":"); maybeNested = (ch == "("); } - + return ret("comment", "comment"); } @@ -264,10 +264,10 @@ CodeMirror.defineMode("xquery", function() { // if we're in a string and in an XML block, allow an embedded code block if(stream.match("{", false) && isInXmlAttributeBlock(state)) { state.tokenize = tokenBase; - return ret("string", "string"); + return ret("string", "string"); } - + while (ch = stream.next()) { if (ch == quote) { popStateStack(state); @@ -278,16 +278,16 @@ CodeMirror.defineMode("xquery", function() { // if we're in a string and in an XML block, allow an embedded code block in an attribute if(stream.match("{", false) && isInXmlAttributeBlock(state)) { state.tokenize = tokenBase; - return ret("string", "string"); + return ret("string", "string"); } } } - + return ret("string", "string"); }; } - + // tokenizer for variables function tokenVariable(stream, state) { var isVariableChar = /[\w\$_-]/; @@ -304,7 +304,7 @@ CodeMirror.defineMode("xquery", function() { state.tokenize = tokenBase; return ret("variable", "variable"); } - + // tokenizer for XML tags function tokenTag(name, isclose) { return function(stream, state) { @@ -322,7 +322,7 @@ CodeMirror.defineMode("xquery", function() { return ret("tag", "tag"); } else { - state.tokenize = tokenBase; + state.tokenize = tokenBase; } return ret("tag", "tag"); }; @@ -331,7 +331,7 @@ CodeMirror.defineMode("xquery", function() { // tokenizer for XML attributes function tokenAttribute(stream, state) { var ch = stream.next(); - + if(ch == "/" && stream.eat(">")) { if(isInXmlAttributeBlock(state)) popStateStack(state); if(isInXmlBlock(state)) popStateStack(state); @@ -347,7 +347,7 @@ CodeMirror.defineMode("xquery", function() { if (ch == '"' || ch == "'") return chain(stream, state, tokenString(ch, tokenAttribute)); - if(!isInXmlAttributeBlock(state)) + if(!isInXmlAttributeBlock(state)) pushStateStack(state, { type: "attribute", name: name, tokenize: tokenAttribute}); stream.eat(/[a-zA-Z_:]/); @@ -357,18 +357,18 @@ CodeMirror.defineMode("xquery", function() { // the case where the attribute has not value and the tag was closed if(stream.match(">", false) || stream.match("/", false)) { popStateStack(state); - state.tokenize = tokenBase; + state.tokenize = tokenBase; } return ret("attribute", "attribute"); } - - // handle comments, including nested + + // handle comments, including nested function tokenXMLComment(stream, state) { var ch; while (ch = stream.next()) { if (ch == "-" && stream.match("->", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment"); } } @@ -380,7 +380,7 @@ CodeMirror.defineMode("xquery", function() { var ch; while (ch = stream.next()) { if (ch == "]" && stream.match("]", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment"); } } @@ -391,20 +391,20 @@ CodeMirror.defineMode("xquery", function() { var ch; while (ch = stream.next()) { if (ch == "?" && stream.match(">", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment meta"); } } } - - + + // functions to test the current context of the state function isInXmlBlock(state) { return isIn(state, "tag"); } function isInXmlAttributeBlock(state) { return isIn(state, "attribute"); } function isInXmlConstructor(state) { return isIn(state, "xmlconstructor"); } function isInString(state) { return isIn(state, "string"); } - function isEQNameAhead(stream) { + function isEQNameAhead(stream) { // assume we've already eaten a quote (") if(stream.current() === '"') return stream.match(/^[^\"]+\"\:/, false); @@ -413,21 +413,21 @@ CodeMirror.defineMode("xquery", function() { else return false; } - + function isIn(state, type) { - return (state.stack.length && state.stack[state.stack.length - 1].type == type); + return (state.stack.length && state.stack[state.stack.length - 1].type == type); } - + function pushStateStack(state, newState) { state.stack.push(newState); } - + function popStateStack(state) { state.stack.pop(); var reinstateTokenize = state.stack.length && state.stack[state.stack.length-1].tokenize; state.tokenize = reinstateTokenize || tokenBase; } - + // the interface for the mode API return { startState: function() { diff --git a/plugins/webeditor/codemirror/mode/yaml/yaml.js b/plugins/webeditor/codemirror/mode/yaml/yaml.js index 59e2641a0..b21a0524d 100755 --- a/plugins/webeditor/codemirror/mode/yaml/yaml.js +++ b/plugins/webeditor/codemirror/mode/yaml/yaml.js @@ -1,8 +1,8 @@ CodeMirror.defineMode("yaml", function() { - + var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); - + return { token: function(stream, state) { var ch = stream.peek(); @@ -31,7 +31,7 @@ CodeMirror.defineMode("yaml", function() { return "atom"; } if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } - + /* inline pairs/lists */ if (stream.match(/^(\{|\}|\[|\])/)) { if (ch == '{') @@ -44,7 +44,7 @@ CodeMirror.defineMode("yaml", function() { state.inlineList--; return 'meta'; } - + /* list seperator */ if (state.inlineList > 0 && !esc && ch == ',') { stream.next(); @@ -58,7 +58,7 @@ CodeMirror.defineMode("yaml", function() { stream.next(); return 'meta'; } - + /* start of value of a pair */ if (state.pairStart) { /* block literals */ diff --git a/plugins/webeditor/codemirror/mode/z80/z80.js b/plugins/webeditor/codemirror/mode/z80/z80.js index c026790dc..60e9dad7c 100755 --- a/plugins/webeditor/codemirror/mode/z80/z80.js +++ b/plugins/webeditor/codemirror/mode/z80/z80.js @@ -7,7 +7,7 @@ CodeMirror.defineMode('z80', function() var variables2 = /^(n?[zc]|p[oe]?|m)\b/i; var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\b/i; var numbers = /^([\da-f]+h|[0-7]+o|[01]+b|\d+)\b/i; - + return {startState: function() { return {context: 0}; @@ -15,24 +15,24 @@ CodeMirror.defineMode('z80', function() { if (!stream.column()) state.context = 0; - + if (stream.eatSpace()) return null; - + var w; - + if (stream.eatWhile(/\w/)) { w = stream.current(); - + if (stream.indentation()) { if (state.context == 1 && variables1.test(w)) return 'variable-2'; - + if (state.context == 2 && variables2.test(w)) return 'variable-3'; - + if (keywords1.test(w)) { state.context = 1; @@ -48,7 +48,7 @@ CodeMirror.defineMode('z80', function() state.context = 3; return 'keyword'; } - + if (errors.test(w)) return 'error'; } @@ -72,11 +72,11 @@ CodeMirror.defineMode('z80', function() { if (w == '"') break; - + if (w == '\\') stream.next(); } - + return 'string'; } else if (stream.eat('\'')) @@ -87,7 +87,7 @@ CodeMirror.defineMode('z80', function() else if (stream.eat('.') || stream.sol() && stream.eat('#')) { state.context = 4; - + if (stream.eatWhile(/\w/)) return 'def'; } @@ -105,7 +105,7 @@ CodeMirror.defineMode('z80', function() { stream.next(); } - + return null; }}; }); diff --git a/plugins/webeditor/codemirror/test/index.html b/plugins/webeditor/codemirror/test/index.html index 0a0da5bd8..65f45693b 100755 --- a/plugins/webeditor/codemirror/test/index.html +++ b/plugins/webeditor/codemirror/test/index.html @@ -84,7 +84,7 @@

      CodeMirror: Test Suite

      running = false, // Flag that states tests are running quit = false, // Flag to quit tests ASAP verbose = false; // Adds message for *every* test to output - + function runHarness(){ if (running) { quit = true; @@ -115,7 +115,7 @@

      CodeMirror: Test Suite

      ""; runTests(displayTest); } - + function setStatus(message, className, force){ if (quit && !force) return; if (!message) throw("must provide message"); diff --git a/plugins/webeditor/codemirror/test/lint/acorn.js b/plugins/webeditor/codemirror/test/lint/acorn.js index 6323b1fc6..80bc5565c 100755 --- a/plugins/webeditor/codemirror/test/lint/acorn.js +++ b/plugins/webeditor/codemirror/test/lint/acorn.js @@ -647,7 +647,7 @@ } // Read an integer, octal integer, or floating-point number. - + function readNumber(ch) { var start = tokPos, isFloat = ch === "."; if (!isFloat && readInt(10) == null) raise(start, "Invalid number"); @@ -808,7 +808,7 @@ // ### Parser utilities // Continue to the next token. - + function next() { lastStart = tokStart; lastEnd = tokEnd; @@ -1071,7 +1071,7 @@ // In `return` (and `break`/`continue`), the keywords with // optional arguments, we eagerly look for a semicolon or the // possibility to insert one. - + if (eat(_semi) || canInsertSemicolon()) node.argument = null; else { node.argument = parseExpression(); semicolon(); } return finishNode(node, "ReturnStatement"); @@ -1086,7 +1086,7 @@ // Statements under must be grouped (by label) in SwitchCase // nodes. `cur` is used to keep the node that we are currently // adding statements to. - + for (var cur, sawDefault; tokType != _braceR;) { if (tokType === _case || tokType === _default) { var isCase = tokType === _case; @@ -1460,7 +1460,7 @@ // New's precedence is slightly tricky. It must allow its argument // to be a `[]` or dot subscript expression, but not a call — at - // least, not without wrapping it in parentheses. Thus, it uses the + // least, not without wrapping it in parentheses. Thus, it uses the function parseNew() { var node = startNode(); diff --git a/plugins/webeditor/codemirror/theme/twilight.css b/plugins/webeditor/codemirror/theme/twilight.css index fd8944ba8..40f4bd7ce 100755 --- a/plugins/webeditor/codemirror/theme/twilight.css +++ b/plugins/webeditor/codemirror/theme/twilight.css @@ -23,4 +23,3 @@ .cm-s-twilight .cm-header { color: #FF6400; } .cm-s-twilight .cm-hr { color: #AEAEAE; } .cm-s-twilight .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } /**/ - diff --git a/plugins/webeditor/codemirror/theme/xq-light.css b/plugins/webeditor/codemirror/theme/xq-light.css index 08784d58c..79d2472e8 100755 --- a/plugins/webeditor/codemirror/theme/xq-light.css +++ b/plugins/webeditor/codemirror/theme/xq-light.css @@ -40,4 +40,4 @@ THE SOFTWARE. .cm-s-xq-light span.cm-attribute {color: #7F007F;} .cm-s-xq-light .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-xq-light .CodeMirror-matchingbracket {border:1px solid grey;color:black !important;background:yellow;} \ No newline at end of file +.cm-s-xq-light .CodeMirror-matchingbracket {border:1px solid grey;color:black !important;background:yellow;} diff --git a/plugins/webeditor/index.html b/plugins/webeditor/index.html index 3d9b0d64d..4afc90a42 100755 --- a/plugins/webeditor/index.html +++ b/plugins/webeditor/index.html @@ -163,4 +163,3 @@ - diff --git a/poetry.lock b/poetry.lock index c9835b31c..8ad893217 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "apscheduler" @@ -39,24 +39,6 @@ files = [ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - [[package]] name = "backports-zoneinfo" version = "0.2.1" @@ -87,13 +69,13 @@ tzdata = ["tzdata"] [[package]] name = "blinker" -version = "1.6.2" +version = "1.7.0" description = "Fast, simple object-to-object and broadcast signaling" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "blinker-1.6.2-py3-none-any.whl", hash = "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0"}, - {file = "blinker-1.6.2.tar.gz", hash = "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213"}, + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, ] [[package]] @@ -123,173 +105,187 @@ bump2version = "*" [[package]] name = "certifi" -version = "2023.7.22" +version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] pycparser = "*" +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + [[package]] name = "charset-normalizer" -version = "3.2.0" +version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] @@ -319,109 +315,123 @@ files = [ [[package]] name = "coverage" -version = "6.5.0" +version = "7.4.0" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, - {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, - {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, + {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, + {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, + {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, + {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, + {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, + {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, + {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, + {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, + {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, + {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, + {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, + {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, ] +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + [package.extras] toml = ["tomli"] [[package]] name = "cryptography" -version = "41.0.3" +version = "42.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507"}, - {file = "cryptography-41.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47"}, - {file = "cryptography-41.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116"}, - {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c"}, - {file = "cryptography-41.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae"}, - {file = "cryptography-41.0.3-cp37-abi3-win32.whl", hash = "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306"}, - {file = "cryptography-41.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906"}, - {file = "cryptography-41.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84"}, - {file = "cryptography-41.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1"}, - {file = "cryptography-41.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4"}, - {file = "cryptography-41.0.3.tar.gz", hash = "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34"}, + {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434"}, + {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01"}, + {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd"}, + {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3"}, + {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b"}, + {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87"}, + {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17"}, + {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d"}, + {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec"}, + {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc"}, + {file = "cryptography-42.0.0-cp37-abi3-win32.whl", hash = "sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4"}, + {file = "cryptography-42.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0"}, + {file = "cryptography-42.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf"}, + {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689"}, + {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0"}, + {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139"}, + {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2"}, + {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513"}, + {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8"}, + {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81"}, + {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221"}, + {file = "cryptography-42.0.0-cp39-abi3-win32.whl", hash = "sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b"}, + {file = "cryptography-42.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94"}, + {file = "cryptography-42.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e"}, + {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3"}, + {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f"}, + {file = "cryptography-42.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08"}, + {file = "cryptography-42.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f"}, + {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440"}, + {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0"}, + {file = "cryptography-42.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce"}, + {file = "cryptography-42.0.0.tar.gz", hash = "sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4"}, ] [package.dependencies] -cffi = ">=1.12" +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -441,6 +451,17 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "docutils" version = "0.20.1" @@ -454,76 +475,43 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.3" +version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] -name = "flake8" -version = "6.1.0" -description = "the modular source code checker: pep8 pyflakes and co" +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" - -[[package]] -name = "flake8-bugbear" -version = "23.7.10" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.8" files = [ - {file = "flake8-bugbear-23.7.10.tar.gz", hash = "sha256:0ebdc7d8ec1ca8bd49347694562381f099f4de2f8ec6bda7a7dca65555d9e0d4"}, - {file = "flake8_bugbear-23.7.10-py3-none-any.whl", hash = "sha256:d99d005114020fbef47ed5e4aebafd22f167f9a0fbd0d8bf3c9e90612cb25c34"}, + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=6.0.0" - [package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "flake8-import-order" -version = "0.18.2" -description = "Flake8 and pylama plugin that checks the ordering of import statements." -optional = false -python-versions = "*" -files = [ - {file = "flake8-import-order-0.18.2.tar.gz", hash = "sha256:e23941f892da3e0c09d711babbb0c73bc735242e9b216b726616758a920d900e"}, - {file = "flake8_import_order-0.18.2-py2.py3-none-any.whl", hash = "sha256:82ed59f1083b629b030ee9d3928d9e06b6213eb196fe745b3a7d4af2168130df"}, -] - -[package.dependencies] -pycodestyle = "*" -setuptools = "*" +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "flask" -version = "2.3.3" +version = "3.0.1" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.8" files = [ - {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, - {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, + {file = "flask-3.0.1-py3-none-any.whl", hash = "sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13"}, + {file = "flask-3.0.1.tar.gz", hash = "sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403"}, ] [package.dependencies] @@ -532,7 +520,7 @@ click = ">=8.1.3" importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.1.2" Jinja2 = ">=3.1.2" -Werkzeug = ">=2.3.7" +Werkzeug = ">=3.0.0" [package.extras] async = ["asgiref (>=3.2)"] @@ -624,53 +612,67 @@ gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] tornado = ["tornado (>=0.2)"] +[[package]] +name = "identify" +version = "2.5.33" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, + {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "importlib-metadata" -version = "6.8.0" +version = "7.0.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "importlib-resources" -version = "6.0.1" +version = "6.1.1" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.0.1-py3-none-any.whl", hash = "sha256:134832a506243891221b88b4ae1213327eea96ceb4e407a00d790bb0626f45cf"}, - {file = "importlib_resources-6.0.1.tar.gz", hash = "sha256:4359457e42708462b9626a04657c6208ad799ceb41e5c58c57ffa0e6a098a5d4"}, + {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, + {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -683,23 +685,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - [[package]] name = "itsdangerous" version = "2.1.2" @@ -746,13 +731,13 @@ trio = ["async_generator", "trio"] [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -763,13 +748,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" -version = "24.2.0" +version = "24.3.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.8" files = [ - {file = "keyring-24.2.0-py3-none-any.whl", hash = "sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6"}, - {file = "keyring-24.2.0.tar.gz", hash = "sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509"}, + {file = "keyring-24.3.0-py3-none-any.whl", hash = "sha256:4446d35d636e6a10b8bce7caa66913dd9eca5fd222ca03a3d42c38608ac30836"}, + {file = "keyring-24.3.0.tar.gz", hash = "sha256:e730ecffd309658a08ee82535a3b5ec4b4c8669a9be11efb66249d8e0aeb9a25"}, ] [package.dependencies] @@ -781,19 +766,19 @@ pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] -completion = ["shtab"] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +completion = ["shtab (>=1.1.0)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [[package]] name = "limits" -version = "3.6.0" +version = "3.7.0" description = "Rate limiting utilities" optional = false python-versions = ">=3.7" files = [ - {file = "limits-3.6.0-py3-none-any.whl", hash = "sha256:32fe29a398352c71bc43d53773117d47e22c5ea4200aef28d3f5fdee10334cd7"}, - {file = "limits-3.6.0.tar.gz", hash = "sha256:57a9c69fd37ad1e4fa3886dff8d035227e1f6af87f47e9118627e72cf1ced3bf"}, + {file = "limits-3.7.0-py3-none-any.whl", hash = "sha256:c528817b7fc15f3e86ad091ba3e40231f6430a91b753db864767684cda8a7f2e"}, + {file = "limits-3.7.0.tar.gz", hash = "sha256:124c6a04d2f4b20990fb1de019eec9474d6c1346c70d8fd0561609b86998b64a"}, ] [package.dependencies] @@ -840,72 +825,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] @@ -937,40 +921,54 @@ test = ["pytest", "pytest-cov"] [[package]] name = "more-itertools" -version = "10.1.0" +version = "10.2.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" files = [ - {file = "more-itertools-10.1.0.tar.gz", hash = "sha256:626c369fa0eb37bac0291bce8259b332fd59ac792fa5497b59837309cd5b114a"}, - {file = "more_itertools-10.1.0-py3-none-any.whl", hash = "sha256:64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6"}, + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, ] [[package]] name = "nh3" -version = "0.2.14" -description = "Ammonia HTML sanitizer Python binding" +version = "0.2.15" +description = "Python bindings to the ammonia HTML sanitization library." optional = false python-versions = "*" files = [ - {file = "nh3-0.2.14-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9be2f68fb9a40d8440cbf34cbf40758aa7f6093160bfc7fb018cce8e424f0c3a"}, - {file = "nh3-0.2.14-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f99212a81c62b5f22f9e7c3e347aa00491114a5647e1f13bbebd79c3e5f08d75"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7771d43222b639a4cd9e341f870cee336b9d886de1ad9bec8dddab22fe1de450"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:525846c56c2bcd376f5eaee76063ebf33cf1e620c1498b2a40107f60cfc6054e"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e8986f1dd3221d1e741fda0a12eaa4a273f1d80a35e31a1ffe579e7c621d069e"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18415df36db9b001f71a42a3a5395db79cf23d556996090d293764436e98e8ad"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:377aaf6a9e7c63962f367158d808c6a1344e2b4f83d071c43fbd631b75c4f0b2"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0be5c792bd43d0abef8ca39dd8acb3c0611052ce466d0401d51ea0d9aa7525"}, - {file = "nh3-0.2.14-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:93a943cfd3e33bd03f77b97baa11990148687877b74193bf777956b67054dcc6"}, - {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac8056e937f264995a82bf0053ca898a1cb1c9efc7cd68fa07fe0060734df7e4"}, - {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:203cac86e313cf6486704d0ec620a992c8bc164c86d3a4fd3d761dd552d839b5"}, - {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:5529a3bf99402c34056576d80ae5547123f1078da76aa99e8ed79e44fa67282d"}, - {file = "nh3-0.2.14-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aed56a86daa43966dd790ba86d4b810b219f75b4bb737461b6886ce2bde38fd6"}, - {file = "nh3-0.2.14-cp37-abi3-win32.whl", hash = "sha256:116c9515937f94f0057ef50ebcbcc10600860065953ba56f14473ff706371873"}, - {file = "nh3-0.2.14-cp37-abi3-win_amd64.whl", hash = "sha256:88c753efbcdfc2644a5012938c6b9753f1c64a5723a67f0301ca43e7b85dcf0e"}, - {file = "nh3-0.2.14.tar.gz", hash = "sha256:a0c509894fd4dccdff557068e5074999ae3b75f4c5a2d6fb5415e782e25679c4"}, + {file = "nh3-0.2.15-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9c0d415f6b7f2338f93035bba5c0d8c1b464e538bfbb1d598acd47d7969284f0"}, + {file = "nh3-0.2.15-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6f42f99f0cf6312e470b6c09e04da31f9abaadcd3eb591d7d1a88ea931dca7f3"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac19c0d68cd42ecd7ead91a3a032fdfff23d29302dbb1311e641a130dfefba97"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0d77272ce6d34db6c87b4f894f037d55183d9518f948bba236fe81e2bb4e28"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8d595df02413aa38586c24811237e95937ef18304e108b7e92c890a06793e3bf"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86e447a63ca0b16318deb62498db4f76fc60699ce0a1231262880b38b6cff911"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3277481293b868b2715907310c7be0f1b9d10491d5adf9fce11756a97e97eddf"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60684857cfa8fdbb74daa867e5cad3f0c9789415aba660614fe16cd66cbb9ec7"}, + {file = "nh3-0.2.15-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b803a5875e7234907f7d64777dfde2b93db992376f3d6d7af7f3bc347deb305"}, + {file = "nh3-0.2.15-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0d02d0ff79dfd8208ed25a39c12cbda092388fff7f1662466e27d97ad011b770"}, + {file = "nh3-0.2.15-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f3b53ba93bb7725acab1e030bc2ecd012a817040fd7851b332f86e2f9bb98dc6"}, + {file = "nh3-0.2.15-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:b1e97221cedaf15a54f5243f2c5894bb12ca951ae4ddfd02a9d4ea9df9e1a29d"}, + {file = "nh3-0.2.15-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5167a6403d19c515217b6bcaaa9be420974a6ac30e0da9e84d4fc67a5d474c5"}, + {file = "nh3-0.2.15-cp37-abi3-win32.whl", hash = "sha256:427fecbb1031db085eaac9931362adf4a796428ef0163070c484b5a768e71601"}, + {file = "nh3-0.2.15-cp37-abi3-win_amd64.whl", hash = "sha256:bc2d086fb540d0fa52ce35afaded4ea526b8fc4d3339f783db55c95de40ef02e"}, + {file = "nh3-0.2.15.tar.gz", hash = "sha256:d1e30ff2d8d58fb2a14961f7aac1bbb1c51f9bdd7da727be35c63826060b0bf3"}, ] +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "ordered-set" version = "4.1.0" @@ -987,13 +985,13 @@ dev = ["black", "mypy", "pytest"] [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -1010,15 +1008,30 @@ files = [ [package.extras] testing = ["pytest", "pytest-cov"] +[[package]] +name = "platformdirs" +version = "4.1.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1026,16 +1039,23 @@ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] -name = "pycodestyle" -version = "2.11.0" -description = "Python style guide checker" +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, - {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "pycparser" version = "2.21" @@ -1064,40 +1084,30 @@ snowballstemmer = ">=2.2.0" [package.extras] toml = ["tomli (>=1.2.3)"] -[[package]] -name = "pyflakes" -version = "3.1.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, -] - [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.2" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -1111,6 +1121,39 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-click" +version = "1.1.0" +description = "Pytest plugin for Click" +optional = false +python-versions = "*" +files = [ + {file = "pytest_click-1.1.0-py3-none-any.whl", hash = "sha256:eade4742c2f02c345e78a32534a43e8db04acf98d415090539dacc880b7cd0e9"}, + {file = "pytest_click-1.1.0.tar.gz", hash = "sha256:fdd9f6721f877dda021e7c5dc73e70aecd37e5ed23ec6820f8a7b3fd7b4f8d30"}, +] + +[package.dependencies] +click = ">=6.0" +pytest = ">=5.0" + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + [[package]] name = "pytz" version = "2023.3.post1" @@ -1133,6 +1176,66 @@ files = [ {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, ] +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + [[package]] name = "readme-renderer" version = "42.0" @@ -1154,13 +1257,13 @@ md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "redis" -version = "5.0.0" +version = "5.0.1" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.0-py3-none-any.whl", hash = "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0"}, - {file = "redis-5.0.0.tar.gz", hash = "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120"}, + {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, + {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, ] [package.dependencies] @@ -1221,13 +1324,13 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.5.2" +version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, - {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, ] [package.dependencies] @@ -1253,6 +1356,32 @@ files = [ click = ">=5.0.0" redis = ">=4.0.0" +[[package]] +name = "ruff" +version = "0.1.14" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, + {file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, + {file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, + {file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, + {file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, +] + [[package]] name = "secretstorage" version = "3.3.3" @@ -1270,19 +1399,19 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "68.2.0" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.0-py3-none-any.whl", hash = "sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8"}, - {file = "setuptools-68.2.0.tar.gz", hash = "sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1317,80 +1446,59 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tqdm" -version = "4.66.1" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, - {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - [[package]] name = "twine" -version = "3.8.0" +version = "4.0.2" description = "Collection of utilities for publishing packages on PyPI" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"}, - {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"}, + {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, + {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, ] [package.dependencies] -colorama = ">=0.4.3" importlib-metadata = ">=3.6" keyring = ">=15.1" pkginfo = ">=1.8.1" -readme-renderer = ">=21.0" +readme-renderer = ">=35.0" requests = ">=2.20" requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" rfc3986 = ">=1.4.0" -tqdm = ">=4.14" +rich = ">=12.0.0" urllib3 = ">=1.26.0" [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "tzdata" -version = "2023.3" +version = "2023.4" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, + {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, + {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, ] [[package]] name = "tzlocal" -version = "5.0.1" +version = "5.2" description = "tzinfo object for the local timezone" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tzlocal-5.0.1-py3-none-any.whl", hash = "sha256:f3596e180296aaf2dbd97d124fe76ae3a0e3d32b258447de7b939b3fd4be992f"}, - {file = "tzlocal-5.0.1.tar.gz", hash = "sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803"}, + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, ] [package.dependencies] @@ -1398,34 +1506,53 @@ files = [ tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] [[package]] name = "urllib3" -version = "2.0.4" +version = "2.1.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, - {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "virtualenv" +version = "20.25.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "werkzeug" -version = "2.3.7" +version = "3.0.1" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"}, - {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"}, + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, ] [package.dependencies] @@ -1436,104 +1563,99 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "wrapt" -version = "1.15.0" +version = "1.16.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] [[package]] name = "zipp" -version = "3.16.2" +version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"}, - {file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"}, + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [metadata] lock-version = "2.0" -python-versions = "^3.8.1" -content-hash = "e0f7a98346299c8e4a8c1ee3418605b8ea3906290806d9eac029e918c7f2a7df" +python-versions = ">=3.8.1" +content-hash = "ca0032b5003833a8a1adeb62c73da7750c3673cc5caebaec2611f33c2f4252d6" diff --git a/proselint/__init__.py b/proselint/__init__.py index f1e5525c9..b18e507e5 100644 --- a/proselint/__init__.py +++ b/proselint/__init__.py @@ -1,6 +1,9 @@ -# flake8: noqa - """Proselint applies advice from great writers to your writing.""" + +from . import checks from . import tools +from .config_base import proselint_base as config_default +from .config_paths import proselint_path as path +from .logger import log -__all__ = ("tools") +__all__ = ["checks", "config_default", "log", "path", "tools"] diff --git a/proselint/__main__.py b/proselint/__main__.py index 7b36e658e..ae5e50faf 100644 --- a/proselint/__main__.py +++ b/proselint/__main__.py @@ -3,8 +3,7 @@ This lets you run python -m proselint. """ - from .command_line import proselint -if __name__ == '__main__': +if __name__ == "__main__": proselint() diff --git a/proselint/checks/__init__.py b/proselint/checks/__init__.py index a568e93b2..a8207401e 100644 --- a/proselint/checks/__init__.py +++ b/proselint/checks/__init__.py @@ -1 +1,405 @@ -"""All the checks are organized into modules and places here.""" +"""All the checks are organized into modules and placed in subdirectories. + +This file contains: +- contained function-set called by linter +- _check-functions used by the checks + +This submodule gets used for multiprocessing! +All custom code is contained in here. + +""" + +from __future__ import annotations + +import functools +import importlib +import re +from enum import Enum +from typing import Callable +from typing import Optional +from typing import TypeAlias + +from proselint.logger import log + +ResultCheck: TypeAlias = tuple[int, int, str, str, Optional[str]] +# content: start_pos, end_pos, check_name, message, replacement) +# NOTE1: NewType() is too strict here +# NOTE2: py312 can use -> +# type ResultCheck = tuple[int, int, str, str, Optional[str]] + + +############################################################################### +# Check-Interface used by linter ############################################## +############################################################################### + + +def get_checks(options: dict) -> list[Callable[[str, str], list[ResultCheck]]]: + """Extract the checks. + Rule: fn-name must begin with "check", so check_xyz() is ok + """ + # TODO: benchmark consecutive runs of this + # config should only translate once to check-list + checks = [] + check_names = [key for (key, val) in options["checks"].items() if val] + + for check_name in check_names: + try: + module = importlib.import_module("." + check_name, "proselint.checks") + except ModuleNotFoundError: + log.exception( + "requested config-flag '%s' not found in proselint.checks", + check_name, + ) + continue + checks += [ + getattr(module, d) for d in dir(module) if re.match(r"^check", d) + ] + + log.debug("Collected %d checks to run", len(checks)) + return checks + + +def run_checks(_check: Callable, _text: str, source: str = "") -> list: + # TODO: frozenset is hashable (list without duplicates) + # -> check-list, result-lists padding + # -> some checks expect words in text and need something around it + # -> this prevents edge-cases + _text = f" \n{_text}\n " # maybe not the fastest OP + errors = [] + results = _check(_text) + for result in results: + (start, end, check_name, message, replacements) = result + (line, column) = get_line_and_column(_text, start) + if not is_quoted(start, _text): + # note: + # - switch to 1based counting by adding +1 + # - for line it cancels out with -1 from padding + errors += [ + ( + check_name, + message, + source, # can't be Path, unless pickle changes + line, # +1 -1, cancel out + column + 1, + start + 1, + end + 1, + end - start, + "warning", + replacements, + ), + ] + return errors + + +def get_line_and_column(text, position): + """Return the line number and column of a position in a string.""" + position_counter = 0 + line_no = 0 + for line in text.splitlines(True): + if (position_counter + len(line.rstrip())) >= position: + break + position_counter += len(line) + line_no += 1 + return line_no, position - position_counter + + +def is_quoted(position: int, text: str) -> bool: + """Determine if the position in the text falls within a quote.""" + + def matching(quotemark1: str, quotemark2: str) -> bool: + straight = "\"'" + curly = "“”" + return (quotemark1 in straight and quotemark2 in straight) or ( + quotemark1 in curly and quotemark2 in curly + ) + + def find_ranges(_text: str) -> list[tuple[int, int]]: + # TODO: optimize, as it is top3 time-waster (of module-functions) + # this could be a 1d array / LUT + s = 0 + q = pc = "" + start = None + ranges = [] + seps = " .,:;-\r\n" + quotes = ['"', "“", "”", "'"] + for i, c in enumerate(_text + "\n"): + if s == 0 and c in quotes and pc in seps: + start = i + s = 1 + q = c + elif s == 1 and matching(c, q): + s = 2 + elif s == 2: + if c in seps: + ranges.append((start + 1, i - 1)) + start = None + s = 0 + else: + s = 1 + pc = c + return ranges + + def position_in_ranges( + ranges: list[tuple[int, int]], _position: int + ) -> bool: + return any(start <= _position < end for start, end in ranges) + + return position_in_ranges(find_ranges(text), position) + + +############################################################################### +# The actual check-sub-functions used by the checks ########################## +############################################################################### + + +def consistency_check( + text: str, + word_pairs: list, + err: str, + msg: str, + offset: int = 0, +) -> list[ResultCheck]: + """Build a consistency checker for the given word_pairs.""" + results = [] + + for w in word_pairs: + matches = [ + list(re.finditer(w[0], text)), + list(re.finditer(w[1], text)), + ] + + if len(matches[0]) > 0 and len(matches[1]) > 0: + idx_minority = len(matches[0]) > len(matches[1]) + + results += [ + ( + m.start() + offset, + m.end() + offset, + err, + msg.format(w[not idx_minority], m.group(0)), + w[not idx_minority], + ) + for m in matches[idx_minority] + ] + + return results + + +def preferred_forms_check( # noqa: PLR0913, PLR0917 + text: str, + items: list, + err: str, + msg: str, + ignore_case: bool = True, + offset: int = 0, +) -> list[ResultCheck]: + """Build a checker that suggests the preferred form.""" + flags = re.IGNORECASE if ignore_case else 0 + regex = r"[\W^]{}[\W$]" # TODO: replace with enum below? + + return [ + ( + m.start() + 1 + offset, + m.end() + offset, + err, + msg.format(item[0], m.group(0).strip()), + item[0], + ) + for item in items + for r in item[1] + for m in re.finditer(regex.format(r), text, flags=flags) + ] + # TODO: can we speed up str.format() ? + # fast-string? or do padding already in checks + + +# PADDINGS, add more if needed +class Pd(str, Enum): + disabled = r"{}" + # choose for checks with custom regex + whitespace = r"\s{}\s" + # -> req whitespace around (no punctuation!) + sep_in_txt = r"(?:^|\W){}[\W$]" + # req non-text character around + # -> finds item as long it is surrounded by any non-word character: + # - whitespace + # - punctuation + # - newline ... + + +def existence_check( # noqa: PLR0913, PLR0917 + text: str, + re_items: list, + err: str, + msg: str, + ignore_case: bool = True, + string: bool = False, # todo: why not default on? + offset: int = 0, # todo: some checks set this strangely + padding: Pd = Pd.sep_in_txt, + dotall: bool = False, + excluded_topics: Optional[list] = None, + exceptions=(), +) -> list[ResultCheck]: + """Build a checker that prohibits certain words or phrases.""" + flags = 0 + if ignore_case: + flags |= re.IGNORECASE + if string: + flags |= re.UNICODE + if dotall: + flags |= re.DOTALL + + errors: list[ResultCheck] = [] + + # If the topic of the text is in the excluded list, return immediately. + if excluded_topics: + tps = topics(text) + if any(t in excluded_topics for t in tps): + return errors + + rx = "|".join(padding.format(_item) for _item in re_items) + for m in re.finditer(rx, text, flags=flags): + txt = m.group(0).strip() + if any(re.search(exception, txt) for exception in exceptions): + continue + errors.append( + ( + m.start() + 1 + offset, + m.end() + offset, + err, + msg.format(txt), + None, + ), + ) + # TODO: doesn't the padding alter the start+end? + + return errors + + +def simple_existence_check( + text: str, pattern: str, err: str, msg: str, offset: int = 0 +): + """Build a checker for single patters. + in comparison to existence_check: + - does not work on lists + - no padding + - excluded topics or exceptions + + TODO: maybe add ignorecase + """ + + return [ + (inst.start() + offset, inst.end() + offset, err, msg, None) + for inst in re.finditer(pattern, text) + ] + + +def detector_50_Cent(text: str) -> tuple[str, float]: + """Determine whether 50 Cent is a topic.""" + keywords = [ + "50 Cent", + "rap", + "hip hop", + "Curtis James Jackson III", + "Curtis Jackson", + "Eminem", + "Dre", + "Get Rich or Die Tryin'", + "G-Unit", + "Street King Immortal", + "In da Club", + "Interscope", + ] + num_keywords = sum(word in text for word in keywords) + return "50 Cent", float(num_keywords > 2) + + +def topics(text: str) -> list[str]: + """Return a list of topics.""" + detectors = [ + detector_50_Cent, + ] + ts = [detector(text) for detector in detectors] + return [t[0] for t in ts if t[1] > 0.95] + + +def context(text, position, level="paragraph"): + """Get sentence or paragraph that surrounds the given position.""" + if level == "sentence": + pass + elif level == "paragraph": + pass + + return "" + + +############################################################################### +# Wrapper ##################################################################### +############################################################################### + + +def limit_results(value: int): + """Decorate a check to truncate error output to a specified limit.""" + + def wrapper(fn): + @functools.wraps(fn) + def wrapped(*args, **kwargs): + return _truncate_errors(fn(*args, **kwargs), value) + + return wrapped + + return wrapper + + +def _truncate_errors( + errors: list[ResultCheck], + limit: int, +) -> list[ResultCheck]: + """If limit was specified, truncate the list of errors. + + Give the total number of times that the error was found elsewhere. + """ + if len(errors) > limit: + start1, end1, err1, msg1, replacements = errors[0] + + if len(errors) == limit + 1: + msg1 += " Found once elsewhere." + else: + msg1 += f" Found {len(errors)} times elsewhere." + + errors = [(start1, end1, err1, msg1, replacements)] + errors[1:limit] + + return errors + + +def ppm_threshold(threshold: float): + """Decorate a check to error if the PPM threshold is surpassed.""" + + def wrapped(fn): + @functools.wraps(fn) + def wrapper(*args, **kwargs): + _len = 0 # neutral element + if "text" in kwargs: + _len = len(kwargs["text"]) + elif len(args) > 0: + _len = len(args[0]) + return _threshold_check(fn(*args, **kwargs), threshold, _len) + + return wrapper + + return wrapped + + +def _threshold_check(errors: list, threshold: float, length: int): + """Check that returns an error if the PPM threshold is surpassed.""" + if length > 0: + errcount = len(errors) + # statistics only work with big numbers, so add some workarounds + if errcount < 2: + return [] + length = max(length, 1000) + + ppm = (errcount / length) * 1e6 + if ppm > threshold: + return [errors[0]] + return [] diff --git a/proselint/checks/airlinese/misc.py b/proselint/checks/airlinese/misc.py index 62e73903f..e0dd3098e 100644 --- a/proselint/checks/airlinese/misc.py +++ b/proselint/checks/airlinese/misc.py @@ -12,11 +12,13 @@ Airlinese. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "airlinese.misc" msg = "'{}' is airlinese." diff --git a/proselint/checks/annotations/misc.py b/proselint/checks/annotations/misc.py index 9a0f3955d..17c231477 100644 --- a/proselint/checks/annotations/misc.py +++ b/proselint/checks/annotations/misc.py @@ -12,16 +12,18 @@ Annotation left in text. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "annotations.misc" msg = "Annotation left in text." - annotations = [ + items = [ "FIXME", "FIX ME", "TODO", @@ -30,5 +32,4 @@ def check(text): "FIX THIS", ] - return existence_check( - text, annotations, err, msg, ignore_case=False, join=True) + return existence_check(text, items, err, msg, ignore_case=False) diff --git a/proselint/checks/archaism/misc.py b/proselint/checks/archaism/misc.py index 7cfe443f9..9faeb918d 100644 --- a/proselint/checks/archaism/misc.py +++ b/proselint/checks/archaism/misc.py @@ -12,11 +12,13 @@ Archaism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "archaism.misc" msg = "'{}' is archaic." @@ -79,4 +81,4 @@ def check(text): # except in the sense “causing an abortion.” ] - return existence_check(text, archaisms, err, msg, join=True) + return existence_check(text, archaisms, err, msg) diff --git a/proselint/checks/cliches/hell.py b/proselint/checks/cliches/hell.py index 0f9199fc3..57e084739 100644 --- a/proselint/checks/cliches/hell.py +++ b/proselint/checks/cliches/hell.py @@ -12,16 +12,19 @@ Never use the phrase 'all hell broke loose'. """ -from proselint.tools import existence_check, max_errors, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results -@max_errors(1) -@memoize -def check_repeated_exclamations(text): + +@limit_results(1) +def check_repeated_exclamations(text: str) -> list[ResultCheck]: """Check the text.""" err = "leonard.hell" msg = "Never use the words 'all hell broke loose'." - regex = r"all hell broke loose" + items = ["all hell broke loose"] - return existence_check(text, [regex], err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/cliches/misc.py b/proselint/checks/cliches/misc.py index b28bbb745..02ae09840 100644 --- a/proselint/checks/cliches/misc.py +++ b/proselint/checks/cliches/misc.py @@ -1,10 +1,12 @@ """Cliches are cliché.""" -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check_cliches_garner(text): + +def check_cliches_garner(text: str) -> list[ResultCheck]: """Check the text. source: Garner's Modern American Usage @@ -13,7 +15,7 @@ def check_cliches_garner(text): err = "cliches.garner" msg = "'{}' is cliché." - cliches = [ + items = [ "a fate worse than death", "alas and alack", "at the end of the day", @@ -94,20 +96,22 @@ def check_cliches_garner(text): "wool pulled over our eyes", "writ large", ] - return existence_check(text, cliches, err, msg, join=True) + return existence_check(text, items, err, msg) -@memoize -def check_cliches_write_good(text): +def check_cliches_write_good_a_to_c(text: str) -> list[ResultCheck]: """Check the text. + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + source: write-good source_url: https://github.com/btford/write-good """ err = "cliches.write_good" msg = "'{}' is a cliché." - cliches = [ + items = [ "a chip off the old block", "a clean slate", "a dark and stormy night", @@ -260,6 +264,23 @@ def check_cliches_write_good(text): "cute as a button", "cute as a puppy", "cuts to the quick", + ] + return existence_check(text, items, err, msg) + + +def check_cliches_write_good_d_to_j(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + + source: write-good + source_url: https://github.com/btford/write-good + """ + err = "cliches.write_good" + msg = "'{}' is a cliché." + + items = [ "dark before the dawn", "day in, day out", "dead as a doornail", @@ -429,6 +450,23 @@ def check_cliches_write_good(text): "just a hop, skip, and a jump", "just the ticket", "justice is blind", + ] + return existence_check(text, items, err, msg) + + +def check_cliches_write_good_k_to_o(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + + source: write-good + source_url: https://github.com/btford/write-good + """ + err = "cliches.write_good" + msg = "'{}' is a cliché." + + items = [ "keep a stiff upper lip", "keep an eye on", "keep it simple, stupid", @@ -581,6 +619,23 @@ def check_cliches_write_good(text): "out on a limb", "over a barrel", "over the hump", + ] + return existence_check(text, items, err, msg) + + +def check_cliches_write_good_p_to_s(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + + source: write-good + source_url: https://github.com/btford/write-good + """ + err = "cliches.write_good" + msg = "'{}' is a cliché." + + items = [ "pain and suffering", "pain in the", "panic button", @@ -700,6 +755,23 @@ def check_cliches_write_good(text): "stuffed shirt", "sweating blood", "sweating bullets", + ] + return existence_check(text, items, err, msg) + + +def check_cliches_write_good_t_to_z(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + + source: write-good + source_url: https://github.com/btford/write-good + """ + err = "cliches.write_good" + msg = "'{}' is a cliché." + + items = [ "take a load off", "take one for the team", "take the bait", @@ -806,12 +878,10 @@ def check_cliches_write_good(text): "young and foolish", "young and vibrant", ] + return existence_check(text, items, err, msg) - return existence_check(text, cliches, err, msg, join=True) - -@memoize -def check_cliches_gnu_diction(text): +def check_cliches_gnu_diction(text: str) -> list[ResultCheck]: """Check the text. source: GNU diction @@ -820,7 +890,7 @@ def check_cliches_gnu_diction(text): err = "cliches.gnu_diction" msg = "'{}' is a cliché." - list = [ + items = [ "a matter of concern", "all things being equal", "as a last resort", @@ -850,4 +920,168 @@ def check_cliches_gnu_diction(text): "you are hereby advised that", ] - return existence_check(text, list, err, msg, join=True, ignore_case=True) + return existence_check(text, items, err, msg, ignore_case=True) + + +def check_cliches_nigel(text: str) -> list[ResultCheck]: + """Check the text. + + source: Nigel Ree's Dictionary of Cliches + source_url: bit.ly/3sL091j + """ + err = "cliches.nigel" + msg = "'{}' is cliché." + + cliches = [ + "abhors a vacuum", + "accident waiting to happen", + "Achilles' heel", + "acquired taste", + "active consideration", + "added bonus", + "agenda-setting", + "agonizing reappraisal", + "alive and well", + "all-time low", + "ample opportunity", + "and now, if you'll excuse me", + "as every schoolboy knows", + "as night follows day", + "as surely as night follows day", + "at daggers drawn", + "at this moment in time", + "at this point in time", + "auspicious occasion", + "back to basics", + "baptism of fire", + "bare necessities", + "bears eloquent testimony", + "beggars all description", + "best-kept secret", + "birth pangs", + "bitter experience", + "blazing inferno", + "brave new world", + "breathing space", + "brought to a satisfactory conclusion", + "caring and sharing", + "case of the tail wagging the dog", + "categorical denial", + "caught up in a sinister web", + "caught up in a sinister plot", + "chequered career", + "chicken and egg situation", + "close proximity", + "closely knit community", + "common decency", + "competitive edge", + "concerted effort", + "concrete jungle", + "cream of the crop", + "crowd pleaser", + "cruellest month", + "crumbling edifice", + "crushing blow", + "crux of the matter", + "crux of the case", + "crux of the problem", + "daunting prospect", + "deafening silence", + "death knell", + "defining moment", + "deserving of serious consideration", + "dire straits", + "dizzy heights", + "down memory lane", + "draconian powers", + "drastic measures", + "dream turned to nightmare", + "dulcet tones", + "earth shattering", + "echo these fears", + "elder statesman", + "end of civilization as we know it", + "existing conditions", + "exposed to the ravages", + "fair sex", + "fate worse than death", + "flights of fancy", + "fly on the wall", + "foregone conclusion", + "further ado", + "gentle sex", + "gone but not forgotten", + "greatly to be desired", + "hair's breadth", + "hand in glove", + "having said that", + "heads must roll", + "heads will roll", + "heady mixture", + "heart of gold", + "hearts of gold", + "heinous crime", + "honeymoon is over for", + "hotly contested", + "humble abode", + "ill-gotten gains", + "illustrious dead", + "in a very real sense", + "in no uncertain terms", + "in the cold light of day", + "in the land of the living", + "in the lap of luxury", + "in this day and age", + "it was a dark and stormy night", + "it was going to be a long night", + "it was just like a fairy tale", + "just deserts", + "killer instinct", + "land of the living", + "light at the end of the tunnel", + "like a dream come true", + "like a fish out of water", + "long hot summer", + "major setback", + "man behind the myth", + "merciful release", + "miles of golden sand", + "nail into the coffin", + "needs no introduction", + "nipped in the bud", + "no pun intended", + "not a pretty sight", + "of paramount importance", + "of which more anon", + "only time will tell", + "rash act", + "ravages of time", + "redeeming feature", + "redeeming features", + "rest is history", + "rooted objection", + "rough old trade", + "rude awakening", + "select few", + "seminal classic", + "seminal work", + "sick and tired", + "sight for sore eyes", + "silent majority", + "sorely missed", + "speculation was rife", + "spending spree", + "thrown into the melting-pot", + "time immemorial", + "tragedy struck when", + "unbridled passion", + "unimpeachable authority", + "usual suspects", + "when all is said and done", + "when the chips are down", + "wind of change", + "yawning gap", + ] + + return existence_check(text, cliches, err, msg, ignore_case=True) + diff --git a/proselint/checks/consistency/spacing.py b/proselint/checks/consistency/spacing.py index 54bece47e..f7813e767 100644 --- a/proselint/checks/consistency/spacing.py +++ b/proselint/checks/consistency/spacing.py @@ -13,11 +13,13 @@ a period, in the same document. """ -from proselint.tools import consistency_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import consistency_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "consistency.spacing" msg = "Inconsistent spacing after period (1 vs. 2 spaces)." diff --git a/proselint/checks/consistency/spelling.py b/proselint/checks/consistency/spelling.py index f710ee9b7..1e3b519a8 100644 --- a/proselint/checks/consistency/spelling.py +++ b/proselint/checks/consistency/spelling.py @@ -19,11 +19,13 @@ is preferred, one thing is always wrong: you mustn't use two different spellings in the same document. """ -from proselint.tools import consistency_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import consistency_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "consistency.spelling" msg = "Inconsistent spelling of '{}' (vs. '{}')." diff --git a/proselint/checks/corporate_speak/misc.py b/proselint/checks/corporate_speak/misc.py index 9c502113f..a37bfe4af 100644 --- a/proselint/checks/corporate_speak/misc.py +++ b/proselint/checks/corporate_speak/misc.py @@ -12,16 +12,18 @@ Avoid these cases of business jargon. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "corporate_speak.misc" msg = "Minimize your use of corporate catchphrases like this one." - list = [ + items = [ "at the end of the day", "back to the drawing board", "hit the ground running", @@ -49,4 +51,4 @@ def check(text): "on my plate", ] - return existence_check(text, list, err, msg, ignore_case=True) + return existence_check(text, items, err, msg, ignore_case=True) diff --git a/proselint/checks/cursing/filth.py b/proselint/checks/cursing/filth.py index 7b07db34a..e20cad869 100644 --- a/proselint/checks/cursing/filth.py +++ b/proselint/checks/cursing/filth.py @@ -12,17 +12,21 @@ Filthy words. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "cursing.filth" - msg = """Nobody ever tells you this as a kid, but you're supposed to avoid - this word.""" + msg = ( + "Nobody ever tells you this as a kid,", + "but you're supposed to avoid this word.", + ) - list = [ + items = [ "shit", "piss", "fuck", @@ -35,4 +39,4 @@ def check(text): "twat", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/cursing/nfl.py b/proselint/checks/cursing/nfl.py index ffac518d6..551935ed8 100644 --- a/proselint/checks/cursing/nfl.py +++ b/proselint/checks/cursing/nfl.py @@ -12,16 +12,18 @@ Words the NFL won't print on a jersey. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check_a_to_e(text: str) -> list[ResultCheck]: """Check the text.""" err = "cursing.nfl" msg = "The NFL won't print this word on a jersey." - list = [ + items = [ "420", "666", "2 on 1", @@ -380,6 +382,16 @@ def check(text): "erection", "evl", "excrement", + ] + return existence_check(text, items, err, msg) + + +def check_f_to_h(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "cursing.nfl" + msg = "The NFL won't print this word on a jersey." + + items = [ "f toyota", "f.i.n.e.", "f.u.c.k.", @@ -597,6 +609,16 @@ def check(text): "hottotrot", "hussy", "hustler", + ] + return existence_check(text, items, err, msg) + + +def check_i_to_p(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "cursing.nfl" + msg = "The NFL won't print this word on a jersey." + + items = [ "i love beer", "i luv beer", "id ten t", @@ -867,6 +889,16 @@ def check(text): "pussypounder", "putt pirate", "pwt", + ] + return existence_check(text, items, err, msg) + + +def check_q_to_z(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "cursing.nfl" + msg = "The NFL won't print this word on a jersey." + + items = [ "queef", "queer", "quickie", @@ -1176,7 +1208,7 @@ def check(text): "wuutang", "xxx", "yellow man", - "yellowman" + "yellowman", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/cursing/nword.py b/proselint/checks/cursing/nword.py index 31aafe3f1..c91d65c05 100644 --- a/proselint/checks/cursing/nword.py +++ b/proselint/checks/cursing/nword.py @@ -12,17 +12,19 @@ Take responsibility with the shitty words you wanna say. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "cursing.nword" msg = "Take responsibility for the shitty words you want to say." - list = [ + items = [ "the n-word", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/dates_times/am_pm.py b/proselint/checks/dates_times/am_pm.py index 75a59b3c9..77a13eb60 100644 --- a/proselint/checks/dates_times/am_pm.py +++ b/proselint/checks/dates_times/am_pm.py @@ -10,21 +10,24 @@ --- """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check_lowercase_periods(text): + +def check_lowercase_periods(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.am_pm.lowercase_periods" msg = "With lowercase letters, the periods are standard." - return existence_check(text, [r"\d{1,2} ?[ap]m"], err, msg, - ignore_case=False) + return existence_check( + text, [r"\d{1,2} ?[ap]m"], err, msg, ignore_case=False + ) -@memoize -def check_spacing(text): +def check_spacing(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.am_pm.spacing" msg = "It's standard to put a space before 'a.m.' or 'p.m.'." @@ -32,27 +35,27 @@ def check_spacing(text): return existence_check(text, [r"\d{1,2}[ap]\.?m\.?"], err, msg) -@memoize -def check_midnight_noon(text): +def check_midnight_noon(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.am_pm.midnight_noon" - msg = ("12 a.m. and 12 p.m. are wrong and confusing." - " Use 'midnight' or 'noon'.") + + msg = ( + "12 a.m. and 12 p.m. are wrong and confusing. Use 'midnight' or 'noon'." + ) return existence_check(text, [r"12 ?[ap]\.?m\.?"], err, msg) -@memoize -def check_redundancy(text): +def check_redundancy(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.am_pm.midnight_noon" - msg = ("'a.m.' is always morning; 'p.m.' is always night.") + msg = "'a.m.' is always morning; 'p.m.' is always night." - list = [ + items = [ r"\d{1,2} ?a\.?m\.? in the morning", r"\d{1,2} ?p\.?m\.? in the evening", r"\d{1,2} ?p\.?m\.? at night", r"\d{1,2} ?p\.?m\.? in the afternoon", ] - return existence_check(text, list, err, msg, join=True) + return existence_check(text, items, err, msg, padding=Pd.disabled) diff --git a/proselint/checks/dates_times/dates.py b/proselint/checks/dates_times/dates.py index 114dba180..f126f71d3 100644 --- a/proselint/checks/dates_times/dates.py +++ b/proselint/checks/dates_times/dates.py @@ -12,57 +12,52 @@ Dates. """ +from __future__ import annotations + import calendar -from proselint.tools import existence_check, memoize +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check_decade_apostrophes_short(text): +def check_decade_apostrophes_short(text: str) -> list[ResultCheck]: """Check the text for dates of the form X0's.""" err = "dates_times.dates" msg = "Apostrophes aren't needed for decades." + items = [r"\d0\'s"] + return existence_check(text, items, err, msg, excluded_topics=["50 Cent"]) - regex = r"\d0\'s" - - return existence_check( - text, [regex], err, msg, excluded_topics=["50 Cent"]) - -@memoize -def check_decade_apostrophes_long(text): +def check_decade_apostrophes_long(text: str) -> list[ResultCheck]: """Check the text for dates of the form XXX0's.""" err = "dates_times.dates" msg = "Apostrophes aren't needed for decades." - - regex = r"\d\d\d0\'s" - return existence_check(text, [regex], err, msg) + items = [r"\d\d\d0\'s"] + return existence_check(text, items, err, msg) -@memoize -def check_dash_and_from(text): +def check_dash_and_from(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.dates" msg = "When specifying a date range, write 'from X to Y'." + items = [r"[fF]rom \d+[^ \t\n\r\f\va-zA-Z0-9_\.]\d+"] + return existence_check(text, items, err, msg) - regex = r"[fF]rom \d+[^ \t\n\r\f\va-zA-Z0-9_\.]\d+" - return existence_check(text, [regex], err, msg) - -def check_month_year_comma(text): +def check_month_year_comma(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.dates" msg = "When specifying a month and year, no comma is needed." - - regex = r"(?:" + "|".join(calendar.month_name[1:]) + r"), \d{3,}" - return existence_check(text, [regex], err, msg) + items = [r"(?:" + "|".join(calendar.month_name[1:]) + r"), \d{3,}"] + # note: strangely month_name[0] is "" + return existence_check(text, items, err, msg, padding=Pd.disabled) -@memoize -def check_month_of_year(text): +def check_month_of_year(text: str) -> list[ResultCheck]: """Check the text.""" err = "dates_times.dates" msg = "When specifying a month and year, 'of' is unnecessary." - - regex = r"(?:" + "|".join(calendar.month_name[1:]) + r") of \d{3,}" - return existence_check(text, [regex], err, msg) + items = [r"(?:" + "|".join(calendar.month_name[1:]) + r") of \d{3,}"] + # note: strangely month_name[0] is "" + return existence_check(text, items, err, msg, padding=Pd.disabled) diff --git a/proselint/checks/hedging/misc.py b/proselint/checks/hedging/misc.py index a433f49bd..31be3c842 100644 --- a/proselint/checks/hedging/misc.py +++ b/proselint/checks/hedging/misc.py @@ -12,11 +12,13 @@ Points out hedging. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "hedging.misc" msg = "Hedging. Just say it." diff --git a/proselint/checks/hyperbole/misc.py b/proselint/checks/hyperbole/misc.py index f351b6d99..bcf29f8d4 100644 --- a/proselint/checks/hyperbole/misc.py +++ b/proselint/checks/hyperbole/misc.py @@ -12,18 +12,21 @@ Hyperbolic language. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "hyperbolic.misc" msg = "'{}' is hyperbolic." words = [ r"[a-z]*[!]{2,}", - r"[a-z]*\?{2,}" + r"[a-z]*\?{2,}", ] - return existence_check(text, words, err, msg) + return existence_check(text, words, err, msg, padding=Pd.disabled) diff --git a/proselint/checks/inprogress/__init__.py b/proselint/checks/inprogress/__init__.py new file mode 100644 index 000000000..0455588fc --- /dev/null +++ b/proselint/checks/inprogress/__init__.py @@ -0,0 +1 @@ +"""InProgress.""" diff --git a/proselint/checks/inprogress/capitalization_errors.py b/proselint/checks/inprogress/capitalization_errors.py index b4c2da679..1ac4fd120 100644 --- a/proselint/checks/inprogress/capitalization_errors.py +++ b/proselint/checks/inprogress/capitalization_errors.py @@ -15,19 +15,21 @@ World War Ii should have correct capitalization. """ -from proselint.tools import blacklist, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "MSC104" msg = "Don't fail to capitalize roman numeral abbreviations." - pwd_regex = " (I(i*)|i*)" + numerals_regex = " (I(i*)|i*)" - password = [ - f"World War{pwd_regex}", + items = [ + f"World War{numerals_regex}", ] - return blacklist(text, password, err, msg) + return existence_check(text, items, err, msg, ignore_case=False) diff --git a/proselint/checks/inprogress/example_check.py b/proselint/checks/inprogress/example_check.py deleted file mode 100644 index 4a39ed339..000000000 --- a/proselint/checks/inprogress/example_check.py +++ /dev/null @@ -1,25 +0,0 @@ -"""First line is always wrong. - ---- -layout: post -source: Nobody -source_url: ??? -title: Firse line is always wrong. -date: 2014-06-10 12:31:19 -categories: writing ---- - -The first line always is always wrong. - -""" -from proselint.tools import reverse - - -def check(text): - """Check the text.""" - error_code = "example.first" - msg = "First line always has an error." - - reverse(text) - - return [(1, 1, error_code, msg)] diff --git a/proselint/checks/inprogress/template_check.py b/proselint/checks/inprogress/template_check.py new file mode 100644 index 000000000..788ba711e --- /dev/null +++ b/proselint/checks/inprogress/template_check.py @@ -0,0 +1,34 @@ +"""Template for new checks + +--- +layout: post +source: Nobody +source_url: ??? +title: First line is always wrong. +date: 2014-06-10 12:31:19 +categories: writing +--- + +Note: + - this is just a small example + - there are more lint-checks available + - check-fn must at least begin with "check" to be found, + so check() & check_xyz() are fine + - don't forget to add unittests and an entry in the base-config + +""" +from __future__ import annotations + +from proselint.checks import ResultCheck +from proselint.checks import existence_check + + +def check(text: str) -> list[ResultCheck]: + """Check the text.""" + error_code = "example.first" + msg = "First line always has an error." + + items = ["first line"] + + # note: + return existence_check(text, items, error_code, msg, ignore_case=False) diff --git a/proselint/checks/jargon/misc.py b/proselint/checks/jargon/misc.py index 5dac520bb..25144df70 100644 --- a/proselint/checks/jargon/misc.py +++ b/proselint/checks/jargon/misc.py @@ -1,22 +1,23 @@ -"""Cliches. +"""Jargon. --- layout: post source: Garner's Modern American Usage source_url: http://bit.ly/1T4alrY -title: a vs. an +title: Jargon date: 2014-06-10 12:31:19 categories: writing --- -Cliches are cliché. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "jargon.misc" msg = "'{}' is jargon. Can you replace it with something more standard?" @@ -30,4 +31,4 @@ def check(text): "disincentivize", ] - return existence_check(text, jargon, err, msg, join=True) + return existence_check(text, jargon, err, msg) diff --git a/proselint/checks/lexical_illusions/misc.py b/proselint/checks/lexical_illusions/misc.py index 04f81e18e..2caa8d0f6 100644 --- a/proselint/checks/lexical_illusions/misc.py +++ b/proselint/checks/lexical_illusions/misc.py @@ -13,16 +13,25 @@ and this happens most often between line breaks. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "lexical_illusions.misc" msg = "There's a lexical illusion here: a word is repeated." regex = r"\b(? list[ResultCheck]: """Flag offensive words based on the GLAAD reference guide.""" err = "glaad.offensive_terms" msg = "Offensive term. Remove it or consider the context." - list = [ + items = [ "fag", "faggot", "dyke", @@ -32,9 +34,9 @@ def check(text): "gay agenda", "transvestite", "homosexual lifestyle", - "gay lifestyle" + "gay lifestyle", # homo - may create false positives without additional context - # FIXME use topic detetor to decide whether "homo" is offensive + # FIXME use topic detector to decide whether "homo" is offensive ] - return existence_check(text, list, err, msg, join=True, ignore_case=False) + return existence_check(text, items, err, msg, ignore_case=False) diff --git a/proselint/checks/lgbtq/terms.py b/proselint/checks/lgbtq/terms.py index de070a8fc..2ed8af34f 100644 --- a/proselint/checks/lgbtq/terms.py +++ b/proselint/checks/lgbtq/terms.py @@ -14,25 +14,27 @@ Associated Press have also adopted this style guide. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest preferred forms given the reference document.""" err = "glaad.terms" msg = "Possibly offensive term. Consider using '{}' instead of '{}'." - list = [ - ["gay man", ["homosexual man"]], - ["gay men", ["homosexual men"]], - ["lesbian", ["homosexual woman"]], - ["lesbians", ["homosexual women"]], - ["gay people", ["homosexual people"]], - ["gay couple", ["homosexual couple"]], + items = [ + ["gay man", ["homosexual man"]], + ["gay men", ["homosexual men"]], + ["lesbian", ["homosexual woman"]], + ["lesbians", ["homosexual women"]], + ["gay people", ["homosexual people"]], + ["gay couple", ["homosexual couple"]], ["sexual orientation", ["sexual preference"]], - ["openly gay", ["admitted homosexual", "avowed homosexual"]], - ["equal rights", ["special rights"]] - ] + ["openly gay", ["admitted homosexual", "avowed homosexual"]], + ["equal rights", ["special rights"]], + ] - return preferred_forms_check(text, list, err, msg, ignore_case=False) + return preferred_forms_check(text, items, err, msg, ignore_case=False) diff --git a/proselint/checks/links/broken.py b/proselint/checks/links/broken.py index d9da727ec..1fe29c75e 100644 --- a/proselint/checks/links/broken.py +++ b/proselint/checks/links/broken.py @@ -12,15 +12,17 @@ Check that links are not broken. """ +from __future__ import annotations + import re -import urllib.request as urllib_request # for Python 3 -from socket import error as SocketError +import urllib.request as ulr +from typing import TYPE_CHECKING -from proselint.tools import memoize +if TYPE_CHECKING: + from proselint.checks import ResultCheck -@memoize -def check(text): +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "links.valid" msg = "Broken link: {}" @@ -30,9 +32,11 @@ def check(text): |[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+ |(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\) |[^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019\u21a9]))""", - re.U | re.X) - errors = [] + re.UNICODE | re.VERBOSE, + ) + + results: list[ResultCheck] = [] for m in re.finditer(regex, text): url = m.group(0).strip() @@ -40,20 +44,19 @@ def check(text): url = "http://" + url if is_broken_link(url): - errors.append((m.start(), m.end(), err, msg.format(url), None)) + results.append((m.start(), m.end(), err, msg.format(url), None)) + # TODO: this should probably be rate limited (10/s)? - return errors + return results -@memoize -def is_broken_link(url): +def is_broken_link(url: str) -> bool: """Determine whether the link returns a 404 error.""" try: - request = urllib_request.Request( - url, headers={'User-Agent': 'Mozilla/5.0'}) - urllib_request.urlopen(request).read() + request = ulr.Request(url, headers={"User-Agent": "Mozilla/5.0"}) # noqa: S310 + ulr.urlopen(request).read() # noqa: S310 return False - except urllib_request.URLError: + except ulr.URLError: return True - except SocketError: + except OSError: return True diff --git a/proselint/checks/malapropisms/misc.py b/proselint/checks/malapropisms/misc.py index 57ae14e51..a56ea6627 100644 --- a/proselint/checks/malapropisms/misc.py +++ b/proselint/checks/malapropisms/misc.py @@ -12,11 +12,13 @@ Archaism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "malapropisms.misc" msg = "'{}' is a malapropism." diff --git a/proselint/checks/misc/apologizing.py b/proselint/checks/misc/apologizing.py index 2eb4d35b2..e1807fb8d 100644 --- a/proselint/checks/misc/apologizing.py +++ b/proselint/checks/misc/apologizing.py @@ -12,11 +12,13 @@ Points out excessive apologizing. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "pinker.apologizing" msg = "Excessive apologizing." diff --git a/proselint/checks/misc/back_formations.py b/proselint/checks/misc/back_formations.py index 454bae88c..e1147d9a0 100644 --- a/proselint/checks/misc/back_formations.py +++ b/proselint/checks/misc/back_formations.py @@ -12,17 +12,19 @@ Back-formations. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.back_formations" msg = "Back-formation. '{}' is the preferred form." - list = [ - ["improper", ["improprietous"]], + items = [ + ["improper", ["improprietous"]], ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/misc/bureaucratese.py b/proselint/checks/misc/bureaucratese.py index 89d9ad016..f4dc831d6 100644 --- a/proselint/checks/misc/bureaucratese.py +++ b/proselint/checks/misc/bureaucratese.py @@ -12,11 +12,13 @@ Bureaucratese. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.bureaucratese" msg = "'{}' is bureaucratese." @@ -26,4 +28,4 @@ def check(text): "meets with your approval", ] - return existence_check(text, bureaucratese, err, msg, join=True) + return existence_check(text, bureaucratese, err, msg) diff --git a/proselint/checks/misc/but.py b/proselint/checks/misc/but.py index 2ec8518eb..80edbf786 100644 --- a/proselint/checks/misc/but.py +++ b/proselint/checks/misc/but.py @@ -1,10 +1,10 @@ """Don't start a paragraph with 'But'. --- -layout: -source: Justin Jungé +layout: post +source: Justin Jungé source_url: -title: +title: paragraph-start-with-but date: 2016-03-10 12:31:19 categories: writing --- @@ -12,14 +12,19 @@ Paragraphs should not start with certain bad words. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Do not start a paragraph with a 'But'.""" err = "misc.but" msg = "No paragraph should start with a 'But'." regex = r"(^|([\n\r]+))(\s*)But" - return existence_check(text, [regex], err, msg, ignore_case=False) + return existence_check( + text, [regex], err, msg, ignore_case=False, padding=Pd.disabled + ) diff --git a/proselint/checks/misc/capitalization.py b/proselint/checks/misc/capitalization.py index befc57d78..4ad769c09 100644 --- a/proselint/checks/misc/capitalization.py +++ b/proselint/checks/misc/capitalization.py @@ -12,81 +12,79 @@ Incorrect capitalization. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" - err = "misc.captalization" + err = "misc.capitalization" msg = "Incorrect capitalization. '{}' is the preferred form." - list = [ - ["Stone Age", ["stone age"]], - ["space age", ["Space Age"]], - ["the American West", ["the American west"]], - ["Mother Nature", ["mother nature"]], + items = [ + ["Stone Age", ["stone age"]], + ["space age", ["Space Age"]], + ["the American West", ["the American west"]], + ["Mother Nature", ["mother nature"]], ] - return preferred_forms_check(text, list, err, msg, ignore_case=False) - + return preferred_forms_check(text, items, err, msg, ignore_case=False) -# @memoize -# def check_seasons(text): -# """Suggest the preferred forms.""" -# err = "MAU102" -# msg = "Seasons shouldn't be capitalized. '{}' is the preferred form." -# list = [ -# # ["winter", ["Winter"]], -# # ["fall", ["Fall"]], -# # ["summer", ["Summer"]], -# # ["spring", ["Spring"]], -# ] +def check_seasons(text: str) -> list[ResultCheck]: + """Suggest the preferred forms.""" + err = "MAU102" + msg = "Seasons shouldn't be capitalized. '{}' is the preferred form." + items = [ + ["winter", ["Winter"]], + # ["fall", ["Fall"]], + ["summer", ["Summer"]], + ["spring", ["Spring"]], + ] -# return preferred_forms_check(text, list, err, msg, ignore_case=False) + return preferred_forms_check(text, items, err, msg, ignore_case=False) -@memoize -def check_months(text): +def check_months(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "MAU102" msg = "Months should be capitalized. '{}' is the preferred form." - list = [ - - ["January", ["january"]], - ["February", ["february"]], + items = [ + ["January", ["january"]], + ["February", ["february"]], # ["March", ["march"]], - ["April", ["april"]], + ["April", ["april"]], # ["May", ["may"]], - ["June", ["june"]], - ["July", ["july"]], - ["August", ["august"]], - ["September", ["september"]], - ["October", ["october"]], - ["November", ["november"]], - ["December", ["december"]], + ["June", ["june"]], + ["July", ["july"]], + ["August", ["august"]], + ["September", ["september"]], + ["October", ["october"]], + ["November", ["november"]], + ["December", ["december"]], ] + # TODO: deal better with collisions / false positives + # i.e. "(you|he|...) may proceed" follows a pattern - return preferred_forms_check(text, list, err, msg, ignore_case=False) + return preferred_forms_check(text, items, err, msg, ignore_case=False) -@memoize -def check_days(text): +def check_days(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "MAU102" msg = "Days of the week should be capitalized. '{}' is the preferred form." - list = [ - - ["Monday", ["monday"]], - ["Tuesday", ["tuesday"]], - ["Wednesday", ["wednesday"]], - ["Thursday", ["thursday"]], - ["Friday", ["friday"]], - ["Saturday", ["saturday"]], - ["Sunday", ["sunday"]], + items = [ + ["Monday", ["monday"]], + ["Tuesday", ["tuesday"]], + ["Wednesday", ["wednesday"]], + ["Thursday", ["thursday"]], + ["Friday", ["friday"]], + ["Saturday", ["saturday"]], + ["Sunday", ["sunday"]], ] - return preferred_forms_check(text, list, err, msg, ignore_case=False) + return preferred_forms_check(text, items, err, msg, ignore_case=False) diff --git a/proselint/checks/misc/chatspeak.py b/proselint/checks/misc/chatspeak.py index 3ee7cbeb6..8f105338f 100644 --- a/proselint/checks/misc/chatspeak.py +++ b/proselint/checks/misc/chatspeak.py @@ -12,11 +12,13 @@ Chatspeak. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.chatspeak" msg = "'{}' is chatspeak. Write it out." @@ -44,7 +46,7 @@ def check(text): "THNX", "THX", "TTYL", - "XOXO" + "XOXO", ] return existence_check(text, words, err, msg) diff --git a/proselint/checks/misc/commercialese.py b/proselint/checks/misc/commercialese.py index 4ed63826e..91df94d22 100644 --- a/proselint/checks/misc/commercialese.py +++ b/proselint/checks/misc/commercialese.py @@ -12,11 +12,14 @@ Commercialese. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.commercialese" msg = "'{}' is commercialese." @@ -29,25 +32,40 @@ def check(text): "further to yours of", "further to your letter", "in regard to", - r"inst\.", "in the amount of", "of even date", "pending receipt of", "please be advised that", "please return same", "pleasure of a reply", - r"prox\.", "pursuant to your request", "regarding the matter", "regret to inform", "thanking you in advance", "the undersigned", "this acknowledges your letter", - r"ult\." "we are pleased to note", "with regard to", "your favor has come to hand", - "yours of even date" + "yours of even date", + ] + + return existence_check(text, commercialese, err, msg) + + +def check_abbrev(text: str) -> list[ResultCheck]: + """ + source: https://www.ourcivilisation.com/smartboard/shop/gowerse/abc/cmmrcls.htm + """ + err = "misc.commercialese.abbreviations" + msg = "'{}' is commercialese. Depending on audience switch to {}" + + items = [ + ["this month", [r"inst\."]], + ["next month", [r"prox\."]], + ["last month", [r"ult\."]], + ["cost, insurance, freight", [r"c\.i\.f\."]], + ["Free On Board", [r"f\.o\.b\."]], ] - return existence_check(text, commercialese, err, msg, join=True) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/misc/composition.py b/proselint/checks/misc/composition.py index 9f8b74412..ed36c6c4a 100644 --- a/proselint/checks/misc/composition.py +++ b/proselint/checks/misc/composition.py @@ -45,40 +45,43 @@ 11. Place the emphatic word of a sentence at the end. * MDPNB: Principle of recency. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "strunk_white.composition" msg = "Try '{}' instead of '{}'." bad_forms = [ # Put statements in positive form - ["dishonest", ["not honest"]], - ["trifling", ["not important"]], - ["forgot", ["did not remember"]], - ["ignored", ["did not pay (any )?attention to"]], - ["distrusted", ["did not have much confidence in"]], - + ["dishonest", ["not honest"]], + ["trifling", ["not important"]], + ["forgot", ["did not remember"]], + ["ignored", ["did not pay (any )?attention to"]], + ["distrusted", ["did not have much confidence in"]], # Omit needless words - ["whether", ["the question as to whether"]], - ["no doubt", ["there is no doubt but that"]], - ["used for fuel", ["used for fuel purposes"]], - ["he", ["he is a man who"]], - ["hastily", ["in a hasty manner"]], - ["this subject", ["this is a subject that"]], - ["Her story is strange.", ["Her story is a strange one."]], - ["because", ["the reason why is that"]], - ["because / since", ["owing to the fact that"]], - ["although / though", ["in spite of the fact that"]], - ["remind you / notify you", - ["call your attention to the fact that"]], - ["I did not know that / I was unaware that", - ["I was unaware of the fact that"]], - ["his failure", ["the fact that he had not succeeded"]], - ["my arrival", ["the fact that i had arrived"]] + ["whether", ["the question as to whether"]], + ["no doubt", ["there is no doubt but that"]], + ["used for fuel", ["used for fuel purposes"]], + ["he", ["he is a man who"]], + ["hastily", ["in a hasty manner"]], + ["this subject", ["this is a subject that"]], + ["Her story is strange.", ["Her story is a strange one."]], + ["because", ["the reason why is that"]], + ["because / since", ["owing to the fact that"]], + ["although / though", ["in spite of the fact that"]], + ["remind you / notify you", ["call your attention to the fact that"]], + [ + "I did not know that / I was unaware that", + ["I was unaware of the fact that"], + ], + ["failed", ["(had )?not succeeded"]], + ["fail", ["not succeed"]], + ["my arrival", ["the fact that i had arrived"]], ] return preferred_forms_check(text, bad_forms, err, msg) diff --git a/proselint/checks/misc/currency.py b/proselint/checks/misc/currency.py index 8ef85601f..e14f451b5 100644 --- a/proselint/checks/misc/currency.py +++ b/proselint/checks/misc/currency.py @@ -12,17 +12,19 @@ Symbols. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.currency" msg = "Incorrect use of symbols in {}." symbols = [ - r"\$[\d]* ?(?:dollars|usd|us dollars)" + r"\$[\d]* ?(?:dollars|usd|us dollars)", ] return existence_check(text, symbols, err, msg) diff --git a/proselint/checks/misc/debased.py b/proselint/checks/misc/debased.py index 529ed5206..7e82a0fd5 100644 --- a/proselint/checks/misc/debased.py +++ b/proselint/checks/misc/debased.py @@ -12,20 +12,22 @@ Too much yelling. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.debased" - msg = "Bad usage, debased language, a continuous temptation." + msg = "Debased language is a continuous temptation." - list = [ + items = [ "a not unjustifiable assumption", "leaves much to be desired", "would serve no purpose", "a consideration which we should do well to bear in mind", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/misc/false_plurals.py b/proselint/checks/misc/false_plurals.py index 33963ca58..371fdab41 100644 --- a/proselint/checks/misc/false_plurals.py +++ b/proselint/checks/misc/false_plurals.py @@ -12,11 +12,14 @@ Using the incorrect form of the plural. """ -from proselint.tools import existence_check, memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.false_plurals.examples" msg = "The plural is {}" @@ -29,8 +32,7 @@ def check(text): return preferred_forms_check(text, preferences, err, msg) -@memoize -def check_kudos(text): +def check_kudos(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.false_plurals.kudos" msg = "Kudos is singular." diff --git a/proselint/checks/misc/greylist.py b/proselint/checks/misc/greylist.py index 6a66c6c93..dc0a3938b 100644 --- a/proselint/checks/misc/greylist.py +++ b/proselint/checks/misc/greylist.py @@ -10,41 +10,52 @@ --- Strunk & White say: +1. Form the possessive singular of nouns by adding 's. +2. In a series of three or more terms with a conjunction, use a comma after +each term except the last. +3. Enclose parenthetic expressions between commas. ("This rule is difficult to +apply.") +4. Place a comma before a conjunction introducing an independent clause. +5. Do not join independent clauses with a comma; use a semi-colon. Or a period. +6. Do not break sentences in two. Do not use periods for the role of commas. +7. Use a colon after an independent clause if you introduce: a list of +particulars, an appositive, an application, or an illustrative quotation. +8. Use a dash to set off an abrupt break or interruption and to announce a +long appositive or summary. +9. The number of the subject determines the number of the verb. (MDPNB: This +will require nltk & syntactic parsing) +10. Use the proper case(grammatical gender) of pronouns. + * MDPNB: hard case: "Give this work to whoever looks idle." `whoever looks + idle` is the object of `to`. +11. A participial phrase at the beginning of a sentence must refer to the +grammatical subject. """ -import re +from __future__ import annotations -from proselint.tools import memoize +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "strunk_white.greylist" msg = "Use of '{}'. {}" - bad_words = [ - "obviously", - "utilize" - ] - - explanations = { - "obviously": - "This is obviously an inadvisable word to use.", - "utilize": - r"Do you know anyone who *needs* to utilize the word utilize?" + bad_words = { + "obviously": "This is obviously an inadvisable word to use.", + "utilize": "Do you know anyone who needs to utilize the word utilize?", } - errors = [] - for word in bad_words: - occ = [m for m in re.finditer(word, text.lower())] - for o in occ: - errors.append(( - o.start(), - o.end(), + results = [] + for word, expl in bad_words.items(): + results.extend( + existence_check( + text, + [word], err, - msg.format(word, explanations[word]), - None)) - - return errors + msg.format(word, expl), + ), + ) + return results diff --git a/proselint/checks/misc/illogic.py b/proselint/checks/misc/illogic.py index 75c436cf1..3493102e7 100644 --- a/proselint/checks/misc/illogic.py +++ b/proselint/checks/misc/illogic.py @@ -12,11 +12,13 @@ Archaism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.illogic" msg = "'{}' is illogical." @@ -36,24 +38,17 @@ def check(text): return existence_check(text, illogics, err, msg, offset=1) -@memoize -def check_coin_a_phrase_from(text): +def check_coin_a_phrase_from(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.illogic.coin" msg = "You can't coin an existing phrase. Did you mean 'borrow'?" - - regex = "to coin a phrase from" - - return existence_check(text, [regex], err, msg, offset=1) + items = ["to coin a phrase from"] + return existence_check(text, items, err, msg, offset=1) -@memoize -def check_without_your_collusion(text): +def check_without_your_collusion(text: str) -> list[ResultCheck]: """Check the textself.""" err = "misc.illogic.collusion" msg = "It's impossible to defraud yourself. Try 'aquiescence'." - - regex = "without your collusion" - - return existence_check( - text, [regex], err, msg, require_padding=False, offset=-1) + items = ["without your collusion"] + return existence_check(text, items, err, msg, offset=-1) diff --git a/proselint/checks/misc/inferior_superior.py b/proselint/checks/misc/inferior_superior.py index 1ddabc822..4de06762e 100644 --- a/proselint/checks/misc/inferior_superior.py +++ b/proselint/checks/misc/inferior_superior.py @@ -12,18 +12,20 @@ Corrects 'inferior/superior than' to 'inferior/superior to'. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.inferior_superior" msg = "'Inferior' and 'superior' are not true comparatives. Use '{}'." preferences = [ - ["inferior to", ["inferior than"]], - ["superior to", ["superior than"]], + ["inferior to", ["inferior than"]], + ["superior to", ["superior than"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/misc/institution_name.py b/proselint/checks/misc/institution_name.py index 042aec31e..191c18d95 100644 --- a/proselint/checks/misc/institution_name.py +++ b/proselint/checks/misc/institution_name.py @@ -12,11 +12,13 @@ Institution names. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check_vtech(text): + +def check_vtech(text: str) -> list[ResultCheck]: """Suggest the correct name. source: Virginia Tech Division of Student Affairs @@ -26,7 +28,9 @@ def check_vtech(text): msg = "Incorrect name. Use '{}' instead of '{}'." institution = [ - ["Virginia Polytechnic Institute and State University", - ["Virginia Polytechnic and State University"]], + [ + "Virginia Polytechnic Institute and State University", + ["Virginia Polytechnic and State University"], + ], ] return preferred_forms_check(text, institution, err, msg) diff --git a/proselint/checks/misc/latin.py b/proselint/checks/misc/latin.py index 6e8fe8179..3caf1e041 100644 --- a/proselint/checks/misc/latin.py +++ b/proselint/checks/misc/latin.py @@ -12,20 +12,22 @@ Back-formations. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "pinker.latin" msg = "Use English. '{}' is the preferred form." - list = [ - ["other things being equal", ["ceteris paribus"]], - ["among other things", ["inter alia"]], - ["in and of itself", ["simpliciter"]], + items = [ + ["other things being equal", ["ceteris paribus"]], + ["among other things", ["inter alia"]], + ["in and of itself", ["simpliciter"]], ["having made the necessary changes", ["mutatis mutandis"]], ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/misc/many_a.py b/proselint/checks/misc/many_a.py index 7192a5436..af0ba83e7 100644 --- a/proselint/checks/misc/many_a.py +++ b/proselint/checks/misc/many_a.py @@ -12,19 +12,21 @@ The idiom 'many a' requires a singular verb. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.many_a" msg = "'many a' requires a singular verb." preferences = [ - ["is many a", ["are many a"]], - ["has been many a", ["have been many a"]], - ["was many a", ["were many a"]], + ["is many a", ["are many a"]], + ["has been many a", ["have been many a"]], + ["was many a", ["were many a"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/misc/metaconcepts.py b/proselint/checks/misc/metaconcepts.py deleted file mode 100644 index 7d12f39bc..000000000 --- a/proselint/checks/misc/metaconcepts.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Metaconcepts. - ---- -layout: post -source: Pinker's book on writing -source_url: ??? -title: misuse of scare quotes -date: 2014-06-10 12:31:19 -categories: writing ---- - -Point out misuse of scare quotes. - -""" -# from proselint.tools import memoize, existence_check - - -# @memoize -# def check(text): -# """Suggest the preferred forms.""" -# err = "pinker.metaconcepts" -# msg = "Misuse of 'scare quotes'. Delete them." - -# narcissism = [ -# "the 'take-home message'", -# ] - -# return existence_check(text, narcissism, err, msg) diff --git a/proselint/checks/misc/metadiscourse.py b/proselint/checks/misc/metadiscourse.py index 477a5812e..3a6830ae3 100644 --- a/proselint/checks/misc/metadiscourse.py +++ b/proselint/checks/misc/metadiscourse.py @@ -12,11 +12,13 @@ Points out metadiscourse. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "pinker.metadiscourse" msg = "Excessive metadiscourse." diff --git a/proselint/checks/misc/narcissism.py b/proselint/checks/misc/narcissism.py index 4ad9874e5..1c5279fc6 100644 --- a/proselint/checks/misc/narcissism.py +++ b/proselint/checks/misc/narcissism.py @@ -12,11 +12,13 @@ Points out academic narcissism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "pinker.narcissism" msg = "Professional narcissism. Talk about the subject, not its study." diff --git a/proselint/checks/misc/not_guilty.py b/proselint/checks/misc/not_guilty.py index d393a1a0e..cd8afb431 100644 --- a/proselint/checks/misc/not_guilty.py +++ b/proselint/checks/misc/not_guilty.py @@ -22,11 +22,13 @@ """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.not_guilty" msg = "'not guilty beyond a reasonable doubt' is an ambiguous phrasing." diff --git a/proselint/checks/misc/phrasal_adjectives.py b/proselint/checks/misc/phrasal_adjectives.py index ba57341a8..6e97bff32 100644 --- a/proselint/checks/misc/phrasal_adjectives.py +++ b/proselint/checks/misc/phrasal_adjectives.py @@ -12,30 +12,35 @@ Phrasal adjectives. """ -from proselint.tools import existence_check, memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import preferred_forms_check -@memoize -def check_ly(text): + +def check_ly(text: str) -> list[ResultCheck]: """Check the text.""" err = "garner.phrasal_adjectives.ly" - msg = """No hyphen is necessary in phrasal adjectives with an adverb - ending in -ly, unless the -ly adverb is part of a longer - phrase""" + msg = ( + "No hyphen is necessary in phrasal adjectives with an adverb " + "ending in -ly, unless the -ly adverb is part of a longer phrase" + ) - regex = r"\s[^\s-]+ly-" + items = [r"\s[^\s-]+ly-"] - return existence_check(text, [regex], err, msg, - require_padding=False, offset=-1) + return existence_check( + text, items, err, msg, padding=Pd.disabled, offset=-1 + ) -@memoize -def check(text): +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "garner.phrasal_adjectives.examples" - msg = """Hyphenate '{1}', a phrasal adjective, as '{0}'.""" + msg = "Hyphenate '{1}', a phrasal adjective, as '{0}'." - list = [ + items = [ ["across-the-board discounts", ["across the board discounts"]], ["acute-care treatment", ["acute care treatment"]], ["agreed-upon answer", ["agreed upon answer"]], @@ -61,8 +66,10 @@ def check(text): ["hit-and-run statute", ["hit and run statute"]], ["HIV-negative person", ["HIV negative person"]], ["HIV-positive person", ["HIV positive person"]], - ["information-technology personnel", - ["information technology personnel"]], + [ + "information-technology personnel", + ["information technology personnel"], + ], ["intellectual-property rights", ["intellectual property rights"]], ["interest-group pressures", ["interest group pressures"]], ["joint-stock company", ["joint stock company"]], @@ -122,18 +129,19 @@ def check(text): ["stained-glass window", ["stained glass window"]], ["free-range chicken", ["free range chicken"]], ["free-range poultry", ["free range poultry"]], - ["non-profit-making organization", - ["non profit making organization", - "non-profit making organization", - "non profit-making organization"]], - - + [ + "non-profit-making organization", + [ + "non profit making organization", + "non-profit making organization", + "non profit-making organization", + ], + ], # Harmony ["three-part harmony", ["three part harmony"]], ["four-part harmony", ["four part harmony"]], ["six-part harmony", ["six part harmony"]], ["eight-part harmony", ["eight part harmony"]], - # Losses and gains. ["first-quarter loss", ["first quarter loss"]], ["second-quarter loss", ["second quarter loss"]], @@ -143,7 +151,6 @@ def check(text): ["second-quarter gain", ["second quarter gain"]], ["third-quarter gain", ["third quarter gain"]], ["fourth-quarter gain", ["fourth quarter gain"]], - ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/misc/preferred_forms.py b/proselint/checks/misc/preferred_forms.py index c2456e0c1..bcb463692 100644 --- a/proselint/checks/misc/preferred_forms.py +++ b/proselint/checks/misc/preferred_forms.py @@ -12,165 +12,160 @@ Points out preferred forms. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "garner.preferred_forms" msg = "'{}' is the preferred form." preferences = [ - # Obsolete words - ["imprimatur", ["imprimature"]], - + ["imprimatur", ["imprimature"]], # Proper nouns - ["Halloween", ["haloween", "hallowe'en"]], - ["Khrushchev", ["Khruschev", "Kruschev"]], - ["Ku Klux Klan", ["Klu Klux Klan"]], - ["Pontius Pilate", ["Pontius Pilot"]], - + ["Halloween", ["haloween", "hallowe'en"]], + ["Khrushchev", ["Khruschev", "Kruschev"]], + ["Ku Klux Klan", ["Klu Klux Klan"]], + ["Pontius Pilate", ["Pontius Pilot"]], # Plurals - ["hippopotamuses", ["hippopotami"]], - ["manifestos", ["manifesti"]], + ["hippopotamuses", ["hippopotami"]], + ["manifestos", ["manifesti"]], # ["matrixes", ["matrices"]], - ["mongooses", ["mongeese"]], - ["narcissi", ["narcissuses"]], - ["retinas", ["retinae"]], - ["sopranos", ["soprani"]], - ["titmice", ["titmouses"]], - + ["mongooses", ["mongeese"]], + ["narcissi", ["narcissuses"]], + ["retinas", ["retinae"]], + ["sopranos", ["soprani"]], + ["titmice", ["titmouses"]], # Hyphenated words - ["long-standing", ["longstanding"]], - ["sans serif", ["sans-serif", "sanserif"]], - ["tortfeasor", ["tort feasor", "tort-feasor"]], - ["transship", ["tranship", "trans-ship"]], - ["transshipped", ["transhipped", "trans-shipped"]], - ["transshipping", ["transhipping", "trans-shipping"]], - ["non sequitur", ["non-sequitur"]], - + ["long-standing", ["longstanding"]], + ["sans serif", ["sans-serif", "sanserif"]], + ["tortfeasor", ["tort feasor", "tort-feasor"]], + ["transship", ["tranship", "trans-ship"]], + ["transshipped", ["transhipped", "trans-shipped"]], + ["transshipping", ["transhipping", "trans-shipping"]], + ["non sequitur", ["non-sequitur"]], # Misc - ["attitude", ["mental attitude"]], - ["Chief Justice of the United States", - ["Chief Justice of the United States Supreme Court", - "Chief Justice of the Supreme Court of the United States."]], - ["chitterlings", ["chitlings", "chitlins"]], - ["combustion engine", ["combustible engine"]], + ["attitude", ["mental attitude"]], + [ + "Chief Justice of the United States", + [ + "Chief Justice of the United States Supreme Court", + "Chief Justice of the Supreme Court of the United States.", + ], + ], + ["chitterlings", ["chitlings", "chitlins"]], + ["combustion engine", ["combustible engine"]], ["during / throughout", ["for the duration of"]], - ["foreclose on", ["foreclose againt"]], - ["friend in common", ["mutual friend"]], - ["in regard to", ["in regards to"]], - ["infectious", ["infectuous"]], - ["inferable", ["inferrable", "inferrible"]], - ["knowing that", ["in light of the fact that"]], - ["lanyard", ["laniard"]], - ["largess", ["largesse"]], - ["lasagna", ["lasagne"]], - ["leery", ["leary"]], - ["lend me her", ["loan me her"]], - ["lend me his", ["loan me his"]], - ["lend me their", ["loan me their"]], - ["lend me your", ["loan me your"]], - ["lent me her", ["loaned me her"]], - ["lent me his", ["loaned me his"]], - ["lent me their", ["loaned me their"]], - ["lent me your", ["loaned me your"]], - ["linguist", ["linguistician"]], - ["matzo-ball", ["matzoh-ball", - "matza-ball", - "matzah-ball", - "matsah-ball"]], - ["mayoralty", ["mayorality"]], - ["mealy-mouthed", ["mealymouthed"]], - ["mean-spirited", ["meanspirited"]], - ["midwifed", ["midwived"]], - ["moniker", ["monicker"]], - ["musical revue", ["musical review"]], - ["mustache", ["moustache"]], - ["nonplussed", ["nonplused"]], - ["nonplussing", ["nonplusing"]], - ["non sequitur", ["nonsequitur"]], - ["not nearly as", ["nowhere near as"]], - ["off", ["off of"]], - ["podiatrist", ["chiropodist"]], - ["podiatry", ["chiropody"]], - ["shoo-in", ["shoe-in"]], - ["suicide", ["suicide victim"]], - ["the highway median", ["the highway medium"]], - ["vaipidity", ["vapidness"]], - ["weather vane", ["weather vein", "weather vain"]], - ["with regard to", ["with regards to"]], - + ["foreclose on", ["foreclose againt"]], + ["friend in common", ["mutual friend"]], + ["in regard to", ["in regards to"]], + ["infectious", ["infectuous"]], + ["inferable", ["inferrable", "inferrible"]], + ["knowing that", ["in light of the fact that"]], + ["lanyard", ["laniard"]], + ["largess", ["largesse"]], + ["lasagna", ["lasagne"]], + ["leery", ["leary"]], + ["lend me her", ["loan me her"]], + ["lend me his", ["loan me his"]], + ["lend me their", ["loan me their"]], + ["lend me your", ["loan me your"]], + ["lent me her", ["loaned me her"]], + ["lent me his", ["loaned me his"]], + ["lent me their", ["loaned me their"]], + ["lent me your", ["loaned me your"]], + ["linguist", ["linguistician"]], + [ + "matzo-ball", + ["matzoh-ball", "matza-ball", "matzah-ball", "matsah-ball"], + ], + ["mayoralty", ["mayorality"]], + ["mealy-mouthed", ["mealymouthed"]], + ["mean-spirited", ["meanspirited"]], + ["midwifed", ["midwived"]], + ["moniker", ["monicker"]], + ["musical revue", ["musical review"]], + ["mustache", ["moustache"]], + ["nonplussed", ["nonplused"]], + ["nonplussing", ["nonplusing"]], + ["non sequitur", ["nonsequitur"]], + ["not nearly as", ["nowhere near as"]], + ["off", ["off of"]], + ["podiatrist", ["chiropodist"]], + ["podiatry", ["chiropody"]], + ["shoo-in", ["shoe-in"]], + ["suicide", ["suicide victim"]], + ["the highway median", ["the highway medium"]], + ["vaipidity", ["vapidness"]], + ["weather vane", ["weather vein", "weather vain"]], + ["with regard to", ["with regards to"]], # Idioms - ["a couple of people", ["a couple people"]], - ["all the time", ["all of the time"]], - ["as follows", ["as follow"]], - ["bulk large", ["bulk largely"]], - ["burying the lede", ["burying the lead"]], - ["came to naught", ["came to nought"]], - ["come off it", ["come off of it"]], + ["a couple of people", ["a couple people"]], + ["all the time", ["all of the time"]], + ["as follows", ["as follow"]], + ["bulk large", ["bulk largely"]], + ["burying the lede", ["burying the lead"]], + ["came to naught", ["came to nought"]], + ["come off it", ["come off of it"]], ["corroborating evidence", ["corroborative evidence"]], - ["dear departed", ["dearly departed"]], - ["default on a loan", ["default to a loan"]], - ["draw an inference", ["make an inference"]], - ["in the meantime", ["in the meanwhile"]], - ["long distances", ["lengthy distances"]], - ["madding crowd", ["maddening crowd"]], - ["Magna Carta", ["Magna Charta"]], + ["dear departed", ["dearly departed"]], + ["default on a loan", ["default to a loan"]], + ["draw an inference", ["make an inference"]], + ["in the meantime", ["in the meanwhile"]], + ["long distances", ["lengthy distances"]], + ["madding crowd", ["maddening crowd"]], + ["Magna Carta", ["Magna Charta"]], ["marriage of convenience", ["mariage de convenance"]], - ["Meanwhile,", ["Meantime,"]], - ["Midwest", ["Middle West"]], - ["Midwestern", ["Middle Western"]], - ["modi operandi", ["modes of operandi"]], - ["modus operandi", ["mode of operandi"]], - ["motion seconded", ["notion seconded"]], - ["mucous membranes", ["mucus membranes"]], - ["must pass muster", ["must past muster"]], - ["neck-and-neck", ["neck-in-neck"]], - ["no-holds-barred", ["no-holes-barred"]], - ["oil magnate", ["oil magnet"]], - ["punch up the lede", ["punch up the lead"]], - ["railroad magnate", ["railroad magnet"]], + ["Meanwhile,", ["Meantime,"]], + ["Midwest", ["Middle West"]], + ["Midwestern", ["Middle Western"]], + ["modi operandi", ["modes of operandi"]], + ["modus operandi", ["mode of operandi"]], + ["motion seconded", ["notion seconded"]], + ["mucous membranes", ["mucus membranes"]], + ["must pass muster", ["must past muster"]], + ["neck-and-neck", ["neck-in-neck"]], + ["no-holds-barred", ["no-holes-barred"]], + ["oil magnate", ["oil magnet"]], + ["punch up the lede", ["punch up the lead"]], + ["railroad magnate", ["railroad magnet"]], ["seconded the motion", ["seconded the notion"]], ["statute of limitationas", ["statute of limits"]], ["take precedence over", ["take prescience over"]], - ["the last two", ["both of the last two"]], - ["the last two", ["both of the last"]], - ["unorganic food", ["inorganic food"]], - ["vale of tears", ["veil of tears"]], - ["Venus flytrap", ["Venus's flytrap", "Venus' flytrap"]], - ["was accused of", ["was accused with"]], - + ["the last two", ["both of the last two"]], + ["the last two", ["both of the last"]], + ["unorganic food", ["inorganic food"]], + ["vale of tears", ["veil of tears"]], + ["Venus flytrap", ["Venus's flytrap", "Venus' flytrap"]], + ["was accused of", ["was accused with"]], # Verbosity - ["try to", ["make an attempt to"]], - ["try to", ["make attempts to"]], - ["try to", ["make efforts to"]], - ["tried to", ["made an attempt to"]], - ["tried to", ["made attempts to"]], - ["tried to", ["made efforts to"]], - ["modern", ["modern-day"]], - + ["try to", ["make an attempt to"]], + ["try to", ["make attempts to"]], + ["try to", ["make efforts to"]], + ["tried to", ["made an attempt to"]], + ["tried to", ["made attempts to"]], + ["tried to", ["made efforts to"]], + ["modern", ["modern-day"]], # Grammar - ["be misled", ["be mislead"]], - ["was misled", ["was mislead"]], - ["were misled", ["were mislead"]], - + ["be misled", ["be mislead"]], + ["was misled", ["was mislead"]], + ["were misled", ["were mislead"]], # Euphemisms ["a search-and-destroy mission", ["armed reconnaissance"]], - ["abortion", ["pregnancy termination"]], - ["bisexual", ["sexually ambidextrous"]], - ["exterminator", ["extermination engineer"]], - ["firing", ["permanent layoff"]], - ["rat-catcher", ["rodent operative"]], - + ["abortion", ["pregnancy termination"]], + ["bisexual", ["sexually ambidextrous"]], + ["exterminator", ["extermination engineer"]], + ["firing", ["permanent layoff"]], + ["rat-catcher", ["rodent operative"]], # Tenses - ["mistook", ["mistaked"]], - + ["mistook", ["mistaked"]], # Accents - ["né", ["ne"]], - ["née", ["nee"]], + ["né", ["ne"]], + ["née", ["nee"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/misc/pretension.py b/proselint/checks/misc/pretension.py index 71183094e..fe6ffdbc6 100644 --- a/proselint/checks/misc/pretension.py +++ b/proselint/checks/misc/pretension.py @@ -9,24 +9,27 @@ categories: writing --- -Never use the phrase 'all hell broke loose'. +Points out pretension. """ -from proselint.tools import existence_check, max_errors, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results -@max_errors(1) -@memoize -def check(text): + +@limit_results(1) +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "ogilvy.pretension" msg = "Jargon words like this one are the hallmarks of a pretentious ass." - list = [ + items = [ "reconceptualize", "demassification", "attitudinally", "judgmentally", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/misc/professions.py b/proselint/checks/misc/professions.py index 6bce6710a..3734dc033 100644 --- a/proselint/checks/misc/professions.py +++ b/proselint/checks/misc/professions.py @@ -12,19 +12,20 @@ Professions. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.professions" msg = "'{}' is the name of that job." preferences = [ - - ["cobbler", ["shoe repair guy"]], - ["geometer", ["geometrist"]], + ["cobbler", ["shoe repair guy"]], + ["geometer", ["geometrist"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/misc/punctuation.py b/proselint/checks/misc/punctuation.py index 08bd11b8e..dc8a5f8af 100644 --- a/proselint/checks/misc/punctuation.py +++ b/proselint/checks/misc/punctuation.py @@ -12,17 +12,19 @@ Dates. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "garner.punctuation" msg = "Misplaced punctuation. It's 'et al.'" - list = [ + items = [ "et. al", - "et. al." + "et. al.", ] - return existence_check(text, list, err, msg, join=True) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/misc/scare_quotes.py b/proselint/checks/misc/scare_quotes.py index 4c5fa8048..f581a5905 100644 --- a/proselint/checks/misc/scare_quotes.py +++ b/proselint/checks/misc/scare_quotes.py @@ -12,17 +12,19 @@ Points out misuse of scare quotes. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "pinker.scare_quotes" msg = "Misuse of 'scare quotes'. Delete them." - narcissism = [ + items = [ "the 'take-home message'", ] - return existence_check(text, narcissism, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/misc/suddenly.py b/proselint/checks/misc/suddenly.py index e4fc98201..f9af5c8fc 100644 --- a/proselint/checks/misc/suddenly.py +++ b/proselint/checks/misc/suddenly.py @@ -10,7 +10,7 @@ --- “Sudden” means quickly and without warning, but using the word “suddenly” both -slows down the action and warns your reader. Do you know what’s more effective +slows down the action and warns your reader. Do you know what's more effective for creating the sense of the sudden? Just saying what happens. When using “suddenly,” you communicate through the narrator that the action @@ -20,20 +20,31 @@ no sensory experience or concrete fact to hold on to. Just … suddenly. Feel free to employ “suddenly” in situations where the suddenness is not -apparent in the action itself. For example, in “Suddenly, I don’t hate you +apparent in the action itself. For example, in “Suddenly, I don't hate you anymore,” the “suddenly” substantially changes the way we think about the shift in emotional calibration. """ -from proselint.tools import existence_check, max_errors, memoize +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results -@max_errors(3) -@memoize -def check(text): + +@limit_results(3) +def check(text: str) -> list[ResultCheck]: """Advice on sudden vs suddenly.""" err = "misc.suddenly" msg = "Suddenly is nondescript, slows the action, and warns your reader." regex = "Suddenly," - return existence_check(text, [regex], err, msg, require_padding=False, - offset=-1, ignore_case=False) + return existence_check( + text, + [regex], + err, + msg, + padding=Pd.disabled, + offset=-1, + ignore_case=False, + ) diff --git a/proselint/checks/misc/tense_present.py b/proselint/checks/misc/tense_present.py index 2c4a6d6f2..41083f3a2 100644 --- a/proselint/checks/misc/tense_present.py +++ b/proselint/checks/misc/tense_present.py @@ -12,16 +12,16 @@ Archaism. """ -import re +from __future__ import annotations -from proselint.tools import memoize +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.tense_present" - msg = r"'{}'." + msg = "'{}'." illogics = [ r"up to \d{1,3}% ?[-\u2014\u2013]{0,3} ?(?:or|and) more\W?", @@ -37,15 +37,11 @@ def check(text): r"i ?(?:feel|am feeling|am|'m|'m feeling) nauseous", ] - errors = [] - for i in illogics: - for m in re.finditer(fr"\s{i}\s", text, flags=re.U | re.I): - txt = m.group(0).strip() - errors.append(( - m.start() + 1, - m.end(), - err, - msg.format(txt), - None)) - - return errors + return existence_check( + text, + illogics, + err, + msg, + ignore_case=True, + string=True, + ) diff --git a/proselint/checks/misc/usage.py b/proselint/checks/misc/usage.py deleted file mode 100644 index 881ac7d0c..000000000 --- a/proselint/checks/misc/usage.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Elementary Rules of Usage. - ---- -layout: post -source: Strunk & White -source_url: ??? -title: Elementary Rules of Usage -date: 2014-06-10 12:31:19 -categories: writing ---- - -Strunk & White say: -1. Form the possessive singular of nouns by adding 's. -2. In a series of three or more terms with a conjunction, use a comma after -each term except the last. -3. Enclose parenthetic expressions between commas. ("This rule is difficult to -apply.") -4. Place a comma before a conjunction introducing an independent clause. -5. Do not join independent clauses with a comma; use a semi-colon. Or a period. -6. Do not break sentences in two. Do not use periods for the role of commas. -7. Use a colon after an independent clause if you introduce: a list of -particulars, an appositive, an application, or an illustrative quotation. -8. Use a dash to set off an abrupt break or interruption and to announce a -long appositive or summary. -9. The number of the subject determines the number of the verb. (MDPNB: This -will require nltk & syntactic parsing) -10. Use the proper case(grammatical gender) of pronouns. - * MDPNB: hard case: "Give this work to whoever looks idle." `whoever looks - idle` is the object of `to`. -11. A participial phrase at the beginning of a sentence must refer to the -grammatical subject. -""" - -# from proselint.tools import memoize -# import re - - -# @memoize -# def check(text): -# err = "strunk_white.usage" -# msg = "Use of '{}'. {}" - -# bad_words = [ -# "obviously", -# "utilize" -# ] - -# explanations = { -# "obviously": -# "This is obviously an inadvisable word to use.", -# "utilize": -# r"Do you know anyone who *needs* to utilize the word utilize?" -# } - -# errors = [] -# for word in bad_words: -# occ = [m for m in re.finditer(word, text.lower())] -# for o in occ: -# errors.append((o.start(), o.end(), err, -# msg.format(word, explanations[word]))) - -# return errors diff --git a/proselint/checks/misc/waxed.py b/proselint/checks/misc/waxed.py index 15fa31e0d..bd5ca6840 100644 --- a/proselint/checks/misc/waxed.py +++ b/proselint/checks/misc/waxed.py @@ -15,40 +15,50 @@ specified tone. In this meaning a following modifier must be an adj. not an adverb ('He waxed enthusiastic [not enthusiastically] about Australia'). """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +import re -@memoize -def check(text): +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check + + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" + if not any(re.finditer("wax", text, re.IGNORECASE)): + # early exit for a niche and costly check + return [] + err = "misc.waxed" msg = "The modifier following 'waxed' must be an adj.: '{}' is correct" waxes = ["wax", "waxes", "waxed", "waxing"] - modifiers = [("ebullient", "ebulliently"), - ("ecstatic", "ecstatically"), - ("eloquent", "eloquently"), - ("enthusiastic", "enthusiastically"), - ("euphoric", "euphorically"), - ("indignant", "indignantly"), - ("lyrical", "lyrically"), - ("melancholic", "melancholically"), - ("metaphorical", "metaphorically"), - ("nostalgic", "nostalgically"), - ("patriotic", "patriotically"), - ("philosophical", "philosophically"), - ("poetic", "poetically"), - ("rhapsodic", "rhapsodically"), - ("romantic", "romantically"), - ("sentimental", "sentimentally") - ] - - def pairs(word): - return [[word + ' ' + pair[0], [word + ' ' + pair[1]]] - for pair in modifiers] - - preferred = [] - for word in waxes: - preferred += pairs(word) - - return preferred_forms_check(text, preferred, err, msg) + modifiers = [ + ("ebullient", "ebulliently"), + ("ecstatic", "ecstatically"), + ("eloquent", "eloquently"), + ("enthusiastic", "enthusiastically"), + ("euphoric", "euphorically"), + ("indignant", "indignantly"), + ("lyrical", "lyrically"), + ("melancholic", "melancholically"), + ("metaphorical", "metaphorically"), + ("nostalgic", "nostalgically"), + ("patriotic", "patriotically"), + ("philosophical", "philosophically"), + ("poetic", "poetically"), + ("rhapsodic", "rhapsodically"), + ("romantic", "romantically"), + ("sentimental", "sentimentally"), + ] + + # NOTE: python automatically caches this calculation for reruns + # check with benchmark_checks.py + + items = [ + [word + " " + pair[0], [word + " " + pair[1]]] + for pair in modifiers + for word in waxes + ] + + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/misc/whence.py b/proselint/checks/misc/whence.py index e480fd1a7..9f30d51d9 100644 --- a/proselint/checks/misc/whence.py +++ b/proselint/checks/misc/whence.py @@ -12,11 +12,13 @@ From whence it came. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "misc.whence" msg = "The 'from' in 'from whence' is not needed." diff --git a/proselint/checks/mixed_metaphors/misc.py b/proselint/checks/mixed_metaphors/misc.py index e22e26e6a..909002014 100644 --- a/proselint/checks/mixed_metaphors/misc.py +++ b/proselint/checks/mixed_metaphors/misc.py @@ -1,12 +1,15 @@ """Mixed metaphors.""" -from proselint.tools import (existence_check, max_errors, memoize, - preferred_forms_check) +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results +from proselint.checks import preferred_forms_check -@max_errors(1) -@memoize -def check_bottleneck(text): + +@limit_results(1) +def check_bottleneck(text: str) -> list[ResultCheck]: """Avoid mixing metaphors about bottles and their necks. source: Sir Ernest Gowers @@ -14,7 +17,7 @@ def check_bottleneck(text): """ err = "mixed_metaphors.misc.bottleneck" msg = "Mixed metaphor — bottles with big necks are easy to pass through." - list = [ + items = [ "biggest bottleneck", "big bottleneck", "large bottleneck", @@ -24,11 +27,10 @@ def check_bottleneck(text): "massive bottleneck", ] - return existence_check(text, list, err, msg) + return existence_check(text, items, err, msg) -@memoize -def check_misc(text): +def check_misc(text: str) -> list[ResultCheck]: """Avoid mixing metaphors. source: Garner's Modern American Usage @@ -38,12 +40,11 @@ def check_misc(text): msg = "Mixed metaphor. Try '{}'." preferences = [ - - ["cream rises to the top", ["cream rises to the crop"]], - ["fasten your seatbelts", ["button your seatbelts"]], - ["a minute to decompress", ["a minute to decompose"]], + ["cream rises to the top", ["cream rises to the crop"]], + ["fasten your seatbelts", ["button your seatbelts"]], + ["a minute to decompress", ["a minute to decompose"]], ["sharpest tool in the shed", ["sharpest marble in the (shed|box)"]], - ["not rocket science", ["not rocket surgery"]], + ["not rocket science", ["not rocket surgery"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/mondegreens/misc.py b/proselint/checks/mondegreens/misc.py index 5064cd3e2..a3212c53e 100644 --- a/proselint/checks/mondegreens/misc.py +++ b/proselint/checks/mondegreens/misc.py @@ -12,24 +12,26 @@ Points out preferred form. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "misc.mondegreens" msg = "'{}' is the preferred form." - list = [ + items = [ ["a girl with kaleidoscope eyes", ["a girl with colitis goes by"]], - ["a partridge in a pear tree", ["a part-red gingerbread tree"]], - ["attorney and notary public", ["attorney and not a republic"]], - ["beck and call", ["beckon call"]], - ["for all intents and purposes", ["for all intensive purposes"]], - ["laid him on the green", ["Lady Mondegreen"]], - ["all of the other reindeer", ["Olive, the other reindeer"]], - ["to the manner born", ["to the manor born"]], + ["a partridge in a pear tree", ["a part-red gingerbread tree"]], + ["attorney and notary public", ["attorney and not a republic"]], + ["beck and call", ["beckon call"]], + ["for all intents and purposes", ["for all intensive purposes"]], + ["laid him on the green", ["Lady Mondegreen"]], + ["all of the other reindeer", ["Olive, the other reindeer"]], + ["to the manner born", ["to the manor born"]], ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/needless_variants/misc.py b/proselint/checks/needless_variants/misc.py index 13e51cd7f..f33df9056 100644 --- a/proselint/checks/needless_variants/misc.py +++ b/proselint/checks/needless_variants/misc.py @@ -12,376 +12,395 @@ Points out use of needless variants. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): - """Suggest the preferred forms.""" + +def check_1(text: str) -> list[ResultCheck]: + """Suggest the preferred forms. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ err = "needless_variants.misc" msg = "Needless variant. '{}' is the preferred form." preferences = [ - # Needless variants - ["abolition", ["abolishment"]], - ["accessory", ["accessary"]], - ["accredit", ["accreditate"]], - ["accrual", ["accruement"]], - ["accumulate", ["cumulate"]], - ["accused", ["accusee"]], - ["acquaintance", ["acquaintanceship"]], - ["acquittal", ["acquitment"]], - ["administer", ["administrate"]], - ["administered", ["administrated"]], - ["administering", ["administrating"]], - ["adulterous", ["adulterate"]], - ["advisory", ["advisatory"]], - ["advocate", ["advocator"]], - ["alleger", ["allegator"]], - ["allusive", ["allusory"]], - ["ameliorate", ["meliorate"]], - ["amorous", ["amative"]], - ["amortization", ["amortizement"]], - ["amphibology", ["amphiboly"]], - ["anachronism", ["parachronism"]], - ["anecdotist", ["anecdotalist"]], - ["anilingus", ["anilinctus"]], - ["anticipatory", ["anticipative"]], - ["antithetical", ["antithetic"]], - ["applicable", ["applicative"]], - ["applicable", ["applicatory"]], - ["applicator", ["applier"]], - ["approbatory", ["approbative"]], - ["arbitrageur", ["arbitrager"]], - ["arsenious", ["arsenous"]], - ["ascendancy", ["ascendance"]], - ["ascendancy", ["ascendence"]], - ["ascendancy", ["ascendency"]], - ["authorial", ["auctorial"]], - ["averment", ["averral"]], - ["barbed wire", ["barbwire"]], - ["beneficent", ["benefic"]], - ["benign", ["benignant"]], - ["bestowal", ["bestowment"]], - ["betrothal", ["betrothment"]], - ["blameworthiness", ["blamableness"]], - ["buck naked", ["butt naked"]], - ["captor", ["capturer"]], - ["carte blanche", ["carta blanca"]], - ["casualties", ["casualities"]], - ["casualty", ["casuality"]], - ["catch fire", ["catch on fire"]], - ["catholically", ["catholicly"]], - ["ceasefire", ["cease fire"]], - ["cellphone", ["cell phone", "cell-phone"]], - ["channel", ["channelize"]], - ["chaplaincy", ["chaplainship"]], - ["chrysalis", ["chrysalid"]], - ["chrysalises", ["chrysalids"]], - ["cigarette", ["cigaret"]], - ["cliquish", ["cliquey", "cliquy"]], - ["cognitive", ["cognitional"]], - ["cohabit", ["cohabitate"]], - ["cohabitant", ["cohabitor"]], - ["collodion", ["collodium"]], - ["collusive", ["collusory"]], - ["commemorative", ["commemoratory"]], - ["commonage", ["commonty"]], - ["communicative", ["communicatory"]], - ["compensatory", ["compensative"]], - ["complacency", ["complacence"]], - ["complicit", ["complicitous"]], - ["compute", ["computate"]], - ["comrade", ["camarade"]], - ["conciliatory", ["conciliative"]], - ["concomitance", ["concomitancy"]], - ["condonation", ["condonance"]], - ["confirmatory", ["confirmative"]], - ["congruence", ["congruency"]], - ["connote", ["connotate"]], - ["consanguine", ["consanguineal"]], - ["conspicuousness", ["conspicuity"]], - ["conspirator", ["conspiratorialist"]], + ["abolition", ["abolishment"]], + ["accessory", ["accessary"]], + ["accredit", ["accreditate"]], + ["accrual", ["accruement"]], + ["accumulate", ["cumulate"]], + ["accused", ["accusee"]], + ["acquaintance", ["acquaintanceship"]], + ["acquittal", ["acquitment"]], + ["administer", ["administrate"]], + ["administered", ["administrated"]], + ["administering", ["administrating"]], + ["adulterous", ["adulterate"]], + ["advisory", ["advisatory"]], + ["advocate", ["advocator"]], + ["alleger", ["allegator"]], + ["allusive", ["allusory"]], + ["ameliorate", ["meliorate"]], + ["amorous", ["amative"]], + ["amortization", ["amortizement"]], + ["amphibology", ["amphiboly"]], + ["anachronism", ["parachronism"]], + ["anecdotist", ["anecdotalist"]], + ["anilingus", ["anilinctus"]], + ["anticipatory", ["anticipative"]], + ["antithetical", ["antithetic"]], + ["applicable", ["applicative"]], + ["applicable", ["applicatory"]], + ["applicator", ["applier"]], + ["approbatory", ["approbative"]], + ["arbitrageur", ["arbitrager"]], + ["arsenious", ["arsenous"]], + ["ascendancy", ["ascendance"]], + ["ascendancy", ["ascendence"]], + ["ascendancy", ["ascendency"]], + ["authorial", ["auctorial"]], + ["averment", ["averral"]], + ["barbed wire", ["barbwire"]], + ["beneficent", ["benefic"]], + ["benign", ["benignant"]], + ["bestowal", ["bestowment"]], + ["betrothal", ["betrothment"]], + ["blameworthiness", ["blamableness"]], + ["buck naked", ["butt naked"]], + ["captor", ["capturer"]], + ["carte blanche", ["carta blanca"]], + ["casualties", ["casualities"]], + ["casualty", ["casuality"]], + ["catch fire", ["catch on fire"]], + ["catholically", ["catholicly"]], + ["ceasefire", ["cease fire"]], + ["cellphone", ["cell phone", "cell-phone"]], + ["channel", ["channelize"]], + ["chaplaincy", ["chaplainship"]], + ["chrysalis", ["chrysalid"]], + ["chrysalises", ["chrysalids"]], + ["cigarette", ["cigaret"]], + ["cliquish", ["cliquey", "cliquy"]], + ["cognitive", ["cognitional"]], + ["cohabit", ["cohabitate"]], + ["cohabitant", ["cohabitor"]], + ["collodion", ["collodium"]], + ["collusive", ["collusory"]], + ["commemorative", ["commemoratory"]], + ["commonage", ["commonty"]], + ["communicative", ["communicatory"]], + ["compensatory", ["compensative"]], + ["complacency", ["complacence"]], + ["complicit", ["complicitous"]], + ["compute", ["computate"]], + ["comrade", ["camarade"]], + ["conciliatory", ["conciliative"]], + ["concomitance", ["concomitancy"]], + ["condonation", ["condonance"]], + ["confirmatory", ["confirmative"]], + ["congruence", ["congruency"]], + ["connote", ["connotate"]], + ["consanguine", ["consanguineal"]], + ["conspicuousness", ["conspicuity"]], + ["conspirator", ["conspiratorialist"]], ["constitutionalist", ["constitutionist"]], - ["contemporaneous", ["cotemporaneous"]], - ["contemporary", ["cotemporary"]], - ["contigency", ["contingence"]], - ["contributory", ["contributary"]], - ["contumacy", ["contumacity"]], - ["convertible", ["conversible"]], - ["conveyance", ["conveyal"]], - ["corroborative", ["corroboratory"]], - ["coworker", ["coemployee"]], - ["curative", ["curatory"]], - ["daredevilry", ["daredeviltry"]], - ["deceptive", ["deceptious"]], - ["defamatory", ["defamative"]], - ["degenerative", ["degeneratory"]], - ["delimit", ["delimitate"]], - ["delusive", ["delusory"]], - ["denunciation", ["denouncement"]], - ["depositary", ["depositee"]], - ["depreciatory", ["depreciative"]], - ["deprivation", ["deprival"]], - ["derogatory", ["derogative"]], - ["destructible", ["destroyable"]], - ["dethrone", ["disenthrone"]], - ["detoxify", ["detoxicate"]], - ["detractive", ["detractory"]], - ["deuterogamy", ["digamy"]], - ["deviance", ["deviancy"]], - ["deviant", ["deviationist"]], - ["digitize", ["digitalize"]], - ["diminution", ["diminishment"]], - ["diplomat", ["diplomatist"]], - ["disciplinary", ["disciplinatory"]], - ["discriminating", ["discriminant"]], - ["disintegrative", ["disintegratory"]], - ["dismissal", ["dismission"]], - ["disorient", ["disorientate"]], - ["disoriented", ["disorientated"]], - ["disquiet", ["disquieten"]], - ["dissociate", ["disassociate"]], - ["distrait", ["distraite"]], - ["divergence", ["divergency"]], - ["divisible", ["dividable"]], - ["doctrinaire", ["doctrinary"]], - ["documentary", ["documental"]], - ["domesticate", ["domesticize"]], - ["doubt", ["misdoubt"]], - ["duplicative", ["duplicatory"]], - ["dutiful", ["duteous"]], - ["educationist", ["educationalist"]], - ["educative", ["educatory"]], - ["empanel", ["impanel"]], - ["encumbrance", ["cumbrance"]], - ["endow", ["indow"]], - ["endue", ["indue"]], - ["enigmas", ["enigmatas"]], - ["enlarge", ["enlargen"]], - ["epic", ["epical"]], - ["eroticism", ["erotism"]], - ["ethicist", ["ethician"]], - ["ex officio", ["ex officiis"]], - ["exculpatory", ["exculpative"]], - ["exigency", ["exigence"]], - ["exigent", ["exigeant"]], - ["exoticism", ["exotism"]], - ["expediency", ["expedience"]], - ["expedient", ["expediential"]], - ["expedient", ["expediential"]], - ["extendable", ["extensible"]], - ["eyeing", ["eying"]], - ["fief", ["fiefdom"]], - ["flagrancy", ["flagrance"]], - ["flatulence", ["flatulency"]], - ["fraudulent", ["defraudulent"]], - ["fraudulent", ["fraudful"]], - ["funereal", ["funebrial"]], - ["geographic", ["geographical"]], - ["geometric", ["geometrical"]], - ["goatherd", ["goatherder"]], - ["grievance", ["aggrievance"]], - ["gustatory", ["gustatorial"]], - ["habit", ["habitude"]], - ["henceforth", ["henceforward"]], - ["hesitancy", ["hesitance"]], - ["heterogeneous", ["heterogenous"]], - ["hierarchical", ["hierarchic"]], - ["hindmost", ["hindermost"]], - ["honoree", ["honorand"]], - ["hypostatize", ["hypostasize"]], - ["hysterical", ["hysteric"]], - ["idolize", ["idolatrize"]], - ["impersonation", ["personation"]], - ["impervious", ["imperviable"]], - ["importunity", ["importunacy"]], - ["impotence", ["impotency"]], - ["imprimatur", ["imprimatura"]], - ["improper", ["improprietous"]], - ["incitement", ["incitation"]], - ["inconsistency", ["inconsistence"]], - ["incriminate", ["criminate"]], - ["inculpatory", ["culpatory"]], - ["incurrence", ["incurment"]], - ["infrequent", ["unfrequent"]], - ["inhibitory", ["inhibitive"]], - ["innovative", ["innovational"]], - ["inquisitorial", ["inquisitional"]], - ["insistence", ["insistment"]], - ["instillation", ["instillment"]], - ["instinctive", ["instinctual"]], - ["insubstantial", ["unsubstantial"]], - ["insurer", ["insuror"]], - ["insurrectionary", ["insurrectional"]], - ["interpret", ["interpretate"]], - ["intervention", ["intervenience"]], - ["ironic", ["ironical"]], - ["irrevocable", ["unrevokable"]], - ["judgmental", ["judgmatic"]], - ["jury-rigged", ["gerry-rigged"]], - ["jury-rigged", ["jerry-rigged"]], - ["kaffeeklatsch", ["Coffee klatsch", "coffee klatch"]], - ["knickknack", ["nicknack"]], - ["labyrinthine", ["labyrinthian"]], - ["laudatory", ["laudative"]], - ["legitimation", ["legitimatization"]], - ["legitimation", ["legitimization"]], - ["legitimize", ["legitimatize"]], - ["lengthwise", ["lengthways"]], - ["licorice", ["liquorice"]], - ["life-size", ["life-sized"]], - ["lithe", ["lithesome"]], - ["loath", ["loth"]], - ["lollypop", ["lollipop"]], - ["lubricious", ["lubricous"]], - ["mayhem", ["maihem"]], + ["contemporaneous", ["cotemporaneous"]], + ["contemporary", ["cotemporary"]], + ["contigency", ["contingence"]], + ["contributory", ["contributary"]], + ["contumacy", ["contumacity"]], + ["convertible", ["conversible"]], + ["conveyance", ["conveyal"]], + ["corroborative", ["corroboratory"]], + ["coworker", ["coemployee"]], + ["curative", ["curatory"]], + ["daredevilry", ["daredeviltry"]], + ["deceptive", ["deceptious"]], + ["defamatory", ["defamative"]], + ["degenerative", ["degeneratory"]], + ["delimit", ["delimitate"]], + ["delusive", ["delusory"]], + ["denunciation", ["denouncement"]], + ["depositary", ["depositee"]], + ["depreciatory", ["depreciative"]], + ["deprivation", ["deprival"]], + ["derogatory", ["derogative"]], + ["destructible", ["destroyable"]], + ["dethrone", ["disenthrone"]], + ["detoxify", ["detoxicate"]], + ["detractive", ["detractory"]], + ["deuterogamy", ["digamy"]], + ["deviance", ["deviancy"]], + ["deviant", ["deviationist"]], + ["digitize", ["digitalize"]], + ["diminution", ["diminishment"]], + ["diplomat", ["diplomatist"]], + ["disciplinary", ["disciplinatory"]], + ["discriminating", ["discriminant"]], + ["disintegrative", ["disintegratory"]], + ["dismissal", ["dismission"]], + ["disorient", ["disorientate"]], + ["disoriented", ["disorientated"]], + ["disquiet", ["disquieten"]], + ["dissociate", ["disassociate"]], + ["distrait", ["distraite"]], + ["divergence", ["divergency"]], + ["divisible", ["dividable"]], + ["doctrinaire", ["doctrinary"]], + ["documentary", ["documental"]], + ["domesticate", ["domesticize"]], + ["doubt", ["misdoubt"]], + ["duplicative", ["duplicatory"]], + ["dutiful", ["duteous"]], + ["educationist", ["educationalist"]], + ["educative", ["educatory"]], + ["empanel", ["impanel"]], + ["encumbrance", ["cumbrance"]], + ["endow", ["indow"]], + ["endue", ["indue"]], + ["enigmas", ["enigmatas"]], + ["enlarge", ["enlargen"]], + ["epic", ["epical"]], + ["eroticism", ["erotism"]], + ["ethicist", ["ethician"]], + ["ex officio", ["ex officiis"]], + ["exculpatory", ["exculpative"]], + ["exigency", ["exigence"]], + ["exigent", ["exigeant"]], + ["exoticism", ["exotism"]], + ["expediency", ["expedience"]], + ["expedient", ["expediential"]], + ["expedient", ["expediential"]], + ["extendable", ["extensible"]], + ["eyeing", ["eying"]], + ["fief", ["fiefdom"]], + ["flagrancy", ["flagrance"]], + ["flatulence", ["flatulency"]], + ["fraudulent", ["defraudulent"]], + ["fraudulent", ["fraudful"]], + ["funereal", ["funebrial"]], + ["geographic", ["geographical"]], + ["geometric", ["geometrical"]], + ["goatherd", ["goatherder"]], + ["grievance", ["aggrievance"]], + ["gustatory", ["gustatorial"]], + ["habit", ["habitude"]], + ["henceforth", ["henceforward"]], + ["hesitancy", ["hesitance"]], + ["heterogeneous", ["heterogenous"]], + ["hierarchical", ["hierarchic"]], + ["hindmost", ["hindermost"]], + ["honoree", ["honorand"]], + ["hypostatize", ["hypostasize"]], + ["hysterical", ["hysteric"]], + ] + return preferred_forms_check(text, preferences, err, msg) + + +def check_2(text: str) -> list[ResultCheck]: + """Suggest the preferred forms. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ + err = "needless_variants.misc" + msg = "Needless variant. '{}' is the preferred form." + + preferences = [ + ["idolize", ["idolatrize"]], + ["impersonation", ["personation"]], + ["impervious", ["imperviable"]], + ["importunity", ["importunacy"]], + ["impotence", ["impotency"]], + ["imprimatur", ["imprimatura"]], + ["improper", ["improprietous"]], + ["incitement", ["incitation"]], + ["inconsistency", ["inconsistence"]], + ["incriminate", ["criminate"]], + ["inculpatory", ["culpatory"]], + ["incurrence", ["incurment"]], + ["infrequent", ["unfrequent"]], + ["inhibitory", ["inhibitive"]], + ["innovative", ["innovational"]], + ["inquisitorial", ["inquisitional"]], + ["insistence", ["insistment"]], + ["instillation", ["instillment"]], + ["instinctive", ["instinctual"]], + ["insubstantial", ["unsubstantial"]], + ["insurer", ["insuror"]], + ["insurrectionary", ["insurrectional"]], + ["interpret", ["interpretate"]], + ["intervention", ["intervenience"]], + ["ironic", ["ironical"]], + ["irrevocable", ["unrevokable"]], + ["judgmental", ["judgmatic"]], + ["jury-rigged", ["gerry-rigged"]], + ["jury-rigged", ["jerry-rigged"]], + ["kaffeeklatsch", ["Coffee klatsch", "coffee klatch"]], + ["knickknack", ["nicknack"]], + ["labyrinthine", ["labyrinthian"]], + ["laudatory", ["laudative"]], + ["legitimation", ["legitimatization"]], + ["legitimation", ["legitimization"]], + ["legitimize", ["legitimatize"]], + ["lengthwise", ["lengthways"]], + ["licorice", ["liquorice"]], + ["life-size", ["life-sized"]], + ["lithe", ["lithesome"]], + ["loath", ["loth"]], + ["lollypop", ["lollipop"]], + ["lubricious", ["lubricous"]], + ["mayhem", ["maihem"]], ["medical marijuana", ["medicinal marijuana"]], - ["minimize", ["minimalize"]], - ["monetize", ["monetarize"]], - ["movable", ["moveable"]], - ["murk", ["mirk"]], - ["murky", ["mirky"]], - ["narcissism", ["narcism"]], - ["neglectful", ["neglective"]], - ["negligence", ["negligency"]], - ["neologist", ["neologizer"]], - ["neurological", ["neurologic"]], - ["nictitate", ["nictate"]], - ["normality", ["normalcy"]], - ["numbness", ["numbedness"]], - ["omissible", ["omittable"]], - ["onomatopoeic", ["onomatopoetic"]], - ["opined", ["opinioned"]], + ["minimize", ["minimalize"]], + ["monetize", ["monetarize"]], + ["movable", ["moveable"]], + ["murk", ["mirk"]], + ["murky", ["mirky"]], + ["narcissism", ["narcism"]], + ["neglectful", ["neglective"]], + ["negligence", ["negligency"]], + ["neologist", ["neologizer"]], + ["neurological", ["neurologic"]], + ["nictitate", ["nictate"]], + ["normality", ["normalcy"]], + ["numbness", ["numbedness"]], + ["omissible", ["omittable"]], + ["onomatopoeic", ["onomatopoetic"]], + ["opined", ["opinioned"]], ["optimal advantage", ["optimum advantage"]], - ["orient", ["orientate"]], - ["outsize", ["outsized"]], - ["oversize", ["oversized"]], - ["overthrow", ["overthrowal"]], - ["pacifist", ["pacificist"]], - ["parti-colored", ["parti-color"]], - ["parti-colored", ["party-colored"]], - ["participatory", ["participative"]], - ["partner", ["copartner"]], - ["partnership", ["copartnership"]], - # ["password", ["passcode"]], # FIXME - ["patina", ["patine"]], - ["pederast", ["paederast"]], - ["pediatrician", ["pediatrist"]], - ["pejorative", ["perjorative"]], - ["penumbral", ["penumbrous"]], - ["permissive", ["permissory"]], - ["permute", ["permutate"]], - ["pharmaceutical", ["pharmaceutic"]], - ["pleurisy", ["pleuritis"]], - ["policyholder", ["policy holder"]], - ["policyholder", ["policyowner"]], - ["politicize", ["politicalize"]], - ["pre-Columbian", ["precolumbian"]], - ["precedence", ["precedency"]], - ["preceptorial", ["preceptoral"]], - ["precipitancy", ["precipitance"]], - ["precipitate", ["precipitant"]], - ["preclusive", ["preclusory"]], - ["prefectorial", ["prefectoral"]], - ["preponderantly", ["preponderately"]], - ["preservation", ["preserval"]], - ["preventive", ["preventative"]], - ["proconsulate", ["proconsulship"]], - ["procreative", ["procreational"]], - ["procurement", ["procurance"]], - ["propulsion", ["propelment"]], - ["propulsive", ["propulsory"]], - ["prosecutory", ["prosecutive"]], - ["protective", ["protectory"]], - ["provocative", ["provocatory"]], - ["prurience", ["pruriency"]], - ["psychical", ["psychal"]], - ["punitive", ["punitory"]], - ["pygmy", ["pygmean", "pygmaen"]], - ["quantify", ["quantitate"]], - ["questionnaire", ["questionary"]], - ["quiescence", ["quiescency"]], - ["rabbi", ["rabbin"]], - ["reasonableness", ["reasonability"]], - ["recidivous", ["recidivistic"]], - ["recriminatory", ["recriminative"]], - ["recruitment", ["recruital"]], - ["recurrence", ["recurrency"]], - ["recusal", ["recusation"]], - ["recusal", ["recusement"]], - ["recusancy", ["recusance"]], - ["redemptive", ["redemptory"]], - ["referable", ["referrable"]], - ["referable", ["referrible"]], - ["refutative", ["refutatory"]], - ["remission", ["remittal"]], - ["remittance", ["remitment"]], - ["renounceable", ["renunciable"]], - ["renunciation", ["renouncement"]], - ["reparative", ["reparatory"]], - ["repudiatory", ["repudiative"]], - ["requital", ["requitement"]], - ["rescission", ["rescindment"]], - ["restoration", ["restoral"]], - ["reticence", ["reticency"]], - ["retributive", ["retributional", "retributionary"]], - ["review", ["reviewal"]], - ["revision", ["revisal"]], - ["revisionary", ["revisional"]], - ["revocable", ["revokable", "revokeable"]], - ["revolt", ["revolute"]], - ["salience", ["saliency"]], - ["salutary", ["salutiferous"]], - ["sensory", ["sensatory"]], - ["sessional", ["sessionary"]], - ["shareholder", ["shareowner"]], - ["sickly", ["sicklily"]], - ["signatory", ["signator"]], - ["slander", ["slanderize"]], - ["societal", ["societary"]], - ["sodomite", ["sodomist"]], - ["solicit", ["solicitate"]], - ["speculative", ["speculatory"]], - ["spirituous", ["spiritous"]], - ["statutory", ["statutorial"]], - ["submersible", ["submergeable"]], - ["submission", ["submittal"]], - ["subtle", ["subtile"]], - ["succubus", ["succuba"]], - ["sufficiency", ["sufficience"]], - ["supplicant", ["suppliant"]], - ["surmise", ["surmisal"]], - ["suspendable", ["suspendible"]], - ["swathe", ["enswathe"]], - ["synthesize", ["synthetize"]], - ["systematize", ["systemize"]], - ["T-shirt", ["tee-shirt"]], - ["tactile", ["tactual"]], - ["tangential", ["tangental"]], - ["tautological", ["tautologous"]], - ["thenceforth", ["thenceforward"]], - ["transience", ["transiency"]], - ["transposition", ["transposal"]], - ["transposition", ["transposal"]], - ["unalterable", ["inalterable"]], - ["uncommunicative", ["incommunicative"]], - ["uncontrollable", ["incontrollable"]], - ["unenforceable", ["nonenforceable"]], - ["unnavigable", ["innavigable"]], - ["unreasonableness", ["unreasonability"]], - ["unsolvable", ["insolvable"]], - ["usurpation", ["usurpature"]], - ["variational", ["variative"]], - ["vegetative", ["vegetive"]], - ["vindictive", ["vindicative"]], - ["vituperative", ["vituperous"]], - ["vociferous", ["vociferant"]], - ["volitional", ["volitive"]], - ["wolfish", ["wolvish"]], - ["wolverine", ["wolverene"]], - ["Zoroastrianism", ["Zoroastrism"]], + ["orient", ["orientate"]], + ["outsize", ["outsized"]], + ["oversize", ["oversized"]], + ["overthrow", ["overthrowal"]], + ["pacifist", ["pacificist"]], + ["parti-colored", ["parti-color"]], + ["parti-colored", ["party-colored"]], + ["participatory", ["participative"]], + ["partner", ["copartner"]], + ["partnership", ["copartnership"]], + # ["password", ["passcode"]], # common term > 2020 + ["patina", ["patine"]], + ["pederast", ["paederast"]], + ["pediatrician", ["pediatrist"]], + ["pejorative", ["perjorative"]], + ["penumbral", ["penumbrous"]], + ["permissive", ["permissory"]], + ["permute", ["permutate"]], + ["pharmaceutical", ["pharmaceutic"]], + ["pleurisy", ["pleuritis"]], + ["policyholder", ["policy holder"]], + ["policyholder", ["policyowner"]], + ["politicize", ["politicalize"]], + ["pre-Columbian", ["precolumbian"]], + ["precedence", ["precedency"]], + ["preceptorial", ["preceptoral"]], + ["precipitancy", ["precipitance"]], + ["precipitate", ["precipitant"]], + ["preclusive", ["preclusory"]], + ["prefectorial", ["prefectoral"]], + ["preponderantly", ["preponderately"]], + ["preservation", ["preserval"]], + ["preventive", ["preventative"]], + ["proconsulate", ["proconsulship"]], + ["procreative", ["procreational"]], + ["procurement", ["procurance"]], + ["propulsion", ["propelment"]], + ["propulsive", ["propulsory"]], + ["prosecutory", ["prosecutive"]], + ["protective", ["protectory"]], + ["provocative", ["provocatory"]], + ["prurience", ["pruriency"]], + ["psychical", ["psychal"]], + ["punitive", ["punitory"]], + ["pygmy", ["pygmean", "pygmaen"]], + ["quantify", ["quantitate"]], + ["questionnaire", ["questionary"]], + ["quiescence", ["quiescency"]], + ["rabbi", ["rabbin"]], + ["reasonableness", ["reasonability"]], + ["recidivous", ["recidivistic"]], + ["recriminatory", ["recriminative"]], + ["recruitment", ["recruital"]], + ["recurrence", ["recurrency"]], + ["recusal", ["recusation"]], + ["recusal", ["recusement"]], + ["recusancy", ["recusance"]], + ["redemptive", ["redemptory"]], + ["referable", ["referrable"]], + ["referable", ["referrible"]], + ["refutative", ["refutatory"]], + ["remission", ["remittal"]], + ["remittance", ["remitment"]], + ["renounceable", ["renunciable"]], + ["renunciation", ["renouncement"]], + ["reparative", ["reparatory"]], + ["repudiatory", ["repudiative"]], + ["requital", ["requitement"]], + ["rescission", ["rescindment"]], + ["restoration", ["restoral"]], + ["reticence", ["reticency"]], + ["retributive", ["retributional", "retributionary"]], + ["review", ["reviewal"]], + ["revision", ["revisal"]], + ["revisionary", ["revisional"]], + ["revocable", ["revokable", "revokeable"]], + ["revolt", ["revolute"]], + ["salience", ["saliency"]], + ["salutary", ["salutiferous"]], + ["sensory", ["sensatory"]], + ["sessional", ["sessionary"]], + ["shareholder", ["shareowner"]], + ["sickly", ["sicklily"]], + ["signatory", ["signator"]], + ["slander", ["slanderize"]], + ["societal", ["societary"]], + ["sodomite", ["sodomist"]], + ["solicit", ["solicitate"]], + ["speculative", ["speculatory"]], + ["spirituous", ["spiritous"]], + ["statutory", ["statutorial"]], + ["submersible", ["submergeable"]], + ["submission", ["submittal"]], + ["subtle", ["subtile"]], + ["succubus", ["succuba"]], + ["sufficiency", ["sufficience"]], + ["supplicant", ["suppliant"]], + ["surmise", ["surmisal"]], + ["suspendable", ["suspendible"]], + ["swathe", ["enswathe"]], + ["synthesize", ["synthetize"]], + ["systematize", ["systemize"]], + ["T-shirt", ["tee-shirt"]], + ["tactile", ["tactual"]], + ["tangential", ["tangental"]], + ["tautological", ["tautologous"]], + ["thenceforth", ["thenceforward"]], + ["transience", ["transiency"]], + ["transposition", ["transposal"]], + ["transposition", ["transposal"]], + ["unalterable", ["inalterable"]], + ["uncommunicative", ["incommunicative"]], + ["uncontrollable", ["incontrollable"]], + ["unenforceable", ["nonenforceable"]], + ["unnavigable", ["innavigable"]], + ["unreasonableness", ["unreasonability"]], + ["unsolvable", ["insolvable"]], + ["usurpation", ["usurpature"]], + ["variational", ["variative"]], + ["vegetative", ["vegetive"]], + ["vindictive", ["vindicative"]], + ["vituperative", ["vituperous"]], + ["vociferous", ["vociferant"]], + ["volitional", ["volitive"]], + ["wolfish", ["wolvish"]], + ["wolverine", ["wolverene"]], + ["Zoroastrianism", ["Zoroastrism"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/nonwords/misc.py b/proselint/checks/nonwords/misc.py index cf6ead862..c867275c6 100644 --- a/proselint/checks/nonwords/misc.py +++ b/proselint/checks/nonwords/misc.py @@ -12,50 +12,51 @@ Nonwords. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "nonwords.misc" msg = "Nonword, try '{}'." preferences = [ - - ["doubtless' or 'undoubtedly", ["doubtlessly"]], - ["analysis", ["analyzation"]], - ["annoyance", ["annoyment"]], - ["confirmand", ["confirmant"]], - ["confirmands", ["confirmants"]], - ["converse", ["conversate"]], - ["cranded", ["crained"]], - ["disbursement' or 'dispersal", ["dispersement"]], + ["doubtless' or 'undoubtedly", ["doubtlessly"]], + ["analysis", ["analyzation"]], + ["annoyance", ["annoyment"]], + ["confirmand", ["confirmant"]], + ["confirmands", ["confirmants"]], + ["converse", ["conversate"]], + ["cranded", ["crained"]], + ["disbursement' or 'dispersal", ["dispersement"]], ["discomfort' or 'discomfiture", ["discomforture"]], - ["effrontery", ["affrontery"]], - ["forbearance", ["forebearance"]], - ["improper", ["improprietous"]], - ["inclement", ["inclimate"]], + ["effrontery", ["affrontery"]], + ["forbearance", ["forebearance"]], + ["improper", ["improprietous"]], + ["inclement", ["inclimate"]], ["relatively low price' or 'affordability", ["relative inexpense"]], - ["inimical", ["inimicable"]], - ["regardless", ["irregardless"]], - ["minimize", ["minimalize"]], - ["minimized", ["minimalized"]], - ["minimizes", ["minimalizes"]], - ["minimizing", ["minimalizing"]], + ["inimical", ["inimicable"]], + ["regardless", ["irregardless"]], + ["minimize", ["minimalize"]], + ["minimized", ["minimalized"]], + ["minimizes", ["minimalizes"]], + ["minimizing", ["minimalizing"]], # muchly - ["optimize", ["optimalize"]], - ["paralysis", ["paralyzation"]], - ["pettifog", ["pettifogger"]], - ["proper", ["proprietous"]], - ["quell' or 'quash", ["squelch"]], - ["seldom", ["seldomly"]], + ["optimize", ["optimalize"]], + ["paralysis", ["paralyzation"]], + ["pettifog", ["pettifogger"]], + ["proper", ["proprietous"]], + ["quell' or 'quash", ["squelch"]], + ["seldom", ["seldomly"]], # slinged - ["thus", ["thusly"]], - ["categorically", ["uncategorically"]], + ["thus", ["thusly"]], + ["categorically", ["uncategorically"]], ["undoubtedly' or 'indubitably", ["undoubtably"]], - ["unequivocal", ["unequivocable"]], - ["mercilessly", ["unmercilessly"]], + ["unequivocal", ["unequivocable"]], + ["mercilessly", ["unmercilessly"]], ["unrelentingly' or relentlessly", ["unrelentlessly"]], ] diff --git a/proselint/checks/oxymorons/misc.py b/proselint/checks/oxymorons/misc.py index be2aa09a9..0f21e0063 100644 --- a/proselint/checks/oxymorons/misc.py +++ b/proselint/checks/oxymorons/misc.py @@ -12,11 +12,13 @@ Archaism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "oxymorons.misc" msg = "'{}' is an oxymoron." @@ -45,4 +47,4 @@ def check(text): # "executive secretary", ] - return existence_check(text, oxymorons, err, msg, offset=1, join=True) + return existence_check(text, oxymorons, err, msg, offset=1) diff --git a/proselint/checks/psychology/misc.py b/proselint/checks/psychology/misc.py index 764ecf8d1..a4c1d9040 100644 --- a/proselint/checks/psychology/misc.py +++ b/proselint/checks/psychology/misc.py @@ -12,40 +12,41 @@ Psychological and psychiatric terms to avoid. """ -from proselint.tools import existence_check, memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import preferred_forms_check -@memoize -def check_lie_detector_test(text): + +def check_lie_detector_test(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "psychology.lie_detector" msg = "Polygraph machines measure arousal, not lying per se. Try {}." - list = [ - ["polygraph test", ["lie detector test"]], - ["polygraph machine", ["lie detector machine"]], + items = [ + ["polygraph test", ["lie detector test"]], + ["polygraph machine", ["lie detector machine"]], ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) -@memoize -def check_p_equals_zero(text): +def check_p_equals_zero(text: str) -> list[ResultCheck]: """Check for p = 0.000.""" err = "psychology.p_equals_zero" msg = "Unless p really equals zero, you should use more decimal places." - list = [ + items = [ "p = 0.00", "p = 0.000", "p = 0.0000", ] - return existence_check(text, list, err, msg, join=True) + return existence_check(text, items, err, msg) -@memoize -def check_mental_telepathy(text): +def check_mental_telepathy(text: str) -> list[ResultCheck]: """Check for 'mental telepathy'.""" err = "psychology.mental_telepathy" msg = "This is redundant because all purported telepathy is mental." diff --git a/proselint/checks/punctuation_spacing/__init__.py b/proselint/checks/punctuation_spacing/__init__.py new file mode 100644 index 000000000..b600712c1 --- /dev/null +++ b/proselint/checks/punctuation_spacing/__init__.py @@ -0,0 +1 @@ +"""check for puntuation spacing.""" diff --git a/proselint/checks/punctuation_spacing/misc.py b/proselint/checks/punctuation_spacing/misc.py new file mode 100644 index 000000000..28faf6f96 --- /dev/null +++ b/proselint/checks/punctuation_spacing/misc.py @@ -0,0 +1,71 @@ +"""Checks for acceptable spacing around punctuation. + +--- +layout: post +source: puntuation_spacing. +source_url: https://style.mla.org/colons-how-to-use-them/ +title: Checks for acceptable spacing around punctuation. +date: 2023-04-12 12:31:19 +categories: writing +--- + +Checks for acceptable spacing around punctuation. + - checks for acceptable spacing after ending punctuation + - (!?) which must be 1 or 2 spaces. +""" +from __future__ import annotations + +from proselint.checks import ResultCheck +from proselint.checks import simple_existence_check + + +def check(text: str) -> list[ResultCheck]: + """Combine the above tests into one - + these are quick and should be load-balanced + """ + + results: list[ResultCheck] = [] + results.extend(find_end_punctuation_spacing(text)) + results.extend(find_general_spacing(text)) + results.extend(find_comma_spacing(text)) + return results + + +def find_end_punctuation_spacing(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "punctuation_spacing.misc.end_punctuation" + msg = "Unacceptable number of spaces behind ! or ? (must be 1 or 2)." + + pattern = r"[?!]\s{2,} |[?!](?!\s|$)" + + return simple_existence_check(text, pattern, err, msg) + + +# checks for acceptable behind ,";: which should be no more or less than 1 +def find_general_spacing(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "punctuation_spacing.misc.general" + msg = '"Unacceptable number of spaces behind ";: (must be 1)."' + + pattern = r'[;:"]\s{1,} |[;:](;:\s|$])' + + return simple_existence_check(text, pattern, err, msg) + + +# comma is slightly more complex, consider the number 1,000 +def find_comma_spacing(text: str) -> list[ResultCheck]: + """Check the text.""" + err = "punctuation_spacing.misc.comma" + msg = ( + "Unacceptable number of spaces behind " + "(must be 1) except when used in numbers." + ) + + pattern = r';:"]\s{2,} |[;:](;:\s|$])' + + return simple_existence_check(text, pattern, err, msg) + + +# period is complex consider the cases of ellipsis, period between numbers +# as a decimal, period to signify subsections (A.23), period used in +# between abbreviations Washington D.C. diff --git a/proselint/checks/redundancy/misc.py b/proselint/checks/redundancy/misc.py index ddd58aff6..59d5ac9e8 100644 --- a/proselint/checks/redundancy/misc.py +++ b/proselint/checks/redundancy/misc.py @@ -1,23 +1,24 @@ """Redundancy.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "redundancy.wallace" msg = "Redundancy. Use '{}' instead of '{}'." redundancies = [ - ["rectangular", ["rectangular in shape"]], - ["audible", ["audible to the ear"]], + ["rectangular", ["rectangular in shape"]], + ["audible", ["audible to the ear"]], ] return preferred_forms_check(text, redundancies, err, msg) -@memoize -def check_garner(text): +def check_garner(text: str) -> list[ResultCheck]: """Suggest the preferred forms. source: Garner's Modern American Usage @@ -27,101 +28,100 @@ def check_garner(text): msg = "Redundancy. Use '{}' instead of '{}'." redundancies = [ - ["adequate", ["adequate enough"]], - ["admitted", ["self-admitted"]], - ["affidavit", ["sworn affidavit"]], - ["agreement", ["mutual agreement"]], - ["alumnus", ["former alumnus"]], - ["antithetical", ["directly antithetical"]], - ["approximately", ["approximately about"]], - ["associate", ["associate together(?: in groups)?"]], - ["bivouac", ["temporary bivouac", "bivouac camp"]], - ["blend", ["blend together"]], - ["but", ["but nevertheless"]], - ["charged with...", ["accused of a charge"]], - ["circumstances of", ["circumstances surrounding"]], - ["circumstances", ["surrounding circumstances"]], - ["close", ["close proximity"]], - ["collaborate", ["collaborate together"]], - ["collaborator", ["fellow collaborator"]], - ["collaborators", ["fellow collaborators"]], - ["collocated", ["collocated together"]], - ["colleagues", ["fellow colleagues"]], - ["combine", ["combine together"]], - ["complacent", ["self-complacent"]], - ["confessed", ["self-confessed"]], - ["connect", ["connect together"]], - ["consensus", ["(?:general )?consensus of opinion"]], - ["consolidate", ["consolidate together"]], - ["continues to", ["still continues to"]], - ["contradictory", ["mutually contradictory"]], - ["cooperation", ["mutual cooperation"]], - ["couple", ["couple together"]], - ["crisis", ["serious crisis"]], - ["eliminate", ["entirely eliminate"]], - ["especially", ["most especially"]], - ["fact", ["actual fact"]], - ["facts", ["true facts"]], - ["forecast", ["future forecast"]], - ["founding fathers", ["founding forefathers"]], - ["free", ["free and gratis"]], - ["free", ["free gratis"]], - ["full", ["completely full"]], - ["fundamentals", ["basic fundamentals"]], - ["gift", ["free gift"]], - ["innovation", ["new innovation"]], - ["interact", ["interact with each other"]], - ["large", ["large-size"]], - ["meld", ["meld together"]], - ["merge", ["merge together"]], - ["mingle", ["mingle together"]], - ["mix", ["mix together"]], - ["mutual feelings", ["mutual feelings for eachother"]], - ["mutual respect", ["mutual respect for each other"]], - ["native citizen", ["native-born citizen"]], - ["necessity", ["absolute necessity"]], - ["obvious", ["blatantly obvious"]], - ["pause", ["pause for a moment"]], - ["planning", ["advance planning"]], - ["plans", ["future plans"]], - ["pooled", ["pooled together"]], - ["potable water", ["potable drinking water"]], - ["potable water", ["potable drinking water"]], - ["recruit", ["new recruit"]], - ["reelected", ["reelected for another term"]], - ["refer", ["refer back"]], - ["regress", ["regress back"]], - ["repay them", ["repay them back"]], - ["repay", ["repay back"]], - ["repeat", ["repeat again"]], - ["repeat", ["repeat back"]], - ["repeat", ["repeat the same"]], - ["repeated", ["repeated the same"]], - ["reprieve", ["temporary reprieve"]], - ["respite", ["brief respite"]], - ["retirement", ["retiral", "retiracy"]], - ["retreat", ["retreat back"]], - ["return", ["return back"]], - ["scrutinize", ["closely scrutinize"]], - ["software", ["software program"]], - ["surrounded", ["surrounded on all sides"]], - ["the nation", ["the whole entire nation"]], - ["throughout the", ["throughout the entire"]], - ["timpani", ["timpani drum"]], - ["twins", ["pair of twins"]], - ["vacancy", ["unfilled vacancy"]], - ["various", ["various different"]], - ["veteran", ["former veteran"]], - ["visible", ["visible to the eye"]], - ["vocation", ["professional vocation"]], - ["while", ["while at the same time"]], + ["adequate", ["adequate enough"]], + ["admitted", ["self-admitted"]], + ["affidavit", ["sworn affidavit"]], + ["agreement", ["mutual agreement"]], + ["alumnus", ["former alumnus"]], + ["antithetical", ["directly antithetical"]], + ["approximately", ["approximately about"]], + ["associate", ["associate together(?: in groups)?"]], + ["bivouac", ["temporary bivouac", "bivouac camp"]], + ["blend", ["blend together"]], + ["but", ["but nevertheless"]], + ["charged with...", ["accused of a charge"]], + ["circumstances of", ["circumstances surrounding"]], + ["circumstances", ["surrounding circumstances"]], + ["close", ["close proximity"]], + ["collaborate", ["collaborate together"]], + ["collaborator", ["fellow collaborator"]], + ["collaborators", ["fellow collaborators"]], + ["collocated", ["collocated together"]], + ["colleagues", ["fellow colleagues"]], + ["combine", ["combine together"]], + ["complacent", ["self-complacent"]], + ["confessed", ["self-confessed"]], + ["connect", ["connect together"]], + ["consensus", ["(?:general )?consensus of opinion"]], + ["consolidate", ["consolidate together"]], + ["continues to", ["still continues to"]], + ["contradictory", ["mutually contradictory"]], + ["cooperation", ["mutual cooperation"]], + ["couple", ["couple together"]], + ["crisis", ["serious crisis"]], + ["eliminate", ["entirely eliminate"]], + ["especially", ["most especially"]], + ["fact", ["actual fact"]], + ["facts", ["true facts"]], + ["forecast", ["future forecast"]], + ["founding fathers", ["founding forefathers"]], + ["free", ["free and gratis"]], + ["free", ["free gratis"]], + ["full", ["completely full"]], + ["fundamentals", ["basic fundamentals"]], + ["gift", ["free gift"]], + ["innovation", ["new innovation"]], + ["interact", ["interact with each other"]], + ["large", ["large-size"]], + ["meld", ["meld together"]], + ["merge", ["merge together"]], + ["mingle", ["mingle together"]], + ["mix", ["mix together"]], + ["mutual feelings", ["mutual feelings for eachother"]], + ["mutual respect", ["mutual respect for each other"]], + ["native citizen", ["native-born citizen"]], + ["necessity", ["absolute necessity"]], + ["obvious", ["blatantly obvious"]], + ["pause", ["pause for a moment"]], + ["planning", ["advance planning"]], + ["plans", ["future plans"]], + ["pooled", ["pooled together"]], + ["potable water", ["potable drinking water"]], + ["potable water", ["potable drinking water"]], + ["recruit", ["new recruit"]], + ["reelected", ["reelected for another term"]], + ["refer", ["refer back"]], + ["regress", ["regress back"]], + ["repay them", ["repay them back"]], + ["repay", ["repay back"]], + ["repeat", ["repeat again"]], + ["repeat", ["repeat back"]], + ["repeat", ["repeat the same"]], + ["repeated", ["repeated the same"]], + ["reprieve", ["temporary reprieve"]], + ["respite", ["brief respite"]], + ["retirement", ["retiral", "retiracy"]], + ["retreat", ["retreat back"]], + ["return", ["return back"]], + ["scrutinize", ["closely scrutinize"]], + ["software", ["software program"]], + ["surrounded", ["surrounded on all sides"]], + ["the nation", ["the whole entire nation"]], + ["throughout the", ["throughout the entire"]], + ["timpani", ["timpani drum"]], + ["twins", ["pair of twins"]], + ["vacancy", ["unfilled vacancy"]], + ["various", ["various different"]], + ["veteran", ["former veteran"]], + ["visible", ["visible to the eye"]], + ["vocation", ["professional vocation"]], + ["while", ["while at the same time"]], ] return preferred_forms_check(text, redundancies, err, msg) -@memoize -def check_nordquist(text): +def check_nordquist(text: str) -> list[ResultCheck]: """Suggest the preferred forms. source: Richard Nordquist @@ -131,367 +131,394 @@ def check_nordquist(text): msg = "Redundancy. Use '{}' instead of '{}'." redundancies = [ - ["essential", ["absolutely essential"]], - ["necessary", ["absolutely necessary"]], - ["a.m.", ["a.m. in the morning"]], - ["p.m.", ["p.m. at night"]], + ["essential", ["absolutely essential"]], + ["necessary", ["absolutely necessary"]], + ["a.m.", ["a.m. in the morning"]], + ["p.m.", ["p.m. at night"]], ] return preferred_forms_check(text, redundancies, err, msg) -@memoize -def check_atd(text): - """Check for redundancies from After the Deadline.""" +def check_atd_1(text: str) -> list[ResultCheck]: + """Check for redundancies from After the Deadline. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ err = "after_the_deadline.redundancy" msg = "Redundancy. Use '{}' instead of '{}'." redundancies = [ - ["Bō", ["Bo Staff"]], - ["Challah", ["Challah bread"]], - ["Hallah", ["Hallah bread"]], - ["Challah", ["Challah bread"]], - ["I", ["I myself", "I personally"]], - ["Mount Fuji", ["Mount Fujiyama"]], - ["Milky Way", ["Milky Way galaxy"]], - ["Rio Grande", ["Rio Grande river"]], - ["adage", ["old adage"]], - ["add", ["add a further", "add an additional"]], - ["advance", ["advance forward"]], - ["alternative", ["alternative choice"]], - ["amaretto", ["amaretto almond"]], - ["annihilate", ["completely annihilate"]], - ["anniversary", ["annual anniversary"]], - ["anonymous", ["unnamed anonymous"]], - ["as", ["equally as"]], - ["ascend", ["ascend up"]], - ["ask", ["ask the question"]], - ["assemble", ["assemble together"]], - ["at present the", ["at the present time the"]], - ["at this point", ["at this point in time"]], - ["attach", ["attach together"]], - ["autumn", ["autumn season"]], - ["bald", ["bald-headed"]], - ["balsa", ["balsa wood"]], - ["belongings", ["personal belongings"]], - ["benefits", ["desirable benefits"]], - ["bento", ["bento box"]], - ["best", ["best ever"]], - ["bit", ["tiny bit"]], - ["blend", ["blend together"]], - ["bond", ["common bond"]], - ["bonus", ["added bonus", "extra bonus"]], - ["bouquet", ["bouquet of flowers"]], - ["breakthrough", ["major breakthrough"]], - ["bride", ["new bride"]], - ["brief", ["brief in duration"]], - ["bruin", ["bruin bear"]], - ["hot", ["burning hot"]], - ["cacophony", ["cacophony of sound"]], - ["cameo", ["brief cameo", "cameo appearance"]], - ["cancel", ["cancel out"]], - ["cash", ["cash money"]], - ["chai", ["chai tea"]], - ["chance", ["random chance"]], - ["charm", ["personal charm"]], - ["circle", ["circle around", "round circle"]], - ["circulate", ["circulate around"]], - ["classify", ["classify into groups"]], - ["classmates", ["fellow classmates"]], - ["cliche", ["old cliche", "overused cliche"]], - ["climb", ["climb up"]], - ["clock", ["time clock"]], - ["collaborate", ["collaborate together"]], - ["collaboration", ["joint collaboration"]], - ["colleague", ["fellow colleague"]], - ["combine", ["combine together"]], - ["commute", ["commute back and forth"]], - ["compete", ["compete with each other"]], - ["comprise", ["comprise of"]], - ["comprises", ["comprises of"]], - ["conceived", ["first conceived"]], - ["conclusion", ["final conclusion"]], - ["confer", ["confer together"]], - ["confrontation", ["direct confrontation"]], + ["Bō", ["Bo Staff"]], + ["Challah", ["Challah bread"]], + ["Hallah", ["Hallah bread"]], + ["Challah", ["Challah bread"]], + ["I", ["I myself", "I personally"]], + ["Mount Fuji", ["Mount Fujiyama"]], + ["Milky Way", ["Milky Way galaxy"]], + ["Rio Grande", ["Rio Grande river"]], + ["adage", ["old adage"]], + ["add", ["add a further", "add an additional"]], + ["advance", ["advance forward"]], + ["alternative", ["alternative choice"]], + ["amaretto", ["amaretto almond"]], + ["annihilate", ["completely annihilate"]], + ["anniversary", ["annual anniversary"]], + ["anonymous", ["unnamed anonymous"]], + ["as", ["equally as"]], + ["ascend", ["ascend up"]], + ["ask", ["ask the question"]], + ["assemble", ["assemble together"]], + ["at present the", ["at the present time the"]], + ["at this point", ["at this point in time"]], + ["attach", ["attach together"]], + ["autumn", ["autumn season"]], + ["bald", ["bald-headed"]], + ["balsa", ["balsa wood"]], + ["belongings", ["personal belongings"]], + ["benefits", ["desirable benefits"]], + ["bento", ["bento box"]], + ["best", ["best ever"]], + ["bit", ["tiny bit"]], + ["blend", ["blend together"]], + ["bond", ["common bond"]], + ["bonus", ["added bonus", "extra bonus"]], + ["bouquet", ["bouquet of flowers"]], + ["breakthrough", ["major breakthrough"]], + ["bride", ["new bride"]], + ["brief", ["brief in duration"]], + ["bruin", ["bruin bear"]], + ["hot", ["burning hot"]], + ["cacophony", ["cacophony of sound"]], + ["cameo", ["brief cameo", "cameo appearance"]], + ["cancel", ["cancel out"]], + ["cash", ["cash money"]], + ["chai", ["chai tea"]], + ["chance", ["random chance"]], + ["charm", ["personal charm"]], + ["circle", ["circle around", "round circle"]], + ["circulate", ["circulate around"]], + ["classify", ["classify into groups"]], + ["classmates", ["fellow classmates"]], + ["cliche", ["old cliche", "overused cliche"]], + ["climb", ["climb up"]], + ["clock", ["time clock"]], + ["collaborate", ["collaborate together"]], + ["collaboration", ["joint collaboration"]], + ["colleague", ["fellow colleague"]], + ["combine", ["combine together"]], + ["commute", ["commute back and forth"]], + ["compete", ["compete with each other"]], + ["comprise", ["comprise of"]], + ["comprises", ["comprises of"]], + ["conceived", ["first conceived"]], + ["conclusion", ["final conclusion"]], + ["confer", ["confer together"]], + ["confrontation", ["direct confrontation"]], # ["confused", ["confused state"]], - ["connect", ["connect together", "connect up"]], - ["consensus", ["consensus of opinion", "general consensus"]], - ["consult", ["consult with"]], - ["conversation", ["oral conversation"]], - ["cool", ["cool down"]], - ["cooperate", ["cooperate together"]], - ["cooperation", ["mutual cooperation"]], - ["copy", ["duplicate copy"]], - ["core", ["inner core"]], - ["cost", ["cost the sum of"]], - ["could", ["could possibly"]], - ["coupon", ["money-saving coupon"]], - ["created", ["originally created"]], - ["crisis", ["crisis situation"]], - ["crouch", ["crouch down"]], - ["currently", ["now currently"]], - ["custom", ["old custom", "usual custom"]], - ["danger", ["serious danger"]], - ["dates", ["dates back"]], - ["decision", ["definite decision"]], - ["depreciate", ["depreciate in value"]], - ["descend", ["descend down"]], - ["destroy", ["totally destroy"]], - ["destroyed", ["completely destroyed"]], - ["destruction", ["total destruction"]], - ["details", ["specific details"]], - ["dilemma", ["difficult dilemma"]], - ["disappear", ["disappear from sight"]], - ["discovered", ["originally discovered"]], - ["dive", ["dive down"]], - ["done", ["over and done with"]], - ["drawing", ["illustrated drawing"]], - ["drop", ["drop down"]], - ["dune", ["sand dune"]], - ["during", ["during the course of"]], - ["dwindle", ["dwindle down"]], - ["dwindled", ["dwindled down"]], - ["every", ["each and every"]], - ["earlier", ["earlier in time"]], - ["eliminate", ["completely eliminate", "eliminate altogether", - "entirely eliminate"]], - ["ember", ["glowing ember"]], - ["embers", ["burning embers"]], - ["emergency", ["emergency situation", "unexpected emergency"]], - ["empty", ["empty out"]], - ["enclosed", ["enclosed herein"]], - ["end", ["final end"]], - ["engulfed", ["completely engulfed"]], - ["enter", ["enter in", "enter into"]], - ["equal", ["equal to one another"]], - ["eradicate", ["eradicate completely"]], - ["essential", ["absolutely essential"]], - ["estimated at", ["estimated at about", - "estimated at approximately", - "estimated at around"]], - ["etc.", ["and etc."]], - ["evolve", ["evolve over time"]], - ["exaggerate", ["over exaggerate"]], - ["exited", ["exited from"]], - ["experience", ["actual experience", "past experience"]], - ["experts", ["knowledgeable experts"]], - ["extradite", ["extradite back"]], + ["connect", ["connect together", "connect up"]], + ["consensus", ["consensus of opinion", "general consensus"]], + ["consult", ["consult with"]], + ["conversation", ["oral conversation"]], + ["cool", ["cool down"]], + ["cooperate", ["cooperate together"]], + ["cooperation", ["mutual cooperation"]], + ["copy", ["duplicate copy"]], + ["core", ["inner core"]], + ["cost", ["cost the sum of"]], + ["could", ["could possibly"]], + ["coupon", ["money-saving coupon"]], + ["created", ["originally created"]], + ["crisis", ["crisis situation"]], + ["crouch", ["crouch down"]], + ["currently", ["now currently"]], + ["custom", ["old custom", "usual custom"]], + ["danger", ["serious danger"]], + ["dates", ["dates back"]], + ["decision", ["definite decision"]], + ["depreciate", ["depreciate in value"]], + ["descend", ["descend down"]], + ["destroy", ["totally destroy"]], + ["destroyed", ["completely destroyed"]], + ["destruction", ["total destruction"]], + ["details", ["specific details"]], + ["dilemma", ["difficult dilemma"]], + ["disappear", ["disappear from sight"]], + ["discovered", ["originally discovered"]], + ["dive", ["dive down"]], + ["done", ["over and done with"]], + ["drawing", ["illustrated drawing"]], + ["drop", ["drop down"]], + ["dune", ["sand dune"]], + ["during", ["during the course of"]], + ["dwindle", ["dwindle down"]], + ["dwindled", ["dwindled down"]], + ["every", ["each and every"]], + ["earlier", ["earlier in time"]], + [ + "eliminate", + [ + "completely eliminate", + "eliminate altogether", + "entirely eliminate", + ], + ], + ["ember", ["glowing ember"]], + ["embers", ["burning embers"]], + ["emergency", ["emergency situation", "unexpected emergency"]], + ["empty", ["empty out"]], + ["enclosed", ["enclosed herein"]], + ["end", ["final end"]], + ["engulfed", ["completely engulfed"]], + ["enter", ["enter in", "enter into"]], + ["equal", ["equal to one another"]], + ["eradicate", ["eradicate completely"]], + ["essential", ["absolutely essential"]], + [ + "estimated at", + [ + "estimated at about", + "estimated at approximately", + "estimated at around", + ], + ], + ["etc.", ["and etc."]], + ["evolve", ["evolve over time"]], + ["exaggerate", ["over exaggerate"]], + ["exited", ["exited from"]], + ["experience", ["actual experience", "past experience"]], + ["experts", ["knowledgeable experts"]], + ["extradite", ["extradite back"]], ["face the consequences", ["face up to the consequences"]], - ["face the fact", ["face up to the fact"]], + ["face the fact", ["face up to the fact"]], ["face the challenge", ["face up to the challenge"]], - ["face the problem", ["face up to the problem"]], - ["facilitate", ["facilitate easier"]], - ["fact", ["established fact"]], - ["facts", ["actual facts", "hard facts", "true facts"]], - ["fad", ["passing fad"]], - ["fall", ["fall down"]], - ["fall", ["fall season"]], - ["feat", ["major feat"]], - ["feel", ["feel inside"]], - ["feelings", ["inner feelings"]], - ["few", ["few in number"]], - ["filled", ["completely filled", "filled to capacity"]], - ["first", ["first of all"]], - ["first time", ["first time ever"]], - ["fist", ["closed fist"]], - ["fly", ["fly through the air"]], - ["focus", ["focus in", "main focus"]], - ["follow", ["follow after"]], - ["for example", ["as for example"]], + ["face the problem", ["face up to the problem"]], + ["facilitate", ["facilitate easier"]], + ["fact", ["established fact"]], + ["facts", ["actual facts", "hard facts", "true facts"]], + ["fad", ["passing fad"]], + ["fall", ["fall down"]], + ["fall", ["fall season"]], + ["feat", ["major feat"]], + ["feel", ["feel inside"]], + ["feelings", ["inner feelings"]], + ["few", ["few in number"]], + ["filled", ["completely filled", "filled to capacity"]], + ["first", ["first of all"]], + ["first time", ["first time ever"]], + ["fist", ["closed fist"]], + ["fly", ["fly through the air"]], + ["focus", ["focus in", "main focus"]], + ["follow", ["follow after"]], + ["for example", ["as for example"]], # ["foremost", ["first and foremost"]], - ["forever", ["forever and ever"]], - ["free", ["for free"]], - ["friend", ["personal friend"]], - ["friendship", ["personal friendship"]], - ["full", ["full to capacity"]], - ["fundamentals", ["basic fundamentals"]], - ["fuse", ["fuse together"]], - ["gather", ["gather together", "gather up"]], - ["get up", ["get up on his feet", "get up on your feet"]], - ["gift", ["free gift"]], - ["gifts", ["free gifts"]], - ["goal", ["ultimate goal"]], + ["forever", ["forever and ever"]], + ["free", ["for free"]], + ["friend", ["personal friend"]], + ["friendship", ["personal friendship"]], + ["full", ["full to capacity"]], + ["fundamentals", ["basic fundamentals"]], + ["fuse", ["fuse together"]], + ["gather", ["gather together", "gather up"]], + ["get up", ["get up on his feet", "get up on your feet"]], + ["gift", ["free gift"]], + ["gifts", ["free gifts"]], + ["goal", ["ultimate goal"]], # ["graduate", ["former graduate"]], - ["grow", ["grow in size"]], - ["guarantee", ["absolute guarantee"]], - ["gunman", ["armed gunman"]], - ["gunmen", ["armed gunmen"]], - ["habitat", ["native habitat"]], - ["had done", ["had done previously"]], - ["halves", ["two equal halves"]], + ["grow", ["grow in size"]], + ["guarantee", ["absolute guarantee"]], + ["gunman", ["armed gunman"]], + ["gunmen", ["armed gunmen"]], + ["habitat", ["native habitat"]], + ["had done", ["had done previously"]], + ["halves", ["two equal halves"]], # ["has", ["has got"]], # ["have", ["have got"]], - ["haven", ["safe haven"]], + ["haven", ["safe haven"]], # ["he", ["he himself"]], - ["heat", ["heat up"]], - ["history", ["past history"]], - ["hoist", ["hoist up"]], - ["hole", ["empty hole"]], - ["honcho", ["head honcho"]], - ["ice", ["frozen ice"]], - ["ideal", ["perfect ideal"]], - ["identical", ["same identical"]], - ["identification", ["positive identification"]], - ["imports", ["foreign imports"]], - ["impulse", ["sudden impulse"]], - ["in fact", ["in actual fact"]], - ["in the yard", ["outside in the yard"]], - ["inclusive", ["all inclusive"]], - ["incredible", ["incredible to believe"]], - ["incumbent", ["present incumbent"]], + ["heat", ["heat up"]], + ["history", ["past history"]], + ["hoist", ["hoist up"]], + ["hole", ["empty hole"]], + ["honcho", ["head honcho"]], + ] + return preferred_forms_check(text, redundancies, err, msg) + + +def check_atd_2(text: str) -> list[ResultCheck]: + """Check for redundancies from After the Deadline. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ + err = "after_the_deadline.redundancy" + msg = "Redundancy. Use '{}' instead of '{}'." + + redundancies = [ + ["ice", ["frozen ice"]], + ["ideal", ["perfect ideal"]], + ["identical", ["same identical"]], + ["identification", ["positive identification"]], + ["imports", ["foreign imports"]], + ["impulse", ["sudden impulse"]], + ["in fact", ["in actual fact"]], + ["in the yard", ["outside in the yard"]], + ["inclusive", ["all inclusive"]], + ["incredible", ["incredible to believe"]], + ["incumbent", ["present incumbent"]], # ["indicted", ["indicted on a charge"]], - ["industry", ["private industry"]], - ["injuries", ["harmful injuries"]], - ["innovation", ["new innovation"]], - ["innovative", ["innovative new", "new innovative"]], + ["industry", ["private industry"]], + ["injuries", ["harmful injuries"]], + ["innovation", ["new innovation"]], + ["innovative", ["innovative new", "new innovative"]], # ["input", ["input into"]], - ["instinct", ["natural instinct", "naturally instinct"]], - ["integrate", ["integrate together", - "integrate with each other"]], - ["interdependent", ["interdependent on each other", - "mutually interdependent"]], - ["introduced", ["introduced for the first time"]], - ["invention", ["new invention"]], - ["kneel", ["kneel down"]], - ["knots", ["knots per hour"]], + ["instinct", ["natural instinct", "naturally instinct"]], + ["integrate", ["integrate together", "integrate with each other"]], + [ + "interdependent", + ["interdependent on each other", "mutually interdependent"], + ], + ["introduced", ["introduced for the first time"]], + ["invention", ["new invention"]], + ["kneel", ["kneel down"]], + ["knots", ["knots per hour"]], # ["last", ["last of all"]], # ["later", ["later time"]], - ["lift", ["lift up"]], - ["lingers", ["still lingers"]], + ["lift", ["lift up"]], + ["lingers", ["still lingers"]], ["look to the future", ["look ahead to the future"]], - ["love triangle", ["three-way love triangle"]], - ["maintained", ["constantly maintained"]], - ["manually", ["manually by hand"]], - ["marina", ["boat marina"]], - ["may", ["may possibly"]], - ["meet", ["meet together", "meet with each other"]], - ["memories", ["past memories"]], - ["merge", ["merge together"]], - ["merged", ["merged together"]], - ["meshed", ["meshed together"]], - ["midnight", ["twelve midnight"]], - ["migraine", ["migraine headache"]], - ["minestrone", ["minestrone soup"]], - ["mix", ["mix together"]], - ["moment", ["brief moment", "moment in time"]], - ["monopoly", ["complete monopoly"]], - ["mural", ["wall mural"]], - ["mutual respect", ["mutual respect for each other"]], + ["love triangle", ["three-way love triangle"]], + ["maintained", ["constantly maintained"]], + ["manually", ["manually by hand"]], + ["marina", ["boat marina"]], + ["may", ["may possibly"]], + ["meet", ["meet together", "meet with each other"]], + ["memories", ["past memories"]], + ["merge", ["merge together"]], + ["merged", ["merged together"]], + ["meshed", ["meshed together"]], + ["midnight", ["twelve midnight"]], + ["migraine", ["migraine headache"]], + ["minestrone", ["minestrone soup"]], + ["mix", ["mix together"]], + ["moment", ["brief moment", "moment in time"]], + ["monopoly", ["complete monopoly"]], + ["mural", ["wall mural"]], + ["mutual respect", ["mutual respect for each other"]], ["mutually dependent", ["mutually dependent on each other"]], - ["mystery", ["unsolved mystery"]], + ["mystery", ["unsolved mystery"]], # ["naked", ["bare naked"]], - ["nape", ["nape of her neck"]], - ["necessary", ["absolutely necessary"]], - ["never", ["never at any time"]], - ["noon", ["12 noon", "12 o'clock noon", "high noon", - "twelve noon"]], - ["nostalgia", ["nostalgia for the past"]], - ["number of", ["number of different"]], - ["opening", ["exposed opening"]], - ["my opinion", ["my personal opinion"]], - ["opposites", ["exact opposites", "polar opposites"]], - ["opposite", ["exact opposite", "polar opposite"]], - ["orbits", ["orbits around"]], - ["outcome", ["final outcome"]], - ["panacea", ["universal panacea"]], - ["pending", ["now pending"]], - ["penetrate", ["penetrate through"]], - ["persists", ["still persists"]], - ["pioneer", ["old pioneer"]], - ["plan", ["plan ahead", "plan in advance", - "proposed plan"]], - ["planning", ["advance planning", "forward planning"]], - ["plans", ["future plans"]], - ["plan", ["future plan"]], - ["point", ["point in time"]], - ["point", ["sharp point"]], - ["postpone", ["postpone until later"]], - ["pouring rain", ["pouring down rain"]], - ["preview", ["advance preview"]], + ["nape", ["nape of her neck"]], + ["necessary", ["absolutely necessary"]], + ["never", ["never at any time"]], + ["noon", ["12 noon", "12 o'clock noon", "high noon", "twelve noon"]], + ["nostalgia", ["nostalgia for the past"]], + ["number of", ["number of different"]], + ["opening", ["exposed opening"]], + ["my opinion", ["my personal opinion"]], + ["opposites", ["exact opposites", "polar opposites"]], + ["opposite", ["exact opposite", "polar opposite"]], + ["orbits", ["orbits around"]], + ["outcome", ["final outcome"]], + ["panacea", ["universal panacea"]], + ["pending", ["now pending"]], + ["penetrate", ["penetrate through"]], + ["persists", ["still persists"]], + ["pioneer", ["old pioneer"]], + ["plan", ["plan ahead", "plan in advance", "proposed plan"]], + ["planning", ["advance planning", "forward planning"]], + ["plans", ["future plans"]], + ["plan", ["future plan"]], + ["point", ["point in time"]], + ["point", ["sharp point"]], + ["postpone", ["postpone until later"]], + ["pouring rain", ["pouring down rain"]], + ["preview", ["advance preview"]], ["previously listed", ["previously listed above"]], - ["probed", ["probed into"]], - ["proceed", ["proceed ahead"]], - ["prosthesis", ["artificial prosthesis"]], + ["probed", ["probed into"]], + ["proceed", ["proceed ahead"]], + ["prosthesis", ["artificial prosthesis"]], # ["protrude", ["protrude out"]], - ["proverb", ["old proverb"]], + ["proverb", ["old proverb"]], # ["proximity", ["close proximity"]], - ["put off", ["put off until later"]], + ["put off", ["put off until later"]], # ["raise", ["raise up"]], - ["re-elect", ["re-elect for another term"]], - ["reason is", ["reason is because"]], - ["recur", ["recur again"]], - ["recurrence", ["future recurrence"]], - ["refer", ["refer back"]], - ["reflect", ["reflect back"]], + ["re-elect", ["re-elect for another term"]], + ["reason is", ["reason is because"]], + ["recur", ["recur again"]], + ["recurrence", ["future recurrence"]], + ["refer", ["refer back"]], + ["reflect", ["reflect back"]], # ["relevant", ["highly relevant"]], - ["remain", ["continue to remain"]], - ["remains", ["still remains"]], - ["replica", ["exact replica"]], - ["reply", ["reply back"]], + ["remain", ["continue to remain"]], + ["remains", ["still remains"]], + ["replica", ["exact replica"]], + ["reply", ["reply back"]], # ["requirements", ["necessary requirements"]], - ["reservations", ["advance reservations"]], - ["retreat", ["retreat back"]], - ["revert", ["revert back"]], - ["round", ["round in shape"]], - ["rule of thumb", ["rough rule of thumb"]], - ["rumor", ["unconfirmed rumor"]], - ["rustic", ["rustic country"]], - ["same", ["exact same", "precise same", "same exact"]], - ["sanctuary", ["safe sanctuary"]], - ["satisfaction", ["full satisfaction"]], - ["scrutinize", ["scrutinize in detail"]], - ["scrutiny", ["careful scrutiny", "close scrutiny"]], - ["secret", ["secret that cannot be told"]], - ["seek", ["seek to find"]], - ["separated", ["separated apart from each other"]], - ["share", ["share together"]], - ["shiny", ["shiny in appearance"]], - ["sincere", ["truly sincere"]], - ["sink", ["sink down"]], - ["skipped", ["skipped over"]], + ["reservations", ["advance reservations"]], + ["retreat", ["retreat back"]], + ["revert", ["revert back"]], + ["round", ["round in shape"]], + ["rule of thumb", ["rough rule of thumb"]], + ["rumor", ["unconfirmed rumor"]], + ["rustic", ["rustic country"]], + ["same", ["exact same", "precise same", "same exact"]], + ["sanctuary", ["safe sanctuary"]], + ["satisfaction", ["full satisfaction"]], + ["scrutinize", ["scrutinize in detail"]], + ["scrutiny", ["careful scrutiny", "close scrutiny"]], + ["secret", ["secret that cannot be told"]], + ["seek", ["seek to find"]], + ["separated", ["separated apart from each other"]], + ["share", ["share together"]], + ["shiny", ["shiny in appearance"]], + ["sincere", ["truly sincere"]], + ["sink", ["sink down"]], + ["skipped", ["skipped over"]], # ["slow", ["slow speed"]], # ["small", ["small size"]], - ["soft", ["soft in texture", "soft to the touch"]], - ["sole", ["sole of the foot"]], - ["some time", ["some time to come"]], - ["speck", ["small speck"]], - ["speed", ["rate of speed"]], - ["spell out", ["spell out in detail"]], - ["spiked", ["spiked upward", "spiked upwards"]], - ["spring", ["spring season"]], - ["stranger", ["anonymous stranger"]], - ["studio audience", ["live studio audience"]], - ["subway", ["underground subway"]], - ["sufficient", ["sufficient enough"]], - ["summer", ["summer season"]], - ["sure", ["absolutely sure"]], - ["surprise", ["unexpected surprise"]], - ["surround", ["completely surround"]], - ["surrounded", ["surrounded on all sides"]], - ["tall", ["tall in height", "tall in stature"]], - ["telepathy", ["mental telepathy"]], - ["ten", ["ten in number"]], - ["these", ["these ones"]], + ["soft", ["soft in texture", "soft to the touch"]], + ["sole", ["sole of the foot"]], + ["some time", ["some time to come"]], + ["speck", ["small speck"]], + ["speed", ["rate of speed"]], + ["spell out", ["spell out in detail"]], + ["spiked", ["spiked upward", "spiked upwards"]], + ["spring", ["spring season"]], + ["stranger", ["anonymous stranger"]], + ["studio audience", ["live studio audience"]], + ["subway", ["underground subway"]], + ["sufficient", ["sufficient enough"]], + ["summer", ["summer season"]], + ["sure", ["absolutely sure"]], + ["surprise", ["unexpected surprise"]], + ["surround", ["completely surround"]], + ["surrounded", ["surrounded on all sides"]], + ["tall", ["tall in height", "tall in stature"]], + ["telepathy", ["mental telepathy"]], + ["ten", ["ten in number"]], + ["these", ["these ones"]], # ["they", ["they themselves"]], - ["those", ["those ones"]], - ["trench", ["open trench"]], - ["truth", ["honest truth"]], - ["tundra", ["frozen tundra"]], - ["ultimatum", ["final ultimatum"]], + ["those", ["those ones"]], + ["trench", ["open trench"]], + ["truth", ["honest truth"]], + ["tundra", ["frozen tundra"]], + ["ultimatum", ["final ultimatum"]], # ["undeniable", ["undeniable truth"]], - ["undergraduate", ["undergraduate student"]], + ["undergraduate", ["undergraduate student"]], # ["unintentional", ["unintentional mistake"]], - ["vacillate", ["vacillate back and forth"]], - ["veteran", ["former veteran"]], - ["visible", ["visible to the eye"]], - ["warn", ["warn in advance"]], - ["warning", ["advance warning"]], - ["water heater", ["hot water heater"]], - ["in which we live", ["in which we live in"]], - ["winter", ["winter season"]], - ["witness", ["live witness"]], - ["yakitori", ["yakitori chicken"]], - ["yerba mate", ["yerba mate tea"]], - ["yes", ["affirmative yes"]], + ["vacillate", ["vacillate back and forth"]], + ["veteran", ["former veteran"]], + ["visible", ["visible to the eye"]], + ["warn", ["warn in advance"]], + ["warning", ["advance warning"]], + ["water heater", ["hot water heater"]], + ["in which we live", ["in which we live in"]], + ["winter", ["winter season"]], + ["witness", ["live witness"]], + ["yakitori", ["yakitori chicken"]], + ["yerba mate", ["yerba mate tea"]], + ["yes", ["affirmative yes"]], ] return preferred_forms_check(text, redundancies, err, msg) diff --git a/proselint/checks/redundancy/ras_syndrome.py b/proselint/checks/redundancy/ras_syndrome.py index dae75add0..2d2bdcddf 100644 --- a/proselint/checks/redundancy/ras_syndrome.py +++ b/proselint/checks/redundancy/ras_syndrome.py @@ -1,35 +1,37 @@ """Redundant Acronym Syndrome (RAS) syndrome.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "garner.redundancy.ras" msg = "RAS syndrome. Use '{}' instead of '{}'." redundancies = [ - ["ABM", ["ABM missile"]], - ["ACT", ["ACT test"]], - ["ABMs", ["ABM missiles"]], - ["ABS", ["ABS braking system"]], - ["ATM", ["ATM machine"]], - ["CD", ["CD disc"]], - ["CPI", ["CPI Index"]], - ["GPS", ["GPS system"]], - ["GUI", ["GUI interface"]], - ["HIV", ["HIV virus"]], - ["ISBN", ["ISBN number"]], - ["LCD", ["LCD display"]], - ["PDF", ["PDF format"]], - ["PIN", ["PIN number"]], - ["RAS", ["RAS syndrome"]], - ["RIP", ["RIP in peace"]], - ["RSVP", ["please RSVP"]], - ["SALT", ["SALT talks"]], - ["SAT", ["SAT test"]], - ["UPC", ["UPC codes"]], + ["ABM", ["ABM missile"]], + ["ACT", ["ACT test"]], + ["ABMs", ["ABM missiles"]], + ["ABS", ["ABS braking system"]], + ["ATM", ["ATM machine"]], + ["CD", ["CD disc"]], + ["CPI", ["CPI Index"]], + ["GPS", ["GPS system"]], + ["GUI", ["GUI interface"]], + ["HIV", ["HIV virus"]], + ["ISBN", ["ISBN number"]], + ["LCD", ["LCD display"]], + ["PDF", ["PDF format"]], + ["PIN", ["PIN number"]], + ["RAS", ["RAS syndrome"]], + ["RIP", ["RIP in peace"]], + ["RSVP", ["please RSVP"]], + ["SALT", ["SALT talks"]], + ["SAT", ["SAT test"]], + ["UPC", ["UPC codes"]], ] return preferred_forms_check(text, redundancies, err, msg) diff --git a/proselint/checks/security/credit_card.py b/proselint/checks/security/credit_card.py index 9bf558284..35ace8c94 100644 --- a/proselint/checks/security/credit_card.py +++ b/proselint/checks/security/credit_card.py @@ -12,11 +12,13 @@ Credit card number printed. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "security.credit_card" msg = "Don't put credit card numbers in plain text." diff --git a/proselint/checks/security/password.py b/proselint/checks/security/password.py index c5d271ba5..0754b7139 100644 --- a/proselint/checks/security/password.py +++ b/proselint/checks/security/password.py @@ -11,23 +11,25 @@ Don't put pass """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "security.password" msg = "Don't put passwords in plain text." - pwd_regex = r"[:]? [\S]{6,30}" + _regex = r"[:]? [\S]{6,30}" - password = [ - f"the password is{pwd_regex}", - f"my password is{pwd_regex}", - f"the password's{pwd_regex}", - f"my password's{pwd_regex}", - f"^[pP]assword{pwd_regex}", + items = [ + f"the password is{_regex}", + f"my password is{_regex}", + f"the password's{_regex}", + f"my password's{_regex}", + f"^[pP]assword{_regex}", ] - return existence_check(text, password, err, msg) + return existence_check(text, items, err, msg) diff --git a/proselint/checks/sexism/misc.py b/proselint/checks/sexism/misc.py index cd38324c8..f81a00d48 100644 --- a/proselint/checks/sexism/misc.py +++ b/proselint/checks/sexism/misc.py @@ -12,60 +12,62 @@ Points out sexist language. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check_sexism(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "sexism.misc" msg = "Gender bias. Use '{}' instead of '{}'." sexism = [ - ["anchor", ["anchorman", "anchorwoman"]], - ["chair", ["chairman", "chairwoman"]], - ["drafter", ["draftman", "draftwoman"]], - ["ombuds", ["ombudsman", "ombudswoman"]], - ["tribe member", ["tribesman", "tribeswoman"]], - ["police officer", ["policeman", "policewoman"]], - ["firefighter", ["fireman", "firewoman"]], - ["mail carrier", ["mailman", "mailwoman"]], - ["history", ["herstory"]], - ["women", ["womyn"]], - ["poet", ["poetess"]], - ["author", ["authoress"]], - ["waiter", ["waitress"]], - ["lawyer", ["lady lawyer"]], - ["doctor", ["woman doctor"]], - ["bookseller", ["female booksalesman"]], - ["air pilot", ["female airman"]], - ["executor", ["executrix"]], - ["prosecutor", ["prosecutrix"]], - ["testator", ["testatrix"]], + ["anchor", ["anchorman", "anchorwoman"]], + ["chair", ["chairman", "chairwoman"]], + ["drafter", ["draftman", "draftwoman"]], + ["ombuds", ["ombudsman", "ombudswoman"]], + ["tribe member", ["tribesman", "tribeswoman"]], + ["police officer", ["policeman", "policewoman"]], + ["firefighter", ["fireman", "firewoman"]], + ["mail carrier", ["mailman", "mailwoman"]], + ["history", ["herstory"]], + ["women", ["womyn"]], + ["poet", ["poetess"]], + ["author", ["authoress"]], + ["waiter", ["waitress"]], + ["lawyer", ["lady lawyer"]], + ["doctor", ["woman doctor"]], + ["bookseller", ["female booksalesman"]], + ["air pilot", ["female airman"]], + ["executor", ["executrix"]], + ["prosecutor", ["prosecutrix"]], + ["testator", ["testatrix"]], ["husband and wife", ["man and wife"]], - ["chairs", ["chairmen and chairs"]], - ["men and women", ["men and girls"]], - ["comedian", ["comedienne"]], - ["confidant", ["confidante"]], - ["scientist", ["woman scientist"]], - ["scientists", ["women scientists"]] + ["chairs", ["chairmen and chairs"]], + ["men and women", ["men and girls"]], + ["comedian", ["comedienne"]], + ["confidant", ["confidante"]], + ["scientist", ["woman scientist"]], + ["scientists", ["women scientists"]], # ["hero", ["heroine"]] ] - errors = preferred_forms_check(text, sexism, err, msg, ignore_case=False) + return preferred_forms_check(text, sexism, err, msg, ignore_case=False) + +def check_preferred_form(text: str) -> list[ResultCheck]: + err = "sexism.misc" msg = "Not a preferred form. Use '{}' instead of '{}'." pref = [ - ["anchor", ["anchorperson"]], - ["chair", ["chairperson"]], - ["drafter", ["draftperson"]], - ["ombuds", ["ombudsperson"]], - ["tribe member", ["tribesperson"]], - ["police officer", ["policeperson"]], - ["firefighter", ["fireperson"]], - ["mail carrier", ["mailperson"]], + ["anchor", ["anchorperson"]], + ["chair", ["chairperson"]], + ["drafter", ["draftperson"]], + ["ombuds", ["ombudsperson"]], + ["tribe member", ["tribesperson"]], + ["police officer", ["policeperson"]], + ["firefighter", ["fireperson"]], + ["mail carrier", ["mailperson"]], ] - for x in preferred_forms_check(text, pref, err, msg, ignore_case=False): - errors.append(x) - - return errors + return preferred_forms_check(text, pref, err, msg, ignore_case=False) diff --git a/proselint/checks/skunked_terms/misc.py b/proselint/checks/skunked_terms/misc.py index af8ce7d70..ad2398d76 100644 --- a/proselint/checks/skunked_terms/misc.py +++ b/proselint/checks/skunked_terms/misc.py @@ -12,15 +12,19 @@ Archaism. """ -from proselint.tools import existence_check, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Check the text.""" err = "skunked_terms.misc" - msg = """'{}' is a bit of a skunked term, impossible to use without issue. - Find some other way to say it.""" + msg = ( + "'{}' is a skunked term, impossible to use without issue. " + "Find another way to say it." + ) skunked_terms = [ "bona fides", diff --git a/proselint/checks/spelling/able_atable.py b/proselint/checks/spelling/able_atable.py index 64e0ea9de..488ea9f3c 100644 --- a/proselint/checks/spelling/able_atable.py +++ b/proselint/checks/spelling/able_atable.py @@ -1,84 +1,85 @@ """-able vs. -atable.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-able vs. -atable.""" err = "spelling.able_atable" msg = "-able vs. -atable. '{}' is the preferred spelling." preferences = [ - - ["abbreviable", ["abbreviatable"]], - ["abdicable", ["abdicatable"]], - ["abrogable", ["abrogatable"]], - ["accommodable", ["accommodatable"]], - ["accumulable", ["accumulatable"]], - ["activable", ["activatable"]], - ["administrable", ["administratable"]], - ["adulterable", ["adulteratable"]], - ["affiliable", ["affiliatable"]], - ["aggregable", ["aggregatable"]], - ["agitable", ["agitatable"]], - ["alienable", ["alienatable"]], - ["alleviable", ["alleviatable"]], - ["allocable", ["allocatable"]], - ["ameliorable", ["amelioratable"]], - ["annihilable", ["annihilatable"]], - ["appreciable", ["appreciatable"]], - ["appropriable", ["appropriatable"]], - ["arbitrable", ["arbitratable"]], - ["articulable", ["articulatable"]], - ["calculable", ["calculatable"]], - ["communicable", ["communicatable"]], - ["compensable", ["compensatable"]], - ["confiscable", ["confiscatable"]], - ["corroborable", ["corroboratable"]], - ["cultivable", ["cultivatable"]], - ["delegable", ["delegatable"]], - ["delineable", ["delineatable"]], - ["demonstrable", ["demonstratable"]], - ["detonable", ["detonatable"]], - ["differentiable", ["differentiatable"]], - ["eradicable", ["eradicatable"]], - ["evacuable", ["evacuatable"]], - ["evaluable", ["evaluatable"]], - ["expropriable", ["expropriatable"]], - ["generable", ["generatable"]], - ["indicable", ["indicatable"]], - ["inebriable", ["inebriatable"]], - ["inextirpable", ["inextirpatable"]], - ["inextricable", ["inextricatable"]], - ["infatuable", ["infatuatable"]], - ["infuriable", ["infuriatable"]], - ["invalidable", ["invalidatable"]], - ["investigable", ["investigatable"]], - ["isolable", ["isolatable"]], - ["litigable", ["litigatable"]], - ["manipulable", ["manipulatable"]], - ["medicable", ["medicatable"]], - ["navigable", ["navigatable"]], - ["obligable", ["obligatable"]], - ["obviable", ["obviatable"]], - ["operable", ["operatable"]], - ["originable", ["originatable"]], - ["participable", ["participatable"]], - ["penetrable", ["penetratable"]], - ["perpetrable", ["perpetratable"]], - ["perpetuable", ["perpetuatable"]], - ["predicable", ["predicatable"]], - ["propagable", ["propagatable"]], - ["regulable", ["regulatable"]], - ["replicable", ["replicatable"]], - ["repudiable", ["repudiatable"]], - ["segregable", ["segregatable"]], - ["separable", ["separatable"]], - ["subjugable", ["subjugatable"]], - ["vindicable", ["vindicatable"]], - ["violable", ["violatable"]], - ["vitiable", ["vitiatable"]] + ["abbreviable", ["abbreviatable"]], + ["abdicable", ["abdicatable"]], + ["abrogable", ["abrogatable"]], + ["accommodable", ["accommodatable"]], + ["accumulable", ["accumulatable"]], + ["activable", ["activatable"]], + ["administrable", ["administratable"]], + ["adulterable", ["adulteratable"]], + ["affiliable", ["affiliatable"]], + ["aggregable", ["aggregatable"]], + ["agitable", ["agitatable"]], + ["alienable", ["alienatable"]], + ["alleviable", ["alleviatable"]], + ["allocable", ["allocatable"]], + ["ameliorable", ["amelioratable"]], + ["annihilable", ["annihilatable"]], + ["appreciable", ["appreciatable"]], + ["appropriable", ["appropriatable"]], + ["arbitrable", ["arbitratable"]], + ["articulable", ["articulatable"]], + ["calculable", ["calculatable"]], + ["communicable", ["communicatable"]], + ["compensable", ["compensatable"]], + ["confiscable", ["confiscatable"]], + ["corroborable", ["corroboratable"]], + ["cultivable", ["cultivatable"]], + ["delegable", ["delegatable"]], + ["delineable", ["delineatable"]], + ["demonstrable", ["demonstratable"]], + ["detonable", ["detonatable"]], + ["differentiable", ["differentiatable"]], + ["eradicable", ["eradicatable"]], + ["evacuable", ["evacuatable"]], + ["evaluable", ["evaluatable"]], + ["expropriable", ["expropriatable"]], + ["generable", ["generatable"]], + ["indicable", ["indicatable"]], + ["inebriable", ["inebriatable"]], + ["inextirpable", ["inextirpatable"]], + ["inextricable", ["inextricatable"]], + ["infatuable", ["infatuatable"]], + ["infuriable", ["infuriatable"]], + ["invalidable", ["invalidatable"]], + ["investigable", ["investigatable"]], + ["isolable", ["isolatable"]], + ["litigable", ["litigatable"]], + ["manipulable", ["manipulatable"]], + ["medicable", ["medicatable"]], + ["navigable", ["navigatable"]], + ["obligable", ["obligatable"]], + ["obviable", ["obviatable"]], + ["operable", ["operatable"]], + ["originable", ["originatable"]], + ["participable", ["participatable"]], + ["penetrable", ["penetratable"]], + ["perpetrable", ["perpetratable"]], + ["perpetuable", ["perpetuatable"]], + ["predicable", ["predicatable"]], + ["propagable", ["propagatable"]], + ["regulable", ["regulatable"]], + ["replicable", ["replicatable"]], + ["repudiable", ["repudiatable"]], + ["segregable", ["segregatable"]], + ["separable", ["separatable"]], + ["subjugable", ["subjugatable"]], + ["vindicable", ["vindicatable"]], + ["violable", ["violatable"]], + ["vitiable", ["vitiatable"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/spelling/able_ible.py b/proselint/checks/spelling/able_ible.py index 331d7f6de..2241547b7 100644 --- a/proselint/checks/spelling/able_ible.py +++ b/proselint/checks/spelling/able_ible.py @@ -1,173 +1,173 @@ """-able vs. -ible.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-able vs. -ible.""" err = "spelling.able_ible" msg = "-able vs. -ible. '{}' is the preferred spelling." preferences = [ - - ["actionable", ["actionible"]], - ["addable", ["addible"]], - ["admittable", ["admittible"]], - ["advisable", ["advisible"]], - ["affectable", ["affectible"]], - ["allegeable", ["allegeible"]], - ["analyzable", ["analyzible"]], - ["annexable", ["annexible"]], - ["arrestable", ["arrestible"]], - ["ascendable", ["ascendible"]], - ["assertable", ["assertible"]], - ["assessable", ["assessible"]], - ["averageable", ["averageible"]], - ["bailable", ["bailible"]], - ["blamable", ["blamible"]], - ["changeable", ["changeible"]], - ["chargeable", ["chargeible"]], + ["actionable", ["actionible"]], + ["addable", ["addible"]], + ["admittable", ["admittible"]], + ["advisable", ["advisible"]], + ["affectable", ["affectible"]], + ["allegeable", ["allegeible"]], + ["analyzable", ["analyzible"]], + ["annexable", ["annexible"]], + ["arrestable", ["arrestible"]], + ["ascendable", ["ascendible"]], + ["assertable", ["assertible"]], + ["assessable", ["assessible"]], + ["averageable", ["averageible"]], + ["bailable", ["bailible"]], + ["blamable", ["blamible"]], + ["changeable", ["changeible"]], + ["chargeable", ["chargeible"]], ["circumscribable", ["circumscribible"]], - ["commensurable", ["commensurible"]], - ["committable", ["committible"]], - ["condensable", ["condensible"]], - ["connectable", ["connectible"]], - ["contestable", ["contestible"]], - ["contractable", ["contractible"]], - ["conversable", ["conversible"]], - ["convictable", ["convictible"]], - ["correctable", ["correctible"]], - ["definable", ["definible"]], - ["detectable", ["detectible"]], - ["diagnosable", ["diagnosible"]], - ["discussable", ["discussible"]], - ["endorsable", ["endorsible"]], - ["enforceable", ["enforceible"]], - ["evadable", ["evadible"]], - ["excisable", ["excisible"]], - ["excludable", ["excludible"]], - ["expandable", ["expandible"]], - ["extendable", ["extendible"]], - ["extractable", ["extractible"]], - ["ignitable", ["ignitible"]], - ["immovable", ["immovible"]], - ["improvable", ["improvible"]], - ["includable", ["includible"]], - ["inferable", ["inferible"]], - ["inventable", ["inventible"]], - ["investable", ["investible"]], - ["lapsable", ["lapsible"]], - ["lovable", ["lovible"]], - ["mixable", ["mixible"]], - ["movable", ["movible"]], - ["noticeable", ["noticeible"]], - ["offendable", ["offendible"]], - ["patentable", ["patentible"]], - ["persuadable", ["persuadible"]], - ["preventable", ["preventible"]], - ["processable", ["processible"]], - ["protectable", ["protectible"]], - ["ratable", ["ratible"]], - ["redressable", ["redressible"]], - ["referable", ["referible"]], - ["retractable", ["retractible"]], - ["revisable", ["revisible"]], - ["rinsable", ["rinsible"]], - ["salable", ["salible"]], - ["suspendable", ["suspendible"]], - ["tractable", ["tractible"]], - ["transferable", ["transferible"]], - ["transmittable", ["transmittible"]], - ["willable", ["willible"]], - - ["accessible", ["accessable"]], - ["adducible", ["adducable"]], - ["admissible", ["admissable"]], - ["audible", ["audable"]], - ["avertible", ["avertable"]], - ["collapsible", ["collapsable"]], - ["collectible", ["collectable"]], - ["combustible", ["combustable"]], - ["compactible", ["compactable"]], - ["compatible", ["compatable"]], - ["comprehensible", ["comprehensable"]], - ["compressible", ["compressable"]], - ["concussible", ["concussable"]], - ["conductible", ["conductable"]], - ["contemptible", ["contemptable"]], - ["controvertible", ["controvertable"]], - ["convertible", ["convertable"]], - ["corrodible", ["corrodable"]], - ["corruptible", ["corruptable"]], - ["credible", ["credable"]], - ["deducible", ["deducable"]], - ["deductible", ["deductable"]], - ["defeasible", ["defeasable"]], - ["defensible", ["defensable"]], - ["descendible", ["descendable"]], - ["destructible", ["destructable"]], - ["diffusible", ["diffusable"]], - ["digestible", ["digestable"]], - ["discernible", ["discernable"]], - ["dismissible", ["dismissable"]], - ["divisible", ["divisable"]], - ["edible", ["edable"]], - ["educible", ["educable"]], - ["eligible", ["eligable"]], - ["erodible", ["erodable"]], - ["exhaustible", ["exhaustable"]], - ["expressible", ["expressable"]], - ["fallible", ["fallable"]], - ["feasible", ["feasable"]], - ["flexible", ["flexable"]], - ["forcible", ["forcable"]], - ["fusible", ["fusable"]], - ["gullible", ["gullable"]], - ["horrible", ["horrable"]], - ["impressible", ["impressable"]], - ["inadmissible", ["inadmissable"]], - ["incorrigible", ["incorrigable"]], - ["indelible", ["indelable"]], - ["inexpressible", ["inexpressable"]], - ["intelligible", ["intelligable"]], - ["interfusible", ["interfusable"]], - ["invincible", ["invincable"]], - ["irascible", ["irascable"]], - ["irresistible", ["irresistable"]], - ["legible", ["legable"]], - ["negligible", ["negligable"]], - ["omissible", ["omissable"]], - ["oppressible", ["oppressable"]], - ["ostensible", ["ostensable"]], - ["perceptible", ["perceptable"]], - ["perfectible", ["perfectable"]], - ["permissible", ["permissable"]], - ["plausible", ["plausable"]], - ["possible", ["possable"]], - ["producible", ["producable"]], - ["reducible", ["reducable"]], - ["remissible", ["remissable"]], - ["reprehensible", ["reprehensable"]], - ["repressible", ["repressable"]], - ["resistible", ["resistable"]], - ["responsible", ["responsable"]], - ["reversible", ["reversable"]], - ["revertible", ["revertable"]], - ["risible", ["risable"]], - ["seducible", ["seducable"]], - ["sensible", ["sensable"]], - ["submersible", ["submersable"]], - ["submergible", ["submergable"]], - ["suggestible", ["suggestable"]], - ["suppressible", ["suppressable"]], - ["susceptible", ["susceptable"]], - ["terrible", ["terrable"]], - ["transfusible", ["transfusable"]], - ["transmissible", ["transmissable"]], - ["uncollectible", ["uncollectable"]], - ["vendible", ["vendable"]], - ["visible", ["visable"]] + ["commensurable", ["commensurible"]], + ["committable", ["committible"]], + ["condensable", ["condensible"]], + ["connectable", ["connectible"]], + ["contestable", ["contestible"]], + ["contractable", ["contractible"]], + ["conversable", ["conversible"]], + ["convictable", ["convictible"]], + ["correctable", ["correctible"]], + ["definable", ["definible"]], + ["detectable", ["detectible"]], + ["diagnosable", ["diagnosible"]], + ["discussable", ["discussible"]], + ["endorsable", ["endorsible"]], + ["enforceable", ["enforceible"]], + ["evadable", ["evadible"]], + ["excisable", ["excisible"]], + ["excludable", ["excludible"]], + ["expandable", ["expandible"]], + ["extendable", ["extendible"]], + ["extractable", ["extractible"]], + ["ignitable", ["ignitible"]], + ["immovable", ["immovible"]], + ["improvable", ["improvible"]], + ["includable", ["includible"]], + ["inferable", ["inferible"]], + ["inventable", ["inventible"]], + ["investable", ["investible"]], + ["lapsable", ["lapsible"]], + ["lovable", ["lovible"]], + ["mixable", ["mixible"]], + ["movable", ["movible"]], + ["noticeable", ["noticeible"]], + ["offendable", ["offendible"]], + ["patentable", ["patentible"]], + ["persuadable", ["persuadible"]], + ["preventable", ["preventible"]], + ["processable", ["processible"]], + ["protectable", ["protectible"]], + ["ratable", ["ratible"]], + ["redressable", ["redressible"]], + ["referable", ["referible"]], + ["retractable", ["retractible"]], + ["revisable", ["revisible"]], + ["rinsable", ["rinsible"]], + ["salable", ["salible"]], + ["suspendable", ["suspendible"]], + ["tractable", ["tractible"]], + ["transferable", ["transferible"]], + ["transmittable", ["transmittible"]], + ["willable", ["willible"]], + ["accessible", ["accessable"]], + ["adducible", ["adducable"]], + ["admissible", ["admissable"]], + ["audible", ["audable"]], + ["avertible", ["avertable"]], + ["collapsible", ["collapsable"]], + ["collectible", ["collectable"]], + ["combustible", ["combustable"]], + ["compactible", ["compactable"]], + ["compatible", ["compatable"]], + ["comprehensible", ["comprehensable"]], + ["compressible", ["compressable"]], + ["concussible", ["concussable"]], + ["conductible", ["conductable"]], + ["contemptible", ["contemptable"]], + ["controvertible", ["controvertable"]], + ["convertible", ["convertable"]], + ["corrodible", ["corrodable"]], + ["corruptible", ["corruptable"]], + ["credible", ["credable"]], + ["deducible", ["deducable"]], + ["deductible", ["deductable"]], + ["defeasible", ["defeasable"]], + ["defensible", ["defensable"]], + ["descendible", ["descendable"]], + ["destructible", ["destructable"]], + ["diffusible", ["diffusable"]], + ["digestible", ["digestable"]], + ["discernible", ["discernable"]], + ["dismissible", ["dismissable"]], + ["divisible", ["divisable"]], + ["edible", ["edable"]], + ["educible", ["educable"]], + ["eligible", ["eligable"]], + ["erodible", ["erodable"]], + ["exhaustible", ["exhaustable"]], + ["expressible", ["expressable"]], + ["fallible", ["fallable"]], + ["feasible", ["feasable"]], + ["flexible", ["flexable"]], + ["forcible", ["forcable"]], + ["fusible", ["fusable"]], + ["gullible", ["gullable"]], + ["horrible", ["horrable"]], + ["impressible", ["impressable"]], + ["inadmissible", ["inadmissable"]], + ["incorrigible", ["incorrigable"]], + ["indelible", ["indelable"]], + ["inexpressible", ["inexpressable"]], + ["intelligible", ["intelligable"]], + ["interfusible", ["interfusable"]], + ["invincible", ["invincable"]], + ["irascible", ["irascable"]], + ["irresistible", ["irresistable"]], + ["legible", ["legable"]], + ["negligible", ["negligable"]], + ["omissible", ["omissable"]], + ["oppressible", ["oppressable"]], + ["ostensible", ["ostensable"]], + ["perceptible", ["perceptable"]], + ["perfectible", ["perfectable"]], + ["permissible", ["permissable"]], + ["plausible", ["plausable"]], + ["possible", ["possable"]], + ["producible", ["producable"]], + ["reducible", ["reducable"]], + ["remissible", ["remissable"]], + ["reprehensible", ["reprehensable"]], + ["repressible", ["repressable"]], + ["resistible", ["resistable"]], + ["responsible", ["responsable"]], + ["reversible", ["reversable"]], + ["revertible", ["revertable"]], + ["risible", ["risable"]], + ["seducible", ["seducable"]], + ["sensible", ["sensable"]], + ["submersible", ["submersable"]], + ["submergible", ["submergable"]], + ["suggestible", ["suggestable"]], + ["suppressible", ["suppressable"]], + ["susceptible", ["susceptable"]], + ["terrible", ["terrable"]], + ["transfusible", ["transfusable"]], + ["transmissible", ["transmissable"]], + ["uncollectible", ["uncollectable"]], + ["vendible", ["vendable"]], + ["visible", ["visable"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/spelling/ally_ly.py b/proselint/checks/spelling/ally_ly.py index 8da588759..de4bf1911 100644 --- a/proselint/checks/spelling/ally_ly.py +++ b/proselint/checks/spelling/ally_ly.py @@ -1,10 +1,12 @@ """-ally vs. -ly.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-ally vs. -ly.""" err = "spelling.ally_ly" msg = "-ally vs. -ly. '{}' is the correct spelling." diff --git a/proselint/checks/spelling/ance_ence.py b/proselint/checks/spelling/ance_ence.py index 1089aad1b..f88735aee 100644 --- a/proselint/checks/spelling/ance_ence.py +++ b/proselint/checks/spelling/ance_ence.py @@ -1,16 +1,17 @@ """-ance vs. -ence.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-ance vs. -ence.""" err = "spelling.ance_ence" msg = "-ance vs. -ence. '{}' is the correct spelling." preferences = [ - ["appearance", ["appearence"]], ["occurrence", ["occurrance"]], ["resistance", ["resistence"]], diff --git a/proselint/checks/spelling/athletes.py b/proselint/checks/spelling/athletes.py index 73e692b7b..d4e62cdd0 100644 --- a/proselint/checks/spelling/athletes.py +++ b/proselint/checks/spelling/athletes.py @@ -12,33 +12,35 @@ Points out misspellings. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "spelling.athletes" msg = "Misspelling of athlete's name. '{}' is the preferred form." misspellings = [ - ["Dwyane Wade", ["Dwayne Wade"]], - ["Miikka Kiprusoff", ["Mikka Kiprusoff"]], - ["Mark Buehrle", ["Mark Buerhle"]], - ["Skylar Diggins", ["Skyler Diggins"]], + ["Dwyane Wade", ["Dwayne Wade"]], + ["Miikka Kiprusoff", ["Mikka Kiprusoff"]], + ["Mark Buehrle", ["Mark Buerhle"]], + ["Skylar Diggins", ["Skyler Diggins"]], ["Agnieszka Radwanska", ["Agnieska Radwanska"]], - ["J.J. Redick", ["J.J. Reddick"]], - ["Manny Pacquiao", ["Manny Packquaio"]], - ["Antawn Jamison", ["Antwan Jamison"]], - ["Cal Ripken", ["Cal Ripkin"]], - ["Jhonny Peralta", ["Johnny Peralta"]], - ["Monta Ellis", ["Monte Ellis"]], - ["Alex Rodriguez", ["Alex Rodriquez"]], - ["Mark Teixeira", ["Mark Texeira"]], - ["Brett Favre", ["Brett Farve"]], - ["Torii Hunter", ["Tori Hunter"]], - ["Stephen Curry", ["Stephon Curry"]], - ["Mike Krzyzewski", ["Mike Kryzewski"]], + ["J.J. Redick", ["J.J. Reddick"]], + ["Manny Pacquiao", ["Manny Packquaio"]], + ["Antawn Jamison", ["Antwan Jamison"]], + ["Cal Ripken", ["Cal Ripkin"]], + ["Jhonny Peralta", ["Johnny Peralta"]], + ["Monta Ellis", ["Monte Ellis"]], + ["Alex Rodriguez", ["Alex Rodriquez"]], + ["Mark Teixeira", ["Mark Texeira"]], + ["Brett Favre", ["Brett Farve"]], + ["Torii Hunter", ["Tori Hunter"]], + ["Stephen Curry", ["Stephon Curry"]], + ["Mike Krzyzewski", ["Mike Kryzewski"]], ] return preferred_forms_check(text, misspellings, err, msg) diff --git a/proselint/checks/spelling/ely_ly.py b/proselint/checks/spelling/ely_ly.py index 437e3422b..d80c67ac7 100644 --- a/proselint/checks/spelling/ely_ly.py +++ b/proselint/checks/spelling/ely_ly.py @@ -1,16 +1,17 @@ """-ely vs. -ly.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-ely vs. -ly.""" err = "spelling.ely_ly" msg = "-ely vs. -ly. '{}' is the correct spelling." preferences = [ - ["completely", ["completly"]], ["immediately", ["immediatly"]], ["unfortunately", ["unfortunatly"]], diff --git a/proselint/checks/spelling/em_im_en_in.py b/proselint/checks/spelling/em_im_en_in.py index 680a522d0..308412d5e 100644 --- a/proselint/checks/spelling/em_im_en_in.py +++ b/proselint/checks/spelling/em_im_en_in.py @@ -1,65 +1,66 @@ """Em vs. im, en vs. in.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """em- vs. en-, im- vs. in-.""" err = "spelling.em_im_en_in" msg = "em-, im-, en-, and in-. '{}' is the preferred spelling." preferences = [ - - ["embalm", ["imbalm"]], - ["embark", ["imbark"]], - ["embed", ["imbed"]], - ["embitter", ["imbitter"]], - ["emblaze", ["imblaze"]], - ["embody", ["imbody"]], - ["embolden", ["imbolden"]], - ["embosom", ["imbosom"]], - ["embower", ["imbower"]], - ["embrown", ["imbrown"]], - ["empanel", ["impanel"]], - ["empower", ["impower"]], - ["encage", ["incage"]], + ["embalm", ["imbalm"]], + ["embark", ["imbark"]], + ["embed", ["imbed"]], + ["embitter", ["imbitter"]], + ["emblaze", ["imblaze"]], + ["embody", ["imbody"]], + ["embolden", ["imbolden"]], + ["embosom", ["imbosom"]], + ["embower", ["imbower"]], + ["embrown", ["imbrown"]], + ["empanel", ["impanel"]], + ["empower", ["impower"]], + ["encage", ["incage"]], ["encapsulate", ["incapsulate"]], - ["encase", ["incase"]], - ["enclasp", ["inclasp"]], - ["encumber", ["incumber"]], + ["encase", ["incase"]], + ["enclasp", ["inclasp"]], + ["encumber", ["incumber"]], ["encumbrance", ["incumbrance"]], - ["endow", ["indow"]], - ["endowment", ["indowment"]], - ["endue", ["indue"]], - ["enfold", ["infold"]], - ["engraft", ["ingraft"]], - ["engulf", ["ingulf"]], - ["enlace", ["inlace"]], - ["enmesh", ["inmesh"]], - ["ensheathe", ["insheathe"]], - ["enshrine", ["inshrine"]], - ["ensnare", ["insnare"]], - ["ensoul", ["insoul"]], - ["ensphere", ["insphere"]], - ["enthrall", ["inthrall"]], - ["enthrone", ["inthrone"]], - ["entitle", ["intitle"]], - ["entomb", ["intomb"]], - ["entreat", ["intreat"]], - ["entrench", ["intrench"]], - ["entrust", ["intrust"]], - ["entwine", ["intwine"]], - ["entwist", ["intwist"]], - ["enwind", ["inwind"]], - ["enwrap", ["inwrap"]], - ["enwreathe", ["inwreathe"]], - ["imbrue", ["embrue"]], - ["impale", ["empale"]], - ["impoverish", ["empoverish"]], - ["inflame", ["enflame"]], - ["ingrain", ["engrain"]], - ["inure", ["enure"]], + ["endow", ["indow"]], + ["endowment", ["indowment"]], + ["endue", ["indue"]], + ["enfold", ["infold"]], + ["engraft", ["ingraft"]], + ["engulf", ["ingulf"]], + ["enlace", ["inlace"]], + ["enmesh", ["inmesh"]], + ["ensheathe", ["insheathe"]], + ["enshrine", ["inshrine"]], + ["ensnare", ["insnare"]], + ["ensoul", ["insoul"]], + ["ensphere", ["insphere"]], + ["enthrall", ["inthrall"]], + ["enthrone", ["inthrone"]], + ["entitle", ["intitle"]], + ["entomb", ["intomb"]], + ["entreat", ["intreat"]], + ["entrench", ["intrench"]], + ["entrust", ["intrust"]], + ["entwine", ["intwine"]], + ["entwist", ["intwist"]], + ["enwind", ["inwind"]], + ["enwrap", ["inwrap"]], + ["enwreathe", ["inwreathe"]], + ["imbrue", ["embrue"]], + ["impale", ["empale"]], + ["impoverish", ["empoverish"]], + ["inflame", ["enflame"]], + ["ingrain", ["engrain"]], + ["inure", ["enure"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/spelling/er_or.py b/proselint/checks/spelling/er_or.py index d4c84e324..59bbe82de 100644 --- a/proselint/checks/spelling/er_or.py +++ b/proselint/checks/spelling/er_or.py @@ -1,41 +1,42 @@ """-er vs. -or.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-er vs. -or.""" err = "spelling.er_or" msg = "-er vs. -or. '{}' is the preferred spelling." preferences = [ - - ["abductor", ["abducter"]], - ["abettor", ["abbeter"]], - ["acquirer", ["acquiror"]], - ["adapter", ["adaptor"]], - ["collector", ["collecter"]], - ["conjurer", ["conjuror"]], - ["corrupter", ["corruptor"]], - ["digester", ["digestor"]], - ["dispenser", ["dispensor"]], - ["distributor", ["distributer"]], - ["endorser", ["endorsor"]], - ["eraser", ["erasor"]], - ["idolater", ["idolator"]], - ["impostor", ["imposter"]], - ["infiltrator", ["infiltrater"]], - ["investor", ["invester"]], - ["manipulator", ["manipulater"]], - ["mortgagor", ["mortgager"]], - ["persecutor", ["persecuter"]], - ["promoter", ["promotor"]], - ["promoter", ["promotor"]], - ["purveyor", ["purveyer"]], - ["requester", ["requestor"]], - ["reviser", ["revisor"]], - ["surveyor", ["surveyer"]], + ["abductor", ["abducter"]], + ["abettor", ["abbeter"]], + ["acquirer", ["acquiror"]], + ["adapter", ["adaptor"]], + ["collector", ["collecter"]], + ["conjurer", ["conjuror"]], + ["corrupter", ["corruptor"]], + ["digester", ["digestor"]], + ["dispenser", ["dispensor"]], + ["distributor", ["distributer"]], + ["endorser", ["endorsor"]], + ["eraser", ["erasor"]], + ["idolater", ["idolator"]], + ["impostor", ["imposter"]], + ["infiltrator", ["infiltrater"]], + ["investor", ["invester"]], + ["manipulator", ["manipulater"]], + ["mortgagor", ["mortgager"]], + ["persecutor", ["persecuter"]], + ["promoter", ["promotor"]], + ["promoter", ["promotor"]], + ["purveyor", ["purveyer"]], + ["requester", ["requestor"]], + ["reviser", ["revisor"]], + ["surveyor", ["surveyer"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/spelling/in_un.py b/proselint/checks/spelling/in_un.py index 4f92f6b2e..cc9b8b639 100644 --- a/proselint/checks/spelling/in_un.py +++ b/proselint/checks/spelling/in_un.py @@ -1,20 +1,21 @@ """in- vs. un-.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """in- vs un-.""" err = "spelling.in_un" msg = "in- vs. un-. '{}' is the preferred spelling." preferences = [ - - ["inadvisable", ["unadvisable"]], - ["inalienable", ["unalienable"]], - ["inexpressive", ["unexpressive"]], - ["infeasible", ["unfeasible"]], + ["inadvisable", ["unadvisable"]], + ["inalienable", ["unalienable"]], + ["inexpressive", ["unexpressive"]], + ["infeasible", ["unfeasible"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/spelling/misc.py b/proselint/checks/spelling/misc.py index 33a0d0d92..209791968 100644 --- a/proselint/checks/spelling/misc.py +++ b/proselint/checks/spelling/misc.py @@ -12,135 +12,135 @@ Points out misspellings. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "spelling.misc" msg = "Misspelling. '{}' is the preferred spelling." misspellings = [ - ["a lot", ["alot"]], - ["accommodable", ["accomodatable"]], - ["anilingus", ["analingus"]], - ["aren't i", ["amn't i"]], - ["aren't i", ["an't i"]], - ["barbed wire", ["bob wire"]], - ["chargeable", ["chargable"]], - ["chiffonier", ["chiffonnier"]], - ["chintzy", ["chinchy"]], - ["chipotle", ["chipolte"]], - ["chlorophyll", ["chlorophyl"]], - ["chocolaty", ["chocolatey"]], - ["chronicle", ["chronical"]], - ["chronicles", ["chronicals"]], - ["coleslaw", ["coldslaw"]], - ["choosy", ["choosey"]], - ["cummerbund", ["kummerbund"]], - ["financeable", ["financable"]], - ["fluorescent", ["flourescent"]], - ["fluoridation", ["flouridation"]], - ["fluoride", ["flouride"]], - ["foreclose", ["forclose"]], - ["forswear", ["foreswear"]], - ["free rein", ["free reign"]], - ["gist", ["jist"]], - ["glamour", ["glamor"]], - ["granddad", ["grandad"]], - ["grandpa", ["granpa"]], - ["highfalutin", ["highfaluting", "highfalutin'", "hifalutin"]], - ["Hippocratic", ["hypocratic"]], - ["hirable", ["hireable"]], - ["holistic", ["wholistic"]], - ["ideology", ["idealogy"]], - ["idiosyncrasy", ["ideosyncracy"]], - ["improvise", ["improvize"]], - ["incurrence", ["incurment"]], - ["inevitability", ["inevitableness"]], - ["innovate", ["inovate"]], - ["inoculation", ["innoculation", "inocculation"]], - ["integral", ["intergral"]], - ["inundate", ["innundate"]], - ["inundated", ["innundated"]], - ["inundates", ["innundates"]], - ["inundating", ["innundating"]], - ["iridescent", ["irridescent"]], - ["irrelevant", ["irrevelant"]], - ["jujitsu", ["jiujutsu"]], - ["kaleidoscope", ["kaleidascope"]], - ["knickknack", ["knicknack"]], - ["lassos", ["lassoes"]], - ["lessee", ["leasee"]], - ["lessor", ["leasor"]], - ["liaison", ["liason"]], - ["liaison", ["laison"]], - ["lightning rod", ["lightening rod"]], + ["a lot", ["alot"]], + ["accommodable", ["accomodatable"]], + ["anilingus", ["analingus"]], + ["aren't i", ["amn't i"]], + ["aren't i", ["an't i"]], + ["barbed wire", ["bob wire"]], + ["chargeable", ["chargable"]], + ["chiffonier", ["chiffonnier"]], + ["chintzy", ["chinchy"]], + ["chipotle", ["chipolte"]], + ["chlorophyll", ["chlorophyl"]], + ["chocolaty", ["chocolatey"]], + ["chronicle", ["chronical"]], + ["chronicles", ["chronicals"]], + ["coleslaw", ["coldslaw"]], + ["choosy", ["choosey"]], + ["cummerbund", ["kummerbund"]], + ["financeable", ["financable"]], + ["fluorescent", ["flourescent"]], + ["fluoridation", ["flouridation"]], + ["fluoride", ["flouride"]], + ["foreclose", ["forclose"]], + ["forswear", ["foreswear"]], + ["free rein", ["free reign"]], + ["gist", ["jist"]], + ["glamour", ["glamor"]], + ["granddad", ["grandad"]], + ["grandpa", ["granpa"]], + ["highfalutin", ["highfaluting", "highfalutin'", "hifalutin"]], + ["Hippocratic", ["hypocratic"]], + ["hirable", ["hireable"]], + ["holistic", ["wholistic"]], + ["ideology", ["idealogy"]], + ["idiosyncrasy", ["ideosyncracy"]], + ["improvise", ["improvize"]], + ["incurrence", ["incurment"]], + ["inevitability", ["inevitableness"]], + ["innovate", ["inovate"]], + ["inoculation", ["innoculation", "inocculation"]], + ["integral", ["intergral"]], + ["inundate", ["innundate"]], + ["inundated", ["innundated"]], + ["inundates", ["innundates"]], + ["inundating", ["innundating"]], + ["iridescent", ["irridescent"]], + ["irrelevant", ["irrevelant"]], + ["jujitsu", ["jiujutsu"]], + ["kaleidoscope", ["kaleidascope"]], + ["knickknack", ["knicknack"]], + ["lassos", ["lassoes"]], + ["lessee", ["leasee"]], + ["lessor", ["leasor"]], + ["liaison", ["liason"]], + ["liaison", ["laison"]], + ["lightning rod", ["lightening rod"]], ["struck by lightning", ["struck by lightening"]], - ["liquefy", ["liquify"]], - ["loathsome", ["loathesome"]], - ["lodestar", ["loadstar"]], - ["to make do", ["to make due"]], - ["mademoiselle", ["madamoiselle"]], - ["martial arts", ["marshall arts"]], - ["court-martial", ["court marshall", "court-marshall"]], - ["maelstrom", ["maelstorm"]], - ["mafia", ["maffia"]], - ["mafiosi", ["mafiosos"]], - ["mangoes", ["mangos"]], - ["marijuana", ["marihuana"]], - ["marshmallow", ["marshmellow"]], - ["martial law", ["marshall law"]], - ["massacring", ["massacering", "massacreing"]], - ["measles", ["measels"]], - ["memento", ["momento"]], - ["minuscule", ["miniscule"]], - ["mileage", ["milage"]], - ["milieu", ["mileau"]], - ["millennium", ["millenium"]], - ["millennia", ["millenia"]], - ["millenniums", ["milleniums"]], - ["millipede", ["millepede"]], - ["millionaire", ["millionnaire"]], - ["milquetoast", ["milktoast"]], - ["mimicked", ["mimiced"]], - ["mimicking", ["mimicing"]], - ["misspelling", ["mispelling"]], - ["mischievous", ["mischievious"]], - ["moccasin", ["mocasin", "moccassin", "mocassin"]], - ["monologue", ["monolog"]], - ["monologuist", ["monologist"]], - ["naphtha", ["naptha"]], - ["navel orange", ["naval orange"]], - ["neckwear", ["neckware"]], - ["newsstand", ["newstand"]], - ["non sequitur", ["non sequiter", - "non sequitar", - "non sequitor"]], - ["nouveau riche", ["nouveau rich"]], - ["occurred", ["occured"]], + ["liquefy", ["liquify"]], + ["loathsome", ["loathesome"]], + ["lodestar", ["loadstar"]], + ["to make do", ["to make due"]], + ["mademoiselle", ["madamoiselle"]], + ["martial arts", ["marshall arts"]], + ["court-martial", ["court marshall", "court-marshall"]], + ["maelstrom", ["maelstorm"]], + ["mafia", ["maffia"]], + ["mafiosi", ["mafiosos"]], + ["mangoes", ["mangos"]], + ["marijuana", ["marihuana"]], + ["marshmallow", ["marshmellow"]], + ["martial law", ["marshall law"]], + ["massacring", ["massacering", "massacreing"]], + ["measles", ["measels"]], + ["memento", ["momento"]], + ["minuscule", ["miniscule"]], + ["mileage", ["milage"]], + ["milieu", ["mileau"]], + ["millennium", ["millenium"]], + ["millennia", ["millenia"]], + ["millenniums", ["milleniums"]], + ["millipede", ["millepede"]], + ["millionaire", ["millionnaire"]], + ["milquetoast", ["milktoast"]], + ["mimicked", ["mimiced"]], + ["mimicking", ["mimicing"]], + ["misspelling", ["mispelling"]], + ["mischievous", ["mischievious"]], + ["moccasin", ["mocasin", "moccassin", "mocassin"]], + ["monologue", ["monolog"]], + ["monologuist", ["monologist"]], + ["naphtha", ["naptha"]], + ["navel orange", ["naval orange"]], + ["neckwear", ["neckware"]], + ["newsstand", ["newstand"]], + ["non sequitur", ["non sequiter", "non sequitar", "non sequitor"]], + ["nouveau riche", ["nouveau rich"]], + ["occurred", ["occured"]], ["plantar fasciitis", ["planter fasciitis", "plantar fascitis"]], - ["pledgeable", ["pledgable", "pledgeble"]], - ["portentous", ["portentuous", "portentious"]], - ["praying mantis", ["preying mantis"]], - ["preestablished", ["prestablished"]], - ["preexisting", ["prexisting"]], - ["presumptuous", ["presumptious"]], - ["rarefy", ["rarify"]], - ["reckless", ["wreckless"]], - ["remuneration", ["renumeration"]], - ["restaurateur", ["restauranteur"]], - ["reverie", ["revery"]], - ["spicy", ["spicey"]], - ["stupefy", ["stupify"]], - ["subtly", ["subtley"]], - ["till", ["\'till?"]], - ["tinderbox", ["tenderbox"]], - ["timpani", ["tympani"]], - ["a timpani", ["a timpano"]], - ["umpteenth", ["umteenth"]], - ["verbiage", ["verbage"]], + ["pledgeable", ["pledgable", "pledgeble"]], + ["portentous", ["portentuous", "portentious"]], + ["praying mantis", ["preying mantis"]], + ["preestablished", ["prestablished"]], + ["preexisting", ["prexisting"]], + ["presumptuous", ["presumptious"]], + ["rarefy", ["rarify"]], + ["reckless", ["wreckless"]], + ["remuneration", ["renumeration"]], + ["restaurateur", ["restauranteur"]], + ["reverie", ["revery"]], + ["spicy", ["spicey"]], + ["stupefy", ["stupify"]], + ["subtly", ["subtley"]], + ["till", ["'till?"]], + ["tinderbox", ["tenderbox"]], + ["timpani", ["tympani"]], + ["a timpani", ["a timpano"]], + ["umpteenth", ["umteenth"]], + ["verbiage", ["verbage"]], ] return preferred_forms_check(text, misspellings, err, msg) diff --git a/proselint/checks/spelling/ve_of.py b/proselint/checks/spelling/ve_of.py index 359082567..ba8f11ff0 100644 --- a/proselint/checks/spelling/ve_of.py +++ b/proselint/checks/spelling/ve_of.py @@ -1,16 +1,17 @@ """-ve vs. -of.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """-ve vs. -of.""" err = "spelling.ve_of" msg = "-ve vs. -of. '{}' is the preferred spelling." preferences = [ - ["could've", ["could of"]], ["should've", ["should of"]], ["would've", ["would of"]], diff --git a/proselint/checks/terms/animal_adjectives.py b/proselint/checks/terms/animal_adjectives.py index 15d03ae5a..055fc991a 100644 --- a/proselint/checks/terms/animal_adjectives.py +++ b/proselint/checks/terms/animal_adjectives.py @@ -12,59 +12,60 @@ Animal words. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "garner.animal_labels" msg = "There's a word for this: '{}'." preferences = [ - - ["accipitrine", ["hawk-like"]], - ["anserine", ["goose-like"]], - ["aquiline", ["eagle-like"]], - ["avine", ["bird-like"]], - ["cancrine", ["crab-like"]], - ["hircine", ["goat-like"]], - ["damine", ["deer-like"]], - ["corvine", ["crow-like", "raven-like"]], - ["crocodiline", ["crocodile-like"]], - ["crotaline", ["rattlesnake-like"]], - ["falconine", ["falcon-like"]], - ["ferine", ["wild animal-like"]], + ["accipitrine", ["hawk-like"]], + ["anserine", ["goose-like"]], + ["aquiline", ["eagle-like"]], + ["avine", ["bird-like"]], + ["cancrine", ["crab-like"]], + ["hircine", ["goat-like"]], + ["damine", ["deer-like"]], + ["corvine", ["crow-like", "raven-like"]], + ["crocodiline", ["crocodile-like"]], + ["crotaline", ["rattlesnake-like"]], + ["falconine", ["falcon-like"]], + ["ferine", ["wild animal-like"]], ["hippopotamine", ["hippopotamus-like"]], - ["hirundine", ["swallow-like"]], - ["hystricine", ["porcupine-like"]], - ["lacertine", ["lizard-like"]], - ["laridine", ["gull-like"]], - ["leporine", ["hare-like"]], - ["lumbricine", ["earthworm-like"]], - ["lupine", ["wolf-like"]], - ["murine", ["mouse-like"]], - ["ovine", ["sheep-like"]], - ["pardine", ["leopard-like", "panther-like"]], - ["passerine", ["sparrow-like"]], - ["pavonine", ["peacock-like"]], - ["picine", ["woodpecker-like"]], - ["piscine", ["fish-like"]], - ["ranine", ["frog-like"]], + ["hirundine", ["swallow-like"]], + ["hystricine", ["porcupine-like"]], + ["lacertine", ["lizard-like"]], + ["laridine", ["gull-like"]], + ["leporine", ["hare-like"]], + ["lumbricine", ["earthworm-like"]], + ["lupine", ["wolf-like"]], + ["murine", ["mouse-like"]], + ["ovine", ["sheep-like"]], + ["pardine", ["leopard-like", "panther-like"]], + ["passerine", ["sparrow-like"]], + ["pavonine", ["peacock-like"]], + ["picine", ["woodpecker-like"]], + ["piscine", ["fish-like"]], + ["ranine", ["frog-like"]], ["scolopendrine", ["centipede-like"]], - ["soricine", ["shrew-like"]], - ["struthionine", ["ostrich-like"]], - ["suilline", ["swine-like"]], - ["taurine", ["bull-like", "ox-like"]], - ["tigrine", ["tiger-like"]], - ["vespine", ["wasp-like"]], - ["viperine", ["viper-like"]], - ["vituline", ["calf-like", "veal-like"]], - ["viverrine", ["mongoose-like"]], - ["vulpine", ["fox-like"]], - ["vulturine", ["vulture-like"]], - ["zebrine", ["zebra-like"]], - ["zibeline", ["sable-like"]], + ["soricine", ["shrew-like"]], + ["struthionine", ["ostrich-like"]], + ["suilline", ["swine-like"]], + ["taurine", ["bull-like", "ox-like"]], + ["tigrine", ["tiger-like"]], + ["vespine", ["wasp-like"]], + ["viperine", ["viper-like"]], + ["vituline", ["calf-like", "veal-like"]], + ["viverrine", ["mongoose-like"]], + ["vulpine", ["fox-like"]], + ["vulturine", ["vulture-like"]], + ["zebrine", ["zebra-like"]], + ["zibeline", ["sable-like"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/terms/denizen_labels.py b/proselint/checks/terms/denizen_labels.py index ba166cb21..a790fd440 100644 --- a/proselint/checks/terms/denizen_labels.py +++ b/proselint/checks/terms/denizen_labels.py @@ -1,10 +1,12 @@ """Denizen labels.""" -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms. source: Garner's Modern American Usage @@ -14,50 +16,48 @@ def check(text): msg = "'{}' is the preferred denizen label." preferences = [ - - ["Afrikaner", ["Afrikaaner"]], - ["Afrikaner", ["Afrikander"]], - ["Alabamian", ["Alabaman"]], - ["Albuquerquean", ["Albuquerquian"]], - ["Anchorageite", ["Anchoragite"]], - ["Angeleno", ["Los Angelean"]], - ["Arizonan", ["Arizonian"]], - ["Arkansan", ["Arkansawyer"]], - ["Belarusian", ["Belarusan"]], - ["Caymanian", ["Cayman Islander"]], - ["Coloradan", ["Coloradoan"]], - ["Fairbanksan", ["Fairbanksian"]], - ["Fort Worthian", ["Fort Worther"]], - ["Grenadan", ["Grenadian"]], - ["Hong Konger", ["Hong Kongite", "Hong Kongian"]], - ["Hoosier", ["Indianan", "Indianian"]], - ["Illinoisan", ["Illinoisian"]], - ["Iowan", ["Iowegian"]], - ["Louisianian", ["Louisianan"]], - ["Michigander", ["Michiganite", "Michiganian"]], - ["Missourian", ["Missouran"]], - ["Monegasque", ["Monacan"]], - ["Neapolitan", ["Neopolitan"]], - ["New Hampshirite", ["New Hampshireite", "New Hampshireman"]], - ["New Jerseyan", ["New Jerseyite"]], - ["New Orleanian", ["New Orleansian"]], - ["Nutmegger", ["Connecticuter"]], - ["Oklahoma Cityan", ["Oklahoma Citian"]], - ["Oklahoman", ["Oklahomian"]], - ["Seattleite", ["Seattlite"]], - ["Surinamese", ["Surinamer"]], - ["Tallahasseean", ["Tallahassean"]], - ["Tennessean", ["Tennesseean"]], - ["Tusconan", ["Tusconian", "Tusconite"]], - ["Utahn", ["Utahan"]], - ["Saudi", ["Saudi Arabian"]], + ["Afrikaner", ["Afrikaaner"]], + ["Afrikaner", ["Afrikander"]], + ["Alabamian", ["Alabaman"]], + ["Albuquerquean", ["Albuquerquian"]], + ["Anchorageite", ["Anchoragite"]], + ["Angeleno", ["Los Angelean"]], + ["Arizonan", ["Arizonian"]], + ["Arkansan", ["Arkansawyer"]], + ["Belarusian", ["Belarusan"]], + ["Caymanian", ["Cayman Islander"]], + ["Coloradan", ["Coloradoan"]], + ["Fairbanksan", ["Fairbanksian"]], + ["Fort Worthian", ["Fort Worther"]], + ["Grenadan", ["Grenadian"]], + ["Hong Konger", ["Hong Kongite", "Hong Kongian"]], + ["Hoosier", ["Indianan", "Indianian"]], + ["Illinoisan", ["Illinoisian"]], + ["Iowan", ["Iowegian"]], + ["Louisianian", ["Louisianan"]], + ["Michigander", ["Michiganite", "Michiganian"]], + ["Missourian", ["Missouran"]], + ["Monegasque", ["Monacan"]], + ["Neapolitan", ["Neopolitan"]], + ["New Hampshirite", ["New Hampshireite", "New Hampshireman"]], + ["New Jerseyan", ["New Jerseyite"]], + ["New Orleanian", ["New Orleansian"]], + ["Nutmegger", ["Connecticuter"]], + ["Oklahoma Cityan", ["Oklahoma Citian"]], + ["Oklahoman", ["Oklahomian"]], + ["Seattleite", ["Seattlite"]], + ["Surinamese", ["Surinamer"]], + ["Tallahasseean", ["Tallahassean"]], + ["Tennessean", ["Tennesseean"]], + ["Tusconan", ["Tusconian", "Tusconite"]], + ["Utahn", ["Utahan"]], + ["Saudi", ["Saudi Arabian"]], ] return preferred_forms_check(text, preferences, err, msg) -@memoize -def check_denizen_labels_norris(text): +def check_denizen_labels_norris(text: str) -> list[ResultCheck]: """Suggest the preferred forms. source: Mary Norris @@ -67,20 +67,20 @@ def check_denizen_labels_norris(text): msg = "Would you like '{}'?" preferences = [ - ["Mancunian", ["Manchesterian"]], - ["Mancunians", ["Manchesterians"]], - ["Vallisoletano", ["Valladolidian"]], - ["Wulfrunian", ["Wolverhamptonian", "Wolverhamptonite"]], - ["Novocastrian", ["Newcastleite", "Newcastlite"]], - ["Trifluvian", ["Trois-Rivièrester"]], - ["Leodenisian", ["Leedsian"]], - ["Minneapolitan", ["Minneapolisian"]], - ["Hartlepudlian", ["Hartlepoolian"]], - ["Liverpudlian", ["Liverpoolian"]], - ["Haligonian", ["Halifaxer"]], - ["Varsovian", ["Warsawer", "Warsawian"]], - ["Providentian", ["Providencian", "Providencer"]], - ["Tridentine", ["Trentian", "Trentonian"]], + ["Mancunian", ["Manchesterian"]], + ["Mancunians", ["Manchesterians"]], + ["Vallisoletano", ["Valladolidian"]], + ["Wulfrunian", ["Wolverhamptonian", "Wolverhamptonite"]], + ["Novocastrian", ["Newcastleite", "Newcastlite"]], + ["Trifluvian", ["Trois-Rivièrester"]], + ["Leodenisian", ["Leedsian"]], + ["Minneapolitan", ["Minneapolisian"]], + ["Hartlepudlian", ["Hartlepoolian"]], + ["Liverpudlian", ["Liverpoolian"]], + ["Haligonian", ["Halifaxer"]], + ["Varsovian", ["Warsawer", "Warsawian"]], + ["Providentian", ["Providencian", "Providencer"]], + ["Tridentine", ["Trentian", "Trentonian"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/terms/eponymous_adjectives.py b/proselint/checks/terms/eponymous_adjectives.py index 81e1dd094..bda0d91fa 100644 --- a/proselint/checks/terms/eponymous_adjectives.py +++ b/proselint/checks/terms/eponymous_adjectives.py @@ -12,19 +12,20 @@ Eponymous adjectives. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "garner.eponymous_adjective" msg = "'{}' is the preferred eponymous adjective." preferences = [ - - ["Mephistophelean", ["Mephistophelian"]], - ["Shakespearean", ["Shakespearian"]], + ["Mephistophelean", ["Mephistophelian"]], + ["Shakespearean", ["Shakespearian"]], ] return preferred_forms_check(text, preferences, err, msg) diff --git a/proselint/checks/terms/venery.py b/proselint/checks/terms/venery.py index f6d24cf59..e58699d79 100644 --- a/proselint/checks/terms/venery.py +++ b/proselint/checks/terms/venery.py @@ -12,65 +12,72 @@ Names for groups of animals. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +import re -@memoize -def check(text): +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check + + +def check(text: str) -> list[ResultCheck]: """Check the text.""" + if not any(re.finditer("group|bunch", text, flags=re.IGNORECASE)): + return [] + err = "oxford.venery_terms" msg = "The venery term is '{}'." term_list = [ - ["alligators", "congregation"], - ["antelopes", "herd"], - ["baboons", "troop"], - ["badgers", "cete"], - ["bats", "colony"], - ["bears", "sloth"], - ["buffalo", "herd"], - ["bullfinches", "bellowing"], - ["caribou", "herd"], - ["cats", "glaring"], + ["alligators", "congregation"], + ["antelopes", "herd"], + ["baboons", "troop"], + ["badgers", "cete"], + ["bats", "colony"], + ["bears", "sloth"], + ["buffalo", "herd"], + ["bullfinches", "bellowing"], + ["caribou", "herd"], + ["cats", "glaring"], ["caterpillars", "army"], - ["cockroaches", "intrusion"], - ["coyotes", "pack"], - ["crows", "murder"], - ["dogs", "pack"], - ["eagles", "convocation"], - ["emus", "mob"], - ["flamingos", "stand"], - ["frogs", "army"], - ["goldfinches", "charm"], - ["gorillas", "band"], - ["guineafowl", "rasp"], - ["hedgehogs", "array"], - ["herons", "siege"], - ["hogs", "parcel"], - ["hyenas", "cackle"], - ["ibex", "herd"], - ["iguanas", "mess"], - ["lions", "pride"], - ["locusts", "plague"], - ["mackerel", "shoal"], - ["mares", "stud"], - ["minnows", "shoal"], - ["moose", "herd"], - ["mosquitoes", "scourge"], + ["cockroaches", "intrusion"], + ["coyotes", "pack"], + ["crows", "murder"], + ["dogs", "pack"], + ["eagles", "convocation"], + ["emus", "mob"], + ["flamingos", "stand"], + ["frogs", "army"], + ["goldfinches", "charm"], + ["gorillas", "band"], + ["guineafowl", "rasp"], + ["hedgehogs", "array"], + ["herons", "siege"], + ["hogs", "parcel"], + ["hyenas", "cackle"], + ["ibex", "herd"], + ["iguanas", "mess"], + ["lions", "pride"], + ["locusts", "plague"], + ["mackerel", "shoal"], + ["mares", "stud"], + ["minnows", "shoal"], + ["moose", "herd"], + ["mosquitoes", "scourge"], ["nightingales", "watch"], - ["oysters", "bed"], - ["partridges", "covey"], - ["pelicans", "pod"], - ["raccoons", "gaze"], - ["ravens", "unkindness"], + ["oysters", "bed"], + ["partridges", "covey"], + ["pelicans", "pod"], + ["raccoons", "gaze"], + ["ravens", "unkindness"], ["rhinoceroses", "crash"], - ["sea urchins", "sea"], - ["starlings", "murmuration"], - ["toads", "knot"], - ["wombats", "wisdom"], - ["woodcocks", "fall"], - ["woodpeckers", "descent"], - ["wrens", "herd"], + ["sea urchins", "sea"], + ["starlings", "murmuration"], + ["toads", "knot"], + ["wombats", "wisdom"], + ["woodcocks", "fall"], + ["woodpeckers", "descent"], + ["wrens", "herd"], ] generic_terms = [ @@ -78,11 +85,14 @@ def check(text): "bunch", ] - list = [] + # NOTE: python automatically caches this calculation for reruns + # check with benchmark_checks.py + + items = [] for term_pair in term_list: for generic in generic_terms: wrong = f"a {generic} of {term_pair[0]}" right = f"a {term_pair[1]} of {term_pair[0]}" - list += [[right, [wrong]]] + items += [[right, [wrong]]] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/typography/diacritical_marks.py b/proselint/checks/typography/diacritical_marks.py index 08a2805ee..9cf9a3cd1 100644 --- a/proselint/checks/typography/diacritical_marks.py +++ b/proselint/checks/typography/diacritical_marks.py @@ -2,111 +2,108 @@ Use of diacritical marks where common. """ -from proselint.tools import memoize, preferred_forms_check +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import preferred_forms_check -@memoize -def check(text): + +def check(text: str) -> list[ResultCheck]: """Suggest the preferred forms.""" err = "typography.diacritical_marks" msg = "Use diacritical marks in '{}'." - list = [ + items = [ # French loanwords - ["beau idéal", ["beau ideal"]], - ["boutonnière", ["boutonniere"]], - ["bric-à-brac", ["bric-a-brac"]], - ["café", ["cafe"]], - ["cause célèbre", ["cause celebre"]], - ["chèvre", ["chevre"]], - ["cliché", ["cliche"]], - ["comme ci comme ça", ["comme ci comme ca", "comsi comsa"]], - ["consommé", ["consomme"]], - ["coup d'état", ["coup d'etat"]], - ["coup de grâce", ["coup de grace"]], - ["crudités", ["crudites"]], - ["crème brûlée", ["creme brulee"]], - ["crème de menthe", ["creme de menthe"]], - ["crème fraîche", ["creme fraice"]], - ["crème fraîche", ["creme fresh"]], - ["crêpe", ["crepe"]], - ["débutante", ["debutante"]], - ["décor", ["decor"]], - ["déjà vu", ["deja vu"]], - ["dénouement", ["denouement"]], - ["façade", ["facade"]], - ["fiancé", ["fiance"]], - ["fiancée", ["fiancee"]], - ["flambé", ["flambe"]], - ["garçon", ["garcon"]], - ["lycée", ["lycee"]], - ["maître d", ["maitre d"]], - ["ménage à trois", ["menage a trois"]], - ["négligée", ["negligee"]], + ["beau idéal", ["beau ideal"]], + ["boutonnière", ["boutonniere"]], + ["bric-à-brac", ["bric-a-brac"]], + ["café", ["cafe"]], + ["cause célèbre", ["cause celebre"]], + ["chèvre", ["chevre"]], + ["cliché", ["cliche"]], + ["comme ci comme ça", ["comme ci comme ca", "comsi comsa"]], + ["consommé", ["consomme"]], + ["coup d'état", ["coup d'etat"]], + ["coup de grâce", ["coup de grace"]], + ["crudités", ["crudites"]], + ["crème brûlée", ["creme brulee"]], + ["crème de menthe", ["creme de menthe"]], + ["crème fraîche", ["creme fraice"]], + ["crème fraîche", ["creme fresh"]], + ["crêpe", ["crepe"]], + ["débutante", ["debutante"]], + ["décor", ["decor"]], + ["déjà vu", ["deja vu"]], + ["dénouement", ["denouement"]], + ["façade", ["facade"]], + ["fiancé", ["fiance"]], + ["fiancée", ["fiancee"]], + ["flambé", ["flambe"]], + ["garçon", ["garcon"]], + ["lycée", ["lycee"]], + ["maître d", ["maitre d"]], + ["ménage à trois", ["menage a trois"]], + ["négligée", ["negligee"]], ["papier-mâché", ["papier-mache", "paper mache", "paper-mache"]], - ["protégé", ["protege"]], - ["protégée", ["protegee"]], - ["purée", ["puree"]], - ["raison d'être", ["raison d'etre"]], - ["my résumé", ["my resume"]], - ["your résumé", ["your resume"]], - ["his résumé", ["his resume"]], - ["her résumé", ["her resume"]], - ["a résumé", ["a resume"]], - ["the résumé", ["the resume"]], - ["risqué", ["risque"]], - ["roué", ["roue"]], - ["soirée", ["soiree"]], - ["soufflé", ["souffle"]], - ["soupçon", ["soupcon"]], - ["touché", ["touche"]], - ["tête-à-tête", ["tete-a-tete"]], - ["voilà", ["voila"]], - ["à la carte", ["a la carte"]], - ["à la mode", ["a la mode"]], - ["émigré", ["emigre"]], - + ["protégé", ["protege"]], + ["protégée", ["protegee"]], + ["purée", ["puree"]], + ["raison d'être", ["raison d'etre"]], + ["my résumé", ["my resume"]], + ["your résumé", ["your resume"]], + ["his résumé", ["his resume"]], + ["her résumé", ["her resume"]], + ["a résumé", ["a resume"]], + ["the résumé", ["the resume"]], + ["risqué", ["risque"]], + ["roué", ["roue"]], + ["soirée", ["soiree"]], + ["soufflé", ["souffle"]], + ["soupçon", ["soupcon"]], + ["touché", ["touche"]], + ["tête-à-tête", ["tete-a-tete"]], + ["voilà", ["voila"]], + ["à la carte", ["a la carte"]], + ["à la mode", ["a la mode"]], + ["émigré", ["emigre"]], # Spanish loanwords - ["El Niño", ["El Nino"]], - ["jalapeño", ["jalapeno"]], - ["La Niña", ["La Nina"]], - ["piña colada", ["pina colada"]], - ["señor", ["senor"]], - ["señora", ["senora"]], - ["señorita", ["senorita"]], - + ["El Niño", ["El Nino"]], + ["jalapeño", ["jalapeno"]], + ["La Niña", ["La Nina"]], + ["piña colada", ["pina colada"]], + ["señor", ["senor"]], + ["señora", ["senora"]], + ["señorita", ["senorita"]], # Portuguese loanwords - ["açaí", ["acai"]], - + ["açaí", ["acai"]], # German loanwords - ["doppelgänger", ["doppelganger"]], - ["Führer", ["Fuhrer"]], - ["Gewürztraminer", ["Gewurztraminer"]], - ["vis-à-vis", ["vis-a-vis"]], - ["Übermensch", ["Ubermensch"]], - + ["doppelgänger", ["doppelganger"]], + ["Führer", ["Fuhrer"]], + ["Gewürztraminer", ["Gewurztraminer"]], + ["vis-à-vis", ["vis-a-vis"]], + ["Übermensch", ["Ubermensch"]], # Swedish loanwords - ["filmjölk", ["filmjolk"]], - ["smörgåsbord", ["smorgasbord"]], - + ["filmjölk", ["filmjolk"]], + ["smörgåsbord", ["smorgasbord"]], # Names, places, and companies - ["Beyoncé", ["Beyonce"]], - ["Brontë", ["Bronte"]], - ["Brontë", ["Bronte"]], - ["Champs-Élysées", ["Champs-Elysees"]], - ["Citroën", ["Citroen"]], - ["Curaçao", ["Curacao"]], - ["Häagen-Dazs", ["Haagen-Dazs", "Haagen Dazs"]], - ["Löwenbräu", ["Lowenbrau"]], - ["Monégasque", ["Monegasque"]], - ["Mötley Crüe", ["Motley Crue"]], - ["Nescafé", ["Nescafe"]], - ["Queensrÿche", ["Queensryche"]], - ["Québec", ["Quebec"]], - ["Québécois", ["Quebecois"]], - ["Ångström", ["Angstrom"]], - ["ångström", ["angstrom"]], - ["Škoda", ["Skoda"]], + ["Beyoncé", ["Beyonce"]], + ["Brontë", ["Bronte"]], + ["Brontë", ["Bronte"]], + ["Champs-Élysées", ["Champs-Elysees"]], + ["Citroën", ["Citroen"]], + ["Curaçao", ["Curacao"]], + ["Häagen-Dazs", ["Haagen-Dazs", "Haagen Dazs"]], + ["Löwenbräu", ["Lowenbrau"]], + ["Monégasque", ["Monegasque"]], + ["Mötley Crüe", ["Motley Crue"]], + ["Nescafé", ["Nescafe"]], + ["Queensrÿche", ["Queensryche"]], + ["Québec", ["Quebec"]], + ["Québécois", ["Quebecois"]], + ["Ångström", ["Angstrom"]], + ["ångström", ["angstrom"]], + ["Škoda", ["Skoda"]], ] - return preferred_forms_check(text, list, err, msg) + return preferred_forms_check(text, items, err, msg) diff --git a/proselint/checks/typography/exclamation.py b/proselint/checks/typography/exclamation.py index 1e13025cf..697563c6c 100644 --- a/proselint/checks/typography/exclamation.py +++ b/proselint/checks/typography/exclamation.py @@ -12,29 +12,40 @@ Too much yelling. """ -from proselint.tools import existence_check, max_errors, memoize, ppm_threshold +from __future__ import annotations +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results +from proselint.checks import ppm_threshold -@max_errors(1) -@memoize -def check_repeated_exclamations(text): + +@limit_results(1) +def check_repeated_exclamations(text: str) -> list[ResultCheck]: """Check the text.""" err = "leonard.exclamation.multiple" msg = "Stop yelling. Keep your exclamation points under control." - regex = r"[\!]\s*?[\!]{1,}" + items = [r"[\!]\s*?[\!]{1,}"] - return existence_check(text, [regex], err, msg, require_padding=False, - ignore_case=False, dotall=True) + return existence_check( + text, + items, + err, + msg, + padding=Pd.disabled, + ignore_case=False, + dotall=True, + ) -@ppm_threshold(30) -@memoize -def check_exclamations_ppm(text): +@ppm_threshold(30) # TODO: isn't that way too low? +def check_exclamations_ppm(text: str) -> list[ResultCheck]: """Make sure that the exclamation ppm is under 30.""" err = "leonard.exclamation.30ppm" msg = "More than 30 ppm of exclamations. Keep them under control." - regex = r"\w!" + items = [r"\w!"] - return existence_check(text, [regex], err, msg, require_padding=False) + return existence_check(text, items, err, msg, padding=Pd.disabled) diff --git a/proselint/checks/typography/symbols.py b/proselint/checks/typography/symbols.py index 6e940d3cd..c25df212d 100644 --- a/proselint/checks/typography/symbols.py +++ b/proselint/checks/typography/symbols.py @@ -4,88 +4,92 @@ source_url: http://practicaltypography.com/ """ -from proselint.tools import existence_check, max_errors, memoize - - -@max_errors(3) -@memoize -def check_ellipsis(text): - """Use an ellipsis instead of three dots.""" - err = "typography.symbols.ellipsis" - msg = "'...' is an approximation, use the ellipsis symbol '…'." - regex = r"\.\.\." - - return existence_check(text, [regex], err, msg, require_padding=False, - offset=0) - - -@max_errors(1) -@memoize -def check_copyright_symbol(text): - """Use the copyright symbol instead of (c).""" - err = "typography.symbols.copyright" - msg = "(c) is a goofy alphabetic approximation, use the symbol ©." - regex = r"\([cC]\)" - - return existence_check(text, [regex], err, msg, require_padding=False) - - -@max_errors(3) -@memoize -def check_trademark_symbol(text): - """Use the trademark symbol instead of (TM).""" - err = "typography.symbols.trademark" - msg = "(TM) is a goofy alphabetic approximation, use the symbol ™." - regex = r"\(TM\)" - - return existence_check(text, [regex], err, msg, require_padding=False) - - -@max_errors(3) -@memoize -def check_registered_trademark_symbol(text): - """Use the registered trademark symbol instead of (R).""" - err = "typography.symbols.trademark" - msg = "(R) is a goofy alphabetic approximation, use the symbol ®." - regex = r"\([rR]\)" - - return existence_check(text, [regex], err, msg, require_padding=False) - - -@max_errors(3) -@memoize -def check_sentence_spacing(text): - """Use no more than two spaces after a period.""" - err = "typography.symbols.sentence_spacing" - msg = "More than two spaces after the period; use 1 or 2." - regex = r"\. {3}" - - return existence_check(text, [regex], err, msg, require_padding=False) - - -@max_errors(3) -@memoize -def check_multiplication_symbol(text): - """Use the multiplication symbol ×, not the lowercase letter x.""" - err = "typography.symbols.multiplication_symbol" - msg = "Use the multiplication symbol ×, not the letter x." - regex = r"[0-9]+ ?x ?[0-9]+" - - return existence_check(text, [regex], err, msg, require_padding=False) - - -@max_errors(3) -@memoize -def check_curly_quotes(text): - """Use curly quotes, not straight quotes.""" - err = "typography.symbols.curly_quotes" - msg = 'Use curly quotes “”, not straight quotes "".' - regex = r"\"[\w\s\d]+\"" - - return existence_check(text, [regex], err, msg, require_padding=False) - -# @memoize -# def check_en_dash_separated_names(text): -# """Use an en-dash to separate names.""" -# # [u"[A-Z][a-z]{1,10}[-\u2014][A-Z][a-z]{1,10}", -# # u"Use an en dash (–) to separate names."], +from __future__ import annotations + +from proselint.checks import Pd +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results + + +def check(text: str) -> list[ResultCheck]: + # NOTE: these tests are pretty fast, so they are combined for a more even + # load + + @limit_results(3) + def check_ellipsis(_text: str) -> list[ResultCheck]: + """Use an ellipsis instead of three dots.""" + err = "typography.symbols.ellipsis" + msg = "'...' is an approximation, use the ellipsis symbol '…'." + items = [r"\.\.\."] + + return existence_check(_text, items, err, msg, padding=Pd.disabled) + + @limit_results(1) + def check_copyright_symbol(_text: str) -> list[ResultCheck]: + """Use the copyright symbol instead of (c).""" + err = "typography.symbols.copyright" + msg = "(c) is a goofy alphabetic approximation, use the symbol ©." + regex = r"\([cC]\)" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + @limit_results(3) + def check_trademark_symbol(_text: str) -> list[ResultCheck]: + """Use the trademark symbol instead of (TM).""" + err = "typography.symbols.trademark" + msg = "(TM) is a goofy alphabetic approximation, use the symbol ™." + regex = r"\(TM\)" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + @limit_results(3) + def check_registered_trademark_symbol(_text: str) -> list[ResultCheck]: + """Use the registered trademark symbol instead of (R).""" + err = "typography.symbols.trademark" + msg = "(R) is a goofy alphabetic approximation, use the symbol ®." + regex = r"\([rR]\)" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + @limit_results(3) + def check_sentence_spacing(_text: str) -> list[ResultCheck]: + """Use no more than two spaces after a period.""" + err = "typography.symbols.sentence_spacing" + msg = "More than two spaces after the period; use 1 or 2." + regex = r"\. {3}" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + @limit_results(3) + def check_multiplication_symbol(_text: str) -> list[ResultCheck]: + """Use the multiplication symbol ×, not the lowercase letter x.""" + err = "typography.symbols.multiplication_symbol" + msg = "Use the multiplication symbol ×, not the letter x." + regex = r"[0-9]+ ?x ?[0-9]+" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + @limit_results(3) + def check_curly_quotes(_text: str) -> list[ResultCheck]: + """Use curly quotes, not straight quotes.""" + err = "typography.symbols.curly_quotes" + msg = 'Use curly quotes “”, not straight quotes "".' + regex = r"\"[\w\s\d]+\"" + + return existence_check(_text, [regex], err, msg, padding=Pd.disabled) + + def disabled_check_en_dash_separated_names(text: str) -> list[ResultCheck]: + """Use an en-dash to separate names.""" + # [u"[A-Z][a-z]{1,10}[-\u2014][A-Z][a-z]{1,10}", + # u"Use an en dash (–) to separate names."], + return [] # TODO + + result = check_ellipsis(text) + result.extend(check_copyright_symbol(text)) + result.extend(check_trademark_symbol(text)) + result.extend(check_registered_trademark_symbol(text)) + result.extend(check_sentence_spacing(text)) + result.extend(check_multiplication_symbol(text)) + result.extend(check_curly_quotes(text)) + return result diff --git a/proselint/checks/uncomparables/misc.py b/proselint/checks/uncomparables/misc.py index e85039f60..f81041b73 100644 --- a/proselint/checks/uncomparables/misc.py +++ b/proselint/checks/uncomparables/misc.py @@ -15,12 +15,12 @@ can be a little tricky. Among other uncomparables are precise, exact, correct, entire, accurate, preferable, inevitable, possible, false; there are probably two dozen in all. These adjectives all describe absolute, non-negotiable -states: something is either false or it’s not; something is either inevitable -or it’s not. Many writers get careless and try to modify uncomparables with +states: something is either false or it's not; something is either inevitable +or it's not. Many writers get careless and try to modify uncomparables with comparatives like more and less or intensives like very. But if you really think about them, the core assertions in sentences like “War is becoming increasingly inevitable as Middle East tensions rise”; “Their cost estimate was -more accurate than the other firms’”; and “As a mortician, he has a very unique +more accurate than the other firms'”; and “As a mortician, he has a very unique attitude” are nonsense. If something is inevitable, it is bound to happen; it cannot be bound to happen and then somehow even more bound to happen. Unique already means one-of-a-kind, so the adj. phrase very unique is at best @@ -33,7 +33,7 @@ problem of uncomparables is the dissimilarities between Standard Written English and the language of advertising. Advertising English, which probably deserves to be studied as its own dialect, operates under different syntactic -rules than SWE, mainly because AE’s goals and assumptions are different. +rules than SWE, mainly because AE's goals and assumptions are different. Sentences like “We offer a totally unique dining experience”; “Come on down and receive your free gift”; and “Save up to 50 per cent… and more!” are perfectly OK in Advertising English — but this is because Advertising English is aimed at @@ -43,17 +43,15 @@ One axiom of Standard Written English is that your reader is paying close attention and expects you to have done the same. """ +from __future__ import annotations + import itertools -from proselint.tools import existence_check, memoize +from proselint.checks import ResultCheck +from proselint.checks import existence_check -@memoize -def check(text): - """Check the text.""" - err = "uncomparables.misc" - msg = "Comparison of an uncomparable: '{}' is not comparable." - +def compile_uncomparables() -> list: comparators = [ "most", "more", @@ -65,10 +63,10 @@ def check(text): "extremely", "increasingly", "kind of", - "mildly" + "mildly", ] - uncomparables = [ + _uncomparables = [ "absolute", "adequate", "chief", @@ -110,10 +108,37 @@ def check(text): exceptions = [ ("more", "perfect"), - ("more", "possible") # FIXME + ("more", "possible"), + ] + return [ + rf"{i[0]}\s{i[1]}" + for i in itertools.product(comparators, _uncomparables) + if i not in exceptions ] - uncomparables = [fr"{i[0]}\s{i[1]}" for i in itertools.product( - comparators, uncomparables) if i not in exceptions] - return existence_check(text, uncomparables, err, msg, require_padding=True) +items = compile_uncomparables() + + +def check_1(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ + err = "uncomparables.misc" + msg = "Comparison of an uncomparable: '{}' is not comparable." + + return existence_check(text, items[: round(len(items) / 2)], err, msg) + + +def check_2(text: str) -> list[ResultCheck]: + """Check the text. + + NOTE: this was one of the slowest Checks, + so it was segmented to even the load for parallelization + """ + err = "uncomparables.misc" + msg = "Comparison of an uncomparable: '{}' is not comparable." + + return existence_check(text, items[round(len(items) / 2) :], err, msg) diff --git a/proselint/checks/weasel_words/misc.py b/proselint/checks/weasel_words/misc.py index 14d757a4d..9941e5fd6 100644 --- a/proselint/checks/weasel_words/misc.py +++ b/proselint/checks/weasel_words/misc.py @@ -12,11 +12,36 @@ Weasel words clearly weaken various aspects of a number of your sentences. """ +from __future__ import annotations +from typing import TYPE_CHECKING -# def check(text): +from proselint.checks import existence_check -# error_code = "weasel_words.misc" -# msg = "Weasel words present." +if TYPE_CHECKING: + from proselint.checks import ResultCheck -# return [(1, 1, error_code, msg)] + +def check(text: str) -> list[ResultCheck]: + """ + source: https://en.wikipedia.org/wiki/Weasel_word + """ + error_code = "weasel_words.misc" + msg = "Weasel words aka. anonymous authority present ({})." + + items = [ + # vague + "evidence suggests", + "in most respects", + "some people", + "somewhat", + # false authority + "it has been decided", + "it is said", + "it is thought", + "mistakes were made", + "researchers believe", + "some people say", + ] + + return existence_check(text, items, error_code, msg) diff --git a/proselint/checks/weasel_words/very.py b/proselint/checks/weasel_words/very.py index 35a36d078..4c14c4fca 100644 --- a/proselint/checks/weasel_words/very.py +++ b/proselint/checks/weasel_words/very.py @@ -13,17 +13,22 @@ delete it and the writing will be just as it should be. """ -from proselint.tools import existence_check, max_errors, memoize +from __future__ import annotations +from proselint.checks import ResultCheck +from proselint.checks import existence_check +from proselint.checks import limit_results -@max_errors(1) -@memoize -def check(text): + +@limit_results(1) +def check(text: str) -> list[ResultCheck]: """Avoid 'very'.""" err = "weasel_words.very" - msg = ("Substitute 'damn' every time you're " - "inclined to write 'very'; your editor will delete it " - "and the writing will be just as it should be.") + msg = ( + "Substitute 'damn' every time you're " + "inclined to write 'very'; your editor will delete it " + "and the writing will be just as it should be." + ) regex = "very" return existence_check(text, [regex], err, msg) diff --git a/proselint/command_line.py b/proselint/command_line.py index 3cdeb2f98..2334831ad 100644 --- a/proselint/command_line.py +++ b/proselint/command_line.py @@ -1,185 +1,185 @@ """Command line utility for proselint.""" +from __future__ import annotations + import json import os -import shutil -import subprocess +import signal +import subprocess # noqa: S404 import sys -import traceback +import time +from pathlib import Path +from typing import TYPE_CHECKING +from typing import Optional +from typing import Union import click -from .config import default -from .tools import (close_cache_shelves, close_cache_shelves_after, - errors_to_json, lint, load_options) -from .version import __version__ - -CONTEXT_SETTINGS = {"help_option_names": ['-h', '--help']} -base_url = "proselint.com/" -proselint_path = os.path.dirname(os.path.realpath(__file__)) -demo_file = os.path.join(proselint_path, "demo.md") - - -# TODO: fix broken corpus -def timing_test(corpus="0.1.0"): - """Measure timing performance on the named corpus.""" - import time - dirname = os.path.dirname - corpus_path = os.path.join( - dirname(dirname(os.path.realpath(__file__))), "corpora", corpus) - start = time.time() - for file in os.listdir(corpus_path): - filepath = os.path.join(corpus_path, file) - if ".md" == filepath[-3:]: - subprocess.call(["proselint", filepath, ">/dev/null"]) - - return time.time() - start +from proselint.tools import output_errors +from . import tools +from .config_base import Output +from .config_base import proselint_base +from .config_paths import demo_file +from .logger import log +from .logger import set_verbosity +from .memoizer import cache +from .version import __version__ -def clear_cache(): - """Delete the contents of the cache.""" - click.echo("Deleting the cache...") - - # see issue #624 - _delete_compiled_python_files() - _delete_cache() - +if TYPE_CHECKING: + from types import FrameType -def _delete_compiled_python_files(): - """Remove files with a 'pyc' extension.""" - for path, _, files in os.walk(os.getcwd()): - for fname in [f for f in files if os.path.splitext(f)[1] == ".pyc"]: - try: - os.remove(os.path.join(path, fname)) - except OSError: - pass +CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]} +base_url = "proselint.com/" -def _delete_cache(): - """Remove the proselint cache.""" - proselint_cache = os.path.join("proselint", "cache") - try: - shutil.rmtree(proselint_cache) - except OSError: - pass +def exit_gracefully(_signum: int, _frame: FrameType | None) -> None: + log.warning("Exiting!") + sys.exit(0) -def print_errors(filename, errors, output_json=False, compact=False): - """Print the errors, resulting from lint, for filename.""" - if output_json: - click.echo(errors_to_json(errors)) +def run_benchmark(corpus: str = "0.1.0") -> float: + """Measure timing performance on the named corpus.""" + # force a clean slate + cache.clear() + # corpus was removed in https://github.com/amperser/proselint/pull/186 + log.error("Corpus is unavailable for the time being -> using demo") + corpus_path = demo_file.parent # proselint_path.parent / "corpora" / corpus + for _type in ["uncached", "cached"]: + start = time.time() + for file in os.listdir(corpus_path): + filepath = corpus_path / file + if filepath.suffix == ".md": + subprocess.call( + ["proselint", "--demo", "-o", "compact"], # noqa: S607 + # filepath.as_posix() + timeout=4, + shell=False, # noqa: S603 + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + duration = time.time() - start + log.info("Linting corpus %s took %.3f s.", _type, duration) + log.info( + "Note: this has full CLI-Overhead, " + "to compare linting-performance run 'proselint --demo'", + ) + return duration - else: - for error in errors: - (check, message, line, column, start, end, - extent, severity, replacements) = error +@click.command(context_settings=CONTEXT_SETTINGS) +@click.option( + "--config", + is_flag=False, + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + help="Path to configuration file.", +) +@click.option( + "--verbose", + "-v", + is_flag=True, + help="Give verbose output.", +) +@click.option("--clean", "-c", is_flag=True, help="Clear the cache and exit.") +@click.option("--benchmark", "-b", is_flag=True, help="Time on a corpus.") +@click.option("--demo", is_flag=True, help="Run over demo file.") +@click.option( + "--output-format", + "-o", + type=click.Choice(Output.names()), + default=None, + help="Override config to change format.", +) +@click.option("--dump-config", is_flag=True, help="Prints current config.") +@click.option( + "--dump-default-config", is_flag=True, help="Prints default config." +) +@click.option("--version", is_flag=True) +@click.argument( + "paths", nargs=-1, type=click.Path(exists=True, resolve_path=True) +) +def proselint( # noqa: PLR0912, PLR0913, PLR0917, C901 + paths: Union[list[Path], Path, None], + config: Optional[Path] = None, + clean: bool = False, # TODO: this should be a subcommand + verbose: bool = False, + output_format: Optional[str] = None, + benchmark: bool = False, + demo: bool = False, + dump_config: bool = False, # TODO: this should be a subcommand + dump_default_config: bool = False, # TODO: this should be a switch in dump-cmd + version: bool = False, +): + """Create the CLI for proselint, a linter for prose.""" + signal.signal(signal.SIGTERM, exit_gracefully) + signal.signal(signal.SIGINT, exit_gracefully) - if compact: - filename = "-" + if verbose: + set_verbosity(True) - click.echo( - filename + ":" + - str(1 + line) + ":" + - str(1 + column) + ": " + - check + " " + - message) + if version: + click.echo("Proselint v%s" % __version__) + log.debug("Python v%s", sys.version) + log.debug("Click v%s", click.__version__) + sys.exit(0) + if clean: + cache.clear() + sys.exit(0) -@click.command(context_settings=CONTEXT_SETTINGS) -@click.version_option(__version__, '--version', '-v', message='%(version)s') -@click.option('--config', is_flag=False, type=click.Path(), - help="Path to configuration file.") -@click.option('--debug', '-d', is_flag=True, help="Give verbose output.") -@click.option('--clean', '-c', is_flag=True, help="Clear the cache.") -@click.option('--json', '-j', 'output_json', is_flag=True, - help="Output as JSON.") -@click.option('--time', '-t', is_flag=True, help="Time on a corpus.") -@click.option('--demo', is_flag=True, help="Run over demo file.") -@click.option('--compact', is_flag=True, help="Shorten output.") -@click.option('--dump-config', is_flag=True, help="Prints current config.") -@click.option('--dump-default-config', is_flag=True, - help="Prints default config.") -@click.argument('paths', nargs=-1, type=click.Path()) -@close_cache_shelves_after -def proselint(paths=None, config=None, version=None, clean=None, - debug=None, output_json=None, time=None, demo=None, compact=None, - dump_config=None, dump_default_config=None): - """Create the CLI for proselint, a linter for prose.""" if dump_default_config: - return print(json.dumps(default, sort_keys=True, indent=4)) + click.echo(json.dumps(proselint_base, sort_keys=True, indent=4)) + sys.exit(0) + + if isinstance(config, str): + config = Path(config) + config = tools.load_options(config) - config = load_options(config, default) if dump_config: - print(json.dumps(config, sort_keys=True, indent=4)) - return + click.echo(json.dumps(config, sort_keys=True, indent=4)) + sys.exit(0) - if time: - # click.echo(timing_test()) - print("This option does not work for the time being.") - return + if output_format in Output.names(): + config["output_format"] = output_format - # In debug or clean mode, delete cache & *.pyc files before running. - if debug or clean: - clear_cache() + if benchmark: + run_benchmark() + sys.exit(0) # Use the demo file by default. if demo: + log.info("Demo-mode activated") paths = [demo_file] - # Expand the list of directories and files. - filepaths = extract_files(list(paths)) - - # Lint the files - num_errors = 0 - - # Use stdin if no paths were specified - if len(paths) == 0: - filepaths.append('-') - - for fp in filepaths: - if fp == '-': - fp = '' - f = sys.stdin - else: - try: - f = click.open_file(fp, 'r', "utf-8", "replace") - except Exception: - traceback.print_exc() - sys.exit(2) - errors = lint(f, debug, config) - num_errors += len(errors) - print_errors(fp, errors, output_json, compact) - - # Return an exit code - close_cache_shelves() - if num_errors > 0: - sys.exit(1) - else: - sys.exit(0) - - -def extract_files(files): - """Expand list of paths to include all text files matching the pattern.""" - expanded_files = [] - legal_extensions = [".md", ".txt", ".rtf", ".html", ".tex", ".markdown"] - - for f in files: - # If it's a directory, recursively walk through it and find the files. - if os.path.isdir(f): - for dir_, _, filenames in os.walk(f): - for filename in filenames: - fn, file_extension = os.path.splitext(filename) - if file_extension in legal_extensions: - joined_file = os.path.join(dir_, filename) - expanded_files.append(joined_file) - - # Otherwise add the file directly. - else: - expanded_files.append(f) - - return expanded_files - - -if __name__ == '__main__': + # prepare list + if paths is None: + paths = [] + if isinstance(paths, str): + paths = [Path(paths)] + if isinstance(paths, Path): + paths = [paths] + if isinstance(paths, (list, tuple)): + paths = [Path(path) for path in paths] + log.debug("Paths to lint: %s", paths) + + ts_start = time.time() + results = tools.lint_path(paths, config) + duration = time.time() - ts_start + + error_sum = 0 + for _file, _errors in results.items(): + output_errors(_errors, config, _file) + error_sum += len(_errors) + + log.info( + "Found %d lint-warnings in %.3f s (%d files, %.2f kiByte)", + error_sum, + duration, + len(results), + tools.last_char_count / 1024, + ) + sys.exit(error_sum) # Return an exit code + + +if __name__ == "__main__": proselint() diff --git a/proselint/config.py b/proselint/config_base.py similarity index 82% rename from proselint/config.py rename to proselint/config_base.py index 9de52af7e..97e6bd4e2 100644 --- a/proselint/config.py +++ b/proselint/config_base.py @@ -1,7 +1,26 @@ """Proselint config - replacement for default .proselintrc since #1212.""" +from __future__ import annotations -default = { +from enum import IntEnum + + +class Output(IntEnum): + full = default = 0 + json = 1 + compact = 2 + # implement printing to file? + + @classmethod + def names(cls) -> list[str]: + return [_e.name for _e in cls] + + +proselint_base: dict = { "max_errors": 1000, + "parallelize": True, # note: has an overhead, noticeable for small content + # TODO: add disabled force-option for usage as module + # can be trouble on windows if used wrong + "output_format": Output.full.name, "checks": { "airlinese.misc": True, "annotations.misc": True, @@ -35,12 +54,12 @@ "misc.currency": True, "misc.debased": True, "misc.false_plurals": True, + "misc.greylist": True, "misc.illogic": True, "misc.inferior_superior": True, "misc.institution_name": True, "misc.latin": True, "misc.many_a": True, - "misc.metaconcepts": True, "misc.metadiscourse": True, "misc.narcissism": True, "misc.not_guilty": True, @@ -60,6 +79,7 @@ "nonwords.misc": True, "oxymorons.misc": True, "psychology.misc": True, + "punctuation_spacing.misc": True, "redundancy.misc": True, "redundancy.ras_syndrome": True, "skunked_terms.misc": True, @@ -86,6 +106,6 @@ "typography.symbols": True, "uncomparables.misc": True, "weasel_words.misc": True, - "weasel_words.very": True - } + "weasel_words.very": True, + }, } diff --git a/proselint/config_paths.py b/proselint/config_paths.py new file mode 100644 index 000000000..3bd8219aa --- /dev/null +++ b/proselint/config_paths.py @@ -0,0 +1,30 @@ +import os +from pathlib import Path + +proselint_path = Path(__file__).parent +demo_file = proselint_path / "demo.md" + +user_path = Path("~").expanduser() +cwd_path = Path.cwd() + + +def _get_xdg_path(variable_name: str, default_path: Path) -> Path: + _value = os.environ.get(variable_name) + if not _value: + return default_path + return Path(_value) + + +config_global_path = Path("/etc/proselintrc") +config_xdg_path = _get_xdg_path("XDG_CONFIG_HOME", user_path / ".config") + +config_user_paths = [ + # highest prio is user provided path then as followed + cwd_path / ".proselintrc.json", + config_xdg_path / "proselint/config.json", + user_path / ".proselintrc.json", +] + +cache_xdg_path = _get_xdg_path("XDG_CACHE_HOME", user_path / ".cache") +cache_user_path = cache_xdg_path / "proselint" +cache_user_path.mkdir(parents=True, exist_ok=True) diff --git a/proselint/logger.py b/proselint/logger.py new file mode 100644 index 000000000..49e684bc1 --- /dev/null +++ b/proselint/logger.py @@ -0,0 +1,48 @@ +"""Advanced substitute for print() + +short reminder for format-strings: +%s string +%d decimal +%f float +%o decimal as octal +%x decimal as hex + +%05d pad right (aligned with 5chars) +%-05d pad left (left aligned) +%06.2f 6chars float, including dec point, with 2 chars after +%.5s truncate to 5 chars + +%% for a percent character + +""" +import logging +import logging.handlers + +# todo: maybe use with rich to allow links + +log = logging.getLogger("proselint") +log.setLevel(logging.INFO) +# log.propagate = 0 + +logging.basicConfig(format="%(message)s") + +# console_handler = logging.StreamHandler(sys.stderr) +# console_handler.setLevel(logging.INFO) +# log.addHandler(console_handler) + + +def set_verbosity(debug: bool = False) -> None: + if debug: + log.setLevel(logging.DEBUG) + logging.basicConfig(format="%(name)s %(levelname)s: %(message)s") + else: + log.setLevel(logging.INFO) + logging.basicConfig(format="%(message)s", force=True) + # only needed in debug mode: + # logging._srcfile = None + # logging.logThreads = 0 + # logging.logProcesses = 0 + + +def get_verbosity() -> bool: + return log.level == logging.DEBUG diff --git a/proselint/memoizer.py b/proselint/memoizer.py new file mode 100644 index 000000000..45f3a7504 --- /dev/null +++ b/proselint/memoizer.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import contextlib +import functools +import hashlib +import pickle # noqa: S403 +import shutil +from concurrent.futures import Executor +from concurrent.futures import Future +from datetime import datetime +from datetime import timedelta +from typing import IO +from typing import TYPE_CHECKING +from typing import Callable +from typing import Optional +from typing import TypedDict +from typing import Union + +from typing_extensions import Self +from typing_extensions import Unpack + +from . import config_base +from .checks import get_checks +from .config_paths import cache_user_path +from .logger import log +from .version import __version__ as version + +if TYPE_CHECKING: + from types import TracebackType + + from .tools import ResultLint + + +class Cache: + save_path = cache_user_path / "cache.pickle" + _instance: Optional[Self] = None + + @classmethod + def __new__(cls, *_args: tuple, **_kwargs: Unpack[TypedDict]) -> Self: + """Implements singleton class.""" + if cls._instance is None: + cls._instance = object.__new__(cls) + return cls._instance + return cls._instance + + def __init__(self) -> None: + self._data: dict[str, list[ResultLint]] = {} + self._age: dict[str, int] = {} + # TODO: make timezone aware, possibly use UTC? + self._ts_now: int = round(datetime.now().timestamp()) # noqa: DTZ005 + self._modified: bool = False + self.fname2key: dict[str, str] = {} + + def __exit__( + self, + typ: Optional[type[BaseException]] = None, + exc: Optional[BaseException] = None, + tb: Optional[TracebackType] = None, + extra_arg: int = 0, + ) -> None: + """Close previously opened cache shelves.""" + self.to_file() + self._data.clear() + Cache._instance = None + + def __del__(self): + self.to_file() + Cache._instance = None + + def __getitem__(self, key: str) -> list[ResultLint]: + """Allows direct dict access. + This allows instance["key"], in addition to instance.data["key"]""" + return self._data[key] + + def __setitem__(self, key: str, value: list[ResultLint]): + self._data[key] = value + self._age[key] = self._ts_now + self._modified = True + + def to_file(self) -> None: + # only save when needed + if (not self._modified) or len(self._data) < 1: + return + if not cache_user_path.is_dir(): + cache_user_path.mkdir(parents=True) + age_max: int = self._ts_now - round(timedelta(days=1).total_seconds()) + # todo: reread on file-change. parallel runs, + for _key in self._data: + # TODO: could be speed up with dict_base - dict_entries_to_remove + if self._age[_key] < age_max: + self._age.pop(_key) + self._data.pop(_key) + + with self.save_path.open("wb", buffering=-1) as fd: + pickle.dump( + [self._data, self._age, version], + fd, + fix_imports=False, + protocol=pickle.HIGHEST_PROTOCOL, + ) + # log.debug(" -> stored cache") # too late when exiting + + @classmethod + def from_file(cls) -> Self: + _inst = cls() + if cls.save_path.exists(): + with ( + contextlib.suppress(EOFError), + cls.save_path.open("rb", buffering=-1) as fd, + ): + data = pickle.load(fd, fix_imports=False) # noqa: S301 + # TODO: consider replacing pickle with something faster + # only restore if data fits and package-version matches + if ( + isinstance(data, list) + and len(data) >= 3 + and data[2] == version + ): + _inst._data = data[0] # noqa: SLF001 + _inst._age = data[1] # noqa: SLF001 + log.debug(" -> found & restored cache") + return _inst + + def clear(self) -> None: + """Delete the contents of the cache.""" + log.debug("Deleting the cache...") + with contextlib.suppress(OSError): + shutil.rmtree(cache_user_path) + self._data.clear() + self._age.clear() + + @staticmethod + def calculate_key(text: str, checks: list[Callable]) -> str: + """For accessing Cache-entry""" + + text_hash = hashlib.sha224(text.encode("utf-8")).hexdigest()[:50] + chck_list = [f"{c.__module__}.{c.__name__}" for c in checks] + chck_hash = hashlib.sha224( + " ".join(chck_list).encode("utf-8") + ).hexdigest()[:10] + # NOTE: Skip hashing config! + # -> Valid assumption that (current) config has + # no influence on result below this level + # WARNING: frozenset(checks).__hash__() varies between runs + + return text_hash + chck_hash + + +cache = Cache.from_file() + + +############################################################################### +# Memoizer Access -> Wrapper ################################################## +############################################################################### + + +def memoize_future(result: list[ResultLint], file_name: str) -> None: + """used to later add result -> when working with multiprocessing""" + key = cache.fname2key.get(file_name) + log.debug("[Memoizer] LateStore %s", key) + cache[key] = result + + +def memoize_lint( + fn: Callable, +) -> Callable: + """Cache results of lint() on disk.""" + + @functools.wraps(fn) + def wrapped( + content: Union[str, IO], + config: Optional[dict] = None, + checks: Optional[list[Callable]] = None, + file_name: Optional[str] = None, + *, + _exe: Optional[Executor] = None, + ) -> list: + if not isinstance(content, str): + return fn(content, config, checks, file_name, _exe=_exe) + # TODO: filename enables 2d-dict + # d[funcSig,fileSig] = (input_hash,result) + # -> smaller dicts + + if not isinstance(config, dict): + config = config_base.proselint_base + if checks is None: + checks = get_checks(config) + key = cache.calculate_key(content, checks) + + try: + return cache[key] + except KeyError: + _res = fn(content, config, checks, file_name, _exe=_exe) + # only store finished results + if len(_res) == 0 or not isinstance(_res[0], Future): + log.debug("[Memoizer] Store %s", key) + cache[key] = _res + cache.fname2key[file_name] = key + return _res + + return wrapped diff --git a/proselint/score.py b/proselint/score.py index 551d7bc74..7e351022c 100644 --- a/proselint/score.py +++ b/proselint/score.py @@ -1,11 +1,15 @@ -"""Compute the lintscore on the corpus.""" +"""Compute the lintscore on the corpus. +NOTE: relic, probably broken +""" import os import re -import subprocess +import subprocess # noqa: S404 +from pathlib import Path -proselint_path = os.path.dirname(os.path.realpath(__file__)) +from .config_paths import proselint_path +from .logger import log def score(check=None): @@ -28,43 +32,44 @@ def score(check=None): """ tp = 0 fp = 0 - - parent_directory = os.path.dirname(proselint_path) - path_to_corpus = os.path.join(parent_directory, "corpora", "0.1.0") + # corpus was removed in https://github.com/amperser/proselint/pull/186 + path_to_corpus = proselint_path.parent / "corpora" / "0.1.0" for root, _, files in os.walk(path_to_corpus): - files = [f for f in files if f.endswith(".md")] - for f in files: - fullpath = os.path.join(root, f) + files_md = [f for f in files if f.endswith(".md")] + root_path = Path(root) + for file_md in files_md: + fullpath = root_path / file_md # Run the linter. - print(f"Linting {f}") - out = subprocess.check_output(["proselint", fullpath]) + log.debug("Linting %s", file_md) + out = subprocess.check_output(["proselint", fullpath]) # noqa: S603, S607 + # TODO: this can just call lint() ?!? # Determine the number of errors. regex = r".+?:(?P\d+):(?P\d+): (?P.+)" num_errors = len(tuple(re.finditer(regex, out))) - print(f"Found {num_errors} errors.") + log.debug(" -> found %d errors.", num_errors) # Open the document. - subprocess.call(["open", fullpath]) + subprocess.call(["open", fullpath]) # noqa: S603, S607 # Ask the scorer how many of the errors were false alarms? + # TODO: this should not be manual - data can be stored in corpus input_val = None while not isinstance(input_val, int): try: input_val = input("# of false alarms? ") if input_val == "exit": - return - else: - input_val = int(input_val) - fp += input_val - tp += (num_errors - input_val) - except ValueError: + return None + input_val = int(input_val) + fp += input_val + tp += num_errors - input_val + except ValueError: # noqa: PERF203 pass - print(f"Currently {tp} hits and {fp} false alarms\n---") + log.debug(" -> currently %d hits and %d false alarms", tp, fp) if (tp + fp) > 0: return tp * (1.0 * tp / (tp + fp)) ** 2 - else: - return 0 + + return 0 diff --git a/proselint/tools.py b/proselint/tools.py index e69bf1992..ff0329d0b 100644 --- a/proselint/tools.py +++ b/proselint/tools.py @@ -1,501 +1,299 @@ """General-purpose tools shared across linting checks.""" +from __future__ import annotations + import copy -import dbm -import functools -import hashlib -import importlib -import inspect import json import os -import re -import shelve import sys -import traceback +from concurrent.futures import Executor +from concurrent.futures import Future +from concurrent.futures import ProcessPoolExecutor +from pathlib import Path +from typing import IO +from typing import Callable +from typing import Optional +from typing import TypeAlias +from typing import Union from warnings import showwarning as warn -from . import config - -_cache_shelves = dict() -proselint_path = os.path.dirname(os.path.realpath(__file__)) -home_dir = os.path.expanduser("~") -cwd = os.getcwd() - - -def close_cache_shelves(): - """Close previously opened cache shelves.""" - for cache in _cache_shelves.values(): - cache.close() - _cache_shelves.clear() - - -def close_cache_shelves_after(f): - """Decorate a function to ensure cache shelves are closed after call.""" - @functools.wraps(f) - def wrapped(*args, **kwargs): - f(*args, **kwargs) - close_cache_shelves() - return wrapped - - -def _get_xdg_path(variable_name, default_path): - path = os.environ.get(variable_name) - if path is None or path == '': - return default_path - else: - return path - - -def _get_xdg_config_home(): - return _get_xdg_path('XDG_CONFIG_HOME', os.path.join(home_dir, '.config')) - - -def _get_xdg_cache_home(): - return _get_xdg_path('XDG_CACHE_HOME', os.path.join(home_dir, '.cache')) - - -def _get_cache(cachepath): - if cachepath in _cache_shelves: - return _cache_shelves[cachepath] - - try: - cache = shelve.open(cachepath, protocol=2) - except dbm.error: - # dbm error on open - delete and retry - print('Error (%s) opening %s - will attempt to delete and re-open.' % - (sys.exc_info()[1], cachepath)) - try: - os.remove(cachepath) - cache = shelve.open(cachepath, protocol=2) - except Exception: - print('Error on re-open: %s' % sys.exc_info()[1]) - cache = None - except Exception: - # unknown error - print('Could not open cache file %s, maybe name collision. ' - 'Error: %s' % (cachepath, traceback.format_exc())) - cache = None - - # Don't fail on bad caches - if cache is None: - print('Using in-memory shelf for cache file %s' % cachepath) - cache = shelve.Shelf(dict()) - - _cache_shelves[cachepath] = cache - return cache - - -def memoize(f): - """Cache results of computations on disk.""" - # Determine the location of the cache. - cache_dirname = os.path.join(_get_xdg_cache_home(), 'proselint') - legacy_cache_dirname = os.path.join(home_dir, ".proselint") - - if not os.path.isdir(cache_dirname): - # Migrate the cache from the legacy path to XDG compliant location. - if os.path.isdir(legacy_cache_dirname): - os.rename(legacy_cache_dirname, cache_dirname) - # Create the cache if it does not already exist. - else: - os.makedirs(cache_dirname) - - cache_filename = f.__module__ + "." + f.__name__ - cachepath = os.path.join(cache_dirname, cache_filename) - - @functools.wraps(f) - def wrapped(*args, **kwargs): - - # handle instance methods - if hasattr(f, '__self__'): - args = args[1:] - - signature = cache_filename.encode("utf-8") - - tempargdict = inspect.getcallargs(f, *args, **kwargs) - - for item in list(tempargdict.items()): - if item[0] == "text": - signature += item[1].encode("utf-8") +from . import config_base +from .checks import get_checks +from .checks import run_checks +from .config_base import Output +from .config_paths import config_global_path +from .config_paths import config_user_paths +from .logger import log +from .memoizer import memoize_future +from .memoizer import memoize_lint - key = hashlib.sha256(signature).hexdigest() - cache = _get_cache(cachepath) +ResultLint: TypeAlias = tuple[str, str, str, int, int, int, int, int, str, str] +# content: check_name, message, source, line, column, start, end, length, type, +# replacement - try: - return cache[key] - except KeyError: - value = f(*args, **kwargs) - cache[key] = value - cache.sync() - return value - except TypeError: - call_to = f.__module__ + '.' + f.__name__ - print('Warning: could not disk cache call to %s;' - 'it probably has unhashable args. Error: %s' % - (call_to, traceback.format_exc())) - return f(*args, **kwargs) - return wrapped +############################################################################### +# Config ###################################################################### +############################################################################### -def get_checks(options): - """Extract the checks.""" - sys.path.append(proselint_path) - checks = [] - check_names = [key for (key, val) in options["checks"].items() if val] - - for check_name in check_names: - module = importlib.import_module("checks." + check_name) - for d in dir(module): - if re.match("check", d): - checks.append(getattr(module, d)) - - return checks - - -def deepmerge_dicts(dict1, dict2): - """Deep merge dictionaries, second dict will take priority.""" - result = copy.deepcopy(dict1) +def _deepmerge_dicts( + base: dict, + overrides: dict, +) -> dict: + """Deep merge dictionaries, second dict will take priority. + # Note: this could be faster, just sum dicts -> not relevant here + """ + result = copy.deepcopy(base) - for key, value in dict2.items(): + for key, value in overrides.items(): if isinstance(value, dict): - result[key] = deepmerge_dicts(result[key] or {}, value) + result[key] = _deepmerge_dicts(result.get(key) or {}, value) else: result[key] = value return result -def load_options(config_file_path=None, conf_default=None): +def load_options( + config_file_path: Optional[Path] = None, +) -> dict: """Read various proselintrc files, allowing user overrides.""" - conf_default = conf_default or {} - if os.path.isfile("/etc/proselintrc"): - conf_default = json.load(open("/etc/proselintrc")) - - user_config_paths = [ - os.path.join(cwd, '.proselintrc.json'), - os.path.join(_get_xdg_config_home(), 'proselint', 'config.json'), - os.path.join(home_dir, '.proselintrc.json') - ] + cfg_default = config_base.proselint_base + + if config_global_path.is_file(): + log.debug("Config read from global '%s' (as base)", config_global_path) + _cfg = json.load(config_global_path.open()) + cfg_default = _deepmerge_dicts(cfg_default, _cfg) if config_file_path: - if not os.path.isfile(config_file_path): + if not config_file_path.is_file(): raise FileNotFoundError( - f"Config file {config_file_path} does not exist") - user_config_paths.insert(0, config_file_path) + f"Config file {config_file_path} does not exist" + ) + config_user_paths.insert(0, config_file_path) user_options = {} - for path in user_config_paths: - if os.path.isfile(path): - user_options = json.load(open(path)) + for path in config_user_paths: + if path.is_file(): + log.debug("Config read from '%s'", path) + user_options = json.load(path.open()) break - oldpath = path.replace(".json", "") - if os.path.isfile(oldpath): - warn(f"{oldpath} was found instead of a JSON file." - f" Rename to {path}.", DeprecationWarning, "", 0) - user_options = json.load(open(oldpath)) + path_old = path.with_suffix("") + if path_old.is_file() and path.suffix: + warn( + f"Found {path_old} instead of a JSON file. Rename to {path}.", + DeprecationWarning, + "", + 0, + ) + user_options = json.load(path_old.open()) break - return deepmerge_dicts(conf_default, user_options) + return _deepmerge_dicts(cfg_default, user_options) -def errors_to_json(errors): - """Convert the errors to JSON.""" - out = [] - for e in errors: - out.append({ - "check": e[0], - "message": e[1], - "line": 1 + e[2], - "column": 1 + e[3], - "start": 1 + e[4], - "end": 1 + e[5], - "extent": e[6], - "severity": e[7], - "replacements": e[8], - }) +############################################################################### +# Linting ##################################################################### +############################################################################### - return json.dumps( - {"status": "success", "data": {"errors": out}}, sort_keys=True) +last_char_count: int = 0 -def line_and_column(text, position): - """Return the line number and column of a position in a string.""" - position_counter = 0 - line_no = 0 - for line in text.splitlines(True): - if (position_counter + len(line.rstrip())) >= position: - break - position_counter += len(line) - line_no += 1 - return (line_no, position - position_counter) +def extract_files(paths: list[Path]) -> list[Path]: + """Expand list of paths to include all text files matching the pattern.""" + expanded_files = [] + legal_extensions = [".md", ".txt", ".rtf", ".html", ".tex", ".markdown"] + for _path in paths: + # If it's a directory, recursively walk through it and find the files. + if _path.is_dir(): + for _dir, _, _filenames in os.walk(_path): + _path = Path(_dir) + for filename in _filenames: + _file_path = _path / filename + if _file_path.suffix.lower() in legal_extensions: + expanded_files.append(_file_path) -def lint(input_file, debug=False, config=config.default): - """Run the linter on the input file.""" - if isinstance(input_file, str): - text = input_file - else: - text = input_file.read() - - # Get the checks. - checks = get_checks(config) - - # Apply all the checks. - errors = [] - for check in checks: - - result = check(text) - - for error in result: - (start, end, check, message, replacements) = error - (line, column) = line_and_column(text, start) - if not is_quoted(start, text): - errors += [(check, message, line, column, start, end, - end - start, "warning", replacements)] - - if len(errors) > config["max_errors"]: - break - - # Sort the errors by line and column number. - errors = sorted(errors[:config["max_errors"]], key=lambda e: (e[2], e[3])) + # Otherwise add the file directly. + else: + expanded_files.append(_path) - return errors + return expanded_files -def assert_error(text, check, n=1): - """Assert that text has n errors of type check.""" - assert_error.description = f"No {check} error for '{text}'" - assert len([error[0] for error in lint(text) if error[0] == check]) == n +@memoize_lint +def lint( + content: Union[str, IO], + config: Optional[dict] = None, + checks: Optional[list[Callable]] = None, + source: str = "", + *, + _exe: Optional[Executor] = None, +) -> list[ResultLint]: + """Run the linter on the input file.""" + if isinstance(content, str): # noqa: SIM108 + _text = content + else: + _text = content.read() -def consistency_check(text, word_pairs, err, msg, offset=0): - """Build a consistency checker for the given word_pairs.""" - errors = [] + # TODO: this is also done by the memoizer now + # think about joining lint with wrapper + if not isinstance(config, dict): + config = config_base.proselint_base + if checks is None: + checks = get_checks(config) - msg = " ".join(msg.split()) + ret_future = _exe is not None - for w in word_pairs: - matches = [ - [m for m in re.finditer(w[0], text)], - [m for m in re.finditer(w[1], text)] + # Apply all the checks. + if config["parallelize"]: + if not ret_future: + log.debug("[Lint] created inner Executor for parallelization") + _exe = ProcessPoolExecutor() + else: + log.debug("[Lint] used outer Executor for parallelization") + # NOTE: ThreadPoolExecutor is only concurrent, but not multi-cpu + # NOTE: .map() is build on .submit(), harder to use here, same speed + futures = [ + _exe.submit(run_checks, check, _text, source) for check in checks ] + if ret_future: + # this will skip the memoizer + return futures + errors = [_e for _ft in futures for _e in _ft.result()] + # errors.extend([_ft.result for _ft in futures]), try it + else: # single process + results = [run_checks(check, _text) for check in checks] + errors = [_e for _res in results for _e in _res] - if len(matches[0]) > 0 and len(matches[1]) > 0: + # Sort the errors by line and column number. + return sorted(errors[: config["max_errors"]], key=lambda e: (e[2], e[3])) - idx_minority = len(matches[0]) > len(matches[1]) - for m in matches[idx_minority]: - errors.append(( - m.start() + offset, - m.end() + offset, - err, - msg.format(w[~idx_minority], m.group(0)), - w[~idx_minority])) +def lint_path( + paths: Union[Path, list[Path]], + config: Optional[dict] = None, +) -> dict[Path, list[ResultLint]]: + """Lint path with files or point to specific file""" + # Expand the list of directories and files. + filepaths = extract_files(paths) - return errors + if not isinstance(config, dict): + config = config_base.proselint_base + checks = get_checks(config) + results = {} + chars = 0 -def preferred_forms_check(text, list, err, msg, ignore_case=True, offset=0): - """Build a checker that suggests the preferred form.""" - if ignore_case: - flags = re.IGNORECASE + if len(paths) == 0: + # Use stdin if no paths were specified + log.info("No path specified -> will read from ") + results[""] = lint(sys.stdin, config=config, checks=checks) else: - flags = 0 - - msg = " ".join(msg.split()) - - errors = [] - regex = r"[\W^]{}[\W$]" - for p in list: - for r in p[1]: - for m in re.finditer(regex.format(r), text, flags=flags): - txt = m.group(0).strip() - errors.append(( - m.start() + 1 + offset, - m.end() + offset, - err, - msg.format(p[0], txt), - p[0])) - - return errors - - -def existence_check(text, list, err, msg, ignore_case=True, str=False, - offset=0, require_padding=True, dotall=False, - excluded_topics=None, exceptions=(), join=False): - """Build a checker that prohibits certain words or phrases.""" - flags = 0 - - msg = " ".join(msg.split()) - - if ignore_case: - flags = flags | re.IGNORECASE + # offer "outer" executor, to make multiprocessing more effective + exe = ProcessPoolExecutor() if config["parallelize"] else None + for file in filepaths: + log.debug("Analyzing '%s'", file.name) + try: + with file.open(encoding="utf-8", errors="replace") as _fh: + content = _fh.read() + except Exception: + log.exception( + "[LintPath] Error opening '%s' -> will skip", file.name + ) + continue + results[file] = lint( + content, config, checks, file.as_posix(), _exe=exe + ) + chars += len(content) + + # fetch result from futures, if needed + for _file, _errors in results.items(): + if len(_errors) > 0 and isinstance(_errors[0], Future): + _errors = [_e for _ft in _errors for _e in _ft.result()] + _errors = sorted( + _errors[: config["max_errors"]], + key=lambda e: (e[2], e[3]), + ) + # memoizer could also iterate, fetch results and sort + memoize_future(_errors, _file.as_posix()) + # write back data so no futures are returned + results[_file] = _errors + + # bad style ... but + global last_char_count # noqa: PLW0603 + last_char_count = chars + return results + + +def errors_to_json(items: list[ResultLint]) -> str: + """Convert the errors to JSON.""" + out = [ + { + "check": item[0], + "message": item[1], + "source": item[2], + "line": item[3], + "column": item[4], + "start": item[5], + "end": item[6], + "extent": item[7], + "severity": item[8], + "replacements": item[9], + } + for item in items + ] - if str: - flags = flags | re.UNICODE + return json.dumps( + {"status": "success", "data": {"errors": out}}, sort_keys=True + ) - if dotall: - flags = flags | re.DOTALL - if require_padding: - regex = r"(?:^|\W){}[\W$]" +def output_errors( + errors: list[ResultLint], + config: dict, + file_path: Optional[Path] = None, +) -> None: + """Print the errors, resulting from lint, for filename.""" + try: + out_fmt = Output[config["output_format"]] + except KeyError: + out_fmt = Output[config_base.proselint_base["output_format"]] + + if not isinstance(errors, list): + log.error( + "[OutputError] no list provided " + "(guess: results of lint_path() need to be extracted first)" + ) + return + + if out_fmt == Output.json: + log.info(errors_to_json(errors)) else: - regex = r"{}" - - errors = [] - - # If the topic of the text is in the excluded list, return immediately. - if excluded_topics: - tps = topics(text) - if any([t in excluded_topics for t in tps]): - return errors - - rx = "|".join(regex.format(w) for w in list) - for m in re.finditer(rx, text, flags=flags): - txt = m.group(0).strip() - if any([re.search(exception, txt) for exception in exceptions]): - continue - errors.append(( - m.start() + 1 + offset, - m.end() + offset, - err, - msg.format(txt), - None)) - - return errors - - -def max_errors(limit): - """Decorate a check to truncate error output to a specified limit.""" - def wrapper(f): - @functools.wraps(f) - def wrapped(*args, **kwargs): - return truncate_errors(f(*args, **kwargs), limit) - return wrapped - return wrapper - - -def truncate_errors(errors, limit=float("inf")): - """If limit was specified, truncate the list of errors. - - Give the total number of times that the error was found elsewhere. - """ - if len(errors) > limit: - start1, end1, err1, msg1, replacements = errors[0] - - if len(errors) == limit + 1: - msg1 += " Found once elsewhere." - else: - msg1 += f" Found {len(errors)} times elsewhere." - - errors = [(start1, end1, err1, msg1, replacements)] + errors[1:limit] - - return errors - - -def ppm_threshold(threshold): - """Decorate a check to error if the PPM threshold is surpassed.""" - def wrapped(f): - @functools.wraps(f) - def wrapper(*args, **kwargs): - return threshold_check(f(*args, **kwargs), threshold, len(args[0])) - return wrapper - return wrapped - - -def threshold_check(errors, threshold, length): - """Check that returns an error if the PPM threshold is surpassed.""" - if length > 0: - errcount = len(errors) - ppm = (errcount / length) * 1e6 - - if ppm >= threshold and errcount >= 1: - return [errors[0]] - return [] - - -def is_quoted(position, text): - """Determine if the position in the text falls within a quote.""" - def matching(quotemark1, quotemark2): - straight = '\"\'' - curly = '“”' - if quotemark1 in straight and quotemark2 in straight: - return True - if quotemark1 in curly and quotemark2 in curly: - return True - else: - return False - - def find_ranges(text): - s = 0 - q = pc = '' - start = None - ranges = [] - seps = " .,:;-\r\n" - quotes = ['\"', '“', '”', "'"] - for i, c in enumerate(text + "\n"): - if s == 0 and c in quotes and pc in seps: - start = i - s = 1 - q = c - elif s == 1 and matching(c, q): - s = 2 - elif s == 2: - if c in seps: - ranges.append((start+1, i-1)) - start = None - s = 0 + for error in errors: + ( + check, + message, + source, + line, + column, + _, # start, + _, # end, + _, # extent, + _, # severity, + _, # replacements, + ) = error + + if isinstance(file_path, Path): + if out_fmt == Output.compact: + source = file_path.name else: - s = 1 - pc = c - return ranges - - def position_in_ranges(ranges, position): - for start, end in ranges: - if start <= position < end: - return True - return False - - return position_in_ranges(find_ranges(text), position) - - -def detector_50_Cent(text): - """Determine whether 50 Cent is a topic.""" - keywords = [ - "50 Cent", - "rap", - "hip hop", - "Curtis James Jackson III", - "Curtis Jackson", - "Eminem", - "Dre", - "Get Rich or Die Tryin'", - "G-Unit", - "Street King Immortal", - "In da Club", - "Interscope", - ] - num_keywords = sum(word in text for word in keywords) - return ("50 Cent", float(num_keywords > 2)) - - -def topics(text): - """Return a list of topics.""" - detectors = [ - detector_50_Cent - ] - ts = [] - for detector in detectors: - ts.append(detector(text)) - - return [t[0] for t in ts if t[1] > 0.95] - - -def context(text, position, level="paragraph"): - """Get sentence or paragraph that surrounds the given position.""" - if level == "sentence": - pass - elif level == "paragraph": - pass + source = file_path.absolute().as_uri() + # TODO: would be nice to supress "file:///" + # https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + elif out_fmt == Output.compact: + source = "" - return "" + log.info("%s:%d:%d: %s %s", source, line, column, check, message) diff --git a/proselint/version.py b/proselint/version.py index 8cdcee149..8f07b62ef 100644 --- a/proselint/version.py +++ b/proselint/version.py @@ -1,3 +1,3 @@ """Proselint version number.""" -__version__ = "0.13.0" +__version__ = "0.15.0" diff --git a/pyproject.toml b/pyproject.toml index 37a711461..34bd5556e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,34 +12,41 @@ include = ["proselint/", "tests/", "proselint/demo.md"] exclude = ["tests/.gitignore"] [tool.poetry.dependencies] -python = "^3.8.1" -click = "^8.0.0" +python = ">=3.8.1" +click = ">=8.0.0" +typing_extensions = ">=4.9.0" -[tool.poetry.group.dev.dependencies] -gmail = { git = "https://github.com/charlierguo/gmail.git" } -APScheduler = ">=3.5.3" +[tool.poetry.extras] + + +[tool.poetry.dev-dependencies] bumpversion = ">=0.5.3" -coverage = "^6.1" +coverage = "^7.4.0" +mock = ">=2.0.0" +pydocstyle = "^6.1.1" +pytest = ">=7.0.0" +pytest-click = ">=1.1.0" +pytest-cov = ">=4.0.0" +twine = ">=4.0.0" +pre-commit = ">=3.0.0" +# web-stuff +APScheduler = ">=3.5.3" Flask-API = ">=1.0" Flask-Cors = ">=3.0.4" Flask = ">=1.1.4" Flask-Limiter = ">=1.0.1" gunicorn = ">=19.8.1" -mock = ">=2.0.0" -pytest = "^7.4.0" +gmail = { git = "https://github.com/charlierguo/gmail.git" } redis = ">=2.10.6" requests = ">=2.19.1" rq = ">=0.12.0" -pydocstyle = "^6.3.0" -twine = "^3.5.0" -flake8 = "^6.1.0" -flake8-bugbear = "^23.7.10" -flake8-import-order = "^0.18.2" -isort = "^5.12.0" [tool.poetry.scripts] proselint = "proselint.command_line:proselint" +[tool.poetry.group.dev.dependencies] +ruff = "^0.1.14" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/research/comparison/tools.csv b/research/comparison/tools.csv index 860f7a301..4fb80f6eb 100644 --- a/research/comparison/tools.csv +++ b/research/comparison/tools.csv @@ -61,4 +61,4 @@ WinProof,http://www.franklinhu.com/winproof.htm,en,0,0,0,0,0,0,0,0,0,0 WordRake,http://www.wordrake.com/,en,0,0,0,0,0,0,0,0,0,0 write-good,https://github.com/btford/write-good,en,1,0,0,0,0,0,0,0,0,0 Writer's Workbench,http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=6772006,en,1,0,0,0,0,0,0,0,0,0 -Writer's Workbench (1),http://www.emo.com/,en,0,0,0,0,0,0,0,0,0,0 \ No newline at end of file +Writer's Workbench (1),http://www.emo.com/,en,0,0,0,0,0,0,0,0,0,0 diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/benchmark_checks.py b/scripts/benchmark_checks.py new file mode 100644 index 000000000..42618495f --- /dev/null +++ b/scripts/benchmark_checks.py @@ -0,0 +1,95 @@ +""" Benchmark Checks + +goal: find slowest ones, equalize them a bit + + +rules of thumb + +- only adjust extreme cases +- fastest + - if they are in same file, try to combine them in one check +- slowest + - find ways to optimize + - maybe split them up if >> 10x of mean() +- alternative: add prio to scheduler by sorting checks (keyword in fn-name) + +learnings + +- python seems to automatically cache internal calculation for reruns (if const) +- lru_cache and memoizer only call for trouble during multiprocessing + +""" +from timeit import timeit + +import proselint +from proselint import memoizer + +file_path = proselint.path / "demo.md" + +_cfg = proselint.config_default + + +with file_path.open(encoding="utf-8", errors="replace") as fh: + _text = fh.read() + +_checks = proselint.tools.get_checks(_cfg) + +# ######################################################################### +print("\n############# Benchmark manually optimized Checks ###################") +print("\n############# * or with calculations in check() ###################") + +specials = [ + "proselint.checks.misc.waxed.check", + "proselint.checks.terms.venery.check", + "proselint.checks.uncomparables.misc.check_1", +] + +memoizer.cache.clear() +for check in _checks: + _name = f"{check.__module__}.{check.__name__}" + if _name in specials: + memoizer.cache.clear() + _dur = timeit("_e = check(_text)", globals=locals(), number=1) + print(f"{_name} took {_dur * 1000:.3f} ms -> uncached") + _dur = timeit("_e = check(_text)", globals=locals(), number=1) + print(f"{_name} took {_dur * 1000:.3f} ms -> cached") + _dur = timeit("_e = check(_text)", globals=locals(), number=1) + print(f"{_name} took {_dur * 1000:.3f} ms -> cached") + _dur = timeit("_e = check(_text)", globals=locals(), number=1) + print(f"{_name} took {_dur * 1000:.3f} ms -> cached") + +# ########## output with memoize_const +# +# proselint.checks.misc.waxed.check took 0.067 ms -> uncached +# proselint.checks.misc.waxed.check took 0.029 ms -> cached +# proselint.checks.terms.venery.check took 7.476 ms -> uncached +# proselint.checks.terms.venery.check took 7.208 ms -> cached +# proselint.checks.uncomparables.misc.check_1 took 19.959 ms -> uncached +# proselint.checks.uncomparables.misc.check_1 took 19.694 ms -> cached + +# ########## output without extra caching (except uncomparables) + +# proselint.checks.misc.waxed.check took 0.077 ms -> uncached +# proselint.checks.misc.waxed.check took 0.029 ms -> cached +# proselint.checks.terms.venery.check took 10.782 ms -> uncached +# proselint.checks.terms.venery.check took 7.339 ms -> cached +# proselint.checks.uncomparables.misc.check_1 took 24.678 ms -> uncached +# proselint.checks.uncomparables.misc.check_1 took 20.797 ms -> cached + +print("\n############# Benchmark all Checks ###################") + +result = {} +for check in _checks: + _name = f"{check.__module__}.{check.__name__}" + result[_name] = timeit("_e = check(_text)", globals=locals(), number=5) + +result = dict(sorted(result.items(), key=lambda item: item[1])) +for _key, _value in result.items(): + print(f"{_key}: {_value}") + +_val = list(result.values()) +print(f"count: {len(_val)}") +print(f"min: {min(_val)}") +print(f"median: {_val[round(len(_val) / 2)]}") +print(f"mean: {sum(_val) / len(_val)}") +print(f"max: {max(_val)}") diff --git a/scripts/benchmark_constants.py b/scripts/benchmark_constants.py new file mode 100644 index 000000000..8469a0b83 --- /dev/null +++ b/scripts/benchmark_constants.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from functools import cached_property +from functools import lru_cache +from timeit import timeit + +# LEARNING: +# - results of fns are not automatically cached +# - something like @lru_cache can speed up reruns during runtime +# - cached_property only works on classes and behaves like lru-cache +# -> switch to persistent cache by extending proselints memoizer +# TODO: add unittests for FNs in proselint to detect regressions + +print("\n################################### LRU-Cache") + + +@lru_cache +def costly_fn() -> list[int]: + return list(range(10_000_000)) + + +_t = timeit("_e = costly_fn()", globals=globals(), number=1) +print(f"1st fn-call took {_t * 1e3:4.3f} ms -> cached from now on") + +_t = timeit("_e = costly_fn()", globals=globals(), number=1) +print(f"2nd fn-call took {_t * 1e3:4.3f} ms") + +_t = timeit("_e = costly_fn()", globals=globals(), number=1) +print(f"3rd fn-call took {_t * 1e3:4.3f} ms") + +_t = timeit("_e = costly_fn()", globals=globals(), number=1) +print(f"4th fn-call took {_t * 1e3:4.3f} ms") + +_t = timeit("_e = list(range(10_000_000))", globals=globals(), number=1) +print(f"raw-call took {_t * 1e3:4.3f} ms -> seems to be always a bit slower") + + +print("\n################################### cached property") + + +class Costly: + @cached_property + def compute(self) -> list: + return list(range(10_000_000)) + + +_t = timeit("_e = Costly().compute", globals=globals(), number=1) +print(f"1st prop-call with instantiation {_t * 1e3:4.3f} ms -> uncached") + +_t = timeit("_e = Costly().compute", globals=globals(), number=1) +print( + f"2nd prop-call with instantiation {_t * 1e3:4.3f} ms -> newly created, uncached)", +) + +_c = Costly() + +_t = timeit("_e = _c.compute", globals=globals(), number=1) +print(f"1nd prop-call took {_t * 1e3:4.3f} ms ->") + +_t = timeit("_e = _c.compute", globals=globals(), number=1) +print(f"1st prop-call took {_t * 1e3:4.3f} ms -> now cached property)") diff --git a/scripts/benchmark_hashes.py b/scripts/benchmark_hashes.py new file mode 100644 index 000000000..ef2f4c816 --- /dev/null +++ b/scripts/benchmark_hashes.py @@ -0,0 +1,34 @@ +import hashlib +from timeit import timeit + +import proselint + +################################################################## +# Benchmark Hash-Algos +# -> on newer computer md5 is only slightly faster than sha256 (6%) +# -> choose sha224 for now (fastest on newer computers) +# TODO: test on old hw, x32 +################################################################## + +file_path = proselint.path / "demo.md" +f = file_path.open(encoding="utf-8", errors="replace") +text = f.read() + +# trial +_hash = hashlib.sha512(text.encode("utf-8")).hexdigest() + +contenders = { + "md5": "hashlib.md5(text.encode('utf-8')).hexdigest()", + "sha1": "hashlib.sha1(text.encode('utf-8')).hexdigest()", + "sha224": "hashlib.sha224(text.encode('utf-8')).hexdigest()", + "sha256": "hashlib.sha256(text.encode('utf-8')).hexdigest()", + "sha384": "hashlib.sha384(text.encode('utf-8')).hexdigest()", + "sha512": "hashlib.sha512(text.encode('utf-8')).hexdigest()", + "builtin": "hash(text)", + "strhash": "text.__hash__()", +} + +for _key, _val in contenders.items(): + _dur = timeit(_val, globals=globals(), number=1000) + _hash = eval(_val) # noqa: S307 + print(f"{_key} took {_dur * 1000:.2f} ms, result= {_hash}") diff --git a/scripts/benchmark_linter.py b/scripts/benchmark_linter.py new file mode 100644 index 000000000..f16eec508 --- /dev/null +++ b/scripts/benchmark_linter.py @@ -0,0 +1,49 @@ +from timeit import timeit + +import proselint +from proselint import memoizer +from proselint.tools import get_checks + +################################################################## +# Benchmark Linter & Cache +################################################################## + +if __name__ == "__main__": + # NOTE: Safeguard for windows + multiprocessing + python + # -> This script crashes when par=True + + file_path = proselint.path / "demo.md" + + _cfg = proselint.config_default + options = { + "serial": False, + "PAR": True, + } + + with file_path.open(encoding="utf-8", errors="replace") as fh: + _text = fh.read() + _checks = get_checks(_cfg) + # TODO: experiment with overhead of these + + for _name, _val in options.items(): + _cfg["parallelize"] = _val # TODO: interface changed + memoizer.cache.clear() + t1 = timeit( + "e1 = proselint.tools.lint(_text, _cfg, _checks)", + globals=locals(), + number=1, + ) + print(f"Lint with cfg={_name} took {t1 * 1e3:4.3f} ms uncached") + memoizer.cache.clear() + t2 = timeit( + "e1 = proselint.tools.lint(_text, _cfg, _checks)", + globals=locals(), + number=1, + ) + print(f"Lint with cfg={_name} took {t2 * 1e3:4.3f} ms uncached rerun") + t3 = timeit( + "e2 = proselint.tools.lint(_text, _cfg, _checks)", + globals=locals(), + number=1, + ) + print(f"Lint with cfg={_name} took {t3 * 1e3:4.3f} ms cached") diff --git a/scripts/benchmark_list_alternatives.py b/scripts/benchmark_list_alternatives.py new file mode 100644 index 000000000..861f430b4 --- /dev/null +++ b/scripts/benchmark_list_alternatives.py @@ -0,0 +1,59 @@ +from collections import deque +from timeit import timeit + +print("############# assemble with list-comprehension") + +el = deque(_i for _i in range(10)) +print(el) + +_dur = timeit( + stmt="el = deque(_i for _i in range(100))", globals=locals(), number=100_000 +) +print(f"dq_1k_a took {_dur * 1000:.2f} ms") +_dur = timeit( + stmt="el = deque([_i for _i in range(100)])", + globals=locals(), + number=100_000, +) +print(f"dq_l1k_a took {_dur * 1000:.2f} ms") +_dur = timeit( + stmt="el = list([_i for _i in range(100)])", + globals=locals(), + number=100_000, +) +print(f"lst_1k_a took {_dur * 1000:.2f} ms") + +print("############# assemble with iterable") +_dur = timeit(stmt="el = deque(range(100))", globals=locals(), number=100_000) +print(f"dq_1k_b took {_dur * 1000:.2f} ms") +_dur = timeit(stmt="el = deque([range(100)])", globals=locals(), number=100_000) +print(f"dq_l1k_b took {_dur * 1000:.2f} ms") +_dur = timeit(stmt="el = list(range(100))", globals=locals(), number=100_000) +print(f"lst_1k_b took {_dur * 1000:.2f} ms") + +print("############# extend with append()") +el = deque() +for _i in range(10): + el.append(_i) +print(el) + +x = 5 +_dur = timeit( + stmt="el.append(x)", setup="el = deque()", globals=locals(), number=100_000 +) +print(f"dq_app took {_dur * 1000:.2f} ms") +_dur = timeit( + stmt="el.append(x)", setup="el = []", globals=locals(), number=100_000 +) +print(f"dq_app took {_dur * 1000:.2f} ms") + +print("############# extend with extend(iterable)") +x = list(range(100)) +_dur = timeit( + stmt="el.extend(x)", setup="el = deque()", globals=locals(), number=100_000 +) +print(f"dq_app took {_dur * 1000:.2f} ms") +_dur = timeit( + stmt="el.extend(x)", setup="el = []", globals=locals(), number=100_000 +) +print(f"dq_app took {_dur * 1000:.2f} ms") diff --git a/scripts/generate_posts.py b/scripts/generate_posts.py index 5b9513fdb..8bb69186f 100755 --- a/scripts/generate_posts.py +++ b/scripts/generate_posts.py @@ -1,48 +1,47 @@ """Generate blog posts from check docstrings.""" -from builtins import str -from builtins import range -import os import ast -import datetime - +import os +from datetime import datetime +from pathlib import Path -grandparent = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -checks_dir = os.path.join(grandparent, "proselint", "checks") -listing = os.walk(checks_dir) +project_path = Path(__file__).parent.parent +checks_path = project_path / "proselint" / "checks" +listing = os.walk(checks_path) -def is_check(fn): +def is_check(file_path: Path) -> bool: """Check whether a file contains a check.""" - if not fn[-3:] == ".py": + if file_path.suffix != ".py": return False - if fn[-11:] == "__init__.py": + if file_path.name == "__init__.py": return False - if "inprogress" in fn: + if "inprogress" in file_path.name: return False return True -for root, subdirs, files in listing: +for root, _, files in listing: + root_path = Path(root) for file in files: - fn = os.path.join(root, file) - if is_check(fn): - M = ast.parse(''.join(open(os.path.join(checks_dir, fn)))) + file_path = root_path / file + if is_check(file_path): + M = ast.parse("".join(file_path.open())) docstring = ast.get_docstring(M) head, sep, tail = docstring.partition("title: ") docstring = head + sep + " :" + tail[4:] - post_filename = os.path.join( - os.path.join(grandparent, "site", "_posts"), - str(datetime.date.today()) + "-" + docstring[0:6] + ".md") + post_filename = ( + project_path + / f"site/_posts/{datetime.now().date()}-{docstring[0:6]}.md" + ) # Chop off the first two lines - for i in range(2): - docstring = '\n'.join(docstring.split('\n')[1:]) + docstring = "\n".join(docstring.split("\n")[2:]) # Create a new post in the blog. - with open(post_filename, 'w') as f: - f.write(docstring.encode('utf8')) + with post_filename.open("xb") as f: # TODO: added b, so test + f.write(docstring.encode("utf8")) diff --git a/scripts/insert_demo.py b/scripts/insert_demo.py index ca9fa8327..b0e398fe8 100755 --- a/scripts/insert_demo.py +++ b/scripts/insert_demo.py @@ -1,31 +1,26 @@ """Insert the demo into the codemirror site.""" -import os import fileinput import shutil +from pathlib import Path -proselint_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +project_path = Path(__file__).parent.parent -code_mirror_path = os.path.join( - proselint_path, - "plugins", - "webeditor") +code_mirror_path = project_path / "plugins" / "webeditor" -code_mirror_demo_path = os.path.join(code_mirror_path, "index.html") +code_mirror_demo_path = code_mirror_path / "index.html" -live_write_path = os.path.join(proselint_path, "site", "write") +live_write_path = project_path / "site" / "write" -if os.path.exists(live_write_path): +if live_write_path.exists(): shutil.rmtree(live_write_path) shutil.copytree(code_mirror_path, live_write_path) -demo_path = os.path.join(proselint_path, "proselint", "demo.md") +demo_path = project_path / "proselint" / "demo.md" -with open(demo_path, "r") as f: +with demo_path.open() as f: demo = f.read() -for line in fileinput.input( - os.path.join(live_write_path, "index.html"), inplace=True): - +for line in fileinput.input(live_write_path / "index.html", inplace=True): if "##DEMO_PLACEHOLDER##" in line: print(demo) else: diff --git a/scripts/profile_linter.py b/scripts/profile_linter.py new file mode 100644 index 000000000..5d6cd716e --- /dev/null +++ b/scripts/profile_linter.py @@ -0,0 +1,25 @@ +import cProfile + +import proselint +from proselint import memoizer + +################################################################## +# Profile Linter & Cache +# -> on newer computer overhead is ~ 100 ms +# -> demo-lint finishes in 500 ms / 100 ms (uncached, cached) +################################################################## + +file_path = proselint.path / "demo.md" +f = file_path.open(encoding="utf-8", errors="replace") + +memoizer.cache.clear() + +sort_by = "tottime" # tottime or cumtime, see readme + +cProfile.run("errors = proselint.tools.lint(f)", sort=sort_by) +cProfile.run("errors = proselint.tools.lint(f)", sort=sort_by) +# cProfile.run(f'proselint --compact {file_path.absolute()}', sort=sort_by) + +errors = proselint.tools.lint(f) +num_errors = len(errors) +print(f"Errors found {num_errors}") diff --git a/scripts/test_hyperlinks_on_shell.py b/scripts/test_hyperlinks_on_shell.py new file mode 100644 index 000000000..eda327e08 --- /dev/null +++ b/scripts/test_hyperlinks_on_shell.py @@ -0,0 +1,74 @@ +import contextlib +import logging +from pathlib import Path + +log = logging.getLogger("proselint") +log.setLevel(logging.DEBUG) +logging.basicConfig(format="%(message)s") + +file_path = Path("../proselint/demo.md") +cwd_path = Path.cwd() + +log.info("\n################ URI-format") + +file_str = "file:///./" + file_path.as_posix() +log.info("rel link: %s", file_str) + +file_str = "file:///./" + file_path.absolute().as_posix() +log.info("abs link: %s", file_str) + +file_str = file_path.absolute().as_uri() +log.info("uri link: %s", file_str) + +log.info("\n################ rel path") + +_file = file_path.absolute() +_cwd = cwd_path.absolute() +rel_path_str = _file.relative_to(_cwd).as_posix() +log.info("%s:5:30: simulation with rel1", rel_path_str) +rel_path_str = rel_path_str[3:] # ditch "../" +log.info("%s:5:30: simulation with rel2", rel_path_str) +rel_path_str = rel_path_str.replace("/", "\\") +log.info("%s:5:30: simulation with rel3", rel_path_str) +# this is working for ruff on windows in pycharm-console + +log.info("\n################ unicode hyperlinks") +# https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + +file_str = f"\u001b]8;;{file_path.absolute().as_posix()}\u001b\\{file_path.name}\u001b]8;;\u001b\\" +log.info("u0a link: %s", file_str) + +file_str = f"\u001b]8;;{file_path.absolute().as_uri()}\u001b\\{file_path.name}\u001b]8;;\u001b\\" +log.info("u0b link: %s", file_str) + +file_str = ( + f"\x1b]8;;{file_path.absolute().as_posix()}\a{file_path.name}\x1b]8;;\a" +) +log.info("x1a link: %s", file_str) + +file_str = ( + f"\x1b]8;;{file_path.absolute().as_uri()}\a{file_path.name}\x1b]8;;\a" +) +log.info("x1b link: %s", file_str) + +file_str = ( + f"\x1b]8;;{file_path.absolute().as_uri()}\a{file_path.name}\x1b]8;;\a" +) +log.info("x1b link: %s", file_str) + +log.info("\n################ rich links and more") + +with contextlib.suppress(ImportError): + from rich import print as printr + + file_str = f"[link={file_path.absolute().as_uri()}]{file_path.name}[/link]" + printr(f"rich link: {file_str}") + + file_str = f"[link=https://www.google.com]{file_path.name}[/link]" + printr(f"rich web: {file_str}") + + file_str = f"'./{file_path.as_posix()}:4:3'" + printr(f"test link: {file_str}") + + file_str = f"'{file_path.absolute().as_posix()}:4:3'" + printr(f"test link: {file_str}") diff --git a/scripts_web/__init__.py b/scripts_web/__init__.py new file mode 100644 index 000000000..c3cdf496d --- /dev/null +++ b/scripts_web/__init__.py @@ -0,0 +1,3 @@ +"""Flask Web App for proselint""" + +# TODO: this could be a separate module instead of scripts diff --git a/app.py b/scripts_web/app.py similarity index 73% rename from app.py rename to scripts_web/app.py index 94b6636b2..6bd6d8643 100644 --- a/app.py +++ b/scripts_web/app.py @@ -1,25 +1,35 @@ """Web app that serves proselint's API.""" -from flask import Flask, request, jsonify, make_response, Response -from flask_cors import CORS, cross_origin -from flask_limiter import Limiter +from __future__ import annotations + +import hashlib from functools import wraps from urllib.parse import unquote -import hashlib -import proselint + +from flask import Flask +from flask import Response +from flask import jsonify +from flask import make_response +from flask import request +from flask_cors import CORS +from flask_cors import cross_origin +from flask_limiter import Limiter from rq import Queue -from worker import conn +import proselint +from proselint.tools import ResultLint + +from .worker import conn app = Flask(__name__) cors = CORS(app) -app.config['CORS_HEADERS'] = "Origin, X-Requested-With, Content-Type, Accept" +app.config["CORS_HEADERS"] = "Origin, X-Requested-With, Content-Type, Accept" limiter = Limiter(app) q = Queue(connection=conn) -def worker_function(text): +def worker_function(text: str) -> list[ResultLint]: """Lint the text using a worker dyno.""" return proselint.tools.lint(text) @@ -28,7 +38,8 @@ def worker_function(text): def ratelimit_handler(e): """Inform user that the rate limit has been exceeded.""" return make_response( - jsonify(status="error", message="Rate limit exceeded."), 429) + jsonify(status="error", message="Rate limit exceeded."), 429 + ) def check_auth(username, password): @@ -61,19 +72,23 @@ def check_auth(username, password): def authenticate(): """Send a 401 response that enables basic auth.""" return Response( - 'Could not verify your access level for that URL.\n' - 'You have to login with proper credentials', 401, - {'WWW-Authenticate': 'Basic realm="Login Required"'}) + "Could not verify your access level for that URL.\n" + "You have to login with proper credentials", + 401, + {"WWW-Authenticate": 'Basic realm="Login Required"'}, + ) def requires_auth(f): """Decorate methods that require authentication.""" + @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): return authenticate() return f(*args, **kwargs) + return decorated @@ -83,39 +98,36 @@ def rate(): if not auth or not check_auth(auth.username, auth.password): return "60/minute" - else: - return "600/minute" + + return "600/minute" -@app.route('/v0/', methods=['GET', 'POST']) +@app.route("/v0/", methods=["GET", "POST"]) @limiter.limit(rate) @cross_origin() # allow all origins all methods. def lint(): """Run linter on the provided text and return the results.""" - if 'text' in request.values: - text = unquote(request.values['text']) + if "text" in request.values: + text = unquote(request.values["text"]) job = q.enqueue(worker_function, text) - return jsonify(job_id=job.id), 202 - elif 'job_id' in request.values: - job = q.fetch_job(request.values['job_id']) + if "job_id" in request.values: + job = q.fetch_job(request.values["job_id"]) if not job: return jsonify( - status="error", - message="No job with requested job_id."), 404 + status="error", message="No job with requested job_id." + ), 404 - elif job.result is None: - return jsonify( - status="error", - message="Job is not yet ready."), 202 - - else: - errors = [] - for i, e in enumerate(job.result): - app.logger.debug(e) - errors.append({ + if job.return_value() is None: + return jsonify(status="error", message="Job is not yet ready."), 202 + + errors = [] + for _, e in enumerate(job.return_value()): + app.logger.debug(e) + errors.append( + { "check": e[0], "message": e[1], "line": e[2], @@ -127,12 +139,11 @@ def lint(): "replacements": e[8], "source_name": "", "source_url": "", - }) - return jsonify( - status="success", - data={"errors": errors}) + }, + ) + return jsonify(status="success", data={"errors": errors}) -if __name__ == '__main__': +if __name__ == "__main__": app.debug = True app.run() diff --git a/clock.py b/scripts_web/clock.py similarity index 73% rename from clock.py rename to scripts_web/clock.py index 1be0cb3ac..81daeaf87 100644 --- a/clock.py +++ b/scripts_web/clock.py @@ -1,17 +1,20 @@ """Check emails to editor@proselint.com, lint them, and reply.""" -from apscheduler.schedulers.blocking import BlockingScheduler -import gmail -import smtplib -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from worker import conn -import requests import hashlib import json -import os import logging +import os import re +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +import gmail +import requests +from apscheduler.schedulers.blocking import BlockingScheduler +from worker import conn + +from proselint import log logging.basicConfig() scheduler = BlockingScheduler() @@ -20,7 +23,7 @@ user = "hello@lifelinter.com" user_to = "editor@proselint.com" name = "proselint" -password = os.environ['gmail_password'] +password = os.environ["gmail_password"] tagline = "Proselint, a linter for prose." url = "http://proselint.com" @@ -32,7 +35,7 @@ def quoted(string, every=64): return "> " + re.sub("\r\n(?=[^\r\n])", "\r\n> ", string) -@scheduler.scheduled_job('interval', minutes=0.25) +@scheduler.scheduled_job("interval", minutes=0.25) def check_email(): """Check the mail account and lint new mail.""" server = smtplib.SMTP("smtp.gmail.com", 587) @@ -50,24 +53,24 @@ def check_email(): # resulting job_ids somewhere (in Redis, I suppose), keyed by a hash of the # email. for u in unread: - u.fetch() - signature = (u.fr.decode('utf-8') + - u.subject.decode('utf-8') + - u.body.decode('utf-8')) + signature = ( + u.fr.decode("utf-8") + + u.subject.decode("utf-8") + + u.body.decode("utf-8") + ) - hash = hashlib.sha256(signature.encode('utf-8')).hexdigest() + _hash = hashlib.sha256(signature.encode("utf-8")).hexdigest() - if user_to in u.to or user_to in u.headers.get('Cc', []): - - job_id = conn.get(hash) + if user_to in u.to or user_to in u.headers.get("Cc", []): + job_id = conn.get(_hash) if not job_id: # If the email hasn't been sent for processing, send it. r = requests.post(api_url, data={"text": u.body}) - conn.set(hash, r.json()["job_id"]) - print("Email {} sent for processing.".format(hash)) + conn.set(_hash, r.json()["job_id"]) + log.info("Email %s sent for processing.", _hash) else: # Otherwise, check whether the results are ready, and if so, @@ -75,19 +78,18 @@ def check_email(): r = requests.get(api_url, params={"job_id": job_id}) if r.json()["status"] == "success": - reply = quoted(u.body) - errors = r.json()['data']['errors'] + errors = r.json()["data"]["errors"] reply += "\r\n\r\n".join([json.dumps(e) for e in errors]) msg = MIMEMultipart() - msg["From"] = "{} <{}>".format(name, user) + msg["From"] = f"{name} <{user}>" msg["To"] = u.fr msg["Subject"] = "Re: " + u.subject - if u.headers.get('Message-ID'): - msg.add_header("In-Reply-To", u.headers['Message-ID']) - msg.add_header("References", u.headers['Message-ID']) + if u.headers.get("Message-ID"): + msg.add_header("In-Reply-To", u.headers["Message-ID"]) + msg.add_header("References", u.headers["Message-ID"]) body = reply + "\r\n\r\n--\r\n" + tagline + "\r\n" + url msg.attach(MIMEText(body, "plain")) @@ -99,7 +101,7 @@ def check_email(): u.read() u.archive() - print("Email {} has been replied to.".format(hash)) + log.info("Email %s has been replied to.", _hash) scheduler.start() diff --git a/scripts_web/worker.py b/scripts_web/worker.py new file mode 100644 index 000000000..b7082ae28 --- /dev/null +++ b/scripts_web/worker.py @@ -0,0 +1,19 @@ +"""Heroku web worker.""" + +import os + +import redis +from rq import Connection +from rq import Queue +from rq import Worker + +listen = ["high", "default", "low"] + +redis_url = os.getenv("REDISTOGO_URL", "redis://localhost:6379") + +conn = redis.from_url(redis_url) + +if __name__ == "__main__": + with Connection(conn): + worker = Worker(list(map(Queue, listen))) + worker.work() diff --git a/site/_config.yml b/site/_config.yml index 10365d0fe..e229ee4ae 100755 --- a/site/_config.yml +++ b/site/_config.yml @@ -38,4 +38,4 @@ sass: gems: - jekyll-sitemap redcarpet: - extensions: ['tables'] \ No newline at end of file + extensions: ['tables'] diff --git a/site/_drafts/2014-06-09-so-what-is-jekyll.md b/site/_drafts/2014-06-09-so-what-is-jekyll.md index f5bcb4257..dd1d4d0e3 100755 --- a/site/_drafts/2014-06-09-so-what-is-jekyll.md +++ b/site/_drafts/2014-06-09-so-what-is-jekyll.md @@ -6,8 +6,8 @@ summary: Transform your plain text into static websites and blogs. Simple, st categories: jekyll pixyll --- -Jekyll is a tool for transforming your plain text into static websites and -blogs. It is simple, static, and blog-aware. Jekyll uses the +Jekyll is a tool for transforming your plain text into static websites and +blogs. It is simple, static, and blog-aware. Jekyll uses the [Liquid](http://docs.shopify.com/themes/liquid-basics) templating language and has builtin [Markdown](http://daringfireball.net/projects/markdown/) and [Textile](http://en.wikipedia.org/wiki/Textile_(markup_language)) support. diff --git a/site/_layouts/center.html b/site/_layouts/center.html index 3edfced6b..80002573b 100755 --- a/site/_layouts/center.html +++ b/site/_layouts/center.html @@ -14,4 +14,4 @@ {% include footer.html %} - \ No newline at end of file + diff --git a/site/browserconfig.xml b/site/browserconfig.xml index c55414822..7d453d3bb 100755 --- a/site/browserconfig.xml +++ b/site/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff diff --git a/site/images/browserconfig.xml b/site/images/browserconfig.xml index c55414822..7d453d3bb 100755 --- a/site/images/browserconfig.xml +++ b/site/images/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff diff --git a/site/images/manifest.json b/site/images/manifest.json index 013d4a6a5..758f8d2a6 100755 --- a/site/images/manifest.json +++ b/site/images/manifest.json @@ -38,4 +38,4 @@ "density": "4.0" } ] -} \ No newline at end of file +} diff --git a/site/manifest.json b/site/manifest.json index 013d4a6a5..758f8d2a6 100755 --- a/site/manifest.json +++ b/site/manifest.json @@ -38,4 +38,4 @@ "density": "4.0" } ] -} \ No newline at end of file +} diff --git a/tests/__init__.py b/tests/__init__.py index b33611c6d..24cad21ad 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1,6 @@ """Tests for proselint.""" + +# todo: +# - switch to "assert check("text") == []" or .__len__() == 0 +# - for multi-checks put check into test-fn with pytest-wrapper +# - OR: test_pass should also crosscheck with lint() diff --git a/tests/_test_version.py b/tests/_test_version.py deleted file mode 100644 index e66fb1a9a..000000000 --- a/tests/_test_version.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Test version number.""" -from click.testing import CliRunner - -from proselint.command_line import proselint -from proselint.version import __version__ - -from .check import Check - - -class TestCheck(Check): - """Test class for version number.""" - - __test__ = True - - def test_version(self): - """Make sure the version number is correct.""" - runner = CliRunner() - - output = runner.invoke(proselint, "--version") - assert __version__ in output.stdout diff --git a/tests/check.py b/tests/check.py index 345c9162f..12d376c16 100644 --- a/tests/check.py +++ b/tests/check.py @@ -1,63 +1,46 @@ """Check that a check is working.""" -import codecs import os -from unittest import TestCase +from pathlib import Path +from proselint.tools import lint -class Check(TestCase): - """All tests inherit from Check.""" - - __test__ = False - - def setUp(self): - """Create a placeholder for setup procedure.""" - pass - def tearDown(self): - """Create a placeholder for teardown procedure.""" - pass +def assert_error(check: str, text: str, n: int = 1): + # TODO: check_in_result - replace remaining + """Assert that text has n errors of type check.""" + assert_error.description = f"No {check} error for '{text}'" + assert check in [error[0] for error in lint(text)] - @property - def this_check(self): - """Create a placeholder for the specific check.""" - raise NotImplementedError - def passes(self, lst): - """Check if the test runs cleanly on the given text.""" - if isinstance(lst, str): - lst = [lst] - - errors = [] - for text in lst: - errors += self.this_check.check.__wrapped__(text) - - return len(errors) == 0 +class Check: + """All tests inherit from Check.""" - def wpe_too_high(self): + def wpe_too_high(check) -> None: # todo is this salvagable? """Check whether the check is too noisy.""" min_wpe = 50 - examples_dir = os.path.join(os.getcwd(), "tests", "corpus") - examples = os.listdir(examples_dir) + examples_path = ( + Path.cwd() / "tests" / "corpus" + ) # TODO: probably old path to corpus + examples = os.listdir(examples_path) for example in examples: - example_path = os.path.join(examples_dir, example) + example_path = examples_path / example if ".DS_Store" in example_path: break - # Compute the number of words per (wpe) error. - with codecs.open(example_path, "r", encoding='utf-8') as f: - text = f.read() - num_errors = len(self.this_check.check.__wrapped__(text)) + # Compute the number of words per error. + with example_path.open(encoding="utf-8") as fh: + text = fh.read() + num_errors = len(check.__wrapped__(text)) num_words = len(text) try: wpe = 1.0 * num_words / num_errors except ZeroDivisionError: - wpe = float('Inf') + wpe = float("Inf") # Make sure that - assert wpe > min_wpe, \ - f"{example} has only {round(wpe, 2)} wpe." + assert wpe > min_wpe, f"{example} has only {round(wpe, 2)} wpe." diff --git a/tests/checks/__init__.py b/tests/checks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/checks/test_annotations.py b/tests/checks/test_annotations.py new file mode 100644 index 000000000..6a47050e8 --- /dev/null +++ b/tests/checks/test_annotations.py @@ -0,0 +1,12 @@ +"""Tests for annotations.misc check.""" + +from proselint.checks.annotations.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for annotations.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "Add it to the to do list.") + assert_fail(check, "Add it to the TODO list.") diff --git a/tests/checks/test_archaism.py b/tests/checks/test_archaism.py new file mode 100644 index 000000000..1bd9a7657 --- /dev/null +++ b/tests/checks/test_archaism.py @@ -0,0 +1,12 @@ +"""Tests for archaism.misc check.""" + +from proselint.checks.archaism.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for archaism.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "I want to sleep, and maybe dream.") + assert_fail(check, "I want to sleep, perchance to dream.") diff --git a/tests/checks/test_cliches.py b/tests/checks/test_cliches.py new file mode 100644 index 000000000..beaa23dfe --- /dev/null +++ b/tests/checks/test_cliches.py @@ -0,0 +1,78 @@ +"""Test the Cliches.misc module.""" + +from proselint.checks.cliches import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_garner(): + """Basic checks on check_cliches_garner.""" + text = "Worse than a fate worse than death." + check = misc.check_cliches_garner + assert_pass(check, "No cliches here.") + assert_fail(check, text) + assert "cliches.garner" in check(text)[0] + + +def test_write_good(): + """Basic checks on check_cliches_write_good.""" + text = "He's a chip off the old block." + check = misc.check_cliches_write_good_a_to_c + assert_pass(check, "No cliches here.") + assert_fail(check, text) + assert "cliches.write_good" in check(text)[0] + + +def test_write_good_c(): + """Basic checks on check_cliches_write_good.""" + check = misc.check_cliches_write_good_a_to_c + assert_pass(check, "No cliches here.") + assert_fail(check, "You sound like a broken record.") + assert_fail(check, "You gave me a crash course in xyz.") + + +def test_write_good_g(): + """Basic checks on check_cliches_write_good.""" + check = misc.check_cliches_write_good_d_to_j + assert_pass(check, "No cliches here.") + assert_fail(check, "You always had a green thumb.") + + +def test_write_good_k(): + """Basic checks on check_cliches_write_good.""" + check = misc.check_cliches_write_good_k_to_o + assert_pass(check, "No cliches here.") + assert_fail(check, "You know the score.") + assert_fail(check, "You payed out of pocket for years.") + + +def test_write_good_s(): + """Basic checks on check_cliches_write_good.""" + check = misc.check_cliches_write_good_p_to_s + assert_pass(check, "No cliches here.") + assert_fail(check, "I feel sick as a dog since yesterday.") + assert_fail(check, "I feel sick as a dog.") + + +def test_write_good_t(): + """Basic checks on check_cliches_write_good.""" + check = misc.check_cliches_write_good_t_to_z + assert_pass(check, "No cliches here.") + assert_fail(check, "You, me and that uphill battle.") + assert_fail(check, "You, me and that uphill battle with him.") + + +def test_gnu_diction(): + """Basic check on check_cliches_gnu_diction.""" + text = "It's a matter of concern." + check = misc.check_cliches_gnu_diction + assert_pass(check, "No cliches here.") + assert_fail(check, text) + assert "cliches.gnu_diction" in check(text)[0] + + +def test_cliches_nigel_basic(): + """Basic check on check_cliches_nigel.""" + check = misc.check_cliches_nigel + assert_pass(check, "No cliches here.") + assert_fail(check, "He's sick and tired of it.") diff --git a/tests/checks/test_consistency_spacing.py b/tests/checks/test_consistency_spacing.py new file mode 100644 index 000000000..c4b0cffbf --- /dev/null +++ b/tests/checks/test_consistency_spacing.py @@ -0,0 +1,12 @@ +"""Tests for consistency.spacing check.""" + +from proselint.checks.consistency.spacing import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for consistency.spacing.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "This is good. Only one space each time. Every time.") + assert_fail(check, "This is bad. Not consistent. At all.") diff --git a/tests/checks/test_consistency_spelling.py b/tests/checks/test_consistency_spelling.py new file mode 100644 index 000000000..e5839a98b --- /dev/null +++ b/tests/checks/test_consistency_spelling.py @@ -0,0 +1,13 @@ +"""Tests for consistency.spelling check.""" + +from proselint.checks.consistency.spelling import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for consistency.spelling.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "The centre for the arts is the art centre.") + assert_pass(check, "The center for the arts is the art center.") + assert_fail(check, "The centre of the arts is the art center.") diff --git a/tests/checks/test_corporate_speak.py b/tests/checks/test_corporate_speak.py new file mode 100644 index 000000000..dc6f3269a --- /dev/null +++ b/tests/checks/test_corporate_speak.py @@ -0,0 +1,12 @@ +"""Tests for corporate_speak.misc check.""" + +from proselint.checks.corporate_speak.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for corporate_speak.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "We will discuss it later.") + assert_fail(check, "We will circle back around to it.") diff --git a/tests/checks/test_cursing_filth.py b/tests/checks/test_cursing_filth.py new file mode 100644 index 000000000..590b3ac25 --- /dev/null +++ b/tests/checks/test_cursing_filth.py @@ -0,0 +1,11 @@ +"""Tests for cursing.filth check.""" + +from proselint.checks.cursing.filth import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for cursing.filth.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Bad shit in this phrase.") diff --git a/tests/checks/test_cursing_nfl.py b/tests/checks/test_cursing_nfl.py new file mode 100644 index 000000000..c0ac84c04 --- /dev/null +++ b/tests/checks/test_cursing_nfl.py @@ -0,0 +1,34 @@ +"""Tests for cursing.nfl check.""" + +from proselint.checks.cursing import nfl +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_a(): + """Basic smoke test for cursing.nfl.""" + check = nfl.check_a_to_e + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The QB is named ball licker.") + + +def test_f(): + """Basic smoke test for cursing.nfl.""" + check = nfl.check_f_to_h + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The difference between femme and famme.") + + +def test_i(): + """Basic smoke test for cursing.nfl.""" + check = nfl.check_i_to_p + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Interracial is the word.") + assert_fail(check, "you jackass, be funny.") + + +def test_q(): + """Basic smoke test for cursing.nfl.""" + check = nfl.check_q_to_z + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "To rent a fuck or not to rent a fuck.") diff --git a/tests/checks/test_cursing_nword.py b/tests/checks/test_cursing_nword.py new file mode 100644 index 000000000..798382750 --- /dev/null +++ b/tests/checks/test_cursing_nword.py @@ -0,0 +1,11 @@ +"""Tests for cursing.nword check.""" + +from proselint.checks.cursing.nword import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for cursing.nword.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The n-word.") diff --git a/tests/checks/test_dates_times_am_pm.py b/tests/checks/test_dates_times_am_pm.py new file mode 100644 index 000000000..b2a2e9231 --- /dev/null +++ b/tests/checks/test_dates_times_am_pm.py @@ -0,0 +1,58 @@ +"""Tests for dates_times.am_pm check.""" + +from proselint.checks.dates_times import am_pm +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_lowercase_periods(): + """Basic smoke test. + + This is for the function + dates_times.am_pm.check_lowercase_periods. + + """ + check = am_pm.check_lowercase_periods + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened at 7 a.m.") + assert_fail(check, "It happened at 7 am.") + assert_pass(check, "On Wed, Sep 21, 2016 at 11:42 AM -0400, X wrote:") + + +def test_spacing(): + """Basic smoke test. + + This is for the function + dates_times.am_pm.check_spacing. + + """ + check = am_pm.check_spacing + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened at 7 a.m.") + assert_fail(check, "It happened at 7a.m.") + + +def test_midnight_noon(): + """Basic smoke test. + + This for the function + dates_times.am_pm.midnight_noon. + + """ + check = am_pm.check_midnight_noon + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened at noon.") + assert_fail(check, "It happened at 12 a.m.") + + +def test_redundancy(): + """Basic smoke test. + + This for the function + dates_times.am_pm.check_redundancy. + + """ + check = am_pm.check_redundancy + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened at 7 a.m.") + assert_fail(check, "It happened at 7a.m. in the morning.") diff --git a/tests/checks/test_dates_times_dates.py b/tests/checks/test_dates_times_dates.py new file mode 100644 index 000000000..8202f1ee6 --- /dev/null +++ b/tests/checks/test_dates_times_dates.py @@ -0,0 +1,70 @@ +"""Tests for dates_times.dates check.""" + +from proselint.checks.dates_times import dates +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_decade_apostrophes_short(): + """Basic smoke test. + + This for the function + dates_times.dates.check_decade_apostrophes_short. + + """ + check = dates.check_decade_apostrophes_short + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened in the 90s.") + assert_fail(check, "It happened in the 90's.") + + +def test_decade_apostrophes_long(): + """Basic smoke test. + + This is for the function + dates_times.dates.decade_apostrophes_long. + + """ + check = dates.check_decade_apostrophes_long + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened in the 1980s.") + assert_fail(check, "It happened in the 1980's.") + + +def test_dash_and_from(): + """Basic smoke test. + + This for the function + dates_times.dates.dash_and_from. + + """ + check = dates.check_dash_and_from + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened from 2000 to 2005.") + assert_fail(check, "It happened from 2000-2005.") + + +def test_month_year_comma(): + """Basic smoke test. + + This is for the function + dates_times.dates.check_month_year_comma. + + """ + check = dates.check_month_year_comma + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened in August 2008.") + assert_fail(check, "It happened in August, 2008.") + + +def test_month_of_year(): + """Basic smoke test. + + This is for the function + dates_times.dates.check_month_of_year. + + """ + check = dates.check_month_of_year + assert_pass(check, "Basic smoke phrase without issues.") + assert_pass(check, "It happened in August 2008.") + assert_fail(check, "It happened in August of 2008.") diff --git a/tests/checks/test_garner_dates.py b/tests/checks/test_garner_dates.py new file mode 100644 index 000000000..42235f215 --- /dev/null +++ b/tests/checks/test_garner_dates.py @@ -0,0 +1,29 @@ +"""Test garner.dates.""" +from proselint.checks.dates_times import dates +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_50s_hyphenation(): + """Find unneeded hyphen in 50's.""" + + text = """The 50's were swell.""" + check = dates.check_decade_apostrophes_short + assert_fail(check, text) + + +def test_50_cent_hyphenation(): + """Don't flag 50's when it refers to 50 Cent's manager.""" + text = """ + Dr. Dre suggested to 50's manager that he look into signing + Eminem to the G-Unit record label. + """ + check = dates.check_decade_apostrophes_short + assert_pass(check, text) + + +def test_dash_and_from(): + """Test garner.check_dash_and_from.""" + text = """From 1999-2002, Sally served as chair of the committee.""" + check = dates.check_dash_and_from + assert_fail(check, text) diff --git a/tests/checks/test_hedging.py b/tests/checks/test_hedging.py new file mode 100644 index 000000000..f5c9cc0e5 --- /dev/null +++ b/tests/checks/test_hedging.py @@ -0,0 +1,11 @@ +"""Tests for hedging.misc check.""" + +from proselint.checks.hedging.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for hedging.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I would argue that this is a good test.") diff --git a/tests/checks/test_hyperbole.py b/tests/checks/test_hyperbole.py new file mode 100644 index 000000000..65ed0b017 --- /dev/null +++ b/tests/checks/test_hyperbole.py @@ -0,0 +1,11 @@ +"""Tests for hyperbole.misc check.""" + +from proselint.checks.hyperbole.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for hyperbole.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "So exaggerated!!!") diff --git a/tests/checks/test_jargon.py b/tests/checks/test_jargon.py new file mode 100644 index 000000000..cc14099f2 --- /dev/null +++ b/tests/checks/test_jargon.py @@ -0,0 +1,11 @@ +"""Tests for jargon.misc check.""" + +from proselint.checks.jargon.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for jargon.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I agree it's in the affirmative.") diff --git a/tests/checks/test_leonard.py b/tests/checks/test_leonard.py new file mode 100644 index 000000000..b880cc50c --- /dev/null +++ b/tests/checks/test_leonard.py @@ -0,0 +1,22 @@ +"""Test garner.dates.""" +from proselint.checks.typography import exclamation +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_repeated_exclamations(): + """Test leonard.exclamation. with exclamation marks.""" + check = exclamation.check_repeated_exclamations + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "The QUICK BROWN fox juMPED over the lazy cat.") + assert_fail(check, "Sally sells seashells and they were too expensive!!!!") + + +def test_exclamation_ppm(): + check = exclamation.check_exclamations_ppm + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "Sally sells seashells and they were too expensive!") + assert_fail( + check, + "Sally sells seashells and they were too expensive! They were not!", + ) diff --git a/tests/checks/test_lexical_illusions.py b/tests/checks/test_lexical_illusions.py new file mode 100644 index 000000000..f76932dcb --- /dev/null +++ b/tests/checks/test_lexical_illusions.py @@ -0,0 +1,21 @@ +"""Tests for lexical_illusions.misc check.""" + +from proselint.checks.lexical_illusions.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for lexical_illusions.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Paris in the the springtime.") + assert_pass(check, "And he's gone, gone with the breeze") + assert_pass(check, "You should know that that sentence wasn't wrong.") + assert_pass(check, "She had had dessert on the balcony.") + assert_fail(check, "You should know that that that was wrong.") + assert_pass(check, "The practitioner's side") + assert_pass(check, "An antimatter particle") + assert_pass(check, "The theory") + assert_pass(check, "She had coffee at the Foo-bar bar.") + assert_fail(check, "She had coffee at the Foo bar bar.") + assert_fail(check, "She she coffee at the Foo-bar.") diff --git a/tests/checks/test_lgbtq_offensive_terms.py b/tests/checks/test_lgbtq_offensive_terms.py new file mode 100644 index 000000000..7ae344057 --- /dev/null +++ b/tests/checks/test_lgbtq_offensive_terms.py @@ -0,0 +1,12 @@ +"""Tests for lgbtq.terms check.""" + +from proselint.checks.lgbtq.offensive_terms import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for lgbtq.offensive_terms.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "I once met a gay man.") + assert_fail(check, "I once met a fag.") diff --git a/tests/checks/test_lgbtq_terms.py b/tests/checks/test_lgbtq_terms.py new file mode 100644 index 000000000..b8b452d01 --- /dev/null +++ b/tests/checks/test_lgbtq_terms.py @@ -0,0 +1,22 @@ +"""Tests for lgbtq.offensive_terms check.""" + +from proselint.checks.lgbtq.terms import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for lgbtq.terms.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "They were a gay couple.") + assert_fail(check, "He was a homosexual man.") + + +def test_homosexual_term(): + """Check that the term homosexual does not get caught.""" + assert_pass(check, "Homosexual.") + + +def test_sexual_preference(): + """Check that sexual preference is flagged.""" + assert_fail(check, "My sexual preference is for women.") diff --git a/tests/checks/test_malaproprisms.py b/tests/checks/test_malaproprisms.py new file mode 100644 index 000000000..060c8fd76 --- /dev/null +++ b/tests/checks/test_malaproprisms.py @@ -0,0 +1,11 @@ +"""Tests for malaproprisms.misc check.""" + +from proselint.checks.malapropisms.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for malaproprisms.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Found in the Infinitesimal Universe.") diff --git a/tests/checks/test_misc_apologizing.py b/tests/checks/test_misc_apologizing.py new file mode 100644 index 000000000..dc23666b8 --- /dev/null +++ b/tests/checks/test_misc_apologizing.py @@ -0,0 +1,11 @@ +"""Tests for misc.apologizing check.""" + +from proselint.checks.misc.apologizing import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.apologizing.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "More research is needed.") diff --git a/tests/checks/test_misc_back_formations.py b/tests/checks/test_misc_back_formations.py new file mode 100644 index 000000000..df878ebf1 --- /dev/null +++ b/tests/checks/test_misc_back_formations.py @@ -0,0 +1,11 @@ +"""Tests for misc.back_formations check.""" + +from proselint.checks.misc.back_formations import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.back_formations.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It is an improprietous use.") diff --git a/tests/checks/test_misc_bureaucratese.py b/tests/checks/test_misc_bureaucratese.py new file mode 100644 index 000000000..e566e813c --- /dev/null +++ b/tests/checks/test_misc_bureaucratese.py @@ -0,0 +1,11 @@ +"""Tests for misc.bureaucratese check.""" + +from proselint.checks.misc.bureaucratese import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.bureaucratese.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I hope the report meets with your approval.") diff --git a/tests/checks/test_misc_but.py b/tests/checks/test_misc_but.py new file mode 100644 index 000000000..51ec2382c --- /dev/null +++ b/tests/checks/test_misc_but.py @@ -0,0 +1,15 @@ +"""Tests for misc.but check.""" + +from proselint.checks.misc.but import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.but.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, 'But I never start with the word "but".') + assert_pass( + check, + 'I never start with the word "but", but I might use it sometimes.', + ) diff --git a/tests/checks/test_misc_capitalization.py b/tests/checks/test_misc_capitalization.py new file mode 100644 index 000000000..e859893fc --- /dev/null +++ b/tests/checks/test_misc_capitalization.py @@ -0,0 +1,34 @@ +"""Tests for misc.capitalization check.""" + +from proselint.checks.misc import capitalization +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for misc.capitalization.check.""" + check = capitalization.check + assert_pass(check, "Smoke Stone Age with nothing flagged.") + assert_fail(check, "It goes back to the stone age.") + + +def test_seasons(): + """Basic smoke test for misc.capitalization.check_months.""" + check = capitalization.check_seasons + assert_pass(check, "Smoke winter with nothing flagged") + assert_fail(check, "A nice day during Winter.") + assert_fail(check, "A nice day in Spring.") + + +def test_months(): + """Basic smoke test for misc.capitalization.check_months.""" + check = capitalization.check_months + assert_pass(check, "Smoke phrase with nothing flagged") + assert_fail(check, "A nice day in june.") + + +def test_days(): + """Basic smoke test for misc.capitalization.check_days.""" + check = capitalization.check_days + assert_pass(check, "Smoke phrase with nothing flagged") + assert_fail(check, "It happened on friday.") diff --git a/tests/checks/test_misc_chatspeak.py b/tests/checks/test_misc_chatspeak.py new file mode 100644 index 000000000..086fcb75c --- /dev/null +++ b/tests/checks/test_misc_chatspeak.py @@ -0,0 +1,11 @@ +"""Tests for misc.chatspeak check.""" + +from proselint.checks.misc.chatspeak import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.chatspeak.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "BRB getting coffee.") diff --git a/tests/checks/test_misc_commercialese.py b/tests/checks/test_misc_commercialese.py new file mode 100644 index 000000000..6b927dc41 --- /dev/null +++ b/tests/checks/test_misc_commercialese.py @@ -0,0 +1,11 @@ +"""Tests for misc.commercialese check.""" + +from proselint.checks.misc.commercialese import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def tests(): + """Basic smoke test for misc.commercialese.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "We regret to inform you of this.") diff --git a/tests/checks/test_misc_composition.py b/tests/checks/test_misc_composition.py new file mode 100644 index 000000000..4179f7357 --- /dev/null +++ b/tests/checks/test_misc_composition.py @@ -0,0 +1,11 @@ +"""Tests for misc.composition check.""" + +from proselint.checks.misc.composition import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.composition.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "His story is not honest.") diff --git a/tests/checks/test_misc_currency.py b/tests/checks/test_misc_currency.py new file mode 100644 index 000000000..2aaa6d2ee --- /dev/null +++ b/tests/checks/test_misc_currency.py @@ -0,0 +1,11 @@ +"""Tests for misc.currency check.""" + +from proselint.checks.misc.currency import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.currency.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It cost $10 dollars.") diff --git a/tests/checks/test_misc_debased.py b/tests/checks/test_misc_debased.py new file mode 100644 index 000000000..c892b7989 --- /dev/null +++ b/tests/checks/test_misc_debased.py @@ -0,0 +1,11 @@ +"""Tests for misc.debased check.""" + +from proselint.checks.misc.debased import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.debased.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "This leaves much to be desired.") diff --git a/tests/checks/test_misc_false_plurals.py b/tests/checks/test_misc_false_plurals.py new file mode 100644 index 000000000..a87e194b1 --- /dev/null +++ b/tests/checks/test_misc_false_plurals.py @@ -0,0 +1,19 @@ +"""Tests for misc.false_plurals check.""" + +from proselint.checks.misc import false_plurals +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for misc.false_plurals.""" + check = false_plurals.check + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "There were several phenomenons.") + + +def test_kudos(): + """Basic smoke test for misc.false_plurals.kudos.""" + check = false_plurals.check_kudos + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I give you many kudos.") diff --git a/tests/checks/test_misc_greylist.py b/tests/checks/test_misc_greylist.py new file mode 100644 index 000000000..e9149f2cd --- /dev/null +++ b/tests/checks/test_misc_greylist.py @@ -0,0 +1,23 @@ +"""Tests for misc.greylist check.""" + +from proselint.checks.misc.greylist import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for misc.greylist.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "She should utilize her knowledge.") + assert_fail( + check, "This is obviously an inadvisable word to use obviously." + ) + + +def test_utilized(): + """Don't produce an error when 'use' is used correctly.""" + assert_pass(check, "I use a hammer to drive nails into wood.") + assert_fail(check, "I utilize a hammer to drive nails into wood.") + assert_fail( + check, "Do you know anyone who *needs* to utilize the word utilize?" + ) diff --git a/tests/checks/test_misc_illogic.py b/tests/checks/test_misc_illogic.py new file mode 100644 index 000000000..acc6fa143 --- /dev/null +++ b/tests/checks/test_misc_illogic.py @@ -0,0 +1,26 @@ +"""Tests for misc.illogic check.""" + +from proselint.checks.misc import illogic +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for misc.illogic.""" + check = illogic.check + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "We should preplan the trip.") + + +def test_coin_a_phrase_from(): + """Basic smoke test for misc.illogic.check_coin_a_phrase_from.""" + check = illogic.check_coin_a_phrase_from + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "To coin a phrase from him, No diggity") + + +def test_without_your_collusion(): + """Basic smoke test for misc.illogic.""" + check = illogic.check_without_your_collusion + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Not Without your collusion you won't'.") diff --git a/tests/checks/test_misc_inferior_superior.py b/tests/checks/test_misc_inferior_superior.py new file mode 100644 index 000000000..4c44d88e7 --- /dev/null +++ b/tests/checks/test_misc_inferior_superior.py @@ -0,0 +1,11 @@ +"""Tests for misc.inferior_superior check.""" + +from proselint.checks.misc.inferior_superior import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.inferior_superior.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was more inferior than the alternative.") diff --git a/tests/checks/test_misc_latin.py b/tests/checks/test_misc_latin.py new file mode 100644 index 000000000..97af9b8ea --- /dev/null +++ b/tests/checks/test_misc_latin.py @@ -0,0 +1,11 @@ +"""Tests for misc.latin check.""" + +from proselint.checks.misc.latin import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.latin.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "And ceteris paribus, it was good.") diff --git a/tests/checks/test_misc_many_a.py b/tests/checks/test_misc_many_a.py new file mode 100644 index 000000000..b0f083cba --- /dev/null +++ b/tests/checks/test_misc_many_a.py @@ -0,0 +1,11 @@ +"""Tests for misc.many_a check.""" + +from proselint.checks.misc.many_a import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.many_a.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "There were many a day I thought about it.") diff --git a/tests/checks/test_misc_metadiscourse.py b/tests/checks/test_misc_metadiscourse.py new file mode 100644 index 000000000..45874a094 --- /dev/null +++ b/tests/checks/test_misc_metadiscourse.py @@ -0,0 +1,11 @@ +"""Tests for misc.metadiscourse check.""" + +from proselint.checks.misc.metadiscourse import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.metadiscourse.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It's based on the rest of this article.") diff --git a/tests/checks/test_misc_narcissism.py b/tests/checks/test_misc_narcissism.py new file mode 100644 index 000000000..7e286e752 --- /dev/null +++ b/tests/checks/test_misc_narcissism.py @@ -0,0 +1,15 @@ +"""Tests for misc.narcissism check.""" + +from proselint.checks.misc.narcissism import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.narcissism.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail( + check, + """In recent years, an increasing number of scientists have studied + the problem in detail.""", + ) diff --git a/tests/checks/test_misc_not_guilty.py b/tests/checks/test_misc_not_guilty.py new file mode 100644 index 000000000..2e54572a5 --- /dev/null +++ b/tests/checks/test_misc_not_guilty.py @@ -0,0 +1,11 @@ +"""Tests for misc.not_guilty check.""" + +from proselint.checks.misc.not_guilty import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.not_guilty.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "She is not guilty beyond a reasonable doubt.") diff --git a/tests/checks/test_misc_phrasal_adjectives.py b/tests/checks/test_misc_phrasal_adjectives.py new file mode 100644 index 000000000..4723214af --- /dev/null +++ b/tests/checks/test_misc_phrasal_adjectives.py @@ -0,0 +1,20 @@ +"""Tests for misc.phrasal_adjectives check.""" + +from proselint.checks.misc import phrasal_adjectives +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for misc.phrasal_adjectives.""" + check = phrasal_adjectives.check + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "There were across the board discounts.") + + +def test_ly(): + """Basic smoke test for misc.phrasal_adjectives.check_ly.""" + check = phrasal_adjectives.check_ly + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "He ran swiftly-fast.") + assert_pass(check, "The not-so-hotly-contested result was fine.") diff --git a/tests/checks/test_misc_preferred_forms.py b/tests/checks/test_misc_preferred_forms.py new file mode 100644 index 000000000..732746261 --- /dev/null +++ b/tests/checks/test_misc_preferred_forms.py @@ -0,0 +1,11 @@ +"""Tests for misc.preferred_forms check.""" + +from proselint.checks.misc.preferred_forms import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.preferred_forms.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was almost haloween.") diff --git a/tests/checks/test_misc_pretension.py b/tests/checks/test_misc_pretension.py new file mode 100644 index 000000000..0a9c1253b --- /dev/null +++ b/tests/checks/test_misc_pretension.py @@ -0,0 +1,11 @@ +"""Tests for misc.pretension check.""" + +from proselint.checks.misc.pretension import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.pretension.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "We need to reconceptualize the project.") diff --git a/tests/checks/test_misc_professions.py b/tests/checks/test_misc_professions.py new file mode 100644 index 000000000..619a1f7bb --- /dev/null +++ b/tests/checks/test_misc_professions.py @@ -0,0 +1,11 @@ +"""Tests for misc.professions check.""" + +from proselint.checks.misc.professions import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.professions.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I really need a shoe repair guy.") diff --git a/tests/checks/test_misc_punctuation.py b/tests/checks/test_misc_punctuation.py new file mode 100644 index 000000000..38f9b35de --- /dev/null +++ b/tests/checks/test_misc_punctuation.py @@ -0,0 +1,11 @@ +"""Tests for misc.punctuation check.""" + +from proselint.checks.misc.punctuation import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.punctuation.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "See Smith et. al.") diff --git a/tests/checks/test_misc_scare_quotes.py b/tests/checks/test_misc_scare_quotes.py new file mode 100644 index 000000000..9a59ec382 --- /dev/null +++ b/tests/checks/test_misc_scare_quotes.py @@ -0,0 +1,11 @@ +"""Tests for misc.scare_quotes check.""" + +from proselint.checks.misc.scare_quotes import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.scare_quotes.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "What was the 'take-home message'?") diff --git a/tests/checks/test_misc_suddenly.py b/tests/checks/test_misc_suddenly.py new file mode 100644 index 000000000..e2c70c210 --- /dev/null +++ b/tests/checks/test_misc_suddenly.py @@ -0,0 +1,11 @@ +"""Tests for misc.suddenly check.""" + +from proselint.checks.misc.suddenly import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.suddenly.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Suddenly, it all made sense.") diff --git a/tests/checks/test_misc_tense_present.py b/tests/checks/test_misc_tense_present.py new file mode 100644 index 000000000..988916dd4 --- /dev/null +++ b/tests/checks/test_misc_tense_present.py @@ -0,0 +1,19 @@ +"""Tests for misc.tense_present check.""" + +from proselint.checks.misc.tense_present import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.tense_present.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + + assert_pass(check, "I did it by accident honestly.") + assert_fail(check, "I did it on accident honestly.") + assert_fail(check, "I did it On accident honestly.") + + assert_fail(check, "Told you something between you and i.") + assert_fail(check, "Told you something between you and I.") + + assert_fail(check, "I feel nauseous.") diff --git a/tests/checks/test_misc_waxed.py b/tests/checks/test_misc_waxed.py new file mode 100644 index 000000000..16de9f8d3 --- /dev/null +++ b/tests/checks/test_misc_waxed.py @@ -0,0 +1,13 @@ +"""Tests for misc.waxed check.""" + +from proselint.checks.misc.waxed import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.waxed.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "Wax me if you can.") + assert_pass(check, "He waxed poetic.") + assert_fail(check, "They really could wax poetically.") diff --git a/tests/checks/test_misc_whence.py b/tests/checks/test_misc_whence.py new file mode 100644 index 000000000..04f24b31c --- /dev/null +++ b/tests/checks/test_misc_whence.py @@ -0,0 +1,11 @@ +"""Tests for misc.whence check.""" + +from proselint.checks.misc.whence import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for misc.whence.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Go back from whence you came!") diff --git a/tests/checks/test_mixed_metaphors.py b/tests/checks/test_mixed_metaphors.py new file mode 100644 index 000000000..b7768f6a0 --- /dev/null +++ b/tests/checks/test_mixed_metaphors.py @@ -0,0 +1,19 @@ +"""Tests for mixed_metaphors.misc check.""" + +from proselint.checks.mixed_metaphors import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_bottleneck(): + """Basic smoke test for check_bottleneck.""" + check = misc.check_bottleneck + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The project produced a huge bottleneck.") + + +def test_misc(): + """Basic smoke test for check_misc.""" + check = misc.check_misc + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Writing tests is not rocket surgery.") diff --git a/tests/checks/test_mondegreens.py b/tests/checks/test_mondegreens.py new file mode 100644 index 000000000..abda93ea3 --- /dev/null +++ b/tests/checks/test_mondegreens.py @@ -0,0 +1,12 @@ +"""Tests for mondegreens.misc check.""" + +from proselint.checks.mondegreens.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for mondegreens.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "... and laid him on the green.") + assert_fail(check, "..and Lady Mondegreen.") diff --git a/tests/checks/test_needless_variants.py b/tests/checks/test_needless_variants.py new file mode 100644 index 000000000..cc60b9901 --- /dev/null +++ b/tests/checks/test_needless_variants.py @@ -0,0 +1,18 @@ +"""Tests for needless_variants.misc check.""" +from proselint.checks.needless_variants import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def check(text: str) -> list: + # combined test + result = [] + result.extend(misc.check_1(text)) + result.extend(misc.check_2(text)) + return result + + +def test(): + """Basic smoke test for needless_variants.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was an extensible telescope.") diff --git a/tests/checks/test_nonwords.py b/tests/checks/test_nonwords.py new file mode 100644 index 000000000..4a501d275 --- /dev/null +++ b/tests/checks/test_nonwords.py @@ -0,0 +1,11 @@ +"""Tests for nonwords.misc check.""" + +from proselint.checks.nonwords.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for nonwords.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The test was good irregardless.") diff --git a/tests/checks/test_oxymorons.py b/tests/checks/test_oxymorons.py new file mode 100644 index 000000000..a424d33ae --- /dev/null +++ b/tests/checks/test_oxymorons.py @@ -0,0 +1,11 @@ +"""Tests for oxymorons.misc check.""" + +from proselint.checks.oxymorons.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for oxymorons.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "He needed an exact estimate.") diff --git a/tests/checks/test_psychology.py b/tests/checks/test_psychology.py new file mode 100644 index 000000000..d13c1b7e5 --- /dev/null +++ b/tests/checks/test_psychology.py @@ -0,0 +1,26 @@ +"""Tests for psychology.misc check.""" + +from proselint.checks.psychology import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_lie_detector_test(): + """Basic smoke test for psychology.misc.check_lie_detector_test.""" + check = misc.check_lie_detector_test + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The defendant took a lie detector test.") + + +def test_p_equals_zero(): + """Basic smoke test for psychology.misc.check_p_equals_zero.""" + check = misc.check_p_equals_zero + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The effect was highly signficant at p = 0.00.") + + +def test_mental_telepathy(): + """Basic smoke test for psychology.misc.check_mental_telepathy.""" + check = misc.check_mental_telepathy + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I've been practicing mental telepathy.") diff --git a/tests/checks/test_punctuation_spacing.py b/tests/checks/test_punctuation_spacing.py new file mode 100644 index 000000000..fc2c21c7c --- /dev/null +++ b/tests/checks/test_punctuation_spacing.py @@ -0,0 +1,23 @@ +"""Tests for punctuation_spacing.misc check.""" + +from proselint.checks.punctuation_spacing.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_end_punctuation(): + """Tests end punctuation spacing.""" + # misc.check_end_punctuation_spacing + assert_pass(check, "Smoke phrase with nothing flagged!") + assert_fail(check, "flagged! ") + assert_pass(check, "flagged? ") + assert_fail(check, "flagged? ") + + +def test_general_punctuation(): + """Tests general puncutation spacing.""" + # misc.general_spacing_check + assert_pass(check, "The quick brown fox jumps; over the lazy dog!") + assert_fail(check, "The quick brown fox jumps; over the lazy dog!") + assert_pass(check, "The quick brown fox jumps:over the lazy dog!") + assert_pass(check, "The quick brown fox jumps :over the lazy dog!") diff --git a/tests/checks/test_redundancy_misc.py b/tests/checks/test_redundancy_misc.py new file mode 100644 index 000000000..9c305f747 --- /dev/null +++ b/tests/checks/test_redundancy_misc.py @@ -0,0 +1,41 @@ +"""Tests for redundancy.misc check.""" +from __future__ import annotations + +from proselint.checks.redundancy import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_misc(): + """Basic smoke test for redundancy.misc.check.""" + check = misc.check + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The table was rectangular in shape.") + + +def test_garner(): + """Basic smoke test for redundancy.misc.check_garner.""" + check = misc.check_garner + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was blatantly obvious what to do next.") + + +def test_nordquist(): + """Basic smoke test for redundancy.misc.check_norquist.""" + check = misc.check_nordquist + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Taking the package was absolutely essential.") + + +def test_atd(): + """Basic smoke test for redundancy.misc.check_norquist.""" + + def check(text: str) -> list: + # combined test + result = [] + result.extend(misc.check_atd_1(text)) + result.extend(misc.check_atd_2(text)) + return result + + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "He often repeated the old adage.") diff --git a/tests/checks/test_redundancy_ras_syndrome.py b/tests/checks/test_redundancy_ras_syndrome.py new file mode 100644 index 000000000..830739d4e --- /dev/null +++ b/tests/checks/test_redundancy_ras_syndrome.py @@ -0,0 +1,11 @@ +"""Tests for redundancy.ras_syndrome check.""" + +from proselint.checks.redundancy.ras_syndrome import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for redundancy.ras_syndrome.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Please enter your PIN number.") diff --git a/tests/checks/test_security_credit_card.py b/tests/checks/test_security_credit_card.py new file mode 100644 index 000000000..adad4ec39 --- /dev/null +++ b/tests/checks/test_security_credit_card.py @@ -0,0 +1,14 @@ +"""Tests for security.credit_card check.""" + +from proselint.checks.security.credit_card import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for security.credit_card. + + This makes use of a test MasterCard number. + """ + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "My credit card number is 5555555555554444.") diff --git a/tests/checks/test_security_password.py b/tests/checks/test_security_password.py new file mode 100644 index 000000000..2a091e020 --- /dev/null +++ b/tests/checks/test_security_password.py @@ -0,0 +1,12 @@ +"""Tests for security.password check.""" + +from proselint.checks.security.password import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for security.password.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The password is 123456.") + assert_fail(check, "My password is PASSWORD.") diff --git a/tests/checks/test_sexism.py b/tests/checks/test_sexism.py new file mode 100644 index 000000000..f32ea2967 --- /dev/null +++ b/tests/checks/test_sexism.py @@ -0,0 +1,23 @@ +"""Tests for sexism.misc check.""" + +from proselint.checks.sexism import misc +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_sexism(): + """Basic smoke test for sexism.misc.""" + check = misc.check_sexism + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The legal team had two lawyers and a lady lawyer.") + + +def test_preferred_form(): + """Basic smoke test for sexism.misc.""" + check = misc.check_preferred_form + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, "Hello Mr. Birdperson. You look good.") + assert_pass(check, "Hello Mr. birdperson. still looking good.") + assert_pass(check, "Oh Chairperson - happy face.") + assert_fail(check, "Oh chairperson - why so sad.") + assert_fail(check, "You get the mailperson.") diff --git a/tests/checks/test_skunked_terms.py b/tests/checks/test_skunked_terms.py new file mode 100644 index 000000000..fe35a732f --- /dev/null +++ b/tests/checks/test_skunked_terms.py @@ -0,0 +1,11 @@ +"""Tests for skunked_terms.misc check.""" + +from proselint.checks.skunked_terms.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for skunked_terms.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I gave an impassionate defence of the situation.") diff --git a/tests/checks/test_spelling_able_atable.py b/tests/checks/test_spelling_able_atable.py new file mode 100644 index 000000000..bdc4a1f3b --- /dev/null +++ b/tests/checks/test_spelling_able_atable.py @@ -0,0 +1,11 @@ +"""Tests for spelling.able_atable check.""" + +from proselint.checks.spelling.able_atable import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.able_atable.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "There was a demonstratable difference.") diff --git a/tests/checks/test_spelling_able_ible.py b/tests/checks/test_spelling_able_ible.py new file mode 100644 index 000000000..fcc17ae70 --- /dev/null +++ b/tests/checks/test_spelling_able_ible.py @@ -0,0 +1,11 @@ +"""Tests for spelling.able_ible check.""" + +from proselint.checks.spelling.able_ible import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.able_ible.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was a sensable decision.") diff --git a/tests/checks/test_spelling_ally_ly.py b/tests/checks/test_spelling_ally_ly.py new file mode 100644 index 000000000..11ea73e80 --- /dev/null +++ b/tests/checks/test_spelling_ally_ly.py @@ -0,0 +1,11 @@ +"""Tests for spelling.ally_ly check.""" + +from proselint.checks.spelling.ally_ly import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.ally_ly.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "She was accidently fired.") diff --git a/tests/checks/test_spelling_ance_ence.py b/tests/checks/test_spelling_ance_ence.py new file mode 100644 index 000000000..63a04aa0d --- /dev/null +++ b/tests/checks/test_spelling_ance_ence.py @@ -0,0 +1,11 @@ +"""Tests for spelling.ance_ence check.""" + +from proselint.checks.spelling.ance_ence import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.ance_ence.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The resistence was futile.") diff --git a/tests/checks/test_spelling_athletes.py b/tests/checks/test_spelling_athletes.py new file mode 100644 index 000000000..54c285a08 --- /dev/null +++ b/tests/checks/test_spelling_athletes.py @@ -0,0 +1,11 @@ +"""Tests for spelling.athletes check.""" + +from proselint.checks.spelling.athletes import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.athletes.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "One of the greats: Cal Ripkin.") diff --git a/tests/checks/test_spelling_ely_ly.py b/tests/checks/test_spelling_ely_ly.py new file mode 100644 index 000000000..7970f94ab --- /dev/null +++ b/tests/checks/test_spelling_ely_ly.py @@ -0,0 +1,11 @@ +"""Tests for spelling.ely_ly check.""" + +from proselint.checks.spelling.ely_ly import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.ely_ly.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "She was completly unprepared.") diff --git a/tests/checks/test_spelling_em_im_en_in.py b/tests/checks/test_spelling_em_im_en_in.py new file mode 100644 index 000000000..ff4d32e82 --- /dev/null +++ b/tests/checks/test_spelling_em_im_en_in.py @@ -0,0 +1,11 @@ +"""Tests for spelling.em_im_en_in check.""" + +from proselint.checks.spelling.em_im_en_in import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.em_im_en_in.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "We shall imbark on a voyage.") diff --git a/tests/checks/test_spelling_er_or.py b/tests/checks/test_spelling_er_or.py new file mode 100644 index 000000000..0b430b3b2 --- /dev/null +++ b/tests/checks/test_spelling_er_or.py @@ -0,0 +1,11 @@ +"""Tests for spelling.er_or check.""" + +from proselint.checks.spelling.er_or import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.er_or.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "She met with the invester.") diff --git a/tests/checks/test_spelling_in_un.py b/tests/checks/test_spelling_in_un.py new file mode 100644 index 000000000..a0427c13d --- /dev/null +++ b/tests/checks/test_spelling_in_un.py @@ -0,0 +1,11 @@ +"""Tests for spelling.in_un check.""" + +from proselint.checks.spelling.in_un import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.in_un.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The plan was unfeasible.") diff --git a/tests/checks/test_spelling_misc.py b/tests/checks/test_spelling_misc.py new file mode 100644 index 000000000..8a1504862 --- /dev/null +++ b/tests/checks/test_spelling_misc.py @@ -0,0 +1,11 @@ +"""Tests for spelling.misc check.""" + +from proselint.checks.spelling.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I like this alot.") diff --git a/tests/checks/test_spelling_ve_of.py b/tests/checks/test_spelling_ve_of.py new file mode 100644 index 000000000..deae094a5 --- /dev/null +++ b/tests/checks/test_spelling_ve_of.py @@ -0,0 +1,11 @@ +"""Tests for spelling.ve_of check.""" + +from proselint.checks.spelling.ve_of import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for spelling.ve_of.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "This could of been the third test.") diff --git a/tests/checks/test_terms_animal_adjectives.py b/tests/checks/test_terms_animal_adjectives.py new file mode 100644 index 000000000..961235704 --- /dev/null +++ b/tests/checks/test_terms_animal_adjectives.py @@ -0,0 +1,11 @@ +"""Tests for terms.animal_adjectives check.""" + +from proselint.checks.terms.animal_adjectives import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for terms.animal_adjectives.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It was some bird-like creature.") diff --git a/tests/checks/test_terms_denizen_labels.py b/tests/checks/test_terms_denizen_labels.py new file mode 100644 index 000000000..af3e99a5b --- /dev/null +++ b/tests/checks/test_terms_denizen_labels.py @@ -0,0 +1,11 @@ +"""Tests for terms.denizen_labels check.""" + +from proselint.checks.terms.denizen_labels import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for terms.denizen_labels.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "He was definitely a Hong Kongite.") diff --git a/tests/checks/test_terms_eponymous_adjectives.py b/tests/checks/test_terms_eponymous_adjectives.py new file mode 100644 index 000000000..d99692442 --- /dev/null +++ b/tests/checks/test_terms_eponymous_adjectives.py @@ -0,0 +1,11 @@ +"""Tests for terms.eponymous_adjectives check.""" + +from proselint.checks.terms.eponymous_adjectives import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for terms.eponymous_adjectives.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The writing wasn't Shakespearian.") diff --git a/tests/checks/test_terms_venery.py b/tests/checks/test_terms_venery.py new file mode 100644 index 000000000..a130b015c --- /dev/null +++ b/tests/checks/test_terms_venery.py @@ -0,0 +1,16 @@ +"""Tests for terms.venery check.""" + +from proselint.checks.terms.venery import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for terms.venery.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + + assert_pass(check, "There was a congregation of alligators.") + assert_fail(check, "There was a group of alligators.") + + assert_pass(check, "There was a wisdom of wombats.") + assert_fail(check, "There was a bunch of wombats.") diff --git a/tests/checks/test_typography_diacritical_marks.py b/tests/checks/test_typography_diacritical_marks.py new file mode 100644 index 000000000..a15b15a42 --- /dev/null +++ b/tests/checks/test_typography_diacritical_marks.py @@ -0,0 +1,11 @@ +"""Tests for typography.diacritical_marks check.""" + +from proselint.checks.typography.diacritical_marks import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for typography.diacritical_marks.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "He saw the performance by Beyonce.") diff --git a/tests/checks/test_typography_exclamation.py b/tests/checks/test_typography_exclamation.py new file mode 100644 index 000000000..fbf1ee6de --- /dev/null +++ b/tests/checks/test_typography_exclamation.py @@ -0,0 +1,25 @@ +"""Tests for typography.exclamation check.""" + +from proselint.checks.typography import exclamation +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_repeated_exclamations(): + """Basic smoke test. + + Test for typography.exclamation.check_repeated_exclamations. + """ + check = exclamation.check_repeated_exclamations + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I'm really excited!!") + + +def test_exclamations_ppm(): + """Basic smoke test. + + Test for typography.exclamation.check_exclamations_ppm. + """ + check = exclamation.check_exclamations_ppm + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "I'm really excited! Really!") diff --git a/tests/checks/test_typography_symbols.py b/tests/checks/test_typography_symbols.py new file mode 100644 index 000000000..aecc53304 --- /dev/null +++ b/tests/checks/test_typography_symbols.py @@ -0,0 +1,62 @@ +"""Test Butterick's symbols.""" + +from proselint.checks.typography.symbols import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test_ellipsis(): + """Find ... in a string.""" + # check = symbols.check_ellipsis + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The long and winding road...") + + +def test_copyright(): + """Find a (c) or (C) in a string.""" + # check = symbols.check_copyright_symbol + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Show me the money! (C)") + assert_fail(check, "Show me the money! (c)") + + +def test_trademark(): + """Find a (TM) or (tm) in a string.""" + # check = symbols.check_trademark_symbol + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The Fresh Maker (TM)") + assert_fail(check, "The Fresh Maker (tm)") + + +def test_registered_trademark(): + """Find a (r) or (R) in a string.""" + # check = symbols.check_registered_trademark_symbol + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Just Do It (R)") + assert_fail(check, "Just Do It (r)") + + +def test_sentence_spacing(): + """Find a sentence followed by three or more spaces.""" + # check = symbols.check_sentence_spacing + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "This is a sentence. This is another.") + + +def test_multiplication(): + """Find an x between two digits.""" + # check = symbols.check_multiplication_symbol + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "It is obvious that 2 x 2 = 4.") + + +def test_curly_quotes(): + """Find "" quotes in a string.""" + # check = symbols.check_curly_quotes + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_pass(check, """This is “a sentence”. Look at it.""") + assert_fail(check, """This is "another sentence". How faulty.""") + assert_fail(check, """"This should produce an error", he said.""") + assert_pass(check, """“This should produce no error”, he said.""") + assert_fail(check, """Alas, "it should here too".""") + assert_pass(check, """A 'singular' should not, though.""") diff --git a/tests/checks/test_uncomparables.py b/tests/checks/test_uncomparables.py new file mode 100644 index 000000000..9ab9293ad --- /dev/null +++ b/tests/checks/test_uncomparables.py @@ -0,0 +1,40 @@ +"""Test uncomparables.misc""" +from __future__ import annotations + +from proselint.checks.uncomparables.misc import check_1 +from proselint.checks.uncomparables.misc import check_2 +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def check(text: str) -> list: + # combined test, + result = [] + result.extend(check_1(text)) + result.extend(check_2(text)) + return result + + +def test_misc(): + """Basic smoke test for uncomparables.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The item was more unique.") + + +def test_sample_phrases(): + """Find 'very unique'.""" + assert_fail(check, "This sentence is very unique.") + + +def test_spaces(): + """Handle spacing correctly.""" + assert_fail(check, "This sentence is very\nunique.") + assert_fail(check, "Kind of complete.") + assert_pass(check, "Every perfect instance.") + + +def test_constitutional(): + """Don't flag exceptions.""" + assert_pass(check, "A more perfect union.") + assert_pass(check, "A more possible future.") + assert_fail(check, "An increasingly possible future.") diff --git a/tests/checks/test_weasel_words_misc.py b/tests/checks/test_weasel_words_misc.py new file mode 100644 index 000000000..baeeeb73a --- /dev/null +++ b/tests/checks/test_weasel_words_misc.py @@ -0,0 +1,13 @@ +"""Tests for weasel_words.misc check.""" + +from proselint.checks.weasel_words.misc import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for weasel_words.misc.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "Some people say this is bad.") + assert_fail(check, "This is somewhat crazy.") + assert_fail(check, "It is said this is wrong.") diff --git a/tests/checks/test_weasel_words_very.py b/tests/checks/test_weasel_words_very.py new file mode 100644 index 000000000..761c6ccd2 --- /dev/null +++ b/tests/checks/test_weasel_words_very.py @@ -0,0 +1,11 @@ +"""Tests for weasel_words.very check.""" + +from proselint.checks.weasel_words.very import check +from tests.conftest import assert_fail +from tests.conftest import assert_pass + + +def test(): + """Basic smoke test for weasel_words.very.""" + assert_pass(check, "Smoke phrase with nothing flagged.") + assert_fail(check, "The book was very interesting.") diff --git a/tests/test_gmeu/test_a_A.py b/tests/checks_gmeu/test_a_A.py similarity index 80% rename from tests/test_gmeu/test_a_A.py rename to tests/checks_gmeu/test_a_A.py index 1d6fae55e..9377c964b 100644 --- a/tests/test_gmeu/test_a_A.py +++ b/tests/checks_gmeu/test_a_A.py @@ -2,24 +2,15 @@ """Test GMEU entry 'a', part A.""" +from tests.check import assert_error +from tests.conftest import assert_fail -from proselint.tools import assert_error -from tests.check import Check +def check(text: str) -> list: + return [assert_error, text, "misc.a_vs_an"] -class chk: - def check(self, text): - return assert_error, text, "misc.a_vs_an" - - -class TestCheck(Check): - """The test class for GMEU entry A - using the correct determiner.""" - - @property - def this_check(self): - return chk - +def test_misc_a_vs_an(): sentences_with_errors = [ "Are you an Yankee fan?", "Coffee tastes less sweet in an white mug.", @@ -51,6 +42,6 @@ def this_check(self): "She laughed aloud, an hysterical sort of giggled, quickly stifled", ] - def test_smoke(self): - for sentence in self.sentences_with_errors: - assert not self.passes(sentence) + for sentence in sentences_with_errors: + assert_fail(check, sentence) # fixme - there is no check for that yet + # assert_pass(check, "Smoke phrase with nothing flagged.") diff --git a/tests/checks_gmeu/test_a_B.py b/tests/checks_gmeu/test_a_B.py new file mode 100644 index 000000000..9d21011ef --- /dev/null +++ b/tests/checks_gmeu/test_a_B.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +"""Test GMEU entry 'a', part B.""" + +from tests.check import assert_error +from tests.conftest import assert_fail + + +def check(text: str) -> list: + return assert_error, text, "misc.a_distributive" + + +def test_misc_a_vs_an_p2(): + sentences = [ + "An apple per day keeps the doctor away.", + "I sleep eight hours per night.", + "Their policy allows one golf cart a couple.", + "The company donated five books a student.", + "Our a-unit cost is less than $1000.", + "The $50-a-parent fee seems unreasonably high.", + ] + for sentence in sentences: + assert_fail(check, sentence) # fixme + + # assert_pass(check, "Smoke phrase with nothing flagged.") diff --git a/tests/test_gmeu/test_a_C.py b/tests/checks_gmeu/test_a_C.py similarity index 100% rename from tests/test_gmeu/test_a_C.py rename to tests/checks_gmeu/test_a_C.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..9c9f0b417 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,44 @@ +from typing import Callable +from typing import Union + +from proselint.tools import lint + + +def check_pass(check: Callable, texts: Union[list, str]) -> bool: + """Check if the test runs cleanly on the given text.""" + if isinstance(texts, str): + texts = [texts] + return not any(len(check(text)) > 0 for text in texts) + + +def assert_pass(check: Callable, texts: Union[list, str]) -> None: + """Check if the test runs cleanly on the given text.""" + if isinstance(texts, str): + texts = [texts] + for _text in texts: + assert check(_text) == [] + + +def assert_fail(check: Callable, texts: Union[list, str]) -> None: + """Check if the test runs triggers on the given text.""" + if isinstance(texts, str): + texts = [texts] + for _text in texts: + assert check(_text) != [] + + +def check_in_lint_result(check: str, text: str, n: int = 1): + """Assert that text has n errors of type check.""" + assert check in [error[0] for error in lint(text)] + # todo: n not checked, should not assert + + +def print_invoke_return(result) -> None: + print(f"result::{result}") + + print(f"exit::{result.exit_code}") + print(f"retval::{result.return_value}") + + print(f"output::{result.output}") + print(f"stdout::{result.stdout}") + print(f" _bytes::{result.stdout_bytes}") diff --git a/tests/illegal_chars.txt b/tests/illegal_chars.txt new file mode 100644 index 000000000..91c770f27 --- /dev/null +++ b/tests/illegal_chars.txt @@ -0,0 +1,3 @@ +This file contains an illegal character on the next line: +don� +Note: don't rename file to 'test_xyz' otherwise pytest will raise diff --git a/tests/invalid-chars.txt b/tests/invalid-chars.txt index 895a90eab..543d83bdd 100644 --- a/tests/invalid-chars.txt +++ b/tests/invalid-chars.txt @@ -1,2 +1,3 @@ -This file contains an illegal character on the next line... -don� +This file contains an invalid character on the next line: +don� +Note: don't rename file to 'test_xyz' otherwise pytest will raise diff --git a/tests/test-proselintrc.json b/tests/test-proselintrc.json index ea295608f..362767f9a 100644 --- a/tests/test-proselintrc.json +++ b/tests/test-proselintrc.json @@ -1,5 +1,7 @@ { "max_errors": 1000, + "parallelize": true, + "output_format": null, "checks": { "airlinese.misc" : true, "annotations.misc" : true, @@ -18,7 +20,7 @@ "hyperbole.misc" : true, "jargon.misc" : true, "lexical_illusions.misc" : true, - "lgbtq.offensive_terms" : true, + "lgbtq.offensive_terms" : true, "lgbtq.terms" : true, "links.broken" : false, "malapropisms.misc" : true, @@ -33,12 +35,12 @@ "misc.currency" : true, "misc.debased" : true, "misc.false_plurals" : true, + "misc.greylist" : false, "misc.illogic" : true, "misc.inferior_superior" : true, "misc.institution_name" : true, "misc.latin" : true, "misc.many_a" : true, - "misc.metaconcepts" : true, "misc.metadiscourse" : true, "misc.narcissism" : true, "misc.not_guilty" : true, @@ -58,6 +60,7 @@ "nonwords.misc" : true, "oxymorons.misc" : true, "psychology.misc" : true, + "punctuation_spacing.misc" : true, "redundancy.misc" : true, "redundancy.ras_syndrome" : true, "skunked_terms.misc" : true, diff --git a/tests/test_annotations.py b/tests/test_annotations.py deleted file mode 100644 index 7687cab4e..000000000 --- a/tests/test_annotations.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for annotations.misc check.""" - -from proselint.checks.annotations import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for annotations.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for annotations.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""Add it to the to do list.""") - assert not self.passes("""Add it to the TODO list.""") diff --git a/tests/test_archaism.py b/tests/test_archaism.py deleted file mode 100644 index 6cbe34955..000000000 --- a/tests/test_archaism.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for archaism.misc check.""" - -from proselint.checks.archaism import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for archaism.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for archaism.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""I want to sleep, and maybe dream.""") - assert not self.passes("""I want to sleep, perchance to dream.""") diff --git a/tests/test_butterick_symbols.py b/tests/test_butterick_symbols.py deleted file mode 100644 index e4397b28e..000000000 --- a/tests/test_butterick_symbols.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Test Butterick's symbols.""" - -from proselint.checks.typography import symbols as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for typography.symbols.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_ellipsis(self): - """Find ... in a string.""" - assert chk.check_ellipsis("""The long and winding road...""") - - def test_copyright(self): - """Find a (c) or (C) in a string.""" - assert chk.check_copyright_symbol("""Show me the money! (C)""") - assert chk.check_copyright_symbol("""Show me the money! (c)""") - - def test_trademark(self): - """Find a (TM) or (tm) in a string.""" - assert chk.check_trademark_symbol("""The Fresh Maker (TM)""") - assert chk.check_trademark_symbol("""The Fresh Maker (tm)""") - - def test_registered_trademark(self): - """Find a (r) or (R) in a string.""" - assert chk.check_registered_trademark_symbol("""Just Do It (R)""") - assert chk.check_registered_trademark_symbol("""Just Do It (r)""") - - def test_sentence_spacing(self): - """Find a sentence followed by three or more spaces.""" - assert chk.check_sentence_spacing( - """This is a sentence. This is another.""") - - def test_multiplication(self): - """Find an x between two digits.""" - assert chk.check_multiplication_symbol( - """It is obvious that 2 x 2 = 4.""") - - def test_curly_quotes(self): # FIXME - """Find "" quotes in a string.""" - pass diff --git a/tests/test_cache_clear.py b/tests/test_cache_clear.py new file mode 100644 index 000000000..91521d26f --- /dev/null +++ b/tests/test_cache_clear.py @@ -0,0 +1,25 @@ +"""Tests for the clear cache operation from proselint.command_line.""" + +from unittest import mock + +from proselint import memoizer +from proselint.config_paths import cache_user_path + + +@mock.patch("shutil.rmtree") +def test_rm_cache(mock_rmtree): + """Correct directory is removed.""" + memoizer.cache.clear() + mock_rmtree.assert_called_with(cache_user_path) + + +@mock.patch("shutil.rmtree", side_effect=PermissionError) +def test_no_permission(mock_rmtree): + """Ignore if unable to delete.""" + memoizer.cache.clear() + + +@mock.patch("shutil.rmtree", side_effect=OSError) +def test_on_oserror(mock_rmtree): + """Ignore if general OSError.""" + memoizer.cache.clear() diff --git a/tests/test_cache_function.py b/tests/test_cache_function.py new file mode 100644 index 000000000..de26b2fa8 --- /dev/null +++ b/tests/test_cache_function.py @@ -0,0 +1,52 @@ +from timeit import timeit + +import proselint +from proselint import memoizer + + +def test_speed(): + """compare uncached vs cached reads""" + repetitions = 2 + + demo_path = proselint.path / "demo.md" + _code = "errors = proselint.tools.lint(_text)" + _setup = "import proselint" + with demo_path.open(encoding="utf-8", errors="replace") as demo_fh: + _text = demo_fh.read() + + # make sure it works + memoizer.cache.clear() + errors = proselint.tools.lint(_text) + assert len(errors) > 0 + + # without cache + _dur1 = 0.0 + for _ in range(repetitions): + memoizer.cache.clear() + _dur1 += timeit(_code, setup=_setup, globals=locals(), number=1) + + # with cache + _dur2 = 0.0 + for _ in range(repetitions): + _dur2 += timeit(_code, setup=_setup, globals=locals(), number=1) + + # without cache, confirmation + _dur3 = 0.0 + for _ in range(repetitions): + memoizer.cache.clear() + _dur3 += timeit(_code, setup=_setup, globals=locals(), number=1) + + assert _dur2 < 0.7 * _dur1 + assert _dur2 < 0.7 * _dur3 + + +def test_consistency(): + """compare result-count""" + demo_path = proselint.path / "demo.md" + demo_fh = demo_path.open(encoding="utf-8", errors="replace") + demo_str = demo_fh.read() + memoizer.cache.clear() + errors_a = proselint.tools.lint(demo_str) + errors_b = proselint.tools.lint(demo_str) + + assert len(errors_a) == len(errors_b) diff --git a/tests/test_clear_cache.py b/tests/test_clear_cache.py deleted file mode 100644 index 3d597a349..000000000 --- a/tests/test_clear_cache.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Tests for the clear cache operation from proselint.command_line.""" - -import os -import unittest - -from proselint import command_line as cl - -try: - from unittest import mock -except ImportError: - # Py2.x - from unittest import mock - -try: - from builtins import PermissionError -except ImportError: - - class PermissionError(OSError): - """Introduced in Py3.3, emulate for earlier versions.""" - - def __init__(self, *args, **kwargs): - """Constructor.""" - OSError.__init__(self, *args, **kwargs) - -try: - from builtins import FileNotFoundError -except ImportError: - - class FileNotFoundError(OSError): - """Introduced in Py3.3, emulate for earlier versions.""" - - def __init__(self, *args, **kwargs): - """Constructor.""" - OSError.__init__(self, *args, **kwargs) - -try: - from builtins import IsADirectoryError -except ImportError: - - class IsADirectoryError(OSError): - """Introduced in Py3.3, emulate for earlier versions.""" - - def __init__(self, *args, **kwargs): - """Constructor.""" - OSError.__init__(self, *args, **kwargs) - - -class Test__delete_compiled_python_files(unittest.TestCase): - """proselint.command_line._delete_compiled_python_files().""" - - def setUp(self): - """init common data.""" - self.base_dir = '.' - self.python_file = 'a.py' - self.pyc_file = 'a.pyc' - self.dot_pyc = '.pyc' - - self.files = [ - (self.base_dir, ('dummy',), (self.pyc_file, - self.python_file, - self.dot_pyc)) - ] - - self.pyc_file_path = os.path.join(self.base_dir, self.pyc_file) - self.python_file_path = os.path.join(self.base_dir, self.python_file) - self.dot_pyc_path = os.path.join(self.base_dir, self.python_file) - - @mock.patch('os.walk') - @mock.patch('os.remove') - def test_delete_pyc_file(self, mock_remove, mock_walk): - """Ensure 'pyc' files are removed.""" - mock_walk.return_value = self.files - - cl._delete_compiled_python_files() - mock_remove.assert_called_with(self.pyc_file_path) - - @mock.patch('os.walk') - @mock.patch('os.remove') - def test_files_not_deleted(self, mock_remove, mock_walk): - """Ensure non 'pyc' files are not removed.""" - mock_walk.return_value = self.files - cl._delete_compiled_python_files() - - with self.assertRaises(AssertionError): - mock_remove.assert_called_with(self.python_file_path) - - with self.assertRaises(AssertionError): - mock_remove.assert_called_with(self.dot_pyc_path) - - @mock.patch('os.walk') - @mock.patch('os.remove', side_effect=PermissionError) - def test_no_permission(self, mock_remove, mock_walk): - """Ignore if unable to delete files.""" - mock_walk.return_value = self.files - cl._delete_compiled_python_files() - - @mock.patch('os.walk') - @mock.patch('os.remove', side_effect=OSError) - def test_on_oserror(self, mock_remove, mock_walk): - """Ignore if OSError.""" - mock_walk.return_value = self.files - cl._delete_compiled_python_files() - - @mock.patch('os.walk') - @mock.patch('os.remove', side_effect=FileNotFoundError) - def test_files_not_found(self, mock_remove, mock_walk): - """Ignore if file not found.""" - mock_walk.return_value = self.files - cl._delete_compiled_python_files() - - @mock.patch('os.walk') - @mock.patch('os.remove', side_effect=IsADirectoryError) - def test__remove_dir(self, mock_remove, mock_walk): - """Ignore if attempt to delete a directory.""" - mock_walk.return_value = self.files - cl._delete_compiled_python_files() - - -class Test__delete_cache(unittest.TestCase): - """proselint.command_line.__delete_cache().""" - - def setUp(self): - """Init common data.""" - self.cache_path = os.path.join("proselint", "cache") - - @mock.patch('shutil.rmtree') - def test_rm_cache(self, mock_rmtree): - """Correct directory is removed.""" - cl._delete_cache() - mock_rmtree.assert_called_with(self.cache_path) - - @mock.patch('shutil.rmtree', side_effect=PermissionError) - def test_no_permission(self, mock_rmtree): - """Ignore if unable to delete.""" - cl._delete_cache() - - @mock.patch('shutil.rmtree', side_effect=OSError) - def test_on_oserror(self, mock_rmtree): - """Ignore if general OSError.""" - cl._delete_cache() diff --git a/tests/test_cliches.py b/tests/test_cliches.py deleted file mode 100644 index 68db664e5..000000000 --- a/tests/test_cliches.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Test the Cliches.misc module.""" - -from proselint.checks.cliches import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for cliches.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def setUp(self): - """Create test sentences.""" - self.l_garner = """Worse than a fate worse than death.""" - self.l_write_good = """He's a chip off the old block.""" - self.l_gnu_diction = """It's a matter of concern.""" - - def test_cliches_garner_basic(self): - """Basic checks on check_cliches_garner.""" - assert chk.check_cliches_garner("""No cliches here.""") == [] - # use one of the example cliches to verify basic functionality - assert chk.check_cliches_garner(self.l_garner) != [] - assert "cliches.garner" in chk.check_cliches_garner(self.l_garner)[0] - - def test_cliches_write_good_basic(self): - """Basic checks on check_cliches_write_good.""" - assert chk.check_cliches_write_good("""No cliches here.""") == [] - # use one of the example cliches to verify basic functionality - assert chk.check_cliches_write_good(self.l_write_good) != [] - assert "cliches.write_good" in chk.check_cliches_write_good( - self.l_write_good)[0] - - def test_cliches_gnu_diction_basic(self): - """Basic check on check_cliches_gnu_diction.""" - assert chk.check_cliches_gnu_diction("""No cliches here.""") == [] - # use one of the example cliches to verify basic functionality - assert chk.check_cliches_gnu_diction(self.l_gnu_diction) != [] - assert "cliches.gnu_diction" in chk.check_cliches_gnu_diction( - self.l_gnu_diction)[0] diff --git a/tests/test_config_default.py b/tests/test_config_default.py new file mode 100644 index 000000000..6e1725158 --- /dev/null +++ b/tests/test_config_default.py @@ -0,0 +1,74 @@ +import importlib +import os +import re +from pathlib import Path + +import proselint +from proselint.tools import lint +from proselint.tools import load_options + + +def test_config_flag_ignore_unavailable(): + """program should not crash with faulty config -> just inform""" + cfg = load_options() + cfg["checks"]["misc.santa_clause_is_real"] = True + lint("Smoke phrase with nothing flagged.", config=cfg) + + +def test_config_default_flag_for_check_missing(): + """test default-config for completeness + Note: if triggered the default config has a flag that points to a missing check + -> remove it! + """ + checks = [] + check_names = load_options()["checks"].keys() + + for check_name in check_names: + module = importlib.import_module("." + check_name, "proselint.checks") + checks += [ + getattr(module, d) for d in dir(module) if re.match("check", d) + ] + + +def is_check(file_path: Path) -> bool: + """Check whether a file contains a check.""" + if file_path.suffix != ".py": + return False + if file_path.name == "__init__.py": + return False + if "inprogress" in file_path.as_posix(): + return False + return True + + +def test_config_default_check_for_flag_missing(): + """test default-config for completeness + Note: if triggered the flag / key is missing in default config + -> add it! + """ + cfg = load_options() + checks_path = (proselint.path / "checks").absolute() + listing = os.walk(checks_path) + # todo: this should recurse in sub-dirs + + for _root, _, _files in listing: + root_path = Path(_root) + for _file in _files: + file_path = root_path / _file + if is_check(file_path): + if not (file_path.parent / "__init__.py").exists(): + raise FileNotFoundError( + "Check-Directory is missing '__init__.py'" + ) + + flag_name = ( + str( + file_path.absolute() + .with_suffix("") + .relative_to(checks_path), + ) + .replace("\\", ".") + .replace("/", ".") + ) + + assert flag_name in cfg["checks"] diff --git a/tests/test_config_flag.py b/tests/test_config_flag.py index 3aa459e21..1ccea1957 100644 --- a/tests/test_config_flag.py +++ b/tests/test_config_flag.py @@ -1,65 +1,83 @@ """Test user option overrides using --config and load_options""" import json -import os from pathlib import Path -from unittest import TestCase from unittest.mock import patch from click.testing import CliRunner from proselint.command_line import proselint -from proselint.config import default -from proselint.tools import deepmerge_dicts, load_options +from proselint.config_base import proselint_base +from proselint.tools import _deepmerge_dicts # noqa: PLC2701 +from proselint.tools import load_options +from tests.conftest import print_invoke_return -runner = CliRunner() - -CONFIG_FILE = str(Path(__file__, "../test-proselintrc.json").resolve()) -FLAG = f"--config '{CONFIG_FILE}'" +CONFIG_FILE = Path(__file__, "../test-proselintrc.json").resolve() +FLAG = ["--config", str(CONFIG_FILE)] def test_deepmerge_dicts(): """Test deepmerge_dicts""" - d1 = {'a': 1, 'b': {'c': 2, 'd': 3}} - d2 = {'a': 2, 'b': {'c': 3, 'e': 4}} - assert deepmerge_dicts(d1, d2) == {'a': 2, 'b': {'c': 3, 'd': 3, 'e': 4}} + d1 = {"a": 1, "b": {"c": 2, "d": 3}} + d2 = {"a": 2, "b": {"c": 3, "e": 4}} + assert _deepmerge_dicts(d1, d2) == {"a": 2, "b": {"c": 3, "d": 3, "e": 4}} -@patch("os.path.isfile") -def test_load_options_function(isfile): - """Test load_options by specifying a user options path""" +def test_load_options_function_default(): + assert load_options()["checks"]["uncomparables.misc"] + +def test_load_options_function(): + """Test load_options by specifying a user options path""" isfile.side_effect = CONFIG_FILE.__eq__ - overrides = load_options(CONFIG_FILE, default) - assert load_options(conf_default=default)["checks"]["uncomparables.misc"] + overrides = load_options(CONFIG_FILE) + default = load_options() + assert default == proselint_base + assert default["checks"]["uncomparables.misc"] assert not overrides["checks"]["uncomparables.misc"] - isfile.side_effect = os.path.join(os.getcwd(), ".proselintrc.json").__eq__ - - TestCase().assertRaises(FileNotFoundError, load_options) + isfile.side_effect = (Path.cwd() / ".proselintrc.json").__eq__ -def test_config_flag(): +def test_config_flag_demo(): """Test the --config CLI argument""" - output = runner.invoke(proselint, "--demo") - assert "uncomparables.misc" in output.stdout + result = CliRunner().invoke(proselint, ["--demo", "-v"]) + print_invoke_return(result) + assert "uncomparables.misc" in result.stdout - output = runner.invoke(proselint, f"--demo {FLAG}") - assert "uncomparables.misc" not in output.stdout - assert "FileNotFoundError" != output.exc_info[0].__name__ - output = runner.invoke(proselint, "--demo --config non_existent_file") - assert output.exit_code == 1 - assert "FileNotFoundError" == output.exc_info[0].__name__ +def test_config_flag_config(): + result = CliRunner().invoke(proselint, ["--demo", "-v", *FLAG]) + print_invoke_return(result) + assert "uncomparables.misc" not in result.stdout - output = runner.invoke(proselint, "non_existent_file") - assert output.exit_code == 2 +def test_config_flag_config_nonexist(): + result = CliRunner().invoke( + proselint, ["--demo", "--config", "non_existent_file"] + ) + assert result.exit_code != 0 + assert result.exc_info[0].__name__ == "SystemExit" + # was FileNotFoundError, but click is now doing pre-checks -def test_dump_config(): + +def test_config_flag_data_nonexist(): + result = CliRunner().invoke(proselint, "non_existent_file") + assert result.exit_code != 0 + assert result.exc_info[0].__name__ == "SystemExit" + # was FileNotFoundError, but click is now doing pre-checks + + +def test_dump_config_default(): """Test --dump-default-config and --dump-config""" - output = runner.invoke(proselint, "--dump-default-config") - assert json.loads(output.stdout) == default + result = CliRunner().invoke(proselint, "--dump-default-config") + assert json.loads(result.stdout) == proselint_base - output = runner.invoke(proselint, f"--dump-config {FLAG}") - assert json.loads(output.stdout) == json.load(open(CONFIG_FILE)) + +def test_dump_config(): + """this test is not optimal + if triggered, the input-cfg was extended with the default-config + -> add missing flags to input-cfg! + """ + output = CliRunner().invoke(proselint, ["--dump-config", *FLAG]) + assert json.loads(output.stdout) == json.load(CONFIG_FILE.open()) diff --git a/tests/test_consistency_check.py b/tests/test_consistency_check.py index 27a2eed52..8b32570d1 100644 --- a/tests/test_consistency_check.py +++ b/tests/test_consistency_check.py @@ -1,31 +1,13 @@ """Test the consistency_check function from the tools.py module.""" -from proselint.tools import consistency_check as chk +from proselint.checks import consistency_check -from .check import Check - -class TestCheck(Check): - """The test class for tools.consistency_check.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def setUp(self): - """Create some test fixtures.""" - self.L = [['colour', 'color']] - self.err = 'error message' - self.msg = 'inconsistent form of {} vs {}' - - def test_smoke(self): - """Basic smoke test for consistency_check.""" - assert chk( - "Painting colour on color", self.L, self.err, self.msg) != [] - assert chk( - "Painting colour on colour", self.L, self.err, self.msg) == [] - assert chk( - "Painting color on color", self.L, self.err, self.msg) == [] +def test_consistency_check(): + """Basic smoke test for consistency_check.""" + items = [["colour", "color"]] + err = "error message" + msg = "inconsistent form of {} vs {}" + assert consistency_check("Painting colour on color", items, err, msg) != [] + assert consistency_check("Painting colour on colour", items, err, msg) == [] + assert consistency_check("Painting color on color", items, err, msg) == [] diff --git a/tests/test_consistency_spacing.py b/tests/test_consistency_spacing.py deleted file mode 100644 index 6d53ed4b1..000000000 --- a/tests/test_consistency_spacing.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Tests for consistency.spacing check.""" - -from proselint.checks.consistency import spacing as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for consistency.spacing.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for consistency.spacing.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes( - """This is good. Only one space each time. Every time.""") - assert not self.passes("""This is bad. Not consistent. At all.""") diff --git a/tests/test_consistency_spelling.py b/tests/test_consistency_spelling.py deleted file mode 100644 index 9fa0487a1..000000000 --- a/tests/test_consistency_spelling.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Tests for consistency.spelling check.""" - -from proselint.checks.consistency import spelling as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for consistency.spelling.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for consistency.spelling.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""The centre for the arts is the art centre.""") - assert self.passes("""The center for the arts is the art center.""") - assert not self.passes("""The centre of the arts is the art center.""") diff --git a/tests/test_corporate_speak.py b/tests/test_corporate_speak.py deleted file mode 100644 index 22e2b9d15..000000000 --- a/tests/test_corporate_speak.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for corporate_speak.misc check.""" - -from proselint.checks.corporate_speak import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for corporate_speak.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for corporate_speak.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""We will discuss it later.""") - assert not self.passes("""We will circle back around to it.""") diff --git a/tests/test_cursing_filth.py b/tests/test_cursing_filth.py deleted file mode 100644 index dde014e21..000000000 --- a/tests/test_cursing_filth.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for cursing.filth check.""" - -from proselint.checks.cursing import filth as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for cursing.filth.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for cursing.filth.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""Bad shit in this phrase.""") diff --git a/tests/test_cursing_nfl.py b/tests/test_cursing_nfl.py deleted file mode 100644 index 3c4757d72..000000000 --- a/tests/test_cursing_nfl.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for cursing.nfl check.""" - -from proselint.checks.cursing import nfl as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for cursing.nfl.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for cursing.nfl.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The QB is named ball licker.""") diff --git a/tests/test_cursing_nword.py b/tests/test_cursing_nword.py deleted file mode 100644 index 06c75bfd8..000000000 --- a/tests/test_cursing_nword.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for cursing.nword check.""" - -from proselint.checks.cursing import nword as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for cursing.nword.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for cursing.nword.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The n-word.""") diff --git a/tests/test_dates_times_am_pm.py b/tests/test_dates_times_am_pm.py deleted file mode 100644 index 9e32a37f4..000000000 --- a/tests/test_dates_times_am_pm.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Tests for dates_times.am_pm check.""" - -from proselint.checks.dates_times import am_pm as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for dates_times.am_pm.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_check_lowercase_periods(self): - """Basic smoke test. - - This is for the function - dates_times.am_pm.check_lowercase_periods. - - """ - assert chk.check_lowercase_periods( - "Basic smoke phrase without issues.") == [] - assert chk.check_lowercase_periods( - "It happened at 7 a.m.") == [] - assert chk.check_lowercase_periods( - "It happened at 7 am.") != [] - assert chk.check_lowercase_periods( - "On Wed, Sep 21, 2016 at 11:42 AM -0400, X wrote:") == [] - - def test_smoke_check_spacing(self): - """Basic smoke test. - - This is for the function - dates_times.am_pm.check_spacing. - - """ - assert chk.check_spacing( - "Basic smoke phrase without issues.") == [] - assert chk.check_spacing( - "It happened at 7 a.m.") == [] - assert chk.check_spacing( - "It happened at 7a.m.") != [] - - def test_smoke_check_midnight_noon(self): - """Basic smoke test. - - This for the function - dates_times.am_pm.midnight_noon. - - """ - assert chk.check_midnight_noon( - "Basic smoke phrase without issues.") == [] - assert chk.check_midnight_noon( - "It happened at noon.") == [] - assert chk.check_midnight_noon( - "It happened at 12 a.m.") != [] - - def test_smoke_check_redundancy(self): - """Basic smoke test. - - This for the function - dates_times.am_pm.check_redundancy. - - """ - assert len(chk.check_redundancy( - "Basic smoke phrase without issues.")) == 0 - assert len(chk.check_redundancy( - "It happened at 7 a.m.")) == 0 - assert len(chk.check_redundancy( - "It happened at 7a.m. in the morning.")) == 1 diff --git a/tests/test_dates_times_dates.py b/tests/test_dates_times_dates.py deleted file mode 100644 index 7612d71fe..000000000 --- a/tests/test_dates_times_dates.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests for dates_times.dates check.""" - -from proselint.checks.dates_times import dates as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for dates_times.dates.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_check_decade_apostrophes_short(self): - """Basic smoke test. - - This for the function - dates_times.dates.check_decade_apostrophes_short. - - """ - assert chk.check_decade_apostrophes_short( - "Basic smoke phrase without issues.") == [] - assert chk.check_decade_apostrophes_short( - "It happened in the 90s.") == [] - assert chk.check_decade_apostrophes_short( - "It happened in the 90's.") != [] - - def test_smoke_check_decade_apostrophes_long(self): - """Basic smoke test. - - This is for the function - dates_times.dates.decade_apostrophes_long. - - """ - assert chk.check_decade_apostrophes_long( - "Basic smoke phrase without issues.") == [] - assert chk.check_decade_apostrophes_long( - "It happened in the 1980s.") == [] - assert chk.check_decade_apostrophes_long( - "It happened in the 1980's.") != [] - - def test_smoke_check_dash_and_from(self): - """Basic smoke test. - - This for the function - dates_times.dates.dash_and_from. - - """ - assert chk.check_dash_and_from( - "Basic smoke phrase without issues.") == [] - assert chk.check_dash_and_from( - "It happened from 2000 to 2005.") == [] - assert chk.check_dash_and_from( - "It happened from 2000-2005.") != [] - - def test_smoke_check_month_year_comma(self): - """Basic smoke test. - - This is for the function - dates_times.dates.check_month_year_comma. - - """ - assert chk.check_month_year_comma( - "Basic smoke phrase without issues.") == [] - assert chk.check_month_year_comma( - "It happened in August 2008.") == [] - assert chk.check_month_year_comma( - "It happened in August, 2008.") != [] - - def test_smoke_check_month_of_year(self): - """Basic smoke test. - - This is for the function - dates_times.dates.check_month_of_year. - - """ - assert chk.check_month_of_year( - "Basic smoke phrase without issues.") == [] - assert chk.check_month_of_year( - "It happened in August 2008.") == [] - assert chk.check_month_of_year( - "It happened in August of 2008.") != [] diff --git a/tests/test_existence_check.py b/tests/test_existence_check.py index 504f273fa..09e6d4ab4 100644 --- a/tests/test_existence_check.py +++ b/tests/test_existence_check.py @@ -1,64 +1,73 @@ """Test the existence_check function from the tools.py module.""" -from proselint.tools import existence_check as chk +from proselint.checks import Pd +from proselint.checks import existence_check -from .check import Check +def test_existence_check(): + """Basic smoke test to determine that existence_check is working.""" + items = ["abc"] + err = "error message" + msg = "it exists" + assert existence_check("abc is as easy as 123", items, err, msg) != [] + assert existence_check("easy breezy", items, err, msg) == [] + assert err in existence_check("abc is as easy as 123", items, err, msg)[0] + assert msg in existence_check("abc is as easy as 123", items, err, msg)[0] -class TestCheck(Check): - """The test class for tools.existence_check.""" - __test__ = True +def test_existence_check_multiple_matches(): + """Test that multiple matches are found correctly.""" + items = ["abc"] + err = "error message" + msg = "it exists" + assert ( + len(existence_check("abc and abc are as easy as 123", items, err, msg)) + == 2 + ) + assert ( + len( + existence_check( + "ABC and abc are as easy as 123", + items, + err, + msg, + ignore_case=True, + ), + ) + == 2 + ) + assert ( + len( + existence_check( + "ABC and abc are as easy as 123", + items, + err, + msg, + ignore_case=False, + ), + ) + == 1 + ) + assert existence_check("abcabc are easy as 123", items, err, msg) == [] - @property - def this_check(self): - """Boilerplate.""" - return chk - def setUp(self): - """setUp method creating some test fixtures.""" - self.L = ['abc'] - self.err = 'error message' - self.msg = 'it exists' - - def test_smoke(self): - """Basic smoke test to determine that existence_check is working.""" - assert chk( - """abc is as easy as 123""", self.L, self.err, self.msg) != [] - assert chk( - """easy breezy""", self.L, self.err, self.msg) == [] - assert self.err in chk( - """abc is as easy as 123""", self.L, self.err, self.msg)[0] - assert self.msg in chk( - """abc is as easy as 123""", self.L, self.err, self.msg)[0] - - def test_multiple_matches(self): - """Test that multiple matches are found correctly.""" - assert len( - chk("""abc and abc are as easy as 123""", - self.L, self.err, self.msg)) == 2 - assert len( - chk("""ABC and abc are as easy as 123""", - self.L, self.err, self.msg, ignore_case=True)) == 2 - assert len( - chk("""ABC and abc are as easy as 123""", - self.L, self.err, self.msg, ignore_case=False)) == 1 - assert chk( - """abcabc are easy as 123""", self.L, self.err, self.msg) == [] - - def test_string_types(self): - """Test that the function works with different string types.""" - assert chk('abc is easy as 123', self.L, self.err, self.msg) != [] - assert chk("abc is easy as 123", self.L, self.err, self.msg) != [] - assert chk(u'abc is easy as 123', self.L, self.err, self.msg) != [] - assert chk(u"abc is easy as 123", self.L, self.err, self.msg) != [] - - def test_exceptions(self): - """Test that existence_check does not report excluded phrases""" - regex = [r"\b(\w+)\b\s\1"] - no = ["should should"] - errs = chk("should should flag flag.", regex, "", "", - require_padding=False) - assert len(errs) == 2 - errs = chk("should should flag flag.", regex, "", "", exceptions=no, - require_padding=False) - assert len(errs) == 1 +def test_existence_check_exceptions(): + """Test that existence_check does not report excluded phrases""" + regex = [r"\b(\w+)\b\s\1"] + no = ["should should"] + errs = existence_check( + "should should flag flag.", + regex, + "", + "", + padding=Pd.disabled, + ) + assert len(errs) == 2 + errs = existence_check( + "should should flag flag.", + regex, + "", + "", + exceptions=no, + padding=Pd.disabled, + ) + assert len(errs) == 1 diff --git a/tests/test_exit_codes.py b/tests/test_exit_codes.py index 8f30d239b..871978d98 100644 --- a/tests/test_exit_codes.py +++ b/tests/test_exit_codes.py @@ -4,23 +4,14 @@ from proselint.command_line import proselint -from .check import Check +def test_exit_code_demo(): + """Ensure that linting the demo returns an exit code of 1.""" + result = CliRunner().invoke(proselint, "--demo") + assert result.exit_code >= 1 -class TestExitCodes(Check): - """Test class for CLI exit codes""" - __test__ = True - - def setUp(self): - self.runner = CliRunner() - - def test_exit_code_demo(self): - """Ensure that linting the demo returns an exit code of 1.""" - output = self.runner.invoke(proselint, "--demo") - assert output.exit_code == 1 - - def test_exit_code_version(self): - """Ensure that getting the version returns an exit code of 0.""" - output = self.runner.invoke(proselint, "--version") - assert output.exit_code == 0 +def test_exit_code_version(): + """Ensure that getting the version returns an exit code of 0.""" + result = CliRunner().invoke(proselint, "--version") + assert result.exit_code == 0 diff --git a/tests/test_garner_dates.py b/tests/test_garner_dates.py deleted file mode 100644 index 4a1f42bfb..000000000 --- a/tests/test_garner_dates.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Test garner.dates.""" - -from proselint.checks.dates_times import dates - -from .check import Check - - -class TestCheck(Check): - """Test class for garner.dates.""" - - __test__ = True - - def test_50s_hyphenation(self): - """Find unneeded hyphen in 50's.""" - text = """The 50's were swell.""" - errors = dates.check_decade_apostrophes_short(text) - assert len(errors) == 1 - - def test_50_Cent_hyphenation(self): - """Don't flag 50's when it refers to 50 Cent's manager.""" - text = """ - Dr. Dre suggested to 50's manager that he look into signing - Eminem to the G-Unit record label. - """ - errors = dates.check_decade_apostrophes_short(text) - assert len(errors) == 0 - - def test_dash_and_from(self): - """Test garner.check_dash_and_from.""" - text = """From 1999-2002, Sally served as chair of the committee.""" - errors = dates.check_dash_and_from(text) - print(errors) - assert len(errors) == 1 diff --git a/tests/test_gmeu/test_a_B.py b/tests/test_gmeu/test_a_B.py deleted file mode 100644 index 509056d13..000000000 --- a/tests/test_gmeu/test_a_B.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -"""Test GMEU entry 'a', part B.""" - - -from proselint.tools import assert_error - -from tests.check import Check - - -class chk: - def check(self, text): - return assert_error, text, "misc.a_distributive" - - -class TestCheck(Check): - """The test class for GMEU entry A - using a over per in the distributive - sense. - """ - - @property - def this_check(self): - return chk - - def test_smoke(self): - sentences = [ - "An apple per day keeps the doctor away.", - "I sleep eight hours per night.", - "Their policy allows one golf cart a couple.", - "The company donated five books a student.", - "Our a-unit cost is less than $1000.", - "The $50-a-parent fee seems unreasonably high." - ] - for sentence in sentences: - assert not self.passes(sentence) diff --git a/tests/test_hedging.py b/tests/test_hedging.py deleted file mode 100644 index 7826c36cb..000000000 --- a/tests/test_hedging.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for hedging.misc check.""" - -from proselint.checks.hedging import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for hedging.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for hedging.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""I would argue that this is a good test.""") diff --git a/tests/test_hyperbole.py b/tests/test_hyperbole.py deleted file mode 100644 index 77f61668f..000000000 --- a/tests/test_hyperbole.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for hyperbole.misc check.""" - -from proselint.checks.hyperbole import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for hyperbole.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for hyperbole.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""So exaggerated!!!""") diff --git a/tests/test_invalid_chars.py b/tests/test_invalid_chars.py index 803dedeb1..0f86997ae 100644 --- a/tests/test_invalid_chars.py +++ b/tests/test_invalid_chars.py @@ -5,22 +5,18 @@ from click.testing import CliRunner from proselint.command_line import proselint - -from .check import Check +from tests.conftest import print_invoke_return CHAR_FILE = Path(__file__, "../invalid-chars.txt").resolve() -class TestInvalidCharacters(Check): - """Test class for testing invalid characters on the CLI""" - - __test__ = True - - def test_invalid_characters(self): - """Ensure that invalid characters do not break proselint.""" - runner = CliRunner() +def test_invalid_characters(): + """Ensure that invalid characters do not break proselint.""" + runner = CliRunner() - output = runner.invoke(proselint, CHAR_FILE) + result = runner.invoke(proselint, CHAR_FILE.as_posix()) + print_invoke_return(result) - assert "UnicodeDecodeError" not in output.stdout - assert "FileNotFoundError" not in output.stdout + assert result.exit_code == 0 + assert "UnicodeDecodeError" not in result.stdout + assert "FileNotFoundError" not in result.stdout diff --git a/tests/test_jargon.py b/tests/test_jargon.py deleted file mode 100644 index 873dfaea9..000000000 --- a/tests/test_jargon.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for jargon.misc check.""" - -from proselint.checks.jargon import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for jargon.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for jargon.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""I agree it's in the affirmative.""") diff --git a/tests/test_leonard.py b/tests/test_leonard.py deleted file mode 100644 index 3395bbeb1..000000000 --- a/tests/test_leonard.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Test garner.dates.""" - -from proselint.checks.typography import exclamation - -from .check import Check - - -class TestCheck(Check): - """Test class for leonard.exclamation.""" - - __test__ = True - - def test_capitalization_and_no_exclamation(self): - """Don't throw error when phrase has capitalization.""" - text = """ - The QUICK BROWN fox juMPED over the lazy cat. - """ - errors = exclamation.check_repeated_exclamations(text) - assert len(errors) == 0 - - def test_exclamation(self): - """Test leonard.exclamation. with exclamation marks.""" - text = """Sally sells seashells and they were too expensive!!!!""" - errors = exclamation.check_repeated_exclamations(text) - print(errors) - assert len(errors) == 1 diff --git a/tests/test_lexical_illusions.py b/tests/test_lexical_illusions.py deleted file mode 100644 index ee1616f18..000000000 --- a/tests/test_lexical_illusions.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Tests for lexical_illusions.misc check.""" - -from proselint.checks.lexical_illusions import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for lexical_illusions.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for lexical_illusions.misc.""" - assert self.passes("Smoke phrase with nothing flagged.") - assert not self.passes("Paris in the the springtime.") - assert self.passes("And he's gone, gone with the breeze") - assert self.passes("You should know that that sentence wasn't wrong.") - assert self.passes("She had had dessert on the balcony.") - assert not self.passes("You should know that that that was wrong.") - assert self.passes("The practitioner's side") - assert self.passes("An antimatter particle") - assert self.passes("The theory") - assert self.passes("She had coffee at the Foo-bar bar.") diff --git a/tests/test_lgbtq_offensive_terms.py b/tests/test_lgbtq_offensive_terms.py deleted file mode 100644 index 9c5fe9a3d..000000000 --- a/tests/test_lgbtq_offensive_terms.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for lgbtq.terms check.""" - -from proselint.checks.lgbtq import offensive_terms as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for lgbtq.offensive_terms.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for lgbtq.offensive_terms.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""I once met a gay man.""") - assert not self.passes("""I once met a fag.""") diff --git a/tests/test_lgbtq_terms.py b/tests/test_lgbtq_terms.py deleted file mode 100644 index f8c2409ba..000000000 --- a/tests/test_lgbtq_terms.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Tests for lgbtq.offensive_terms check.""" - -from proselint.checks.lgbtq import terms as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for lgbtq.terms.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for lgbtq.terms.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""They were a gay couple.""") - assert not self.passes("""He was a homosexual man.""") - - def test_homosexual_term(self): - """Check that the term homosexual does not get caught.""" - assert self.passes("""Homosexual.""") - - def test_sexual_prefence(self): - """Check that sexual preference is flagged.""" - assert not self.passes("""My sexual preference is for women.""") diff --git a/tests/test_malaproprisms.py b/tests/test_malaproprisms.py deleted file mode 100644 index b6324feb4..000000000 --- a/tests/test_malaproprisms.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for malaproprisms.misc check.""" - -from proselint.checks.malapropisms import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for malaproprisms.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for malaproprisms.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""Found in the Infinitesimal Universe.""") diff --git a/tests/test_misc_apologizing.py b/tests/test_misc_apologizing.py deleted file mode 100644 index 99fc92819..000000000 --- a/tests/test_misc_apologizing.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.apologizing check.""" - -from proselint.checks.misc import apologizing as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.apologizing.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.apologizing.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""More research is needed.""") diff --git a/tests/test_misc_back_formations.py b/tests/test_misc_back_formations.py deleted file mode 100644 index 65e54eae3..000000000 --- a/tests/test_misc_back_formations.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.back_formations check.""" - -from proselint.checks.misc import back_formations as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.back_formations.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.back_formations.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It is an improprietous use.""") diff --git a/tests/test_misc_bureaucratese.py b/tests/test_misc_bureaucratese.py deleted file mode 100644 index 39ab91068..000000000 --- a/tests/test_misc_bureaucratese.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for misc.bureaucratese check.""" - -from proselint.checks.misc import bureaucratese as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.bureaucratese.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.bureaucratese.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """I hope the report meets with your approval.""") diff --git a/tests/test_misc_but.py b/tests/test_misc_but.py deleted file mode 100644 index 056e6734a..000000000 --- a/tests/test_misc_but.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Tests for misc.but check.""" - -from proselint.checks.misc import but as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.but.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.but.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""But I never start with the word "but".""") - assert self.passes("""I never start with the word "but", -but might use it after a linebreak.""") diff --git a/tests/test_misc_capitalization.py b/tests/test_misc_capitalization.py deleted file mode 100644 index 19c19c5e4..000000000 --- a/tests/test_misc_capitalization.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests for misc.capitalization check.""" - -from proselint.checks.misc import capitalization as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.capitalization.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.capitalization.check.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It goes back to the stone age.""") - - def test_smoke_check_months(self): - """Basic smoke test for misc.capitalization.check_months.""" - assert chk.check_months("""Smoke phrase with nothing flagged""") == [] - assert chk.check_months("""A nice day in june.""") != [] - - def test_smoke_check_days(self): - """Basic smoke test for misc.capitalization.check_days.""" - assert chk.check_days("""Smoke phrase with nothing flagged""") == [] - assert chk.check_days("""It happened on friday.""") != [] diff --git a/tests/test_misc_chatspeak.py b/tests/test_misc_chatspeak.py deleted file mode 100644 index d0c0006db..000000000 --- a/tests/test_misc_chatspeak.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.chatspeak check.""" - -from proselint.checks.misc import chatspeak as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.chatspeak.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.chatspeak.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""BRB getting coffee.""") diff --git a/tests/test_misc_commercialese.py b/tests/test_misc_commercialese.py deleted file mode 100644 index 482c92456..000000000 --- a/tests/test_misc_commercialese.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.commercialese check.""" - -from proselint.checks.misc import commercialese as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.commercialese.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.commercialese.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""We regret to inform you of this.""") diff --git a/tests/test_misc_composition.py b/tests/test_misc_composition.py deleted file mode 100644 index 8a9597aba..000000000 --- a/tests/test_misc_composition.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.composition check.""" - -from proselint.checks.misc import composition as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.composition.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.composition.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""His story is not honest.""") diff --git a/tests/test_misc_currency.py b/tests/test_misc_currency.py deleted file mode 100644 index f0211ff71..000000000 --- a/tests/test_misc_currency.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.currency check.""" - -from proselint.checks.misc import currency as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.currency.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.currency.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It cost $10 dollars.""") diff --git a/tests/test_misc_debased.py b/tests/test_misc_debased.py deleted file mode 100644 index e59bcc44e..000000000 --- a/tests/test_misc_debased.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.debased check.""" - -from proselint.checks.misc import debased as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.debased.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.debased.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""This leaves much to be desired.""") diff --git a/tests/test_misc_false_plurals.py b/tests/test_misc_false_plurals.py deleted file mode 100644 index 726dba4c2..000000000 --- a/tests/test_misc_false_plurals.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests for misc.false_plurals check.""" - -from proselint.checks.misc import false_plurals as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.false_plurals.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.false_plurals.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""There were several phenomenons.""") - - def test_smoke_kudos(self): - """Basic smoke test for misc.false_plurals.kudos.""" - assert chk.check_kudos("""Smoke phrase with nothing flagged.""") == [] - assert chk.check_kudos("""I give you many kudos.""") != [] diff --git a/tests/test_misc_greylist.py b/tests/test_misc_greylist.py deleted file mode 100644 index a17925dc3..000000000 --- a/tests/test_misc_greylist.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.greylist check.""" - -from proselint.checks.misc import greylist as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.greylist.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.greylist.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""She should utilize her knowledge.""") diff --git a/tests/test_misc_illogic.py b/tests/test_misc_illogic.py deleted file mode 100644 index 2a73f4aa5..000000000 --- a/tests/test_misc_illogic.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for misc.illogic check.""" - -from proselint.checks.misc import illogic as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.illogic.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.illogic.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""We should preplan the trip.""") - - def test_smoke_coin_a_phrase_from(self): - """Basic smoke test for misc.illogic.check_coin_a_phrase_from.""" - assert chk.check_coin_a_phrase_from( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_coin_a_phrase_from( - """To coin a phrase from him, No diggity""") != [] - - def test_smoke_check_without_your_collusion(self): - """Basic smoke test for misc.illogic.""" - assert chk.check_without_your_collusion( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_without_your_collusion( - """Not Without your collusion you won't'.""") != [] diff --git a/tests/test_misc_inferior_superior.py b/tests/test_misc_inferior_superior.py deleted file mode 100644 index ceb46c07b..000000000 --- a/tests/test_misc_inferior_superior.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for misc.inferior_superior check.""" - -from proselint.checks.misc import inferior_superior as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.inferior_superior.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.inferior_superior.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """It was more inferior than the alternative.""") diff --git a/tests/test_misc_latin.py b/tests/test_misc_latin.py deleted file mode 100644 index 437280bc7..000000000 --- a/tests/test_misc_latin.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.latin check.""" - -from proselint.checks.misc import latin as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.latin.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.latin.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""And ceteris paribus, it was good.""") diff --git a/tests/test_misc_many_a.py b/tests/test_misc_many_a.py deleted file mode 100644 index 230b00596..000000000 --- a/tests/test_misc_many_a.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for misc.many_a check.""" - -from proselint.checks.misc import many_a as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.many_a.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.many_a.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """There were many a day I thought about it.""") diff --git a/tests/test_misc_metaconcepts.py b/tests/test_misc_metaconcepts.py deleted file mode 100644 index 611addaf6..000000000 --- a/tests/test_misc_metaconcepts.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Tests for misc.metaconcepts check.""" - -from proselint.checks.misc import metaconcepts as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.metaconcepts.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.metaconcepts.""" - pass diff --git a/tests/test_misc_metadiscourse.py b/tests/test_misc_metadiscourse.py deleted file mode 100644 index cacddaab6..000000000 --- a/tests/test_misc_metadiscourse.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.metadiscourse check.""" - -from proselint.checks.misc import metadiscourse as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.metadiscourse.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.metadiscourse.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It's based on the rest of this article.""") diff --git a/tests/test_misc_narcissism.py b/tests/test_misc_narcissism.py deleted file mode 100644 index 85de5550f..000000000 --- a/tests/test_misc_narcissism.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Tests for misc.narcissism check.""" - -from proselint.checks.misc import narcissism as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.narcissism.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.narcissism.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """In recent years, an increasing number of scientists have studied - the problem in detail.""") diff --git a/tests/test_misc_not_guilty.py b/tests/test_misc_not_guilty.py deleted file mode 100644 index aa72af5ec..000000000 --- a/tests/test_misc_not_guilty.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for misc.not_guilty check.""" - -from proselint.checks.misc import not_guilty as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.not_guilty.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.not_guilty.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """She is not guilty beyond a reasonable doubt.""") diff --git a/tests/test_misc_phrasal_adjectives.py b/tests/test_misc_phrasal_adjectives.py deleted file mode 100644 index 50d1711d2..000000000 --- a/tests/test_misc_phrasal_adjectives.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Tests for misc.phrasal_adjectives check.""" - -from proselint.checks.misc import phrasal_adjectives as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.phrasal_adjectives.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.phrasal_adjectives.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""There were across the board discounts.""") - - def test_smoke_ly(self): - """Basic smoke test for misc.phrasal_adjectives.check_ly.""" - assert chk.check_ly("""Smoke phrase with nothing flagged.""") == [] - assert chk.check_ly("""He ran swiftly-fast.""") - assert chk.check_ly("""The not-so-hotly-contested - result was fine.""") == [] diff --git a/tests/test_misc_preferred_forms.py b/tests/test_misc_preferred_forms.py deleted file mode 100644 index 4b7564b6e..000000000 --- a/tests/test_misc_preferred_forms.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.preferred_forms check.""" - -from proselint.checks.misc import preferred_forms as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.preferred_forms.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.preferred_forms.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It was almost haloween.""") diff --git a/tests/test_misc_pretension.py b/tests/test_misc_pretension.py deleted file mode 100644 index 16bda4635..000000000 --- a/tests/test_misc_pretension.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.pretension check.""" - -from proselint.checks.misc import pretension as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.pretension.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.pretension.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""We need to reconceptualize the project.""") diff --git a/tests/test_misc_professions.py b/tests/test_misc_professions.py deleted file mode 100644 index 3c86e6c50..000000000 --- a/tests/test_misc_professions.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.professions check.""" - -from proselint.checks.misc import professions as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.professions.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.professions.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""I really need a shoe repair guy.""") diff --git a/tests/test_misc_punctuation.py b/tests/test_misc_punctuation.py deleted file mode 100644 index 5be04d531..000000000 --- a/tests/test_misc_punctuation.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.punctuation check.""" - -from proselint.checks.misc import punctuation as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.punctuation.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.punctuation.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""See Smith et. al.""") diff --git a/tests/test_misc_scare_quotes.py b/tests/test_misc_scare_quotes.py deleted file mode 100644 index eb71273ba..000000000 --- a/tests/test_misc_scare_quotes.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.scare_quotes check.""" - -from proselint.checks.misc import scare_quotes as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.scare_quotes.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.scare_quotes.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""What was the 'take-home message'?""") diff --git a/tests/test_misc_suddenly.py b/tests/test_misc_suddenly.py deleted file mode 100644 index 997221a91..000000000 --- a/tests/test_misc_suddenly.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.suddenly check.""" - -from proselint.checks.misc import suddenly as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.suddenly.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.suddenly.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""Suddenly, it all made sense.""") diff --git a/tests/test_misc_tense_present.py b/tests/test_misc_tense_present.py deleted file mode 100644 index 9b5bc4d81..000000000 --- a/tests/test_misc_tense_present.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.tense_present check.""" - -from proselint.checks.misc import tense_present as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.tense_present.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.tense_present.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""I did it on accident honestly.""") diff --git a/tests/test_misc_usage.py b/tests/test_misc_usage.py deleted file mode 100644 index 5ca67299b..000000000 --- a/tests/test_misc_usage.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Tests for misc.usage check.""" - -from proselint.checks.misc import usage as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.usage.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.usage.""" - pass diff --git a/tests/test_misc_waxed.py b/tests/test_misc_waxed.py deleted file mode 100644 index 836bbcef8..000000000 --- a/tests/test_misc_waxed.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.waxed check.""" - -from proselint.checks.misc import waxed as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.waxed.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.waxed.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""They really could wax poetically.""") diff --git a/tests/test_misc_whence.py b/tests/test_misc_whence.py deleted file mode 100644 index 584032f91..000000000 --- a/tests/test_misc_whence.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for misc.whence check.""" - -from proselint.checks.misc import whence as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for misc.whence.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for misc.whence.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""Go back from whence you came!""") diff --git a/tests/test_mixed_metaphors.py b/tests/test_mixed_metaphors.py deleted file mode 100644 index b781fe2f0..000000000 --- a/tests/test_mixed_metaphors.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Tests for mixed_metaphors.misc check.""" - -from proselint.checks.mixed_metaphors import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for mixed_metaphors.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_bottleneck(self): - """Basic smoke test for check_bottleneck.""" - assert chk.check_bottleneck( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_bottleneck( - """The project produced a huge bottleneck.""") != [] - - def test_smoke_misc(self): - """Basic smoke test for check_misc.""" - assert chk.check_misc( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_misc( - """Writing tests is not rocket surgery.""") != [] diff --git a/tests/test_mondegreens.py b/tests/test_mondegreens.py deleted file mode 100644 index 65ebdb862..000000000 --- a/tests/test_mondegreens.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for mondegreens.misc check.""" - -from proselint.checks.mondegreens import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for mondegreens.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for mondegreens.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert self.passes("""... and laid him on the green.""") - assert not self.passes("""..and Lady Mondegreen.""") diff --git a/tests/test_needless_variants.py b/tests/test_needless_variants.py deleted file mode 100644 index 23d8fa985..000000000 --- a/tests/test_needless_variants.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for needless_variants.misc check.""" - -from proselint.checks.needless_variants import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for needless_variants.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for needless_variants.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It was an extensible telescope.""") diff --git a/tests/test_nonwords.py b/tests/test_nonwords.py deleted file mode 100644 index 6bdbf41f5..000000000 --- a/tests/test_nonwords.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for nonwords.misc check.""" - -from proselint.checks.nonwords import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for nonwords.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for nonwords.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The test was good irregardless.""") diff --git a/tests/test_oxymorons.py b/tests/test_oxymorons.py deleted file mode 100644 index b74983f9b..000000000 --- a/tests/test_oxymorons.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for oxymorons.misc check.""" - -from proselint.checks.oxymorons import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for oxymorons.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for oxymorons.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""He needed an exact estimate.""") diff --git a/tests/test_preferred_forms_check.py b/tests/test_preferred_forms_check.py index 7591ea728..b6801e096 100644 --- a/tests/test_preferred_forms_check.py +++ b/tests/test_preferred_forms_check.py @@ -1,39 +1,32 @@ """Test the preferred_forms_check function from the tools.py module.""" -from proselint.tools import preferred_forms_check as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for tools.preferred_forms_check.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def setUp(self): - """Create some test fixtures.""" - self.L = [['use', ['utilize']]] - self.l_caps = [["Stone Age", ["stone age"]]] - self.err = 'error message' - self.msg = 'use the preferred form' - - def test_smoke(self): - """Basic smoke test for preferred_forms_check.""" - assert chk( - "We utilize this tech", self.L, self.err, self.msg) != [] - assert chk( - "We use this tech", self.L, self.err, self.msg) == [] - - def test_capitalization(self): - """Test for preferred forms involving capitalization.""" - assert not chk( - "In the stone age", self.l_caps, self.err, self.msg, - ignore_case=False) - assert chk( - "In the Stone Age", self.l_caps, self.err, self.msg, - ignore_case=False) == [] +from proselint.checks import preferred_forms_check + + +def test_preferred_forms_check(): + """Basic smoke test for preferred_forms_check.""" + items = [["use", ["utilize"]]] + err = "error message" + msg = "use the preferred form" + assert preferred_forms_check("We utilize this tech", items, err, msg) != [] + assert preferred_forms_check("We use this tech", items, err, msg) == [] + + +def test_preferred_forms_check_capitalization(): + """Test for preferred forms involving capitalization.""" + items = [["Stone Age", ["stone age"]]] + err = "error message" + msg = "use the preferred form" + assert not preferred_forms_check( + "In the stone age", + items, + err, + msg, + ignore_case=False, + ) + assert ( + preferred_forms_check( + "In the Stone Age", items, err, msg, ignore_case=False + ) + == [] + ) diff --git a/tests/test_psychology.py b/tests/test_psychology.py deleted file mode 100644 index 44db93b53..000000000 --- a/tests/test_psychology.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Tests for psychology.misc check.""" - -from proselint.checks.psychology import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for psychology.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_lie_detector_test(self): - """Basic smoke test for psychology.misc.check_lie_detector_test.""" - assert chk.check_lie_detector_test( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_lie_detector_test( - """The defendant took a lie detector test.""") != [] - - def test_smoke_p_equals_zero(self): - """Basic smoke test for psychology.misc.check_p_equals_zero.""" - assert chk.check_p_equals_zero( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_p_equals_zero( - """The effect was highly signficant at p = 0.00.""") != [] - - def test_smoke_mental_telepathy(self): - """Basic smoke test for psychology.misc.check_mental_telepathy.""" - assert chk.check_mental_telepathy( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_mental_telepathy( - """I've been practicing mental telepathy.""") != [] diff --git a/tests/test_redundancy_misc.py b/tests/test_redundancy_misc.py deleted file mode 100644 index 8f496d850..000000000 --- a/tests/test_redundancy_misc.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Tests for redundancy.misc check.""" - -from proselint.checks.redundancy import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for redundancy.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_check(self): - """Basic smoke test for redundancy.misc.check.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The table was rectangular in shape.""") - - def test_smoke_garner(self): - """Basic smoke test for redundancy.misc.check_garner.""" - assert chk.check_garner( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_garner( - """It was blatantly obvious what to do next.""") != [] - - def test_smoke_nordquist(self): - """Basic smoke test for redundancy.misc.check_norquist.""" - assert chk.check_nordquist( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_nordquist( - """Taking the package was absolutely essential.""") != [] - - def test_smoke_atd(self): - """Basic smoke test for redundancy.misc.check_norquist.""" - assert chk.check_atd( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_atd( - """He often repeated the old adage.""") != [] diff --git a/tests/test_redundancy_ras_syndrome.py b/tests/test_redundancy_ras_syndrome.py deleted file mode 100644 index 2e702f3f1..000000000 --- a/tests/test_redundancy_ras_syndrome.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for redundancy.ras_syndrome check.""" - -from proselint.checks.redundancy import ras_syndrome as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for redundancy.ras_syndrome.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for redundancy.ras_syndrome.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""Please enter your PIN number.""") diff --git a/tests/test_scripts.py b/tests/test_scripts.py new file mode 100644 index 000000000..85464031b --- /dev/null +++ b/tests/test_scripts.py @@ -0,0 +1,27 @@ +import os +import subprocess +from pathlib import Path + +import pytest + + +@pytest.fixture +def script_path() -> Path: + path = Path(__file__).resolve().parent.parent / "scripts" + os.chdir(path) + return path + + +scripts = [ + "benchmark_checks.py", + "benchmark_constants.py", + "benchmark_hashes.py", + "benchmark_linter.py", + "benchmark_list_alternatives.py", + "test_hyperlinks_on_shell.py", +] + + +@pytest.mark.parametrize("file", scripts) +def test_scripts(script_path: Path, file) -> None: + subprocess.check_call(f"python {script_path / file}", shell=True) diff --git a/tests/test_security_credit_card.py b/tests/test_security_credit_card.py deleted file mode 100644 index 1e004090c..000000000 --- a/tests/test_security_credit_card.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Tests for security.credit_card check.""" - -from proselint.checks.security import credit_card as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for security.credit_card.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for security.credit_card. - - This makes use of a test MasterCard number. - """ - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """My credit card number is 5555555555554444.""") diff --git a/tests/test_security_password.py b/tests/test_security_password.py deleted file mode 100644 index 5cee163a9..000000000 --- a/tests/test_security_password.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for security.password check.""" - -from proselint.checks.security import password as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for security.password.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for security.password.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The password is 123456.""") - assert not self.passes("""My password is PASSWORD.""") diff --git a/tests/test_sexism.py b/tests/test_sexism.py deleted file mode 100644 index 33e887c47..000000000 --- a/tests/test_sexism.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for sexism.misc check.""" - -from proselint.checks.sexism import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for sexism.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for sexism.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """The legal team had two lawyers and a lady lawyer.""") diff --git a/tests/test_skunked_terms.py b/tests/test_skunked_terms.py deleted file mode 100644 index d8e06abf9..000000000 --- a/tests/test_skunked_terms.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Tests for skunked_terms.misc check.""" - -from proselint.checks.skunked_terms import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for skunked_terms.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for skunked_terms.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes( - """I gave an impassionate defence of the situation.""") diff --git a/tests/test_spelling_able_atable.py b/tests/test_spelling_able_atable.py deleted file mode 100644 index 4ea3ea95c..000000000 --- a/tests/test_spelling_able_atable.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.able_atable check.""" - -from proselint.checks.spelling import able_atable as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.able_atable.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.able_atable.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""There was a demonstratable difference.""") diff --git a/tests/test_spelling_able_ible.py b/tests/test_spelling_able_ible.py deleted file mode 100644 index 971e9fe12..000000000 --- a/tests/test_spelling_able_ible.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.able_ible check.""" - -from proselint.checks.spelling import able_ible as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.able_ible.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.able_ible.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It was a sensable decision.""") diff --git a/tests/test_spelling_ally_ly.py b/tests/test_spelling_ally_ly.py deleted file mode 100644 index e6225d955..000000000 --- a/tests/test_spelling_ally_ly.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.ally_ly check.""" - -from proselint.checks.spelling import ally_ly as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.ally_ly.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.ally_ly.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""She was accidently fired.""") diff --git a/tests/test_spelling_ance_ence.py b/tests/test_spelling_ance_ence.py deleted file mode 100644 index 52efa62c2..000000000 --- a/tests/test_spelling_ance_ence.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.ance_ence check.""" - -from proselint.checks.spelling import ance_ence as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.ance_ence.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.ance_ence.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The resistence was futile.""") diff --git a/tests/test_spelling_athletes.py b/tests/test_spelling_athletes.py deleted file mode 100644 index 2610f2427..000000000 --- a/tests/test_spelling_athletes.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.athletes check.""" - -from proselint.checks.spelling import athletes as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.athletes.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.athletes.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""One of the greats: Cal Ripkin.""") diff --git a/tests/test_spelling_ely_ly.py b/tests/test_spelling_ely_ly.py deleted file mode 100644 index a76ef8a25..000000000 --- a/tests/test_spelling_ely_ly.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.ely_ly check.""" - -from proselint.checks.spelling import ely_ly as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.ely_ly.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.ely_ly.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""She was completly unprepared.""") diff --git a/tests/test_spelling_em_im_en_in.py b/tests/test_spelling_em_im_en_in.py deleted file mode 100644 index 41645f9b0..000000000 --- a/tests/test_spelling_em_im_en_in.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.em_im_en_in check.""" - -from proselint.checks.spelling import em_im_en_in as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.em_im_en_in.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.em_im_en_in.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""We shall imbark on a voyage.""") diff --git a/tests/test_spelling_er_or.py b/tests/test_spelling_er_or.py deleted file mode 100644 index 2f4b332bc..000000000 --- a/tests/test_spelling_er_or.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.er_or check.""" - -from proselint.checks.spelling import er_or as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.er_or.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.er_or.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""She met with the invester.""") diff --git a/tests/test_spelling_in_un.py b/tests/test_spelling_in_un.py deleted file mode 100644 index 459cdd8d0..000000000 --- a/tests/test_spelling_in_un.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.in_un check.""" - -from proselint.checks.spelling import in_un as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.in_un.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.in_un.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The plan was unfeasible.""") diff --git a/tests/test_spelling_misc.py b/tests/test_spelling_misc.py deleted file mode 100644 index a4fa5f753..000000000 --- a/tests/test_spelling_misc.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.misc check.""" - -from proselint.checks.spelling import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""I like this alot.""") diff --git a/tests/test_spelling_ve_of.py b/tests/test_spelling_ve_of.py deleted file mode 100644 index dabd0f7a7..000000000 --- a/tests/test_spelling_ve_of.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for spelling.ve_of check.""" - -from proselint.checks.spelling import ve_of as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for spelling.ve_of.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for spelling.ve_of.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""This could of been the third test.""") diff --git a/tests/test_strunk_white_eos.py b/tests/test_strunk_white_eos.py deleted file mode 100644 index 6fc95a644..000000000 --- a/tests/test_strunk_white_eos.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Unit tests for strunk_white_eos.""" - -# from .check import Check -# from proselint.checks.strunkwhite import elementary_composition as chk - - -# class TestCheck(Check): -# """Define the suite of checks.""" - -# __test__ = True - -# @property -# def this_check(self): -# return chk - -# def test_with_utilized(self): -# """Don't produce an error when 'use' is used correctly.""" -# assert self.check("I use a hammer to drive nails into wood.") - -# def test_no_utilized(self): -# """Produce an error when 'utilize' is used in place of 'use'.""" -# assert not self.check("I utilize a hammer to drive nails into wood.") diff --git a/tests/test_terms_animal_adjectives.py b/tests/test_terms_animal_adjectives.py deleted file mode 100644 index 4eee98bd6..000000000 --- a/tests/test_terms_animal_adjectives.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for terms.animal_adjectives check.""" - -from proselint.checks.terms import animal_adjectives as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for terms.animal_adjectives.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for terms.animal_adjectives.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""It was some bird-like creature.""") diff --git a/tests/test_terms_denizen_labels.py b/tests/test_terms_denizen_labels.py deleted file mode 100644 index 486a068e8..000000000 --- a/tests/test_terms_denizen_labels.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for terms.denizen_labels check.""" - -from proselint.checks.terms import denizen_labels as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for terms.denizen_labels.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for terms.denizen_labels.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""He was definitely a Hong Kongite.""") diff --git a/tests/test_terms_eponymous_adjectives.py b/tests/test_terms_eponymous_adjectives.py deleted file mode 100644 index 674d0046e..000000000 --- a/tests/test_terms_eponymous_adjectives.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for terms.eponymous_adjectives check.""" - -from proselint.checks.terms import eponymous_adjectives as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for terms.eponymous_adjectives.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for terms.eponymous_adjectives.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The writing wasn't Shakespearian.""") diff --git a/tests/test_terms_venery.py b/tests/test_terms_venery.py deleted file mode 100644 index 1747a53bf..000000000 --- a/tests/test_terms_venery.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for terms.venery check.""" - -from proselint.checks.terms import venery as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for terms.venery.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for terms.venery.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""There was a group of alligators.""") diff --git a/tests/test_tools.py b/tests/test_tools.py index ec110d6eb..a4c23fe10 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,39 +1,27 @@ """Test the tools module.""" +from __future__ import annotations -from proselint.config import default -from proselint.tools import lint as proselint, load_options +from proselint.tools import lint -from .check import Check +def extract_line_col(error): + """Extract the line and column number from an error tuple.""" + _, _, _, line, column, _, _, _, _, _ = error + return line, column -def lint(text): - return proselint(text, config=load_options(conf_default=default)) +def test_errors_sorted(): + """Test that errors are sorted by line and column number.""" + text = """But this is a very bad sentence. + This is also a no-good sentence. -class TestLint(Check): - """The test class for tools.lint.""" + """ + lines_and_cols = [extract_line_col(e) for e in lint(text)] + assert sorted(lines_and_cols) == lines_and_cols - __test__ = True - def setUp(self): - """setUp method creating text fixtures.""" - self.text = """But this is a very bad sentence. -This is also a no-good sentence. - -""" - self.text_with_no_newline = """A very bad sentence.""" - - def extract_line_col(self, error): - """Extract the line and column number from an error tuple.""" - _, _, line, column, _, _, _, _, _ = error - return line, column - - def test_errors_sorted(self): - """Test that errors are sorted by line and column number.""" - lines_and_cols = [self.extract_line_col(e) for e in lint(self.text)] - assert sorted(lines_and_cols) == lines_and_cols - - def test_on_no_newlines(self): - """Test that lint works on text without a terminal newline.""" - assert len(lint(self.text_with_no_newline)) == 1 +def test_on_no_newlines(): + """Test that lint works on text without a terminal newline.""" + text_with_no_newline = "A very bad sentence." + assert len(lint(text_with_no_newline)) == 1 diff --git a/tests/test_topic_detector.py b/tests/test_topic_detector.py index 3889647f6..75397bb95 100644 --- a/tests/test_topic_detector.py +++ b/tests/test_topic_detector.py @@ -1,9 +1,9 @@ """Test the topic detector tool.""" -from proselint.tools import topics +from proselint.checks import topics -def test_50_Cent_detector_on_topic(): +def test_50_cent_detector_on_topic(): """Check precision of the 50 Cent topic-detector.""" text = """With the aid of Eminem and Dr. Dre (who produced his first major-label album, Get Rich or Die Tryin'), Jackson became one diff --git a/tests/test_typography_diacritical_marks.py b/tests/test_typography_diacritical_marks.py deleted file mode 100644 index 39f83ed1e..000000000 --- a/tests/test_typography_diacritical_marks.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for typography.diacritical_marks check.""" - -from proselint.checks.typography import diacritical_marks as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for typography.diacritical_marks.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for typography.diacritical_marks.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""He saw the performance by Beyonce.""") diff --git a/tests/test_typography_exclamation.py b/tests/test_typography_exclamation.py deleted file mode 100644 index 77ab68980..000000000 --- a/tests/test_typography_exclamation.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Tests for typography.exclamation check.""" - -from proselint.checks.typography import exclamation as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for typography.exclamation.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke_repeated_exclamations(self): - """Basic smoke test. - - Test for typography.exclamation.check_repeated_exclamations. - """ - assert chk.check_repeated_exclamations( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_repeated_exclamations( - """I'm really excited!!""") != [] - - def test_smoke_exclamations_ppm(self): - """Basic smoke test. - - Test for typography.exclamation.check_exclamations_ppm. - """ - assert chk.check_exclamations_ppm( - """Smoke phrase with nothing flagged.""") == [] - assert chk.check_exclamations_ppm( - """I'm really excited! Really!""") != [] diff --git a/tests/test_typography_symbols.py b/tests/test_typography_symbols.py deleted file mode 100644 index c5690bb07..000000000 --- a/tests/test_typography_symbols.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Test Butterick's symbols.""" - -from proselint.checks.typography import symbols as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for typography.symbols.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_ellipsis(self): - """Find ... in a string.""" - assert chk.check_ellipsis("""The long and winding road...""") - - def test_copyright(self): - """Find a (c) or (C) in a string.""" - assert chk.check_copyright_symbol("""Show me the money! (C)""") - assert chk.check_copyright_symbol("""Show me the money! (c)""") - - def test_trademark(self): - """Find a (TM) or (tm) in a string.""" - assert chk.check_trademark_symbol("""The Fresh Maker (TM)""") - assert chk.check_trademark_symbol("""The Fresh Maker (tm)""") - - def test_registered_trademark(self): - """Find a (r) or (R) in a string.""" - assert chk.check_registered_trademark_symbol("""Just Do It (R)""") - assert chk.check_registered_trademark_symbol("""Just Do It (r)""") - - def test_sentence_spacing(self): - """Find a sentence followed by three or more spaces.""" - assert chk.check_sentence_spacing( - """This is a sentence. This is another.""") - - def test_multiplication(self): - """Find an x between two digits.""" - assert chk.check_multiplication_symbol( - """It is obvious that 2 x 2 = 4.""") - - def test_curly_quotes(self): # FIXME - """Find "" quotes in a string.""" - assert chk.check_curly_quotes( - "\"This should produce an error\", he said.") - assert not chk.check_curly_quotes("But this should not.") - assert chk.check_curly_quotes("Alas, \"it should here too\".") - assert not chk.check_curly_quotes("\"A singular should not, though.") diff --git a/tests/test_uncomparables.py b/tests/test_uncomparables.py deleted file mode 100644 index d5eddb26a..000000000 --- a/tests/test_uncomparables.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Test uncomparables.misc""" - -from proselint.checks.uncomparables import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for uncomparables.misc.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for uncomparables.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The item was more unique.""") - - def test_sample_phrases(self): - """Find 'very unique'.""" - assert not self.passes("""This sentence is very unique.""") - - def test_spaces(self): - """Handle spacing correctly.""" - assert not self.passes("""This sentence is very\nunique.""") - assert not self.passes("""Kind of complete.""") - assert self.passes("""Every perfect instance.""") - - def test_constitutional(self): - """Don't flag exceptions.""" - assert self.passes("""A more perfect union.""") diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 000000000..bbe084164 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,14 @@ +"""Test version number.""" +from click.testing import CliRunner + +from proselint.command_line import proselint +from proselint.version import __version__ +from tests.conftest import print_invoke_return + + +def test_version(): + """Make sure the version number is correct.""" + result = CliRunner().invoke(proselint, "--version") + + print_invoke_return(result) + assert __version__ in result.stdout diff --git a/tests/test_weasel_words_misc.py b/tests/test_weasel_words_misc.py deleted file mode 100644 index 4e30582dd..000000000 --- a/tests/test_weasel_words_misc.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Tests for weasel_words.misc check.""" - -from proselint.checks.weasel_words import misc as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for weasel_words.misc.""" - __test__ = False - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for weasel_words.misc.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - # TODO: add test when check is implemented diff --git a/tests/test_weasel_words_very.py b/tests/test_weasel_words_very.py deleted file mode 100644 index 7f22a7bcf..000000000 --- a/tests/test_weasel_words_very.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Tests for weasel_words.very check.""" - -from proselint.checks.weasel_words import very as chk - -from .check import Check - - -class TestCheck(Check): - """The test class for weasel_words.very.""" - - __test__ = True - - @property - def this_check(self): - """Boilerplate.""" - return chk - - def test_smoke(self): - """Basic smoke test for weasel_words.very.""" - assert self.passes("""Smoke phrase with nothing flagged.""") - assert not self.passes("""The book was very interesting.""") diff --git a/worker.py b/worker.py deleted file mode 100644 index bfc87b1f4..000000000 --- a/worker.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Heroku web worker.""" - -from builtins import map - -import os -import redis -from rq import Worker, Queue, Connection - -listen = ['high', 'default', 'low'] - -redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379') - -conn = redis.from_url(redis_url) - -if __name__ == '__main__': - with Connection(conn): - worker = Worker(list(map(Queue, listen))) - worker.work()