#
tokens: 81137/50000 1/958 files (page 228/236)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 228 of 236. Use http://codebase.md/seanivore/mcp-code-analyzer?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .DS_Store
├── .gitignore
├── bin
│   └── mcp-code-analyzer.js
├── dist
│   ├── mcp_code_analyzer-0.1.0-py3-none-any.whl
│   └── mcp_code_analyzer-0.1.0.tar.gz
├── LICENSE
├── package-lock.json
├── package.json
├── pyproject.toml
├── README.md
├── setup.py
├── src
│   ├── index.ts
│   └── mcp_code_analyzer
│       ├── __init__.py
│       ├── __main__.py
│       ├── __pycache__
│       │   ├── __init__.cpython-311.pyc
│       │   └── server.cpython-311.pyc
│       └── server.py
├── test_analyzer.py
├── test_code.py
├── test_package.py
├── test.py
├── tsconfig.json
└── venv
    ├── bin
    │   ├── activate
    │   ├── activate.csh
    │   ├── activate.fish
    │   ├── Activate.ps1
    │   ├── hatchling
    │   ├── pip
    │   ├── pip3
    │   ├── pip3.11
    │   ├── pyproject-build
    │   ├── python
    │   ├── python3
    │   └── python3.11
    ├── lib
    │   └── python3.11
    │       └── site-packages
    │           ├── _distutils_hack
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   └── override.cpython-311.pyc
    │           │   └── override.py
    │           ├── _mcp_code_analyzer.pth
    │           ├── build-1.2.2.post1.dist-info
    │           │   ├── entry_points.txt
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── REQUESTED
    │           │   └── WHEEL
    │           ├── distutils-precedence.pth
    │           ├── hatchling
    │           │   ├── __about__.py
    │           │   ├── __init__.py
    │           │   ├── __main__.py
    │           │   ├── __pycache__
    │           │   │   ├── __about__.cpython-311.pyc
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── __main__.cpython-311.pyc
    │           │   │   ├── build.cpython-311.pyc
    │           │   │   └── ouroboros.cpython-311.pyc
    │           │   ├── bridge
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── app.cpython-311.pyc
    │           │   │   └── app.py
    │           │   ├── build.py
    │           │   ├── builders
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── app.cpython-311.pyc
    │           │   │   │   ├── binary.cpython-311.pyc
    │           │   │   │   ├── config.cpython-311.pyc
    │           │   │   │   ├── constants.cpython-311.pyc
    │           │   │   │   ├── custom.cpython-311.pyc
    │           │   │   │   ├── macos.cpython-311.pyc
    │           │   │   │   ├── sdist.cpython-311.pyc
    │           │   │   │   ├── utils.cpython-311.pyc
    │           │   │   │   └── wheel.cpython-311.pyc
    │           │   │   ├── app.py
    │           │   │   ├── binary.py
    │           │   │   ├── config.py
    │           │   │   ├── constants.py
    │           │   │   ├── custom.py
    │           │   │   ├── hooks
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── custom.cpython-311.pyc
    │           │   │   │   │   └── version.cpython-311.pyc
    │           │   │   │   ├── custom.py
    │           │   │   │   ├── plugin
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── hooks.cpython-311.pyc
    │           │   │   │   │   │   └── interface.cpython-311.pyc
    │           │   │   │   │   ├── hooks.py
    │           │   │   │   │   └── interface.py
    │           │   │   │   └── version.py
    │           │   │   ├── macos.py
    │           │   │   ├── plugin
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── hooks.cpython-311.pyc
    │           │   │   │   │   └── interface.cpython-311.pyc
    │           │   │   │   ├── hooks.py
    │           │   │   │   └── interface.py
    │           │   │   ├── sdist.py
    │           │   │   ├── utils.py
    │           │   │   └── wheel.py
    │           │   ├── cli
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   └── __init__.cpython-311.pyc
    │           │   │   ├── dep
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   └── core.cpython-311.pyc
    │           │   │   │   └── core.py
    │           │   │   ├── metadata
    │           │   │   │   ├── __init__.py
    │           │   │   │   └── __pycache__
    │           │   │   │       └── __init__.cpython-311.pyc
    │           │   │   └── version
    │           │   │       ├── __init__.py
    │           │   │       └── __pycache__
    │           │   │           └── __init__.cpython-311.pyc
    │           │   ├── dep
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── core.cpython-311.pyc
    │           │   │   └── core.py
    │           │   ├── licenses
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── supported.cpython-311.pyc
    │           │   │   └── supported.py
    │           │   ├── metadata
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── core.cpython-311.pyc
    │           │   │   │   ├── custom.cpython-311.pyc
    │           │   │   │   ├── spec.cpython-311.pyc
    │           │   │   │   └── utils.cpython-311.pyc
    │           │   │   ├── core.py
    │           │   │   ├── custom.py
    │           │   │   ├── plugin
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── hooks.cpython-311.pyc
    │           │   │   │   │   └── interface.cpython-311.pyc
    │           │   │   │   ├── hooks.py
    │           │   │   │   └── interface.py
    │           │   │   ├── spec.py
    │           │   │   └── utils.py
    │           │   ├── ouroboros.py
    │           │   ├── plugin
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── exceptions.cpython-311.pyc
    │           │   │   │   ├── manager.cpython-311.pyc
    │           │   │   │   ├── specs.cpython-311.pyc
    │           │   │   │   └── utils.cpython-311.pyc
    │           │   │   ├── exceptions.py
    │           │   │   ├── manager.py
    │           │   │   ├── specs.py
    │           │   │   └── utils.py
    │           │   ├── py.typed
    │           │   ├── utils
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── constants.cpython-311.pyc
    │           │   │   │   ├── context.cpython-311.pyc
    │           │   │   │   └── fs.cpython-311.pyc
    │           │   │   ├── constants.py
    │           │   │   ├── context.py
    │           │   │   └── fs.py
    │           │   └── version
    │           │       ├── __init__.py
    │           │       ├── __pycache__
    │           │       │   ├── __init__.cpython-311.pyc
    │           │       │   └── core.cpython-311.pyc
    │           │       ├── core.py
    │           │       ├── scheme
    │           │       │   ├── __init__.py
    │           │       │   ├── __pycache__
    │           │       │   │   ├── __init__.cpython-311.pyc
    │           │       │   │   └── standard.cpython-311.pyc
    │           │       │   ├── plugin
    │           │       │   │   ├── __init__.py
    │           │       │   │   ├── __pycache__
    │           │       │   │   │   ├── __init__.cpython-311.pyc
    │           │       │   │   │   ├── hooks.cpython-311.pyc
    │           │       │   │   │   └── interface.cpython-311.pyc
    │           │       │   │   ├── hooks.py
    │           │       │   │   └── interface.py
    │           │       │   └── standard.py
    │           │       └── source
    │           │           ├── __init__.py
    │           │           ├── __pycache__
    │           │           │   ├── __init__.cpython-311.pyc
    │           │           │   ├── code.cpython-311.pyc
    │           │           │   ├── env.cpython-311.pyc
    │           │           │   └── regex.cpython-311.pyc
    │           │           ├── code.py
    │           │           ├── env.py
    │           │           ├── plugin
    │           │           │   ├── __init__.py
    │           │           │   ├── __pycache__
    │           │           │   │   ├── __init__.cpython-311.pyc
    │           │           │   │   ├── hooks.cpython-311.pyc
    │           │           │   │   └── interface.cpython-311.pyc
    │           │           │   ├── hooks.py
    │           │           │   └── interface.py
    │           │           └── regex.py
    │           ├── hatchling-1.26.3.dist-info
    │           │   ├── entry_points.txt
    │           │   ├── INSTALLER
    │           │   ├── licenses
    │           │   │   └── LICENSE.txt
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── REQUESTED
    │           │   └── WHEEL
    │           ├── mcp_code_analyzer-0.1.0.dist-info
    │           │   ├── direct_url.json
    │           │   ├── INSTALLER
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── REQUESTED
    │           │   └── WHEEL
    │           ├── packaging
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── _elffile.cpython-311.pyc
    │           │   │   ├── _manylinux.cpython-311.pyc
    │           │   │   ├── _musllinux.cpython-311.pyc
    │           │   │   ├── _parser.cpython-311.pyc
    │           │   │   ├── _structures.cpython-311.pyc
    │           │   │   ├── _tokenizer.cpython-311.pyc
    │           │   │   ├── markers.cpython-311.pyc
    │           │   │   ├── metadata.cpython-311.pyc
    │           │   │   ├── requirements.cpython-311.pyc
    │           │   │   ├── specifiers.cpython-311.pyc
    │           │   │   ├── tags.cpython-311.pyc
    │           │   │   ├── utils.cpython-311.pyc
    │           │   │   └── version.cpython-311.pyc
    │           │   ├── _elffile.py
    │           │   ├── _manylinux.py
    │           │   ├── _musllinux.py
    │           │   ├── _parser.py
    │           │   ├── _structures.py
    │           │   ├── _tokenizer.py
    │           │   ├── licenses
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── _spdx.cpython-311.pyc
    │           │   │   └── _spdx.py
    │           │   ├── markers.py
    │           │   ├── metadata.py
    │           │   ├── py.typed
    │           │   ├── requirements.py
    │           │   ├── specifiers.py
    │           │   ├── tags.py
    │           │   ├── utils.py
    │           │   └── version.py
    │           ├── packaging-24.2.dist-info
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── LICENSE.APACHE
    │           │   ├── LICENSE.BSD
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   └── WHEEL
    │           ├── pathspec
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── _meta.cpython-311.pyc
    │           │   │   ├── gitignore.cpython-311.pyc
    │           │   │   ├── pathspec.cpython-311.pyc
    │           │   │   ├── pattern.cpython-311.pyc
    │           │   │   └── util.cpython-311.pyc
    │           │   ├── _meta.py
    │           │   ├── gitignore.py
    │           │   ├── pathspec.py
    │           │   ├── pattern.py
    │           │   ├── patterns
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── gitwildmatch.cpython-311.pyc
    │           │   │   └── gitwildmatch.py
    │           │   ├── py.typed
    │           │   └── util.py
    │           ├── pathspec-0.12.1.dist-info
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   └── WHEEL
    │           ├── pip
    │           │   ├── __init__.py
    │           │   ├── __main__.py
    │           │   ├── __pip-runner__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── __main__.cpython-311.pyc
    │           │   │   └── __pip-runner__.cpython-311.pyc
    │           │   ├── _internal
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── build_env.cpython-311.pyc
    │           │   │   │   ├── cache.cpython-311.pyc
    │           │   │   │   ├── configuration.cpython-311.pyc
    │           │   │   │   ├── exceptions.cpython-311.pyc
    │           │   │   │   ├── main.cpython-311.pyc
    │           │   │   │   ├── pyproject.cpython-311.pyc
    │           │   │   │   ├── self_outdated_check.cpython-311.pyc
    │           │   │   │   └── wheel_builder.cpython-311.pyc
    │           │   │   ├── build_env.py
    │           │   │   ├── cache.py
    │           │   │   ├── cli
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── autocompletion.cpython-311.pyc
    │           │   │   │   │   ├── base_command.cpython-311.pyc
    │           │   │   │   │   ├── cmdoptions.cpython-311.pyc
    │           │   │   │   │   ├── command_context.cpython-311.pyc
    │           │   │   │   │   ├── index_command.cpython-311.pyc
    │           │   │   │   │   ├── main_parser.cpython-311.pyc
    │           │   │   │   │   ├── main.cpython-311.pyc
    │           │   │   │   │   ├── parser.cpython-311.pyc
    │           │   │   │   │   ├── progress_bars.cpython-311.pyc
    │           │   │   │   │   ├── req_command.cpython-311.pyc
    │           │   │   │   │   ├── spinners.cpython-311.pyc
    │           │   │   │   │   └── status_codes.cpython-311.pyc
    │           │   │   │   ├── autocompletion.py
    │           │   │   │   ├── base_command.py
    │           │   │   │   ├── cmdoptions.py
    │           │   │   │   ├── command_context.py
    │           │   │   │   ├── index_command.py
    │           │   │   │   ├── main_parser.py
    │           │   │   │   ├── main.py
    │           │   │   │   ├── parser.py
    │           │   │   │   ├── progress_bars.py
    │           │   │   │   ├── req_command.py
    │           │   │   │   ├── spinners.py
    │           │   │   │   └── status_codes.py
    │           │   │   ├── commands
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── cache.cpython-311.pyc
    │           │   │   │   │   ├── check.cpython-311.pyc
    │           │   │   │   │   ├── completion.cpython-311.pyc
    │           │   │   │   │   ├── configuration.cpython-311.pyc
    │           │   │   │   │   ├── debug.cpython-311.pyc
    │           │   │   │   │   ├── download.cpython-311.pyc
    │           │   │   │   │   ├── freeze.cpython-311.pyc
    │           │   │   │   │   ├── hash.cpython-311.pyc
    │           │   │   │   │   ├── help.cpython-311.pyc
    │           │   │   │   │   ├── index.cpython-311.pyc
    │           │   │   │   │   ├── inspect.cpython-311.pyc
    │           │   │   │   │   ├── install.cpython-311.pyc
    │           │   │   │   │   ├── list.cpython-311.pyc
    │           │   │   │   │   ├── search.cpython-311.pyc
    │           │   │   │   │   ├── show.cpython-311.pyc
    │           │   │   │   │   ├── uninstall.cpython-311.pyc
    │           │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   ├── cache.py
    │           │   │   │   ├── check.py
    │           │   │   │   ├── completion.py
    │           │   │   │   ├── configuration.py
    │           │   │   │   ├── debug.py
    │           │   │   │   ├── download.py
    │           │   │   │   ├── freeze.py
    │           │   │   │   ├── hash.py
    │           │   │   │   ├── help.py
    │           │   │   │   ├── index.py
    │           │   │   │   ├── inspect.py
    │           │   │   │   ├── install.py
    │           │   │   │   ├── list.py
    │           │   │   │   ├── search.py
    │           │   │   │   ├── show.py
    │           │   │   │   ├── uninstall.py
    │           │   │   │   └── wheel.py
    │           │   │   ├── configuration.py
    │           │   │   ├── distributions
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── base.cpython-311.pyc
    │           │   │   │   │   ├── installed.cpython-311.pyc
    │           │   │   │   │   ├── sdist.cpython-311.pyc
    │           │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   ├── base.py
    │           │   │   │   ├── installed.py
    │           │   │   │   ├── sdist.py
    │           │   │   │   └── wheel.py
    │           │   │   ├── exceptions.py
    │           │   │   ├── index
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── collector.cpython-311.pyc
    │           │   │   │   │   ├── package_finder.cpython-311.pyc
    │           │   │   │   │   └── sources.cpython-311.pyc
    │           │   │   │   ├── collector.py
    │           │   │   │   ├── package_finder.py
    │           │   │   │   └── sources.py
    │           │   │   ├── locations
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _distutils.cpython-311.pyc
    │           │   │   │   │   ├── _sysconfig.cpython-311.pyc
    │           │   │   │   │   └── base.cpython-311.pyc
    │           │   │   │   ├── _distutils.py
    │           │   │   │   ├── _sysconfig.py
    │           │   │   │   └── base.py
    │           │   │   ├── main.py
    │           │   │   ├── metadata
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _json.cpython-311.pyc
    │           │   │   │   │   ├── base.cpython-311.pyc
    │           │   │   │   │   └── pkg_resources.cpython-311.pyc
    │           │   │   │   ├── _json.py
    │           │   │   │   ├── base.py
    │           │   │   │   ├── importlib
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── _compat.cpython-311.pyc
    │           │   │   │   │   │   ├── _dists.cpython-311.pyc
    │           │   │   │   │   │   └── _envs.cpython-311.pyc
    │           │   │   │   │   ├── _compat.py
    │           │   │   │   │   ├── _dists.py
    │           │   │   │   │   └── _envs.py
    │           │   │   │   └── pkg_resources.py
    │           │   │   ├── models
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── candidate.cpython-311.pyc
    │           │   │   │   │   ├── direct_url.cpython-311.pyc
    │           │   │   │   │   ├── format_control.cpython-311.pyc
    │           │   │   │   │   ├── index.cpython-311.pyc
    │           │   │   │   │   ├── installation_report.cpython-311.pyc
    │           │   │   │   │   ├── link.cpython-311.pyc
    │           │   │   │   │   ├── scheme.cpython-311.pyc
    │           │   │   │   │   ├── search_scope.cpython-311.pyc
    │           │   │   │   │   ├── selection_prefs.cpython-311.pyc
    │           │   │   │   │   ├── target_python.cpython-311.pyc
    │           │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   ├── candidate.py
    │           │   │   │   ├── direct_url.py
    │           │   │   │   ├── format_control.py
    │           │   │   │   ├── index.py
    │           │   │   │   ├── installation_report.py
    │           │   │   │   ├── link.py
    │           │   │   │   ├── scheme.py
    │           │   │   │   ├── search_scope.py
    │           │   │   │   ├── selection_prefs.py
    │           │   │   │   ├── target_python.py
    │           │   │   │   └── wheel.py
    │           │   │   ├── network
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── auth.cpython-311.pyc
    │           │   │   │   │   ├── cache.cpython-311.pyc
    │           │   │   │   │   ├── download.cpython-311.pyc
    │           │   │   │   │   ├── lazy_wheel.cpython-311.pyc
    │           │   │   │   │   ├── session.cpython-311.pyc
    │           │   │   │   │   ├── utils.cpython-311.pyc
    │           │   │   │   │   └── xmlrpc.cpython-311.pyc
    │           │   │   │   ├── auth.py
    │           │   │   │   ├── cache.py
    │           │   │   │   ├── download.py
    │           │   │   │   ├── lazy_wheel.py
    │           │   │   │   ├── session.py
    │           │   │   │   ├── utils.py
    │           │   │   │   └── xmlrpc.py
    │           │   │   ├── operations
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── check.cpython-311.pyc
    │           │   │   │   │   ├── freeze.cpython-311.pyc
    │           │   │   │   │   └── prepare.cpython-311.pyc
    │           │   │   │   ├── check.py
    │           │   │   │   ├── freeze.py
    │           │   │   │   ├── install
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── editable_legacy.cpython-311.pyc
    │           │   │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   │   ├── editable_legacy.py
    │           │   │   │   │   └── wheel.py
    │           │   │   │   └── prepare.py
    │           │   │   ├── pyproject.py
    │           │   │   ├── req
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── constructors.cpython-311.pyc
    │           │   │   │   │   ├── req_file.cpython-311.pyc
    │           │   │   │   │   ├── req_install.cpython-311.pyc
    │           │   │   │   │   ├── req_set.cpython-311.pyc
    │           │   │   │   │   └── req_uninstall.cpython-311.pyc
    │           │   │   │   ├── constructors.py
    │           │   │   │   ├── req_file.py
    │           │   │   │   ├── req_install.py
    │           │   │   │   ├── req_set.py
    │           │   │   │   └── req_uninstall.py
    │           │   │   ├── resolution
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   └── base.cpython-311.pyc
    │           │   │   │   ├── base.py
    │           │   │   │   ├── legacy
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── resolver.cpython-311.pyc
    │           │   │   │   │   └── resolver.py
    │           │   │   │   └── resolvelib
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   ├── base.cpython-311.pyc
    │           │   │   │       │   ├── candidates.cpython-311.pyc
    │           │   │   │       │   ├── factory.cpython-311.pyc
    │           │   │   │       │   ├── found_candidates.cpython-311.pyc
    │           │   │   │       │   ├── provider.cpython-311.pyc
    │           │   │   │       │   ├── reporter.cpython-311.pyc
    │           │   │   │       │   ├── requirements.cpython-311.pyc
    │           │   │   │       │   └── resolver.cpython-311.pyc
    │           │   │   │       ├── base.py
    │           │   │   │       ├── candidates.py
    │           │   │   │       ├── factory.py
    │           │   │   │       ├── found_candidates.py
    │           │   │   │       ├── provider.py
    │           │   │   │       ├── reporter.py
    │           │   │   │       ├── requirements.py
    │           │   │   │       └── resolver.py
    │           │   │   ├── self_outdated_check.py
    │           │   │   ├── utils
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _jaraco_text.cpython-311.pyc
    │           │   │   │   │   ├── _log.cpython-311.pyc
    │           │   │   │   │   ├── appdirs.cpython-311.pyc
    │           │   │   │   │   ├── compat.cpython-311.pyc
    │           │   │   │   │   ├── compatibility_tags.cpython-311.pyc
    │           │   │   │   │   ├── datetime.cpython-311.pyc
    │           │   │   │   │   ├── deprecation.cpython-311.pyc
    │           │   │   │   │   ├── direct_url_helpers.cpython-311.pyc
    │           │   │   │   │   ├── egg_link.cpython-311.pyc
    │           │   │   │   │   ├── encoding.cpython-311.pyc
    │           │   │   │   │   ├── entrypoints.cpython-311.pyc
    │           │   │   │   │   ├── filesystem.cpython-311.pyc
    │           │   │   │   │   ├── filetypes.cpython-311.pyc
    │           │   │   │   │   ├── glibc.cpython-311.pyc
    │           │   │   │   │   ├── hashes.cpython-311.pyc
    │           │   │   │   │   ├── logging.cpython-311.pyc
    │           │   │   │   │   ├── misc.cpython-311.pyc
    │           │   │   │   │   ├── packaging.cpython-311.pyc
    │           │   │   │   │   ├── retry.cpython-311.pyc
    │           │   │   │   │   ├── setuptools_build.cpython-311.pyc
    │           │   │   │   │   ├── subprocess.cpython-311.pyc
    │           │   │   │   │   ├── temp_dir.cpython-311.pyc
    │           │   │   │   │   ├── unpacking.cpython-311.pyc
    │           │   │   │   │   ├── urls.cpython-311.pyc
    │           │   │   │   │   ├── virtualenv.cpython-311.pyc
    │           │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   ├── _jaraco_text.py
    │           │   │   │   ├── _log.py
    │           │   │   │   ├── appdirs.py
    │           │   │   │   ├── compat.py
    │           │   │   │   ├── compatibility_tags.py
    │           │   │   │   ├── datetime.py
    │           │   │   │   ├── deprecation.py
    │           │   │   │   ├── direct_url_helpers.py
    │           │   │   │   ├── egg_link.py
    │           │   │   │   ├── encoding.py
    │           │   │   │   ├── entrypoints.py
    │           │   │   │   ├── filesystem.py
    │           │   │   │   ├── filetypes.py
    │           │   │   │   ├── glibc.py
    │           │   │   │   ├── hashes.py
    │           │   │   │   ├── logging.py
    │           │   │   │   ├── misc.py
    │           │   │   │   ├── packaging.py
    │           │   │   │   ├── retry.py
    │           │   │   │   ├── setuptools_build.py
    │           │   │   │   ├── subprocess.py
    │           │   │   │   ├── temp_dir.py
    │           │   │   │   ├── unpacking.py
    │           │   │   │   ├── urls.py
    │           │   │   │   ├── virtualenv.py
    │           │   │   │   └── wheel.py
    │           │   │   ├── vcs
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── bazaar.cpython-311.pyc
    │           │   │   │   │   ├── git.cpython-311.pyc
    │           │   │   │   │   ├── mercurial.cpython-311.pyc
    │           │   │   │   │   ├── subversion.cpython-311.pyc
    │           │   │   │   │   └── versioncontrol.cpython-311.pyc
    │           │   │   │   ├── bazaar.py
    │           │   │   │   ├── git.py
    │           │   │   │   ├── mercurial.py
    │           │   │   │   ├── subversion.py
    │           │   │   │   └── versioncontrol.py
    │           │   │   └── wheel_builder.py
    │           │   ├── _vendor
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── typing_extensions.cpython-311.pyc
    │           │   │   ├── cachecontrol
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _cmd.cpython-311.pyc
    │           │   │   │   │   ├── adapter.cpython-311.pyc
    │           │   │   │   │   ├── cache.cpython-311.pyc
    │           │   │   │   │   ├── controller.cpython-311.pyc
    │           │   │   │   │   ├── filewrapper.cpython-311.pyc
    │           │   │   │   │   ├── heuristics.cpython-311.pyc
    │           │   │   │   │   ├── serialize.cpython-311.pyc
    │           │   │   │   │   └── wrapper.cpython-311.pyc
    │           │   │   │   ├── _cmd.py
    │           │   │   │   ├── adapter.py
    │           │   │   │   ├── cache.py
    │           │   │   │   ├── caches
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── file_cache.cpython-311.pyc
    │           │   │   │   │   │   └── redis_cache.cpython-311.pyc
    │           │   │   │   │   ├── file_cache.py
    │           │   │   │   │   └── redis_cache.py
    │           │   │   │   ├── controller.py
    │           │   │   │   ├── filewrapper.py
    │           │   │   │   ├── heuristics.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── serialize.py
    │           │   │   │   └── wrapper.py
    │           │   │   ├── certifi
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   └── core.cpython-311.pyc
    │           │   │   │   ├── cacert.pem
    │           │   │   │   ├── core.py
    │           │   │   │   └── py.typed
    │           │   │   ├── distlib
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── compat.cpython-311.pyc
    │           │   │   │   │   ├── database.cpython-311.pyc
    │           │   │   │   │   ├── index.cpython-311.pyc
    │           │   │   │   │   ├── locators.cpython-311.pyc
    │           │   │   │   │   ├── manifest.cpython-311.pyc
    │           │   │   │   │   ├── markers.cpython-311.pyc
    │           │   │   │   │   ├── metadata.cpython-311.pyc
    │           │   │   │   │   ├── resources.cpython-311.pyc
    │           │   │   │   │   ├── scripts.cpython-311.pyc
    │           │   │   │   │   ├── util.cpython-311.pyc
    │           │   │   │   │   ├── version.cpython-311.pyc
    │           │   │   │   │   └── wheel.cpython-311.pyc
    │           │   │   │   ├── compat.py
    │           │   │   │   ├── database.py
    │           │   │   │   ├── index.py
    │           │   │   │   ├── locators.py
    │           │   │   │   ├── manifest.py
    │           │   │   │   ├── markers.py
    │           │   │   │   ├── metadata.py
    │           │   │   │   ├── resources.py
    │           │   │   │   ├── scripts.py
    │           │   │   │   ├── t32.exe
    │           │   │   │   ├── t64-arm.exe
    │           │   │   │   ├── t64.exe
    │           │   │   │   ├── util.py
    │           │   │   │   ├── version.py
    │           │   │   │   ├── w32.exe
    │           │   │   │   ├── w64-arm.exe
    │           │   │   │   ├── w64.exe
    │           │   │   │   └── wheel.py
    │           │   │   ├── distro
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   └── distro.cpython-311.pyc
    │           │   │   │   ├── distro.py
    │           │   │   │   └── py.typed
    │           │   │   ├── idna
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── codec.cpython-311.pyc
    │           │   │   │   │   ├── compat.cpython-311.pyc
    │           │   │   │   │   ├── core.cpython-311.pyc
    │           │   │   │   │   ├── idnadata.cpython-311.pyc
    │           │   │   │   │   ├── intranges.cpython-311.pyc
    │           │   │   │   │   ├── package_data.cpython-311.pyc
    │           │   │   │   │   └── uts46data.cpython-311.pyc
    │           │   │   │   ├── codec.py
    │           │   │   │   ├── compat.py
    │           │   │   │   ├── core.py
    │           │   │   │   ├── idnadata.py
    │           │   │   │   ├── intranges.py
    │           │   │   │   ├── package_data.py
    │           │   │   │   ├── py.typed
    │           │   │   │   └── uts46data.py
    │           │   │   ├── msgpack
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── exceptions.cpython-311.pyc
    │           │   │   │   │   ├── ext.cpython-311.pyc
    │           │   │   │   │   └── fallback.cpython-311.pyc
    │           │   │   │   ├── exceptions.py
    │           │   │   │   ├── ext.py
    │           │   │   │   └── fallback.py
    │           │   │   ├── packaging
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _elffile.cpython-311.pyc
    │           │   │   │   │   ├── _manylinux.cpython-311.pyc
    │           │   │   │   │   ├── _musllinux.cpython-311.pyc
    │           │   │   │   │   ├── _parser.cpython-311.pyc
    │           │   │   │   │   ├── _structures.cpython-311.pyc
    │           │   │   │   │   ├── _tokenizer.cpython-311.pyc
    │           │   │   │   │   ├── markers.cpython-311.pyc
    │           │   │   │   │   ├── metadata.cpython-311.pyc
    │           │   │   │   │   ├── requirements.cpython-311.pyc
    │           │   │   │   │   ├── specifiers.cpython-311.pyc
    │           │   │   │   │   ├── tags.cpython-311.pyc
    │           │   │   │   │   ├── utils.cpython-311.pyc
    │           │   │   │   │   └── version.cpython-311.pyc
    │           │   │   │   ├── _elffile.py
    │           │   │   │   ├── _manylinux.py
    │           │   │   │   ├── _musllinux.py
    │           │   │   │   ├── _parser.py
    │           │   │   │   ├── _structures.py
    │           │   │   │   ├── _tokenizer.py
    │           │   │   │   ├── markers.py
    │           │   │   │   ├── metadata.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── requirements.py
    │           │   │   │   ├── specifiers.py
    │           │   │   │   ├── tags.py
    │           │   │   │   ├── utils.py
    │           │   │   │   └── version.py
    │           │   │   ├── pkg_resources
    │           │   │   │   ├── __init__.py
    │           │   │   │   └── __pycache__
    │           │   │   │       └── __init__.cpython-311.pyc
    │           │   │   ├── platformdirs
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   ├── android.cpython-311.pyc
    │           │   │   │   │   ├── api.cpython-311.pyc
    │           │   │   │   │   ├── macos.cpython-311.pyc
    │           │   │   │   │   ├── unix.cpython-311.pyc
    │           │   │   │   │   ├── version.cpython-311.pyc
    │           │   │   │   │   └── windows.cpython-311.pyc
    │           │   │   │   ├── android.py
    │           │   │   │   ├── api.py
    │           │   │   │   ├── macos.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── unix.py
    │           │   │   │   ├── version.py
    │           │   │   │   └── windows.py
    │           │   │   ├── pygments
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   ├── cmdline.cpython-311.pyc
    │           │   │   │   │   ├── console.cpython-311.pyc
    │           │   │   │   │   ├── filter.cpython-311.pyc
    │           │   │   │   │   ├── formatter.cpython-311.pyc
    │           │   │   │   │   ├── lexer.cpython-311.pyc
    │           │   │   │   │   ├── modeline.cpython-311.pyc
    │           │   │   │   │   ├── plugin.cpython-311.pyc
    │           │   │   │   │   ├── regexopt.cpython-311.pyc
    │           │   │   │   │   ├── scanner.cpython-311.pyc
    │           │   │   │   │   ├── sphinxext.cpython-311.pyc
    │           │   │   │   │   ├── style.cpython-311.pyc
    │           │   │   │   │   ├── token.cpython-311.pyc
    │           │   │   │   │   ├── unistring.cpython-311.pyc
    │           │   │   │   │   └── util.cpython-311.pyc
    │           │   │   │   ├── cmdline.py
    │           │   │   │   ├── console.py
    │           │   │   │   ├── filter.py
    │           │   │   │   ├── filters
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   └── __pycache__
    │           │   │   │   │       └── __init__.cpython-311.pyc
    │           │   │   │   ├── formatter.py
    │           │   │   │   ├── formatters
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── _mapping.cpython-311.pyc
    │           │   │   │   │   │   ├── bbcode.cpython-311.pyc
    │           │   │   │   │   │   ├── groff.cpython-311.pyc
    │           │   │   │   │   │   ├── html.cpython-311.pyc
    │           │   │   │   │   │   ├── img.cpython-311.pyc
    │           │   │   │   │   │   ├── irc.cpython-311.pyc
    │           │   │   │   │   │   ├── latex.cpython-311.pyc
    │           │   │   │   │   │   ├── other.cpython-311.pyc
    │           │   │   │   │   │   ├── pangomarkup.cpython-311.pyc
    │           │   │   │   │   │   ├── rtf.cpython-311.pyc
    │           │   │   │   │   │   ├── svg.cpython-311.pyc
    │           │   │   │   │   │   ├── terminal.cpython-311.pyc
    │           │   │   │   │   │   └── terminal256.cpython-311.pyc
    │           │   │   │   │   ├── _mapping.py
    │           │   │   │   │   ├── bbcode.py
    │           │   │   │   │   ├── groff.py
    │           │   │   │   │   ├── html.py
    │           │   │   │   │   ├── img.py
    │           │   │   │   │   ├── irc.py
    │           │   │   │   │   ├── latex.py
    │           │   │   │   │   ├── other.py
    │           │   │   │   │   ├── pangomarkup.py
    │           │   │   │   │   ├── rtf.py
    │           │   │   │   │   ├── svg.py
    │           │   │   │   │   ├── terminal.py
    │           │   │   │   │   └── terminal256.py
    │           │   │   │   ├── lexer.py
    │           │   │   │   ├── lexers
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── _mapping.cpython-311.pyc
    │           │   │   │   │   │   └── python.cpython-311.pyc
    │           │   │   │   │   ├── _mapping.py
    │           │   │   │   │   └── python.py
    │           │   │   │   ├── modeline.py
    │           │   │   │   ├── plugin.py
    │           │   │   │   ├── regexopt.py
    │           │   │   │   ├── scanner.py
    │           │   │   │   ├── sphinxext.py
    │           │   │   │   ├── style.py
    │           │   │   │   ├── styles
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── _mapping.cpython-311.pyc
    │           │   │   │   │   └── _mapping.py
    │           │   │   │   ├── token.py
    │           │   │   │   ├── unistring.py
    │           │   │   │   └── util.py
    │           │   │   ├── pyproject_hooks
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _compat.cpython-311.pyc
    │           │   │   │   │   └── _impl.cpython-311.pyc
    │           │   │   │   ├── _compat.py
    │           │   │   │   ├── _impl.py
    │           │   │   │   └── _in_process
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   └── _in_process.cpython-311.pyc
    │           │   │   │       └── _in_process.py
    │           │   │   ├── requests
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __version__.cpython-311.pyc
    │           │   │   │   │   ├── _internal_utils.cpython-311.pyc
    │           │   │   │   │   ├── adapters.cpython-311.pyc
    │           │   │   │   │   ├── api.cpython-311.pyc
    │           │   │   │   │   ├── auth.cpython-311.pyc
    │           │   │   │   │   ├── certs.cpython-311.pyc
    │           │   │   │   │   ├── compat.cpython-311.pyc
    │           │   │   │   │   ├── cookies.cpython-311.pyc
    │           │   │   │   │   ├── exceptions.cpython-311.pyc
    │           │   │   │   │   ├── help.cpython-311.pyc
    │           │   │   │   │   ├── hooks.cpython-311.pyc
    │           │   │   │   │   ├── models.cpython-311.pyc
    │           │   │   │   │   ├── packages.cpython-311.pyc
    │           │   │   │   │   ├── sessions.cpython-311.pyc
    │           │   │   │   │   ├── status_codes.cpython-311.pyc
    │           │   │   │   │   ├── structures.cpython-311.pyc
    │           │   │   │   │   └── utils.cpython-311.pyc
    │           │   │   │   ├── __version__.py
    │           │   │   │   ├── _internal_utils.py
    │           │   │   │   ├── adapters.py
    │           │   │   │   ├── api.py
    │           │   │   │   ├── auth.py
    │           │   │   │   ├── certs.py
    │           │   │   │   ├── compat.py
    │           │   │   │   ├── cookies.py
    │           │   │   │   ├── exceptions.py
    │           │   │   │   ├── help.py
    │           │   │   │   ├── hooks.py
    │           │   │   │   ├── models.py
    │           │   │   │   ├── packages.py
    │           │   │   │   ├── sessions.py
    │           │   │   │   ├── status_codes.py
    │           │   │   │   ├── structures.py
    │           │   │   │   └── utils.py
    │           │   │   ├── resolvelib
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── providers.cpython-311.pyc
    │           │   │   │   │   ├── reporters.cpython-311.pyc
    │           │   │   │   │   ├── resolvers.cpython-311.pyc
    │           │   │   │   │   └── structs.cpython-311.pyc
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── collections_abc.cpython-311.pyc
    │           │   │   │   │   └── collections_abc.py
    │           │   │   │   ├── providers.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── reporters.py
    │           │   │   │   ├── resolvers.py
    │           │   │   │   └── structs.py
    │           │   │   ├── rich
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   ├── _cell_widths.cpython-311.pyc
    │           │   │   │   │   ├── _emoji_codes.cpython-311.pyc
    │           │   │   │   │   ├── _emoji_replace.cpython-311.pyc
    │           │   │   │   │   ├── _export_format.cpython-311.pyc
    │           │   │   │   │   ├── _extension.cpython-311.pyc
    │           │   │   │   │   ├── _fileno.cpython-311.pyc
    │           │   │   │   │   ├── _inspect.cpython-311.pyc
    │           │   │   │   │   ├── _log_render.cpython-311.pyc
    │           │   │   │   │   ├── _loop.cpython-311.pyc
    │           │   │   │   │   ├── _null_file.cpython-311.pyc
    │           │   │   │   │   ├── _palettes.cpython-311.pyc
    │           │   │   │   │   ├── _pick.cpython-311.pyc
    │           │   │   │   │   ├── _ratio.cpython-311.pyc
    │           │   │   │   │   ├── _spinners.cpython-311.pyc
    │           │   │   │   │   ├── _stack.cpython-311.pyc
    │           │   │   │   │   ├── _timer.cpython-311.pyc
    │           │   │   │   │   ├── _win32_console.cpython-311.pyc
    │           │   │   │   │   ├── _windows_renderer.cpython-311.pyc
    │           │   │   │   │   ├── _windows.cpython-311.pyc
    │           │   │   │   │   ├── _wrap.cpython-311.pyc
    │           │   │   │   │   ├── abc.cpython-311.pyc
    │           │   │   │   │   ├── align.cpython-311.pyc
    │           │   │   │   │   ├── ansi.cpython-311.pyc
    │           │   │   │   │   ├── bar.cpython-311.pyc
    │           │   │   │   │   ├── box.cpython-311.pyc
    │           │   │   │   │   ├── cells.cpython-311.pyc
    │           │   │   │   │   ├── color_triplet.cpython-311.pyc
    │           │   │   │   │   ├── color.cpython-311.pyc
    │           │   │   │   │   ├── columns.cpython-311.pyc
    │           │   │   │   │   ├── console.cpython-311.pyc
    │           │   │   │   │   ├── constrain.cpython-311.pyc
    │           │   │   │   │   ├── containers.cpython-311.pyc
    │           │   │   │   │   ├── control.cpython-311.pyc
    │           │   │   │   │   ├── default_styles.cpython-311.pyc
    │           │   │   │   │   ├── diagnose.cpython-311.pyc
    │           │   │   │   │   ├── emoji.cpython-311.pyc
    │           │   │   │   │   ├── errors.cpython-311.pyc
    │           │   │   │   │   ├── file_proxy.cpython-311.pyc
    │           │   │   │   │   ├── filesize.cpython-311.pyc
    │           │   │   │   │   ├── highlighter.cpython-311.pyc
    │           │   │   │   │   ├── json.cpython-311.pyc
    │           │   │   │   │   ├── jupyter.cpython-311.pyc
    │           │   │   │   │   ├── layout.cpython-311.pyc
    │           │   │   │   │   ├── live_render.cpython-311.pyc
    │           │   │   │   │   ├── live.cpython-311.pyc
    │           │   │   │   │   ├── logging.cpython-311.pyc
    │           │   │   │   │   ├── markup.cpython-311.pyc
    │           │   │   │   │   ├── measure.cpython-311.pyc
    │           │   │   │   │   ├── padding.cpython-311.pyc
    │           │   │   │   │   ├── pager.cpython-311.pyc
    │           │   │   │   │   ├── palette.cpython-311.pyc
    │           │   │   │   │   ├── panel.cpython-311.pyc
    │           │   │   │   │   ├── pretty.cpython-311.pyc
    │           │   │   │   │   ├── progress_bar.cpython-311.pyc
    │           │   │   │   │   ├── progress.cpython-311.pyc
    │           │   │   │   │   ├── prompt.cpython-311.pyc
    │           │   │   │   │   ├── protocol.cpython-311.pyc
    │           │   │   │   │   ├── region.cpython-311.pyc
    │           │   │   │   │   ├── repr.cpython-311.pyc
    │           │   │   │   │   ├── rule.cpython-311.pyc
    │           │   │   │   │   ├── scope.cpython-311.pyc
    │           │   │   │   │   ├── screen.cpython-311.pyc
    │           │   │   │   │   ├── segment.cpython-311.pyc
    │           │   │   │   │   ├── spinner.cpython-311.pyc
    │           │   │   │   │   ├── status.cpython-311.pyc
    │           │   │   │   │   ├── style.cpython-311.pyc
    │           │   │   │   │   ├── styled.cpython-311.pyc
    │           │   │   │   │   ├── syntax.cpython-311.pyc
    │           │   │   │   │   ├── table.cpython-311.pyc
    │           │   │   │   │   ├── terminal_theme.cpython-311.pyc
    │           │   │   │   │   ├── text.cpython-311.pyc
    │           │   │   │   │   ├── theme.cpython-311.pyc
    │           │   │   │   │   ├── themes.cpython-311.pyc
    │           │   │   │   │   ├── traceback.cpython-311.pyc
    │           │   │   │   │   └── tree.cpython-311.pyc
    │           │   │   │   ├── _cell_widths.py
    │           │   │   │   ├── _emoji_codes.py
    │           │   │   │   ├── _emoji_replace.py
    │           │   │   │   ├── _export_format.py
    │           │   │   │   ├── _extension.py
    │           │   │   │   ├── _fileno.py
    │           │   │   │   ├── _inspect.py
    │           │   │   │   ├── _log_render.py
    │           │   │   │   ├── _loop.py
    │           │   │   │   ├── _null_file.py
    │           │   │   │   ├── _palettes.py
    │           │   │   │   ├── _pick.py
    │           │   │   │   ├── _ratio.py
    │           │   │   │   ├── _spinners.py
    │           │   │   │   ├── _stack.py
    │           │   │   │   ├── _timer.py
    │           │   │   │   ├── _win32_console.py
    │           │   │   │   ├── _windows_renderer.py
    │           │   │   │   ├── _windows.py
    │           │   │   │   ├── _wrap.py
    │           │   │   │   ├── abc.py
    │           │   │   │   ├── align.py
    │           │   │   │   ├── ansi.py
    │           │   │   │   ├── bar.py
    │           │   │   │   ├── box.py
    │           │   │   │   ├── cells.py
    │           │   │   │   ├── color_triplet.py
    │           │   │   │   ├── color.py
    │           │   │   │   ├── columns.py
    │           │   │   │   ├── console.py
    │           │   │   │   ├── constrain.py
    │           │   │   │   ├── containers.py
    │           │   │   │   ├── control.py
    │           │   │   │   ├── default_styles.py
    │           │   │   │   ├── diagnose.py
    │           │   │   │   ├── emoji.py
    │           │   │   │   ├── errors.py
    │           │   │   │   ├── file_proxy.py
    │           │   │   │   ├── filesize.py
    │           │   │   │   ├── highlighter.py
    │           │   │   │   ├── json.py
    │           │   │   │   ├── jupyter.py
    │           │   │   │   ├── layout.py
    │           │   │   │   ├── live_render.py
    │           │   │   │   ├── live.py
    │           │   │   │   ├── logging.py
    │           │   │   │   ├── markup.py
    │           │   │   │   ├── measure.py
    │           │   │   │   ├── padding.py
    │           │   │   │   ├── pager.py
    │           │   │   │   ├── palette.py
    │           │   │   │   ├── panel.py
    │           │   │   │   ├── pretty.py
    │           │   │   │   ├── progress_bar.py
    │           │   │   │   ├── progress.py
    │           │   │   │   ├── prompt.py
    │           │   │   │   ├── protocol.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── region.py
    │           │   │   │   ├── repr.py
    │           │   │   │   ├── rule.py
    │           │   │   │   ├── scope.py
    │           │   │   │   ├── screen.py
    │           │   │   │   ├── segment.py
    │           │   │   │   ├── spinner.py
    │           │   │   │   ├── status.py
    │           │   │   │   ├── style.py
    │           │   │   │   ├── styled.py
    │           │   │   │   ├── syntax.py
    │           │   │   │   ├── table.py
    │           │   │   │   ├── terminal_theme.py
    │           │   │   │   ├── text.py
    │           │   │   │   ├── theme.py
    │           │   │   │   ├── themes.py
    │           │   │   │   ├── traceback.py
    │           │   │   │   └── tree.py
    │           │   │   ├── tomli
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _parser.cpython-311.pyc
    │           │   │   │   │   ├── _re.cpython-311.pyc
    │           │   │   │   │   └── _types.cpython-311.pyc
    │           │   │   │   ├── _parser.py
    │           │   │   │   ├── _re.py
    │           │   │   │   ├── _types.py
    │           │   │   │   └── py.typed
    │           │   │   ├── truststore
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _api.cpython-311.pyc
    │           │   │   │   │   ├── _macos.cpython-311.pyc
    │           │   │   │   │   ├── _openssl.cpython-311.pyc
    │           │   │   │   │   ├── _ssl_constants.cpython-311.pyc
    │           │   │   │   │   └── _windows.cpython-311.pyc
    │           │   │   │   ├── _api.py
    │           │   │   │   ├── _macos.py
    │           │   │   │   ├── _openssl.py
    │           │   │   │   ├── _ssl_constants.py
    │           │   │   │   ├── _windows.py
    │           │   │   │   └── py.typed
    │           │   │   ├── typing_extensions.py
    │           │   │   ├── urllib3
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _collections.cpython-311.pyc
    │           │   │   │   │   ├── _version.cpython-311.pyc
    │           │   │   │   │   ├── connection.cpython-311.pyc
    │           │   │   │   │   ├── connectionpool.cpython-311.pyc
    │           │   │   │   │   ├── exceptions.cpython-311.pyc
    │           │   │   │   │   ├── fields.cpython-311.pyc
    │           │   │   │   │   ├── filepost.cpython-311.pyc
    │           │   │   │   │   ├── poolmanager.cpython-311.pyc
    │           │   │   │   │   ├── request.cpython-311.pyc
    │           │   │   │   │   └── response.cpython-311.pyc
    │           │   │   │   ├── _collections.py
    │           │   │   │   ├── _version.py
    │           │   │   │   ├── connection.py
    │           │   │   │   ├── connectionpool.py
    │           │   │   │   ├── contrib
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── _appengine_environ.cpython-311.pyc
    │           │   │   │   │   │   ├── appengine.cpython-311.pyc
    │           │   │   │   │   │   ├── ntlmpool.cpython-311.pyc
    │           │   │   │   │   │   ├── pyopenssl.cpython-311.pyc
    │           │   │   │   │   │   ├── securetransport.cpython-311.pyc
    │           │   │   │   │   │   └── socks.cpython-311.pyc
    │           │   │   │   │   ├── _appengine_environ.py
    │           │   │   │   │   ├── _securetransport
    │           │   │   │   │   │   ├── __init__.py
    │           │   │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   │   ├── bindings.cpython-311.pyc
    │           │   │   │   │   │   │   └── low_level.cpython-311.pyc
    │           │   │   │   │   │   ├── bindings.py
    │           │   │   │   │   │   └── low_level.py
    │           │   │   │   │   ├── appengine.py
    │           │   │   │   │   ├── ntlmpool.py
    │           │   │   │   │   ├── pyopenssl.py
    │           │   │   │   │   ├── securetransport.py
    │           │   │   │   │   └── socks.py
    │           │   │   │   ├── exceptions.py
    │           │   │   │   ├── fields.py
    │           │   │   │   ├── filepost.py
    │           │   │   │   ├── packages
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── six.cpython-311.pyc
    │           │   │   │   │   ├── backports
    │           │   │   │   │   │   ├── __init__.py
    │           │   │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   │   ├── makefile.cpython-311.pyc
    │           │   │   │   │   │   │   └── weakref_finalize.cpython-311.pyc
    │           │   │   │   │   │   ├── makefile.py
    │           │   │   │   │   │   └── weakref_finalize.py
    │           │   │   │   │   └── six.py
    │           │   │   │   ├── poolmanager.py
    │           │   │   │   ├── request.py
    │           │   │   │   ├── response.py
    │           │   │   │   └── util
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   ├── connection.cpython-311.pyc
    │           │   │   │       │   ├── proxy.cpython-311.pyc
    │           │   │   │       │   ├── queue.cpython-311.pyc
    │           │   │   │       │   ├── request.cpython-311.pyc
    │           │   │   │       │   ├── response.cpython-311.pyc
    │           │   │   │       │   ├── retry.cpython-311.pyc
    │           │   │   │       │   ├── ssl_.cpython-311.pyc
    │           │   │   │       │   ├── ssl_match_hostname.cpython-311.pyc
    │           │   │   │       │   ├── ssltransport.cpython-311.pyc
    │           │   │   │       │   ├── timeout.cpython-311.pyc
    │           │   │   │       │   ├── url.cpython-311.pyc
    │           │   │   │       │   └── wait.cpython-311.pyc
    │           │   │   │       ├── connection.py
    │           │   │   │       ├── proxy.py
    │           │   │   │       ├── queue.py
    │           │   │   │       ├── request.py
    │           │   │   │       ├── response.py
    │           │   │   │       ├── retry.py
    │           │   │   │       ├── ssl_.py
    │           │   │   │       ├── ssl_match_hostname.py
    │           │   │   │       ├── ssltransport.py
    │           │   │   │       ├── timeout.py
    │           │   │   │       ├── url.py
    │           │   │   │       └── wait.py
    │           │   │   └── vendor.txt
    │           │   └── py.typed
    │           ├── pip-24.3.1.dist-info
    │           │   ├── AUTHORS.txt
    │           │   ├── entry_points.txt
    │           │   ├── INSTALLER
    │           │   ├── LICENSE.txt
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── REQUESTED
    │           │   ├── top_level.txt
    │           │   └── WHEEL
    │           ├── pkg_resources
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   └── __init__.cpython-311.pyc
    │           │   ├── api_tests.txt
    │           │   ├── py.typed
    │           │   └── tests
    │           │       ├── __init__.py
    │           │       ├── __pycache__
    │           │       │   ├── __init__.cpython-311.pyc
    │           │       │   ├── test_find_distributions.cpython-311.pyc
    │           │       │   ├── test_integration_zope_interface.cpython-311.pyc
    │           │       │   ├── test_markers.cpython-311.pyc
    │           │       │   ├── test_pkg_resources.cpython-311.pyc
    │           │       │   ├── test_resources.cpython-311.pyc
    │           │       │   └── test_working_set.cpython-311.pyc
    │           │       ├── data
    │           │       │   ├── my-test-package_unpacked-egg
    │           │       │   │   └── my_test_package-1.0-py3.7.egg
    │           │       │   │       └── EGG-INFO
    │           │       │   │           ├── dependency_links.txt
    │           │       │   │           ├── PKG-INFO
    │           │       │   │           ├── SOURCES.txt
    │           │       │   │           ├── top_level.txt
    │           │       │   │           └── zip-safe
    │           │       │   ├── my-test-package_zipped-egg
    │           │       │   │   └── my_test_package-1.0-py3.7.egg
    │           │       │   ├── my-test-package-source
    │           │       │   │   ├── __pycache__
    │           │       │   │   │   └── setup.cpython-311.pyc
    │           │       │   │   ├── setup.cfg
    │           │       │   │   └── setup.py
    │           │       │   └── my-test-package-zip
    │           │       │       └── my-test-package.zip
    │           │       ├── test_find_distributions.py
    │           │       ├── test_integration_zope_interface.py
    │           │       ├── test_markers.py
    │           │       ├── test_pkg_resources.py
    │           │       ├── test_resources.py
    │           │       └── test_working_set.py
    │           ├── pluggy
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── _callers.cpython-311.pyc
    │           │   │   ├── _hooks.cpython-311.pyc
    │           │   │   ├── _manager.cpython-311.pyc
    │           │   │   ├── _result.cpython-311.pyc
    │           │   │   ├── _tracing.cpython-311.pyc
    │           │   │   ├── _version.cpython-311.pyc
    │           │   │   └── _warnings.cpython-311.pyc
    │           │   ├── _callers.py
    │           │   ├── _hooks.py
    │           │   ├── _manager.py
    │           │   ├── _result.py
    │           │   ├── _tracing.py
    │           │   ├── _version.py
    │           │   ├── _warnings.py
    │           │   └── py.typed
    │           ├── pluggy-1.5.0.dist-info
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── top_level.txt
    │           │   └── WHEEL
    │           ├── pyproject_hooks
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   └── _impl.cpython-311.pyc
    │           │   ├── _impl.py
    │           │   ├── _in_process
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   └── _in_process.cpython-311.pyc
    │           │   │   └── _in_process.py
    │           │   └── py.typed
    │           ├── pyproject_hooks-1.2.0.dist-info
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   └── WHEEL
    │           ├── setuptools
    │           │   ├── __init__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   ├── _core_metadata.cpython-311.pyc
    │           │   │   ├── _entry_points.cpython-311.pyc
    │           │   │   ├── _imp.cpython-311.pyc
    │           │   │   ├── _importlib.cpython-311.pyc
    │           │   │   ├── _itertools.cpython-311.pyc
    │           │   │   ├── _normalization.cpython-311.pyc
    │           │   │   ├── _path.cpython-311.pyc
    │           │   │   ├── _reqs.cpython-311.pyc
    │           │   │   ├── archive_util.cpython-311.pyc
    │           │   │   ├── build_meta.cpython-311.pyc
    │           │   │   ├── depends.cpython-311.pyc
    │           │   │   ├── discovery.cpython-311.pyc
    │           │   │   ├── dist.cpython-311.pyc
    │           │   │   ├── errors.cpython-311.pyc
    │           │   │   ├── extension.cpython-311.pyc
    │           │   │   ├── glob.cpython-311.pyc
    │           │   │   ├── installer.cpython-311.pyc
    │           │   │   ├── launch.cpython-311.pyc
    │           │   │   ├── logging.cpython-311.pyc
    │           │   │   ├── modified.cpython-311.pyc
    │           │   │   ├── monkey.cpython-311.pyc
    │           │   │   ├── msvc.cpython-311.pyc
    │           │   │   ├── namespaces.cpython-311.pyc
    │           │   │   ├── package_index.cpython-311.pyc
    │           │   │   ├── sandbox.cpython-311.pyc
    │           │   │   ├── unicode_utils.cpython-311.pyc
    │           │   │   ├── version.cpython-311.pyc
    │           │   │   ├── warnings.cpython-311.pyc
    │           │   │   ├── wheel.cpython-311.pyc
    │           │   │   └── windows_support.cpython-311.pyc
    │           │   ├── _core_metadata.py
    │           │   ├── _distutils
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── _collections.cpython-311.pyc
    │           │   │   │   ├── _functools.cpython-311.pyc
    │           │   │   │   ├── _itertools.cpython-311.pyc
    │           │   │   │   ├── _log.cpython-311.pyc
    │           │   │   │   ├── _macos_compat.cpython-311.pyc
    │           │   │   │   ├── _modified.cpython-311.pyc
    │           │   │   │   ├── _msvccompiler.cpython-311.pyc
    │           │   │   │   ├── archive_util.cpython-311.pyc
    │           │   │   │   ├── bcppcompiler.cpython-311.pyc
    │           │   │   │   ├── ccompiler.cpython-311.pyc
    │           │   │   │   ├── cmd.cpython-311.pyc
    │           │   │   │   ├── config.cpython-311.pyc
    │           │   │   │   ├── core.cpython-311.pyc
    │           │   │   │   ├── cygwinccompiler.cpython-311.pyc
    │           │   │   │   ├── debug.cpython-311.pyc
    │           │   │   │   ├── dep_util.cpython-311.pyc
    │           │   │   │   ├── dir_util.cpython-311.pyc
    │           │   │   │   ├── dist.cpython-311.pyc
    │           │   │   │   ├── errors.cpython-311.pyc
    │           │   │   │   ├── extension.cpython-311.pyc
    │           │   │   │   ├── fancy_getopt.cpython-311.pyc
    │           │   │   │   ├── file_util.cpython-311.pyc
    │           │   │   │   ├── filelist.cpython-311.pyc
    │           │   │   │   ├── log.cpython-311.pyc
    │           │   │   │   ├── spawn.cpython-311.pyc
    │           │   │   │   ├── sysconfig.cpython-311.pyc
    │           │   │   │   ├── text_file.cpython-311.pyc
    │           │   │   │   ├── unixccompiler.cpython-311.pyc
    │           │   │   │   ├── util.cpython-311.pyc
    │           │   │   │   ├── version.cpython-311.pyc
    │           │   │   │   ├── versionpredicate.cpython-311.pyc
    │           │   │   │   └── zosccompiler.cpython-311.pyc
    │           │   │   ├── _collections.py
    │           │   │   ├── _functools.py
    │           │   │   ├── _itertools.py
    │           │   │   ├── _log.py
    │           │   │   ├── _macos_compat.py
    │           │   │   ├── _modified.py
    │           │   │   ├── _msvccompiler.py
    │           │   │   ├── archive_util.py
    │           │   │   ├── bcppcompiler.py
    │           │   │   ├── ccompiler.py
    │           │   │   ├── cmd.py
    │           │   │   ├── command
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _framework_compat.cpython-311.pyc
    │           │   │   │   │   ├── bdist_dumb.cpython-311.pyc
    │           │   │   │   │   ├── bdist_rpm.cpython-311.pyc
    │           │   │   │   │   ├── bdist.cpython-311.pyc
    │           │   │   │   │   ├── build_clib.cpython-311.pyc
    │           │   │   │   │   ├── build_ext.cpython-311.pyc
    │           │   │   │   │   ├── build_py.cpython-311.pyc
    │           │   │   │   │   ├── build_scripts.cpython-311.pyc
    │           │   │   │   │   ├── build.cpython-311.pyc
    │           │   │   │   │   ├── check.cpython-311.pyc
    │           │   │   │   │   ├── clean.cpython-311.pyc
    │           │   │   │   │   ├── config.cpython-311.pyc
    │           │   │   │   │   ├── install_data.cpython-311.pyc
    │           │   │   │   │   ├── install_egg_info.cpython-311.pyc
    │           │   │   │   │   ├── install_headers.cpython-311.pyc
    │           │   │   │   │   ├── install_lib.cpython-311.pyc
    │           │   │   │   │   ├── install_scripts.cpython-311.pyc
    │           │   │   │   │   ├── install.cpython-311.pyc
    │           │   │   │   │   ├── register.cpython-311.pyc
    │           │   │   │   │   ├── sdist.cpython-311.pyc
    │           │   │   │   │   └── upload.cpython-311.pyc
    │           │   │   │   ├── _framework_compat.py
    │           │   │   │   ├── bdist_dumb.py
    │           │   │   │   ├── bdist_rpm.py
    │           │   │   │   ├── bdist.py
    │           │   │   │   ├── build_clib.py
    │           │   │   │   ├── build_ext.py
    │           │   │   │   ├── build_py.py
    │           │   │   │   ├── build_scripts.py
    │           │   │   │   ├── build.py
    │           │   │   │   ├── check.py
    │           │   │   │   ├── clean.py
    │           │   │   │   ├── config.py
    │           │   │   │   ├── install_data.py
    │           │   │   │   ├── install_egg_info.py
    │           │   │   │   ├── install_headers.py
    │           │   │   │   ├── install_lib.py
    │           │   │   │   ├── install_scripts.py
    │           │   │   │   ├── install.py
    │           │   │   │   ├── register.py
    │           │   │   │   ├── sdist.py
    │           │   │   │   └── upload.py
    │           │   │   ├── compat
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── py38.cpython-311.pyc
    │           │   │   │   │   └── py39.cpython-311.pyc
    │           │   │   │   ├── py38.py
    │           │   │   │   └── py39.py
    │           │   │   ├── config.py
    │           │   │   ├── core.py
    │           │   │   ├── cygwinccompiler.py
    │           │   │   ├── debug.py
    │           │   │   ├── dep_util.py
    │           │   │   ├── dir_util.py
    │           │   │   ├── dist.py
    │           │   │   ├── errors.py
    │           │   │   ├── extension.py
    │           │   │   ├── fancy_getopt.py
    │           │   │   ├── file_util.py
    │           │   │   ├── filelist.py
    │           │   │   ├── log.py
    │           │   │   ├── spawn.py
    │           │   │   ├── sysconfig.py
    │           │   │   ├── tests
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── support.cpython-311.pyc
    │           │   │   │   │   ├── test_archive_util.cpython-311.pyc
    │           │   │   │   │   ├── test_bdist_dumb.cpython-311.pyc
    │           │   │   │   │   ├── test_bdist_rpm.cpython-311.pyc
    │           │   │   │   │   ├── test_bdist.cpython-311.pyc
    │           │   │   │   │   ├── test_build_clib.cpython-311.pyc
    │           │   │   │   │   ├── test_build_ext.cpython-311.pyc
    │           │   │   │   │   ├── test_build_py.cpython-311.pyc
    │           │   │   │   │   ├── test_build_scripts.cpython-311.pyc
    │           │   │   │   │   ├── test_build.cpython-311.pyc
    │           │   │   │   │   ├── test_ccompiler.cpython-311.pyc
    │           │   │   │   │   ├── test_check.cpython-311.pyc
    │           │   │   │   │   ├── test_clean.cpython-311.pyc
    │           │   │   │   │   ├── test_cmd.cpython-311.pyc
    │           │   │   │   │   ├── test_config_cmd.cpython-311.pyc
    │           │   │   │   │   ├── test_config.cpython-311.pyc
    │           │   │   │   │   ├── test_core.cpython-311.pyc
    │           │   │   │   │   ├── test_cygwinccompiler.cpython-311.pyc
    │           │   │   │   │   ├── test_dir_util.cpython-311.pyc
    │           │   │   │   │   ├── test_dist.cpython-311.pyc
    │           │   │   │   │   ├── test_extension.cpython-311.pyc
    │           │   │   │   │   ├── test_file_util.cpython-311.pyc
    │           │   │   │   │   ├── test_filelist.cpython-311.pyc
    │           │   │   │   │   ├── test_install_data.cpython-311.pyc
    │           │   │   │   │   ├── test_install_headers.cpython-311.pyc
    │           │   │   │   │   ├── test_install_lib.cpython-311.pyc
    │           │   │   │   │   ├── test_install_scripts.cpython-311.pyc
    │           │   │   │   │   ├── test_install.cpython-311.pyc
    │           │   │   │   │   ├── test_log.cpython-311.pyc
    │           │   │   │   │   ├── test_mingwccompiler.cpython-311.pyc
    │           │   │   │   │   ├── test_modified.cpython-311.pyc
    │           │   │   │   │   ├── test_msvccompiler.cpython-311.pyc
    │           │   │   │   │   ├── test_register.cpython-311.pyc
    │           │   │   │   │   ├── test_sdist.cpython-311.pyc
    │           │   │   │   │   ├── test_spawn.cpython-311.pyc
    │           │   │   │   │   ├── test_sysconfig.cpython-311.pyc
    │           │   │   │   │   ├── test_text_file.cpython-311.pyc
    │           │   │   │   │   ├── test_unixccompiler.cpython-311.pyc
    │           │   │   │   │   ├── test_upload.cpython-311.pyc
    │           │   │   │   │   ├── test_util.cpython-311.pyc
    │           │   │   │   │   ├── test_version.cpython-311.pyc
    │           │   │   │   │   ├── test_versionpredicate.cpython-311.pyc
    │           │   │   │   │   └── unix_compat.cpython-311.pyc
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── py38.cpython-311.pyc
    │           │   │   │   │   └── py38.py
    │           │   │   │   ├── support.py
    │           │   │   │   ├── test_archive_util.py
    │           │   │   │   ├── test_bdist_dumb.py
    │           │   │   │   ├── test_bdist_rpm.py
    │           │   │   │   ├── test_bdist.py
    │           │   │   │   ├── test_build_clib.py
    │           │   │   │   ├── test_build_ext.py
    │           │   │   │   ├── test_build_py.py
    │           │   │   │   ├── test_build_scripts.py
    │           │   │   │   ├── test_build.py
    │           │   │   │   ├── test_ccompiler.py
    │           │   │   │   ├── test_check.py
    │           │   │   │   ├── test_clean.py
    │           │   │   │   ├── test_cmd.py
    │           │   │   │   ├── test_config_cmd.py
    │           │   │   │   ├── test_config.py
    │           │   │   │   ├── test_core.py
    │           │   │   │   ├── test_cygwinccompiler.py
    │           │   │   │   ├── test_dir_util.py
    │           │   │   │   ├── test_dist.py
    │           │   │   │   ├── test_extension.py
    │           │   │   │   ├── test_file_util.py
    │           │   │   │   ├── test_filelist.py
    │           │   │   │   ├── test_install_data.py
    │           │   │   │   ├── test_install_headers.py
    │           │   │   │   ├── test_install_lib.py
    │           │   │   │   ├── test_install_scripts.py
    │           │   │   │   ├── test_install.py
    │           │   │   │   ├── test_log.py
    │           │   │   │   ├── test_mingwccompiler.py
    │           │   │   │   ├── test_modified.py
    │           │   │   │   ├── test_msvccompiler.py
    │           │   │   │   ├── test_register.py
    │           │   │   │   ├── test_sdist.py
    │           │   │   │   ├── test_spawn.py
    │           │   │   │   ├── test_sysconfig.py
    │           │   │   │   ├── test_text_file.py
    │           │   │   │   ├── test_unixccompiler.py
    │           │   │   │   ├── test_upload.py
    │           │   │   │   ├── test_util.py
    │           │   │   │   ├── test_version.py
    │           │   │   │   ├── test_versionpredicate.py
    │           │   │   │   └── unix_compat.py
    │           │   │   ├── text_file.py
    │           │   │   ├── unixccompiler.py
    │           │   │   ├── util.py
    │           │   │   ├── version.py
    │           │   │   ├── versionpredicate.py
    │           │   │   └── zosccompiler.py
    │           │   ├── _entry_points.py
    │           │   ├── _imp.py
    │           │   ├── _importlib.py
    │           │   ├── _itertools.py
    │           │   ├── _normalization.py
    │           │   ├── _path.py
    │           │   ├── _reqs.py
    │           │   ├── _vendor
    │           │   │   ├── __pycache__
    │           │   │   │   └── typing_extensions.cpython-311.pyc
    │           │   │   ├── autocommand
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── autoasync.cpython-311.pyc
    │           │   │   │   │   ├── autocommand.cpython-311.pyc
    │           │   │   │   │   ├── automain.cpython-311.pyc
    │           │   │   │   │   ├── autoparse.cpython-311.pyc
    │           │   │   │   │   └── errors.cpython-311.pyc
    │           │   │   │   ├── autoasync.py
    │           │   │   │   ├── autocommand.py
    │           │   │   │   ├── automain.py
    │           │   │   │   ├── autoparse.py
    │           │   │   │   └── errors.py
    │           │   │   ├── autocommand-2.2.2.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── backports
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │   └── tarfile
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __main__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   └── __main__.cpython-311.pyc
    │           │   │   │       └── compat
    │           │   │   │           ├── __init__.py
    │           │   │   │           ├── __pycache__
    │           │   │   │           │   ├── __init__.cpython-311.pyc
    │           │   │   │           │   └── py38.cpython-311.pyc
    │           │   │   │           └── py38.py
    │           │   │   ├── backports.tarfile-1.2.0.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── importlib_metadata
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _adapters.cpython-311.pyc
    │           │   │   │   │   ├── _collections.cpython-311.pyc
    │           │   │   │   │   ├── _compat.cpython-311.pyc
    │           │   │   │   │   ├── _functools.cpython-311.pyc
    │           │   │   │   │   ├── _itertools.cpython-311.pyc
    │           │   │   │   │   ├── _meta.cpython-311.pyc
    │           │   │   │   │   ├── _text.cpython-311.pyc
    │           │   │   │   │   └── diagnose.cpython-311.pyc
    │           │   │   │   ├── _adapters.py
    │           │   │   │   ├── _collections.py
    │           │   │   │   ├── _compat.py
    │           │   │   │   ├── _functools.py
    │           │   │   │   ├── _itertools.py
    │           │   │   │   ├── _meta.py
    │           │   │   │   ├── _text.py
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── py311.cpython-311.pyc
    │           │   │   │   │   │   └── py39.cpython-311.pyc
    │           │   │   │   │   ├── py311.py
    │           │   │   │   │   └── py39.py
    │           │   │   │   ├── diagnose.py
    │           │   │   │   └── py.typed
    │           │   │   ├── importlib_metadata-8.0.0.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── importlib_resources
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _adapters.cpython-311.pyc
    │           │   │   │   │   ├── _common.cpython-311.pyc
    │           │   │   │   │   ├── _itertools.cpython-311.pyc
    │           │   │   │   │   ├── abc.cpython-311.pyc
    │           │   │   │   │   ├── functional.cpython-311.pyc
    │           │   │   │   │   ├── readers.cpython-311.pyc
    │           │   │   │   │   └── simple.cpython-311.pyc
    │           │   │   │   ├── _adapters.py
    │           │   │   │   ├── _common.py
    │           │   │   │   ├── _itertools.py
    │           │   │   │   ├── abc.py
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── py38.cpython-311.pyc
    │           │   │   │   │   │   └── py39.cpython-311.pyc
    │           │   │   │   │   ├── py38.py
    │           │   │   │   │   └── py39.py
    │           │   │   │   ├── functional.py
    │           │   │   │   ├── future
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── adapters.cpython-311.pyc
    │           │   │   │   │   └── adapters.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── readers.py
    │           │   │   │   ├── simple.py
    │           │   │   │   └── tests
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   ├── _path.cpython-311.pyc
    │           │   │   │       │   ├── test_compatibilty_files.cpython-311.pyc
    │           │   │   │       │   ├── test_contents.cpython-311.pyc
    │           │   │   │       │   ├── test_custom.cpython-311.pyc
    │           │   │   │       │   ├── test_files.cpython-311.pyc
    │           │   │   │       │   ├── test_functional.cpython-311.pyc
    │           │   │   │       │   ├── test_open.cpython-311.pyc
    │           │   │   │       │   ├── test_path.cpython-311.pyc
    │           │   │   │       │   ├── test_read.cpython-311.pyc
    │           │   │   │       │   ├── test_reader.cpython-311.pyc
    │           │   │   │       │   ├── test_resource.cpython-311.pyc
    │           │   │   │       │   ├── util.cpython-311.pyc
    │           │   │   │       │   └── zip.cpython-311.pyc
    │           │   │   │       ├── _path.py
    │           │   │   │       ├── compat
    │           │   │   │       │   ├── __init__.py
    │           │   │   │       │   ├── __pycache__
    │           │   │   │       │   │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   │   ├── py312.cpython-311.pyc
    │           │   │   │       │   │   └── py39.cpython-311.pyc
    │           │   │   │       │   ├── py312.py
    │           │   │   │       │   └── py39.py
    │           │   │   │       ├── data01
    │           │   │   │       │   ├── __init__.py
    │           │   │   │       │   ├── __pycache__
    │           │   │   │       │   │   └── __init__.cpython-311.pyc
    │           │   │   │       │   ├── binary.file
    │           │   │   │       │   ├── subdirectory
    │           │   │   │       │   │   ├── __init__.py
    │           │   │   │       │   │   ├── __pycache__
    │           │   │   │       │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │       │   │   └── binary.file
    │           │   │   │       │   ├── utf-16.file
    │           │   │   │       │   └── utf-8.file
    │           │   │   │       ├── data02
    │           │   │   │       │   ├── __init__.py
    │           │   │   │       │   ├── __pycache__
    │           │   │   │       │   │   └── __init__.cpython-311.pyc
    │           │   │   │       │   ├── one
    │           │   │   │       │   │   ├── __init__.py
    │           │   │   │       │   │   ├── __pycache__
    │           │   │   │       │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │       │   │   └── resource1.txt
    │           │   │   │       │   ├── subdirectory
    │           │   │   │       │   │   └── subsubdir
    │           │   │   │       │   │       └── resource.txt
    │           │   │   │       │   └── two
    │           │   │   │       │       ├── __init__.py
    │           │   │   │       │       ├── __pycache__
    │           │   │   │       │       │   └── __init__.cpython-311.pyc
    │           │   │   │       │       └── resource2.txt
    │           │   │   │       ├── namespacedata01
    │           │   │   │       │   ├── binary.file
    │           │   │   │       │   ├── subdirectory
    │           │   │   │       │   │   └── binary.file
    │           │   │   │       │   ├── utf-16.file
    │           │   │   │       │   └── utf-8.file
    │           │   │   │       ├── test_compatibilty_files.py
    │           │   │   │       ├── test_contents.py
    │           │   │   │       ├── test_custom.py
    │           │   │   │       ├── test_files.py
    │           │   │   │       ├── test_functional.py
    │           │   │   │       ├── test_open.py
    │           │   │   │       ├── test_path.py
    │           │   │   │       ├── test_read.py
    │           │   │   │       ├── test_reader.py
    │           │   │   │       ├── test_resource.py
    │           │   │   │       ├── util.py
    │           │   │   │       └── zip.py
    │           │   │   ├── importlib_resources-6.4.0.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── inflect
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── py38.cpython-311.pyc
    │           │   │   │   │   └── py38.py
    │           │   │   │   └── py.typed
    │           │   │   ├── inflect-7.3.1.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── jaraco
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   └── context.cpython-311.pyc
    │           │   │   │   ├── context.py
    │           │   │   │   ├── functools
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __init__.pyi
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │   │   └── py.typed
    │           │   │   │   └── text
    │           │   │   │       ├── __init__.py
    │           │   │   │       ├── __pycache__
    │           │   │   │       │   ├── __init__.cpython-311.pyc
    │           │   │   │       │   ├── layouts.cpython-311.pyc
    │           │   │   │       │   ├── show-newlines.cpython-311.pyc
    │           │   │   │       │   ├── strip-prefix.cpython-311.pyc
    │           │   │   │       │   ├── to-dvorak.cpython-311.pyc
    │           │   │   │       │   └── to-qwerty.cpython-311.pyc
    │           │   │   │       ├── layouts.py
    │           │   │   │       ├── Lorem ipsum.txt
    │           │   │   │       ├── show-newlines.py
    │           │   │   │       ├── strip-prefix.py
    │           │   │   │       ├── to-dvorak.py
    │           │   │   │       └── to-qwerty.py
    │           │   │   ├── jaraco.context-5.3.0.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── jaraco.functools-4.0.1.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── jaraco.text-3.12.1.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── more_itertools
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __init__.pyi
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── more.cpython-311.pyc
    │           │   │   │   │   └── recipes.cpython-311.pyc
    │           │   │   │   ├── more.py
    │           │   │   │   ├── more.pyi
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── recipes.py
    │           │   │   │   └── recipes.pyi
    │           │   │   ├── more_itertools-10.3.0.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   └── WHEEL
    │           │   │   ├── packaging
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _elffile.cpython-311.pyc
    │           │   │   │   │   ├── _manylinux.cpython-311.pyc
    │           │   │   │   │   ├── _musllinux.cpython-311.pyc
    │           │   │   │   │   ├── _parser.cpython-311.pyc
    │           │   │   │   │   ├── _structures.cpython-311.pyc
    │           │   │   │   │   ├── _tokenizer.cpython-311.pyc
    │           │   │   │   │   ├── markers.cpython-311.pyc
    │           │   │   │   │   ├── metadata.cpython-311.pyc
    │           │   │   │   │   ├── requirements.cpython-311.pyc
    │           │   │   │   │   ├── specifiers.cpython-311.pyc
    │           │   │   │   │   ├── tags.cpython-311.pyc
    │           │   │   │   │   ├── utils.cpython-311.pyc
    │           │   │   │   │   └── version.cpython-311.pyc
    │           │   │   │   ├── _elffile.py
    │           │   │   │   ├── _manylinux.py
    │           │   │   │   ├── _musllinux.py
    │           │   │   │   ├── _parser.py
    │           │   │   │   ├── _structures.py
    │           │   │   │   ├── _tokenizer.py
    │           │   │   │   ├── markers.py
    │           │   │   │   ├── metadata.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── requirements.py
    │           │   │   │   ├── specifiers.py
    │           │   │   │   ├── tags.py
    │           │   │   │   ├── utils.py
    │           │   │   │   └── version.py
    │           │   │   ├── packaging-24.1.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── LICENSE.APACHE
    │           │   │   │   ├── LICENSE.BSD
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   └── WHEEL
    │           │   │   ├── platformdirs
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   ├── android.cpython-311.pyc
    │           │   │   │   │   ├── api.cpython-311.pyc
    │           │   │   │   │   ├── macos.cpython-311.pyc
    │           │   │   │   │   ├── unix.cpython-311.pyc
    │           │   │   │   │   ├── version.cpython-311.pyc
    │           │   │   │   │   └── windows.cpython-311.pyc
    │           │   │   │   ├── android.py
    │           │   │   │   ├── api.py
    │           │   │   │   ├── macos.py
    │           │   │   │   ├── py.typed
    │           │   │   │   ├── unix.py
    │           │   │   │   ├── version.py
    │           │   │   │   └── windows.py
    │           │   │   ├── platformdirs-4.2.2.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── licenses
    │           │   │   │   │   └── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   └── WHEEL
    │           │   │   ├── ruff.toml
    │           │   │   ├── tomli
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _parser.cpython-311.pyc
    │           │   │   │   │   ├── _re.cpython-311.pyc
    │           │   │   │   │   └── _types.cpython-311.pyc
    │           │   │   │   ├── _parser.py
    │           │   │   │   ├── _re.py
    │           │   │   │   ├── _types.py
    │           │   │   │   └── py.typed
    │           │   │   ├── tomli-2.0.1.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   └── WHEEL
    │           │   │   ├── typeguard
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── _checkers.cpython-311.pyc
    │           │   │   │   │   ├── _config.cpython-311.pyc
    │           │   │   │   │   ├── _decorators.cpython-311.pyc
    │           │   │   │   │   ├── _exceptions.cpython-311.pyc
    │           │   │   │   │   ├── _functions.cpython-311.pyc
    │           │   │   │   │   ├── _importhook.cpython-311.pyc
    │           │   │   │   │   ├── _memo.cpython-311.pyc
    │           │   │   │   │   ├── _pytest_plugin.cpython-311.pyc
    │           │   │   │   │   ├── _suppression.cpython-311.pyc
    │           │   │   │   │   ├── _transformer.cpython-311.pyc
    │           │   │   │   │   ├── _union_transformer.cpython-311.pyc
    │           │   │   │   │   └── _utils.cpython-311.pyc
    │           │   │   │   ├── _checkers.py
    │           │   │   │   ├── _config.py
    │           │   │   │   ├── _decorators.py
    │           │   │   │   ├── _exceptions.py
    │           │   │   │   ├── _functions.py
    │           │   │   │   ├── _importhook.py
    │           │   │   │   ├── _memo.py
    │           │   │   │   ├── _pytest_plugin.py
    │           │   │   │   ├── _suppression.py
    │           │   │   │   ├── _transformer.py
    │           │   │   │   ├── _union_transformer.py
    │           │   │   │   ├── _utils.py
    │           │   │   │   └── py.typed
    │           │   │   ├── typeguard-4.3.0.dist-info
    │           │   │   │   ├── entry_points.txt
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── top_level.txt
    │           │   │   │   └── WHEEL
    │           │   │   ├── typing_extensions-4.12.2.dist-info
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   └── WHEEL
    │           │   │   ├── typing_extensions.py
    │           │   │   ├── wheel
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __main__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── __main__.cpython-311.pyc
    │           │   │   │   │   ├── _setuptools_logging.cpython-311.pyc
    │           │   │   │   │   ├── bdist_wheel.cpython-311.pyc
    │           │   │   │   │   ├── macosx_libfile.cpython-311.pyc
    │           │   │   │   │   ├── metadata.cpython-311.pyc
    │           │   │   │   │   ├── util.cpython-311.pyc
    │           │   │   │   │   └── wheelfile.cpython-311.pyc
    │           │   │   │   ├── _setuptools_logging.py
    │           │   │   │   ├── bdist_wheel.py
    │           │   │   │   ├── cli
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   ├── convert.cpython-311.pyc
    │           │   │   │   │   │   ├── pack.cpython-311.pyc
    │           │   │   │   │   │   ├── tags.cpython-311.pyc
    │           │   │   │   │   │   └── unpack.cpython-311.pyc
    │           │   │   │   │   ├── convert.py
    │           │   │   │   │   ├── pack.py
    │           │   │   │   │   ├── tags.py
    │           │   │   │   │   └── unpack.py
    │           │   │   │   ├── macosx_libfile.py
    │           │   │   │   ├── metadata.py
    │           │   │   │   ├── util.py
    │           │   │   │   ├── vendored
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   └── __init__.cpython-311.pyc
    │           │   │   │   │   ├── packaging
    │           │   │   │   │   │   ├── __init__.py
    │           │   │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _elffile.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _manylinux.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _musllinux.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _parser.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _structures.cpython-311.pyc
    │           │   │   │   │   │   │   ├── _tokenizer.cpython-311.pyc
    │           │   │   │   │   │   │   ├── markers.cpython-311.pyc
    │           │   │   │   │   │   │   ├── requirements.cpython-311.pyc
    │           │   │   │   │   │   │   ├── specifiers.cpython-311.pyc
    │           │   │   │   │   │   │   ├── tags.cpython-311.pyc
    │           │   │   │   │   │   │   ├── utils.cpython-311.pyc
    │           │   │   │   │   │   │   └── version.cpython-311.pyc
    │           │   │   │   │   │   ├── _elffile.py
    │           │   │   │   │   │   ├── _manylinux.py
    │           │   │   │   │   │   ├── _musllinux.py
    │           │   │   │   │   │   ├── _parser.py
    │           │   │   │   │   │   ├── _structures.py
    │           │   │   │   │   │   ├── _tokenizer.py
    │           │   │   │   │   │   ├── markers.py
    │           │   │   │   │   │   ├── requirements.py
    │           │   │   │   │   │   ├── specifiers.py
    │           │   │   │   │   │   ├── tags.py
    │           │   │   │   │   │   ├── utils.py
    │           │   │   │   │   │   └── version.py
    │           │   │   │   │   └── vendor.txt
    │           │   │   │   └── wheelfile.py
    │           │   │   ├── wheel-0.43.0.dist-info
    │           │   │   │   ├── entry_points.txt
    │           │   │   │   ├── INSTALLER
    │           │   │   │   ├── LICENSE.txt
    │           │   │   │   ├── METADATA
    │           │   │   │   ├── RECORD
    │           │   │   │   ├── REQUESTED
    │           │   │   │   └── WHEEL
    │           │   │   ├── zipp
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   └── glob.cpython-311.pyc
    │           │   │   │   ├── compat
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── py310.cpython-311.pyc
    │           │   │   │   │   └── py310.py
    │           │   │   │   └── glob.py
    │           │   │   └── zipp-3.19.2.dist-info
    │           │   │       ├── INSTALLER
    │           │   │       ├── LICENSE
    │           │   │       ├── METADATA
    │           │   │       ├── RECORD
    │           │   │       ├── REQUESTED
    │           │   │       ├── top_level.txt
    │           │   │       └── WHEEL
    │           │   ├── archive_util.py
    │           │   ├── build_meta.py
    │           │   ├── cli-32.exe
    │           │   ├── cli-64.exe
    │           │   ├── cli-arm64.exe
    │           │   ├── cli.exe
    │           │   ├── command
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── _requirestxt.cpython-311.pyc
    │           │   │   │   ├── alias.cpython-311.pyc
    │           │   │   │   ├── bdist_egg.cpython-311.pyc
    │           │   │   │   ├── bdist_rpm.cpython-311.pyc
    │           │   │   │   ├── bdist_wheel.cpython-311.pyc
    │           │   │   │   ├── build_clib.cpython-311.pyc
    │           │   │   │   ├── build_ext.cpython-311.pyc
    │           │   │   │   ├── build_py.cpython-311.pyc
    │           │   │   │   ├── build.cpython-311.pyc
    │           │   │   │   ├── develop.cpython-311.pyc
    │           │   │   │   ├── dist_info.cpython-311.pyc
    │           │   │   │   ├── easy_install.cpython-311.pyc
    │           │   │   │   ├── editable_wheel.cpython-311.pyc
    │           │   │   │   ├── egg_info.cpython-311.pyc
    │           │   │   │   ├── install_egg_info.cpython-311.pyc
    │           │   │   │   ├── install_lib.cpython-311.pyc
    │           │   │   │   ├── install_scripts.cpython-311.pyc
    │           │   │   │   ├── install.cpython-311.pyc
    │           │   │   │   ├── register.cpython-311.pyc
    │           │   │   │   ├── rotate.cpython-311.pyc
    │           │   │   │   ├── saveopts.cpython-311.pyc
    │           │   │   │   ├── sdist.cpython-311.pyc
    │           │   │   │   ├── setopt.cpython-311.pyc
    │           │   │   │   ├── test.cpython-311.pyc
    │           │   │   │   ├── upload_docs.cpython-311.pyc
    │           │   │   │   └── upload.cpython-311.pyc
    │           │   │   ├── _requirestxt.py
    │           │   │   ├── alias.py
    │           │   │   ├── bdist_egg.py
    │           │   │   ├── bdist_rpm.py
    │           │   │   ├── bdist_wheel.py
    │           │   │   ├── build_clib.py
    │           │   │   ├── build_ext.py
    │           │   │   ├── build_py.py
    │           │   │   ├── build.py
    │           │   │   ├── develop.py
    │           │   │   ├── dist_info.py
    │           │   │   ├── easy_install.py
    │           │   │   ├── editable_wheel.py
    │           │   │   ├── egg_info.py
    │           │   │   ├── install_egg_info.py
    │           │   │   ├── install_lib.py
    │           │   │   ├── install_scripts.py
    │           │   │   ├── install.py
    │           │   │   ├── launcher manifest.xml
    │           │   │   ├── register.py
    │           │   │   ├── rotate.py
    │           │   │   ├── saveopts.py
    │           │   │   ├── sdist.py
    │           │   │   ├── setopt.py
    │           │   │   ├── test.py
    │           │   │   ├── upload_docs.py
    │           │   │   └── upload.py
    │           │   ├── compat
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── py310.cpython-311.pyc
    │           │   │   │   ├── py311.cpython-311.pyc
    │           │   │   │   ├── py312.cpython-311.pyc
    │           │   │   │   └── py39.cpython-311.pyc
    │           │   │   ├── py310.py
    │           │   │   ├── py311.py
    │           │   │   ├── py312.py
    │           │   │   └── py39.py
    │           │   ├── config
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── _apply_pyprojecttoml.cpython-311.pyc
    │           │   │   │   ├── expand.cpython-311.pyc
    │           │   │   │   ├── pyprojecttoml.cpython-311.pyc
    │           │   │   │   └── setupcfg.cpython-311.pyc
    │           │   │   ├── _apply_pyprojecttoml.py
    │           │   │   ├── _validate_pyproject
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── error_reporting.cpython-311.pyc
    │           │   │   │   │   ├── extra_validations.cpython-311.pyc
    │           │   │   │   │   ├── fastjsonschema_exceptions.cpython-311.pyc
    │           │   │   │   │   ├── fastjsonschema_validations.cpython-311.pyc
    │           │   │   │   │   └── formats.cpython-311.pyc
    │           │   │   │   ├── error_reporting.py
    │           │   │   │   ├── extra_validations.py
    │           │   │   │   ├── fastjsonschema_exceptions.py
    │           │   │   │   ├── fastjsonschema_validations.py
    │           │   │   │   ├── formats.py
    │           │   │   │   └── NOTICE
    │           │   │   ├── distutils.schema.json
    │           │   │   ├── expand.py
    │           │   │   ├── NOTICE
    │           │   │   ├── pyprojecttoml.py
    │           │   │   ├── setupcfg.py
    │           │   │   └── setuptools.schema.json
    │           │   ├── depends.py
    │           │   ├── discovery.py
    │           │   ├── dist.py
    │           │   ├── errors.py
    │           │   ├── extension.py
    │           │   ├── glob.py
    │           │   ├── gui-32.exe
    │           │   ├── gui-64.exe
    │           │   ├── gui-arm64.exe
    │           │   ├── gui.exe
    │           │   ├── installer.py
    │           │   ├── launch.py
    │           │   ├── logging.py
    │           │   ├── modified.py
    │           │   ├── monkey.py
    │           │   ├── msvc.py
    │           │   ├── namespaces.py
    │           │   ├── package_index.py
    │           │   ├── sandbox.py
    │           │   ├── script (dev).tmpl
    │           │   ├── script.tmpl
    │           │   ├── tests
    │           │   │   ├── __init__.py
    │           │   │   ├── __pycache__
    │           │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   ├── contexts.cpython-311.pyc
    │           │   │   │   ├── environment.cpython-311.pyc
    │           │   │   │   ├── fixtures.cpython-311.pyc
    │           │   │   │   ├── mod_with_constant.cpython-311.pyc
    │           │   │   │   ├── namespaces.cpython-311.pyc
    │           │   │   │   ├── script-with-bom.cpython-311.pyc
    │           │   │   │   ├── server.cpython-311.pyc
    │           │   │   │   ├── test_archive_util.cpython-311.pyc
    │           │   │   │   ├── test_bdist_deprecations.cpython-311.pyc
    │           │   │   │   ├── test_bdist_egg.cpython-311.pyc
    │           │   │   │   ├── test_bdist_wheel.cpython-311.pyc
    │           │   │   │   ├── test_build_clib.cpython-311.pyc
    │           │   │   │   ├── test_build_ext.cpython-311.pyc
    │           │   │   │   ├── test_build_meta.cpython-311.pyc
    │           │   │   │   ├── test_build_py.cpython-311.pyc
    │           │   │   │   ├── test_build.cpython-311.pyc
    │           │   │   │   ├── test_config_discovery.cpython-311.pyc
    │           │   │   │   ├── test_core_metadata.cpython-311.pyc
    │           │   │   │   ├── test_depends.cpython-311.pyc
    │           │   │   │   ├── test_develop.cpython-311.pyc
    │           │   │   │   ├── test_dist_info.cpython-311.pyc
    │           │   │   │   ├── test_dist.cpython-311.pyc
    │           │   │   │   ├── test_distutils_adoption.cpython-311.pyc
    │           │   │   │   ├── test_easy_install.cpython-311.pyc
    │           │   │   │   ├── test_editable_install.cpython-311.pyc
    │           │   │   │   ├── test_egg_info.cpython-311.pyc
    │           │   │   │   ├── test_extern.cpython-311.pyc
    │           │   │   │   ├── test_find_packages.cpython-311.pyc
    │           │   │   │   ├── test_find_py_modules.cpython-311.pyc
    │           │   │   │   ├── test_glob.cpython-311.pyc
    │           │   │   │   ├── test_install_scripts.cpython-311.pyc
    │           │   │   │   ├── test_logging.cpython-311.pyc
    │           │   │   │   ├── test_manifest.cpython-311.pyc
    │           │   │   │   ├── test_namespaces.cpython-311.pyc
    │           │   │   │   ├── test_packageindex.cpython-311.pyc
    │           │   │   │   ├── test_register.cpython-311.pyc
    │           │   │   │   ├── test_sandbox.cpython-311.pyc
    │           │   │   │   ├── test_sdist.cpython-311.pyc
    │           │   │   │   ├── test_setopt.cpython-311.pyc
    │           │   │   │   ├── test_setuptools.cpython-311.pyc
    │           │   │   │   ├── test_unicode_utils.cpython-311.pyc
    │           │   │   │   ├── test_upload.cpython-311.pyc
    │           │   │   │   ├── test_virtualenv.cpython-311.pyc
    │           │   │   │   ├── test_warnings.cpython-311.pyc
    │           │   │   │   ├── test_wheel.cpython-311.pyc
    │           │   │   │   ├── test_windows_wrappers.cpython-311.pyc
    │           │   │   │   ├── text.cpython-311.pyc
    │           │   │   │   └── textwrap.cpython-311.pyc
    │           │   │   ├── compat
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   └── py39.cpython-311.pyc
    │           │   │   │   └── py39.py
    │           │   │   ├── config
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── test_apply_pyprojecttoml.cpython-311.pyc
    │           │   │   │   │   ├── test_expand.cpython-311.pyc
    │           │   │   │   │   ├── test_pyprojecttoml_dynamic_deps.cpython-311.pyc
    │           │   │   │   │   ├── test_pyprojecttoml.cpython-311.pyc
    │           │   │   │   │   └── test_setupcfg.cpython-311.pyc
    │           │   │   │   ├── downloads
    │           │   │   │   │   ├── __init__.py
    │           │   │   │   │   ├── __pycache__
    │           │   │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   │   └── preload.cpython-311.pyc
    │           │   │   │   │   └── preload.py
    │           │   │   │   ├── setupcfg_examples.txt
    │           │   │   │   ├── test_apply_pyprojecttoml.py
    │           │   │   │   ├── test_expand.py
    │           │   │   │   ├── test_pyprojecttoml_dynamic_deps.py
    │           │   │   │   ├── test_pyprojecttoml.py
    │           │   │   │   └── test_setupcfg.py
    │           │   │   ├── contexts.py
    │           │   │   ├── environment.py
    │           │   │   ├── fixtures.py
    │           │   │   ├── indexes
    │           │   │   │   └── test_links_priority
    │           │   │   │       ├── external.html
    │           │   │   │       └── simple
    │           │   │   │           └── foobar
    │           │   │   │               └── index.html
    │           │   │   ├── integration
    │           │   │   │   ├── __init__.py
    │           │   │   │   ├── __pycache__
    │           │   │   │   │   ├── __init__.cpython-311.pyc
    │           │   │   │   │   ├── helpers.cpython-311.pyc
    │           │   │   │   │   └── test_pip_install_sdist.cpython-311.pyc
    │           │   │   │   ├── helpers.py
    │           │   │   │   └── test_pip_install_sdist.py
    │           │   │   ├── mod_with_constant.py
    │           │   │   ├── namespaces.py
    │           │   │   ├── script-with-bom.py
    │           │   │   ├── server.py
    │           │   │   ├── test_archive_util.py
    │           │   │   ├── test_bdist_deprecations.py
    │           │   │   ├── test_bdist_egg.py
    │           │   │   ├── test_bdist_wheel.py
    │           │   │   ├── test_build_clib.py
    │           │   │   ├── test_build_ext.py
    │           │   │   ├── test_build_meta.py
    │           │   │   ├── test_build_py.py
    │           │   │   ├── test_build.py
    │           │   │   ├── test_config_discovery.py
    │           │   │   ├── test_core_metadata.py
    │           │   │   ├── test_depends.py
    │           │   │   ├── test_develop.py
    │           │   │   ├── test_dist_info.py
    │           │   │   ├── test_dist.py
    │           │   │   ├── test_distutils_adoption.py
    │           │   │   ├── test_easy_install.py
    │           │   │   ├── test_editable_install.py
    │           │   │   ├── test_egg_info.py
    │           │   │   ├── test_extern.py
    │           │   │   ├── test_find_packages.py
    │           │   │   ├── test_find_py_modules.py
    │           │   │   ├── test_glob.py
    │           │   │   ├── test_install_scripts.py
    │           │   │   ├── test_logging.py
    │           │   │   ├── test_manifest.py
    │           │   │   ├── test_namespaces.py
    │           │   │   ├── test_packageindex.py
    │           │   │   ├── test_register.py
    │           │   │   ├── test_sandbox.py
    │           │   │   ├── test_sdist.py
    │           │   │   ├── test_setopt.py
    │           │   │   ├── test_setuptools.py
    │           │   │   ├── test_unicode_utils.py
    │           │   │   ├── test_upload.py
    │           │   │   ├── test_virtualenv.py
    │           │   │   ├── test_warnings.py
    │           │   │   ├── test_wheel.py
    │           │   │   ├── test_windows_wrappers.py
    │           │   │   ├── text.py
    │           │   │   └── textwrap.py
    │           │   ├── unicode_utils.py
    │           │   ├── version.py
    │           │   ├── warnings.py
    │           │   ├── wheel.py
    │           │   └── windows_support.py
    │           ├── setuptools-74.1.2.dist-info
    │           │   ├── entry_points.txt
    │           │   ├── INSTALLER
    │           │   ├── LICENSE
    │           │   ├── METADATA
    │           │   ├── RECORD
    │           │   ├── REQUESTED
    │           │   ├── top_level.txt
    │           │   └── WHEEL
    │           ├── trove_classifiers
    │           │   ├── __init__.py
    │           │   ├── __main__.py
    │           │   ├── __pycache__
    │           │   │   ├── __init__.cpython-311.pyc
    │           │   │   └── __main__.cpython-311.pyc
    │           │   └── py.typed
    │           └── trove_classifiers-2024.10.21.16.dist-info
    │               ├── INSTALLER
    │               ├── LICENSE
    │               ├── METADATA
    │               ├── RECORD
    │               ├── top_level.txt
    │               └── WHEEL
    └── pyvenv.cfg
