diff --git a/.github/.gitignore b/.github/.gitignore deleted file mode 100644 index b9bdbd9..0000000 --- a/.github/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Ignore everything -* - -# Except -!actions/ -!actions/* -!ISSUE_TEMPLATE/ -!ISSUE_TEMPLATE/* -!workflows/ -!workflows/* - -!.gitignore diff --git a/.github/ISSUE_TEMPLATE/.gitignore b/.github/ISSUE_TEMPLATE/.gitignore deleted file mode 100644 index fc83fe4..0000000 --- a/.github/ISSUE_TEMPLATE/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore everything -* - -# Except -!bug_report.yml -!feature_request.yml -!question.md - -!.gitignore diff --git a/.github/actions/.gitignore b/.github/actions/.gitignore deleted file mode 100644 index 4769b9d..0000000 --- a/.github/actions/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Ignore everything -* - -# Except -!build_regex_toolkit/ -!build_regex_toolkit/* -!run-tests/ -!run-tests/* -!setup-conda/ -!setup-conda/* - -!.gitignore diff --git a/.github/actions/build_regex_toolkit/.gitignore b/.github/actions/build_regex_toolkit/.gitignore deleted file mode 100644 index 34b5ce3..0000000 --- a/.github/actions/build_regex_toolkit/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Ignore everything -* - -# Except -!action.yml - -!.gitignore diff --git a/.github/actions/run-tests/.gitignore b/.github/actions/run-tests/.gitignore deleted file mode 100644 index 34b5ce3..0000000 --- a/.github/actions/run-tests/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Ignore everything -* - -# Except -!action.yml - -!.gitignore diff --git a/.github/actions/setup-conda/.gitignore b/.github/actions/setup-conda/.gitignore deleted file mode 100644 index 34b5ce3..0000000 --- a/.github/actions/setup-conda/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Ignore everything -* - -# Except -!action.yml - -!.gitignore diff --git a/.github/workflows/.gitignore b/.github/workflows/.gitignore deleted file mode 100644 index dcba760..0000000 --- a/.github/workflows/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore everything -* - -# Except -!stale-pr.yml -!ubuntu.yml -windows-macos.yml - -!.gitignore diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 31b8edd..271e53c 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -4,7 +4,7 @@ on: push: branches: ["main"] pull_request: - branches: ["main"] + branches: ["main", "*.*.*-release"] paths-ignore: ["docs/**"] env: @@ -15,22 +15,24 @@ permissions: jobs: run-tests: - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} defaults: run: shell: bash -el {0} timeout-minutes: 60 strategy: matrix: + os: ["ubuntu-latest"] python_version: ["310", "311", "312"] + # python_version: ["310", "311", "312", "313"] fail-fast: false - name: Python ${{ matrix.python_version }} + name: ${{ format('{0} Python {1}', matrix.os, matrix.python_version) }} env: ENV_FILE: ci/deps/actions-${{ matrix.python_version }}.yml IS_PYPY: ${{ contains(matrix.python_version, 'pypy') }} concurrency: # https://github.community/t/concurrecy-not-work-for-push/183068/7 - group: ${{ github.event_name == 'push' && github.run_number || github.ref }}-${{ matrix.python_version }} + group: ${{ github.event_name == 'push' && github.run_number || github.ref }}-${{ matrix.os }}-${{ matrix.python_version }} cancel-in-progress: true steps: - name: Checkout @@ -39,7 +41,7 @@ jobs: - name: Set up Conda uses: ./.github/actions/setup-conda with: - environment-name: ${{ matrix.python_version }}-env + environment-name: ${{ matrix.os }}-${{ matrix.python_version }}-env environment-file: ${{ env.ENV_FILE }} - name: Build Regex-Toolkit diff --git a/.gitignore b/.gitignore index b04526a..d9c96b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,165 @@ -# Ignore everything -* - -# Except -!setup.py -!pyproject.toml -!Makefile -!environment.yml -!codecov.yml -!requirements-doc.txt -!requirements-test.txt - -!src/ -!src/* -!tests/ -!tests/* -!ci/ -!ci/* -!docs/ -!docs/* - -!LICENSE -!README.md -!.gitignore - -!.git/ -!.git/* - -!.github/ -!.github/* +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +.idea/ + +# Common user directories +old/ +todo/ + +# Pytest test data +test-data.xml diff --git a/Makefile b/Makefile index 9115bef..d08d717 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ lint: format: @echo 'Formatting code' - ${PYTHON} -m isort src tests docs/render_readme.py - ${PYTHON} -m black src tests docs/render_readme.py + ${PYTHON} -m isort --sp pyproject.toml src tests docs/render_readme.py + ${PYTHON} -m black --config pyproject.toml src tests docs/render_readme.py @echo 'Done' build: diff --git a/README.md b/README.md index d4ab9c9..1380d83 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ You should have received a copy of the GNU General Public License along with thi --- -[Requirements](#requirements) -[Installing](#installing) -[Usage](#usage) -[Library](#library) +- [Requirements](#requirements) +- [Installing](#installing) +- [Usage](#usage) +- [Library](#library) ## Installing @@ -425,12 +425,12 @@ rtk.escape("/", flavor=2) - `ValueError` - Invalid regex flavor. - `TypeError` - Invalid type for `char`. - + -#### `string_as_exp` +#### `string_as_expr` ```python -def string_as_exp(text: str, flavor: int | None = None) -> str +def string_as_expr(text: str, flavor: int | None = None) -> str ``` Create a regex expression that exactly matches a string. @@ -440,10 +440,10 @@ Create a regex expression that exactly matches a string. ```python import regex_toolkit as rtk -rtk.string_as_exp("http://www.example.com") +rtk.string_as_expr("http://www.example.com") # Output: 'https\:\/\/example\.com' -rtk.string_as_exp("http://www.example.com", flavor=2) +rtk.string_as_expr("http://www.example.com", flavor=2) # Output: 'https\x{003a}\x{002f}\x{002f}example\.com' ``` @@ -460,12 +460,12 @@ rtk.string_as_exp("http://www.example.com", flavor=2) - `ValueError` - Invalid regex flavor. - + -#### `strings_as_exp` +#### `strings_as_expr` ```python -def strings_as_exp(texts: Iterable[str], flavor: int | None = None) -> str +def strings_as_expr(texts: Iterable[str], flavor: int | None = None) -> str ``` Create a regex expression that exactly matches any one string. @@ -475,10 +475,10 @@ Create a regex expression that exactly matches any one string. ```python import regex_toolkit as rtk -rtk.strings_as_exp(["apple", "banana", "cherry"]) +rtk.strings_as_expr(["apple", "banana", "cherry"]) # Output: 'banana|cherry|apple' -rtk.strings_as_exp(["apple", "banana", "cherry"], flavor=2) +rtk.strings_as_expr(["apple", "banana", "cherry"], flavor=2) # Output: 'banana|cherry|apple' ``` @@ -495,12 +495,12 @@ rtk.strings_as_exp(["apple", "banana", "cherry"], flavor=2) - `ValueError` - Invalid regex flavor. - + -#### `make_exp` +#### `make_expr` ```python -def make_exp(chars: Iterable[str], flavor: int | None = None) -> str +def make_expr(chars: Iterable[str], flavor: int | None = None) -> str ``` Create a regex expression that exactly matches a list of characters. @@ -513,10 +513,10 @@ The expression is not anchored, so it can be used as part of a larger expression ```python import regex_toolkit as rtk -"[" + rtk.make_exp(["a", "b", "c", "z", "y", "x"]) + "]" +"[" + rtk.make_expr(["a", "b", "c", "z", "y", "x"]) + "]" # Output: '[a-cx-z]' -"[" + rtk.make_exp(["a", "b", "c", "z", "y", "x"], flavor=2) + "]" +"[" + rtk.make_expr(["a", "b", "c", "z", "y", "x"], flavor=2) + "]" # Output: '[a-cx-z]' ``` diff --git a/ci/.gitignore b/ci/.gitignore deleted file mode 100644 index 10b884c..0000000 --- a/ci/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Ignore everything -* - -# Except -!run_tests.sh -!pre_commit.sh -!condarc.yml - -!deps/ -!deps/* - -!.gitignore diff --git a/ci/condarc.yml b/ci/condarc.yml index 051ed76..9d750b7 100644 --- a/ci/condarc.yml +++ b/ci/condarc.yml @@ -1,8 +1,5 @@ # https://docs.conda.io/projects/conda/en/latest/configuration.html -channels: - - conda-forge - # always_yes (NoneType, bool) # aliases: yes # Automatically choose the 'yes' option whenever asked to proceed with a diff --git a/ci/deps/.gitignore b/ci/deps/.gitignore deleted file mode 100644 index 5ec9a8f..0000000 --- a/ci/deps/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Ignore everything -* - -# Except -!actions-310.yml -!actions-311.yml -!actions-312.yml -!actions-313.yml - -!.gitignore diff --git a/ci/deps/actions-310.yml b/ci/deps/actions-310.yml index 6c933fb..894f672 100644 --- a/ci/deps/actions-310.yml +++ b/ci/deps/actions-310.yml @@ -6,7 +6,6 @@ dependencies: # Test dependencies - pytest>=7.0.0 - - pytest-cov - - pytest-xdist>=2.2.0 + - pytest-cov>=6.0.0 - pip: - google-re2>=1.0 diff --git a/ci/deps/actions-311.yml b/ci/deps/actions-311.yml index e663081..0046a2f 100644 --- a/ci/deps/actions-311.yml +++ b/ci/deps/actions-311.yml @@ -6,7 +6,6 @@ dependencies: # Test dependencies - pytest>=7.0.0 - - pytest-cov - - pytest-xdist>=2.2.0 + - pytest-cov>=6.0.0 - pip: - google-re2>=1.0 diff --git a/ci/deps/actions-312.yml b/ci/deps/actions-312.yml index c67a9d6..0e4eea7 100644 --- a/ci/deps/actions-312.yml +++ b/ci/deps/actions-312.yml @@ -6,7 +6,6 @@ dependencies: # Test dependencies - pytest>=7.0.0 - - pytest-cov - - pytest-xdist>=2.2.0 + - pytest-cov>=6.0.0 - pip: - google-re2>=1.0 diff --git a/ci/deps/actions-313.yml b/ci/deps/actions-313.yml new file mode 100644 index 0000000..08ca9ae --- /dev/null +++ b/ci/deps/actions-313.yml @@ -0,0 +1,11 @@ +name: regex_toolkit +channels: + - conda-forge +dependencies: + - python=3.13 + + # Test dependencies + - pytest>=7.0.0 + - pytest-cov>=6.0.0 + - pip: + - google-re2>=1.0 diff --git a/ci/run_tests.sh b/ci/run_tests.sh index bb97064..61e9614 100755 --- a/ci/run_tests.sh +++ b/ci/run_tests.sh @@ -1,12 +1,8 @@ #!/bin/bash -e -# Workaround for pytest-xdist (it collects different tests in the workers if PYTHONHASHSEED is not set) -# https://github.com/pytest-dev/pytest/issues/920 -# https://github.com/pytest-dev/pytest/issues/1075 -export PYTHONHASHSEED=$(python -c 'import random; print(random.randint(1, 4294967295))') - -# May help reproduce flaky CI builds if set in subsequent runs -echo PYTHONHASHSEED=$PYTHONHASHSEED +PYTHONHASHSEED=$(python -c 'import random; print(random.randint(1, 4294967295))') +export PYTHONHASHSEED +echo "PYTHONHASHSEED=$PYTHONHASHSEED" # If no X server is found, we use xvfb to emulate it if [[ $(uname) == "Linux" && -z $DISPLAY ]]; then @@ -14,9 +10,8 @@ if [[ $(uname) == "Linux" && -z $DISPLAY ]]; then XVFB="xvfb-run " fi -# TODO: Consider adding as an input parameter PYTEST_TARGET=tests PYTEST_CMD="${XVFB}pytest -r fEs -s --cov=src --cov-report=xml --cov-append $PYTEST_TARGET" -echo $PYTEST_CMD +echo "$PYTEST_CMD" sh -c "$PYTEST_CMD" diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 95f63f7..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Ignore everything -* - -# Except -!render_readme.py -!config.json - -!templates/ -!templates/* - -!.gitignore diff --git a/docs/templates/.gitignore b/docs/templates/.gitignore index 2e53636..bd5355b 100644 --- a/docs/templates/.gitignore +++ b/docs/templates/.gitignore @@ -1,14 +1 @@ -# Ignore everything -* - -# Except -!body.md.jinja -!header.md.jinja -!footer.md.jinja -!install.md.jinja -!library.md.jinja -!requirements.md.jinja -!usage.md.jinja -!main.md.jinja - -!.gitignore +rendered_libs.md diff --git a/docs/templates/body.md.jinja b/docs/templates/body.md.jinja index 10b23bc..41da2e8 100644 --- a/docs/templates/body.md.jinja +++ b/docs/templates/body.md.jinja @@ -4,10 +4,10 @@ --- -[Requirements](#requirements) -[Installing](#installing) -[Usage](#usage) -[Library](#library) +- [Requirements](#requirements) +- [Installing](#installing) +- [Usage](#usage) +- [Library](#library) ## Installing diff --git a/environment.yml b/environment.yml index 09760df..17a5119 100644 --- a/environment.yml +++ b/environment.yml @@ -1,21 +1,20 @@ # Local development dependencies including docs building, website upload, ASV benchmark -name: regex_toolkit +name: regex-toolkit channels: - conda-forge dependencies: - python=3.10 - pip - # Test dependencies - - pytest>=7.0.0 - - pytest-cov - - pytest-xdist>=2.2.0 - - coverage - - pip: - - google-re2>=1.0 - # Code checks - black=22.10.0 - - flake8=6.0.0 + - pylint>=3.0.0 - isort>=5.2.1 - mypy=1.0 + - codespell>=2.0.0 + + # Test dependencies + - pytest>=7.0.0 + - pytest-cov>=6.0.0 + - pip: + - google-re2>=1.0 diff --git a/pyproject.toml b/pyproject.toml index bce4035..cc48fca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Utilities", "Topic :: Software Development", "Topic :: Software Development :: Libraries", @@ -47,10 +48,7 @@ branch = true source = ["src"] [tool.coverage.paths] -source = [ - "src", - # "*/site-packages" -] +source = ["src"] [tool.coverage.report] ignore_errors = false @@ -78,7 +76,7 @@ max-line-length = 88 disable = [] [tool.black] -target-version = ['py310', 'py311'] +target-version = ["py310", "py311", "py312"] exclude = ''' ( asv_bench/env @@ -135,7 +133,6 @@ sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] [tool.pytest.ini_options] -# NOTE: Have not decided on a minimum version yet minversion = "7.0" addopts = "--strict-config --strict-markers --capture=no --junitxml=test-data.xml" empty_parameter_set_mark = "fail_at_collect" @@ -153,7 +150,6 @@ doctest_optionflags = [ filterwarnings = [] junit_family = "xunit2" markers = [] -# asyncio_mode = "strict" [tool.codespell] ignore-words-list = "" # "foo, bar, baz" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..763b08a --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +black>=22.10.0 +pylint>=3.0.0 +isort>=5.2.1 +mypy>=1.0 +codespell>=2.0.0 diff --git a/requirements-doc.txt b/requirements-doc.txt index 8a39850..396fb5a 100644 --- a/requirements-doc.txt +++ b/requirements-doc.txt @@ -1,2 +1,2 @@ novella==0.2.3 -pydoc-markdown==4.6.4 \ No newline at end of file +pydoc-markdown==4.6.4 diff --git a/requirements-test.txt b/requirements-test.txt index bed0b62..46e405d 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,4 +1,3 @@ pytest>=7.0.0 -pytest-cov -pytest-xdist>=2.2.0 +pytest-cov>=6.0.0 google-re2>=1.0 diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index a17f0c1..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Ignore everything -* - -# Except -!regex_toolkit/ -!regex_toolkit/* - -!.gitignore diff --git a/src/regex_toolkit/.gitignore b/src/regex_toolkit/.gitignore deleted file mode 100644 index 69400b7..0000000 --- a/src/regex_toolkit/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Ignore everything -* - -# Except -!__init__.py -!base.py -!constants.py -!enums.py -!utils.py - -!.gitignore diff --git a/src/regex_toolkit/__init__.py b/src/regex_toolkit/__init__.py index f62e972..4c509fb 100644 --- a/src/regex_toolkit/__init__.py +++ b/src/regex_toolkit/__init__.py @@ -1,8 +1,8 @@ from .base import ( escape, - make_exp, - string_as_exp, - strings_as_exp, + make_expr, + string_as_expr, + strings_as_expr, ) from .utils import ( char_range, @@ -16,7 +16,7 @@ to_utf8, ) -__version__ = "0.1.0" +__version__ = "0.1.1" __all__ = [ "char_range", @@ -24,12 +24,12 @@ "cpoint_to_ord", "escape", "iter_char_range", - "make_exp", + "make_expr", "mask_span", "mask_spans", "ord_to_cpoint", - "string_as_exp", - "strings_as_exp", + "string_as_expr", + "strings_as_expr", "to_nfc", "to_utf8", ] diff --git a/src/regex_toolkit/base.py b/src/regex_toolkit/base.py index 0da577e..97ca90b 100644 --- a/src/regex_toolkit/base.py +++ b/src/regex_toolkit/base.py @@ -8,9 +8,9 @@ __all__ = [ "default_flavor", "escape", - "make_exp", - "string_as_exp", - "strings_as_exp", + "make_expr", + "string_as_expr", + "strings_as_expr", "resolve_flavor", ] @@ -108,15 +108,15 @@ def escape(char: str, flavor: int | None = None) -> str: return _escape2(char) -def _string_as_exp(text: str) -> str: +def _string_as_expr(text: str) -> str: return r"".join(map(_escape, text)) -def _string_as_exp2(text: str) -> str: +def _string_as_expr2(text: str) -> str: return r"".join(map(_escape2, text)) -def string_as_exp(text: str, flavor: int | None = None) -> str: +def string_as_expr(text: str, flavor: int | None = None) -> str: """Create a regex expression that exactly matches a string. Example: @@ -124,10 +124,10 @@ def string_as_exp(text: str, flavor: int | None = None) -> str: ```python import regex_toolkit as rtk - rtk.string_as_exp("http://www.example.com") + rtk.string_as_expr("http://www.example.com") # Output: 'https\\:\\/\\/example\\.com' - rtk.string_as_exp("http://www.example.com", flavor=2) + rtk.string_as_expr("http://www.example.com", flavor=2) # Output: 'https\\x{003a}\\x{002f}\\x{002f}example\\.com' ``` @@ -142,20 +142,20 @@ def string_as_exp(text: str, flavor: int | None = None) -> str: ValueError: Invalid regex flavor. """ if resolve_flavor(flavor) == RegexFlavor.RE: - return _string_as_exp(text) + return _string_as_expr(text) else: - return _string_as_exp2(text) + return _string_as_expr2(text) -def _strings_as_exp(texts: Iterable[str]) -> str: - return r"|".join(map(_string_as_exp, iter_sort_by_len_and_alpha(texts))) +def _strings_as_expr(texts: Iterable[str]) -> str: + return r"|".join(map(_string_as_expr, iter_sort_by_len_and_alpha(texts))) -def _strings_as_exp2(texts: Iterable[str]) -> str: - return r"|".join(map(_string_as_exp2, iter_sort_by_len_and_alpha(texts))) +def _strings_as_expr2(texts: Iterable[str]) -> str: + return r"|".join(map(_string_as_expr2, iter_sort_by_len_and_alpha(texts))) -def strings_as_exp(texts: Iterable[str], flavor: int | None = None) -> str: +def strings_as_expr(texts: Iterable[str], flavor: int | None = None) -> str: """Create a regex expression that exactly matches any one string. Example: @@ -163,10 +163,10 @@ def strings_as_exp(texts: Iterable[str], flavor: int | None = None) -> str: ```python import regex_toolkit as rtk - rtk.strings_as_exp(["apple", "banana", "cherry"]) + rtk.strings_as_expr(["apple", "banana", "cherry"]) # Output: 'banana|cherry|apple' - rtk.strings_as_exp(["apple", "banana", "cherry"], flavor=2) + rtk.strings_as_expr(["apple", "banana", "cherry"], flavor=2) # Output: 'banana|cherry|apple' ``` @@ -184,15 +184,15 @@ def strings_as_exp(texts: Iterable[str], flavor: int | None = None) -> str: unique_texts = set(texts) # if all(map(lambda text: len(text) == 1, unique_texts)): # logger.warning( - # "All strings are of length 1. Consider using make_exp() instead." + # "All strings are of length 1. Consider using make_expr() instead." # ) if flavor == RegexFlavor.RE: - return _strings_as_exp(unique_texts) + return _strings_as_expr(unique_texts) else: - return _strings_as_exp2(unique_texts) + return _strings_as_expr2(unique_texts) -def _make_group_exp(group: list[int]) -> str: +def _make_group_expr(group: list[int]) -> str: if len(group) > 2: # Represent as a character range return _escape(chr(group[0])) + "-" + _escape(chr(group[-1])) @@ -201,7 +201,7 @@ def _make_group_exp(group: list[int]) -> str: return "".join((_escape(chr(char_ord)) for char_ord in group)) -def _make_group_exp2(group: list[int]) -> str: +def _make_group_expr2(group: list[int]) -> str: if len(group) > 2: # Represent as a character range return _escape2(chr(group[0])) + "-" + _escape2(chr(group[-1])) @@ -210,7 +210,7 @@ def _make_group_exp2(group: list[int]) -> str: return "".join((_escape2(chr(char_ord)) for char_ord in group)) -def make_exp(chars: Iterable[str], flavor: int | None = None) -> str: +def make_expr(chars: Iterable[str], flavor: int | None = None) -> str: """Create a regex expression that exactly matches a list of characters. The characters are sorted and grouped into ranges where possible. @@ -221,10 +221,10 @@ def make_exp(chars: Iterable[str], flavor: int | None = None) -> str: ```python import regex_toolkit as rtk - "[" + rtk.make_exp(["a", "b", "c", "z", "y", "x"]) + "]" + "[" + rtk.make_expr(["a", "b", "c", "z", "y", "x"]) + "]" # Output: '[a-cx-z]' - "[" + rtk.make_exp(["a", "b", "c", "z", "y", "x"], flavor=2) + "]" + "[" + rtk.make_expr(["a", "b", "c", "z", "y", "x"], flavor=2) + "]" # Output: '[a-cx-z]' ``` @@ -239,12 +239,12 @@ def make_exp(chars: Iterable[str], flavor: int | None = None) -> str: ValueError: Invalid regex flavor. """ func = ( - _make_group_exp + _make_group_expr if resolve_flavor(flavor) == RegexFlavor.RE - else _make_group_exp2 + else _make_group_expr2 ) - exp = "" + expr = "" group = [] for char_ord in sorted(set(map(ord, chars))): if not group: @@ -255,10 +255,10 @@ def make_exp(chars: Iterable[str], flavor: int | None = None) -> str: group.append(char_ord) else: # Make the group and start a new one - exp += func(group) + expr += func(group) group.clear() group.append(char_ord) if group: # Make any remaining group - exp += func(group) - return exp + expr += func(group) + return expr diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index 9d40e1f..0000000 --- a/tests/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore everything -* - -# Except -!test_base.py -!test_enums.py -!test_utils.py - -!.gitignore diff --git a/tests/test_base.py b/tests/test_base.py index f79461e..7ce360d 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,4 +1,3 @@ -# import random import re from collections.abc import Iterable from itertools import product @@ -63,43 +62,23 @@ # Test helpers -def _check_exp_match(exp: str, text: str, flavor: int, *, should_match: bool) -> bool: - if flavor == 1: - found = bool(re.fullmatch(exp, text)) - return found if should_match else not found - if flavor == 2: - found = bool(re2.fullmatch(exp, text)) - return found if should_match else not found +def _check_expr_match(expr: str, text: str, flavor: int) -> bool: + if flavor == RegexFlavor.RE: + return bool(re.fullmatch(expr, text)) + if flavor == RegexFlavor.RE2: + return bool(re2.fullmatch(expr, text)) raise ValueError(f"Invalid regex flavor: {flavor!r}") -def assert_exp_match( - exp: str, text: str, flavor: int, *, should_match: bool = True -) -> bool: - assert _check_exp_match(exp, text, flavor, should_match=should_match), ( - f"RE{flavor} Pattern: {exp!r} should match {text!r}" - if should_match - else f"RE{flavor} Pattern: {exp!r} should not match {text!r}" - ) - - -def assert_exp_not_match(exp: str, text: str, flavor: int) -> bool: - assert_exp_match(exp, text, flavor, should_match=False) +def assert_expr_match(expr: str, text: str, flavor: int) -> bool: + assert _check_expr_match( + expr, text, flavor + ), f"RE{flavor} Pattern: {expr!r} should match {text!r}" -def assert_exp_match_all( - exp: str, - texts: Iterable[str], - flavor: int, - *, - should_match: bool = True, -) -> bool: +def assert_expr_match_all(expr: str, texts: Iterable[str], flavor: int) -> bool: for text in texts: - assert_exp_match(exp, text, flavor, should_match=should_match) - - -def assert_exp_not_match_any(exp: str, texts: Iterable[str], flavor: int) -> bool: - assert_exp_match_all(exp, texts, flavor, should_match=False) + assert_expr_match(expr, text, flavor) # Resolve flavor @@ -185,31 +164,23 @@ def test_resolve_flavor_None_without_default_raises(): def test_escape_and_escape2_safe(char, expected, flavor): actual = regex_toolkit.escape(char, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, char, flavor) - - exp_to_test = r"^[" + actual + r"]$" - assert_exp_match(exp_to_test, char, flavor) + assert_expr_match(rf"^{actual}$", char, flavor) + assert_expr_match(rf"^[{actual}]$", char, flavor) @pytest.mark.parametrize( - "char, expected_exp", + "char, expected", [(char, f"\\{char}") for char in ALWAYS_ESCAPE], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_escape_and_escape2_escapable(char, expected_exp, flavor): +def test_escape_and_escape2_always_safe(char, expected, flavor): actual = regex_toolkit.escape(char, flavor) - assert actual == expected_exp - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, char, flavor) - - exp_to_test = r"^[" + actual + r"]$" - assert_exp_match(exp_to_test, char, flavor) + assert actual == expected + assert_expr_match(rf"^{actual}$", char, flavor) + assert_expr_match(rf"^[{actual}]$", char, flavor) -def test_escape_and_escape2_calls_expected_inner_func(): +def test_escape_and_escape2_calls_expected_internal_func(): char = "a" flavor = RegexFlavor.RE @@ -266,18 +237,14 @@ def test_escape_and_escape2_non_str(non_str_char, flavor): @pytest.mark.parametrize( - "char, expected_exp", + "char, expected", [(char, f"\\{char}") for char in NON_ASCII_CHARS], ) -def test_escape_unknown(char, expected_exp): +def test_escape_unknown(char, expected): actual = regex_toolkit.escape(char, RegexFlavor.RE) - assert actual == expected_exp - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, char, RegexFlavor.RE) - - exp_to_test = r"^[" + actual + r"]$" - assert_exp_match(exp_to_test, char, RegexFlavor.RE) + assert actual == expected + assert_expr_match(rf"^{actual}$", char, RegexFlavor.RE) + assert_expr_match(rf"^[{actual}]$", char, RegexFlavor.RE) # RE2 - Escape @@ -296,35 +263,26 @@ def test_escape_unknown(char, expected_exp): def test_escape2_unknown(char, expected): actual = regex_toolkit.escape(char, RegexFlavor.RE2) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, char, RegexFlavor.RE2) - - exp_to_test = r"^[" + actual + r"]$" - assert_exp_match(exp_to_test, char, RegexFlavor.RE2) + assert_expr_match(rf"^{actual}$", char, RegexFlavor.RE2) + assert_expr_match(rf"^[{actual}]$", char, RegexFlavor.RE2) def test_escape2_trimmed(): - text = "°" + char = "°" expected = "\\x{00B0}" - actual = regex_toolkit.escape(text, RegexFlavor.RE2) + actual = regex_toolkit.escape(char, RegexFlavor.RE2) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, text, RegexFlavor.RE2) + assert_expr_match(rf"^{actual}$", char, RegexFlavor.RE2) + assert_expr_match(rf"^[{actual}]$", char, RegexFlavor.RE2) def test_escape2_untrimmed(): - text = "🅰" + char = "🅰" expected = "\\x{0001F170}" - actual = regex_toolkit.escape(text, RegexFlavor.RE2) + actual = regex_toolkit.escape(char, RegexFlavor.RE2) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, text, RegexFlavor.RE2) - - exp_to_test = r"^[" + actual + r"]$" - assert_exp_match(exp_to_test, text, RegexFlavor.RE2) + assert_expr_match(rf"^{actual}$", char, RegexFlavor.RE2) + assert_expr_match(rf"^[{actual}]$", char, RegexFlavor.RE2) # RE and RE2 - String as expression @@ -332,23 +290,21 @@ def test_escape2_untrimmed(): @pytest.mark.parametrize("text, expected", [(text, text) for text in ALWAYS_SAFE]) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_string_as_exp_and_exp2_and_exp2_safe_individual_char(text, expected, flavor): - actual = regex_toolkit.string_as_exp(text, flavor) +def test_string_as_expr_and_expr2_and_expr2_safe_individual_char( + text, expected, flavor +): + actual = regex_toolkit.string_as_expr(text, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, text, flavor) + assert_expr_match(rf"^{actual}$", text, flavor) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_string_as_exp_and_exp2_and_exp2_safe_joined_as_one(flavor): +def test_string_as_expr_and_expr2_and_expr2_safe_joined_as_one(flavor): text = "".join(ALWAYS_SAFE) - expected = "".join(ALWAYS_SAFE) - actual = regex_toolkit.string_as_exp(text, flavor) + expected = text + actual = regex_toolkit.string_as_expr(text, flavor) assert actual == expected - - exp_to_test = actual - assert_exp_match(exp_to_test, text, flavor) + assert_expr_match(rf"^{actual}$", text, flavor) @pytest.mark.parametrize( @@ -356,57 +312,51 @@ def test_string_as_exp_and_exp2_and_exp2_safe_joined_as_one(flavor): [(char, f"\\{char}") for char in ALWAYS_ESCAPE], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_string_as_exp_and_exp2_escapable_individual_char(text, expected, flavor): - actual = regex_toolkit.string_as_exp(text, flavor) +def test_string_as_expr_and_expr2_always_safe_individual_char(text, expected, flavor): + actual = regex_toolkit.string_as_expr(text, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, text, flavor) + assert_expr_match(rf"^{actual}$", text, flavor) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_string_as_exp_and_exp2_escapable_joined_as_one(flavor): +def test_string_as_expr_and_expr2_always_safe_joined_as_one(flavor): text = "".join(ALWAYS_ESCAPE) expected = "".join(f"\\{char}" for char in ALWAYS_ESCAPE) - actual = regex_toolkit.string_as_exp(text, flavor) + actual = regex_toolkit.string_as_expr(text, flavor) assert actual == expected - - exp_to_test = actual - assert_exp_match(exp_to_test, text, flavor) + assert_expr_match(rf"^{actual}$", text, flavor) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_string_as_exp_and_exp2_matches_unicode_chars(flavor): +def test_string_as_expr_and_expr2_matches_unicode_chars(flavor): high_surrogate_pair_ords = set(range(0xD800, 0xDBFF + 1)) low_surrogate_pair_ords = set(range(0xDC00, 0xDFFF + 1)) - for i in ( + for ord in ( set(range(0x0000, 0x10FFFF + 1)) - high_surrogate_pair_ords - low_surrogate_pair_ords ): - char = chr(i) - actual = regex_toolkit.string_as_exp(char, flavor) - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, char, flavor) + char = chr(ord) + actual = regex_toolkit.string_as_expr(char, flavor) + assert_expr_match(rf"^{actual}$", char, flavor) -def test_string_as_exp_and_exp2_calls_expected_inner_func(): +def test_string_as_expr_and_expr2_calls_expected_internal_func(): text = "foo" flavor = RegexFlavor.RE - with mock.patch("regex_toolkit.base._string_as_exp") as mock__string_as_exp: - with mock.patch("regex_toolkit.base._string_as_exp2") as mock__string_as_exp2: - regex_toolkit.string_as_exp(text, flavor) - mock__string_as_exp.assert_called_once_with(text) - mock__string_as_exp2.assert_not_called() + with mock.patch("regex_toolkit.base._string_as_expr") as mock__string_as_expr: + with mock.patch("regex_toolkit.base._string_as_expr2") as mock__string_as_expr2: + regex_toolkit.string_as_expr(text, flavor) + mock__string_as_expr.assert_called_once_with(text) + mock__string_as_expr2.assert_not_called() flavor = RegexFlavor.RE2 - with mock.patch("regex_toolkit.base._string_as_exp") as mock__string_as_exp: - with mock.patch("regex_toolkit.base._string_as_exp2") as mock__string_as_exp2: - regex_toolkit.string_as_exp(text, flavor) - mock__string_as_exp.assert_not_called() - mock__string_as_exp2.assert_called_once_with(text) + with mock.patch("regex_toolkit.base._string_as_expr") as mock__string_as_expr: + with mock.patch("regex_toolkit.base._string_as_expr2") as mock__string_as_expr2: + regex_toolkit.string_as_expr(text, flavor) + mock__string_as_expr.assert_not_called() + mock__string_as_expr2.assert_called_once_with(text) # RE - String as expression @@ -416,22 +366,18 @@ def test_string_as_exp_and_exp2_calls_expected_inner_func(): "text, expected", [(text, f"\\{text}") for text in NON_ASCII_CHARS], ) -def test_string_as_exp_unsafe_individual_char(text, expected): - actual = regex_toolkit.string_as_exp(text, RegexFlavor.RE) +def test_string_as_expr_unsafe_individual_char(text, expected): + actual = regex_toolkit.string_as_expr(text, RegexFlavor.RE) assert actual == expected + assert_expr_match(rf"^{actual}$", text, RegexFlavor.RE) - exp_to_test = actual - assert_exp_match(exp_to_test, text, RegexFlavor.RE) - -def test_string_as_exp_unsafe_joined_as_one(): +def test_string_as_expr_unsafe_joined_as_one(): text = "".join(NON_ASCII_CHARS) expected = "".join(f"\\{char}" for char in text) - actual = regex_toolkit.string_as_exp(text, RegexFlavor.RE) + actual = regex_toolkit.string_as_expr(text, RegexFlavor.RE) assert actual == expected - - exp_to_test = actual - assert_exp_match(exp_to_test, text, RegexFlavor.RE) + assert_expr_match(rf"^{actual}$", text, RegexFlavor.RE) # RE2 - String as expression @@ -447,25 +393,21 @@ def test_string_as_exp_unsafe_joined_as_one(): for char in NON_ASCII_CHARS ], ) -def test_string_as_exp2_unknown_individual_char(text, expected): - actual = regex_toolkit.string_as_exp(text, RegexFlavor.RE2) +def test_string_as_expr2_unknown_individual_char(text, expected): + actual = regex_toolkit.string_as_expr(text, RegexFlavor.RE2) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match(exp_to_test, text, RegexFlavor.RE2) + assert_expr_match(rf"^{actual}$", text, RegexFlavor.RE2) -def test_string_as_exp2_unknown_joined_as_one(): +def test_string_as_expr2_unknown_joined_as_one(): text = "".join(NON_ASCII_CHARS) expected = "".join( "\\x{" + format(ord(char), "x").zfill(8).removeprefix("0000").upper() + "}" for char in text ) - actual = regex_toolkit.string_as_exp(text, RegexFlavor.RE2) + actual = regex_toolkit.string_as_expr(text, RegexFlavor.RE2) assert actual == expected - - exp_to_test = actual - assert_exp_match(exp_to_test, text, RegexFlavor.RE2) + assert_expr_match(rf"^{actual}$", text, RegexFlavor.RE2) # RE and RE2 - Strings as expression @@ -487,12 +429,10 @@ def test_string_as_exp2_unknown_joined_as_one(): ], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_safe_of_various_lengths(texts, expected, flavor): - actual = regex_toolkit.strings_as_exp(texts, flavor) +def test_strings_as_expr_and_expr2_safe_of_various_lengths(texts, expected, flavor): + actual = regex_toolkit.strings_as_expr(texts, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, flavor) + assert_expr_match_all(rf"^{actual}$", texts, flavor) @pytest.mark.parametrize( @@ -512,12 +452,12 @@ def test_strings_as_exp_and_exp2_safe_of_various_lengths(texts, expected, flavor ], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_escapable_of_various_lengths(texts, expected, flavor): - actual = regex_toolkit.strings_as_exp(texts, flavor) +def test_strings_as_expr_and_expr2_always_safe_of_various_lengths( + texts, expected, flavor +): + actual = regex_toolkit.strings_as_expr(texts, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, flavor) + assert_expr_match_all(rf"^{actual}$", texts, flavor) @pytest.mark.parametrize( @@ -537,12 +477,10 @@ def test_strings_as_exp_and_exp2_escapable_of_various_lengths(texts, expected, f ], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_reserved_of_various_lengths(texts, expected, flavor): - actual = regex_toolkit.strings_as_exp(texts, flavor) +def test_strings_as_expr_and_expr2_reserved_of_various_lengths(texts, expected, flavor): + actual = regex_toolkit.strings_as_expr(texts, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, flavor) + assert_expr_match_all(rf"^{actual}$", texts, flavor) @pytest.mark.parametrize( @@ -562,53 +500,57 @@ def test_strings_as_exp_and_exp2_reserved_of_various_lengths(texts, expected, fl ], ) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_safe_and_escapable_of_various_lengths( +def test_strings_as_expr_and_expr2_safe_and_always_safe_of_various_lengths( texts, expected, flavor ): - actual = regex_toolkit.strings_as_exp(texts, flavor) + actual = regex_toolkit.strings_as_expr(texts, flavor) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, flavor) + assert_expr_match_all(rf"^{actual}$", texts, flavor) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_with_duplicates(flavor): +def test_strings_as_expr_and_expr2_with_duplicates(flavor): texts = ["a", "A", "abc", "ABC", "ABC", "abc", "A", "a"] - expected = r"ABC|abc|A|a" - actual = regex_toolkit.strings_as_exp(texts, flavor) + actual = regex_toolkit.strings_as_expr(texts, flavor) assert actual == expected + assert_expr_match_all(rf"^{actual}$", texts, flavor) @pytest.mark.parametrize("seq", ["abc", tuple("abc"), list("abc")]) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_various_sequence_types(seq, flavor): - assert regex_toolkit.strings_as_exp(seq, flavor) == "a|b|c" +def test_strings_as_expr_and_expr2_various_sequence_types(seq, flavor): + assert regex_toolkit.strings_as_expr(seq, flavor) == "a|b|c" -@pytest.mark.parametrize("seq", ["", tuple(), list()]) +@pytest.mark.parametrize("texts", ["", tuple(), list()]) @pytest.mark.parametrize("flavor", ALL_REGEX_FLAVORS) -def test_strings_as_exp_and_exp2_empty_of_various_sequence_types(seq, flavor): - assert regex_toolkit.strings_as_exp(seq, flavor) == "" +def test_strings_as_expr_and_expr2_empty_of_various_sequence_types(texts, flavor): + expected = "" + actual = regex_toolkit.strings_as_expr(texts, flavor) + assert actual == expected -def test_strings_as_exp_calls_expected_inner_func(): +def test_strings_as_expr_calls_expected_internal_func(): texts = ["foo", "bar"] flavor = RegexFlavor.RE - with mock.patch("regex_toolkit.base._strings_as_exp") as mock__strings_as_exp: - with mock.patch("regex_toolkit.base._strings_as_exp2") as mock__strings_as_exp2: - regex_toolkit.base.strings_as_exp(texts, flavor) - mock__strings_as_exp.assert_called_once_with(set(texts)) - mock__strings_as_exp2.assert_not_called() + with mock.patch("regex_toolkit.base._strings_as_expr") as mock__strings_as_expr: + with mock.patch( + "regex_toolkit.base._strings_as_expr2" + ) as mock__strings_as_expr2: + regex_toolkit.base.strings_as_expr(texts, flavor) + mock__strings_as_expr.assert_called_once_with(set(texts)) + mock__strings_as_expr2.assert_not_called() flavor = RegexFlavor.RE2 - with mock.patch("regex_toolkit.base._strings_as_exp") as mock__strings_as_exp: - with mock.patch("regex_toolkit.base._strings_as_exp2") as mock__strings_as_exp2: - regex_toolkit.base.strings_as_exp(texts, flavor) - mock__strings_as_exp.assert_not_called() - mock__strings_as_exp2.assert_called_once_with(set(texts)) + with mock.patch("regex_toolkit.base._strings_as_expr") as mock__strings_as_expr: + with mock.patch( + "regex_toolkit.base._strings_as_expr2" + ) as mock__strings_as_expr2: + regex_toolkit.base.strings_as_expr(texts, flavor) + mock__strings_as_expr.assert_not_called() + mock__strings_as_expr2.assert_called_once_with(set(texts)) # RE - Strings as expression @@ -630,12 +572,10 @@ def test_strings_as_exp_calls_expected_inner_func(): for texts in product(NON_ASCII_CHARS, repeat=2) ], ) -def test_strings_as_exp_unsafe_of_various_lengths(texts, expected): - actual = regex_toolkit.strings_as_exp(texts, RegexFlavor.RE) +def test_strings_as_expr_unsafe_of_various_lengths(texts, expected): + actual = regex_toolkit.strings_as_expr(texts, RegexFlavor.RE) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, RegexFlavor.RE) + assert_expr_match_all(rf"^{actual}$", texts, RegexFlavor.RE) # RE2 - Strings as expression @@ -659,12 +599,10 @@ def test_strings_as_exp_unsafe_of_various_lengths(texts, expected): for texts in product(NON_ASCII_CHARS, repeat=2) ], ) -def test_strings_as_exp2_unsafe_of_various_lengths(texts, expected): - actual = regex_toolkit.strings_as_exp(texts, RegexFlavor.RE2) +def test_strings_as_expr2_unsafe_of_various_lengths(texts, expected): + actual = regex_toolkit.strings_as_expr(texts, RegexFlavor.RE2) assert actual == expected - - exp_to_test = r"^" + actual + r"$" - assert_exp_match_all(exp_to_test, texts, RegexFlavor.RE2) + assert_expr_match_all(rf"^{actual}$", texts, RegexFlavor.RE2) # RE - Make expression @@ -693,5 +631,7 @@ def test_strings_as_exp2_unsafe_of_various_lengths(texts, expected): (["d", "a", "b", "c", "a"], "a-d"), ), ) -def test_make_exp(chars, expected): - assert regex_toolkit.make_exp(chars, RegexFlavor.RE) == expected +def test_make_expr(chars, expected): + actual = regex_toolkit.make_expr(chars, RegexFlavor.RE) + assert actual == expected + assert_expr_match_all(rf"^[{actual}]$", chars, RegexFlavor.RE)