diff --git a/.coveragerc b/.coveragerc index 3afa5ae5..d3b3b11b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,13 +1,14 @@ [run] -omit = - # Don't do coverage on test code - sphobjinv/test/* - tests.py +source = + src - # Don't cover code in the env (Termux only?) - env/* +omit = + # Don't worry about covering vendored libraries + src/sphobjinv/_vendored/* [report] exclude_lines = pragma: no cover ^\s*pass\s*$ + +show_missing = True diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index b7f65000..f399d2ac 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -4,7 +4,7 @@ on: push jobs: all_checks: - name: Run all tests, lints, etc. (Python 3.9) + name: Run all tests, lints, etc. (Python 3.10) runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[skip ci]')" @@ -15,7 +15,7 @@ jobs: - name: Install Python uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: '3.10' - name: Update pip & setuptools run: python -m pip install -U pip setuptools @@ -32,9 +32,9 @@ jobs: make html mkdir scratch - - name: Run tests + - name: Run tests & report source coverage run: | - pytest --cov=src --testall --nonloc + pytest --cov --testall --nonloc tox -e sdist_install - name: Run doctests @@ -59,7 +59,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.6', '3.7', '3.8'] + python: ['3.6', '3.7', '3.8', '3.9'] if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: @@ -86,8 +86,7 @@ jobs: make html mkdir scratch - - name: Run tests + - name: Run tests & report source coverage run: | - pytest --cov=src --testall --nonloc + pytest --cov --testall --nonloc tox -e sdist_install - \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..99376a77 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: 'v3.2.0' + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/psf/black + rev: '22.1.0' + hooks: + - id: black diff --git a/.readthedocs.yml b/.readthedocs.yml index 0fe88ff5..62541d22 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,7 +14,7 @@ formats: all # Python and requirements python: - version: 3.8 + version: '3.8' install: - requirements: requirements-rtd.txt - method: pip diff --git a/AUTHORS.md b/AUTHORS.md index c8ddace8..e3b7a59e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -11,13 +11,13 @@ hadn't yet been documented (that happened in [2018](https://github.com/sphinx-do and a fair bit of searching didn't turn up anything promising. Once I dug into the Sphinx code to figure out how to zlib-decompress the object data, it was relatively straightforward -to put together the initial v1.0 of `sphobjinv`, which could only (de)compress objects.inv files to/from plaintext. +to put together the initial v1.0 of `sphobjinv`, which could only (de)compress `objects.inv` files to/from plaintext. As I started to use it regularly in my own documentation work, it became clear that there would be significant advantages from also implementing functionality to assist with object searches, especially in large documentation sets. Also, it seemed likely that a robust API for creation/manipulation of inventory contents would be useful, in order to -assist with things like scraping a non-Sphinx website to generate an objects.inv for cross-referencing in other docs. +assist with things like scraping a non-Sphinx website to generate an `objects.inv` for cross-referencing in other docs. This led to the current object-oriented API of `sphobjinv` v2.x. While there are [numerous](https://github.com/bskinn/sphobjinv/issues) possible enhancements to the project, I'm quite satisfied with its ease of use and usefulness, at least for my purposes, and thus consider it fully stable. -I'm always glad to receive feature requests and (especially) bug reports, though. \ No newline at end of file +I'm always glad to receive feature requests and bug reports, though. \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a807e50e..1f6dab64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,51 @@ and this project strives to adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +### [2.2] - 2022-01-30 + +#### Administrative + + * The project documentation has been updated to reflect the deprecation + of the `python-Levenshtein` speedup. + + * `pre-commit` has been added to the project, primarily to automate + `black` code formatting on every commit. + + * The default trailing-whitespace, end-of-file, YAML syntax, and + large-file-prevention hooks have also been added. + +#### Internal + + * `sphinx-removed-in` was added as a dev and RTD dependency, to provide + the `versionremoved` Sphinx directive. + + +### [2.2b1] - 2021-12-23 + +#### Removed + + * Acceleration of the `suggest` functionality via use of `python-Levenshtein` + is no longer possible due to the vendoring of an early, MIT-licensed version + of `fuzzywuzzy`, as noted below. The `speedup` install extra is now obsolete, + and has been removed. + +#### Internal + + * The `fuzzywuzzy` string matcher was vendored into the project from a point + in its development history before the `python-Levenshtein` dependency, + and its corresponding GPL encumbrance, was introduced. + +#### Administrative + + * Project default branch migrated to `main` from `master`. + + * Standard development Python version bumped to 3.10. + + * Standard development Sphinx version bumped to 4.3.1. + + * Active support for Python 3.11 added. + + ### [2.1] - 2021-04-14 #### Added @@ -246,4 +291,3 @@ and this project strives to adhere to * Programmatic conversion via API is available, but potentially buggy due to poor segregation of cmdline behaviors. This is to be fixed. - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf562b5b..9e04d4e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ in whatever location you prefer. Any Python interpreter 3.6+ *should* work fine. I prefer to use `virtualenv` and create in `./env`: ``` -$ python3.9 -m virtualenv env --prompt="(sphobjinv) " +$ python3.10 -m virtualenv env --prompt="sphobjinv" ``` Activate the environment: @@ -63,6 +63,12 @@ The next step is to upgrade/install the development requirements: (sphobjinv) $ pip install -r requirements-dev.txt ``` +Then, install the pre-commit hooks: + +``` +(sphobjinv) $ pre-commit install +``` + Finally, you'll need to build the Sphinx docs, as some of the tests interact with them: @@ -84,21 +90,21 @@ $ git checkout -b description-of-change ``` This makes it a lot simpler to get your repo fork up to date -after `master` receives further commits. +after `main` receives further commits. -To bring your fork's `master` up to date, you first need to +To bring your fork's `main` up to date, you first need to add the main repo as a new git remote (one-time task): ``` $ git remote add upstream https://github.com/bskinn/sphobjinv ``` -Then, any time you need to refresh the fork's master: +Then, any time you need to refresh the fork's `main`: ``` $ git fetch --all -$ git checkout master -$ git merge upstream/master # (should merge without incident) +$ git checkout main +$ git merge upstream/main # (should merge without incident) $ git push # (should push to your fork without incident) ``` @@ -128,6 +134,9 @@ please go beyond this and add tests that check potential edge cases, bad/malformed/invalid inputs, etc. For bugfixes, add one or more focused regression tests that cover the bug behavior being fixed. +PRs that add [xfail tests for existing bugs](https://blog.ganssle.io/articles/2021/11/pytest-xfail.html) +are also welcomed. + There are some situations where it may make sense to use a `# pragma: no cover` to ignore coverage on certain line(s) of code. Please start a discussion in the issue or PR comments before @@ -138,7 +147,7 @@ configured for the project, it is **not** set up to be an everyday test runner. Instead, it's used to execute a matrix of test environments checking for the compatibility of different Python and dependency versions. You can run it if you want, but you'll need -working versions of all of Python 3.6 through 3.10 +working versions of all of Python 3.6 through 3.11 installed and on `PATH` as `python3.6`, `python3.7`, etc. The nonlocal test suite is run for each `tox` environment, so it's best to use at most two parallel sub-processes to avoid oversaturating @@ -159,7 +168,7 @@ To run the lints locally, it's easiest to use `tox`: $ tox -e flake8 ``` -In some limited circumstances, it may be necessary to add +In some limited circumstances, it may be necessary to add [`# noqa`](https://flake8.pycqa.org/en/stable/user/violations.html#in-line-ignoring-errors) or [`per_file_ignores`](https://flake8.pycqa.org/en/stable/user/options.html#cmdoption-flake8-per-file-ignores) exclusions to the `flake8` lints. @@ -239,7 +248,7 @@ and the link validity checker with `make linkcheck`. Both Github Actions and Azure Pipelines are set up for the project, and should run on any forks of the repository. -Github Actions runs the test suite on Linux for Python 3.6 through 3.9, +Github Actions runs the test suite on Linux for Python 3.6 through 3.10, as well as the `flake8` lints and the Sphinx doctests and link-validity testing, and is configured to run on all commits. The workflow can be skipped per-commit by including `[skip ci]` in the commit message. @@ -247,12 +256,12 @@ per-commit by including `[skip ci]` in the commit message. The Azure Pipelines CI runs an extensive matrix of cross-platform and cross-Python-version tests, as well as numerous other checks. Due to its length, it is configured to run only on release branches -and PRs to `master` or `stable`. It cannot be skipped. +and PRs to `main` or `stable`. It cannot be skipped. ## CHANGELOG -The project [`CHANGELOG`](https://github.com/bskinn/sphobjinv/blob/master/CHANGELOG.md) +The project [`CHANGELOG`](https://github.com/bskinn/sphobjinv/blob/main/CHANGELOG.md) should be updated for the majority of contributions. No tooling is in place (e.g., [`towncrier`](https://github.com/twisted/towncrier)) for automated collation of news items into `CHANGELOG`; @@ -278,4 +287,3 @@ you want to create, though, then don't use them. ## License All contributions will take on the MIT License of the project at large. - diff --git a/LICENSE.txt b/LICENSE.txt index 83a8cff5..7810c538 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2021 Brian Skinn +Copyright (c) 2016-2022 Brian Skinn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index 6205c742..641d13da 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ sphobjinv: Manipulate and inspect Sphinx objects.inv files :alt: GitHub Workflow Status :target: https://github.com/bskinn/sphobjinv/actions -.. image:: https://codecov.io/gh/bskinn/sphobjinv/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/bskinn/sphobjinv/branch/main/graph/badge.svg :target: https://codecov.io/gh/bskinn/sphobjinv **Most Recent Stable Release:** @@ -22,12 +22,19 @@ sphobjinv: Manipulate and inspect Sphinx objects.inv files .. image:: https://img.shields.io/readthedocs/sphobjinv/latest.svg :target: http://sphobjinv.readthedocs.io/en/latest/ +.. image:: https://badges.gitter.im/sphobjinv/community.svg + :alt: Join the chat at https://gitter.im/sphobjinv/community + :target: https://gitter.im/sphobjinv/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge + .. image:: https://img.shields.io/github/license/mashape/apistatus.svg :target: https://github.com/bskinn/sphobjinv/blob/stable/LICENSE.txt .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black +.. image:: https://pepy.tech/badge/sphobjinv/month + :target: https://pepy.tech/project/sphobjinv?versions=2.1b1&versions=2.2b1&versions=2.2&versions=2.1&versions=2.0.1 + ---- **Using Sphinx?** @@ -47,15 +54,21 @@ to cross-reference into, and pass it to ``sphobjinv suggest``. For internal cross-references, locate ``objects.inv`` within ``build/html``:: - $ sphobjinv suggest doc/build/html/objects.inv as_rst -st 50 - - Name Score - -------------------------------------------------------- ------- - :py:method:`sphobjinv.data.SuperDataObj.as_rst` 60 - :std:doc:`cli/implementation/parser` 57 - :py:module:`sphobjinv.cli.parser` 50 - :py:method:`sphobjinv.data.SuperDataObj.as_str` 50 - :py:method:`sphobjinv.inventory.Inventory.objects_rst` 50 + $ sphobjinv suggest doc/build/html/objects.inv as_rst -st 58 + + Name Score + --------------------------------------------------- ------- + :py:property:`sphobjinv.data.SuperDataObj.as_rst` 60 + :py:class:`sphobjinv.cli.parser.PrsConst` 59 + :py:class:`sphobjinv.data.DataFields` 59 + :py:class:`sphobjinv.data.DataObjBytes` 59 + :py:class:`sphobjinv.data.DataObjStr` 59 + :py:class:`sphobjinv.data.SuperDataObj` 59 + :py:class:`sphobjinv.enum.HeaderFields` 59 + :py:class:`sphobjinv.enum.SourceTypes` 59 + :py:function:`sphobjinv.fileops.writebytes` 59 + :py:function:`sphobjinv.fileops.writejson` 59 + :py:class:`sphobjinv.inventory.Inventory` 59 .. end shell command @@ -120,11 +133,11 @@ inventory creation/modification:: >>> import sphobjinv as soi >>> inv = soi.Inventory('doc/build/html/objects.inv') >>> print(inv) - + >>> inv.project 'sphobjinv' >>> inv.version - '2.1' + '2.2' >>> inv.objects[0] DataObjStr(name='sphobjinv.cli.core', domain='py', role='module', priority='0', uri='cli/implementation/core.html#module-$', dispname='-') @@ -146,7 +159,7 @@ Source on `GitHub `__. Bug reports and feature requests are welcomed at the `Issues `__ page there. -Copyright (c) Brian Skinn 2016-2021 +Copyright (c) Brian Skinn 2016-2022 -License: The MIT License. See `LICENSE.txt `__ +License: The MIT License. See `LICENSE.txt `__ for full license terms. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 98328427..da8b7654 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,7 @@ trigger: pr: branches: include: - - master + - main - stable @@ -21,7 +21,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install -U tox displayName: Install tox @@ -43,7 +43,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install interrogate displayName: Install interrogate @@ -67,39 +67,24 @@ stages: spec: '3.8' py39: spec: '3.9' + py310: + spec: '3.10' pypy3: spec: 'pypy3' - platforms: [linux, windows, macOs] + platforms: [linux, windows] - - job: Levenshtein - strategy: - matrix: - linux: - platform: Ubuntu-latest - macOs: - platform: macOS-latest - - pool: - vmImage: $[ variables.platform ] - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.9' - architecture: 'x64' - displayName: Use Python 3.9 - - - script: pip install -U --force-reinstall -r requirements-ci.txt -e .[speedup] - displayName: Install CI requirements, plus python-Levenshtein - - - script: pip list - displayName: Show full environment contents - - - script: cd doc; make html; mkdir scratch - displayName: Build the documentation for tests to work against (Linux/macOs) - - - script: pytest --nonloc -k "not readme" - displayName: Run pytest without README tests + - template: azure-coretest.yml + parameters: + pythons: + py37: + spec: '3.7' + py38: + spec: '3.8' + py39: + spec: '3.9' + py310: + spec: '3.10' + platforms: [macOs] - stage: aux_tests @@ -115,7 +100,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install . -r requirements-rtd.txt displayName: Install project, plus docs requirements @@ -133,7 +118,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install -r requirements-dev.txt displayName: Install full dev requirements @@ -150,7 +135,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install . -r requirements-rtd.txt displayName: Install doc requirements and local project @@ -168,7 +153,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install -r requirements-dev.txt displayName: Install full dev requirements @@ -191,7 +176,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install -r requirements-ci.txt displayName: Install CI requirements @@ -211,7 +196,7 @@ stages: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.9' + versionSpec: '3.10' - script: pip install tox flake8-noqa -r requirements-flake8.txt displayName: Install requirements diff --git a/conftest.py b/conftest.py index 028d33c0..1ed15dd3 100644 --- a/conftest.py +++ b/conftest.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv @@ -114,7 +114,7 @@ class Extensions(str, Enum): # For the URL mode of Inventory instantiation remote_url = ( - "https://github.com/bskinn/sphobjinv/raw/master/" + "https://github.com/bskinn/sphobjinv/raw/main/" "tests/resource/objects_{0}.inv" ) diff --git a/doc/source/api_usage.rst b/doc/source/api_usage.rst index 380101ac..20d8b556 100644 --- a/doc/source/api_usage.rst +++ b/doc/source/api_usage.rst @@ -65,7 +65,7 @@ Remote |objects.inv| files can also be retrieved via URL, with the *url* keyword .. doctest:: api_inspect - >>> inv4 = soi.Inventory(url='https://github.com/bskinn/sphobjinv/raw/master/tests/resource/objects_attrs.inv') + >>> inv4 = soi.Inventory(url='https://github.com/bskinn/sphobjinv/raw/main/tests/resource/objects_attrs.inv') >>> print(inv4) diff --git a/doc/source/cli/convert.rst b/doc/source/cli/convert.rst index ac491796..d0ebc57d 100644 --- a/doc/source/cli/convert.rst +++ b/doc/source/cli/convert.rst @@ -64,7 +64,7 @@ indicated URL): .. doctest:: convert_url - >>> cli_run('sphobjinv convert plain -u https://github.com/bskinn/sphobjinv/raw/master/tests/resource/objects_attrs.inv') + >>> cli_run('sphobjinv convert plain -u https://github.com/bskinn/sphobjinv/raw/main/tests/resource/objects_attrs.inv') Remote inventory found. diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst index bcb1559d..ae6a4a22 100644 --- a/doc/source/cli/index.rst +++ b/doc/source/cli/index.rst @@ -15,7 +15,7 @@ Some notes on these CLI docs: * CLI examples are executed in a sandboxed directory pre-loaded with |cour|\ objects_attrs.inv\ |/cour| (from, e.g., - `here `__). * :class:`~pathlib.Path` (from :mod:`pathlib`) diff --git a/doc/source/cli/suggest.rst b/doc/source/cli/suggest.rst index 67de7e29..d12a9026 100644 --- a/doc/source/cli/suggest.rst +++ b/doc/source/cli/suggest.rst @@ -29,7 +29,7 @@ via :option:`--thresh`: Remote |objects.inv| files can be retrieved for inspection by passing the :option:`--url` flag: -.. command-output:: sphobjinv suggest https://github.com/bskinn/sphobjinv/raw/master/tests/resource/objects_attrs.inv instance -u -t 48 +.. command-output:: sphobjinv suggest https://github.com/bskinn/sphobjinv/raw/main/tests/resource/objects_attrs.inv instance -u -t 48 :cwd: /../../tests/resource The URL provided **MUST** have the leading protocol specified (here, diff --git a/doc/source/conf.py b/doc/source/conf.py index dac34c0c..a532e35f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -18,7 +18,7 @@ # -- Project information ----------------------------------------------------- project = 'sphobjinv' -copyright = '2016-2021, Brian Skinn' +copyright = '2016-2022, Brian Skinn' author = 'Brian Skinn' # The full version for `release`, including alpha/beta/rc tags @@ -40,6 +40,7 @@ "sphinx.ext.napoleon", "sphinxcontrib.programoutput", "sphinx_issues", + "sphinx_removed_in", ] # napoleon configuration @@ -119,7 +120,7 @@ .. |license_txt| replace:: LICENSE.txt -.. _license_txt: https://github.com/bskinn/sphobjinv/blob/master/LICENSE.txt +.. _license_txt: https://github.com/bskinn/sphobjinv/blob/main/LICENSE.txt .. |fuzzywuzzy| replace:: ``fuzzywuzzy`` diff --git a/doc/source/customfile.rst b/doc/source/customfile.rst index d0ec127e..390ec5c1 100644 --- a/doc/source/customfile.rst +++ b/doc/source/customfile.rst @@ -15,7 +15,7 @@ can be found at the GitHub repo .. note:: - These instructions are for |soi| v2.0; + These instructions are for |soi| v2.x; the prior instructions for v1.0 can be found `here `__. @@ -149,7 +149,7 @@ can be found at the GitHub repo 'django': ('https://docs.djangoproject.com/en/dev/', 'https://docs.djangoproject.com/en/dev/_objects/'), # Drawing the Sphinx objects.inv from a local copy, but referring to the 1.7 web docs - 'sphinx': ('https://www.sphinx-doc.org/en/1.7/', '/path/to/local/objects.inv', + 'sphinx': ('https://www.sphinx-doc.org/en/1.7/', '/path/to/local/objects.inv'), } .. MAKE SURE TO UPDATE THESE TWO STEP REFERENCES IF NUMBERING CHANGES!! diff --git a/doc/source/index.rst b/doc/source/index.rst index e9b91cca..cea37fcf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -42,8 +42,8 @@ Install |soi| via |cour|\ pip\ |/cour|:: $ pip install sphobjinv Alternatively, |soi| is packaged with -`multiple POSIX distributions `__, -including: +`multiple POSIX distributions `__ +and package managers, including: * Alpine Linux: ``py3-sphobjinv`` (`info `__) @@ -51,7 +51,9 @@ including: * Fedora: ``python-sphobjinv`` (`info `__) - * Gentoo GURU: ``dev-python/sphobjinv`` (`info `__) + * Gentoo: ``dev-python/sphobjinv`` (`info `__) + + * Guix: ``python-sphobjinv`` (`info `__) * Manjaro: ``python-sphobjinv`` @@ -63,21 +65,22 @@ including: * pkgsrc: ``textproc/py-sphobjinv`` (`info `__) + * spack: ``py-sphobjinv`` (`info `__) + -The package is configured for use both as a +|soi| is configured for use both as a :doc:`command-line script ` and as a :doc:`Python package `. -Installing the optional dependency |python-Levenshtein|_ substantially -accelerates the the "suggest" functionality; see +The optional dependency |python-Levenshtein|_ for accelerating +the "suggest" functionality is no longer available due to a +licensing conflict, and has been deprecated. See :doc:`here ` for more information. The project source repository is on GitHub: `bskinn/sphobjinv `__. -.. Contents -.. -------- .. toctree:: :maxdepth: 1 diff --git a/doc/source/levenshtein.rst b/doc/source/levenshtein.rst index ba155cb4..d9af48ee 100644 --- a/doc/source/levenshtein.rst +++ b/doc/source/levenshtein.rst @@ -1,44 +1,31 @@ .. Info on speedups from python-Levenshtein -Speeding up "suggest" with python-Levenshtein -============================================= +Speeding up "suggest" with python-Levenshtein (DEPRECATED) +========================================================== |soi| uses |fuzzywuzzy|_ for fuzzy-match searching of object names/domains/roles as part of the :meth:`Inventory.suggest() ` functionality, also implemented as the CLI :doc:`suggest ` subcommand. -By default, |fuzzywuzzy|_ uses :class:`difflib.SequenceMatcher` +|fuzzywuzzy|_ uses :class:`difflib.SequenceMatcher` from the Python standard library for its fuzzy searching. -However, it also can use |python-Levenshtein|_, -a Python C extension providing -similar functionality. Thus, |soi| has been implemented with -|python-Levenshtein|_ as an *optional* dependency: it will be -used if it is available. +While earlier versions of |soi| were able to make use of +|fuzzywuzzy|_\ 's optional link to |python-Levenshtein|_, +a Python C extension providing similar functionality, +due to a licensing conflict this is no longer possible. +|soi| now uses a vendored copy of |fuzzywuzzy|_ from an +era when it was released under the MIT License. +Formally: -Installation ------------- +.. versionremoved:: 2.2 -On Linux and MacOS, installing |soi| with the ``speedup`` extra -should work properly to install |python-Levenshtein|_ -in the vast majority of cases:: + Acceleration of the |soi| "suggest" mode via |python-Levenshtein|_ + has been deprecated and is no longer available. - $ pip install sphobjinv[speedup] - -If you need to work with a version of |soi| prior to v2.1, -the ``speedup`` extra is not available and direct installation -is required:: - - $ pip install sphobjinv==2.0.1 python-Levenshtein - -On Windows, as of this writing -`no binary wheel is available on PyPI `__. -So, the easiest way to install is to download a wheel from -`Christoph Gohlke's repository `__ -and run `pip install C:\\path\\to\\wheel`. -Just make sure to match Python version and CPU type (win32 vs win_amd64) -when you download. +The discussion of performance benchmarks and variations in matching +behavior is kept below for historical interest. Performance Benchmark @@ -47,8 +34,7 @@ Performance Benchmark The chart below presents one dataset illustrating the performance enhancement that can be obtained by installing |python-Levenshtein|_. The timings plotted here are from execution of -:func:`timeit.repeat` in the -Python standard library around a +:func:`timeit.repeat` around a :meth:`~sphobjinv.inventory.Inventory.suggest` call, searching for the term "function", for a number of |objects.inv| files from different projects (see diff --git a/doc/source/syntax.rst b/doc/source/syntax.rst index 3ae2f495..65d9601a 100644 --- a/doc/source/syntax.rst +++ b/doc/source/syntax.rst @@ -6,7 +6,7 @@ Sphinx objects.inv v2 Syntax After decompression, "version 2" Sphinx |objects.inv| files follow a syntax that, to the best of this author's ability to determine, is not included in the Sphinx documentation. The below -syntax is believed to be accurate as of Feb 2021 (Sphinx v3.4.3). +syntax is believed to be accurate as of Jan 2022 (Sphinx v4.4.0). Based upon a quick ``git diff`` of the `Sphinx repository `__, it is thought to be valid for all @@ -22,7 +22,7 @@ of an |objects.inv| data line. ---- **The first line** `must be exactly -`__: +`__: .. code-block:: none @@ -31,7 +31,7 @@ of an |objects.inv| data line. ---- **The second and third lines** `must obey -`__ +`__ the template: .. code-block:: none @@ -51,7 +51,7 @@ the |isphx| cross-references: ---- **The fourth line** `must contain -`__ +`__ the string ``zlib`` somewhere within it, but for consistency it should be exactly: .. code-block:: none @@ -62,7 +62,7 @@ the string ``zlib`` somewhere within it, but for consistency it should be exactl **All remaining lines** of the file are the objects data, each laid out in the `following syntax -`__: +`__: .. code-block:: none @@ -124,11 +124,10 @@ the string ``zlib`` somewhere within it, but for consistency it should be exactl ``{priority}`` Flag for `placement in search results - `__. Most will be ``1`` (standard priority) or + `__. Most will be ``1`` (standard priority) or ``-1`` (omit from results) for documentation built by Sphinx. - To note, as of Feb 2021 this value is **not** used by ``intersphinx``; + To note, as of Jan 2022 this value is **not** used by ``intersphinx``; it is only used internally within the search function of the static webpages built *by Sphinx* (|prio_py_search|_ and |prio_js_search|_). Thus, custom inventories likely **MAY** use this field for arbitrary content, if desired, @@ -216,13 +215,14 @@ size of the inventory file: `__ as ``-``. -Thus, a standard |isphx| reference to this method would take the form (the leading -``:py`` could be omitted if ``py`` is the default domain): +Thus, a standard |isphx| reference to this method would take the form: .. code-block:: none :py:meth:`str.join` +The leading ``:py`` here could be omitted if ``py`` is the default domain. + The cross-reference would show as :meth:`str.join` and link to the relative URI: .. code-block:: none diff --git a/pyproject.toml b/pyproject.toml index 50aeb12a..ac84776a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,5 +18,11 @@ exclude = ''' | ^/[.] | ^/doc | ^/env + | ^/src/sphobjinv/_vendored ) ''' + +[tool.interrogate] +exclude = ["src/sphobjinv/_vendored"] +fail-under = 100 +verbose = 1 diff --git a/requirements-ci.txt b/requirements-ci.txt index 4060bd31..7db3e2dd 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,9 +1,8 @@ -attrs>=17.4 +attrs>=19.2 certifi codecov coverage dictdiffer -fuzzywuzzy>=0.3 jsonschema md-toc pytest>=4.4.0 @@ -11,8 +10,9 @@ pytest-check>=0.4 pytest-cov pytest-ordering pytest-timeout -sphinx==3.5.0 +sphinx==4.3.1 sphinx-issues +sphinx-removed-in sphinx-rtd-theme sphinxcontrib-programoutput stdio-mgr>=1.0.1 diff --git a/requirements-dev.txt b/requirements-dev.txt index fa04aef0..d0e2f11a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,11 +1,13 @@ -attrs>=17.4 +attrs>=19.2 +build certifi coverage dictdiffer -fuzzywuzzy>=0.3 +interrogate jsonschema md-toc pep517 +pre-commit pytest>=4.4.0 pytest-check>=0.4 pytest-cov @@ -13,11 +15,12 @@ pytest-ordering pytest-timeout restview rope -sphinx==3.5.0 +sphinx==4.3.1 sphinx-autobuild sphinx-issues -sphinxcontrib-programoutput +sphinx-removed-in sphinx-rtd-theme +sphinxcontrib-programoutput stdio-mgr>=1.0.1 tox twine diff --git a/requirements-flake8.txt b/requirements-flake8.txt index 1d4311d6..35896dde 100644 --- a/requirements-flake8.txt +++ b/requirements-flake8.txt @@ -3,7 +3,6 @@ flake8>=3.7 flake8-2020 flake8-absolute-import flake8-bandit -flake8-black flake8-bugbear flake8-builtins flake8-colors diff --git a/requirements-rtd.txt b/requirements-rtd.txt index a916f574..739e1e36 100644 --- a/requirements-rtd.txt +++ b/requirements-rtd.txt @@ -1,5 +1,6 @@ -attrs>=17.4 -sphinx==3.5.0 +attrs>=19.2 +sphinx==4.3.1 sphinx-issues +sphinx-removed-in sphinx-rtd-theme sphinxcontrib-programoutput diff --git a/setup.cfg b/setup.cfg index 5c02a880..ac61076f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = sphobjinv description = Sphinx objects.inv Inspection/Manipulation Tool url = https://github.com/bskinn/sphobjinv project_urls = - Changelog=https://github.com/bskinn/sphobjinv/blob/master/CHANGELOG.md + Changelog=https://github.com/bskinn/sphobjinv/blob/main/CHANGELOG.md Docs=https://sphobjinv.readthedocs.io/en/stable/ Thank=https://twitter.com/btskinn Donate=https://github.com/sponsors/bskinn @@ -28,6 +28,11 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Topic :: Documentation + Topic :: Documentation :: Sphinx + Topic :: Software Development + Topic :: Software Development :: Documentation Topic :: Utilities Development Status :: 5 - Production/Stable keywords = sphinx, sphinx-doc, inventory, manager, inspector @@ -36,7 +41,6 @@ keywords = sphinx, sphinx-doc, inventory, manager, inspector install_requires = attrs>=19.2 certifi - fuzzywuzzy>=0.8 jsonschema>=3.0 python_requires = >=3.6 @@ -47,10 +51,6 @@ package_dir = [options.packages.find] where = src -[options.extras_require] -speedup = - python-Levenshtein;platform_system!="Windows" - [options.entry_points] console_scripts = sphobjinv=sphobjinv.cli.core:main diff --git a/src/sphobjinv/__init__.py b/src/sphobjinv/__init__.py index d3a81daa..bfdf67be 100644 --- a/src/sphobjinv/__init__.py +++ b/src/sphobjinv/__init__.py @@ -10,7 +10,7 @@ 17 May 2016 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/__main__.py b/src/sphobjinv/__main__.py index f3311c7d..bbf9eb2d 100644 --- a/src/sphobjinv/__main__.py +++ b/src/sphobjinv/__main__.py @@ -10,7 +10,7 @@ 15 May 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/_vendored/__init__.py b/src/sphobjinv/_vendored/__init__.py new file mode 100644 index 00000000..7dfb4e2b --- /dev/null +++ b/src/sphobjinv/_vendored/__init__.py @@ -0,0 +1,28 @@ +r"""``sphobjinv._vendored`` *package definition module*. + +``sphobjinv`` is a toolkit for manipulation and inspection of +Sphinx |objects.inv| files. + +Subpackage marker module for vendored packages. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 11 Dec 2021 + +**Copyright** + \(c) Brian Skinn 2016-2022 + +**Source Repository** + https://github.com/bskinn/sphobjinv + +**Documentation** + https://sphobjinv.readthedocs.io/en/latest + +**License** + The MIT License; see |license_txt|_ for full license terms + +**Members** + +""" \ No newline at end of file diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/LICENSE.txt b/src/sphobjinv/_vendored/fuzzywuzzy/LICENSE.txt new file mode 100644 index 00000000..3a75b0eb --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2011 Adam Cohen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, 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. diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/__init__.py b/src/sphobjinv/_vendored/fuzzywuzzy/__init__.py new file mode 100644 index 00000000..2be667f6 --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/__init__.py @@ -0,0 +1,41 @@ +r"""``sphobjinv._vendored.fuzzywuzzy`` *package definition module*. + +``sphobjinv`` is a toolkit for manipulation and inspection of +Sphinx |objects.inv| files. + +This subpackage vendors an archival version of fuzzywuzzy from +before it incorporated python-Levenshtein and thus acquired +the GPL licence by flow-through, overriding the prior +MIT license of the package. + +All code in this subpackage vendored from: +https://github.com/seatgeek/fuzzywuzzy/tree/4bf28161f7005f3aa9d4d931455ac55126918df7 + +Imports and code details were changed as required to make the code compatible +with current (2021) versions of Python 3. + +Link to MIT-licensed fuzzywuzzy version obtained from the bottom of the README at: +https://github.com/maxbachmann/RapidFuzz/tree/460d291a38922c7fead920de8147798b817653cb + + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 11 Dec 2021 + +**Copyright** + \(c) Brian Skinn 2016-2022 + +**Source Repository** + https://github.com/bskinn/sphobjinv + +**Documentation** + https://sphobjinv.readthedocs.io/en/latest + +**License** + The MIT License; see |license_txt|_ for full license terms + +**Members** + +""" diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/benchmarks.py b/src/sphobjinv/_vendored/fuzzywuzzy/benchmarks.py new file mode 100644 index 00000000..9f0daa3a --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/benchmarks.py @@ -0,0 +1,54 @@ +# -*- coding: utf8 -*- + +from timeit import timeit +from sphobjinv._vendored.fuzzywuzzy import utils + +iterations=100000*10 + +cirque_strings = [ + "cirque du soleil - zarkana - las vegas", + "cirque du soleil ", + "cirque du soleil las vegas", + "zarkana las vegas", + "las vegas cirque du soleil at the bellagio", + "zarakana - cirque du soleil - bellagio" + ] + +choices = [ + "", + "new york yankees vs boston red sox", + "", + "zarakana - cirque du soleil - bellagio", + None, + "cirque du soleil las vegas", + None +] + +mixed_strings = [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.", + "C\\'est la vie", + "Ça va?", + "Cães danados", + u"\xacCamarões assados", + u"a\xac\u1234\u20ac\U00008000" + ] + + +for s in choices: + print 'Test for string: "%s"' % s + # print 'Old: %f' % round(timeit('utils.validate_stringold(\'%s\')' % s, "import utils",number=iterations),4) + print 'New: %f' % round(timeit('utils.validate_string(\'%s\')' % s, "import utils",number=iterations),4) + +print + +for s in mixed_strings: + print 'Test for string: "%s"' % s + #print 'Old: %f' % round(timeit('utils.asciidammitold(\'%s\')' % s, "import utils",number=iterations),4) + print 'New: %f' % round(timeit('utils.asciidammit(\'%s\')' % s, "import utils",number=iterations),4) + +print + +for s in mixed_strings+cirque_strings+choices: + print 'Test for string: "%s"' % s + #print 'Old: %f' % round(timeit('utils.full_processold(\'%s\')' % s, "import utils",number=iterations),4) + print 'New: %f' % round(timeit('utils.full_process(\'%s\')' % s, "import utils",number=iterations),4) diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/fuzz.py b/src/sphobjinv/_vendored/fuzzywuzzy/fuzz.py new file mode 100644 index 00000000..e3026870 --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/fuzz.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +score.py + +Copyright (c) 2011 Adam Cohen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, 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. +""" + +import sys +import os +import re +from difflib import SequenceMatcher +from sphobjinv._vendored.fuzzywuzzy import utils + +REG_TOKEN = re.compile(r"[\w\d]+") # B Skinn 2021-12-11 + +########################### +# Basic Scoring Functions # +########################### + +def ratio(s1, s2): + + if s1 is None: raise TypeError("s1 is None") + if s2 is None: raise TypeError("s2 is None") + + m = SequenceMatcher(None, s1, s2) + return int(100 * m.ratio()) + +# todo: skip duplicate indexes for a little more speed +def partial_ratio(s1, s2): + + if s1 is None: raise TypeError("s1 is None") + if s2 is None: raise TypeError("s2 is None") + + if len(s1) <= len(s2): + shorter = s1; longer = s2; + else: + shorter = s2; longer = s1 + + m = SequenceMatcher(None, shorter, longer) + blocks = m.get_matching_blocks() + + # each block represents a sequence of matching characters in a string + # of the form (idx_1, idx_2, len) + # the best partial match will block align with at least one of those blocks + # e.g. shorter = "abcd", longer = XXXbcdeEEE + # block = (1,3,3) + # best score === ratio("abcd", "Xbcd") + scores = [] + for block in blocks: + long_start = block[1] - block[0] if (block[1] - block[0]) > 0 else 0 + long_end = long_start + len(shorter) + long_substr = longer[long_start:long_end] + + m2 = SequenceMatcher(None, shorter, long_substr) + r = m2.ratio() + if r > .995: return 100 + else: scores.append(r) + + return int(100 * max(scores)) + +############################## +# Advanced Scoring Functions # +############################## + +# Sorted Token +# find all alphanumeric tokens in the string +# sort those tokens and take ratio of resulting joined strings +# controls for unordered string elements +def _token_sort(s1, s2, partial=True): + + if s1 is None: raise TypeError("s1 is None") + if s2 is None: raise TypeError("s2 is None") + + # pull tokens + tokens1 = REG_TOKEN.findall(s1) + tokens2 = REG_TOKEN.findall(s2) + + # sort tokens and join + sorted1 = u" ".join(sorted(tokens1)) + sorted2 = u" ".join(sorted(tokens2)) + + sorted1 = sorted1.strip() + sorted2 = sorted2.strip() + + if partial: + return partial_ratio(sorted1, sorted2) + else: + return ratio(sorted1, sorted2) + +def token_sort_ratio(s1, s2): + return _token_sort(s1, s2, False) + +def partial_token_sort_ratio(s1, s2): + return _token_sort(s1, s2, True) + +# Token Set +# find all alphanumeric tokens in each string...treat them as a set +# construct two strings of the form +# +# take ratios of those two strings +# controls for unordered partial matches +def _token_set(s1, s2, partial=True): + + if s1 is None: raise TypeError("s1 is None") + if s2 is None: raise TypeError("s2 is None") + + # pull tokens + tokens1 = set(REG_TOKEN.findall(s1)) + tokens2 = set(REG_TOKEN.findall(s2)) + + intersection = tokens1.intersection(tokens2) + diff1to2 = tokens1.difference(tokens2) + diff2to1 = tokens2.difference(tokens1) + + sorted_sect = u" ".join(sorted(intersection)) + sorted_1to2 = u" ".join(sorted(diff1to2)) + sorted_2to1 = u" ".join(sorted(diff2to1)) + + combined_1to2 = sorted_sect + " " + sorted_1to2 + combined_2to1 = sorted_sect + " " + sorted_2to1 + + # strip + sorted_sect = sorted_sect.strip() + combined_1to2 = combined_1to2.strip() + combined_2to1 = combined_2to1.strip() + + pairwise = [ + ratio(sorted_sect, combined_1to2), + ratio(sorted_sect, combined_2to1), + ratio(combined_1to2, combined_2to1) + ] + return max(pairwise) + + # if partial: + # # partial_token_set_ratio + # + # else: + # # token_set_ratio + # tsr = ratio(combined_1to2, combined_2to1) + # return tsr + +def token_set_ratio(s1, s2): + return _token_set(s1, s2, False) + +def partial_token_set_ratio(s1, s2): + return _token_set(s1, s2, True) + +# TODO: numerics + +################### +# Combination API # +################### + +# q is for quick +def QRatio(s1, s2): + if not utils.validate_string(s1): return 0 + if not utils.validate_string(s2): return 0 + + p1 = utils.full_process(s1) + p2 = utils.full_process(s2) + + return ratio(p1, p2) + +# w is for weighted +def WRatio(s1, s2): + p1 = utils.full_process(s1) + p2 = utils.full_process(s2) + if not utils.validate_string(p1): return 0 + if not utils.validate_string(p2): return 0 + + # should we look at partials? + try_partial = True + unbase_scale = .95 + partial_scale = .90 + + base = ratio(p1, p2) + len_ratio = float(max(len(p1),len(p2)))/min(len(p1),len(p2)) + + # if strings are similar length, don't use partials + if len_ratio < 1.5: try_partial = False + + # if one string is much much shorter than the other + if len_ratio > 8: partial_scale = .6 + + if try_partial: + partial = partial_ratio(p1, p2) * partial_scale + ptsor = partial_token_sort_ratio(p1, p2) * unbase_scale * partial_scale + ptser = partial_token_set_ratio(p1, p2) * unbase_scale * partial_scale + + return int(max(base, partial, ptsor, ptser)) + else: + tsor = token_sort_ratio(p1, p2) * unbase_scale + tser = token_set_ratio(p1, p2) * unbase_scale + + return int(max(base, tsor, tser)) + diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/process.py b/src/sphobjinv/_vendored/fuzzywuzzy/process.py new file mode 100644 index 00000000..0e0ebd08 --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/process.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +process.py + +Copyright (c) 2011 Adam Cohen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, 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. +""" +from sphobjinv._vendored.fuzzywuzzy.fuzz import * + +import sys, os +from sphobjinv._vendored.fuzzywuzzy import utils + +####################################### +# Find Best Matchs In List Of Choices # +####################################### + +def extract(query, choices, processor=None, scorer=None, limit=5): + + # choices = a list of objects we are attempting to extract values from + # query = an object representing the thing we want to find + # scorer f(OBJ, QUERY) --> INT. We will return the objects with the highest score + # by default, we use score.WRatio() and both OBJ and QUERY should be strings + # processor f(OBJ_A) --> OBJ_B, where the output is an input to scorer + # for example, "processor = lambda x: x[0]" would return the first element in a collection x (of, say, strings) + # this would then be used in the scoring collection + + if choices is None or len(choices) == 0: + return [] + + # default, turn whatever the choice is into a string + if processor is None: + processor = lambda x: utils.asciidammit(x) + + # default: wratio + if scorer is None: + scorer = WRatio + + sl = list() + + for choice in choices: + processed = processor(choice) + score = scorer(query, processed) + tuple = (choice, score) + sl.append(tuple) + + sl.sort(key=lambda i: -1*i[1]) + return sl[:limit] + +########################## +# Find Single Best Match # +########################## + +def extractOne(query, choices, processor=None, scorer=None, score_cutoff=0): + + # convenience method which returns the single best choice + # optional parameter: score_cutoff. + # If the best choice has a score of less than score_cutoff + # we will return none (intuition: not a good enough match) + + best_list = extract(query, choices, processor, scorer, limit=1) + if len(best_list) > 0: + best = best_list[0] + if best[1] > score_cutoff: + return best + else: + return None + else: + return None diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/tests.py b/src/sphobjinv/_vendored/fuzzywuzzy/tests.py new file mode 100644 index 00000000..f647e970 --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/tests.py @@ -0,0 +1,285 @@ +# -*- coding: utf8 -*- + +from sphobjinv._vendored.fuzzywuzzy.fuzz import * +from sphobjinv._vendored.fuzzywuzzy import process +from sphobjinv._vendored.fuzzywuzzy import utils + +import itertools +import unittest + +class UtilsTest(unittest.TestCase): + def setUp(self): + self.s1 = "new york mets" + self.s1a = "new york mets" + self.s2 = "new YORK mets" + self.s3 = "the wonderful new york mets" + self.s4 = "new york mets vs atlanta braves" + self.s5 = "atlanta braves vs new york mets" + self.s6 = "new york mets - atlanta braves" + self.mixed_strings = [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.", + "C'est la vie", + "Ça va?", + "Cães danados", + u"\xacCamarões assados", + u"a\xac\u1234\u20ac\U00008000", + u"\u00C1" + ] + + + def tearDown(self): + pass + + def test_asciidammit(self): + for s in self.mixed_strings: + utils.asciidammit(s) + + def test_asciionly(self): + for s in self.mixed_strings: + # ascii only only runs on strings + s = utils.asciidammit(s) + utils.asciionly(s) + + def test_fullProcess(self): + for s in self.mixed_strings: + utils.full_process(s) + +class RatioTest(unittest.TestCase): + + def setUp(self): + self.s1 = "new york mets" + self.s1a = "new york mets" + self.s2 = "new YORK mets" + self.s3 = "the wonderful new york mets" + self.s4 = "new york mets vs atlanta braves" + self.s5 = "atlanta braves vs new york mets" + self.s6 = "new york mets - atlanta braves" + + self.cirque_strings = [ + "cirque du soleil - zarkana - las vegas", + "cirque du soleil ", + "cirque du soleil las vegas", + "zarkana las vegas", + "las vegas cirque du soleil at the bellagio", + "zarakana - cirque du soleil - bellagio" + ] + + self.baseball_strings = [ + "new york mets vs chicago cubs", + "chicago cubs vs chicago white sox", + "philladelphia phillies vs atlanta braves", + "braves vs mets", + ] + + def tearDown(self): + pass + + def testEqual(self): + self.assertEqual(ratio(self.s1, self.s1a),100) + + def testCaseInsensitive(self): + self.assertNotEqual(ratio(self.s1, self.s2),100) + self.assertEqual(ratio(utils.full_process(self.s1), utils.full_process(self.s2)),100) + + def testPartialRatio(self): + self.assertEqual(partial_ratio(self.s1, self.s3),100) + + def testTokenSortRatio(self): + self.assertEqual(token_sort_ratio(self.s1, self.s1a),100) + + def testPartialTokenSortRatio(self): + self.assertEqual(partial_token_sort_ratio(self.s1, self.s1a),100) + self.assertEqual(partial_token_sort_ratio(self.s4, self.s5),100) + + def testTokenSetRatio(self): + self.assertEqual(token_set_ratio(self.s4, self.s5),100) + + def testPartialTokenSetRatio(self): + self.assertEqual(partial_token_set_ratio(self.s4, self.s5),100) + + def testQuickRatioEqual(self): + self.assertEqual(QRatio(self.s1, self.s1a), 100) + + def testQuickRatioCaseInsensitive(self): + self.assertEqual(QRatio(self.s1, self.s2), 100) + + def testQuickRatioNotEqual(self): + self.assertNotEqual(QRatio(self.s1, self.s3), 100) + + def testWRatioEqual(self): + self.assertEqual(WRatio(self.s1, self.s1a), 100) + + def testWRatioCaseInsensitive(self): + self.assertEqual(WRatio(self.s1, self.s2), 100) + + def testWRatioPartialMatch(self): + # a partial match is scaled by .9 + self.assertEqual(WRatio(self.s1, self.s3), 90) + + def testWRatioMisorderedMatch(self): + # misordered full matches are scaled by .95 + self.assertEqual(WRatio(self.s4, self.s5), 95) + + def testWRatioUnicode(self): + self.assertEqual(WRatio(unicode(self.s1), unicode(self.s1a)), 100) + + def testQRatioUnicode(self): + self.assertEqual(WRatio(unicode(self.s1), unicode(self.s1a)), 100) + + def testIssueSeven(self): + s1 = "HSINCHUANG" + s2 = "SINJHUAN" + s3 = "LSINJHUANG DISTRIC" + s4 = "SINJHUANG DISTRICT" + + self.assertGreater(partial_ratio(s1, s2), 75) + self.assertGreater(partial_ratio(s1, s3), 75) + self.assertGreater(partial_ratio(s1, s4), 75) + + def testWRatioUnicodeString(self): + s1 = u"\u00C1" + s2 = "ABCD" + score = WRatio(s1, s2) + self.assertEqual(0, score) + + def testQRatioUnicodeString(self): + s1 = u"\u00C1" + s2 = "ABCD" + score = QRatio(s1, s2) + self.assertEqual(0, score) + + # test processing methods + def testGetBestChoice1(self): + query = "new york mets at atlanta braves" + best = process.extractOne(query, self.baseball_strings) + self.assertEqual(best[0], "braves vs mets") + + def testGetBestChoice2(self): + query = "philadelphia phillies at atlanta braves" + best = process.extractOne(query, self.baseball_strings) + self.assertEqual(best[0], self.baseball_strings[2]) + + def testGetBestChoice3(self): + query = "atlanta braves at philadelphia phillies" + best = process.extractOne(query, self.baseball_strings) + self.assertEqual(best[0], self.baseball_strings[2]) + + def testGetBestChoice4(self): + query = "chicago cubs vs new york mets" + best = process.extractOne(query, self.baseball_strings) + self.assertEqual(best[0], self.baseball_strings[0]) + +class ProcessTest(unittest.TestCase): + + def setUp(self): + self.s1 = "new york mets" + self.s1a = "new york mets" + self.s2 = "new YORK mets" + self.s3 = "the wonderful new york mets" + self.s4 = "new york mets vs atlanta braves" + self.s5 = "atlanta braves vs new york mets" + self.s6 = "new york mets - atlanta braves" + + self.cirque_strings = [ + "cirque du soleil - zarkana - las vegas", + "cirque du soleil ", + "cirque du soleil las vegas", + "zarkana las vegas", + "las vegas cirque du soleil at the bellagio", + "zarakana - cirque du soleil - bellagio" + ] + + self.baseball_strings = [ + "new york mets vs chicago cubs", + "chicago cubs vs chicago white sox", + "philladelphia phillies vs atlanta braves", + "braves vs mets", + ] + + def testWithProcessor(self): + events = [ + ["chicago cubs vs new york mets", "CitiField", "2011-05-11", "8pm"], + ["new york yankees vs boston red sox", "Fenway Park", "2011-05-11", "8pm"], + ["atlanta braves vs pittsburgh pirates", "PNC Park", "2011-05-11", "8pm"], + ] + query = "new york mets vs chicago cubs" + processor = lambda event: event[0] + + best = process.extractOne(query, events, processor=processor) + self.assertEqual(best[0], events[0]) + + def testWithScorer(self): + choices = [ + "new york mets vs chicago cubs", + "chicago cubs at new york mets", + "atlanta braves vs pittsbugh pirates", + "new york yankees vs boston red sox" + ] + + # in this hypothetical example we care about ordering, so we use quick ratio + query = "new york mets at chicago cubs" + scorer = QRatio + + # first, as an example, the normal way would select the "more 'complete' match of choices[1]" + + best = process.extractOne(query, choices) + self.assertEqual(best[0], choices[1]) + + # now, use the custom scorer + + best = process.extractOne(query, choices, scorer=scorer) + self.assertEqual(best[0], choices[0]) + + def testWithCutoff(self): + choices = [ + "new york mets vs chicago cubs", + "chicago cubs at new york mets", + "atlanta braves vs pittsbugh pirates", + "new york yankees vs boston red sox" + ] + + query = "los angeles dodgers vs san francisco giants" + + # in this situation, this is an event that does not exist in the list + # we don't want to randomly match to something, so we use a reasonable cutoff + + best = process.extractOne(query, choices, score_cutoff=50) + self.assertTrue(best is None) + #self.assertIsNone(best) # unittest.TestCase did not have assertIsNone until Python 2.7 + + # however if we had no cutoff, something would get returned + + #best = process.extractOne(query, choices) + #self.assertIsNotNone(best) + + def testEmptyStrings(self): + choices = [ + "", + "new york mets vs chicago cubs", + "new york yankees vs boston red sox", + "", + "" + ] + + query = "new york mets at chicago cubs" + + best = process.extractOne(query, choices) + self.assertEqual(best[0], choices[1]) + + def testNullStrings(self): + choices = [ + None, + "new york mets vs chicago cubs", + "new york yankees vs boston red sox", + None, + None + ] + + query = "new york mets at chicago cubs" + + best = process.extractOne(query, choices) + self.assertEqual(best[0], choices[1]) + + +if __name__ == '__main__': + unittest.main() # run all tests \ No newline at end of file diff --git a/src/sphobjinv/_vendored/fuzzywuzzy/utils.py b/src/sphobjinv/_vendored/fuzzywuzzy/utils.py new file mode 100644 index 00000000..ab756b42 --- /dev/null +++ b/src/sphobjinv/_vendored/fuzzywuzzy/utils.py @@ -0,0 +1,39 @@ +import string + +bad_chars=b"" # B Skinn 2021-12-11 +for i in range(128,256): + bad_chars+=chr(i).encode() # B Skinn 2021-12-11 +table_from=string.punctuation+string.ascii_uppercase +table_to=' '*len(string.punctuation)+string.ascii_lowercase +trans_table=bytes.maketrans(table_from.encode(), table_to.encode()) # B Skinn 2021-12-11 + + +def asciionly(s): + return s.encode().translate(None, bad_chars).decode() # B Skinn 2021-12-11 + +# remove non-ASCII characters from strings +def asciidammit(s): + if type(s) is str: + return asciionly(s) + elif type(s) is unicode: + return asciionly(s.encode('ascii', 'ignore')) + else: + return asciidammit(unicode(s)) + +def validate_string(s): + try: + if len(s)>0: + return True + else: + return False + except: + return False + +def full_process(s): + s = asciidammit(s) + # B Skinn 2021-12-11 + return s.encode().translate(trans_table, bad_chars).decode().strip() + + + + diff --git a/src/sphobjinv/cli/__init__.py b/src/sphobjinv/cli/__init__.py index db4f8894..8037cfa8 100644 --- a/src/sphobjinv/cli/__init__.py +++ b/src/sphobjinv/cli/__init__.py @@ -10,7 +10,7 @@ 15 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/cli/core.py b/src/sphobjinv/cli/core.py index 0c1a5904..731f1f1a 100644 --- a/src/sphobjinv/cli/core.py +++ b/src/sphobjinv/cli/core.py @@ -10,7 +10,7 @@ 15 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv @@ -89,7 +89,7 @@ def do_suggest(inv, params): all of the returned results unless |cli:ALL| is specified. - No |cour|\ -\\-quiet\ |/cour| option is available here, since + No |cli:QUIET| option is available here, since a silent mode for suggestion output is nonsensical. Parameters diff --git a/src/sphobjinv/cli/load.py b/src/sphobjinv/cli/load.py index 671af7b0..0a5d37cf 100644 --- a/src/sphobjinv/cli/load.py +++ b/src/sphobjinv/cli/load.py @@ -1,4 +1,4 @@ -r"""Module for ``sphobjinv`` *CLI |Inventory| loading*. +r"""*Module for* ``sphobjinv`` *CLI* |Inventory| *loading*. ``sphobjinv`` is a toolkit for manipulation and inspection of Sphinx |objects.inv| files. @@ -10,7 +10,7 @@ 17 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/cli/parser.py b/src/sphobjinv/cli/parser.py index 5d1fe4d3..e959b47d 100644 --- a/src/sphobjinv/cli/parser.py +++ b/src/sphobjinv/cli/parser.py @@ -10,7 +10,7 @@ 15 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv @@ -40,7 +40,7 @@ class PrsConst: #: Version &c. output blurb VER_TXT = ( - f"\nsphobjinv v{__version__}\n\nCopyright (c) Brian Skinn 2016-2021\n" + f"\nsphobjinv v{__version__}\n\nCopyright (c) Brian Skinn 2016-2022\n" "License: The MIT License\n\n" "Bug reports & feature requests:" " https://github.com/bskinn/sphobjinv\n" @@ -187,7 +187,7 @@ def getparser(): prs :class:`~argparse.ArgumentParser` -- Parser for commandline usage - of ``sphobjinv`` + of |soi| """ prs = ap.ArgumentParser( diff --git a/src/sphobjinv/cli/paths.py b/src/sphobjinv/cli/paths.py index 4846887c..5ed69b99 100644 --- a/src/sphobjinv/cli/paths.py +++ b/src/sphobjinv/cli/paths.py @@ -10,7 +10,7 @@ 19 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/cli/ui.py b/src/sphobjinv/cli/ui.py index fee27b8b..ee838e73 100644 --- a/src/sphobjinv/cli/ui.py +++ b/src/sphobjinv/cli/ui.py @@ -10,7 +10,7 @@ 19 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/cli/write.py b/src/sphobjinv/cli/write.py index ccc3773d..e828c49f 100644 --- a/src/sphobjinv/cli/write.py +++ b/src/sphobjinv/cli/write.py @@ -1,4 +1,4 @@ -r"""Module for ``sphobjinv`` *CLI |Inventory| writing*. +r"""*Module for* ``sphobjinv`` *CLI* |Inventory| *writing*. ``sphobjinv`` is a toolkit for manipulation and inspection of Sphinx |objects.inv| files. @@ -10,7 +10,7 @@ 19 Nov 2020 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/data.py b/src/sphobjinv/data.py index df6f7f0d..3674b26a 100644 --- a/src/sphobjinv/data.py +++ b/src/sphobjinv/data.py @@ -10,7 +10,7 @@ 7 Nov 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/enum.py b/src/sphobjinv/enum.py index f7e3da1b..1128e0c2 100644 --- a/src/sphobjinv/enum.py +++ b/src/sphobjinv/enum.py @@ -10,7 +10,7 @@ 4 May 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/error.py b/src/sphobjinv/error.py index cf5f19ed..6731d5cd 100644 --- a/src/sphobjinv/error.py +++ b/src/sphobjinv/error.py @@ -10,7 +10,7 @@ 5 Nov 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/fileops.py b/src/sphobjinv/fileops.py index 1dc241e1..c35d550a 100644 --- a/src/sphobjinv/fileops.py +++ b/src/sphobjinv/fileops.py @@ -10,7 +10,7 @@ 5 Nov 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/inventory.py b/src/sphobjinv/inventory.py index 4492a96f..270481e4 100644 --- a/src/sphobjinv/inventory.py +++ b/src/sphobjinv/inventory.py @@ -10,7 +10,7 @@ 7 Dec 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv @@ -28,7 +28,6 @@ import re import ssl import urllib.request as urlrq -import warnings from zlib import error as zlib_error import attr @@ -452,7 +451,7 @@ def suggest(self, name, *, thresh=50, with_index=False, with_score=False): r"""Suggest objects in the inventory to match a name. :meth:`~Inventory.suggest` makes use of - the powerful pattern-matching library |fuzzywuzzy|_ + the edit-distance scoring library |fuzzywuzzy|_ to identify potential matches to the given `name` within the inventory. The search is performed over the |list| of |str| @@ -517,15 +516,7 @@ def suggest(self, name, *, thresh=50, with_index=False, with_score=False): |cour|\ (as_rst, score, index)\ |/cour| """ - # Suppress any UserWarning about the speed issue - with warnings.catch_warnings(): - warnings.filterwarnings( - action="ignore", - message="Using slow.+levenshtein", - category=UserWarning, - module="fuzz", - ) - from fuzzywuzzy import process as fwp + from sphobjinv._vendored.fuzzywuzzy import process as fwp # Must propagate list index to include in output # Search vals are rst prepended with list index diff --git a/src/sphobjinv/re.py b/src/sphobjinv/re.py index 39b49824..fbbe58a7 100644 --- a/src/sphobjinv/re.py +++ b/src/sphobjinv/re.py @@ -10,7 +10,7 @@ 5 Nov 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/schema.py b/src/sphobjinv/schema.py index 0812110e..39119ca7 100644 --- a/src/sphobjinv/schema.py +++ b/src/sphobjinv/schema.py @@ -11,7 +11,7 @@ 7 Dec 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/src/sphobjinv/version.py b/src/sphobjinv/version.py index 57b516d5..7dcc7e78 100644 --- a/src/sphobjinv/version.py +++ b/src/sphobjinv/version.py @@ -10,7 +10,7 @@ 18 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv @@ -25,4 +25,4 @@ """ -__version__ = "2.1" +__version__ = "2.2" diff --git a/src/sphobjinv/zlib.py b/src/sphobjinv/zlib.py index be329fd8..92a724fa 100644 --- a/src/sphobjinv/zlib.py +++ b/src/sphobjinv/zlib.py @@ -10,7 +10,7 @@ 5 Nov 2017 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** https://github.com/bskinn/sphobjinv diff --git a/tests/test_api_fail.py b/tests/test_api_fail.py index f1a82d54..0dc975ad 100644 --- a/tests/test_api_fail.py +++ b/tests/test_api_fail.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_api_good.py b/tests/test_api_good.py index 0c97ecdb..0bfa29a3 100644 --- a/tests/test_api_good.py +++ b/tests/test_api_good.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv @@ -28,7 +28,6 @@ import copy import itertools as itt import re -import warnings from numbers import Number import dictdiffer @@ -581,41 +580,3 @@ def test_api_inventory_one_object_flatdict(self): # Should not raise an exception; assert is to emphasize this is the check assert soi.Inventory(inv.json_dict()) - - -class TestWarnings: - """Tests for warnings emitted by dependencies.""" - - # The python-Levenshtein warning is only emitted the first time - # fuzzywuzzy.process is imported in a given pytest session. - # Thus, this test *MUST* be run first, in order for the warning - # to be detected in the test. - @pytest.mark.first - def test_api_fuzzywuzzy_warningcheck(self, misc_info): - """Confirm only the Levenshtein warning is raised, if any are.""" - if misc_info.IN_PYPY: - pytest.skip("Don't test warnings in PyPy") # pragma: no cover - - with warnings.catch_warnings(record=True) as wc: - warnings.simplefilter("always") - from fuzzywuzzy import process # noqa: F401 - - # Try to import, and adjust tests accordingly - try: - import Levenshtein # noqa: F401 - except ImportError: - lev_present = False - else: - # Standard testing setup is WITHOUT python-Levenshtein - lev_present = True # pragma: no cover - - if lev_present: - assert len(wc) == 0, "Warning unexpectedly raised" # pragma: no cover - else: - assert len(wc) == 1, "Warning unexpectedly not raised" - - # 'message' will be a Warning instance, thus 'args[0]' - # to retrieve the warning message as str. - assert ( - "levenshtein" in wc[0].message.args[0].lower() - ), "Warning raised for unexpected reason" diff --git a/tests/test_api_good_nonlocal.py b/tests/test_api_good_nonlocal.py index 3f6a28df..e11163fe 100644 --- a/tests/test_api_good_nonlocal.py +++ b/tests/test_api_good_nonlocal.py @@ -10,7 +10,7 @@ 21 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_cli.py b/tests/test_cli.py index 4a3ee1cf..145cc1a9 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_cli_nonlocal.py b/tests/test_cli_nonlocal.py index 8978a13d..6b0c78ed 100644 --- a/tests/test_cli_nonlocal.py +++ b/tests/test_cli_nonlocal.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_fixture.py b/tests/test_fixture.py index 65853790..83616aa8 100644 --- a/tests/test_fixture.py +++ b/tests/test_fixture.py @@ -10,7 +10,7 @@ 20 Mar 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_flake8_ext.py b/tests/test_flake8_ext.py index 7f0593d2..00bae72f 100644 --- a/tests/test_flake8_ext.py +++ b/tests/test_flake8_ext.py @@ -10,7 +10,7 @@ 27 Apr 2019 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tests/test_readme.py b/tests/test_readme.py index 682c8c7a..9070cd3a 100644 --- a/tests/test_readme.py +++ b/tests/test_readme.py @@ -10,7 +10,7 @@ 6 Aug 2018 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv @@ -26,6 +26,7 @@ """ import doctest as dt +import platform import re import shlex import subprocess as sp # noqa: S404 @@ -56,6 +57,10 @@ sphinx_ver != sphinx_req, reason="Skip if Sphinx version mismatches current dev version.", ) +@pytest.mark.skipif( + "pypy" in platform.python_implementation().lower(), + reason="Inconsistent suggest results on PyPy", +) def test_readme_shell_cmds(ensure_doc_scratch, check): """Perform testing on README shell command examples.""" text = Path("README.rst").read_text() diff --git a/tests/test_valid_objects.py b/tests/test_valid_objects.py index c4702dd7..5e3930cd 100644 --- a/tests/test_valid_objects.py +++ b/tests/test_valid_objects.py @@ -10,7 +10,7 @@ 13 Feb 2021 **Copyright** - \(c) Brian Skinn 2016-2021 + \(c) Brian Skinn 2016-2022 **Source Repository** http://www.github.com/bskinn/sphobjinv diff --git a/tox.ini b/tox.ini index ef1627fe..c9448bd0 100644 --- a/tox.ini +++ b/tox.ini @@ -2,15 +2,14 @@ minversion=2.0 isolated_build=True envlist= - py3_{06,07,08,09,10}-sphx_latest-attrs_latest-jsch_latest-fzwz_latest - py3_09-sphx_dev-attrs_dev-jsch_dev-fzwz_dev - py3_09-sphx_{1_6_x,1_x,2_x,3_x,dev}-attrs_latest-jsch_latest-fzwz_latest - py3_09-sphx_latest-attrs_{19_2,19_3,20_1,20_2,20_3,dev}-jsch_latest-fzwz_latest - py3_09-sphx_latest-attrs_latest-jsch_{3_0,3_1,dev}-fzwz_latest - py3_09-sphx_latest-attrs_latest-jsch_latest-fzwz_0_{08,10,12,14,16,17,dev} - py3_06-sphx_1_6_x-attrs_19_2-jsch_3_0-fzwz_0_08 - py3_0{7,8}-sphx_{1,2}_x-attrs_{19,20}_2-jsch_latest-fzwz_0_{10,16} - py3_09-sphx_{2_3_1,2_4_0,3_2_1,3_3_0}-attrs_latest-jsch_latest-fzwz_latest + py3{6,7,8,9,10,11}-sphx_latest-attrs_latest-jsch_latest + py310-sphx_dev-attrs_dev-jsch_dev + py310-sphx_{1_6_x,1_x,2_x,3_x,dev}-attrs_latest-jsch_latest + py310-sphx_latest-attrs_{19_2,19_3,20_1,20_2,20_3,21_2,21_3,dev}-jsch_latest + py310-sphx_latest-attrs_latest-jsch_{3_0,3_1,3_2,4_0,4_1,dev} + py36-sphx_1_6_x-attrs_19_2-jsch_3_0 + py3{7,8,9}-sphx_{1,2}_x-attrs_{19,20}_2-jsch_latest + py310-sphx_{2_3_1,2_4_0,3_2_1,3_3_0,3_4_0}-attrs_latest-jsch_latest sdist_install flake8 @@ -18,7 +17,7 @@ envlist= commands= python --version pip list - pytest {posargs:--nonloc} + pytest {posargs:--nonloc -Wignore} deps= sphx_1_6_x: sphinx<1.7 sphx_1_x: sphinx<2 @@ -28,6 +27,7 @@ deps= sphx_2_4_0: sphinx==2.4.0 sphx_3_2_1: sphinx==3.2.1 sphx_3_3_0: sphinx==3.3.0 + sphx_3_4_0: sphinx==3.4.0 sphx_latest: sphinx sphx_dev: git+https://github.com/sphinx-doc/sphinx @@ -36,27 +36,21 @@ deps= attrs_20_1: attrs==20.1 attrs_20_2: attrs==20.2 attrs_20_3: attrs==20.3 + attrs_21_2: attrs==21.2 + attrs_21_3: attrs==21.3 attrs_latest: attrs attrs_dev: git+https://github.com/python-attrs/attrs jsch_3_0: jsonschema<3.1 jsch_3_1: jsonschema<3.2 + jsch_3_2: jsonschema<3.3 + jsch_4_0: jsonschema<4.1 + jsch_4_1: jsonschema<4.2 + jsch_4_2: jsonschema<4.3 + jsch_4_3: jsonschema<4.4 jsch_latest: jsonschema jsch_dev: git+https://github.com/Julian/jsonschema - fzwz_0_08: fuzzywuzzy==0.8 - fzwz_0_09: fuzzywuzzy==0.9 - fzwz_0_10: fuzzywuzzy==0.10 - fzwz_0_11: fuzzywuzzy==0.11 - fzwz_0_12: fuzzywuzzy==0.12 - fzwz_0_13: fuzzywuzzy==0.13 - fzwz_0_14: fuzzywuzzy==0.14 - fzwz_0_15: fuzzywuzzy==0.15 - fzwz_0_16: fuzzywuzzy==0.16 - fzwz_0_17: fuzzywuzzy==0.17 - fzwz_latest: fuzzywuzzy - fzwz_dev: git+https://github.com/seatgeek/fuzzywuzzy - dictdiffer pytest>=4.4.0 pytest-check>=0.4 @@ -67,24 +61,16 @@ deps= sphinx-rtd-theme sphinxcontrib-programoutput -[testenv:win] -platform=win -basepython= - py3_09: python3.9 - py3_08: python3.8 - py3_07: python3.7 - py3_06: python3.6 - py3_05: python3.5 - [testenv:linux] platform=linux basepython= - py3_10: python3.10 - py3_09: python3.9 - py3_08: python3.8 - py3_07: python3.7 - py3_06: python3.6 - py3_05: python3.5 + py311: python3.11 + py310: python3.10 + py39: python3.9 + py38: python3.8 + py37: python3.7 + py36: python3.6 + py35: python3.5 [testenv:flake8] skip_install=True @@ -131,6 +117,9 @@ xfail_strict = True [flake8] +exclude = + src/sphobjinv/_vendored + # W503: black formats binary operators to start of line ignore = W503 show_source = True