```

# Files

--------------------------------------------------------------------------------
/venv/lib/python3.11/site-packages/setuptools/_vendor/inflect/__init__.py:
--------------------------------------------------------------------------------

```python
   1 | """
   2 | inflect: english language inflection
   3 |  - correctly generate plurals, ordinals, indefinite articles
   4 |  - convert numbers to words
   5 | 
   6 | Copyright (C) 2010 Paul Dyson
   7 | 
   8 | Based upon the Perl module
   9 | `Lingua::EN::Inflect <https://metacpan.org/pod/Lingua::EN::Inflect>`_.
  10 | 
  11 | methods:
  12 |     classical inflect
  13 |     plural plural_noun plural_verb plural_adj singular_noun no num a an
  14 |     compare compare_nouns compare_verbs compare_adjs
  15 |     present_participle
  16 |     ordinal
  17 |     number_to_words
  18 |     join
  19 |     defnoun defverb defadj defa defan
  20 | 
  21 | INFLECTIONS:
  22 |     classical inflect
  23 |     plural plural_noun plural_verb plural_adj singular_noun compare
  24 |     no num a an present_participle
  25 | 
  26 | PLURALS:
  27 |     classical inflect
  28 |     plural plural_noun plural_verb plural_adj singular_noun no num
  29 |     compare compare_nouns compare_verbs compare_adjs
  30 | 
  31 | COMPARISONS:
  32 |     classical
  33 |     compare compare_nouns compare_verbs compare_adjs
  34 | 
  35 | ARTICLES:
  36 |     classical inflect num a an
  37 | 
  38 | NUMERICAL:
  39 |     ordinal number_to_words
  40 | 
  41 | USER_DEFINED:
  42 |     defnoun defverb defadj defa defan
  43 | 
  44 | Exceptions:
  45 |  UnknownClassicalModeError
  46 |  BadNumValueError
  47 |  BadChunkingOptionError
  48 |  NumOutOfRangeError
  49 |  BadUserDefinedPatternError
  50 |  BadRcFileError
  51 |  BadGenderError
  52 | 
  53 | """
  54 | 
  55 | from __future__ import annotations
  56 | 
  57 | import ast
  58 | import collections
  59 | import contextlib
  60 | import functools
  61 | import itertools
  62 | import re
  63 | from numbers import Number
  64 | from typing import (
  65 |     TYPE_CHECKING,
  66 |     Any,
  67 |     Callable,
  68 |     Dict,
  69 |     Iterable,
  70 |     List,
  71 |     Literal,
  72 |     Match,
  73 |     Optional,
  74 |     Sequence,
  75 |     Tuple,
  76 |     Union,
  77 |     cast,
  78 | )
  79 | 
  80 | from more_itertools import windowed_complete
  81 | from typeguard import typechecked
  82 | 
  83 | from .compat.py38 import Annotated
  84 | 
  85 | 
  86 | class UnknownClassicalModeError(Exception):
  87 |     pass
  88 | 
  89 | 
  90 | class BadNumValueError(Exception):
  91 |     pass
  92 | 
  93 | 
  94 | class BadChunkingOptionError(Exception):
  95 |     pass
  96 | 
  97 | 
  98 | class NumOutOfRangeError(Exception):
  99 |     pass
 100 | 
 101 | 
 102 | class BadUserDefinedPatternError(Exception):
 103 |     pass
 104 | 
 105 | 
 106 | class BadRcFileError(Exception):
 107 |     pass
 108 | 
 109 | 
 110 | class BadGenderError(Exception):
 111 |     pass
 112 | 
 113 | 
 114 | def enclose(s: str) -> str:
 115 |     return f"(?:{s})"
 116 | 
 117 | 
 118 | def joinstem(cutpoint: Optional[int] = 0, words: Optional[Iterable[str]] = None) -> str:
 119 |     """
 120 |     Join stem of each word in words into a string for regex.
 121 | 
 122 |     Each word is truncated at cutpoint.
 123 | 
 124 |     Cutpoint is usually negative indicating the number of letters to remove
 125 |     from the end of each word.
 126 | 
 127 |     >>> joinstem(-2, ["ephemeris", "iris", ".*itis"])
 128 |     '(?:ephemer|ir|.*it)'
 129 | 
 130 |     >>> joinstem(None, ["ephemeris"])
 131 |     '(?:ephemeris)'
 132 | 
 133 |     >>> joinstem(5, None)
 134 |     '(?:)'
 135 |     """
 136 |     return enclose("|".join(w[:cutpoint] for w in words or []))
 137 | 
 138 | 
 139 | def bysize(words: Iterable[str]) -> Dict[int, set]:
 140 |     """
 141 |     From a list of words, return a dict of sets sorted by word length.
 142 | 
 143 |     >>> words = ['ant', 'cat', 'dog', 'pig', 'frog', 'goat', 'horse', 'elephant']
 144 |     >>> ret = bysize(words)
 145 |     >>> sorted(ret[3])
 146 |     ['ant', 'cat', 'dog', 'pig']
 147 |     >>> ret[5]
 148 |     {'horse'}
 149 |     """
 150 |     res: Dict[int, set] = collections.defaultdict(set)
 151 |     for w in words:
 152 |         res[len(w)].add(w)
 153 |     return res
 154 | 
 155 | 
 156 | def make_pl_si_lists(
 157 |     lst: Iterable[str],
 158 |     plending: str,
 159 |     siendingsize: Optional[int],
 160 |     dojoinstem: bool = True,
 161 | ):
 162 |     """
 163 |     given a list of singular words: lst
 164 | 
 165 |     an ending to append to make the plural: plending
 166 | 
 167 |     the number of characters to remove from the singular
 168 |     before appending plending: siendingsize
 169 | 
 170 |     a flag whether to create a joinstem: dojoinstem
 171 | 
 172 |     return:
 173 |     a list of pluralised words: si_list (called si because this is what you need to
 174 |     look for to make the singular)
 175 | 
 176 |     the pluralised words as a dict of sets sorted by word length: si_bysize
 177 |     the singular words as a dict of sets sorted by word length: pl_bysize
 178 |     if dojoinstem is True: a regular expression that matches any of the stems: stem
 179 |     """
 180 |     if siendingsize is not None:
 181 |         siendingsize = -siendingsize
 182 |     si_list = [w[:siendingsize] + plending for w in lst]
 183 |     pl_bysize = bysize(lst)
 184 |     si_bysize = bysize(si_list)
 185 |     if dojoinstem:
 186 |         stem = joinstem(siendingsize, lst)
 187 |         return si_list, si_bysize, pl_bysize, stem
 188 |     else:
 189 |         return si_list, si_bysize, pl_bysize
 190 | 
 191 | 
 192 | # 1. PLURALS
 193 | 
 194 | pl_sb_irregular_s = {
 195 |     "corpus": "corpuses|corpora",
 196 |     "opus": "opuses|opera",
 197 |     "genus": "genera",
 198 |     "mythos": "mythoi",
 199 |     "penis": "penises|penes",
 200 |     "testis": "testes",
 201 |     "atlas": "atlases|atlantes",
 202 |     "yes": "yeses",
 203 | }
 204 | 
 205 | pl_sb_irregular = {
 206 |     "child": "children",
 207 |     "chili": "chilis|chilies",
 208 |     "brother": "brothers|brethren",
 209 |     "infinity": "infinities|infinity",
 210 |     "loaf": "loaves",
 211 |     "lore": "lores|lore",
 212 |     "hoof": "hoofs|hooves",
 213 |     "beef": "beefs|beeves",
 214 |     "thief": "thiefs|thieves",
 215 |     "money": "monies",
 216 |     "mongoose": "mongooses",
 217 |     "ox": "oxen",
 218 |     "cow": "cows|kine",
 219 |     "graffito": "graffiti",
 220 |     "octopus": "octopuses|octopodes",
 221 |     "genie": "genies|genii",
 222 |     "ganglion": "ganglions|ganglia",
 223 |     "trilby": "trilbys",
 224 |     "turf": "turfs|turves",
 225 |     "numen": "numina",
 226 |     "atman": "atmas",
 227 |     "occiput": "occiputs|occipita",
 228 |     "sabretooth": "sabretooths",
 229 |     "sabertooth": "sabertooths",
 230 |     "lowlife": "lowlifes",
 231 |     "flatfoot": "flatfoots",
 232 |     "tenderfoot": "tenderfoots",
 233 |     "romany": "romanies",
 234 |     "jerry": "jerries",
 235 |     "mary": "maries",
 236 |     "talouse": "talouses",
 237 |     "rom": "roma",
 238 |     "carmen": "carmina",
 239 | }
 240 | 
 241 | pl_sb_irregular.update(pl_sb_irregular_s)
 242 | # pl_sb_irregular_keys = enclose('|'.join(pl_sb_irregular.keys()))
 243 | 
 244 | pl_sb_irregular_caps = {
 245 |     "Romany": "Romanies",
 246 |     "Jerry": "Jerrys",
 247 |     "Mary": "Marys",
 248 |     "Rom": "Roma",
 249 | }
 250 | 
 251 | pl_sb_irregular_compound = {"prima donna": "prima donnas|prime donne"}
 252 | 
 253 | si_sb_irregular = {v: k for (k, v) in pl_sb_irregular.items()}
 254 | for k in list(si_sb_irregular):
 255 |     if "|" in k:
 256 |         k1, k2 = k.split("|")
 257 |         si_sb_irregular[k1] = si_sb_irregular[k2] = si_sb_irregular[k]
 258 |         del si_sb_irregular[k]
 259 | si_sb_irregular_caps = {v: k for (k, v) in pl_sb_irregular_caps.items()}
 260 | si_sb_irregular_compound = {v: k for (k, v) in pl_sb_irregular_compound.items()}
 261 | for k in list(si_sb_irregular_compound):
 262 |     if "|" in k:
 263 |         k1, k2 = k.split("|")
 264 |         si_sb_irregular_compound[k1] = si_sb_irregular_compound[k2] = (
 265 |             si_sb_irregular_compound[k]
 266 |         )
 267 |         del si_sb_irregular_compound[k]
 268 | 
 269 | # si_sb_irregular_keys = enclose('|'.join(si_sb_irregular.keys()))
 270 | 
 271 | # Z's that don't double
 272 | 
 273 | pl_sb_z_zes_list = ("quartz", "topaz")
 274 | pl_sb_z_zes_bysize = bysize(pl_sb_z_zes_list)
 275 | 
 276 | pl_sb_ze_zes_list = ("snooze",)
 277 | pl_sb_ze_zes_bysize = bysize(pl_sb_ze_zes_list)
 278 | 
 279 | 
 280 | # CLASSICAL "..is" -> "..ides"
 281 | 
 282 | pl_sb_C_is_ides_complete = [
 283 |     # GENERAL WORDS...
 284 |     "ephemeris",
 285 |     "iris",
 286 |     "clitoris",
 287 |     "chrysalis",
 288 |     "epididymis",
 289 | ]
 290 | 
 291 | pl_sb_C_is_ides_endings = [
 292 |     # INFLAMATIONS...
 293 |     "itis"
 294 | ]
 295 | 
 296 | pl_sb_C_is_ides = joinstem(
 297 |     -2, pl_sb_C_is_ides_complete + [f".*{w}" for w in pl_sb_C_is_ides_endings]
 298 | )
 299 | 
 300 | pl_sb_C_is_ides_list = pl_sb_C_is_ides_complete + pl_sb_C_is_ides_endings
 301 | 
 302 | (
 303 |     si_sb_C_is_ides_list,
 304 |     si_sb_C_is_ides_bysize,
 305 |     pl_sb_C_is_ides_bysize,
 306 | ) = make_pl_si_lists(pl_sb_C_is_ides_list, "ides", 2, dojoinstem=False)
 307 | 
 308 | 
 309 | # CLASSICAL "..a" -> "..ata"
 310 | 
 311 | pl_sb_C_a_ata_list = (
 312 |     "anathema",
 313 |     "bema",
 314 |     "carcinoma",
 315 |     "charisma",
 316 |     "diploma",
 317 |     "dogma",
 318 |     "drama",
 319 |     "edema",
 320 |     "enema",
 321 |     "enigma",
 322 |     "lemma",
 323 |     "lymphoma",
 324 |     "magma",
 325 |     "melisma",
 326 |     "miasma",
 327 |     "oedema",
 328 |     "sarcoma",
 329 |     "schema",
 330 |     "soma",
 331 |     "stigma",
 332 |     "stoma",
 333 |     "trauma",
 334 |     "gumma",
 335 |     "pragma",
 336 | )
 337 | 
 338 | (
 339 |     si_sb_C_a_ata_list,
 340 |     si_sb_C_a_ata_bysize,
 341 |     pl_sb_C_a_ata_bysize,
 342 |     pl_sb_C_a_ata,
 343 | ) = make_pl_si_lists(pl_sb_C_a_ata_list, "ata", 1)
 344 | 
 345 | # UNCONDITIONAL "..a" -> "..ae"
 346 | 
 347 | pl_sb_U_a_ae_list = (
 348 |     "alumna",
 349 |     "alga",
 350 |     "vertebra",
 351 |     "persona",
 352 |     "vita",
 353 | )
 354 | (
 355 |     si_sb_U_a_ae_list,
 356 |     si_sb_U_a_ae_bysize,
 357 |     pl_sb_U_a_ae_bysize,
 358 |     pl_sb_U_a_ae,
 359 | ) = make_pl_si_lists(pl_sb_U_a_ae_list, "e", None)
 360 | 
 361 | # CLASSICAL "..a" -> "..ae"
 362 | 
 363 | pl_sb_C_a_ae_list = (
 364 |     "amoeba",
 365 |     "antenna",
 366 |     "formula",
 367 |     "hyperbola",
 368 |     "medusa",
 369 |     "nebula",
 370 |     "parabola",
 371 |     "abscissa",
 372 |     "hydra",
 373 |     "nova",
 374 |     "lacuna",
 375 |     "aurora",
 376 |     "umbra",
 377 |     "flora",
 378 |     "fauna",
 379 | )
 380 | (
 381 |     si_sb_C_a_ae_list,
 382 |     si_sb_C_a_ae_bysize,
 383 |     pl_sb_C_a_ae_bysize,
 384 |     pl_sb_C_a_ae,
 385 | ) = make_pl_si_lists(pl_sb_C_a_ae_list, "e", None)
 386 | 
 387 | 
 388 | # CLASSICAL "..en" -> "..ina"
 389 | 
 390 | pl_sb_C_en_ina_list = ("stamen", "foramen", "lumen")
 391 | 
 392 | (
 393 |     si_sb_C_en_ina_list,
 394 |     si_sb_C_en_ina_bysize,
 395 |     pl_sb_C_en_ina_bysize,
 396 |     pl_sb_C_en_ina,
 397 | ) = make_pl_si_lists(pl_sb_C_en_ina_list, "ina", 2)
 398 | 
 399 | 
 400 | # UNCONDITIONAL "..um" -> "..a"
 401 | 
 402 | pl_sb_U_um_a_list = (
 403 |     "bacterium",
 404 |     "agendum",
 405 |     "desideratum",
 406 |     "erratum",
 407 |     "stratum",
 408 |     "datum",
 409 |     "ovum",
 410 |     "extremum",
 411 |     "candelabrum",
 412 | )
 413 | (
 414 |     si_sb_U_um_a_list,
 415 |     si_sb_U_um_a_bysize,
 416 |     pl_sb_U_um_a_bysize,
 417 |     pl_sb_U_um_a,
 418 | ) = make_pl_si_lists(pl_sb_U_um_a_list, "a", 2)
 419 | 
 420 | # CLASSICAL "..um" -> "..a"
 421 | 
 422 | pl_sb_C_um_a_list = (
 423 |     "maximum",
 424 |     "minimum",
 425 |     "momentum",
 426 |     "optimum",
 427 |     "quantum",
 428 |     "cranium",
 429 |     "curriculum",
 430 |     "dictum",
 431 |     "phylum",
 432 |     "aquarium",
 433 |     "compendium",
 434 |     "emporium",
 435 |     "encomium",
 436 |     "gymnasium",
 437 |     "honorarium",
 438 |     "interregnum",
 439 |     "lustrum",
 440 |     "memorandum",
 441 |     "millennium",
 442 |     "rostrum",
 443 |     "spectrum",
 444 |     "speculum",
 445 |     "stadium",
 446 |     "trapezium",
 447 |     "ultimatum",
 448 |     "medium",
 449 |     "vacuum",
 450 |     "velum",
 451 |     "consortium",
 452 |     "arboretum",
 453 | )
 454 | 
 455 | (
 456 |     si_sb_C_um_a_list,
 457 |     si_sb_C_um_a_bysize,
 458 |     pl_sb_C_um_a_bysize,
 459 |     pl_sb_C_um_a,
 460 | ) = make_pl_si_lists(pl_sb_C_um_a_list, "a", 2)
 461 | 
 462 | 
 463 | # UNCONDITIONAL "..us" -> "i"
 464 | 
 465 | pl_sb_U_us_i_list = (
 466 |     "alumnus",
 467 |     "alveolus",
 468 |     "bacillus",
 469 |     "bronchus",
 470 |     "locus",
 471 |     "nucleus",
 472 |     "stimulus",
 473 |     "meniscus",
 474 |     "sarcophagus",
 475 | )
 476 | (
 477 |     si_sb_U_us_i_list,
 478 |     si_sb_U_us_i_bysize,
 479 |     pl_sb_U_us_i_bysize,
 480 |     pl_sb_U_us_i,
 481 | ) = make_pl_si_lists(pl_sb_U_us_i_list, "i", 2)
 482 | 
 483 | # CLASSICAL "..us" -> "..i"
 484 | 
 485 | pl_sb_C_us_i_list = (
 486 |     "focus",
 487 |     "radius",
 488 |     "genius",
 489 |     "incubus",
 490 |     "succubus",
 491 |     "nimbus",
 492 |     "fungus",
 493 |     "nucleolus",
 494 |     "stylus",
 495 |     "torus",
 496 |     "umbilicus",
 497 |     "uterus",
 498 |     "hippopotamus",
 499 |     "cactus",
 500 | )
 501 | 
 502 | (
 503 |     si_sb_C_us_i_list,
 504 |     si_sb_C_us_i_bysize,
 505 |     pl_sb_C_us_i_bysize,
 506 |     pl_sb_C_us_i,
 507 | ) = make_pl_si_lists(pl_sb_C_us_i_list, "i", 2)
 508 | 
 509 | 
 510 | # CLASSICAL "..us" -> "..us"  (ASSIMILATED 4TH DECLENSION LATIN NOUNS)
 511 | 
 512 | pl_sb_C_us_us = (
 513 |     "status",
 514 |     "apparatus",
 515 |     "prospectus",
 516 |     "sinus",
 517 |     "hiatus",
 518 |     "impetus",
 519 |     "plexus",
 520 | )
 521 | pl_sb_C_us_us_bysize = bysize(pl_sb_C_us_us)
 522 | 
 523 | # UNCONDITIONAL "..on" -> "a"
 524 | 
 525 | pl_sb_U_on_a_list = (
 526 |     "criterion",
 527 |     "perihelion",
 528 |     "aphelion",
 529 |     "phenomenon",
 530 |     "prolegomenon",
 531 |     "noumenon",
 532 |     "organon",
 533 |     "asyndeton",
 534 |     "hyperbaton",
 535 | )
 536 | (
 537 |     si_sb_U_on_a_list,
 538 |     si_sb_U_on_a_bysize,
 539 |     pl_sb_U_on_a_bysize,
 540 |     pl_sb_U_on_a,
 541 | ) = make_pl_si_lists(pl_sb_U_on_a_list, "a", 2)
 542 | 
 543 | # CLASSICAL "..on" -> "..a"
 544 | 
 545 | pl_sb_C_on_a_list = ("oxymoron",)
 546 | 
 547 | (
 548 |     si_sb_C_on_a_list,
 549 |     si_sb_C_on_a_bysize,
 550 |     pl_sb_C_on_a_bysize,
 551 |     pl_sb_C_on_a,
 552 | ) = make_pl_si_lists(pl_sb_C_on_a_list, "a", 2)
 553 | 
 554 | 
 555 | # CLASSICAL "..o" -> "..i"  (BUT NORMALLY -> "..os")
 556 | 
 557 | pl_sb_C_o_i = [
 558 |     "solo",
 559 |     "soprano",
 560 |     "basso",
 561 |     "alto",
 562 |     "contralto",
 563 |     "tempo",
 564 |     "piano",
 565 |     "virtuoso",
 566 | ]  # list not tuple so can concat for pl_sb_U_o_os
 567 | 
 568 | pl_sb_C_o_i_bysize = bysize(pl_sb_C_o_i)
 569 | si_sb_C_o_i_bysize = bysize([f"{w[:-1]}i" for w in pl_sb_C_o_i])
 570 | 
 571 | pl_sb_C_o_i_stems = joinstem(-1, pl_sb_C_o_i)
 572 | 
 573 | # ALWAYS "..o" -> "..os"
 574 | 
 575 | pl_sb_U_o_os_complete = {"ado", "ISO", "NATO", "NCO", "NGO", "oto"}
 576 | si_sb_U_o_os_complete = {f"{w}s" for w in pl_sb_U_o_os_complete}
 577 | 
 578 | 
 579 | pl_sb_U_o_os_endings = [
 580 |     "aficionado",
 581 |     "aggro",
 582 |     "albino",
 583 |     "allegro",
 584 |     "ammo",
 585 |     "Antananarivo",
 586 |     "archipelago",
 587 |     "armadillo",
 588 |     "auto",
 589 |     "avocado",
 590 |     "Bamako",
 591 |     "Barquisimeto",
 592 |     "bimbo",
 593 |     "bingo",
 594 |     "Biro",
 595 |     "bolero",
 596 |     "Bolzano",
 597 |     "bongo",
 598 |     "Boto",
 599 |     "burro",
 600 |     "Cairo",
 601 |     "canto",
 602 |     "cappuccino",
 603 |     "casino",
 604 |     "cello",
 605 |     "Chicago",
 606 |     "Chimango",
 607 |     "cilantro",
 608 |     "cochito",
 609 |     "coco",
 610 |     "Colombo",
 611 |     "Colorado",
 612 |     "commando",
 613 |     "concertino",
 614 |     "contango",
 615 |     "credo",
 616 |     "crescendo",
 617 |     "cyano",
 618 |     "demo",
 619 |     "ditto",
 620 |     "Draco",
 621 |     "dynamo",
 622 |     "embryo",
 623 |     "Esperanto",
 624 |     "espresso",
 625 |     "euro",
 626 |     "falsetto",
 627 |     "Faro",
 628 |     "fiasco",
 629 |     "Filipino",
 630 |     "flamenco",
 631 |     "furioso",
 632 |     "generalissimo",
 633 |     "Gestapo",
 634 |     "ghetto",
 635 |     "gigolo",
 636 |     "gizmo",
 637 |     "Greensboro",
 638 |     "gringo",
 639 |     "Guaiabero",
 640 |     "guano",
 641 |     "gumbo",
 642 |     "gyro",
 643 |     "hairdo",
 644 |     "hippo",
 645 |     "Idaho",
 646 |     "impetigo",
 647 |     "inferno",
 648 |     "info",
 649 |     "intermezzo",
 650 |     "intertrigo",
 651 |     "Iquico",
 652 |     "jumbo",
 653 |     "junto",
 654 |     "Kakapo",
 655 |     "kilo",
 656 |     "Kinkimavo",
 657 |     "Kokako",
 658 |     "Kosovo",
 659 |     "Lesotho",
 660 |     "libero",
 661 |     "libido",
 662 |     "libretto",
 663 |     "lido",
 664 |     "Lilo",
 665 |     "limbo",
 666 |     "limo",
 667 |     "lineno",
 668 |     "lingo",
 669 |     "lino",
 670 |     "livedo",
 671 |     "loco",
 672 |     "logo",
 673 |     "lumbago",
 674 |     "macho",
 675 |     "macro",
 676 |     "mafioso",
 677 |     "magneto",
 678 |     "magnifico",
 679 |     "Majuro",
 680 |     "Malabo",
 681 |     "manifesto",
 682 |     "Maputo",
 683 |     "Maracaibo",
 684 |     "medico",
 685 |     "memo",
 686 |     "metro",
 687 |     "Mexico",
 688 |     "micro",
 689 |     "Milano",
 690 |     "Monaco",
 691 |     "mono",
 692 |     "Montenegro",
 693 |     "Morocco",
 694 |     "Muqdisho",
 695 |     "myo",
 696 |     "neutrino",
 697 |     "Ningbo",
 698 |     "octavo",
 699 |     "oregano",
 700 |     "Orinoco",
 701 |     "Orlando",
 702 |     "Oslo",
 703 |     "panto",
 704 |     "Paramaribo",
 705 |     "Pardusco",
 706 |     "pedalo",
 707 |     "photo",
 708 |     "pimento",
 709 |     "pinto",
 710 |     "pleco",
 711 |     "Pluto",
 712 |     "pogo",
 713 |     "polo",
 714 |     "poncho",
 715 |     "Porto-Novo",
 716 |     "Porto",
 717 |     "pro",
 718 |     "psycho",
 719 |     "pueblo",
 720 |     "quarto",
 721 |     "Quito",
 722 |     "repo",
 723 |     "rhino",
 724 |     "risotto",
 725 |     "rococo",
 726 |     "rondo",
 727 |     "Sacramento",
 728 |     "saddo",
 729 |     "sago",
 730 |     "salvo",
 731 |     "Santiago",
 732 |     "Sapporo",
 733 |     "Sarajevo",
 734 |     "scherzando",
 735 |     "scherzo",
 736 |     "silo",
 737 |     "sirocco",
 738 |     "sombrero",
 739 |     "staccato",
 740 |     "sterno",
 741 |     "stucco",
 742 |     "stylo",
 743 |     "sumo",
 744 |     "Taiko",
 745 |     "techno",
 746 |     "terrazzo",
 747 |     "testudo",
 748 |     "timpano",
 749 |     "tiro",
 750 |     "tobacco",
 751 |     "Togo",
 752 |     "Tokyo",
 753 |     "torero",
 754 |     "Torino",
 755 |     "Toronto",
 756 |     "torso",
 757 |     "tremolo",
 758 |     "typo",
 759 |     "tyro",
 760 |     "ufo",
 761 |     "UNESCO",
 762 |     "vaquero",
 763 |     "vermicello",
 764 |     "verso",
 765 |     "vibrato",
 766 |     "violoncello",
 767 |     "Virgo",
 768 |     "weirdo",
 769 |     "WHO",
 770 |     "WTO",
 771 |     "Yamoussoukro",
 772 |     "yo-yo",
 773 |     "zero",
 774 |     "Zibo",
 775 | ] + pl_sb_C_o_i
 776 | 
 777 | pl_sb_U_o_os_bysize = bysize(pl_sb_U_o_os_endings)
 778 | si_sb_U_o_os_bysize = bysize([f"{w}s" for w in pl_sb_U_o_os_endings])
 779 | 
 780 | 
 781 | # UNCONDITIONAL "..ch" -> "..chs"
 782 | 
 783 | pl_sb_U_ch_chs_list = ("czech", "eunuch", "stomach")
 784 | 
 785 | (
 786 |     si_sb_U_ch_chs_list,
 787 |     si_sb_U_ch_chs_bysize,
 788 |     pl_sb_U_ch_chs_bysize,
 789 |     pl_sb_U_ch_chs,
 790 | ) = make_pl_si_lists(pl_sb_U_ch_chs_list, "s", None)
 791 | 
 792 | 
 793 | # UNCONDITIONAL "..[ei]x" -> "..ices"
 794 | 
 795 | pl_sb_U_ex_ices_list = ("codex", "murex", "silex")
 796 | (
 797 |     si_sb_U_ex_ices_list,
 798 |     si_sb_U_ex_ices_bysize,
 799 |     pl_sb_U_ex_ices_bysize,
 800 |     pl_sb_U_ex_ices,
 801 | ) = make_pl_si_lists(pl_sb_U_ex_ices_list, "ices", 2)
 802 | 
 803 | pl_sb_U_ix_ices_list = ("radix", "helix")
 804 | (
 805 |     si_sb_U_ix_ices_list,
 806 |     si_sb_U_ix_ices_bysize,
 807 |     pl_sb_U_ix_ices_bysize,
 808 |     pl_sb_U_ix_ices,
 809 | ) = make_pl_si_lists(pl_sb_U_ix_ices_list, "ices", 2)
 810 | 
 811 | # CLASSICAL "..[ei]x" -> "..ices"
 812 | 
 813 | pl_sb_C_ex_ices_list = (
 814 |     "vortex",
 815 |     "vertex",
 816 |     "cortex",
 817 |     "latex",
 818 |     "pontifex",
 819 |     "apex",
 820 |     "index",
 821 |     "simplex",
 822 | )
 823 | 
 824 | (
 825 |     si_sb_C_ex_ices_list,
 826 |     si_sb_C_ex_ices_bysize,
 827 |     pl_sb_C_ex_ices_bysize,
 828 |     pl_sb_C_ex_ices,
 829 | ) = make_pl_si_lists(pl_sb_C_ex_ices_list, "ices", 2)
 830 | 
 831 | 
 832 | pl_sb_C_ix_ices_list = ("appendix",)
 833 | 
 834 | (
 835 |     si_sb_C_ix_ices_list,
 836 |     si_sb_C_ix_ices_bysize,
 837 |     pl_sb_C_ix_ices_bysize,
 838 |     pl_sb_C_ix_ices,
 839 | ) = make_pl_si_lists(pl_sb_C_ix_ices_list, "ices", 2)
 840 | 
 841 | 
 842 | # ARABIC: ".." -> "..i"
 843 | 
 844 | pl_sb_C_i_list = ("afrit", "afreet", "efreet")
 845 | 
 846 | (si_sb_C_i_list, si_sb_C_i_bysize, pl_sb_C_i_bysize, pl_sb_C_i) = make_pl_si_lists(
 847 |     pl_sb_C_i_list, "i", None
 848 | )
 849 | 
 850 | 
 851 | # HEBREW: ".." -> "..im"
 852 | 
 853 | pl_sb_C_im_list = ("goy", "seraph", "cherub")
 854 | 
 855 | (si_sb_C_im_list, si_sb_C_im_bysize, pl_sb_C_im_bysize, pl_sb_C_im) = make_pl_si_lists(
 856 |     pl_sb_C_im_list, "im", None
 857 | )
 858 | 
 859 | 
 860 | # UNCONDITIONAL "..man" -> "..mans"
 861 | 
 862 | pl_sb_U_man_mans_list = """
 863 |     ataman caiman cayman ceriman
 864 |     desman dolman farman harman hetman
 865 |     human leman ottoman shaman talisman
 866 | """.split()
 867 | pl_sb_U_man_mans_caps_list = """
 868 |     Alabaman Bahaman Burman German
 869 |     Hiroshiman Liman Nakayaman Norman Oklahoman
 870 |     Panaman Roman Selman Sonaman Tacoman Yakiman
 871 |     Yokohaman Yuman
 872 | """.split()
 873 | 
 874 | (
 875 |     si_sb_U_man_mans_list,
 876 |     si_sb_U_man_mans_bysize,
 877 |     pl_sb_U_man_mans_bysize,
 878 | ) = make_pl_si_lists(pl_sb_U_man_mans_list, "s", None, dojoinstem=False)
 879 | (
 880 |     si_sb_U_man_mans_caps_list,
 881 |     si_sb_U_man_mans_caps_bysize,
 882 |     pl_sb_U_man_mans_caps_bysize,
 883 | ) = make_pl_si_lists(pl_sb_U_man_mans_caps_list, "s", None, dojoinstem=False)
 884 | 
 885 | # UNCONDITIONAL "..louse" -> "..lice"
 886 | pl_sb_U_louse_lice_list = ("booklouse", "grapelouse", "louse", "woodlouse")
 887 | 
 888 | (
 889 |     si_sb_U_louse_lice_list,
 890 |     si_sb_U_louse_lice_bysize,
 891 |     pl_sb_U_louse_lice_bysize,
 892 | ) = make_pl_si_lists(pl_sb_U_louse_lice_list, "lice", 5, dojoinstem=False)
 893 | 
 894 | pl_sb_uninflected_s_complete = [
 895 |     # PAIRS OR GROUPS SUBSUMED TO A SINGULAR...
 896 |     "breeches",
 897 |     "britches",
 898 |     "pajamas",
 899 |     "pyjamas",
 900 |     "clippers",
 901 |     "gallows",
 902 |     "hijinks",
 903 |     "headquarters",
 904 |     "pliers",
 905 |     "scissors",
 906 |     "testes",
 907 |     "herpes",
 908 |     "pincers",
 909 |     "shears",
 910 |     "proceedings",
 911 |     "trousers",
 912 |     # UNASSIMILATED LATIN 4th DECLENSION
 913 |     "cantus",
 914 |     "coitus",
 915 |     "nexus",
 916 |     # RECENT IMPORTS...
 917 |     "contretemps",
 918 |     "corps",
 919 |     "debris",
 920 |     "siemens",
 921 |     # DISEASES
 922 |     "mumps",
 923 |     # MISCELLANEOUS OTHERS...
 924 |     "diabetes",
 925 |     "jackanapes",
 926 |     "series",
 927 |     "species",
 928 |     "subspecies",
 929 |     "rabies",
 930 |     "chassis",
 931 |     "innings",
 932 |     "news",
 933 |     "mews",
 934 |     "haggis",
 935 | ]
 936 | 
 937 | pl_sb_uninflected_s_endings = [
 938 |     # RECENT IMPORTS...
 939 |     "ois",
 940 |     # DISEASES
 941 |     "measles",
 942 | ]
 943 | 
 944 | pl_sb_uninflected_s = pl_sb_uninflected_s_complete + [
 945 |     f".*{w}" for w in pl_sb_uninflected_s_endings
 946 | ]
 947 | 
 948 | pl_sb_uninflected_herd = (
 949 |     # DON'T INFLECT IN CLASSICAL MODE, OTHERWISE NORMAL INFLECTION
 950 |     "wildebeest",
 951 |     "swine",
 952 |     "eland",
 953 |     "bison",
 954 |     "buffalo",
 955 |     "cattle",
 956 |     "elk",
 957 |     "rhinoceros",
 958 |     "zucchini",
 959 |     "caribou",
 960 |     "dace",
 961 |     "grouse",
 962 |     "guinea fowl",
 963 |     "guinea-fowl",
 964 |     "haddock",
 965 |     "hake",
 966 |     "halibut",
 967 |     "herring",
 968 |     "mackerel",
 969 |     "pickerel",
 970 |     "pike",
 971 |     "roe",
 972 |     "seed",
 973 |     "shad",
 974 |     "snipe",
 975 |     "teal",
 976 |     "turbot",
 977 |     "water fowl",
 978 |     "water-fowl",
 979 | )
 980 | 
 981 | pl_sb_uninflected_complete = [
 982 |     # SOME FISH AND HERD ANIMALS
 983 |     "tuna",
 984 |     "salmon",
 985 |     "mackerel",
 986 |     "trout",
 987 |     "bream",
 988 |     "sea-bass",
 989 |     "sea bass",
 990 |     "carp",
 991 |     "cod",
 992 |     "flounder",
 993 |     "whiting",
 994 |     "moose",
 995 |     # OTHER ODDITIES
 996 |     "graffiti",
 997 |     "djinn",
 998 |     "samuri",
 999 |     "offspring",
1000 |     "pence",
1001 |     "quid",
1002 |     "hertz",
1003 | ] + pl_sb_uninflected_s_complete
1004 | # SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE)
1005 | 
1006 | pl_sb_uninflected_caps = [
1007 |     # ALL NATIONALS ENDING IN -ese
1008 |     "Portuguese",
1009 |     "Amoyese",
1010 |     "Borghese",
1011 |     "Congoese",
1012 |     "Faroese",
1013 |     "Foochowese",
1014 |     "Genevese",
1015 |     "Genoese",
1016 |     "Gilbertese",
1017 |     "Hottentotese",
1018 |     "Kiplingese",
1019 |     "Kongoese",
1020 |     "Lucchese",
1021 |     "Maltese",
1022 |     "Nankingese",
1023 |     "Niasese",
1024 |     "Pekingese",
1025 |     "Piedmontese",
1026 |     "Pistoiese",
1027 |     "Sarawakese",
1028 |     "Shavese",
1029 |     "Vermontese",
1030 |     "Wenchowese",
1031 |     "Yengeese",
1032 | ]
1033 | 
1034 | 
1035 | pl_sb_uninflected_endings = [
1036 |     # UNCOUNTABLE NOUNS
1037 |     "butter",
1038 |     "cash",
1039 |     "furniture",
1040 |     "information",
1041 |     # SOME FISH AND HERD ANIMALS
1042 |     "fish",
1043 |     "deer",
1044 |     "sheep",
1045 |     # ALL NATIONALS ENDING IN -ese
1046 |     "nese",
1047 |     "rese",
1048 |     "lese",
1049 |     "mese",
1050 |     # DISEASES
1051 |     "pox",
1052 |     # OTHER ODDITIES
1053 |     "craft",
1054 | ] + pl_sb_uninflected_s_endings
1055 | # SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE)
1056 | 
1057 | 
1058 | pl_sb_uninflected_bysize = bysize(pl_sb_uninflected_endings)
1059 | 
1060 | 
1061 | # SINGULAR WORDS ENDING IN ...s (ALL INFLECT WITH ...es)
1062 | 
1063 | pl_sb_singular_s_complete = [
1064 |     "acropolis",
1065 |     "aegis",
1066 |     "alias",
1067 |     "asbestos",
1068 |     "bathos",
1069 |     "bias",
1070 |     "bronchitis",
1071 |     "bursitis",
1072 |     "caddis",
1073 |     "cannabis",
1074 |     "canvas",
1075 |     "chaos",
1076 |     "cosmos",
1077 |     "dais",
1078 |     "digitalis",
1079 |     "epidermis",
1080 |     "ethos",
1081 |     "eyas",
1082 |     "gas",
1083 |     "glottis",
1084 |     "hubris",
1085 |     "ibis",
1086 |     "lens",
1087 |     "mantis",
1088 |     "marquis",
1089 |     "metropolis",
1090 |     "pathos",
1091 |     "pelvis",
1092 |     "polis",
1093 |     "rhinoceros",
1094 |     "sassafras",
1095 |     "trellis",
1096 | ] + pl_sb_C_is_ides_complete
1097 | 
1098 | 
1099 | pl_sb_singular_s_endings = ["ss", "us"] + pl_sb_C_is_ides_endings
1100 | 
1101 | pl_sb_singular_s_bysize = bysize(pl_sb_singular_s_endings)
1102 | 
1103 | si_sb_singular_s_complete = [f"{w}es" for w in pl_sb_singular_s_complete]
1104 | si_sb_singular_s_endings = [f"{w}es" for w in pl_sb_singular_s_endings]
1105 | si_sb_singular_s_bysize = bysize(si_sb_singular_s_endings)
1106 | 
1107 | pl_sb_singular_s_es = ["[A-Z].*es"]
1108 | 
1109 | pl_sb_singular_s = enclose(
1110 |     "|".join(
1111 |         pl_sb_singular_s_complete
1112 |         + [f".*{w}" for w in pl_sb_singular_s_endings]
1113 |         + pl_sb_singular_s_es
1114 |     )
1115 | )
1116 | 
1117 | 
1118 | # PLURALS ENDING IN uses -> use
1119 | 
1120 | 
1121 | si_sb_ois_oi_case = ("Bolshois", "Hanois")
1122 | 
1123 | si_sb_uses_use_case = ("Betelgeuses", "Duses", "Meuses", "Syracuses", "Toulouses")
1124 | 
1125 | si_sb_uses_use = (
1126 |     "abuses",
1127 |     "applauses",
1128 |     "blouses",
1129 |     "carouses",
1130 |     "causes",
1131 |     "chartreuses",
1132 |     "clauses",
1133 |     "contuses",
1134 |     "douses",
1135 |     "excuses",
1136 |     "fuses",
1137 |     "grouses",
1138 |     "hypotenuses",
1139 |     "masseuses",
1140 |     "menopauses",
1141 |     "misuses",
1142 |     "muses",
1143 |     "overuses",
1144 |     "pauses",
1145 |     "peruses",
1146 |     "profuses",
1147 |     "recluses",
1148 |     "reuses",
1149 |     "ruses",
1150 |     "souses",
1151 |     "spouses",
1152 |     "suffuses",
1153 |     "transfuses",
1154 |     "uses",
1155 | )
1156 | 
1157 | si_sb_ies_ie_case = (
1158 |     "Addies",
1159 |     "Aggies",
1160 |     "Allies",
1161 |     "Amies",
1162 |     "Angies",
1163 |     "Annies",
1164 |     "Annmaries",
1165 |     "Archies",
1166 |     "Arties",
1167 |     "Aussies",
1168 |     "Barbies",
1169 |     "Barries",
1170 |     "Basies",
1171 |     "Bennies",
1172 |     "Bernies",
1173 |     "Berties",
1174 |     "Bessies",
1175 |     "Betties",
1176 |     "Billies",
1177 |     "Blondies",
1178 |     "Bobbies",
1179 |     "Bonnies",
1180 |     "Bowies",
1181 |     "Brandies",
1182 |     "Bries",
1183 |     "Brownies",
1184 |     "Callies",
1185 |     "Carnegies",
1186 |     "Carries",
1187 |     "Cassies",
1188 |     "Charlies",
1189 |     "Cheries",
1190 |     "Christies",
1191 |     "Connies",
1192 |     "Curies",
1193 |     "Dannies",
1194 |     "Debbies",
1195 |     "Dixies",
1196 |     "Dollies",
1197 |     "Donnies",
1198 |     "Drambuies",
1199 |     "Eddies",
1200 |     "Effies",
1201 |     "Ellies",
1202 |     "Elsies",
1203 |     "Eries",
1204 |     "Ernies",
1205 |     "Essies",
1206 |     "Eugenies",
1207 |     "Fannies",
1208 |     "Flossies",
1209 |     "Frankies",
1210 |     "Freddies",
1211 |     "Gillespies",
1212 |     "Goldies",
1213 |     "Gracies",
1214 |     "Guthries",
1215 |     "Hallies",
1216 |     "Hatties",
1217 |     "Hetties",
1218 |     "Hollies",
1219 |     "Jackies",
1220 |     "Jamies",
1221 |     "Janies",
1222 |     "Jannies",
1223 |     "Jeanies",
1224 |     "Jeannies",
1225 |     "Jennies",
1226 |     "Jessies",
1227 |     "Jimmies",
1228 |     "Jodies",
1229 |     "Johnies",
1230 |     "Johnnies",
1231 |     "Josies",
1232 |     "Julies",
1233 |     "Kalgoorlies",
1234 |     "Kathies",
1235 |     "Katies",
1236 |     "Kellies",
1237 |     "Kewpies",
1238 |     "Kristies",
1239 |     "Laramies",
1240 |     "Lassies",
1241 |     "Lauries",
1242 |     "Leslies",
1243 |     "Lessies",
1244 |     "Lillies",
1245 |     "Lizzies",
1246 |     "Lonnies",
1247 |     "Lories",
1248 |     "Lorries",
1249 |     "Lotties",
1250 |     "Louies",
1251 |     "Mackenzies",
1252 |     "Maggies",
1253 |     "Maisies",
1254 |     "Mamies",
1255 |     "Marcies",
1256 |     "Margies",
1257 |     "Maries",
1258 |     "Marjories",
1259 |     "Matties",
1260 |     "McKenzies",
1261 |     "Melanies",
1262 |     "Mickies",
1263 |     "Millies",
1264 |     "Minnies",
1265 |     "Mollies",
1266 |     "Mounties",
1267 |     "Nannies",
1268 |     "Natalies",
1269 |     "Nellies",
1270 |     "Netties",
1271 |     "Ollies",
1272 |     "Ozzies",
1273 |     "Pearlies",
1274 |     "Pottawatomies",
1275 |     "Reggies",
1276 |     "Richies",
1277 |     "Rickies",
1278 |     "Robbies",
1279 |     "Ronnies",
1280 |     "Rosalies",
1281 |     "Rosemaries",
1282 |     "Rosies",
1283 |     "Roxies",
1284 |     "Rushdies",
1285 |     "Ruthies",
1286 |     "Sadies",
1287 |     "Sallies",
1288 |     "Sammies",
1289 |     "Scotties",
1290 |     "Selassies",
1291 |     "Sherries",
1292 |     "Sophies",
1293 |     "Stacies",
1294 |     "Stefanies",
1295 |     "Stephanies",
1296 |     "Stevies",
1297 |     "Susies",
1298 |     "Sylvies",
1299 |     "Tammies",
1300 |     "Terries",
1301 |     "Tessies",
1302 |     "Tommies",
1303 |     "Tracies",
1304 |     "Trekkies",
1305 |     "Valaries",
1306 |     "Valeries",
1307 |     "Valkyries",
1308 |     "Vickies",
1309 |     "Virgies",
1310 |     "Willies",
1311 |     "Winnies",
1312 |     "Wylies",
1313 |     "Yorkies",
1314 | )
1315 | 
1316 | si_sb_ies_ie = (
1317 |     "aeries",
1318 |     "baggies",
1319 |     "belies",
1320 |     "biggies",
1321 |     "birdies",
1322 |     "bogies",
1323 |     "bonnies",
1324 |     "boogies",
1325 |     "bookies",
1326 |     "bourgeoisies",
1327 |     "brownies",
1328 |     "budgies",
1329 |     "caddies",
1330 |     "calories",
1331 |     "camaraderies",
1332 |     "cockamamies",
1333 |     "collies",
1334 |     "cookies",
1335 |     "coolies",
1336 |     "cooties",
1337 |     "coteries",
1338 |     "crappies",
1339 |     "curies",
1340 |     "cutesies",
1341 |     "dogies",
1342 |     "eyries",
1343 |     "floozies",
1344 |     "footsies",
1345 |     "freebies",
1346 |     "genies",
1347 |     "goalies",
1348 |     "groupies",
1349 |     "hies",
1350 |     "jalousies",
1351 |     "junkies",
1352 |     "kiddies",
1353 |     "laddies",
1354 |     "lassies",
1355 |     "lies",
1356 |     "lingeries",
1357 |     "magpies",
1358 |     "menageries",
1359 |     "mommies",
1360 |     "movies",
1361 |     "neckties",
1362 |     "newbies",
1363 |     "nighties",
1364 |     "oldies",
1365 |     "organdies",
1366 |     "overlies",
1367 |     "pies",
1368 |     "pinkies",
1369 |     "pixies",
1370 |     "potpies",
1371 |     "prairies",
1372 |     "quickies",
1373 |     "reveries",
1374 |     "rookies",
1375 |     "rotisseries",
1376 |     "softies",
1377 |     "sorties",
1378 |     "species",
1379 |     "stymies",
1380 |     "sweeties",
1381 |     "ties",
1382 |     "underlies",
1383 |     "unties",
1384 |     "veggies",
1385 |     "vies",
1386 |     "yuppies",
1387 |     "zombies",
1388 | )
1389 | 
1390 | 
1391 | si_sb_oes_oe_case = (
1392 |     "Chloes",
1393 |     "Crusoes",
1394 |     "Defoes",
1395 |     "Faeroes",
1396 |     "Ivanhoes",
1397 |     "Joes",
1398 |     "McEnroes",
1399 |     "Moes",
1400 |     "Monroes",
1401 |     "Noes",
1402 |     "Poes",
1403 |     "Roscoes",
1404 |     "Tahoes",
1405 |     "Tippecanoes",
1406 |     "Zoes",
1407 | )
1408 | 
1409 | si_sb_oes_oe = (
1410 |     "aloes",
1411 |     "backhoes",
1412 |     "canoes",
1413 |     "does",
1414 |     "floes",
1415 |     "foes",
1416 |     "hoes",
1417 |     "mistletoes",
1418 |     "oboes",
1419 |     "pekoes",
1420 |     "roes",
1421 |     "sloes",
1422 |     "throes",
1423 |     "tiptoes",
1424 |     "toes",
1425 |     "woes",
1426 | )
1427 | 
1428 | si_sb_z_zes = ("quartzes", "topazes")
1429 | 
1430 | si_sb_zzes_zz = ("buzzes", "fizzes", "frizzes", "razzes")
1431 | 
1432 | si_sb_ches_che_case = (
1433 |     "Andromaches",
1434 |     "Apaches",
1435 |     "Blanches",
1436 |     "Comanches",
1437 |     "Nietzsches",
1438 |     "Porsches",
1439 |     "Roches",
1440 | )
1441 | 
1442 | si_sb_ches_che = (
1443 |     "aches",
1444 |     "avalanches",
1445 |     "backaches",
1446 |     "bellyaches",
1447 |     "caches",
1448 |     "cloches",
1449 |     "creches",
1450 |     "douches",
1451 |     "earaches",
1452 |     "fiches",
1453 |     "headaches",
1454 |     "heartaches",
1455 |     "microfiches",
1456 |     "niches",
1457 |     "pastiches",
1458 |     "psyches",
1459 |     "quiches",
1460 |     "stomachaches",
1461 |     "toothaches",
1462 |     "tranches",
1463 | )
1464 | 
1465 | si_sb_xes_xe = ("annexes", "axes", "deluxes", "pickaxes")
1466 | 
1467 | si_sb_sses_sse_case = ("Hesses", "Jesses", "Larousses", "Matisses")
1468 | si_sb_sses_sse = (
1469 |     "bouillabaisses",
1470 |     "crevasses",
1471 |     "demitasses",
1472 |     "impasses",
1473 |     "mousses",
1474 |     "posses",
1475 | )
1476 | 
1477 | si_sb_ves_ve_case = (
1478 |     # *[nwl]ives -> [nwl]live
1479 |     "Clives",
1480 |     "Palmolives",
1481 | )
1482 | si_sb_ves_ve = (
1483 |     # *[^d]eaves -> eave
1484 |     "interweaves",
1485 |     "weaves",
1486 |     # *[nwl]ives -> [nwl]live
1487 |     "olives",
1488 |     # *[eoa]lves -> [eoa]lve
1489 |     "bivalves",
1490 |     "dissolves",
1491 |     "resolves",
1492 |     "salves",
1493 |     "twelves",
1494 |     "valves",
1495 | )
1496 | 
1497 | 
1498 | plverb_special_s = enclose(
1499 |     "|".join(
1500 |         [pl_sb_singular_s]
1501 |         + pl_sb_uninflected_s
1502 |         + list(pl_sb_irregular_s)
1503 |         + ["(.*[csx])is", "(.*)ceps", "[A-Z].*s"]
1504 |     )
1505 | )
1506 | 
1507 | _pl_sb_postfix_adj_defn = (
1508 |     ("general", enclose(r"(?!major|lieutenant|brigadier|adjutant|.*star)\S+")),
1509 |     ("martial", enclose("court")),
1510 |     ("force", enclose("pound")),
1511 | )
1512 | 
1513 | pl_sb_postfix_adj: Iterable[str] = (
1514 |     enclose(val + f"(?=(?:-|\\s+){key})") for key, val in _pl_sb_postfix_adj_defn
1515 | )
1516 | 
1517 | pl_sb_postfix_adj_stems = f"({'|'.join(pl_sb_postfix_adj)})(.*)"
1518 | 
1519 | 
1520 | # PLURAL WORDS ENDING IS es GO TO SINGULAR is
1521 | 
1522 | si_sb_es_is = (
1523 |     "amanuenses",
1524 |     "amniocenteses",
1525 |     "analyses",
1526 |     "antitheses",
1527 |     "apotheoses",
1528 |     "arterioscleroses",
1529 |     "atheroscleroses",
1530 |     "axes",
1531 |     # 'bases', # bases -> basis
1532 |     "catalyses",
1533 |     "catharses",
1534 |     "chasses",
1535 |     "cirrhoses",
1536 |     "cocces",
1537 |     "crises",
1538 |     "diagnoses",
1539 |     "dialyses",
1540 |     "diereses",
1541 |     "electrolyses",
1542 |     "emphases",
1543 |     "exegeses",
1544 |     "geneses",
1545 |     "halitoses",
1546 |     "hydrolyses",
1547 |     "hypnoses",
1548 |     "hypotheses",
1549 |     "hystereses",
1550 |     "metamorphoses",
1551 |     "metastases",
1552 |     "misdiagnoses",
1553 |     "mitoses",
1554 |     "mononucleoses",
1555 |     "narcoses",
1556 |     "necroses",
1557 |     "nemeses",
1558 |     "neuroses",
1559 |     "oases",
1560 |     "osmoses",
1561 |     "osteoporoses",
1562 |     "paralyses",
1563 |     "parentheses",
1564 |     "parthenogeneses",
1565 |     "periphrases",
1566 |     "photosyntheses",
1567 |     "probosces",
1568 |     "prognoses",
1569 |     "prophylaxes",
1570 |     "prostheses",
1571 |     "preces",
1572 |     "psoriases",
1573 |     "psychoanalyses",
1574 |     "psychokineses",
1575 |     "psychoses",
1576 |     "scleroses",
1577 |     "scolioses",
1578 |     "sepses",
1579 |     "silicoses",
1580 |     "symbioses",
1581 |     "synopses",
1582 |     "syntheses",
1583 |     "taxes",
1584 |     "telekineses",
1585 |     "theses",
1586 |     "thromboses",
1587 |     "tuberculoses",
1588 |     "urinalyses",
1589 | )
1590 | 
1591 | pl_prep_list = """
1592 |     about above across after among around at athwart before behind
1593 |     below beneath beside besides between betwixt beyond but by
1594 |     during except for from in into near of off on onto out over
1595 |     since till to under until unto upon with""".split()
1596 | 
1597 | pl_prep_list_da = pl_prep_list + ["de", "du", "da"]
1598 | 
1599 | pl_prep_bysize = bysize(pl_prep_list_da)
1600 | 
1601 | pl_prep = enclose("|".join(pl_prep_list_da))
1602 | 
1603 | pl_sb_prep_dual_compound = rf"(.*?)((?:-|\s+)(?:{pl_prep})(?:-|\s+))a(?:-|\s+)(.*)"
1604 | 
1605 | 
1606 | singular_pronoun_genders = {
1607 |     "neuter",
1608 |     "feminine",
1609 |     "masculine",
1610 |     "gender-neutral",
1611 |     "feminine or masculine",
1612 |     "masculine or feminine",
1613 | }
1614 | 
1615 | pl_pron_nom = {
1616 |     # NOMINATIVE    REFLEXIVE
1617 |     "i": "we",
1618 |     "myself": "ourselves",
1619 |     "you": "you",
1620 |     "yourself": "yourselves",
1621 |     "she": "they",
1622 |     "herself": "themselves",
1623 |     "he": "they",
1624 |     "himself": "themselves",
1625 |     "it": "they",
1626 |     "itself": "themselves",
1627 |     "they": "they",
1628 |     "themself": "themselves",
1629 |     #   POSSESSIVE
1630 |     "mine": "ours",
1631 |     "yours": "yours",
1632 |     "hers": "theirs",
1633 |     "his": "theirs",
1634 |     "its": "theirs",
1635 |     "theirs": "theirs",
1636 | }
1637 | 
1638 | si_pron: Dict[str, Dict[str, Union[str, Dict[str, str]]]] = {
1639 |     "nom": {v: k for (k, v) in pl_pron_nom.items()}
1640 | }
1641 | si_pron["nom"]["we"] = "I"
1642 | 
1643 | 
1644 | pl_pron_acc = {
1645 |     # ACCUSATIVE    REFLEXIVE
1646 |     "me": "us",
1647 |     "myself": "ourselves",
1648 |     "you": "you",
1649 |     "yourself": "yourselves",
1650 |     "her": "them",
1651 |     "herself": "themselves",
1652 |     "him": "them",
1653 |     "himself": "themselves",
1654 |     "it": "them",
1655 |     "itself": "themselves",
1656 |     "them": "them",
1657 |     "themself": "themselves",
1658 | }
1659 | 
1660 | pl_pron_acc_keys = enclose("|".join(pl_pron_acc))
1661 | pl_pron_acc_keys_bysize = bysize(pl_pron_acc)
1662 | 
1663 | si_pron["acc"] = {v: k for (k, v) in pl_pron_acc.items()}
1664 | 
1665 | for _thecase, _plur, _gend, _sing in (
1666 |     ("nom", "they", "neuter", "it"),
1667 |     ("nom", "they", "feminine", "she"),
1668 |     ("nom", "they", "masculine", "he"),
1669 |     ("nom", "they", "gender-neutral", "they"),
1670 |     ("nom", "they", "feminine or masculine", "she or he"),
1671 |     ("nom", "they", "masculine or feminine", "he or she"),
1672 |     ("nom", "themselves", "neuter", "itself"),
1673 |     ("nom", "themselves", "feminine", "herself"),
1674 |     ("nom", "themselves", "masculine", "himself"),
1675 |     ("nom", "themselves", "gender-neutral", "themself"),
1676 |     ("nom", "themselves", "feminine or masculine", "herself or himself"),
1677 |     ("nom", "themselves", "masculine or feminine", "himself or herself"),
1678 |     ("nom", "theirs", "neuter", "its"),
1679 |     ("nom", "theirs", "feminine", "hers"),
1680 |     ("nom", "theirs", "masculine", "his"),
1681 |     ("nom", "theirs", "gender-neutral", "theirs"),
1682 |     ("nom", "theirs", "feminine or masculine", "hers or his"),
1683 |     ("nom", "theirs", "masculine or feminine", "his or hers"),
1684 |     ("acc", "them", "neuter", "it"),
1685 |     ("acc", "them", "feminine", "her"),
1686 |     ("acc", "them", "masculine", "him"),
1687 |     ("acc", "them", "gender-neutral", "them"),
1688 |     ("acc", "them", "feminine or masculine", "her or him"),
1689 |     ("acc", "them", "masculine or feminine", "him or her"),
1690 |     ("acc", "themselves", "neuter", "itself"),
1691 |     ("acc", "themselves", "feminine", "herself"),
1692 |     ("acc", "themselves", "masculine", "himself"),
1693 |     ("acc", "themselves", "gender-neutral", "themself"),
1694 |     ("acc", "themselves", "feminine or masculine", "herself or himself"),
1695 |     ("acc", "themselves", "masculine or feminine", "himself or herself"),
1696 | ):
1697 |     try:
1698 |         si_pron[_thecase][_plur][_gend] = _sing  # type: ignore
1699 |     except TypeError:
1700 |         si_pron[_thecase][_plur] = {}
1701 |         si_pron[_thecase][_plur][_gend] = _sing  # type: ignore
1702 | 
1703 | 
1704 | si_pron_acc_keys = enclose("|".join(si_pron["acc"]))
1705 | si_pron_acc_keys_bysize = bysize(si_pron["acc"])
1706 | 
1707 | 
1708 | def get_si_pron(thecase, word, gender) -> str:
1709 |     try:
1710 |         sing = si_pron[thecase][word]
1711 |     except KeyError:
1712 |         raise  # not a pronoun
1713 |     try:
1714 |         return sing[gender]  # has several types due to gender
1715 |     except TypeError:
1716 |         return cast(str, sing)  # answer independent of gender
1717 | 
1718 | 
1719 | # These dictionaries group verbs by first, second and third person
1720 | # conjugations.
1721 | 
1722 | plverb_irregular_pres = {
1723 |     "am": "are",
1724 |     "are": "are",
1725 |     "is": "are",
1726 |     "was": "were",
1727 |     "were": "were",
1728 |     "have": "have",
1729 |     "has": "have",
1730 |     "do": "do",
1731 |     "does": "do",
1732 | }
1733 | 
1734 | plverb_ambiguous_pres = {
1735 |     "act": "act",
1736 |     "acts": "act",
1737 |     "blame": "blame",
1738 |     "blames": "blame",
1739 |     "can": "can",
1740 |     "must": "must",
1741 |     "fly": "fly",
1742 |     "flies": "fly",
1743 |     "copy": "copy",
1744 |     "copies": "copy",
1745 |     "drink": "drink",
1746 |     "drinks": "drink",
1747 |     "fight": "fight",
1748 |     "fights": "fight",
1749 |     "fire": "fire",
1750 |     "fires": "fire",
1751 |     "like": "like",
1752 |     "likes": "like",
1753 |     "look": "look",
1754 |     "looks": "look",
1755 |     "make": "make",
1756 |     "makes": "make",
1757 |     "reach": "reach",
1758 |     "reaches": "reach",
1759 |     "run": "run",
1760 |     "runs": "run",
1761 |     "sink": "sink",
1762 |     "sinks": "sink",
1763 |     "sleep": "sleep",
1764 |     "sleeps": "sleep",
1765 |     "view": "view",
1766 |     "views": "view",
1767 | }
1768 | 
1769 | plverb_ambiguous_pres_keys = re.compile(
1770 |     rf"^({enclose('|'.join(plverb_ambiguous_pres))})((\s.*)?)$", re.IGNORECASE
1771 | )
1772 | 
1773 | 
1774 | plverb_irregular_non_pres = (
1775 |     "did",
1776 |     "had",
1777 |     "ate",
1778 |     "made",
1779 |     "put",
1780 |     "spent",
1781 |     "fought",
1782 |     "sank",
1783 |     "gave",
1784 |     "sought",
1785 |     "shall",
1786 |     "could",
1787 |     "ought",
1788 |     "should",
1789 | )
1790 | 
1791 | plverb_ambiguous_non_pres = re.compile(
1792 |     r"^((?:thought|saw|bent|will|might|cut))((\s.*)?)$", re.IGNORECASE
1793 | )
1794 | 
1795 | # "..oes" -> "..oe" (the rest are "..oes" -> "o")
1796 | 
1797 | pl_v_oes_oe = ("canoes", "floes", "oboes", "roes", "throes", "woes")
1798 | pl_v_oes_oe_endings_size4 = ("hoes", "toes")
1799 | pl_v_oes_oe_endings_size5 = ("shoes",)
1800 | 
1801 | 
1802 | pl_count_zero = ("0", "no", "zero", "nil")
1803 | 
1804 | 
1805 | pl_count_one = ("1", "a", "an", "one", "each", "every", "this", "that")
1806 | 
1807 | pl_adj_special = {"a": "some", "an": "some", "this": "these", "that": "those"}
1808 | 
1809 | pl_adj_special_keys = re.compile(
1810 |     rf"^({enclose('|'.join(pl_adj_special))})$", re.IGNORECASE
1811 | )
1812 | 
1813 | pl_adj_poss = {
1814 |     "my": "our",
1815 |     "your": "your",
1816 |     "its": "their",
1817 |     "her": "their",
1818 |     "his": "their",
1819 |     "their": "their",
1820 | }
1821 | 
1822 | pl_adj_poss_keys = re.compile(rf"^({enclose('|'.join(pl_adj_poss))})$", re.IGNORECASE)
1823 | 
1824 | 
1825 | # 2. INDEFINITE ARTICLES
1826 | 
1827 | # THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND"
1828 | # CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY
1829 | # TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!)
1830 | 
1831 | A_abbrev = re.compile(
1832 |     r"""
1833 | ^(?! FJO | [HLMNS]Y.  | RY[EO] | SQU
1834 |   | ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU])
1835 | [FHLMNRSX][A-Z]
1836 | """,
1837 |     re.VERBOSE,
1838 | )
1839 | 
1840 | # THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A
1841 | # 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE
1842 | # IMPLIES AN ABBREVIATION.
1843 | 
1844 | A_y_cons = re.compile(r"^(y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt))", re.IGNORECASE)
1845 | 
1846 | # EXCEPTIONS TO EXCEPTIONS
1847 | 
1848 | A_explicit_a = re.compile(r"^((?:unabomber|unanimous|US))", re.IGNORECASE)
1849 | 
1850 | A_explicit_an = re.compile(
1851 |     r"^((?:euler|hour(?!i)|heir|honest|hono[ur]|mpeg))", re.IGNORECASE
1852 | )
1853 | 
1854 | A_ordinal_an = re.compile(r"^([aefhilmnorsx]-?th)", re.IGNORECASE)
1855 | 
1856 | A_ordinal_a = re.compile(r"^([bcdgjkpqtuvwyz]-?th)", re.IGNORECASE)
1857 | 
1858 | 
1859 | # NUMERICAL INFLECTIONS
1860 | 
1861 | nth = {
1862 |     0: "th",
1863 |     1: "st",
1864 |     2: "nd",
1865 |     3: "rd",
1866 |     4: "th",
1867 |     5: "th",
1868 |     6: "th",
1869 |     7: "th",
1870 |     8: "th",
1871 |     9: "th",
1872 |     11: "th",
1873 |     12: "th",
1874 |     13: "th",
1875 | }
1876 | nth_suff = set(nth.values())
1877 | 
1878 | ordinal = dict(
1879 |     ty="tieth",
1880 |     one="first",
1881 |     two="second",
1882 |     three="third",
1883 |     five="fifth",
1884 |     eight="eighth",
1885 |     nine="ninth",
1886 |     twelve="twelfth",
1887 | )
1888 | 
1889 | ordinal_suff = re.compile(rf"({'|'.join(ordinal)})\Z")
1890 | 
1891 | 
1892 | # NUMBERS
1893 | 
1894 | unit = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
1895 | teen = [
1896 |     "ten",
1897 |     "eleven",
1898 |     "twelve",
1899 |     "thirteen",
1900 |     "fourteen",
1901 |     "fifteen",
1902 |     "sixteen",
1903 |     "seventeen",
1904 |     "eighteen",
1905 |     "nineteen",
1906 | ]
1907 | ten = [
1908 |     "",
1909 |     "",
1910 |     "twenty",
1911 |     "thirty",
1912 |     "forty",
1913 |     "fifty",
1914 |     "sixty",
1915 |     "seventy",
1916 |     "eighty",
1917 |     "ninety",
1918 | ]
1919 | mill = [
1920 |     " ",
1921 |     " thousand",
1922 |     " million",
1923 |     " billion",
1924 |     " trillion",
1925 |     " quadrillion",
1926 |     " quintillion",
1927 |     " sextillion",
1928 |     " septillion",
1929 |     " octillion",
1930 |     " nonillion",
1931 |     " decillion",
1932 | ]
1933 | 
1934 | 
1935 | # SUPPORT CLASSICAL PLURALIZATIONS
1936 | 
1937 | def_classical = dict(
1938 |     all=False, zero=False, herd=False, names=True, persons=False, ancient=False
1939 | )
1940 | 
1941 | all_classical = {k: True for k in def_classical}
1942 | no_classical = {k: False for k in def_classical}
1943 | 
1944 | 
1945 | # Maps strings to built-in constant types
1946 | string_to_constant = {"True": True, "False": False, "None": None}
1947 | 
1948 | 
1949 | # Pre-compiled regular expression objects
1950 | DOLLAR_DIGITS = re.compile(r"\$(\d+)")
1951 | FUNCTION_CALL = re.compile(r"((\w+)\([^)]*\)*)", re.IGNORECASE)
1952 | PARTITION_WORD = re.compile(r"\A(\s*)(.+?)(\s*)\Z")
1953 | PL_SB_POSTFIX_ADJ_STEMS_RE = re.compile(
1954 |     rf"^(?:{pl_sb_postfix_adj_stems})$", re.IGNORECASE
1955 | )
1956 | PL_SB_PREP_DUAL_COMPOUND_RE = re.compile(
1957 |     rf"^(?:{pl_sb_prep_dual_compound})$", re.IGNORECASE
1958 | )
1959 | DENOMINATOR = re.compile(r"(?P<denominator>.+)( (per|a) .+)")
1960 | PLVERB_SPECIAL_S_RE = re.compile(rf"^({plverb_special_s})$")
1961 | WHITESPACE = re.compile(r"\s")
1962 | ENDS_WITH_S = re.compile(r"^(.*[^s])s$", re.IGNORECASE)
1963 | ENDS_WITH_APOSTROPHE_S = re.compile(r"^(.*)'s?$")
1964 | INDEFINITE_ARTICLE_TEST = re.compile(r"\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z", re.IGNORECASE)
1965 | SPECIAL_AN = re.compile(r"^[aefhilmnorsx]$", re.IGNORECASE)
1966 | SPECIAL_A = re.compile(r"^[bcdgjkpqtuvwyz]$", re.IGNORECASE)
1967 | SPECIAL_ABBREV_AN = re.compile(r"^[aefhilmnorsx][.-]", re.IGNORECASE)
1968 | SPECIAL_ABBREV_A = re.compile(r"^[a-z][.-]", re.IGNORECASE)
1969 | CONSONANTS = re.compile(r"^[^aeiouy]", re.IGNORECASE)
1970 | ARTICLE_SPECIAL_EU = re.compile(r"^e[uw]", re.IGNORECASE)
1971 | ARTICLE_SPECIAL_ONCE = re.compile(r"^onc?e\b", re.IGNORECASE)
1972 | ARTICLE_SPECIAL_ONETIME = re.compile(r"^onetime\b", re.IGNORECASE)
1973 | ARTICLE_SPECIAL_UNIT = re.compile(r"^uni([^nmd]|mo)", re.IGNORECASE)
1974 | ARTICLE_SPECIAL_UBA = re.compile(r"^u[bcfghjkqrst][aeiou]", re.IGNORECASE)
1975 | ARTICLE_SPECIAL_UKR = re.compile(r"^ukr", re.IGNORECASE)
1976 | SPECIAL_CAPITALS = re.compile(r"^U[NK][AIEO]?")
1977 | VOWELS = re.compile(r"^[aeiou]", re.IGNORECASE)
1978 | 
1979 | DIGIT_GROUP = re.compile(r"(\d)")
1980 | TWO_DIGITS = re.compile(r"(\d)(\d)")
1981 | THREE_DIGITS = re.compile(r"(\d)(\d)(\d)")
1982 | THREE_DIGITS_WORD = re.compile(r"(\d)(\d)(\d)(?=\D*\Z)")
1983 | TWO_DIGITS_WORD = re.compile(r"(\d)(\d)(?=\D*\Z)")
1984 | ONE_DIGIT_WORD = re.compile(r"(\d)(?=\D*\Z)")
1985 | 
1986 | FOUR_DIGIT_COMMA = re.compile(r"(\d)(\d{3}(?:,|\Z))")
1987 | NON_DIGIT = re.compile(r"\D")
1988 | WHITESPACES_COMMA = re.compile(r"\s+,")
1989 | COMMA_WORD = re.compile(r", (\S+)\s+\Z")
1990 | WHITESPACES = re.compile(r"\s+")
1991 | 
1992 | 
1993 | PRESENT_PARTICIPLE_REPLACEMENTS = (
1994 |     (re.compile(r"ie$"), r"y"),
1995 |     (
1996 |         re.compile(r"ue$"),
1997 |         r"u",
1998 |     ),  # TODO: isn't ue$ -> u encompassed in the following rule?
1999 |     (re.compile(r"([auy])e$"), r"\g<1>"),
2000 |     (re.compile(r"ski$"), r"ski"),
2001 |     (re.compile(r"[^b]i$"), r""),
2002 |     (re.compile(r"^(are|were)$"), r"be"),
2003 |     (re.compile(r"^(had)$"), r"hav"),
2004 |     (re.compile(r"^(hoe)$"), r"\g<1>"),
2005 |     (re.compile(r"([^e])e$"), r"\g<1>"),
2006 |     (re.compile(r"er$"), r"er"),
2007 |     (re.compile(r"([^aeiou][aeiouy]([bdgmnprst]))$"), r"\g<1>\g<2>"),
2008 | )
2009 | 
2010 | DIGIT = re.compile(r"\d")
2011 | 
2012 | 
2013 | class Words(str):
2014 |     lowered: str
2015 |     split_: List[str]
2016 |     first: str
2017 |     last: str
2018 | 
2019 |     def __init__(self, orig) -> None:
2020 |         self.lowered = self.lower()
2021 |         self.split_ = self.split()
2022 |         self.first = self.split_[0]
2023 |         self.last = self.split_[-1]
2024 | 
2025 | 
2026 | Falsish = Any  # ideally, falsish would only validate on bool(value) is False
2027 | 
2028 | 
2029 | _STATIC_TYPE_CHECKING = TYPE_CHECKING
2030 | # ^-- Workaround for typeguard AST manipulation:
2031 | #     https://github.com/agronholm/typeguard/issues/353#issuecomment-1556306554
2032 | 
2033 | if _STATIC_TYPE_CHECKING:  # pragma: no cover
2034 |     Word = Annotated[str, "String with at least 1 character"]
2035 | else:
2036 | 
2037 |     class _WordMeta(type):  # Too dynamic to be supported by mypy...
2038 |         def __instancecheck__(self, instance: Any) -> bool:
2039 |             return isinstance(instance, str) and len(instance) >= 1
2040 | 
2041 |     class Word(metaclass=_WordMeta):  # type: ignore[no-redef]
2042 |         """String with at least 1 character"""
2043 | 
2044 | 
2045 | class engine:
2046 |     def __init__(self) -> None:
2047 |         self.classical_dict = def_classical.copy()
2048 |         self.persistent_count: Optional[int] = None
2049 |         self.mill_count = 0
2050 |         self.pl_sb_user_defined: List[Optional[Word]] = []
2051 |         self.pl_v_user_defined: List[Optional[Word]] = []
2052 |         self.pl_adj_user_defined: List[Optional[Word]] = []
2053 |         self.si_sb_user_defined: List[Optional[Word]] = []
2054 |         self.A_a_user_defined: List[Optional[Word]] = []
2055 |         self.thegender = "neuter"
2056 |         self.__number_args: Optional[Dict[str, str]] = None
2057 | 
2058 |     @property
2059 |     def _number_args(self):
2060 |         return cast(Dict[str, str], self.__number_args)
2061 | 
2062 |     @_number_args.setter
2063 |     def _number_args(self, val):
2064 |         self.__number_args = val
2065 | 
2066 |     @typechecked
2067 |     def defnoun(self, singular: Optional[Word], plural: Optional[Word]) -> int:
2068 |         """
2069 |         Set the noun plural of singular to plural.
2070 | 
2071 |         """
2072 |         self.checkpat(singular)
2073 |         self.checkpatplural(plural)
2074 |         self.pl_sb_user_defined.extend((singular, plural))
2075 |         self.si_sb_user_defined.extend((plural, singular))
2076 |         return 1
2077 | 
2078 |     @typechecked
2079 |     def defverb(
2080 |         self,
2081 |         s1: Optional[Word],
2082 |         p1: Optional[Word],
2083 |         s2: Optional[Word],
2084 |         p2: Optional[Word],
2085 |         s3: Optional[Word],
2086 |         p3: Optional[Word],
2087 |     ) -> int:
2088 |         """
2089 |         Set the verb plurals for s1, s2 and s3 to p1, p2 and p3 respectively.
2090 | 
2091 |         Where 1, 2 and 3 represent the 1st, 2nd and 3rd person forms of the verb.
2092 | 
2093 |         """
2094 |         self.checkpat(s1)
2095 |         self.checkpat(s2)
2096 |         self.checkpat(s3)
2097 |         self.checkpatplural(p1)
2098 |         self.checkpatplural(p2)
2099 |         self.checkpatplural(p3)
2100 |         self.pl_v_user_defined.extend((s1, p1, s2, p2, s3, p3))
2101 |         return 1
2102 | 
2103 |     @typechecked
2104 |     def defadj(self, singular: Optional[Word], plural: Optional[Word]) -> int:
2105 |         """
2106 |         Set the adjective plural of singular to plural.
2107 | 
2108 |         """
2109 |         self.checkpat(singular)
2110 |         self.checkpatplural(plural)
2111 |         self.pl_adj_user_defined.extend((singular, plural))
2112 |         return 1
2113 | 
2114 |     @typechecked
2115 |     def defa(self, pattern: Optional[Word]) -> int:
2116 |         """
2117 |         Define the indefinite article as 'a' for words matching pattern.
2118 | 
2119 |         """
2120 |         self.checkpat(pattern)
2121 |         self.A_a_user_defined.extend((pattern, "a"))
2122 |         return 1
2123 | 
2124 |     @typechecked
2125 |     def defan(self, pattern: Optional[Word]) -> int:
2126 |         """
2127 |         Define the indefinite article as 'an' for words matching pattern.
2128 | 
2129 |         """
2130 |         self.checkpat(pattern)
2131 |         self.A_a_user_defined.extend((pattern, "an"))
2132 |         return 1
2133 | 
2134 |     def checkpat(self, pattern: Optional[Word]) -> None:
2135 |         """
2136 |         check for errors in a regex pattern
2137 |         """
2138 |         if pattern is None:
2139 |             return
2140 |         try:
2141 |             re.match(pattern, "")
2142 |         except re.error as err:
2143 |             raise BadUserDefinedPatternError(pattern) from err
2144 | 
2145 |     def checkpatplural(self, pattern: Optional[Word]) -> None:
2146 |         """
2147 |         check for errors in a regex replace pattern
2148 |         """
2149 |         return
2150 | 
2151 |     @typechecked
2152 |     def ud_match(self, word: Word, wordlist: Sequence[Optional[Word]]) -> Optional[str]:
2153 |         for i in range(len(wordlist) - 2, -2, -2):  # backwards through even elements
2154 |             mo = re.search(rf"^{wordlist[i]}$", word, re.IGNORECASE)
2155 |             if mo:
2156 |                 if wordlist[i + 1] is None:
2157 |                     return None
2158 |                 pl = DOLLAR_DIGITS.sub(
2159 |                     r"\\1", cast(Word, wordlist[i + 1])
2160 |                 )  # change $n to \n for expand
2161 |                 return mo.expand(pl)
2162 |         return None
2163 | 
2164 |     def classical(self, **kwargs) -> None:
2165 |         """
2166 |         turn classical mode on and off for various categories
2167 | 
2168 |         turn on all classical modes:
2169 |         classical()
2170 |         classical(all=True)
2171 | 
2172 |         turn on or off specific claassical modes:
2173 |         e.g.
2174 |         classical(herd=True)
2175 |         classical(names=False)
2176 | 
2177 |         By default all classical modes are off except names.
2178 | 
2179 |         unknown value in args or key in kwargs raises
2180 |         exception: UnknownClasicalModeError
2181 | 
2182 |         """
2183 |         if not kwargs:
2184 |             self.classical_dict = all_classical.copy()
2185 |             return
2186 |         if "all" in kwargs:
2187 |             if kwargs["all"]:
2188 |                 self.classical_dict = all_classical.copy()
2189 |             else:
2190 |                 self.classical_dict = no_classical.copy()
2191 | 
2192 |         for k, v in kwargs.items():
2193 |             if k in def_classical:
2194 |                 self.classical_dict[k] = v
2195 |             else:
2196 |                 raise UnknownClassicalModeError
2197 | 
2198 |     def num(
2199 |         self, count: Optional[int] = None, show: Optional[int] = None
2200 |     ) -> str:  # (;$count,$show)
2201 |         """
2202 |         Set the number to be used in other method calls.
2203 | 
2204 |         Returns count.
2205 | 
2206 |         Set show to False to return '' instead.
2207 | 
2208 |         """
2209 |         if count is not None:
2210 |             try:
2211 |                 self.persistent_count = int(count)
2212 |             except ValueError as err:
2213 |                 raise BadNumValueError from err
2214 |             if (show is None) or show:
2215 |                 return str(count)
2216 |         else:
2217 |             self.persistent_count = None
2218 |         return ""
2219 | 
2220 |     def gender(self, gender: str) -> None:
2221 |         """
2222 |         set the gender for the singular of plural pronouns
2223 | 
2224 |         can be one of:
2225 |         'neuter'                ('they' -> 'it')
2226 |         'feminine'              ('they' -> 'she')
2227 |         'masculine'             ('they' -> 'he')
2228 |         'gender-neutral'        ('they' -> 'they')
2229 |         'feminine or masculine' ('they' -> 'she or he')
2230 |         'masculine or feminine' ('they' -> 'he or she')
2231 |         """
2232 |         if gender in singular_pronoun_genders:
2233 |             self.thegender = gender
2234 |         else:
2235 |             raise BadGenderError
2236 | 
2237 |     def _get_value_from_ast(self, obj):
2238 |         """
2239 |         Return the value of the ast object.
2240 |         """
2241 |         if isinstance(obj, ast.Num):
2242 |             return obj.n
2243 |         elif isinstance(obj, ast.Str):
2244 |             return obj.s
2245 |         elif isinstance(obj, ast.List):
2246 |             return [self._get_value_from_ast(e) for e in obj.elts]
2247 |         elif isinstance(obj, ast.Tuple):
2248 |             return tuple([self._get_value_from_ast(e) for e in obj.elts])
2249 | 
2250 |         # None, True and False are NameConstants in Py3.4 and above.
2251 |         elif isinstance(obj, ast.NameConstant):
2252 |             return obj.value
2253 | 
2254 |         # Probably passed a variable name.
2255 |         # Or passed a single word without wrapping it in quotes as an argument
2256 |         # ex: p.inflect("I plural(see)") instead of p.inflect("I plural('see')")
2257 |         raise NameError(f"name '{obj.id}' is not defined")
2258 | 
2259 |     def _string_to_substitute(
2260 |         self, mo: Match, methods_dict: Dict[str, Callable]
2261 |     ) -> str:
2262 |         """
2263 |         Return the string to be substituted for the match.
2264 |         """
2265 |         matched_text, f_name = mo.groups()
2266 |         # matched_text is the complete match string. e.g. plural_noun(cat)
2267 |         # f_name is the function name. e.g. plural_noun
2268 | 
2269 |         # Return matched_text if function name is not in methods_dict
2270 |         if f_name not in methods_dict:
2271 |             return matched_text
2272 | 
2273 |         # Parse the matched text
2274 |         a_tree = ast.parse(matched_text)
2275 | 
2276 |         # get the args and kwargs from ast objects
2277 |         args_list = [
2278 |             self._get_value_from_ast(a)
2279 |             for a in a_tree.body[0].value.args  # type: ignore[attr-defined]
2280 |         ]
2281 |         kwargs_list = {
2282 |             kw.arg: self._get_value_from_ast(kw.value)
2283 |             for kw in a_tree.body[0].value.keywords  # type: ignore[attr-defined]
2284 |         }
2285 | 
2286 |         # Call the corresponding function
2287 |         return methods_dict[f_name](*args_list, **kwargs_list)
2288 | 
2289 |     # 0. PERFORM GENERAL INFLECTIONS IN A STRING
2290 | 
2291 |     @typechecked
2292 |     def inflect(self, text: Word) -> str:
2293 |         """
2294 |         Perform inflections in a string.
2295 | 
2296 |         e.g. inflect('The plural of cat is plural(cat)') returns
2297 |         'The plural of cat is cats'
2298 | 
2299 |         can use plural, plural_noun, plural_verb, plural_adj,
2300 |         singular_noun, a, an, no, ordinal, number_to_words,
2301 |         and prespart
2302 | 
2303 |         """
2304 |         save_persistent_count = self.persistent_count
2305 | 
2306 |         # Dictionary of allowed methods
2307 |         methods_dict: Dict[str, Callable] = {
2308 |             "plural": self.plural,
2309 |             "plural_adj": self.plural_adj,
2310 |             "plural_noun": self.plural_noun,
2311 |             "plural_verb": self.plural_verb,
2312 |             "singular_noun": self.singular_noun,
2313 |             "a": self.a,
2314 |             "an": self.a,
2315 |             "no": self.no,
2316 |             "ordinal": self.ordinal,
2317 |             "number_to_words": self.number_to_words,
2318 |             "present_participle": self.present_participle,
2319 |             "num": self.num,
2320 |         }
2321 | 
2322 |         # Regular expression to find Python's function call syntax
2323 |         output = FUNCTION_CALL.sub(
2324 |             lambda mo: self._string_to_substitute(mo, methods_dict), text
2325 |         )
2326 |         self.persistent_count = save_persistent_count
2327 |         return output
2328 | 
2329 |     # ## PLURAL SUBROUTINES
2330 | 
2331 |     def postprocess(self, orig: str, inflected) -> str:
2332 |         inflected = str(inflected)
2333 |         if "|" in inflected:
2334 |             word_options = inflected.split("|")
2335 |             # When two parts of a noun need to be pluralized
2336 |             if len(word_options[0].split(" ")) == len(word_options[1].split(" ")):
2337 |                 result = inflected.split("|")[self.classical_dict["all"]].split(" ")
2338 |             # When only the last part of the noun needs to be pluralized
2339 |             else:
2340 |                 result = inflected.split(" ")
2341 |                 for index, word in enumerate(result):
2342 |                     if "|" in word:
2343 |                         result[index] = word.split("|")[self.classical_dict["all"]]
2344 |         else:
2345 |             result = inflected.split(" ")
2346 | 
2347 |         # Try to fix word wise capitalization
2348 |         for index, word in enumerate(orig.split(" ")):
2349 |             if word == "I":
2350 |                 # Is this the only word for exceptions like this
2351 |                 # Where the original is fully capitalized
2352 |                 # without 'meaning' capitalization?
2353 |                 # Also this fails to handle a capitalizaion in context
2354 |                 continue
2355 |             if word.capitalize() == word:
2356 |                 result[index] = result[index].capitalize()
2357 |             if word == word.upper():
2358 |                 result[index] = result[index].upper()
2359 |         return " ".join(result)
2360 | 
2361 |     def partition_word(self, text: str) -> Tuple[str, str, str]:
2362 |         mo = PARTITION_WORD.search(text)
2363 |         if mo:
2364 |             return mo.group(1), mo.group(2), mo.group(3)
2365 |         else:
2366 |             return "", "", ""
2367 | 
2368 |     @typechecked
2369 |     def plural(self, text: Word, count: Optional[Union[str, int, Any]] = None) -> str:
2370 |         """
2371 |         Return the plural of text.
2372 | 
2373 |         If count supplied, then return text if count is one of:
2374 |             1, a, an, one, each, every, this, that
2375 | 
2376 |         otherwise return the plural.
2377 | 
2378 |         Whitespace at the start and end is preserved.
2379 | 
2380 |         """
2381 |         pre, word, post = self.partition_word(text)
2382 |         if not word:
2383 |             return text
2384 |         plural = self.postprocess(
2385 |             word,
2386 |             self._pl_special_adjective(word, count)
2387 |             or self._pl_special_verb(word, count)
2388 |             or self._plnoun(word, count),
2389 |         )
2390 |         return f"{pre}{plural}{post}"
2391 | 
2392 |     @typechecked
2393 |     def plural_noun(
2394 |         self, text: Word, count: Optional[Union[str, int, Any]] = None
2395 |     ) -> str:
2396 |         """
2397 |         Return the plural of text, where text is a noun.
2398 | 
2399 |         If count supplied, then return text if count is one of:
2400 |             1, a, an, one, each, every, this, that
2401 | 
2402 |         otherwise return the plural.
2403 | 
2404 |         Whitespace at the start and end is preserved.
2405 | 
2406 |         """
2407 |         pre, word, post = self.partition_word(text)
2408 |         if not word:
2409 |             return text
2410 |         plural = self.postprocess(word, self._plnoun(word, count))
2411 |         return f"{pre}{plural}{post}"
2412 | 
2413 |     @typechecked
2414 |     def plural_verb(
2415 |         self, text: Word, count: Optional[Union[str, int, Any]] = None
2416 |     ) -> str:
2417 |         """
2418 |         Return the plural of text, where text is a verb.
2419 | 
2420 |         If count supplied, then return text if count is one of:
2421 |             1, a, an, one, each, every, this, that
2422 | 
2423 |         otherwise return the plural.
2424 | 
2425 |         Whitespace at the start and end is preserved.
2426 | 
2427 |         """
2428 |         pre, word, post = self.partition_word(text)
2429 |         if not word:
2430 |             return text
2431 |         plural = self.postprocess(
2432 |             word,
2433 |             self._pl_special_verb(word, count) or self._pl_general_verb(word, count),
2434 |         )
2435 |         return f"{pre}{plural}{post}"
2436 | 
2437 |     @typechecked
2438 |     def plural_adj(
2439 |         self, text: Word, count: Optional[Union[str, int, Any]] = None
2440 |     ) -> str:
2441 |         """
2442 |         Return the plural of text, where text is an adjective.
2443 | 
2444 |         If count supplied, then return text if count is one of:
2445 |             1, a, an, one, each, every, this, that
2446 | 
2447 |         otherwise return the plural.
2448 | 
2449 |         Whitespace at the start and end is preserved.
2450 | 
2451 |         """
2452 |         pre, word, post = self.partition_word(text)
2453 |         if not word:
2454 |             return text
2455 |         plural = self.postprocess(word, self._pl_special_adjective(word, count) or word)
2456 |         return f"{pre}{plural}{post}"
2457 | 
2458 |     @typechecked
2459 |     def compare(self, word1: Word, word2: Word) -> Union[str, bool]:
2460 |         """
2461 |         compare word1 and word2 for equality regardless of plurality
2462 | 
2463 |         return values:
2464 |         eq - the strings are equal
2465 |         p:s - word1 is the plural of word2
2466 |         s:p - word2 is the plural of word1
2467 |         p:p - word1 and word2 are two different plural forms of the one word
2468 |         False - otherwise
2469 | 
2470 |         >>> compare = engine().compare
2471 |         >>> compare("egg", "eggs")
2472 |         's:p'
2473 |         >>> compare('egg', 'egg')
2474 |         'eq'
2475 | 
2476 |         Words should not be empty.
2477 | 
2478 |         >>> compare('egg', '')
2479 |         Traceback (most recent call last):
2480 |         ...
2481 |         typeguard.TypeCheckError:...is not an instance of inflect.Word
2482 |         """
2483 |         norms = self.plural_noun, self.plural_verb, self.plural_adj
2484 |         results = (self._plequal(word1, word2, norm) for norm in norms)
2485 |         return next(filter(None, results), False)
2486 | 
2487 |     @typechecked
2488 |     def compare_nouns(self, word1: Word, word2: Word) -> Union[str, bool]:
2489 |         """
2490 |         compare word1 and word2 for equality regardless of plurality
2491 |         word1 and word2 are to be treated as nouns
2492 | 
2493 |         return values:
2494 |         eq - the strings are equal
2495 |         p:s - word1 is the plural of word2
2496 |         s:p - word2 is the plural of word1
2497 |         p:p - word1 and word2 are two different plural forms of the one word
2498 |         False - otherwise
2499 | 
2500 |         """
2501 |         return self._plequal(word1, word2, self.plural_noun)
2502 | 
2503 |     @typechecked
2504 |     def compare_verbs(self, word1: Word, word2: Word) -> Union[str, bool]:
2505 |         """
2506 |         compare word1 and word2 for equality regardless of plurality
2507 |         word1 and word2 are to be treated as verbs
2508 | 
2509 |         return values:
2510 |         eq - the strings are equal
2511 |         p:s - word1 is the plural of word2
2512 |         s:p - word2 is the plural of word1
2513 |         p:p - word1 and word2 are two different plural forms of the one word
2514 |         False - otherwise
2515 | 
2516 |         """
2517 |         return self._plequal(word1, word2, self.plural_verb)
2518 | 
2519 |     @typechecked
2520 |     def compare_adjs(self, word1: Word, word2: Word) -> Union[str, bool]:
2521 |         """
2522 |         compare word1 and word2 for equality regardless of plurality
2523 |         word1 and word2 are to be treated as adjectives
2524 | 
2525 |         return values:
2526 |         eq - the strings are equal
2527 |         p:s - word1 is the plural of word2
2528 |         s:p - word2 is the plural of word1
2529 |         p:p - word1 and word2 are two different plural forms of the one word
2530 |         False - otherwise
2531 | 
2532 |         """
2533 |         return self._plequal(word1, word2, self.plural_adj)
2534 | 
2535 |     @typechecked
2536 |     def singular_noun(
2537 |         self,
2538 |         text: Word,
2539 |         count: Optional[Union[int, str, Any]] = None,
2540 |         gender: Optional[str] = None,
2541 |     ) -> Union[str, Literal[False]]:
2542 |         """
2543 |         Return the singular of text, where text is a plural noun.
2544 | 
2545 |         If count supplied, then return the singular if count is one of:
2546 |             1, a, an, one, each, every, this, that or if count is None
2547 | 
2548 |         otherwise return text unchanged.
2549 | 
2550 |         Whitespace at the start and end is preserved.
2551 | 
2552 |         >>> p = engine()
2553 |         >>> p.singular_noun('horses')
2554 |         'horse'
2555 |         >>> p.singular_noun('knights')
2556 |         'knight'
2557 | 
2558 |         Returns False when a singular noun is passed.
2559 | 
2560 |         >>> p.singular_noun('horse')
2561 |         False
2562 |         >>> p.singular_noun('knight')
2563 |         False
2564 |         >>> p.singular_noun('soldier')
2565 |         False
2566 | 
2567 |         """
2568 |         pre, word, post = self.partition_word(text)
2569 |         if not word:
2570 |             return text
2571 |         sing = self._sinoun(word, count=count, gender=gender)
2572 |         if sing is not False:
2573 |             plural = self.postprocess(word, sing)
2574 |             return f"{pre}{plural}{post}"
2575 |         return False
2576 | 
2577 |     def _plequal(self, word1: str, word2: str, pl) -> Union[str, bool]:  # noqa: C901
2578 |         classval = self.classical_dict.copy()
2579 |         self.classical_dict = all_classical.copy()
2580 |         if word1 == word2:
2581 |             return "eq"
2582 |         if word1 == pl(word2):
2583 |             return "p:s"
2584 |         if pl(word1) == word2:
2585 |             return "s:p"
2586 |         self.classical_dict = no_classical.copy()
2587 |         if word1 == pl(word2):
2588 |             return "p:s"
2589 |         if pl(word1) == word2:
2590 |             return "s:p"
2591 |         self.classical_dict = classval.copy()
2592 | 
2593 |         if pl == self.plural or pl == self.plural_noun:
2594 |             if self._pl_check_plurals_N(word1, word2):
2595 |                 return "p:p"
2596 |             if self._pl_check_plurals_N(word2, word1):
2597 |                 return "p:p"
2598 |         if pl == self.plural or pl == self.plural_adj:
2599 |             if self._pl_check_plurals_adj(word1, word2):
2600 |                 return "p:p"
2601 |         return False
2602 | 
2603 |     def _pl_reg_plurals(self, pair: str, stems: str, end1: str, end2: str) -> bool:
2604 |         pattern = rf"({stems})({end1}\|\1{end2}|{end2}\|\1{end1})"
2605 |         return bool(re.search(pattern, pair))
2606 | 
2607 |     def _pl_check_plurals_N(self, word1: str, word2: str) -> bool:
2608 |         stem_endings = (
2609 |             (pl_sb_C_a_ata, "as", "ata"),
2610 |             (pl_sb_C_is_ides, "is", "ides"),
2611 |             (pl_sb_C_a_ae, "s", "e"),
2612 |             (pl_sb_C_en_ina, "ens", "ina"),
2613 |             (pl_sb_C_um_a, "ums", "a"),
2614 |             (pl_sb_C_us_i, "uses", "i"),
2615 |             (pl_sb_C_on_a, "ons", "a"),
2616 |             (pl_sb_C_o_i_stems, "os", "i"),
2617 |             (pl_sb_C_ex_ices, "exes", "ices"),
2618 |             (pl_sb_C_ix_ices, "ixes", "ices"),
2619 |             (pl_sb_C_i, "s", "i"),
2620 |             (pl_sb_C_im, "s", "im"),
2621 |             (".*eau", "s", "x"),
2622 |             (".*ieu", "s", "x"),
2623 |             (".*tri", "xes", "ces"),
2624 |             (".{2,}[yia]n", "xes", "ges"),
2625 |         )
2626 | 
2627 |         words = map(Words, (word1, word2))
2628 |         pair = "|".join(word.last for word in words)
2629 | 
2630 |         return (
2631 |             pair in pl_sb_irregular_s.values()
2632 |             or pair in pl_sb_irregular.values()
2633 |             or pair in pl_sb_irregular_caps.values()
2634 |             or any(
2635 |                 self._pl_reg_plurals(pair, stems, end1, end2)
2636 |                 for stems, end1, end2 in stem_endings
2637 |             )
2638 |         )
2639 | 
2640 |     def _pl_check_plurals_adj(self, word1: str, word2: str) -> bool:
2641 |         word1a = word1[: word1.rfind("'")] if word1.endswith(("'s", "'")) else ""
2642 |         word2a = word2[: word2.rfind("'")] if word2.endswith(("'s", "'")) else ""
2643 | 
2644 |         return (
2645 |             bool(word1a)
2646 |             and bool(word2a)
2647 |             and (
2648 |                 self._pl_check_plurals_N(word1a, word2a)
2649 |                 or self._pl_check_plurals_N(word2a, word1a)
2650 |             )
2651 |         )
2652 | 
2653 |     def get_count(self, count: Optional[Union[str, int]] = None) -> Union[str, int]:
2654 |         if count is None and self.persistent_count is not None:
2655 |             count = self.persistent_count
2656 | 
2657 |         if count is not None:
2658 |             count = (
2659 |                 1
2660 |                 if (
2661 |                     (str(count) in pl_count_one)
2662 |                     or (
2663 |                         self.classical_dict["zero"]
2664 |                         and str(count).lower() in pl_count_zero
2665 |                     )
2666 |                 )
2667 |                 else 2
2668 |             )
2669 |         else:
2670 |             count = ""
2671 |         return count
2672 | 
2673 |     # @profile
2674 |     def _plnoun(  # noqa: C901
2675 |         self, word: str, count: Optional[Union[str, int]] = None
2676 |     ) -> str:
2677 |         count = self.get_count(count)
2678 | 
2679 |         # DEFAULT TO PLURAL
2680 | 
2681 |         if count == 1:
2682 |             return word
2683 | 
2684 |         # HANDLE USER-DEFINED NOUNS
2685 | 
2686 |         value = self.ud_match(word, self.pl_sb_user_defined)
2687 |         if value is not None:
2688 |             return value
2689 | 
2690 |         # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS
2691 | 
2692 |         if word == "":
2693 |             return word
2694 | 
2695 |         word = Words(word)
2696 | 
2697 |         if word.last.lower() in pl_sb_uninflected_complete:
2698 |             if len(word.split_) >= 3:
2699 |                 return self._handle_long_compounds(word, count=2) or word
2700 |             return word
2701 | 
2702 |         if word in pl_sb_uninflected_caps:
2703 |             return word
2704 | 
2705 |         for k, v in pl_sb_uninflected_bysize.items():
2706 |             if word.lowered[-k:] in v:
2707 |                 return word
2708 | 
2709 |         if self.classical_dict["herd"] and word.last.lower() in pl_sb_uninflected_herd:
2710 |             return word
2711 | 
2712 |         # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.)
2713 | 
2714 |         mo = PL_SB_POSTFIX_ADJ_STEMS_RE.search(word)
2715 |         if mo and mo.group(2) != "":
2716 |             return f"{self._plnoun(mo.group(1), 2)}{mo.group(2)}"
2717 | 
2718 |         if " a " in word.lowered or "-a-" in word.lowered:
2719 |             mo = PL_SB_PREP_DUAL_COMPOUND_RE.search(word)
2720 |             if mo and mo.group(2) != "" and mo.group(3) != "":
2721 |                 return (
2722 |                     f"{self._plnoun(mo.group(1), 2)}"
2723 |                     f"{mo.group(2)}"
2724 |                     f"{self._plnoun(mo.group(3))}"
2725 |                 )
2726 | 
2727 |         if len(word.split_) >= 3:
2728 |             handled_words = self._handle_long_compounds(word, count=2)
2729 |             if handled_words is not None:
2730 |                 return handled_words
2731 | 
2732 |         # only pluralize denominators in units
2733 |         mo = DENOMINATOR.search(word.lowered)
2734 |         if mo:
2735 |             index = len(mo.group("denominator"))
2736 |             return f"{self._plnoun(word[:index])}{word[index:]}"
2737 | 
2738 |         # handle units given in degrees (only accept if
2739 |         # there is no more than one word following)
2740 |         # degree Celsius => degrees Celsius but degree
2741 |         # fahrenheit hour => degree fahrenheit hours
2742 |         if len(word.split_) >= 2 and word.split_[-2] == "degree":
2743 |             return " ".join([self._plnoun(word.first)] + word.split_[1:])
2744 | 
2745 |         with contextlib.suppress(ValueError):
2746 |             return self._handle_prepositional_phrase(
2747 |                 word.lowered,
2748 |                 functools.partial(self._plnoun, count=2),
2749 |                 '-',
2750 |             )
2751 | 
2752 |         # HANDLE PRONOUNS
2753 | 
2754 |         for k, v in pl_pron_acc_keys_bysize.items():
2755 |             if word.lowered[-k:] in v:  # ends with accusative pronoun
2756 |                 for pk, pv in pl_prep_bysize.items():
2757 |                     if word.lowered[:pk] in pv:  # starts with a prep
2758 |                         if word.lowered.split() == [
2759 |                             word.lowered[:pk],
2760 |                             word.lowered[-k:],
2761 |                         ]:
2762 |                             # only whitespace in between
2763 |                             return word.lowered[:-k] + pl_pron_acc[word.lowered[-k:]]
2764 | 
2765 |         try:
2766 |             return pl_pron_nom[word.lowered]
2767 |         except KeyError:
2768 |             pass
2769 | 
2770 |         try:
2771 |             return pl_pron_acc[word.lowered]
2772 |         except KeyError:
2773 |             pass
2774 | 
2775 |         # HANDLE ISOLATED IRREGULAR PLURALS
2776 | 
2777 |         if word.last in pl_sb_irregular_caps:
2778 |             llen = len(word.last)
2779 |             return f"{word[:-llen]}{pl_sb_irregular_caps[word.last]}"
2780 | 
2781 |         lowered_last = word.last.lower()
2782 |         if lowered_last in pl_sb_irregular:
2783 |             llen = len(lowered_last)
2784 |             return f"{word[:-llen]}{pl_sb_irregular[lowered_last]}"
2785 | 
2786 |         dash_split = word.lowered.split('-')
2787 |         if (" ".join(dash_split[-2:])).lower() in pl_sb_irregular_compound:
2788 |             llen = len(
2789 |                 " ".join(dash_split[-2:])
2790 |             )  # TODO: what if 2 spaces between these words?
2791 |             return (
2792 |                 f"{word[:-llen]}"
2793 |                 f"{pl_sb_irregular_compound[(' '.join(dash_split[-2:])).lower()]}"
2794 |             )
2795 | 
2796 |         if word.lowered[-3:] == "quy":
2797 |             return f"{word[:-1]}ies"
2798 | 
2799 |         if word.lowered[-6:] == "person":
2800 |             if self.classical_dict["persons"]:
2801 |                 return f"{word}s"
2802 |             else:
2803 |                 return f"{word[:-4]}ople"
2804 | 
2805 |         # HANDLE FAMILIES OF IRREGULAR PLURALS
2806 | 
2807 |         if word.lowered[-3:] == "man":
2808 |             for k, v in pl_sb_U_man_mans_bysize.items():
2809 |                 if word.lowered[-k:] in v:
2810 |                     return f"{word}s"
2811 |             for k, v in pl_sb_U_man_mans_caps_bysize.items():
2812 |                 if word[-k:] in v:
2813 |                     return f"{word}s"
2814 |             return f"{word[:-3]}men"
2815 |         if word.lowered[-5:] == "mouse":
2816 |             return f"{word[:-5]}mice"
2817 |         if word.lowered[-5:] == "louse":
2818 |             v = pl_sb_U_louse_lice_bysize.get(len(word))
2819 |             if v and word.lowered in v:
2820 |                 return f"{word[:-5]}lice"
2821 |             return f"{word}s"
2822 |         if word.lowered[-5:] == "goose":
2823 |             return f"{word[:-5]}geese"
2824 |         if word.lowered[-5:] == "tooth":
2825 |             return f"{word[:-5]}teeth"
2826 |         if word.lowered[-4:] == "foot":
2827 |             return f"{word[:-4]}feet"
2828 |         if word.lowered[-4:] == "taco":
2829 |             return f"{word[:-5]}tacos"
2830 | 
2831 |         if word.lowered == "die":
2832 |             return "dice"
2833 | 
2834 |         # HANDLE UNASSIMILATED IMPORTS
2835 | 
2836 |         if word.lowered[-4:] == "ceps":
2837 |             return word
2838 |         if word.lowered[-4:] == "zoon":
2839 |             return f"{word[:-2]}a"
2840 |         if word.lowered[-3:] in ("cis", "sis", "xis"):
2841 |             return f"{word[:-2]}es"
2842 | 
2843 |         for lastlet, d, numend, post in (
2844 |             ("h", pl_sb_U_ch_chs_bysize, None, "s"),
2845 |             ("x", pl_sb_U_ex_ices_bysize, -2, "ices"),
2846 |             ("x", pl_sb_U_ix_ices_bysize, -2, "ices"),
2847 |             ("m", pl_sb_U_um_a_bysize, -2, "a"),
2848 |             ("s", pl_sb_U_us_i_bysize, -2, "i"),
2849 |             ("n", pl_sb_U_on_a_bysize, -2, "a"),
2850 |             ("a", pl_sb_U_a_ae_bysize, None, "e"),
2851 |         ):
2852 |             if word.lowered[-1] == lastlet:  # this test to add speed
2853 |                 for k, v in d.items():
2854 |                     if word.lowered[-k:] in v:
2855 |                         return word[:numend] + post
2856 | 
2857 |         # HANDLE INCOMPLETELY ASSIMILATED IMPORTS
2858 | 
2859 |         if self.classical_dict["ancient"]:
2860 |             if word.lowered[-4:] == "trix":
2861 |                 return f"{word[:-1]}ces"
2862 |             if word.lowered[-3:] in ("eau", "ieu"):
2863 |                 return f"{word}x"
2864 |             if word.lowered[-3:] in ("ynx", "inx", "anx") and len(word) > 4:
2865 |                 return f"{word[:-1]}ges"
2866 | 
2867 |             for lastlet, d, numend, post in (
2868 |                 ("n", pl_sb_C_en_ina_bysize, -2, "ina"),
2869 |                 ("x", pl_sb_C_ex_ices_bysize, -2, "ices"),
2870 |                 ("x", pl_sb_C_ix_ices_bysize, -2, "ices"),
2871 |                 ("m", pl_sb_C_um_a_bysize, -2, "a"),
2872 |                 ("s", pl_sb_C_us_i_bysize, -2, "i"),
2873 |                 ("s", pl_sb_C_us_us_bysize, None, ""),
2874 |                 ("a", pl_sb_C_a_ae_bysize, None, "e"),
2875 |                 ("a", pl_sb_C_a_ata_bysize, None, "ta"),
2876 |                 ("s", pl_sb_C_is_ides_bysize, -1, "des"),
2877 |                 ("o", pl_sb_C_o_i_bysize, -1, "i"),
2878 |                 ("n", pl_sb_C_on_a_bysize, -2, "a"),
2879 |             ):
2880 |                 if word.lowered[-1] == lastlet:  # this test to add speed
2881 |                     for k, v in d.items():
2882 |                         if word.lowered[-k:] in v:
2883 |                             return word[:numend] + post
2884 | 
2885 |             for d, numend, post in (
2886 |                 (pl_sb_C_i_bysize, None, "i"),
2887 |                 (pl_sb_C_im_bysize, None, "im"),
2888 |             ):
2889 |                 for k, v in d.items():
2890 |                     if word.lowered[-k:] in v:
2891 |                         return word[:numend] + post
2892 | 
2893 |         # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS
2894 | 
2895 |         if lowered_last in pl_sb_singular_s_complete:
2896 |             return f"{word}es"
2897 | 
2898 |         for k, v in pl_sb_singular_s_bysize.items():
2899 |             if word.lowered[-k:] in v:
2900 |                 return f"{word}es"
2901 | 
2902 |         if word.lowered[-2:] == "es" and word[0] == word[0].upper():
2903 |             return f"{word}es"
2904 | 
2905 |         if word.lowered[-1] == "z":
2906 |             for k, v in pl_sb_z_zes_bysize.items():
2907 |                 if word.lowered[-k:] in v:
2908 |                     return f"{word}es"
2909 | 
2910 |             if word.lowered[-2:-1] != "z":
2911 |                 return f"{word}zes"
2912 | 
2913 |         if word.lowered[-2:] == "ze":
2914 |             for k, v in pl_sb_ze_zes_bysize.items():
2915 |                 if word.lowered[-k:] in v:
2916 |                     return f"{word}s"
2917 | 
2918 |         if word.lowered[-2:] in ("ch", "sh", "zz", "ss") or word.lowered[-1] == "x":
2919 |             return f"{word}es"
2920 | 
2921 |         # HANDLE ...f -> ...ves
2922 | 
2923 |         if word.lowered[-3:] in ("elf", "alf", "olf"):
2924 |             return f"{word[:-1]}ves"
2925 |         if word.lowered[-3:] == "eaf" and word.lowered[-4:-3] != "d":
2926 |             return f"{word[:-1]}ves"
2927 |         if word.lowered[-4:] in ("nife", "life", "wife"):
2928 |             return f"{word[:-2]}ves"
2929 |         if word.lowered[-3:] == "arf":
2930 |             return f"{word[:-1]}ves"
2931 | 
2932 |         # HANDLE ...y
2933 | 
2934 |         if word.lowered[-1] == "y":
2935 |             if word.lowered[-2:-1] in "aeiou" or len(word) == 1:
2936 |                 return f"{word}s"
2937 | 
2938 |             if self.classical_dict["names"]:
2939 |                 if word.lowered[-1] == "y" and word[0] == word[0].upper():
2940 |                     return f"{word}s"
2941 | 
2942 |             return f"{word[:-1]}ies"
2943 | 
2944 |         # HANDLE ...o
2945 | 
2946 |         if lowered_last in pl_sb_U_o_os_complete:
2947 |             return f"{word}s"
2948 | 
2949 |         for k, v in pl_sb_U_o_os_bysize.items():
2950 |             if word.lowered[-k:] in v:
2951 |                 return f"{word}s"
2952 | 
2953 |         if word.lowered[-2:] in ("ao", "eo", "io", "oo", "uo"):
2954 |             return f"{word}s"
2955 | 
2956 |         if word.lowered[-1] == "o":
2957 |             return f"{word}es"
2958 | 
2959 |         # OTHERWISE JUST ADD ...s
2960 | 
2961 |         return f"{word}s"
2962 | 
2963 |     @classmethod
2964 |     def _handle_prepositional_phrase(cls, phrase, transform, sep):
2965 |         """
2966 |         Given a word or phrase possibly separated by sep, parse out
2967 |         the prepositional phrase and apply the transform to the word
2968 |         preceding the prepositional phrase.
2969 | 
2970 |         Raise ValueError if the pivot is not found or if at least two
2971 |         separators are not found.
2972 | 
2973 |         >>> engine._handle_prepositional_phrase("man-of-war", str.upper, '-')
2974 |         'MAN-of-war'
2975 |         >>> engine._handle_prepositional_phrase("man of war", str.upper, ' ')
2976 |         'MAN of war'
2977 |         """
2978 |         parts = phrase.split(sep)
2979 |         if len(parts) < 3:
2980 |             raise ValueError("Cannot handle words with fewer than two separators")
2981 | 
2982 |         pivot = cls._find_pivot(parts, pl_prep_list_da)
2983 | 
2984 |         transformed = transform(parts[pivot - 1]) or parts[pivot - 1]
2985 |         return " ".join(
2986 |             parts[: pivot - 1] + [sep.join([transformed, parts[pivot], ''])]
2987 |         ) + " ".join(parts[(pivot + 1) :])
2988 | 
2989 |     def _handle_long_compounds(self, word: Words, count: int) -> Union[str, None]:
2990 |         """
2991 |         Handles the plural and singular for compound `Words` that
2992 |         have three or more words, based on the given count.
2993 | 
2994 |         >>> engine()._handle_long_compounds(Words("pair of scissors"), 2)
2995 |         'pairs of scissors'
2996 |         >>> engine()._handle_long_compounds(Words("men beyond hills"), 1)
2997 |         'man beyond hills'
2998 |         """
2999 |         inflection = self._sinoun if count == 1 else self._plnoun
3000 |         solutions = (  # type: ignore
3001 |             " ".join(
3002 |                 itertools.chain(
3003 |                     leader,
3004 |                     [inflection(cand, count), prep],  # type: ignore
3005 |                     trailer,
3006 |                 )
3007 |             )
3008 |             for leader, (cand, prep), trailer in windowed_complete(word.split_, 2)
3009 |             if prep in pl_prep_list_da  # type: ignore
3010 |         )
3011 |         return next(solutions, None)
3012 | 
3013 |     @staticmethod
3014 |     def _find_pivot(words, candidates):
3015 |         pivots = (
3016 |             index for index in range(1, len(words) - 1) if words[index] in candidates
3017 |         )
3018 |         try:
3019 |             return next(pivots)
3020 |         except StopIteration:
3021 |             raise ValueError("No pivot found") from None
3022 | 
3023 |     def _pl_special_verb(  # noqa: C901
3024 |         self, word: str, count: Optional[Union[str, int]] = None
3025 |     ) -> Union[str, bool]:
3026 |         if self.classical_dict["zero"] and str(count).lower() in pl_count_zero:
3027 |             return False
3028 |         count = self.get_count(count)
3029 | 
3030 |         if count == 1:
3031 |             return word
3032 | 
3033 |         # HANDLE USER-DEFINED VERBS
3034 | 
3035 |         value = self.ud_match(word, self.pl_v_user_defined)
3036 |         if value is not None:
3037 |             return value
3038 | 
3039 |         # HANDLE IRREGULAR PRESENT TENSE (SIMPLE AND COMPOUND)
3040 | 
3041 |         try:
3042 |             words = Words(word)
3043 |         except IndexError:
3044 |             return False  # word is ''
3045 | 
3046 |         if words.first in plverb_irregular_pres:
3047 |             return f"{plverb_irregular_pres[words.first]}{words[len(words.first) :]}"
3048 | 
3049 |         # HANDLE IRREGULAR FUTURE, PRETERITE AND PERFECT TENSES
3050 | 
3051 |         if words.first in plverb_irregular_non_pres:
3052 |             return word
3053 | 
3054 |         # HANDLE PRESENT NEGATIONS (SIMPLE AND COMPOUND)
3055 | 
3056 |         if words.first.endswith("n't") and words.first[:-3] in plverb_irregular_pres:
3057 |             return (
3058 |                 f"{plverb_irregular_pres[words.first[:-3]]}n't"
3059 |                 f"{words[len(words.first) :]}"
3060 |             )
3061 | 
3062 |         if words.first.endswith("n't"):
3063 |             return word
3064 | 
3065 |         # HANDLE SPECIAL CASES
3066 | 
3067 |         mo = PLVERB_SPECIAL_S_RE.search(word)
3068 |         if mo:
3069 |             return False
3070 |         if WHITESPACE.search(word):
3071 |             return False
3072 | 
3073 |         if words.lowered == "quizzes":
3074 |             return "quiz"
3075 | 
3076 |         # HANDLE STANDARD 3RD PERSON (CHOP THE ...(e)s OFF SINGLE WORDS)
3077 | 
3078 |         if (
3079 |             words.lowered[-4:] in ("ches", "shes", "zzes", "sses")
3080 |             or words.lowered[-3:] == "xes"
3081 |         ):
3082 |             return words[:-2]
3083 | 
3084 |         if words.lowered[-3:] == "ies" and len(words) > 3:
3085 |             return words.lowered[:-3] + "y"
3086 | 
3087 |         if (
3088 |             words.last.lower() in pl_v_oes_oe
3089 |             or words.lowered[-4:] in pl_v_oes_oe_endings_size4
3090 |             or words.lowered[-5:] in pl_v_oes_oe_endings_size5
3091 |         ):
3092 |             return words[:-1]
3093 | 
3094 |         if words.lowered.endswith("oes") and len(words) > 3:
3095 |             return words.lowered[:-2]
3096 | 
3097 |         mo = ENDS_WITH_S.search(words)
3098 |         if mo:
3099 |             return mo.group(1)
3100 | 
3101 |         # OTHERWISE, A REGULAR VERB (HANDLE ELSEWHERE)
3102 | 
3103 |         return False
3104 | 
3105 |     def _pl_general_verb(
3106 |         self, word: str, count: Optional[Union[str, int]] = None
3107 |     ) -> str:
3108 |         count = self.get_count(count)
3109 | 
3110 |         if count == 1:
3111 |             return word
3112 | 
3113 |         # HANDLE AMBIGUOUS PRESENT TENSES  (SIMPLE AND COMPOUND)
3114 | 
3115 |         mo = plverb_ambiguous_pres_keys.search(word)
3116 |         if mo:
3117 |             return f"{plverb_ambiguous_pres[mo.group(1).lower()]}{mo.group(2)}"
3118 | 
3119 |         # HANDLE AMBIGUOUS PRETERITE AND PERFECT TENSES
3120 | 
3121 |         mo = plverb_ambiguous_non_pres.search(word)
3122 |         if mo:
3123 |             return word
3124 | 
3125 |         # OTHERWISE, 1st OR 2ND PERSON IS UNINFLECTED
3126 | 
3127 |         return word
3128 | 
3129 |     def _pl_special_adjective(
3130 |         self, word: str, count: Optional[Union[str, int]] = None
3131 |     ) -> Union[str, bool]:
3132 |         count = self.get_count(count)
3133 | 
3134 |         if count == 1:
3135 |             return word
3136 | 
3137 |         # HANDLE USER-DEFINED ADJECTIVES
3138 | 
3139 |         value = self.ud_match(word, self.pl_adj_user_defined)
3140 |         if value is not None:
3141 |             return value
3142 | 
3143 |         # HANDLE KNOWN CASES
3144 | 
3145 |         mo = pl_adj_special_keys.search(word)
3146 |         if mo:
3147 |             return pl_adj_special[mo.group(1).lower()]
3148 | 
3149 |         # HANDLE POSSESSIVES
3150 | 
3151 |         mo = pl_adj_poss_keys.search(word)
3152 |         if mo:
3153 |             return pl_adj_poss[mo.group(1).lower()]
3154 | 
3155 |         mo = ENDS_WITH_APOSTROPHE_S.search(word)
3156 |         if mo:
3157 |             pl = self.plural_noun(mo.group(1))
3158 |             trailing_s = "" if pl[-1] == "s" else "s"
3159 |             return f"{pl}'{trailing_s}"
3160 | 
3161 |         # OTHERWISE, NO IDEA
3162 | 
3163 |         return False
3164 | 
3165 |     # @profile
3166 |     def _sinoun(  # noqa: C901
3167 |         self,
3168 |         word: str,
3169 |         count: Optional[Union[str, int]] = None,
3170 |         gender: Optional[str] = None,
3171 |     ) -> Union[str, bool]:
3172 |         count = self.get_count(count)
3173 | 
3174 |         # DEFAULT TO PLURAL
3175 | 
3176 |         if count == 2:
3177 |             return word
3178 | 
3179 |         # SET THE GENDER
3180 | 
3181 |         try:
3182 |             if gender is None:
3183 |                 gender = self.thegender
3184 |             elif gender not in singular_pronoun_genders:
3185 |                 raise BadGenderError
3186 |         except (TypeError, IndexError) as err:
3187 |             raise BadGenderError from err
3188 | 
3189 |         # HANDLE USER-DEFINED NOUNS
3190 | 
3191 |         value = self.ud_match(word, self.si_sb_user_defined)
3192 |         if value is not None:
3193 |             return value
3194 | 
3195 |         # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS
3196 | 
3197 |         if word == "":
3198 |             return word
3199 | 
3200 |         if word in si_sb_ois_oi_case:
3201 |             return word[:-1]
3202 | 
3203 |         words = Words(word)
3204 | 
3205 |         if words.last.lower() in pl_sb_uninflected_complete:
3206 |             if len(words.split_) >= 3:
3207 |                 return self._handle_long_compounds(words, count=1) or word
3208 |             return word
3209 | 
3210 |         if word in pl_sb_uninflected_caps:
3211 |             return word
3212 | 
3213 |         for k, v in pl_sb_uninflected_bysize.items():
3214 |             if words.lowered[-k:] in v:
3215 |                 return word
3216 | 
3217 |         if self.classical_dict["herd"] and words.last.lower() in pl_sb_uninflected_herd:
3218 |             return word
3219 | 
3220 |         if words.last.lower() in pl_sb_C_us_us:
3221 |             return word if self.classical_dict["ancient"] else False
3222 | 
3223 |         # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.)
3224 | 
3225 |         mo = PL_SB_POSTFIX_ADJ_STEMS_RE.search(word)
3226 |         if mo and mo.group(2) != "":
3227 |             return f"{self._sinoun(mo.group(1), 1, gender=gender)}{mo.group(2)}"
3228 | 
3229 |         with contextlib.suppress(ValueError):
3230 |             return self._handle_prepositional_phrase(
3231 |                 words.lowered,
3232 |                 functools.partial(self._sinoun, count=1, gender=gender),
3233 |                 ' ',
3234 |             )
3235 | 
3236 |         with contextlib.suppress(ValueError):
3237 |             return self._handle_prepositional_phrase(
3238 |                 words.lowered,
3239 |                 functools.partial(self._sinoun, count=1, gender=gender),
3240 |                 '-',
3241 |             )
3242 | 
3243 |         # HANDLE PRONOUNS
3244 | 
3245 |         for k, v in si_pron_acc_keys_bysize.items():
3246 |             if words.lowered[-k:] in v:  # ends with accusative pronoun
3247 |                 for pk, pv in pl_prep_bysize.items():
3248 |                     if words.lowered[:pk] in pv:  # starts with a prep
3249 |                         if words.lowered.split() == [
3250 |                             words.lowered[:pk],
3251 |                             words.lowered[-k:],
3252 |                         ]:
3253 |                             # only whitespace in between
3254 |                             return words.lowered[:-k] + get_si_pron(
3255 |                                 "acc", words.lowered[-k:], gender
3256 |                             )
3257 | 
3258 |         try:
3259 |             return get_si_pron("nom", words.lowered, gender)
3260 |         except KeyError:
3261 |             pass
3262 | 
3263 |         try:
3264 |             return get_si_pron("acc", words.lowered, gender)
3265 |         except KeyError:
3266 |             pass
3267 | 
3268 |         # HANDLE ISOLATED IRREGULAR PLURALS
3269 | 
3270 |         if words.last in si_sb_irregular_caps:
3271 |             llen = len(words.last)
3272 |             return f"{word[:-llen]}{si_sb_irregular_caps[words.last]}"
3273 | 
3274 |         if words.last.lower() in si_sb_irregular:
3275 |             llen = len(words.last.lower())
3276 |             return f"{word[:-llen]}{si_sb_irregular[words.last.lower()]}"
3277 | 
3278 |         dash_split = words.lowered.split("-")
3279 |         if (" ".join(dash_split[-2:])).lower() in si_sb_irregular_compound:
3280 |             llen = len(
3281 |                 " ".join(dash_split[-2:])
3282 |             )  # TODO: what if 2 spaces between these words?
3283 |             return "{}{}".format(
3284 |                 word[:-llen],
3285 |                 si_sb_irregular_compound[(" ".join(dash_split[-2:])).lower()],
3286 |             )
3287 | 
3288 |         if words.lowered[-5:] == "quies":
3289 |             return word[:-3] + "y"
3290 | 
3291 |         if words.lowered[-7:] == "persons":
3292 |             return word[:-1]
3293 |         if words.lowered[-6:] == "people":
3294 |             return word[:-4] + "rson"
3295 | 
3296 |         # HANDLE FAMILIES OF IRREGULAR PLURALS
3297 | 
3298 |         if words.lowered[-4:] == "mans":
3299 |             for k, v in si_sb_U_man_mans_bysize.items():
3300 |                 if words.lowered[-k:] in v:
3301 |                     return word[:-1]
3302 |             for k, v in si_sb_U_man_mans_caps_bysize.items():
3303 |                 if word[-k:] in v:
3304 |                     return word[:-1]
3305 |         if words.lowered[-3:] == "men":
3306 |             return word[:-3] + "man"
3307 |         if words.lowered[-4:] == "mice":
3308 |             return word[:-4] + "mouse"
3309 |         if words.lowered[-4:] == "lice":
3310 |             v = si_sb_U_louse_lice_bysize.get(len(word))
3311 |             if v and words.lowered in v:
3312 |                 return word[:-4] + "louse"
3313 |         if words.lowered[-5:] == "geese":
3314 |             return word[:-5] + "goose"
3315 |         if words.lowered[-5:] == "teeth":
3316 |             return word[:-5] + "tooth"
3317 |         if words.lowered[-4:] == "feet":
3318 |             return word[:-4] + "foot"
3319 | 
3320 |         if words.lowered == "dice":
3321 |             return "die"
3322 | 
3323 |         # HANDLE UNASSIMILATED IMPORTS
3324 | 
3325 |         if words.lowered[-4:] == "ceps":
3326 |             return word
3327 |         if words.lowered[-3:] == "zoa":
3328 |             return word[:-1] + "on"
3329 | 
3330 |         for lastlet, d, unass_numend, post in (
3331 |             ("s", si_sb_U_ch_chs_bysize, -1, ""),
3332 |             ("s", si_sb_U_ex_ices_bysize, -4, "ex"),
3333 |             ("s", si_sb_U_ix_ices_bysize, -4, "ix"),
3334 |             ("a", si_sb_U_um_a_bysize, -1, "um"),
3335 |             ("i", si_sb_U_us_i_bysize, -1, "us"),
3336 |             ("a", si_sb_U_on_a_bysize, -1, "on"),
3337 |             ("e", si_sb_U_a_ae_bysize, -1, ""),
3338 |         ):
3339 |             if words.lowered[-1] == lastlet:  # this test to add speed
3340 |                 for k, v in d.items():
3341 |                     if words.lowered[-k:] in v:
3342 |                         return word[:unass_numend] + post
3343 | 
3344 |         # HANDLE INCOMPLETELY ASSIMILATED IMPORTS
3345 | 
3346 |         if self.classical_dict["ancient"]:
3347 |             if words.lowered[-6:] == "trices":
3348 |                 return word[:-3] + "x"
3349 |             if words.lowered[-4:] in ("eaux", "ieux"):
3350 |                 return word[:-1]
3351 |             if words.lowered[-5:] in ("ynges", "inges", "anges") and len(word) > 6:
3352 |                 return word[:-3] + "x"
3353 | 
3354 |             for lastlet, d, class_numend, post in (
3355 |                 ("a", si_sb_C_en_ina_bysize, -3, "en"),
3356 |                 ("s", si_sb_C_ex_ices_bysize, -4, "ex"),
3357 |                 ("s", si_sb_C_ix_ices_bysize, -4, "ix"),
3358 |                 ("a", si_sb_C_um_a_bysize, -1, "um"),
3359 |                 ("i", si_sb_C_us_i_bysize, -1, "us"),
3360 |                 ("s", pl_sb_C_us_us_bysize, None, ""),
3361 |                 ("e", si_sb_C_a_ae_bysize, -1, ""),
3362 |                 ("a", si_sb_C_a_ata_bysize, -2, ""),
3363 |                 ("s", si_sb_C_is_ides_bysize, -3, "s"),
3364 |                 ("i", si_sb_C_o_i_bysize, -1, "o"),
3365 |                 ("a", si_sb_C_on_a_bysize, -1, "on"),
3366 |                 ("m", si_sb_C_im_bysize, -2, ""),
3367 |                 ("i", si_sb_C_i_bysize, -1, ""),
3368 |             ):
3369 |                 if words.lowered[-1] == lastlet:  # this test to add speed
3370 |                     for k, v in d.items():
3371 |                         if words.lowered[-k:] in v:
3372 |                             return word[:class_numend] + post
3373 | 
3374 |         # HANDLE PLURLS ENDING IN uses -> use
3375 | 
3376 |         if (
3377 |             words.lowered[-6:] == "houses"
3378 |             or word in si_sb_uses_use_case
3379 |             or words.last.lower() in si_sb_uses_use
3380 |         ):
3381 |             return word[:-1]
3382 | 
3383 |         # HANDLE PLURLS ENDING IN ies -> ie
3384 | 
3385 |         if word in si_sb_ies_ie_case or words.last.lower() in si_sb_ies_ie:
3386 |             return word[:-1]
3387 | 
3388 |         # HANDLE PLURLS ENDING IN oes -> oe
3389 | 
3390 |         if (
3391 |             words.lowered[-5:] == "shoes"
3392 |             or word in si_sb_oes_oe_case
3393 |             or words.last.lower() in si_sb_oes_oe
3394 |         ):
3395 |             return word[:-1]
3396 | 
3397 |         # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS
3398 | 
3399 |         if word in si_sb_sses_sse_case or words.last.lower() in si_sb_sses_sse:
3400 |             return word[:-1]
3401 | 
3402 |         if words.last.lower() in si_sb_singular_s_complete:
3403 |             return word[:-2]
3404 | 
3405 |         for k, v in si_sb_singular_s_bysize.items():
3406 |             if words.lowered[-k:] in v:
3407 |                 return word[:-2]
3408 | 
3409 |         if words.lowered[-4:] == "eses" and word[0] == word[0].upper():
3410 |             return word[:-2]
3411 | 
3412 |         if words.last.lower() in si_sb_z_zes:
3413 |             return word[:-2]
3414 | 
3415 |         if words.last.lower() in si_sb_zzes_zz:
3416 |             return word[:-2]
3417 | 
3418 |         if words.lowered[-4:] == "zzes":
3419 |             return word[:-3]
3420 | 
3421 |         if word in si_sb_ches_che_case or words.last.lower() in si_sb_ches_che:
3422 |             return word[:-1]
3423 | 
3424 |         if words.lowered[-4:] in ("ches", "shes"):
3425 |             return word[:-2]
3426 | 
3427 |         if words.last.lower() in si_sb_xes_xe:
3428 |             return word[:-1]
3429 | 
3430 |         if words.lowered[-3:] == "xes":
3431 |             return word[:-2]
3432 | 
3433 |         # HANDLE ...f -> ...ves
3434 | 
3435 |         if word in si_sb_ves_ve_case or words.last.lower() in si_sb_ves_ve:
3436 |             return word[:-1]
3437 | 
3438 |         if words.lowered[-3:] == "ves":
3439 |             if words.lowered[-5:-3] in ("el", "al", "ol"):
3440 |                 return word[:-3] + "f"
3441 |             if words.lowered[-5:-3] == "ea" and word[-6:-5] != "d":
3442 |                 return word[:-3] + "f"
3443 |             if words.lowered[-5:-3] in ("ni", "li", "wi"):
3444 |                 return word[:-3] + "fe"
3445 |             if words.lowered[-5:-3] == "ar":
3446 |                 return word[:-3] + "f"
3447 | 
3448 |         # HANDLE ...y
3449 | 
3450 |         if words.lowered[-2:] == "ys":
3451 |             if len(words.lowered) > 2 and words.lowered[-3] in "aeiou":
3452 |                 return word[:-1]
3453 | 
3454 |             if self.classical_dict["names"]:
3455 |                 if words.lowered[-2:] == "ys" and word[0] == word[0].upper():
3456 |                     return word[:-1]
3457 | 
3458 |         if words.lowered[-3:] == "ies":
3459 |             return word[:-3] + "y"
3460 | 
3461 |         # HANDLE ...o
3462 | 
3463 |         if words.lowered[-2:] == "os":
3464 |             if words.last.lower() in si_sb_U_o_os_complete:
3465 |                 return word[:-1]
3466 | 
3467 |             for k, v in si_sb_U_o_os_bysize.items():
3468 |                 if words.lowered[-k:] in v:
3469 |                     return word[:-1]
3470 | 
3471 |             if words.lowered[-3:] in ("aos", "eos", "ios", "oos", "uos"):
3472 |                 return word[:-1]
3473 | 
3474 |         if words.lowered[-3:] == "oes":
3475 |             return word[:-2]
3476 | 
3477 |         # UNASSIMILATED IMPORTS FINAL RULE
3478 | 
3479 |         if word in si_sb_es_is:
3480 |             return word[:-2] + "is"
3481 | 
3482 |         # OTHERWISE JUST REMOVE ...s
3483 | 
3484 |         if words.lowered[-1] == "s":
3485 |             return word[:-1]
3486 | 
3487 |         # COULD NOT FIND SINGULAR
3488 | 
3489 |         return False
3490 | 
3491 |     # ADJECTIVES
3492 | 
3493 |     @typechecked
3494 |     def a(self, text: Word, count: Optional[Union[int, str, Any]] = 1) -> str:
3495 |         """
3496 |         Return the appropriate indefinite article followed by text.
3497 | 
3498 |         The indefinite article is either 'a' or 'an'.
3499 | 
3500 |         If count is not one, then return count followed by text
3501 |         instead of 'a' or 'an'.
3502 | 
3503 |         Whitespace at the start and end is preserved.
3504 | 
3505 |         """
3506 |         mo = INDEFINITE_ARTICLE_TEST.search(text)
3507 |         if mo:
3508 |             word = mo.group(2)
3509 |             if not word:
3510 |                 return text
3511 |             pre = mo.group(1)
3512 |             post = mo.group(3)
3513 |             result = self._indef_article(word, count)
3514 |             return f"{pre}{result}{post}"
3515 |         return ""
3516 | 
3517 |     an = a
3518 | 
3519 |     _indef_article_cases = (
3520 |         # HANDLE ORDINAL FORMS
3521 |         (A_ordinal_a, "a"),
3522 |         (A_ordinal_an, "an"),
3523 |         # HANDLE SPECIAL CASES
3524 |         (A_explicit_an, "an"),
3525 |         (SPECIAL_AN, "an"),
3526 |         (SPECIAL_A, "a"),
3527 |         # HANDLE ABBREVIATIONS
3528 |         (A_abbrev, "an"),
3529 |         (SPECIAL_ABBREV_AN, "an"),
3530 |         (SPECIAL_ABBREV_A, "a"),
3531 |         # HANDLE CONSONANTS
3532 |         (CONSONANTS, "a"),
3533 |         # HANDLE SPECIAL VOWEL-FORMS
3534 |         (ARTICLE_SPECIAL_EU, "a"),
3535 |         (ARTICLE_SPECIAL_ONCE, "a"),
3536 |         (ARTICLE_SPECIAL_ONETIME, "a"),
3537 |         (ARTICLE_SPECIAL_UNIT, "a"),
3538 |         (ARTICLE_SPECIAL_UBA, "a"),
3539 |         (ARTICLE_SPECIAL_UKR, "a"),
3540 |         (A_explicit_a, "a"),
3541 |         # HANDLE SPECIAL CAPITALS
3542 |         (SPECIAL_CAPITALS, "a"),
3543 |         # HANDLE VOWELS
3544 |         (VOWELS, "an"),
3545 |         # HANDLE y...
3546 |         # (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND)
3547 |         (A_y_cons, "an"),
3548 |     )
3549 | 
3550 |     def _indef_article(self, word: str, count: Union[int, str, Any]) -> str:
3551 |         mycount = self.get_count(count)
3552 | 
3553 |         if mycount != 1:
3554 |             return f"{count} {word}"
3555 | 
3556 |         # HANDLE USER-DEFINED VARIANTS
3557 | 
3558 |         value = self.ud_match(word, self.A_a_user_defined)
3559 |         if value is not None:
3560 |             return f"{value} {word}"
3561 | 
3562 |         matches = (
3563 |             f'{article} {word}'
3564 |             for regexen, article in self._indef_article_cases
3565 |             if regexen.search(word)
3566 |         )
3567 | 
3568 |         # OTHERWISE, GUESS "a"
3569 |         fallback = f'a {word}'
3570 |         return next(matches, fallback)
3571 | 
3572 |     # 2. TRANSLATE ZERO-QUANTIFIED $word TO "no plural($word)"
3573 | 
3574 |     @typechecked
3575 |     def no(self, text: Word, count: Optional[Union[int, str]] = None) -> str:
3576 |         """
3577 |         If count is 0, no, zero or nil, return 'no' followed by the plural
3578 |         of text.
3579 | 
3580 |         If count is one of:
3581 |             1, a, an, one, each, every, this, that
3582 |             return count followed by text.
3583 | 
3584 |         Otherwise return count follow by the plural of text.
3585 | 
3586 |         In the return value count is always followed by a space.
3587 | 
3588 |         Whitespace at the start and end is preserved.
3589 | 
3590 |         """
3591 |         if count is None and self.persistent_count is not None:
3592 |             count = self.persistent_count
3593 | 
3594 |         if count is None:
3595 |             count = 0
3596 |         mo = PARTITION_WORD.search(text)
3597 |         if mo:
3598 |             pre = mo.group(1)
3599 |             word = mo.group(2)
3600 |             post = mo.group(3)
3601 |         else:
3602 |             pre = ""
3603 |             word = ""
3604 |             post = ""
3605 | 
3606 |         if str(count).lower() in pl_count_zero:
3607 |             count = 'no'
3608 |         return f"{pre}{count} {self.plural(word, count)}{post}"
3609 | 
3610 |     # PARTICIPLES
3611 | 
3612 |     @typechecked
3613 |     def present_participle(self, word: Word) -> str:
3614 |         """
3615 |         Return the present participle for word.
3616 | 
3617 |         word is the 3rd person singular verb.
3618 | 
3619 |         """
3620 |         plv = self.plural_verb(word, 2)
3621 |         ans = plv
3622 | 
3623 |         for regexen, repl in PRESENT_PARTICIPLE_REPLACEMENTS:
3624 |             ans, num = regexen.subn(repl, plv)
3625 |             if num:
3626 |                 return f"{ans}ing"
3627 |         return f"{ans}ing"
3628 | 
3629 |     # NUMERICAL INFLECTIONS
3630 | 
3631 |     @typechecked
3632 |     def ordinal(self, num: Union[Number, Word]) -> str:
3633 |         """
3634 |         Return the ordinal of num.
3635 | 
3636 |         >>> ordinal = engine().ordinal
3637 |         >>> ordinal(1)
3638 |         '1st'
3639 |         >>> ordinal('one')
3640 |         'first'
3641 |         """
3642 |         if DIGIT.match(str(num)):
3643 |             if isinstance(num, (float, int)) and int(num) == num:
3644 |                 n = int(num)
3645 |             else:
3646 |                 if "." in str(num):
3647 |                     try:
3648 |                         # numbers after decimal,
3649 |                         # so only need last one for ordinal
3650 |                         n = int(str(num)[-1])
3651 | 
3652 |                     except ValueError:  # ends with '.', so need to use whole string
3653 |                         n = int(str(num)[:-1])
3654 |                 else:
3655 |                     n = int(num)  # type: ignore
3656 |             try:
3657 |                 post = nth[n % 100]
3658 |             except KeyError:
3659 |                 post = nth[n % 10]
3660 |             return f"{num}{post}"
3661 |         else:
3662 |             return self._sub_ord(num)
3663 | 
3664 |     def millfn(self, ind: int = 0) -> str:
3665 |         if ind > len(mill) - 1:
3666 |             raise NumOutOfRangeError
3667 |         return mill[ind]
3668 | 
3669 |     def unitfn(self, units: int, mindex: int = 0) -> str:
3670 |         return f"{unit[units]}{self.millfn(mindex)}"
3671 | 
3672 |     def tenfn(self, tens, units, mindex=0) -> str:
3673 |         if tens != 1:
3674 |             tens_part = ten[tens]
3675 |             if tens and units:
3676 |                 hyphen = "-"
3677 |             else:
3678 |                 hyphen = ""
3679 |             unit_part = unit[units]
3680 |             mill_part = self.millfn(mindex)
3681 |             return f"{tens_part}{hyphen}{unit_part}{mill_part}"
3682 |         return f"{teen[units]}{mill[mindex]}"
3683 | 
3684 |     def hundfn(self, hundreds: int, tens: int, units: int, mindex: int) -> str:
3685 |         if hundreds:
3686 |             andword = f" {self._number_args['andword']} " if tens or units else ""
3687 |             # use unit not unitfn as simpler
3688 |             return (
3689 |                 f"{unit[hundreds]} hundred{andword}"
3690 |                 f"{self.tenfn(tens, units)}{self.millfn(mindex)}, "
3691 |             )
3692 |         if tens or units:
3693 |             return f"{self.tenfn(tens, units)}{self.millfn(mindex)}, "
3694 |         return ""
3695 | 
3696 |     def group1sub(self, mo: Match) -> str:
3697 |         units = int(mo.group(1))
3698 |         if units == 1:
3699 |             return f" {self._number_args['one']}, "
3700 |         elif units:
3701 |             return f"{unit[units]}, "
3702 |         else:
3703 |             return f" {self._number_args['zero']}, "
3704 | 
3705 |     def group1bsub(self, mo: Match) -> str:
3706 |         units = int(mo.group(1))
3707 |         if units:
3708 |             return f"{unit[units]}, "
3709 |         else:
3710 |             return f" {self._number_args['zero']}, "
3711 | 
3712 |     def group2sub(self, mo: Match) -> str:
3713 |         tens = int(mo.group(1))
3714 |         units = int(mo.group(2))
3715 |         if tens:
3716 |             return f"{self.tenfn(tens, units)}, "
3717 |         if units:
3718 |             return f" {self._number_args['zero']} {unit[units]}, "
3719 |         return f" {self._number_args['zero']} {self._number_args['zero']}, "
3720 | 
3721 |     def group3sub(self, mo: Match) -> str:
3722 |         hundreds = int(mo.group(1))
3723 |         tens = int(mo.group(2))
3724 |         units = int(mo.group(3))
3725 |         if hundreds == 1:
3726 |             hunword = f" {self._number_args['one']}"
3727 |         elif hundreds:
3728 |             hunword = str(unit[hundreds])
3729 |         else:
3730 |             hunword = f" {self._number_args['zero']}"
3731 |         if tens:
3732 |             tenword = self.tenfn(tens, units)
3733 |         elif units:
3734 |             tenword = f" {self._number_args['zero']} {unit[units]}"
3735 |         else:
3736 |             tenword = f" {self._number_args['zero']} {self._number_args['zero']}"
3737 |         return f"{hunword} {tenword}, "
3738 | 
3739 |     def hundsub(self, mo: Match) -> str:
3740 |         ret = self.hundfn(
3741 |             int(mo.group(1)), int(mo.group(2)), int(mo.group(3)), self.mill_count
3742 |         )
3743 |         self.mill_count += 1
3744 |         return ret
3745 | 
3746 |     def tensub(self, mo: Match) -> str:
3747 |         return f"{self.tenfn(int(mo.group(1)), int(mo.group(2)), self.mill_count)}, "
3748 | 
3749 |     def unitsub(self, mo: Match) -> str:
3750 |         return f"{self.unitfn(int(mo.group(1)), self.mill_count)}, "
3751 | 
3752 |     def enword(self, num: str, group: int) -> str:
3753 |         # import pdb
3754 |         # pdb.set_trace()
3755 | 
3756 |         if group == 1:
3757 |             num = DIGIT_GROUP.sub(self.group1sub, num)
3758 |         elif group == 2:
3759 |             num = TWO_DIGITS.sub(self.group2sub, num)
3760 |             num = DIGIT_GROUP.sub(self.group1bsub, num, 1)
3761 |         elif group == 3:
3762 |             num = THREE_DIGITS.sub(self.group3sub, num)
3763 |             num = TWO_DIGITS.sub(self.group2sub, num, 1)
3764 |             num = DIGIT_GROUP.sub(self.group1sub, num, 1)
3765 |         elif int(num) == 0:
3766 |             num = self._number_args["zero"]
3767 |         elif int(num) == 1:
3768 |             num = self._number_args["one"]
3769 |         else:
3770 |             num = num.lstrip().lstrip("0")
3771 |             self.mill_count = 0
3772 |             # surely there's a better way to do the next bit
3773 |             mo = THREE_DIGITS_WORD.search(num)
3774 |             while mo:
3775 |                 num = THREE_DIGITS_WORD.sub(self.hundsub, num, 1)
3776 |                 mo = THREE_DIGITS_WORD.search(num)
3777 |             num = TWO_DIGITS_WORD.sub(self.tensub, num, 1)
3778 |             num = ONE_DIGIT_WORD.sub(self.unitsub, num, 1)
3779 |         return num
3780 | 
3781 |     @staticmethod
3782 |     def _sub_ord(val):
3783 |         new = ordinal_suff.sub(lambda match: ordinal[match.group(1)], val)
3784 |         return new + "th" * (new == val)
3785 | 
3786 |     @classmethod
3787 |     def _chunk_num(cls, num, decimal, group):
3788 |         if decimal:
3789 |             max_split = -1 if group != 0 else 1
3790 |             chunks = num.split(".", max_split)
3791 |         else:
3792 |             chunks = [num]
3793 |         return cls._remove_last_blank(chunks)
3794 | 
3795 |     @staticmethod
3796 |     def _remove_last_blank(chunks):
3797 |         """
3798 |         Remove the last item from chunks if it's a blank string.
3799 | 
3800 |         Return the resultant chunks and whether the last item was removed.
3801 |         """
3802 |         removed = chunks[-1] == ""
3803 |         result = chunks[:-1] if removed else chunks
3804 |         return result, removed
3805 | 
3806 |     @staticmethod
3807 |     def _get_sign(num):
3808 |         return {'+': 'plus', '-': 'minus'}.get(num.lstrip()[0], '')
3809 | 
3810 |     @typechecked
3811 |     def number_to_words(  # noqa: C901
3812 |         self,
3813 |         num: Union[Number, Word],
3814 |         wantlist: bool = False,
3815 |         group: int = 0,
3816 |         comma: Union[Falsish, str] = ",",
3817 |         andword: str = "and",
3818 |         zero: str = "zero",
3819 |         one: str = "one",
3820 |         decimal: Union[Falsish, str] = "point",
3821 |         threshold: Optional[int] = None,
3822 |     ) -> Union[str, List[str]]:
3823 |         """
3824 |         Return a number in words.
3825 | 
3826 |         group = 1, 2 or 3 to group numbers before turning into words
3827 |         comma: define comma
3828 | 
3829 |         andword:
3830 |             word for 'and'. Can be set to ''.
3831 |             e.g. "one hundred and one" vs "one hundred one"
3832 | 
3833 |         zero: word for '0'
3834 |         one: word for '1'
3835 |         decimal: word for decimal point
3836 |         threshold: numbers above threshold not turned into words
3837 | 
3838 |         parameters not remembered from last call. Departure from Perl version.
3839 |         """
3840 |         self._number_args = {"andword": andword, "zero": zero, "one": one}
3841 |         num = str(num)
3842 | 
3843 |         # Handle "stylistic" conversions (up to a given threshold)...
3844 |         if threshold is not None and float(num) > threshold:
3845 |             spnum = num.split(".", 1)
3846 |             while comma:
3847 |                 (spnum[0], n) = FOUR_DIGIT_COMMA.subn(r"\1,\2", spnum[0])
3848 |                 if n == 0:
3849 |                     break
3850 |             try:
3851 |                 return f"{spnum[0]}.{spnum[1]}"
3852 |             except IndexError:
3853 |                 return str(spnum[0])
3854 | 
3855 |         if group < 0 or group > 3:
3856 |             raise BadChunkingOptionError
3857 | 
3858 |         sign = self._get_sign(num)
3859 | 
3860 |         if num in nth_suff:
3861 |             num = zero
3862 | 
3863 |         myord = num[-2:] in nth_suff
3864 |         if myord:
3865 |             num = num[:-2]
3866 | 
3867 |         chunks, finalpoint = self._chunk_num(num, decimal, group)
3868 | 
3869 |         loopstart = chunks[0] == ""
3870 |         first: bool | None = not loopstart
3871 | 
3872 |         def _handle_chunk(chunk):
3873 |             nonlocal first
3874 | 
3875 |             # remove all non numeric \D
3876 |             chunk = NON_DIGIT.sub("", chunk)
3877 |             if chunk == "":
3878 |                 chunk = "0"
3879 | 
3880 |             if group == 0 and not first:
3881 |                 chunk = self.enword(chunk, 1)
3882 |             else:
3883 |                 chunk = self.enword(chunk, group)
3884 | 
3885 |             if chunk[-2:] == ", ":
3886 |                 chunk = chunk[:-2]
3887 |             chunk = WHITESPACES_COMMA.sub(",", chunk)
3888 | 
3889 |             if group == 0 and first:
3890 |                 chunk = COMMA_WORD.sub(f" {andword} \\1", chunk)
3891 |             chunk = WHITESPACES.sub(" ", chunk)
3892 |             # chunk = re.sub(r"(\A\s|\s\Z)", self.blankfn, chunk)
3893 |             chunk = chunk.strip()
3894 |             if first:
3895 |                 first = None
3896 |             return chunk
3897 | 
3898 |         chunks[loopstart:] = map(_handle_chunk, chunks[loopstart:])
3899 | 
3900 |         numchunks = []
3901 |         if first != 0:
3902 |             numchunks = chunks[0].split(f"{comma} ")
3903 | 
3904 |         if myord and numchunks:
3905 |             numchunks[-1] = self._sub_ord(numchunks[-1])
3906 | 
3907 |         for chunk in chunks[1:]:
3908 |             numchunks.append(decimal)
3909 |             numchunks.extend(chunk.split(f"{comma} "))
3910 | 
3911 |         if finalpoint:
3912 |             numchunks.append(decimal)
3913 | 
3914 |         if wantlist:
3915 |             return [sign] * bool(sign) + numchunks
3916 | 
3917 |         signout = f"{sign} " if sign else ""
3918 |         valout = (
3919 |             ', '.join(numchunks)
3920 |             if group
3921 |             else ''.join(self._render(numchunks, decimal, comma))
3922 |         )
3923 |         return signout + valout
3924 | 
3925 |     @staticmethod
3926 |     def _render(chunks, decimal, comma):
3927 |         first_item = chunks.pop(0)
3928 |         yield first_item
3929 |         first = decimal is None or not first_item.endswith(decimal)
3930 |         for nc in chunks:
3931 |             if nc == decimal:
3932 |                 first = False
3933 |             elif first:
3934 |                 yield comma
3935 |             yield f" {nc}"
3936 | 
3937 |     @typechecked
3938 |     def join(
3939 |         self,
3940 |         words: Optional[Sequence[Word]],
3941 |         sep: Optional[str] = None,
3942 |         sep_spaced: bool = True,
3943 |         final_sep: Optional[str] = None,
3944 |         conj: str = "and",
3945 |         conj_spaced: bool = True,
3946 |     ) -> str:
3947 |         """
3948 |         Join words into a list.
3949 | 
3950 |         e.g. join(['ant', 'bee', 'fly']) returns 'ant, bee, and fly'
3951 | 
3952 |         options:
3953 |         conj: replacement for 'and'
3954 |         sep: separator. default ',', unless ',' is in the list then ';'
3955 |         final_sep: final separator. default ',', unless ',' is in the list then ';'
3956 |         conj_spaced: boolean. Should conj have spaces around it
3957 | 
3958 |         """
3959 |         if not words:
3960 |             return ""
3961 |         if len(words) == 1:
3962 |             return words[0]
3963 | 
3964 |         if conj_spaced:
3965 |             if conj == "":
3966 |                 conj = " "
3967 |             else:
3968 |                 conj = f" {conj} "
3969 | 
3970 |         if len(words) == 2:
3971 |             return f"{words[0]}{conj}{words[1]}"
3972 | 
3973 |         if sep is None:
3974 |             if "," in "".join(words):
3975 |                 sep = ";"
3976 |             else:
3977 |                 sep = ","
3978 |         if final_sep is None:
3979 |             final_sep = sep
3980 | 
3981 |         final_sep = f"{final_sep}{conj}"
3982 | 
3983 |         if sep_spaced:
3984 |             sep += " "
3985 | 
3986 |         return f"{sep.join(words[0:-1])}{final_sep}{words[-1]}"
3987 | 
```
Page 228/236FirstPrevNextLast