This is page 234 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/more_itertools/more.py:
--------------------------------------------------------------------------------
```python
1 | import math
2 | import warnings
3 |
4 | from collections import Counter, defaultdict, deque, abc
5 | from collections.abc import Sequence
6 | from functools import cached_property, partial, reduce, wraps
7 | from heapq import heapify, heapreplace, heappop
8 | from itertools import (
9 | chain,
10 | combinations,
11 | compress,
12 | count,
13 | cycle,
14 | dropwhile,
15 | groupby,
16 | islice,
17 | repeat,
18 | starmap,
19 | takewhile,
20 | tee,
21 | zip_longest,
22 | product,
23 | )
24 | from math import comb, e, exp, factorial, floor, fsum, log, perm, tau
25 | from queue import Empty, Queue
26 | from random import random, randrange, uniform
27 | from operator import itemgetter, mul, sub, gt, lt, ge, le
28 | from sys import hexversion, maxsize
29 | from time import monotonic
30 |
31 | from .recipes import (
32 | _marker,
33 | _zip_equal,
34 | UnequalIterablesError,
35 | consume,
36 | flatten,
37 | pairwise,
38 | powerset,
39 | take,
40 | unique_everseen,
41 | all_equal,
42 | batched,
43 | )
44 |
45 | __all__ = [
46 | 'AbortThread',
47 | 'SequenceView',
48 | 'UnequalIterablesError',
49 | 'adjacent',
50 | 'all_unique',
51 | 'always_iterable',
52 | 'always_reversible',
53 | 'bucket',
54 | 'callback_iter',
55 | 'chunked',
56 | 'chunked_even',
57 | 'circular_shifts',
58 | 'collapse',
59 | 'combination_index',
60 | 'combination_with_replacement_index',
61 | 'consecutive_groups',
62 | 'constrained_batches',
63 | 'consumer',
64 | 'count_cycle',
65 | 'countable',
66 | 'dft',
67 | 'difference',
68 | 'distinct_combinations',
69 | 'distinct_permutations',
70 | 'distribute',
71 | 'divide',
72 | 'doublestarmap',
73 | 'duplicates_everseen',
74 | 'duplicates_justseen',
75 | 'classify_unique',
76 | 'exactly_n',
77 | 'filter_except',
78 | 'filter_map',
79 | 'first',
80 | 'gray_product',
81 | 'groupby_transform',
82 | 'ichunked',
83 | 'iequals',
84 | 'idft',
85 | 'ilen',
86 | 'interleave',
87 | 'interleave_evenly',
88 | 'interleave_longest',
89 | 'intersperse',
90 | 'is_sorted',
91 | 'islice_extended',
92 | 'iterate',
93 | 'iter_suppress',
94 | 'join_mappings',
95 | 'last',
96 | 'locate',
97 | 'longest_common_prefix',
98 | 'lstrip',
99 | 'make_decorator',
100 | 'map_except',
101 | 'map_if',
102 | 'map_reduce',
103 | 'mark_ends',
104 | 'minmax',
105 | 'nth_or_last',
106 | 'nth_permutation',
107 | 'nth_product',
108 | 'nth_combination_with_replacement',
109 | 'numeric_range',
110 | 'one',
111 | 'only',
112 | 'outer_product',
113 | 'padded',
114 | 'partial_product',
115 | 'partitions',
116 | 'peekable',
117 | 'permutation_index',
118 | 'powerset_of_sets',
119 | 'product_index',
120 | 'raise_',
121 | 'repeat_each',
122 | 'repeat_last',
123 | 'replace',
124 | 'rlocate',
125 | 'rstrip',
126 | 'run_length',
127 | 'sample',
128 | 'seekable',
129 | 'set_partitions',
130 | 'side_effect',
131 | 'sliced',
132 | 'sort_together',
133 | 'split_after',
134 | 'split_at',
135 | 'split_before',
136 | 'split_into',
137 | 'split_when',
138 | 'spy',
139 | 'stagger',
140 | 'strip',
141 | 'strictly_n',
142 | 'substrings',
143 | 'substrings_indexes',
144 | 'takewhile_inclusive',
145 | 'time_limited',
146 | 'unique_in_window',
147 | 'unique_to_each',
148 | 'unzip',
149 | 'value_chain',
150 | 'windowed',
151 | 'windowed_complete',
152 | 'with_iter',
153 | 'zip_broadcast',
154 | 'zip_equal',
155 | 'zip_offset',
156 | ]
157 |
158 | # math.sumprod is available for Python 3.12+
159 | _fsumprod = getattr(math, 'sumprod', lambda x, y: fsum(map(mul, x, y)))
160 |
161 |
162 | def chunked(iterable, n, strict=False):
163 | """Break *iterable* into lists of length *n*:
164 |
165 | >>> list(chunked([1, 2, 3, 4, 5, 6], 3))
166 | [[1, 2, 3], [4, 5, 6]]
167 |
168 | By the default, the last yielded list will have fewer than *n* elements
169 | if the length of *iterable* is not divisible by *n*:
170 |
171 | >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3))
172 | [[1, 2, 3], [4, 5, 6], [7, 8]]
173 |
174 | To use a fill-in value instead, see the :func:`grouper` recipe.
175 |
176 | If the length of *iterable* is not divisible by *n* and *strict* is
177 | ``True``, then ``ValueError`` will be raised before the last
178 | list is yielded.
179 |
180 | """
181 | iterator = iter(partial(take, n, iter(iterable)), [])
182 | if strict:
183 | if n is None:
184 | raise ValueError('n must not be None when using strict mode.')
185 |
186 | def ret():
187 | for chunk in iterator:
188 | if len(chunk) != n:
189 | raise ValueError('iterable is not divisible by n.')
190 | yield chunk
191 |
192 | return iter(ret())
193 | else:
194 | return iterator
195 |
196 |
197 | def first(iterable, default=_marker):
198 | """Return the first item of *iterable*, or *default* if *iterable* is
199 | empty.
200 |
201 | >>> first([0, 1, 2, 3])
202 | 0
203 | >>> first([], 'some default')
204 | 'some default'
205 |
206 | If *default* is not provided and there are no items in the iterable,
207 | raise ``ValueError``.
208 |
209 | :func:`first` is useful when you have a generator of expensive-to-retrieve
210 | values and want any arbitrary one. It is marginally shorter than
211 | ``next(iter(iterable), default)``.
212 |
213 | """
214 | for item in iterable:
215 | return item
216 | if default is _marker:
217 | raise ValueError(
218 | 'first() was called on an empty iterable, and no '
219 | 'default value was provided.'
220 | )
221 | return default
222 |
223 |
224 | def last(iterable, default=_marker):
225 | """Return the last item of *iterable*, or *default* if *iterable* is
226 | empty.
227 |
228 | >>> last([0, 1, 2, 3])
229 | 3
230 | >>> last([], 'some default')
231 | 'some default'
232 |
233 | If *default* is not provided and there are no items in the iterable,
234 | raise ``ValueError``.
235 | """
236 | try:
237 | if isinstance(iterable, Sequence):
238 | return iterable[-1]
239 | # Work around https://bugs.python.org/issue38525
240 | elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0):
241 | return next(reversed(iterable))
242 | else:
243 | return deque(iterable, maxlen=1)[-1]
244 | except (IndexError, TypeError, StopIteration):
245 | if default is _marker:
246 | raise ValueError(
247 | 'last() was called on an empty iterable, and no default was '
248 | 'provided.'
249 | )
250 | return default
251 |
252 |
253 | def nth_or_last(iterable, n, default=_marker):
254 | """Return the nth or the last item of *iterable*,
255 | or *default* if *iterable* is empty.
256 |
257 | >>> nth_or_last([0, 1, 2, 3], 2)
258 | 2
259 | >>> nth_or_last([0, 1], 2)
260 | 1
261 | >>> nth_or_last([], 0, 'some default')
262 | 'some default'
263 |
264 | If *default* is not provided and there are no items in the iterable,
265 | raise ``ValueError``.
266 | """
267 | return last(islice(iterable, n + 1), default=default)
268 |
269 |
270 | class peekable:
271 | """Wrap an iterator to allow lookahead and prepending elements.
272 |
273 | Call :meth:`peek` on the result to get the value that will be returned
274 | by :func:`next`. This won't advance the iterator:
275 |
276 | >>> p = peekable(['a', 'b'])
277 | >>> p.peek()
278 | 'a'
279 | >>> next(p)
280 | 'a'
281 |
282 | Pass :meth:`peek` a default value to return that instead of raising
283 | ``StopIteration`` when the iterator is exhausted.
284 |
285 | >>> p = peekable([])
286 | >>> p.peek('hi')
287 | 'hi'
288 |
289 | peekables also offer a :meth:`prepend` method, which "inserts" items
290 | at the head of the iterable:
291 |
292 | >>> p = peekable([1, 2, 3])
293 | >>> p.prepend(10, 11, 12)
294 | >>> next(p)
295 | 10
296 | >>> p.peek()
297 | 11
298 | >>> list(p)
299 | [11, 12, 1, 2, 3]
300 |
301 | peekables can be indexed. Index 0 is the item that will be returned by
302 | :func:`next`, index 1 is the item after that, and so on:
303 | The values up to the given index will be cached.
304 |
305 | >>> p = peekable(['a', 'b', 'c', 'd'])
306 | >>> p[0]
307 | 'a'
308 | >>> p[1]
309 | 'b'
310 | >>> next(p)
311 | 'a'
312 |
313 | Negative indexes are supported, but be aware that they will cache the
314 | remaining items in the source iterator, which may require significant
315 | storage.
316 |
317 | To check whether a peekable is exhausted, check its truth value:
318 |
319 | >>> p = peekable(['a', 'b'])
320 | >>> if p: # peekable has items
321 | ... list(p)
322 | ['a', 'b']
323 | >>> if not p: # peekable is exhausted
324 | ... list(p)
325 | []
326 |
327 | """
328 |
329 | def __init__(self, iterable):
330 | self._it = iter(iterable)
331 | self._cache = deque()
332 |
333 | def __iter__(self):
334 | return self
335 |
336 | def __bool__(self):
337 | try:
338 | self.peek()
339 | except StopIteration:
340 | return False
341 | return True
342 |
343 | def peek(self, default=_marker):
344 | """Return the item that will be next returned from ``next()``.
345 |
346 | Return ``default`` if there are no items left. If ``default`` is not
347 | provided, raise ``StopIteration``.
348 |
349 | """
350 | if not self._cache:
351 | try:
352 | self._cache.append(next(self._it))
353 | except StopIteration:
354 | if default is _marker:
355 | raise
356 | return default
357 | return self._cache[0]
358 |
359 | def prepend(self, *items):
360 | """Stack up items to be the next ones returned from ``next()`` or
361 | ``self.peek()``. The items will be returned in
362 | first in, first out order::
363 |
364 | >>> p = peekable([1, 2, 3])
365 | >>> p.prepend(10, 11, 12)
366 | >>> next(p)
367 | 10
368 | >>> list(p)
369 | [11, 12, 1, 2, 3]
370 |
371 | It is possible, by prepending items, to "resurrect" a peekable that
372 | previously raised ``StopIteration``.
373 |
374 | >>> p = peekable([])
375 | >>> next(p)
376 | Traceback (most recent call last):
377 | ...
378 | StopIteration
379 | >>> p.prepend(1)
380 | >>> next(p)
381 | 1
382 | >>> next(p)
383 | Traceback (most recent call last):
384 | ...
385 | StopIteration
386 |
387 | """
388 | self._cache.extendleft(reversed(items))
389 |
390 | def __next__(self):
391 | if self._cache:
392 | return self._cache.popleft()
393 |
394 | return next(self._it)
395 |
396 | def _get_slice(self, index):
397 | # Normalize the slice's arguments
398 | step = 1 if (index.step is None) else index.step
399 | if step > 0:
400 | start = 0 if (index.start is None) else index.start
401 | stop = maxsize if (index.stop is None) else index.stop
402 | elif step < 0:
403 | start = -1 if (index.start is None) else index.start
404 | stop = (-maxsize - 1) if (index.stop is None) else index.stop
405 | else:
406 | raise ValueError('slice step cannot be zero')
407 |
408 | # If either the start or stop index is negative, we'll need to cache
409 | # the rest of the iterable in order to slice from the right side.
410 | if (start < 0) or (stop < 0):
411 | self._cache.extend(self._it)
412 | # Otherwise we'll need to find the rightmost index and cache to that
413 | # point.
414 | else:
415 | n = min(max(start, stop) + 1, maxsize)
416 | cache_len = len(self._cache)
417 | if n >= cache_len:
418 | self._cache.extend(islice(self._it, n - cache_len))
419 |
420 | return list(self._cache)[index]
421 |
422 | def __getitem__(self, index):
423 | if isinstance(index, slice):
424 | return self._get_slice(index)
425 |
426 | cache_len = len(self._cache)
427 | if index < 0:
428 | self._cache.extend(self._it)
429 | elif index >= cache_len:
430 | self._cache.extend(islice(self._it, index + 1 - cache_len))
431 |
432 | return self._cache[index]
433 |
434 |
435 | def consumer(func):
436 | """Decorator that automatically advances a PEP-342-style "reverse iterator"
437 | to its first yield point so you don't have to call ``next()`` on it
438 | manually.
439 |
440 | >>> @consumer
441 | ... def tally():
442 | ... i = 0
443 | ... while True:
444 | ... print('Thing number %s is %s.' % (i, (yield)))
445 | ... i += 1
446 | ...
447 | >>> t = tally()
448 | >>> t.send('red')
449 | Thing number 0 is red.
450 | >>> t.send('fish')
451 | Thing number 1 is fish.
452 |
453 | Without the decorator, you would have to call ``next(t)`` before
454 | ``t.send()`` could be used.
455 |
456 | """
457 |
458 | @wraps(func)
459 | def wrapper(*args, **kwargs):
460 | gen = func(*args, **kwargs)
461 | next(gen)
462 | return gen
463 |
464 | return wrapper
465 |
466 |
467 | def ilen(iterable):
468 | """Return the number of items in *iterable*.
469 |
470 | >>> ilen(x for x in range(1000000) if x % 3 == 0)
471 | 333334
472 |
473 | This consumes the iterable, so handle with care.
474 |
475 | """
476 | # This approach was selected because benchmarks showed it's likely the
477 | # fastest of the known implementations at the time of writing.
478 | # See GitHub tracker: #236, #230.
479 | counter = count()
480 | deque(zip(iterable, counter), maxlen=0)
481 | return next(counter)
482 |
483 |
484 | def iterate(func, start):
485 | """Return ``start``, ``func(start)``, ``func(func(start))``, ...
486 |
487 | >>> from itertools import islice
488 | >>> list(islice(iterate(lambda x: 2*x, 1), 10))
489 | [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
490 |
491 | """
492 | while True:
493 | yield start
494 | try:
495 | start = func(start)
496 | except StopIteration:
497 | break
498 |
499 |
500 | def with_iter(context_manager):
501 | """Wrap an iterable in a ``with`` statement, so it closes once exhausted.
502 |
503 | For example, this will close the file when the iterator is exhausted::
504 |
505 | upper_lines = (line.upper() for line in with_iter(open('foo')))
506 |
507 | Any context manager which returns an iterable is a candidate for
508 | ``with_iter``.
509 |
510 | """
511 | with context_manager as iterable:
512 | yield from iterable
513 |
514 |
515 | def one(iterable, too_short=None, too_long=None):
516 | """Return the first item from *iterable*, which is expected to contain only
517 | that item. Raise an exception if *iterable* is empty or has more than one
518 | item.
519 |
520 | :func:`one` is useful for ensuring that an iterable contains only one item.
521 | For example, it can be used to retrieve the result of a database query
522 | that is expected to return a single row.
523 |
524 | If *iterable* is empty, ``ValueError`` will be raised. You may specify a
525 | different exception with the *too_short* keyword:
526 |
527 | >>> it = []
528 | >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL
529 | Traceback (most recent call last):
530 | ...
531 | ValueError: too many items in iterable (expected 1)'
532 | >>> too_short = IndexError('too few items')
533 | >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL
534 | Traceback (most recent call last):
535 | ...
536 | IndexError: too few items
537 |
538 | Similarly, if *iterable* contains more than one item, ``ValueError`` will
539 | be raised. You may specify a different exception with the *too_long*
540 | keyword:
541 |
542 | >>> it = ['too', 'many']
543 | >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL
544 | Traceback (most recent call last):
545 | ...
546 | ValueError: Expected exactly one item in iterable, but got 'too',
547 | 'many', and perhaps more.
548 | >>> too_long = RuntimeError
549 | >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL
550 | Traceback (most recent call last):
551 | ...
552 | RuntimeError
553 |
554 | Note that :func:`one` attempts to advance *iterable* twice to ensure there
555 | is only one item. See :func:`spy` or :func:`peekable` to check iterable
556 | contents less destructively.
557 |
558 | """
559 | it = iter(iterable)
560 |
561 | try:
562 | first_value = next(it)
563 | except StopIteration as exc:
564 | raise (
565 | too_short or ValueError('too few items in iterable (expected 1)')
566 | ) from exc
567 |
568 | try:
569 | second_value = next(it)
570 | except StopIteration:
571 | pass
572 | else:
573 | msg = (
574 | 'Expected exactly one item in iterable, but got {!r}, {!r}, '
575 | 'and perhaps more.'.format(first_value, second_value)
576 | )
577 | raise too_long or ValueError(msg)
578 |
579 | return first_value
580 |
581 |
582 | def raise_(exception, *args):
583 | raise exception(*args)
584 |
585 |
586 | def strictly_n(iterable, n, too_short=None, too_long=None):
587 | """Validate that *iterable* has exactly *n* items and return them if
588 | it does. If it has fewer than *n* items, call function *too_short*
589 | with those items. If it has more than *n* items, call function
590 | *too_long* with the first ``n + 1`` items.
591 |
592 | >>> iterable = ['a', 'b', 'c', 'd']
593 | >>> n = 4
594 | >>> list(strictly_n(iterable, n))
595 | ['a', 'b', 'c', 'd']
596 |
597 | Note that the returned iterable must be consumed in order for the check to
598 | be made.
599 |
600 | By default, *too_short* and *too_long* are functions that raise
601 | ``ValueError``.
602 |
603 | >>> list(strictly_n('ab', 3)) # doctest: +IGNORE_EXCEPTION_DETAIL
604 | Traceback (most recent call last):
605 | ...
606 | ValueError: too few items in iterable (got 2)
607 |
608 | >>> list(strictly_n('abc', 2)) # doctest: +IGNORE_EXCEPTION_DETAIL
609 | Traceback (most recent call last):
610 | ...
611 | ValueError: too many items in iterable (got at least 3)
612 |
613 | You can instead supply functions that do something else.
614 | *too_short* will be called with the number of items in *iterable*.
615 | *too_long* will be called with `n + 1`.
616 |
617 | >>> def too_short(item_count):
618 | ... raise RuntimeError
619 | >>> it = strictly_n('abcd', 6, too_short=too_short)
620 | >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL
621 | Traceback (most recent call last):
622 | ...
623 | RuntimeError
624 |
625 | >>> def too_long(item_count):
626 | ... print('The boss is going to hear about this')
627 | >>> it = strictly_n('abcdef', 4, too_long=too_long)
628 | >>> list(it)
629 | The boss is going to hear about this
630 | ['a', 'b', 'c', 'd']
631 |
632 | """
633 | if too_short is None:
634 | too_short = lambda item_count: raise_(
635 | ValueError,
636 | 'Too few items in iterable (got {})'.format(item_count),
637 | )
638 |
639 | if too_long is None:
640 | too_long = lambda item_count: raise_(
641 | ValueError,
642 | 'Too many items in iterable (got at least {})'.format(item_count),
643 | )
644 |
645 | it = iter(iterable)
646 | for i in range(n):
647 | try:
648 | item = next(it)
649 | except StopIteration:
650 | too_short(i)
651 | return
652 | else:
653 | yield item
654 |
655 | try:
656 | next(it)
657 | except StopIteration:
658 | pass
659 | else:
660 | too_long(n + 1)
661 |
662 |
663 | def distinct_permutations(iterable, r=None):
664 | """Yield successive distinct permutations of the elements in *iterable*.
665 |
666 | >>> sorted(distinct_permutations([1, 0, 1]))
667 | [(0, 1, 1), (1, 0, 1), (1, 1, 0)]
668 |
669 | Equivalent to ``set(permutations(iterable))``, except duplicates are not
670 | generated and thrown away. For larger input sequences this is much more
671 | efficient.
672 |
673 | Duplicate permutations arise when there are duplicated elements in the
674 | input iterable. The number of items returned is
675 | `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of
676 | items input, and each `x_i` is the count of a distinct item in the input
677 | sequence.
678 |
679 | If *r* is given, only the *r*-length permutations are yielded.
680 |
681 | >>> sorted(distinct_permutations([1, 0, 1], r=2))
682 | [(0, 1), (1, 0), (1, 1)]
683 | >>> sorted(distinct_permutations(range(3), r=2))
684 | [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
685 |
686 | """
687 |
688 | # Algorithm: https://w.wiki/Qai
689 | def _full(A):
690 | while True:
691 | # Yield the permutation we have
692 | yield tuple(A)
693 |
694 | # Find the largest index i such that A[i] < A[i + 1]
695 | for i in range(size - 2, -1, -1):
696 | if A[i] < A[i + 1]:
697 | break
698 | # If no such index exists, this permutation is the last one
699 | else:
700 | return
701 |
702 | # Find the largest index j greater than j such that A[i] < A[j]
703 | for j in range(size - 1, i, -1):
704 | if A[i] < A[j]:
705 | break
706 |
707 | # Swap the value of A[i] with that of A[j], then reverse the
708 | # sequence from A[i + 1] to form the new permutation
709 | A[i], A[j] = A[j], A[i]
710 | A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1]
711 |
712 | # Algorithm: modified from the above
713 | def _partial(A, r):
714 | # Split A into the first r items and the last r items
715 | head, tail = A[:r], A[r:]
716 | right_head_indexes = range(r - 1, -1, -1)
717 | left_tail_indexes = range(len(tail))
718 |
719 | while True:
720 | # Yield the permutation we have
721 | yield tuple(head)
722 |
723 | # Starting from the right, find the first index of the head with
724 | # value smaller than the maximum value of the tail - call it i.
725 | pivot = tail[-1]
726 | for i in right_head_indexes:
727 | if head[i] < pivot:
728 | break
729 | pivot = head[i]
730 | else:
731 | return
732 |
733 | # Starting from the left, find the first value of the tail
734 | # with a value greater than head[i] and swap.
735 | for j in left_tail_indexes:
736 | if tail[j] > head[i]:
737 | head[i], tail[j] = tail[j], head[i]
738 | break
739 | # If we didn't find one, start from the right and find the first
740 | # index of the head with a value greater than head[i] and swap.
741 | else:
742 | for j in right_head_indexes:
743 | if head[j] > head[i]:
744 | head[i], head[j] = head[j], head[i]
745 | break
746 |
747 | # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)]
748 | tail += head[: i - r : -1] # head[i + 1:][::-1]
749 | i += 1
750 | head[i:], tail[:] = tail[: r - i], tail[r - i :]
751 |
752 | items = sorted(iterable)
753 |
754 | size = len(items)
755 | if r is None:
756 | r = size
757 |
758 | if 0 < r <= size:
759 | return _full(items) if (r == size) else _partial(items, r)
760 |
761 | return iter(() if r else ((),))
762 |
763 |
764 | def intersperse(e, iterable, n=1):
765 | """Intersperse filler element *e* among the items in *iterable*, leaving
766 | *n* items between each filler element.
767 |
768 | >>> list(intersperse('!', [1, 2, 3, 4, 5]))
769 | [1, '!', 2, '!', 3, '!', 4, '!', 5]
770 |
771 | >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2))
772 | [1, 2, None, 3, 4, None, 5]
773 |
774 | """
775 | if n == 0:
776 | raise ValueError('n must be > 0')
777 | elif n == 1:
778 | # interleave(repeat(e), iterable) -> e, x_0, e, x_1, e, x_2...
779 | # islice(..., 1, None) -> x_0, e, x_1, e, x_2...
780 | return islice(interleave(repeat(e), iterable), 1, None)
781 | else:
782 | # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]...
783 | # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]...
784 | # flatten(...) -> x_0, x_1, e, x_2, x_3...
785 | filler = repeat([e])
786 | chunks = chunked(iterable, n)
787 | return flatten(islice(interleave(filler, chunks), 1, None))
788 |
789 |
790 | def unique_to_each(*iterables):
791 | """Return the elements from each of the input iterables that aren't in the
792 | other input iterables.
793 |
794 | For example, suppose you have a set of packages, each with a set of
795 | dependencies::
796 |
797 | {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}}
798 |
799 | If you remove one package, which dependencies can also be removed?
800 |
801 | If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not
802 | associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for
803 | ``pkg_2``, and ``D`` is only needed for ``pkg_3``::
804 |
805 | >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'})
806 | [['A'], ['C'], ['D']]
807 |
808 | If there are duplicates in one input iterable that aren't in the others
809 | they will be duplicated in the output. Input order is preserved::
810 |
811 | >>> unique_to_each("mississippi", "missouri")
812 | [['p', 'p'], ['o', 'u', 'r']]
813 |
814 | It is assumed that the elements of each iterable are hashable.
815 |
816 | """
817 | pool = [list(it) for it in iterables]
818 | counts = Counter(chain.from_iterable(map(set, pool)))
819 | uniques = {element for element in counts if counts[element] == 1}
820 | return [list(filter(uniques.__contains__, it)) for it in pool]
821 |
822 |
823 | def windowed(seq, n, fillvalue=None, step=1):
824 | """Return a sliding window of width *n* over the given iterable.
825 |
826 | >>> all_windows = windowed([1, 2, 3, 4, 5], 3)
827 | >>> list(all_windows)
828 | [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
829 |
830 | When the window is larger than the iterable, *fillvalue* is used in place
831 | of missing values:
832 |
833 | >>> list(windowed([1, 2, 3], 4))
834 | [(1, 2, 3, None)]
835 |
836 | Each window will advance in increments of *step*:
837 |
838 | >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2))
839 | [(1, 2, 3), (3, 4, 5), (5, 6, '!')]
840 |
841 | To slide into the iterable's items, use :func:`chain` to add filler items
842 | to the left:
843 |
844 | >>> iterable = [1, 2, 3, 4]
845 | >>> n = 3
846 | >>> padding = [None] * (n - 1)
847 | >>> list(windowed(chain(padding, iterable), 3))
848 | [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)]
849 | """
850 | if n < 0:
851 | raise ValueError('n must be >= 0')
852 | if n == 0:
853 | yield ()
854 | return
855 | if step < 1:
856 | raise ValueError('step must be >= 1')
857 |
858 | iterable = iter(seq)
859 |
860 | # Generate first window
861 | window = deque(islice(iterable, n), maxlen=n)
862 |
863 | # Deal with the first window not being full
864 | if not window:
865 | return
866 | if len(window) < n:
867 | yield tuple(window) + ((fillvalue,) * (n - len(window)))
868 | return
869 | yield tuple(window)
870 |
871 | # Create the filler for the next windows. The padding ensures
872 | # we have just enough elements to fill the last window.
873 | padding = (fillvalue,) * (n - 1 if step >= n else step - 1)
874 | filler = map(window.append, chain(iterable, padding))
875 |
876 | # Generate the rest of the windows
877 | for _ in islice(filler, step - 1, None, step):
878 | yield tuple(window)
879 |
880 |
881 | def substrings(iterable):
882 | """Yield all of the substrings of *iterable*.
883 |
884 | >>> [''.join(s) for s in substrings('more')]
885 | ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more']
886 |
887 | Note that non-string iterables can also be subdivided.
888 |
889 | >>> list(substrings([0, 1, 2]))
890 | [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)]
891 |
892 | """
893 | # The length-1 substrings
894 | seq = []
895 | for item in iter(iterable):
896 | seq.append(item)
897 | yield (item,)
898 | seq = tuple(seq)
899 | item_count = len(seq)
900 |
901 | # And the rest
902 | for n in range(2, item_count + 1):
903 | for i in range(item_count - n + 1):
904 | yield seq[i : i + n]
905 |
906 |
907 | def substrings_indexes(seq, reverse=False):
908 | """Yield all substrings and their positions in *seq*
909 |
910 | The items yielded will be a tuple of the form ``(substr, i, j)``, where
911 | ``substr == seq[i:j]``.
912 |
913 | This function only works for iterables that support slicing, such as
914 | ``str`` objects.
915 |
916 | >>> for item in substrings_indexes('more'):
917 | ... print(item)
918 | ('m', 0, 1)
919 | ('o', 1, 2)
920 | ('r', 2, 3)
921 | ('e', 3, 4)
922 | ('mo', 0, 2)
923 | ('or', 1, 3)
924 | ('re', 2, 4)
925 | ('mor', 0, 3)
926 | ('ore', 1, 4)
927 | ('more', 0, 4)
928 |
929 | Set *reverse* to ``True`` to yield the same items in the opposite order.
930 |
931 |
932 | """
933 | r = range(1, len(seq) + 1)
934 | if reverse:
935 | r = reversed(r)
936 | return (
937 | (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1)
938 | )
939 |
940 |
941 | class bucket:
942 | """Wrap *iterable* and return an object that buckets the iterable into
943 | child iterables based on a *key* function.
944 |
945 | >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3']
946 | >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character
947 | >>> sorted(list(s)) # Get the keys
948 | ['a', 'b', 'c']
949 | >>> a_iterable = s['a']
950 | >>> next(a_iterable)
951 | 'a1'
952 | >>> next(a_iterable)
953 | 'a2'
954 | >>> list(s['b'])
955 | ['b1', 'b2', 'b3']
956 |
957 | The original iterable will be advanced and its items will be cached until
958 | they are used by the child iterables. This may require significant storage.
959 |
960 | By default, attempting to select a bucket to which no items belong will
961 | exhaust the iterable and cache all values.
962 | If you specify a *validator* function, selected buckets will instead be
963 | checked against it.
964 |
965 | >>> from itertools import count
966 | >>> it = count(1, 2) # Infinite sequence of odd numbers
967 | >>> key = lambda x: x % 10 # Bucket by last digit
968 | >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only
969 | >>> s = bucket(it, key=key, validator=validator)
970 | >>> 2 in s
971 | False
972 | >>> list(s[2])
973 | []
974 |
975 | """
976 |
977 | def __init__(self, iterable, key, validator=None):
978 | self._it = iter(iterable)
979 | self._key = key
980 | self._cache = defaultdict(deque)
981 | self._validator = validator or (lambda x: True)
982 |
983 | def __contains__(self, value):
984 | if not self._validator(value):
985 | return False
986 |
987 | try:
988 | item = next(self[value])
989 | except StopIteration:
990 | return False
991 | else:
992 | self._cache[value].appendleft(item)
993 |
994 | return True
995 |
996 | def _get_values(self, value):
997 | """
998 | Helper to yield items from the parent iterator that match *value*.
999 | Items that don't match are stored in the local cache as they
1000 | are encountered.
1001 | """
1002 | while True:
1003 | # If we've cached some items that match the target value, emit
1004 | # the first one and evict it from the cache.
1005 | if self._cache[value]:
1006 | yield self._cache[value].popleft()
1007 | # Otherwise we need to advance the parent iterator to search for
1008 | # a matching item, caching the rest.
1009 | else:
1010 | while True:
1011 | try:
1012 | item = next(self._it)
1013 | except StopIteration:
1014 | return
1015 | item_value = self._key(item)
1016 | if item_value == value:
1017 | yield item
1018 | break
1019 | elif self._validator(item_value):
1020 | self._cache[item_value].append(item)
1021 |
1022 | def __iter__(self):
1023 | for item in self._it:
1024 | item_value = self._key(item)
1025 | if self._validator(item_value):
1026 | self._cache[item_value].append(item)
1027 |
1028 | yield from self._cache.keys()
1029 |
1030 | def __getitem__(self, value):
1031 | if not self._validator(value):
1032 | return iter(())
1033 |
1034 | return self._get_values(value)
1035 |
1036 |
1037 | def spy(iterable, n=1):
1038 | """Return a 2-tuple with a list containing the first *n* elements of
1039 | *iterable*, and an iterator with the same items as *iterable*.
1040 | This allows you to "look ahead" at the items in the iterable without
1041 | advancing it.
1042 |
1043 | There is one item in the list by default:
1044 |
1045 | >>> iterable = 'abcdefg'
1046 | >>> head, iterable = spy(iterable)
1047 | >>> head
1048 | ['a']
1049 | >>> list(iterable)
1050 | ['a', 'b', 'c', 'd', 'e', 'f', 'g']
1051 |
1052 | You may use unpacking to retrieve items instead of lists:
1053 |
1054 | >>> (head,), iterable = spy('abcdefg')
1055 | >>> head
1056 | 'a'
1057 | >>> (first, second), iterable = spy('abcdefg', 2)
1058 | >>> first
1059 | 'a'
1060 | >>> second
1061 | 'b'
1062 |
1063 | The number of items requested can be larger than the number of items in
1064 | the iterable:
1065 |
1066 | >>> iterable = [1, 2, 3, 4, 5]
1067 | >>> head, iterable = spy(iterable, 10)
1068 | >>> head
1069 | [1, 2, 3, 4, 5]
1070 | >>> list(iterable)
1071 | [1, 2, 3, 4, 5]
1072 |
1073 | """
1074 | it = iter(iterable)
1075 | head = take(n, it)
1076 |
1077 | return head.copy(), chain(head, it)
1078 |
1079 |
1080 | def interleave(*iterables):
1081 | """Return a new iterable yielding from each iterable in turn,
1082 | until the shortest is exhausted.
1083 |
1084 | >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8]))
1085 | [1, 4, 6, 2, 5, 7]
1086 |
1087 | For a version that doesn't terminate after the shortest iterable is
1088 | exhausted, see :func:`interleave_longest`.
1089 |
1090 | """
1091 | return chain.from_iterable(zip(*iterables))
1092 |
1093 |
1094 | def interleave_longest(*iterables):
1095 | """Return a new iterable yielding from each iterable in turn,
1096 | skipping any that are exhausted.
1097 |
1098 | >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8]))
1099 | [1, 4, 6, 2, 5, 7, 3, 8]
1100 |
1101 | This function produces the same output as :func:`roundrobin`, but may
1102 | perform better for some inputs (in particular when the number of iterables
1103 | is large).
1104 |
1105 | """
1106 | i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker))
1107 | return (x for x in i if x is not _marker)
1108 |
1109 |
1110 | def interleave_evenly(iterables, lengths=None):
1111 | """
1112 | Interleave multiple iterables so that their elements are evenly distributed
1113 | throughout the output sequence.
1114 |
1115 | >>> iterables = [1, 2, 3, 4, 5], ['a', 'b']
1116 | >>> list(interleave_evenly(iterables))
1117 | [1, 2, 'a', 3, 4, 'b', 5]
1118 |
1119 | >>> iterables = [[1, 2, 3], [4, 5], [6, 7, 8]]
1120 | >>> list(interleave_evenly(iterables))
1121 | [1, 6, 4, 2, 7, 3, 8, 5]
1122 |
1123 | This function requires iterables of known length. Iterables without
1124 | ``__len__()`` can be used by manually specifying lengths with *lengths*:
1125 |
1126 | >>> from itertools import combinations, repeat
1127 | >>> iterables = [combinations(range(4), 2), ['a', 'b', 'c']]
1128 | >>> lengths = [4 * (4 - 1) // 2, 3]
1129 | >>> list(interleave_evenly(iterables, lengths=lengths))
1130 | [(0, 1), (0, 2), 'a', (0, 3), (1, 2), 'b', (1, 3), (2, 3), 'c']
1131 |
1132 | Based on Bresenham's algorithm.
1133 | """
1134 | if lengths is None:
1135 | try:
1136 | lengths = [len(it) for it in iterables]
1137 | except TypeError:
1138 | raise ValueError(
1139 | 'Iterable lengths could not be determined automatically. '
1140 | 'Specify them with the lengths keyword.'
1141 | )
1142 | elif len(iterables) != len(lengths):
1143 | raise ValueError('Mismatching number of iterables and lengths.')
1144 |
1145 | dims = len(lengths)
1146 |
1147 | # sort iterables by length, descending
1148 | lengths_permute = sorted(
1149 | range(dims), key=lambda i: lengths[i], reverse=True
1150 | )
1151 | lengths_desc = [lengths[i] for i in lengths_permute]
1152 | iters_desc = [iter(iterables[i]) for i in lengths_permute]
1153 |
1154 | # the longest iterable is the primary one (Bresenham: the longest
1155 | # distance along an axis)
1156 | delta_primary, deltas_secondary = lengths_desc[0], lengths_desc[1:]
1157 | iter_primary, iters_secondary = iters_desc[0], iters_desc[1:]
1158 | errors = [delta_primary // dims] * len(deltas_secondary)
1159 |
1160 | to_yield = sum(lengths)
1161 | while to_yield:
1162 | yield next(iter_primary)
1163 | to_yield -= 1
1164 | # update errors for each secondary iterable
1165 | errors = [e - delta for e, delta in zip(errors, deltas_secondary)]
1166 |
1167 | # those iterables for which the error is negative are yielded
1168 | # ("diagonal step" in Bresenham)
1169 | for i, e_ in enumerate(errors):
1170 | if e_ < 0:
1171 | yield next(iters_secondary[i])
1172 | to_yield -= 1
1173 | errors[i] += delta_primary
1174 |
1175 |
1176 | def collapse(iterable, base_type=None, levels=None):
1177 | """Flatten an iterable with multiple levels of nesting (e.g., a list of
1178 | lists of tuples) into non-iterable types.
1179 |
1180 | >>> iterable = [(1, 2), ([3, 4], [[5], [6]])]
1181 | >>> list(collapse(iterable))
1182 | [1, 2, 3, 4, 5, 6]
1183 |
1184 | Binary and text strings are not considered iterable and
1185 | will not be collapsed.
1186 |
1187 | To avoid collapsing other types, specify *base_type*:
1188 |
1189 | >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']]
1190 | >>> list(collapse(iterable, base_type=tuple))
1191 | ['ab', ('cd', 'ef'), 'gh', 'ij']
1192 |
1193 | Specify *levels* to stop flattening after a certain level:
1194 |
1195 | >>> iterable = [('a', ['b']), ('c', ['d'])]
1196 | >>> list(collapse(iterable)) # Fully flattened
1197 | ['a', 'b', 'c', 'd']
1198 | >>> list(collapse(iterable, levels=1)) # Only one level flattened
1199 | ['a', ['b'], 'c', ['d']]
1200 |
1201 | """
1202 | stack = deque()
1203 | # Add our first node group, treat the iterable as a single node
1204 | stack.appendleft((0, repeat(iterable, 1)))
1205 |
1206 | while stack:
1207 | node_group = stack.popleft()
1208 | level, nodes = node_group
1209 |
1210 | # Check if beyond max level
1211 | if levels is not None and level > levels:
1212 | yield from nodes
1213 | continue
1214 |
1215 | for node in nodes:
1216 | # Check if done iterating
1217 | if isinstance(node, (str, bytes)) or (
1218 | (base_type is not None) and isinstance(node, base_type)
1219 | ):
1220 | yield node
1221 | # Otherwise try to create child nodes
1222 | else:
1223 | try:
1224 | tree = iter(node)
1225 | except TypeError:
1226 | yield node
1227 | else:
1228 | # Save our current location
1229 | stack.appendleft(node_group)
1230 | # Append the new child node
1231 | stack.appendleft((level + 1, tree))
1232 | # Break to process child node
1233 | break
1234 |
1235 |
1236 | def side_effect(func, iterable, chunk_size=None, before=None, after=None):
1237 | """Invoke *func* on each item in *iterable* (or on each *chunk_size* group
1238 | of items) before yielding the item.
1239 |
1240 | `func` must be a function that takes a single argument. Its return value
1241 | will be discarded.
1242 |
1243 | *before* and *after* are optional functions that take no arguments. They
1244 | will be executed before iteration starts and after it ends, respectively.
1245 |
1246 | `side_effect` can be used for logging, updating progress bars, or anything
1247 | that is not functionally "pure."
1248 |
1249 | Emitting a status message:
1250 |
1251 | >>> from more_itertools import consume
1252 | >>> func = lambda item: print('Received {}'.format(item))
1253 | >>> consume(side_effect(func, range(2)))
1254 | Received 0
1255 | Received 1
1256 |
1257 | Operating on chunks of items:
1258 |
1259 | >>> pair_sums = []
1260 | >>> func = lambda chunk: pair_sums.append(sum(chunk))
1261 | >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2))
1262 | [0, 1, 2, 3, 4, 5]
1263 | >>> list(pair_sums)
1264 | [1, 5, 9]
1265 |
1266 | Writing to a file-like object:
1267 |
1268 | >>> from io import StringIO
1269 | >>> from more_itertools import consume
1270 | >>> f = StringIO()
1271 | >>> func = lambda x: print(x, file=f)
1272 | >>> before = lambda: print(u'HEADER', file=f)
1273 | >>> after = f.close
1274 | >>> it = [u'a', u'b', u'c']
1275 | >>> consume(side_effect(func, it, before=before, after=after))
1276 | >>> f.closed
1277 | True
1278 |
1279 | """
1280 | try:
1281 | if before is not None:
1282 | before()
1283 |
1284 | if chunk_size is None:
1285 | for item in iterable:
1286 | func(item)
1287 | yield item
1288 | else:
1289 | for chunk in chunked(iterable, chunk_size):
1290 | func(chunk)
1291 | yield from chunk
1292 | finally:
1293 | if after is not None:
1294 | after()
1295 |
1296 |
1297 | def sliced(seq, n, strict=False):
1298 | """Yield slices of length *n* from the sequence *seq*.
1299 |
1300 | >>> list(sliced((1, 2, 3, 4, 5, 6), 3))
1301 | [(1, 2, 3), (4, 5, 6)]
1302 |
1303 | By the default, the last yielded slice will have fewer than *n* elements
1304 | if the length of *seq* is not divisible by *n*:
1305 |
1306 | >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3))
1307 | [(1, 2, 3), (4, 5, 6), (7, 8)]
1308 |
1309 | If the length of *seq* is not divisible by *n* and *strict* is
1310 | ``True``, then ``ValueError`` will be raised before the last
1311 | slice is yielded.
1312 |
1313 | This function will only work for iterables that support slicing.
1314 | For non-sliceable iterables, see :func:`chunked`.
1315 |
1316 | """
1317 | iterator = takewhile(len, (seq[i : i + n] for i in count(0, n)))
1318 | if strict:
1319 |
1320 | def ret():
1321 | for _slice in iterator:
1322 | if len(_slice) != n:
1323 | raise ValueError("seq is not divisible by n.")
1324 | yield _slice
1325 |
1326 | return iter(ret())
1327 | else:
1328 | return iterator
1329 |
1330 |
1331 | def split_at(iterable, pred, maxsplit=-1, keep_separator=False):
1332 | """Yield lists of items from *iterable*, where each list is delimited by
1333 | an item where callable *pred* returns ``True``.
1334 |
1335 | >>> list(split_at('abcdcba', lambda x: x == 'b'))
1336 | [['a'], ['c', 'd', 'c'], ['a']]
1337 |
1338 | >>> list(split_at(range(10), lambda n: n % 2 == 1))
1339 | [[0], [2], [4], [6], [8], []]
1340 |
1341 | At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
1342 | then there is no limit on the number of splits:
1343 |
1344 | >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2))
1345 | [[0], [2], [4, 5, 6, 7, 8, 9]]
1346 |
1347 | By default, the delimiting items are not included in the output.
1348 | To include them, set *keep_separator* to ``True``.
1349 |
1350 | >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True))
1351 | [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']]
1352 |
1353 | """
1354 | if maxsplit == 0:
1355 | yield list(iterable)
1356 | return
1357 |
1358 | buf = []
1359 | it = iter(iterable)
1360 | for item in it:
1361 | if pred(item):
1362 | yield buf
1363 | if keep_separator:
1364 | yield [item]
1365 | if maxsplit == 1:
1366 | yield list(it)
1367 | return
1368 | buf = []
1369 | maxsplit -= 1
1370 | else:
1371 | buf.append(item)
1372 | yield buf
1373 |
1374 |
1375 | def split_before(iterable, pred, maxsplit=-1):
1376 | """Yield lists of items from *iterable*, where each list ends just before
1377 | an item for which callable *pred* returns ``True``:
1378 |
1379 | >>> list(split_before('OneTwo', lambda s: s.isupper()))
1380 | [['O', 'n', 'e'], ['T', 'w', 'o']]
1381 |
1382 | >>> list(split_before(range(10), lambda n: n % 3 == 0))
1383 | [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
1384 |
1385 | At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
1386 | then there is no limit on the number of splits:
1387 |
1388 | >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2))
1389 | [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
1390 | """
1391 | if maxsplit == 0:
1392 | yield list(iterable)
1393 | return
1394 |
1395 | buf = []
1396 | it = iter(iterable)
1397 | for item in it:
1398 | if pred(item) and buf:
1399 | yield buf
1400 | if maxsplit == 1:
1401 | yield [item] + list(it)
1402 | return
1403 | buf = []
1404 | maxsplit -= 1
1405 | buf.append(item)
1406 | if buf:
1407 | yield buf
1408 |
1409 |
1410 | def split_after(iterable, pred, maxsplit=-1):
1411 | """Yield lists of items from *iterable*, where each list ends with an
1412 | item where callable *pred* returns ``True``:
1413 |
1414 | >>> list(split_after('one1two2', lambda s: s.isdigit()))
1415 | [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']]
1416 |
1417 | >>> list(split_after(range(10), lambda n: n % 3 == 0))
1418 | [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
1419 |
1420 | At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
1421 | then there is no limit on the number of splits:
1422 |
1423 | >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2))
1424 | [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]]
1425 |
1426 | """
1427 | if maxsplit == 0:
1428 | yield list(iterable)
1429 | return
1430 |
1431 | buf = []
1432 | it = iter(iterable)
1433 | for item in it:
1434 | buf.append(item)
1435 | if pred(item) and buf:
1436 | yield buf
1437 | if maxsplit == 1:
1438 | buf = list(it)
1439 | if buf:
1440 | yield buf
1441 | return
1442 | buf = []
1443 | maxsplit -= 1
1444 | if buf:
1445 | yield buf
1446 |
1447 |
1448 | def split_when(iterable, pred, maxsplit=-1):
1449 | """Split *iterable* into pieces based on the output of *pred*.
1450 | *pred* should be a function that takes successive pairs of items and
1451 | returns ``True`` if the iterable should be split in between them.
1452 |
1453 | For example, to find runs of increasing numbers, split the iterable when
1454 | element ``i`` is larger than element ``i + 1``:
1455 |
1456 | >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y))
1457 | [[1, 2, 3, 3], [2, 5], [2, 4], [2]]
1458 |
1459 | At most *maxsplit* splits are done. If *maxsplit* is not specified or -1,
1460 | then there is no limit on the number of splits:
1461 |
1462 | >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2],
1463 | ... lambda x, y: x > y, maxsplit=2))
1464 | [[1, 2, 3, 3], [2, 5], [2, 4, 2]]
1465 |
1466 | """
1467 | if maxsplit == 0:
1468 | yield list(iterable)
1469 | return
1470 |
1471 | it = iter(iterable)
1472 | try:
1473 | cur_item = next(it)
1474 | except StopIteration:
1475 | return
1476 |
1477 | buf = [cur_item]
1478 | for next_item in it:
1479 | if pred(cur_item, next_item):
1480 | yield buf
1481 | if maxsplit == 1:
1482 | yield [next_item] + list(it)
1483 | return
1484 | buf = []
1485 | maxsplit -= 1
1486 |
1487 | buf.append(next_item)
1488 | cur_item = next_item
1489 |
1490 | yield buf
1491 |
1492 |
1493 | def split_into(iterable, sizes):
1494 | """Yield a list of sequential items from *iterable* of length 'n' for each
1495 | integer 'n' in *sizes*.
1496 |
1497 | >>> list(split_into([1,2,3,4,5,6], [1,2,3]))
1498 | [[1], [2, 3], [4, 5, 6]]
1499 |
1500 | If the sum of *sizes* is smaller than the length of *iterable*, then the
1501 | remaining items of *iterable* will not be returned.
1502 |
1503 | >>> list(split_into([1,2,3,4,5,6], [2,3]))
1504 | [[1, 2], [3, 4, 5]]
1505 |
1506 | If the sum of *sizes* is larger than the length of *iterable*, fewer items
1507 | will be returned in the iteration that overruns *iterable* and further
1508 | lists will be empty:
1509 |
1510 | >>> list(split_into([1,2,3,4], [1,2,3,4]))
1511 | [[1], [2, 3], [4], []]
1512 |
1513 | When a ``None`` object is encountered in *sizes*, the returned list will
1514 | contain items up to the end of *iterable* the same way that itertools.slice
1515 | does:
1516 |
1517 | >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None]))
1518 | [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]]
1519 |
1520 | :func:`split_into` can be useful for grouping a series of items where the
1521 | sizes of the groups are not uniform. An example would be where in a row
1522 | from a table, multiple columns represent elements of the same feature
1523 | (e.g. a point represented by x,y,z) but, the format is not the same for
1524 | all columns.
1525 | """
1526 | # convert the iterable argument into an iterator so its contents can
1527 | # be consumed by islice in case it is a generator
1528 | it = iter(iterable)
1529 |
1530 | for size in sizes:
1531 | if size is None:
1532 | yield list(it)
1533 | return
1534 | else:
1535 | yield list(islice(it, size))
1536 |
1537 |
1538 | def padded(iterable, fillvalue=None, n=None, next_multiple=False):
1539 | """Yield the elements from *iterable*, followed by *fillvalue*, such that
1540 | at least *n* items are emitted.
1541 |
1542 | >>> list(padded([1, 2, 3], '?', 5))
1543 | [1, 2, 3, '?', '?']
1544 |
1545 | If *next_multiple* is ``True``, *fillvalue* will be emitted until the
1546 | number of items emitted is a multiple of *n*:
1547 |
1548 | >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True))
1549 | [1, 2, 3, 4, None, None]
1550 |
1551 | If *n* is ``None``, *fillvalue* will be emitted indefinitely.
1552 |
1553 | To create an *iterable* of exactly size *n*, you can truncate with
1554 | :func:`islice`.
1555 |
1556 | >>> list(islice(padded([1, 2, 3], '?'), 5))
1557 | [1, 2, 3, '?', '?']
1558 | >>> list(islice(padded([1, 2, 3, 4, 5, 6, 7, 8], '?'), 5))
1559 | [1, 2, 3, 4, 5]
1560 |
1561 | """
1562 | iterable = iter(iterable)
1563 | iterable_with_repeat = chain(iterable, repeat(fillvalue))
1564 |
1565 | if n is None:
1566 | return iterable_with_repeat
1567 | elif n < 1:
1568 | raise ValueError('n must be at least 1')
1569 | elif next_multiple:
1570 |
1571 | def slice_generator():
1572 | for first in iterable:
1573 | yield (first,)
1574 | yield islice(iterable_with_repeat, n - 1)
1575 |
1576 | # While elements exist produce slices of size n
1577 | return chain.from_iterable(slice_generator())
1578 | else:
1579 | # Ensure the first batch is at least size n then iterate
1580 | return chain(islice(iterable_with_repeat, n), iterable)
1581 |
1582 |
1583 | def repeat_each(iterable, n=2):
1584 | """Repeat each element in *iterable* *n* times.
1585 |
1586 | >>> list(repeat_each('ABC', 3))
1587 | ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C']
1588 | """
1589 | return chain.from_iterable(map(repeat, iterable, repeat(n)))
1590 |
1591 |
1592 | def repeat_last(iterable, default=None):
1593 | """After the *iterable* is exhausted, keep yielding its last element.
1594 |
1595 | >>> list(islice(repeat_last(range(3)), 5))
1596 | [0, 1, 2, 2, 2]
1597 |
1598 | If the iterable is empty, yield *default* forever::
1599 |
1600 | >>> list(islice(repeat_last(range(0), 42), 5))
1601 | [42, 42, 42, 42, 42]
1602 |
1603 | """
1604 | item = _marker
1605 | for item in iterable:
1606 | yield item
1607 | final = default if item is _marker else item
1608 | yield from repeat(final)
1609 |
1610 |
1611 | def distribute(n, iterable):
1612 | """Distribute the items from *iterable* among *n* smaller iterables.
1613 |
1614 | >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6])
1615 | >>> list(group_1)
1616 | [1, 3, 5]
1617 | >>> list(group_2)
1618 | [2, 4, 6]
1619 |
1620 | If the length of *iterable* is not evenly divisible by *n*, then the
1621 | length of the returned iterables will not be identical:
1622 |
1623 | >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7])
1624 | >>> [list(c) for c in children]
1625 | [[1, 4, 7], [2, 5], [3, 6]]
1626 |
1627 | If the length of *iterable* is smaller than *n*, then the last returned
1628 | iterables will be empty:
1629 |
1630 | >>> children = distribute(5, [1, 2, 3])
1631 | >>> [list(c) for c in children]
1632 | [[1], [2], [3], [], []]
1633 |
1634 | This function uses :func:`itertools.tee` and may require significant
1635 | storage.
1636 |
1637 | If you need the order items in the smaller iterables to match the
1638 | original iterable, see :func:`divide`.
1639 |
1640 | """
1641 | if n < 1:
1642 | raise ValueError('n must be at least 1')
1643 |
1644 | children = tee(iterable, n)
1645 | return [islice(it, index, None, n) for index, it in enumerate(children)]
1646 |
1647 |
1648 | def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None):
1649 | """Yield tuples whose elements are offset from *iterable*.
1650 | The amount by which the `i`-th item in each tuple is offset is given by
1651 | the `i`-th item in *offsets*.
1652 |
1653 | >>> list(stagger([0, 1, 2, 3]))
1654 | [(None, 0, 1), (0, 1, 2), (1, 2, 3)]
1655 | >>> list(stagger(range(8), offsets=(0, 2, 4)))
1656 | [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)]
1657 |
1658 | By default, the sequence will end when the final element of a tuple is the
1659 | last item in the iterable. To continue until the first element of a tuple
1660 | is the last item in the iterable, set *longest* to ``True``::
1661 |
1662 | >>> list(stagger([0, 1, 2, 3], longest=True))
1663 | [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)]
1664 |
1665 | By default, ``None`` will be used to replace offsets beyond the end of the
1666 | sequence. Specify *fillvalue* to use some other value.
1667 |
1668 | """
1669 | children = tee(iterable, len(offsets))
1670 |
1671 | return zip_offset(
1672 | *children, offsets=offsets, longest=longest, fillvalue=fillvalue
1673 | )
1674 |
1675 |
1676 | def zip_equal(*iterables):
1677 | """``zip`` the input *iterables* together, but raise
1678 | ``UnequalIterablesError`` if they aren't all the same length.
1679 |
1680 | >>> it_1 = range(3)
1681 | >>> it_2 = iter('abc')
1682 | >>> list(zip_equal(it_1, it_2))
1683 | [(0, 'a'), (1, 'b'), (2, 'c')]
1684 |
1685 | >>> it_1 = range(3)
1686 | >>> it_2 = iter('abcd')
1687 | >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL
1688 | Traceback (most recent call last):
1689 | ...
1690 | more_itertools.more.UnequalIterablesError: Iterables have different
1691 | lengths
1692 |
1693 | """
1694 | if hexversion >= 0x30A00A6:
1695 | warnings.warn(
1696 | (
1697 | 'zip_equal will be removed in a future version of '
1698 | 'more-itertools. Use the builtin zip function with '
1699 | 'strict=True instead.'
1700 | ),
1701 | DeprecationWarning,
1702 | )
1703 |
1704 | return _zip_equal(*iterables)
1705 |
1706 |
1707 | def zip_offset(*iterables, offsets, longest=False, fillvalue=None):
1708 | """``zip`` the input *iterables* together, but offset the `i`-th iterable
1709 | by the `i`-th item in *offsets*.
1710 |
1711 | >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1)))
1712 | [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')]
1713 |
1714 | This can be used as a lightweight alternative to SciPy or pandas to analyze
1715 | data sets in which some series have a lead or lag relationship.
1716 |
1717 | By default, the sequence will end when the shortest iterable is exhausted.
1718 | To continue until the longest iterable is exhausted, set *longest* to
1719 | ``True``.
1720 |
1721 | >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True))
1722 | [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')]
1723 |
1724 | By default, ``None`` will be used to replace offsets beyond the end of the
1725 | sequence. Specify *fillvalue* to use some other value.
1726 |
1727 | """
1728 | if len(iterables) != len(offsets):
1729 | raise ValueError("Number of iterables and offsets didn't match")
1730 |
1731 | staggered = []
1732 | for it, n in zip(iterables, offsets):
1733 | if n < 0:
1734 | staggered.append(chain(repeat(fillvalue, -n), it))
1735 | elif n > 0:
1736 | staggered.append(islice(it, n, None))
1737 | else:
1738 | staggered.append(it)
1739 |
1740 | if longest:
1741 | return zip_longest(*staggered, fillvalue=fillvalue)
1742 |
1743 | return zip(*staggered)
1744 |
1745 |
1746 | def sort_together(iterables, key_list=(0,), key=None, reverse=False):
1747 | """Return the input iterables sorted together, with *key_list* as the
1748 | priority for sorting. All iterables are trimmed to the length of the
1749 | shortest one.
1750 |
1751 | This can be used like the sorting function in a spreadsheet. If each
1752 | iterable represents a column of data, the key list determines which
1753 | columns are used for sorting.
1754 |
1755 | By default, all iterables are sorted using the ``0``-th iterable::
1756 |
1757 | >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')]
1758 | >>> sort_together(iterables)
1759 | [(1, 2, 3, 4), ('d', 'c', 'b', 'a')]
1760 |
1761 | Set a different key list to sort according to another iterable.
1762 | Specifying multiple keys dictates how ties are broken::
1763 |
1764 | >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')]
1765 | >>> sort_together(iterables, key_list=(1, 2))
1766 | [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')]
1767 |
1768 | To sort by a function of the elements of the iterable, pass a *key*
1769 | function. Its arguments are the elements of the iterables corresponding to
1770 | the key list::
1771 |
1772 | >>> names = ('a', 'b', 'c')
1773 | >>> lengths = (1, 2, 3)
1774 | >>> widths = (5, 2, 1)
1775 | >>> def area(length, width):
1776 | ... return length * width
1777 | >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area)
1778 | [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)]
1779 |
1780 | Set *reverse* to ``True`` to sort in descending order.
1781 |
1782 | >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True)
1783 | [(3, 2, 1), ('a', 'b', 'c')]
1784 |
1785 | """
1786 | if key is None:
1787 | # if there is no key function, the key argument to sorted is an
1788 | # itemgetter
1789 | key_argument = itemgetter(*key_list)
1790 | else:
1791 | # if there is a key function, call it with the items at the offsets
1792 | # specified by the key function as arguments
1793 | key_list = list(key_list)
1794 | if len(key_list) == 1:
1795 | # if key_list contains a single item, pass the item at that offset
1796 | # as the only argument to the key function
1797 | key_offset = key_list[0]
1798 | key_argument = lambda zipped_items: key(zipped_items[key_offset])
1799 | else:
1800 | # if key_list contains multiple items, use itemgetter to return a
1801 | # tuple of items, which we pass as *args to the key function
1802 | get_key_items = itemgetter(*key_list)
1803 | key_argument = lambda zipped_items: key(
1804 | *get_key_items(zipped_items)
1805 | )
1806 |
1807 | return list(
1808 | zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse))
1809 | )
1810 |
1811 |
1812 | def unzip(iterable):
1813 | """The inverse of :func:`zip`, this function disaggregates the elements
1814 | of the zipped *iterable*.
1815 |
1816 | The ``i``-th iterable contains the ``i``-th element from each element
1817 | of the zipped iterable. The first element is used to determine the
1818 | length of the remaining elements.
1819 |
1820 | >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
1821 | >>> letters, numbers = unzip(iterable)
1822 | >>> list(letters)
1823 | ['a', 'b', 'c', 'd']
1824 | >>> list(numbers)
1825 | [1, 2, 3, 4]
1826 |
1827 | This is similar to using ``zip(*iterable)``, but it avoids reading
1828 | *iterable* into memory. Note, however, that this function uses
1829 | :func:`itertools.tee` and thus may require significant storage.
1830 |
1831 | """
1832 | head, iterable = spy(iter(iterable))
1833 | if not head:
1834 | # empty iterable, e.g. zip([], [], [])
1835 | return ()
1836 | # spy returns a one-length iterable as head
1837 | head = head[0]
1838 | iterables = tee(iterable, len(head))
1839 |
1840 | def itemgetter(i):
1841 | def getter(obj):
1842 | try:
1843 | return obj[i]
1844 | except IndexError:
1845 | # basically if we have an iterable like
1846 | # iter([(1, 2, 3), (4, 5), (6,)])
1847 | # the second unzipped iterable would fail at the third tuple
1848 | # since it would try to access tup[1]
1849 | # same with the third unzipped iterable and the second tuple
1850 | # to support these "improperly zipped" iterables,
1851 | # we create a custom itemgetter
1852 | # which just stops the unzipped iterables
1853 | # at first length mismatch
1854 | raise StopIteration
1855 |
1856 | return getter
1857 |
1858 | return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables))
1859 |
1860 |
1861 | def divide(n, iterable):
1862 | """Divide the elements from *iterable* into *n* parts, maintaining
1863 | order.
1864 |
1865 | >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6])
1866 | >>> list(group_1)
1867 | [1, 2, 3]
1868 | >>> list(group_2)
1869 | [4, 5, 6]
1870 |
1871 | If the length of *iterable* is not evenly divisible by *n*, then the
1872 | length of the returned iterables will not be identical:
1873 |
1874 | >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7])
1875 | >>> [list(c) for c in children]
1876 | [[1, 2, 3], [4, 5], [6, 7]]
1877 |
1878 | If the length of the iterable is smaller than n, then the last returned
1879 | iterables will be empty:
1880 |
1881 | >>> children = divide(5, [1, 2, 3])
1882 | >>> [list(c) for c in children]
1883 | [[1], [2], [3], [], []]
1884 |
1885 | This function will exhaust the iterable before returning.
1886 | If order is not important, see :func:`distribute`, which does not first
1887 | pull the iterable into memory.
1888 |
1889 | """
1890 | if n < 1:
1891 | raise ValueError('n must be at least 1')
1892 |
1893 | try:
1894 | iterable[:0]
1895 | except TypeError:
1896 | seq = tuple(iterable)
1897 | else:
1898 | seq = iterable
1899 |
1900 | q, r = divmod(len(seq), n)
1901 |
1902 | ret = []
1903 | stop = 0
1904 | for i in range(1, n + 1):
1905 | start = stop
1906 | stop += q + 1 if i <= r else q
1907 | ret.append(iter(seq[start:stop]))
1908 |
1909 | return ret
1910 |
1911 |
1912 | def always_iterable(obj, base_type=(str, bytes)):
1913 | """If *obj* is iterable, return an iterator over its items::
1914 |
1915 | >>> obj = (1, 2, 3)
1916 | >>> list(always_iterable(obj))
1917 | [1, 2, 3]
1918 |
1919 | If *obj* is not iterable, return a one-item iterable containing *obj*::
1920 |
1921 | >>> obj = 1
1922 | >>> list(always_iterable(obj))
1923 | [1]
1924 |
1925 | If *obj* is ``None``, return an empty iterable:
1926 |
1927 | >>> obj = None
1928 | >>> list(always_iterable(None))
1929 | []
1930 |
1931 | By default, binary and text strings are not considered iterable::
1932 |
1933 | >>> obj = 'foo'
1934 | >>> list(always_iterable(obj))
1935 | ['foo']
1936 |
1937 | If *base_type* is set, objects for which ``isinstance(obj, base_type)``
1938 | returns ``True`` won't be considered iterable.
1939 |
1940 | >>> obj = {'a': 1}
1941 | >>> list(always_iterable(obj)) # Iterate over the dict's keys
1942 | ['a']
1943 | >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
1944 | [{'a': 1}]
1945 |
1946 | Set *base_type* to ``None`` to avoid any special handling and treat objects
1947 | Python considers iterable as iterable:
1948 |
1949 | >>> obj = 'foo'
1950 | >>> list(always_iterable(obj, base_type=None))
1951 | ['f', 'o', 'o']
1952 | """
1953 | if obj is None:
1954 | return iter(())
1955 |
1956 | if (base_type is not None) and isinstance(obj, base_type):
1957 | return iter((obj,))
1958 |
1959 | try:
1960 | return iter(obj)
1961 | except TypeError:
1962 | return iter((obj,))
1963 |
1964 |
1965 | def adjacent(predicate, iterable, distance=1):
1966 | """Return an iterable over `(bool, item)` tuples where the `item` is
1967 | drawn from *iterable* and the `bool` indicates whether
1968 | that item satisfies the *predicate* or is adjacent to an item that does.
1969 |
1970 | For example, to find whether items are adjacent to a ``3``::
1971 |
1972 | >>> list(adjacent(lambda x: x == 3, range(6)))
1973 | [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)]
1974 |
1975 | Set *distance* to change what counts as adjacent. For example, to find
1976 | whether items are two places away from a ``3``:
1977 |
1978 | >>> list(adjacent(lambda x: x == 3, range(6), distance=2))
1979 | [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)]
1980 |
1981 | This is useful for contextualizing the results of a search function.
1982 | For example, a code comparison tool might want to identify lines that
1983 | have changed, but also surrounding lines to give the viewer of the diff
1984 | context.
1985 |
1986 | The predicate function will only be called once for each item in the
1987 | iterable.
1988 |
1989 | See also :func:`groupby_transform`, which can be used with this function
1990 | to group ranges of items with the same `bool` value.
1991 |
1992 | """
1993 | # Allow distance=0 mainly for testing that it reproduces results with map()
1994 | if distance < 0:
1995 | raise ValueError('distance must be at least 0')
1996 |
1997 | i1, i2 = tee(iterable)
1998 | padding = [False] * distance
1999 | selected = chain(padding, map(predicate, i1), padding)
2000 | adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1))
2001 | return zip(adjacent_to_selected, i2)
2002 |
2003 |
2004 | def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None):
2005 | """An extension of :func:`itertools.groupby` that can apply transformations
2006 | to the grouped data.
2007 |
2008 | * *keyfunc* is a function computing a key value for each item in *iterable*
2009 | * *valuefunc* is a function that transforms the individual items from
2010 | *iterable* after grouping
2011 | * *reducefunc* is a function that transforms each group of items
2012 |
2013 | >>> iterable = 'aAAbBBcCC'
2014 | >>> keyfunc = lambda k: k.upper()
2015 | >>> valuefunc = lambda v: v.lower()
2016 | >>> reducefunc = lambda g: ''.join(g)
2017 | >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc))
2018 | [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')]
2019 |
2020 | Each optional argument defaults to an identity function if not specified.
2021 |
2022 | :func:`groupby_transform` is useful when grouping elements of an iterable
2023 | using a separate iterable as the key. To do this, :func:`zip` the iterables
2024 | and pass a *keyfunc* that extracts the first element and a *valuefunc*
2025 | that extracts the second element::
2026 |
2027 | >>> from operator import itemgetter
2028 | >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3]
2029 | >>> values = 'abcdefghi'
2030 | >>> iterable = zip(keys, values)
2031 | >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1))
2032 | >>> [(k, ''.join(g)) for k, g in grouper]
2033 | [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')]
2034 |
2035 | Note that the order of items in the iterable is significant.
2036 | Only adjacent items are grouped together, so if you don't want any
2037 | duplicate groups, you should sort the iterable by the key function.
2038 |
2039 | """
2040 | ret = groupby(iterable, keyfunc)
2041 | if valuefunc:
2042 | ret = ((k, map(valuefunc, g)) for k, g in ret)
2043 | if reducefunc:
2044 | ret = ((k, reducefunc(g)) for k, g in ret)
2045 |
2046 | return ret
2047 |
2048 |
2049 | class numeric_range(abc.Sequence, abc.Hashable):
2050 | """An extension of the built-in ``range()`` function whose arguments can
2051 | be any orderable numeric type.
2052 |
2053 | With only *stop* specified, *start* defaults to ``0`` and *step*
2054 | defaults to ``1``. The output items will match the type of *stop*:
2055 |
2056 | >>> list(numeric_range(3.5))
2057 | [0.0, 1.0, 2.0, 3.0]
2058 |
2059 | With only *start* and *stop* specified, *step* defaults to ``1``. The
2060 | output items will match the type of *start*:
2061 |
2062 | >>> from decimal import Decimal
2063 | >>> start = Decimal('2.1')
2064 | >>> stop = Decimal('5.1')
2065 | >>> list(numeric_range(start, stop))
2066 | [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')]
2067 |
2068 | With *start*, *stop*, and *step* specified the output items will match
2069 | the type of ``start + step``:
2070 |
2071 | >>> from fractions import Fraction
2072 | >>> start = Fraction(1, 2) # Start at 1/2
2073 | >>> stop = Fraction(5, 2) # End at 5/2
2074 | >>> step = Fraction(1, 2) # Count by 1/2
2075 | >>> list(numeric_range(start, stop, step))
2076 | [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)]
2077 |
2078 | If *step* is zero, ``ValueError`` is raised. Negative steps are supported:
2079 |
2080 | >>> list(numeric_range(3, -1, -1.0))
2081 | [3.0, 2.0, 1.0, 0.0]
2082 |
2083 | Be aware of the limitations of floating point numbers; the representation
2084 | of the yielded numbers may be surprising.
2085 |
2086 | ``datetime.datetime`` objects can be used for *start* and *stop*, if *step*
2087 | is a ``datetime.timedelta`` object:
2088 |
2089 | >>> import datetime
2090 | >>> start = datetime.datetime(2019, 1, 1)
2091 | >>> stop = datetime.datetime(2019, 1, 3)
2092 | >>> step = datetime.timedelta(days=1)
2093 | >>> items = iter(numeric_range(start, stop, step))
2094 | >>> next(items)
2095 | datetime.datetime(2019, 1, 1, 0, 0)
2096 | >>> next(items)
2097 | datetime.datetime(2019, 1, 2, 0, 0)
2098 |
2099 | """
2100 |
2101 | _EMPTY_HASH = hash(range(0, 0))
2102 |
2103 | def __init__(self, *args):
2104 | argc = len(args)
2105 | if argc == 1:
2106 | (self._stop,) = args
2107 | self._start = type(self._stop)(0)
2108 | self._step = type(self._stop - self._start)(1)
2109 | elif argc == 2:
2110 | self._start, self._stop = args
2111 | self._step = type(self._stop - self._start)(1)
2112 | elif argc == 3:
2113 | self._start, self._stop, self._step = args
2114 | elif argc == 0:
2115 | raise TypeError(
2116 | 'numeric_range expected at least '
2117 | '1 argument, got {}'.format(argc)
2118 | )
2119 | else:
2120 | raise TypeError(
2121 | 'numeric_range expected at most '
2122 | '3 arguments, got {}'.format(argc)
2123 | )
2124 |
2125 | self._zero = type(self._step)(0)
2126 | if self._step == self._zero:
2127 | raise ValueError('numeric_range() arg 3 must not be zero')
2128 | self._growing = self._step > self._zero
2129 |
2130 | def __bool__(self):
2131 | if self._growing:
2132 | return self._start < self._stop
2133 | else:
2134 | return self._start > self._stop
2135 |
2136 | def __contains__(self, elem):
2137 | if self._growing:
2138 | if self._start <= elem < self._stop:
2139 | return (elem - self._start) % self._step == self._zero
2140 | else:
2141 | if self._start >= elem > self._stop:
2142 | return (self._start - elem) % (-self._step) == self._zero
2143 |
2144 | return False
2145 |
2146 | def __eq__(self, other):
2147 | if isinstance(other, numeric_range):
2148 | empty_self = not bool(self)
2149 | empty_other = not bool(other)
2150 | if empty_self or empty_other:
2151 | return empty_self and empty_other # True if both empty
2152 | else:
2153 | return (
2154 | self._start == other._start
2155 | and self._step == other._step
2156 | and self._get_by_index(-1) == other._get_by_index(-1)
2157 | )
2158 | else:
2159 | return False
2160 |
2161 | def __getitem__(self, key):
2162 | if isinstance(key, int):
2163 | return self._get_by_index(key)
2164 | elif isinstance(key, slice):
2165 | step = self._step if key.step is None else key.step * self._step
2166 |
2167 | if key.start is None or key.start <= -self._len:
2168 | start = self._start
2169 | elif key.start >= self._len:
2170 | start = self._stop
2171 | else: # -self._len < key.start < self._len
2172 | start = self._get_by_index(key.start)
2173 |
2174 | if key.stop is None or key.stop >= self._len:
2175 | stop = self._stop
2176 | elif key.stop <= -self._len:
2177 | stop = self._start
2178 | else: # -self._len < key.stop < self._len
2179 | stop = self._get_by_index(key.stop)
2180 |
2181 | return numeric_range(start, stop, step)
2182 | else:
2183 | raise TypeError(
2184 | 'numeric range indices must be '
2185 | 'integers or slices, not {}'.format(type(key).__name__)
2186 | )
2187 |
2188 | def __hash__(self):
2189 | if self:
2190 | return hash((self._start, self._get_by_index(-1), self._step))
2191 | else:
2192 | return self._EMPTY_HASH
2193 |
2194 | def __iter__(self):
2195 | values = (self._start + (n * self._step) for n in count())
2196 | if self._growing:
2197 | return takewhile(partial(gt, self._stop), values)
2198 | else:
2199 | return takewhile(partial(lt, self._stop), values)
2200 |
2201 | def __len__(self):
2202 | return self._len
2203 |
2204 | @cached_property
2205 | def _len(self):
2206 | if self._growing:
2207 | start = self._start
2208 | stop = self._stop
2209 | step = self._step
2210 | else:
2211 | start = self._stop
2212 | stop = self._start
2213 | step = -self._step
2214 | distance = stop - start
2215 | if distance <= self._zero:
2216 | return 0
2217 | else: # distance > 0 and step > 0: regular euclidean division
2218 | q, r = divmod(distance, step)
2219 | return int(q) + int(r != self._zero)
2220 |
2221 | def __reduce__(self):
2222 | return numeric_range, (self._start, self._stop, self._step)
2223 |
2224 | def __repr__(self):
2225 | if self._step == 1:
2226 | return "numeric_range({}, {})".format(
2227 | repr(self._start), repr(self._stop)
2228 | )
2229 | else:
2230 | return "numeric_range({}, {}, {})".format(
2231 | repr(self._start), repr(self._stop), repr(self._step)
2232 | )
2233 |
2234 | def __reversed__(self):
2235 | return iter(
2236 | numeric_range(
2237 | self._get_by_index(-1), self._start - self._step, -self._step
2238 | )
2239 | )
2240 |
2241 | def count(self, value):
2242 | return int(value in self)
2243 |
2244 | def index(self, value):
2245 | if self._growing:
2246 | if self._start <= value < self._stop:
2247 | q, r = divmod(value - self._start, self._step)
2248 | if r == self._zero:
2249 | return int(q)
2250 | else:
2251 | if self._start >= value > self._stop:
2252 | q, r = divmod(self._start - value, -self._step)
2253 | if r == self._zero:
2254 | return int(q)
2255 |
2256 | raise ValueError("{} is not in numeric range".format(value))
2257 |
2258 | def _get_by_index(self, i):
2259 | if i < 0:
2260 | i += self._len
2261 | if i < 0 or i >= self._len:
2262 | raise IndexError("numeric range object index out of range")
2263 | return self._start + i * self._step
2264 |
2265 |
2266 | def count_cycle(iterable, n=None):
2267 | """Cycle through the items from *iterable* up to *n* times, yielding
2268 | the number of completed cycles along with each item. If *n* is omitted the
2269 | process repeats indefinitely.
2270 |
2271 | >>> list(count_cycle('AB', 3))
2272 | [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')]
2273 |
2274 | """
2275 | iterable = tuple(iterable)
2276 | if not iterable:
2277 | return iter(())
2278 | counter = count() if n is None else range(n)
2279 | return ((i, item) for i in counter for item in iterable)
2280 |
2281 |
2282 | def mark_ends(iterable):
2283 | """Yield 3-tuples of the form ``(is_first, is_last, item)``.
2284 |
2285 | >>> list(mark_ends('ABC'))
2286 | [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')]
2287 |
2288 | Use this when looping over an iterable to take special action on its first
2289 | and/or last items:
2290 |
2291 | >>> iterable = ['Header', 100, 200, 'Footer']
2292 | >>> total = 0
2293 | >>> for is_first, is_last, item in mark_ends(iterable):
2294 | ... if is_first:
2295 | ... continue # Skip the header
2296 | ... if is_last:
2297 | ... continue # Skip the footer
2298 | ... total += item
2299 | >>> print(total)
2300 | 300
2301 | """
2302 | it = iter(iterable)
2303 |
2304 | try:
2305 | b = next(it)
2306 | except StopIteration:
2307 | return
2308 |
2309 | try:
2310 | for i in count():
2311 | a = b
2312 | b = next(it)
2313 | yield i == 0, False, a
2314 |
2315 | except StopIteration:
2316 | yield i == 0, True, a
2317 |
2318 |
2319 | def locate(iterable, pred=bool, window_size=None):
2320 | """Yield the index of each item in *iterable* for which *pred* returns
2321 | ``True``.
2322 |
2323 | *pred* defaults to :func:`bool`, which will select truthy items:
2324 |
2325 | >>> list(locate([0, 1, 1, 0, 1, 0, 0]))
2326 | [1, 2, 4]
2327 |
2328 | Set *pred* to a custom function to, e.g., find the indexes for a particular
2329 | item.
2330 |
2331 | >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b'))
2332 | [1, 3]
2333 |
2334 | If *window_size* is given, then the *pred* function will be called with
2335 | that many items. This enables searching for sub-sequences:
2336 |
2337 | >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
2338 | >>> pred = lambda *args: args == (1, 2, 3)
2339 | >>> list(locate(iterable, pred=pred, window_size=3))
2340 | [1, 5, 9]
2341 |
2342 | Use with :func:`seekable` to find indexes and then retrieve the associated
2343 | items:
2344 |
2345 | >>> from itertools import count
2346 | >>> from more_itertools import seekable
2347 | >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count())
2348 | >>> it = seekable(source)
2349 | >>> pred = lambda x: x > 100
2350 | >>> indexes = locate(it, pred=pred)
2351 | >>> i = next(indexes)
2352 | >>> it.seek(i)
2353 | >>> next(it)
2354 | 106
2355 |
2356 | """
2357 | if window_size is None:
2358 | return compress(count(), map(pred, iterable))
2359 |
2360 | if window_size < 1:
2361 | raise ValueError('window size must be at least 1')
2362 |
2363 | it = windowed(iterable, window_size, fillvalue=_marker)
2364 | return compress(count(), starmap(pred, it))
2365 |
2366 |
2367 | def longest_common_prefix(iterables):
2368 | """Yield elements of the longest common prefix amongst given *iterables*.
2369 |
2370 | >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf']))
2371 | 'ab'
2372 |
2373 | """
2374 | return (c[0] for c in takewhile(all_equal, zip(*iterables)))
2375 |
2376 |
2377 | def lstrip(iterable, pred):
2378 | """Yield the items from *iterable*, but strip any from the beginning
2379 | for which *pred* returns ``True``.
2380 |
2381 | For example, to remove a set of items from the start of an iterable:
2382 |
2383 | >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
2384 | >>> pred = lambda x: x in {None, False, ''}
2385 | >>> list(lstrip(iterable, pred))
2386 | [1, 2, None, 3, False, None]
2387 |
2388 | This function is analogous to to :func:`str.lstrip`, and is essentially
2389 | an wrapper for :func:`itertools.dropwhile`.
2390 |
2391 | """
2392 | return dropwhile(pred, iterable)
2393 |
2394 |
2395 | def rstrip(iterable, pred):
2396 | """Yield the items from *iterable*, but strip any from the end
2397 | for which *pred* returns ``True``.
2398 |
2399 | For example, to remove a set of items from the end of an iterable:
2400 |
2401 | >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
2402 | >>> pred = lambda x: x in {None, False, ''}
2403 | >>> list(rstrip(iterable, pred))
2404 | [None, False, None, 1, 2, None, 3]
2405 |
2406 | This function is analogous to :func:`str.rstrip`.
2407 |
2408 | """
2409 | cache = []
2410 | cache_append = cache.append
2411 | cache_clear = cache.clear
2412 | for x in iterable:
2413 | if pred(x):
2414 | cache_append(x)
2415 | else:
2416 | yield from cache
2417 | cache_clear()
2418 | yield x
2419 |
2420 |
2421 | def strip(iterable, pred):
2422 | """Yield the items from *iterable*, but strip any from the
2423 | beginning and end for which *pred* returns ``True``.
2424 |
2425 | For example, to remove a set of items from both ends of an iterable:
2426 |
2427 | >>> iterable = (None, False, None, 1, 2, None, 3, False, None)
2428 | >>> pred = lambda x: x in {None, False, ''}
2429 | >>> list(strip(iterable, pred))
2430 | [1, 2, None, 3]
2431 |
2432 | This function is analogous to :func:`str.strip`.
2433 |
2434 | """
2435 | return rstrip(lstrip(iterable, pred), pred)
2436 |
2437 |
2438 | class islice_extended:
2439 | """An extension of :func:`itertools.islice` that supports negative values
2440 | for *stop*, *start*, and *step*.
2441 |
2442 | >>> iterable = iter('abcdefgh')
2443 | >>> list(islice_extended(iterable, -4, -1))
2444 | ['e', 'f', 'g']
2445 |
2446 | Slices with negative values require some caching of *iterable*, but this
2447 | function takes care to minimize the amount of memory required.
2448 |
2449 | For example, you can use a negative step with an infinite iterator:
2450 |
2451 | >>> from itertools import count
2452 | >>> list(islice_extended(count(), 110, 99, -2))
2453 | [110, 108, 106, 104, 102, 100]
2454 |
2455 | You can also use slice notation directly:
2456 |
2457 | >>> iterable = map(str, count())
2458 | >>> it = islice_extended(iterable)[10:20:2]
2459 | >>> list(it)
2460 | ['10', '12', '14', '16', '18']
2461 |
2462 | """
2463 |
2464 | def __init__(self, iterable, *args):
2465 | it = iter(iterable)
2466 | if args:
2467 | self._iterable = _islice_helper(it, slice(*args))
2468 | else:
2469 | self._iterable = it
2470 |
2471 | def __iter__(self):
2472 | return self
2473 |
2474 | def __next__(self):
2475 | return next(self._iterable)
2476 |
2477 | def __getitem__(self, key):
2478 | if isinstance(key, slice):
2479 | return islice_extended(_islice_helper(self._iterable, key))
2480 |
2481 | raise TypeError('islice_extended.__getitem__ argument must be a slice')
2482 |
2483 |
2484 | def _islice_helper(it, s):
2485 | start = s.start
2486 | stop = s.stop
2487 | if s.step == 0:
2488 | raise ValueError('step argument must be a non-zero integer or None.')
2489 | step = s.step or 1
2490 |
2491 | if step > 0:
2492 | start = 0 if (start is None) else start
2493 |
2494 | if start < 0:
2495 | # Consume all but the last -start items
2496 | cache = deque(enumerate(it, 1), maxlen=-start)
2497 | len_iter = cache[-1][0] if cache else 0
2498 |
2499 | # Adjust start to be positive
2500 | i = max(len_iter + start, 0)
2501 |
2502 | # Adjust stop to be positive
2503 | if stop is None:
2504 | j = len_iter
2505 | elif stop >= 0:
2506 | j = min(stop, len_iter)
2507 | else:
2508 | j = max(len_iter + stop, 0)
2509 |
2510 | # Slice the cache
2511 | n = j - i
2512 | if n <= 0:
2513 | return
2514 |
2515 | for index, item in islice(cache, 0, n, step):
2516 | yield item
2517 | elif (stop is not None) and (stop < 0):
2518 | # Advance to the start position
2519 | next(islice(it, start, start), None)
2520 |
2521 | # When stop is negative, we have to carry -stop items while
2522 | # iterating
2523 | cache = deque(islice(it, -stop), maxlen=-stop)
2524 |
2525 | for index, item in enumerate(it):
2526 | cached_item = cache.popleft()
2527 | if index % step == 0:
2528 | yield cached_item
2529 | cache.append(item)
2530 | else:
2531 | # When both start and stop are positive we have the normal case
2532 | yield from islice(it, start, stop, step)
2533 | else:
2534 | start = -1 if (start is None) else start
2535 |
2536 | if (stop is not None) and (stop < 0):
2537 | # Consume all but the last items
2538 | n = -stop - 1
2539 | cache = deque(enumerate(it, 1), maxlen=n)
2540 | len_iter = cache[-1][0] if cache else 0
2541 |
2542 | # If start and stop are both negative they are comparable and
2543 | # we can just slice. Otherwise we can adjust start to be negative
2544 | # and then slice.
2545 | if start < 0:
2546 | i, j = start, stop
2547 | else:
2548 | i, j = min(start - len_iter, -1), None
2549 |
2550 | for index, item in list(cache)[i:j:step]:
2551 | yield item
2552 | else:
2553 | # Advance to the stop position
2554 | if stop is not None:
2555 | m = stop + 1
2556 | next(islice(it, m, m), None)
2557 |
2558 | # stop is positive, so if start is negative they are not comparable
2559 | # and we need the rest of the items.
2560 | if start < 0:
2561 | i = start
2562 | n = None
2563 | # stop is None and start is positive, so we just need items up to
2564 | # the start index.
2565 | elif stop is None:
2566 | i = None
2567 | n = start + 1
2568 | # Both stop and start are positive, so they are comparable.
2569 | else:
2570 | i = None
2571 | n = start - stop
2572 | if n <= 0:
2573 | return
2574 |
2575 | cache = list(islice(it, n))
2576 |
2577 | yield from cache[i::step]
2578 |
2579 |
2580 | def always_reversible(iterable):
2581 | """An extension of :func:`reversed` that supports all iterables, not
2582 | just those which implement the ``Reversible`` or ``Sequence`` protocols.
2583 |
2584 | >>> print(*always_reversible(x for x in range(3)))
2585 | 2 1 0
2586 |
2587 | If the iterable is already reversible, this function returns the
2588 | result of :func:`reversed()`. If the iterable is not reversible,
2589 | this function will cache the remaining items in the iterable and
2590 | yield them in reverse order, which may require significant storage.
2591 | """
2592 | try:
2593 | return reversed(iterable)
2594 | except TypeError:
2595 | return reversed(list(iterable))
2596 |
2597 |
2598 | def consecutive_groups(iterable, ordering=lambda x: x):
2599 | """Yield groups of consecutive items using :func:`itertools.groupby`.
2600 | The *ordering* function determines whether two items are adjacent by
2601 | returning their position.
2602 |
2603 | By default, the ordering function is the identity function. This is
2604 | suitable for finding runs of numbers:
2605 |
2606 | >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40]
2607 | >>> for group in consecutive_groups(iterable):
2608 | ... print(list(group))
2609 | [1]
2610 | [10, 11, 12]
2611 | [20]
2612 | [30, 31, 32, 33]
2613 | [40]
2614 |
2615 | For finding runs of adjacent letters, try using the :meth:`index` method
2616 | of a string of letters:
2617 |
2618 | >>> from string import ascii_lowercase
2619 | >>> iterable = 'abcdfgilmnop'
2620 | >>> ordering = ascii_lowercase.index
2621 | >>> for group in consecutive_groups(iterable, ordering):
2622 | ... print(list(group))
2623 | ['a', 'b', 'c', 'd']
2624 | ['f', 'g']
2625 | ['i']
2626 | ['l', 'm', 'n', 'o', 'p']
2627 |
2628 | Each group of consecutive items is an iterator that shares it source with
2629 | *iterable*. When an an output group is advanced, the previous group is
2630 | no longer available unless its elements are copied (e.g., into a ``list``).
2631 |
2632 | >>> iterable = [1, 2, 11, 12, 21, 22]
2633 | >>> saved_groups = []
2634 | >>> for group in consecutive_groups(iterable):
2635 | ... saved_groups.append(list(group)) # Copy group elements
2636 | >>> saved_groups
2637 | [[1, 2], [11, 12], [21, 22]]
2638 |
2639 | """
2640 | for k, g in groupby(
2641 | enumerate(iterable), key=lambda x: x[0] - ordering(x[1])
2642 | ):
2643 | yield map(itemgetter(1), g)
2644 |
2645 |
2646 | def difference(iterable, func=sub, *, initial=None):
2647 | """This function is the inverse of :func:`itertools.accumulate`. By default
2648 | it will compute the first difference of *iterable* using
2649 | :func:`operator.sub`:
2650 |
2651 | >>> from itertools import accumulate
2652 | >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10
2653 | >>> list(difference(iterable))
2654 | [0, 1, 2, 3, 4]
2655 |
2656 | *func* defaults to :func:`operator.sub`, but other functions can be
2657 | specified. They will be applied as follows::
2658 |
2659 | A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ...
2660 |
2661 | For example, to do progressive division:
2662 |
2663 | >>> iterable = [1, 2, 6, 24, 120]
2664 | >>> func = lambda x, y: x // y
2665 | >>> list(difference(iterable, func))
2666 | [1, 2, 3, 4, 5]
2667 |
2668 | If the *initial* keyword is set, the first element will be skipped when
2669 | computing successive differences.
2670 |
2671 | >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10)
2672 | >>> list(difference(it, initial=10))
2673 | [1, 2, 3]
2674 |
2675 | """
2676 | a, b = tee(iterable)
2677 | try:
2678 | first = [next(b)]
2679 | except StopIteration:
2680 | return iter([])
2681 |
2682 | if initial is not None:
2683 | first = []
2684 |
2685 | return chain(first, map(func, b, a))
2686 |
2687 |
2688 | class SequenceView(Sequence):
2689 | """Return a read-only view of the sequence object *target*.
2690 |
2691 | :class:`SequenceView` objects are analogous to Python's built-in
2692 | "dictionary view" types. They provide a dynamic view of a sequence's items,
2693 | meaning that when the sequence updates, so does the view.
2694 |
2695 | >>> seq = ['0', '1', '2']
2696 | >>> view = SequenceView(seq)
2697 | >>> view
2698 | SequenceView(['0', '1', '2'])
2699 | >>> seq.append('3')
2700 | >>> view
2701 | SequenceView(['0', '1', '2', '3'])
2702 |
2703 | Sequence views support indexing, slicing, and length queries. They act
2704 | like the underlying sequence, except they don't allow assignment:
2705 |
2706 | >>> view[1]
2707 | '1'
2708 | >>> view[1:-1]
2709 | ['1', '2']
2710 | >>> len(view)
2711 | 4
2712 |
2713 | Sequence views are useful as an alternative to copying, as they don't
2714 | require (much) extra storage.
2715 |
2716 | """
2717 |
2718 | def __init__(self, target):
2719 | if not isinstance(target, Sequence):
2720 | raise TypeError
2721 | self._target = target
2722 |
2723 | def __getitem__(self, index):
2724 | return self._target[index]
2725 |
2726 | def __len__(self):
2727 | return len(self._target)
2728 |
2729 | def __repr__(self):
2730 | return '{}({})'.format(self.__class__.__name__, repr(self._target))
2731 |
2732 |
2733 | class seekable:
2734 | """Wrap an iterator to allow for seeking backward and forward. This
2735 | progressively caches the items in the source iterable so they can be
2736 | re-visited.
2737 |
2738 | Call :meth:`seek` with an index to seek to that position in the source
2739 | iterable.
2740 |
2741 | To "reset" an iterator, seek to ``0``:
2742 |
2743 | >>> from itertools import count
2744 | >>> it = seekable((str(n) for n in count()))
2745 | >>> next(it), next(it), next(it)
2746 | ('0', '1', '2')
2747 | >>> it.seek(0)
2748 | >>> next(it), next(it), next(it)
2749 | ('0', '1', '2')
2750 | >>> next(it)
2751 | '3'
2752 |
2753 | You can also seek forward:
2754 |
2755 | >>> it = seekable((str(n) for n in range(20)))
2756 | >>> it.seek(10)
2757 | >>> next(it)
2758 | '10'
2759 | >>> it.relative_seek(-2) # Seeking relative to the current position
2760 | >>> next(it)
2761 | '9'
2762 | >>> it.seek(20) # Seeking past the end of the source isn't a problem
2763 | >>> list(it)
2764 | []
2765 | >>> it.seek(0) # Resetting works even after hitting the end
2766 | >>> next(it), next(it), next(it)
2767 | ('0', '1', '2')
2768 |
2769 | Call :meth:`peek` to look ahead one item without advancing the iterator:
2770 |
2771 | >>> it = seekable('1234')
2772 | >>> it.peek()
2773 | '1'
2774 | >>> list(it)
2775 | ['1', '2', '3', '4']
2776 | >>> it.peek(default='empty')
2777 | 'empty'
2778 |
2779 | Before the iterator is at its end, calling :func:`bool` on it will return
2780 | ``True``. After it will return ``False``:
2781 |
2782 | >>> it = seekable('5678')
2783 | >>> bool(it)
2784 | True
2785 | >>> list(it)
2786 | ['5', '6', '7', '8']
2787 | >>> bool(it)
2788 | False
2789 |
2790 | You may view the contents of the cache with the :meth:`elements` method.
2791 | That returns a :class:`SequenceView`, a view that updates automatically:
2792 |
2793 | >>> it = seekable((str(n) for n in range(10)))
2794 | >>> next(it), next(it), next(it)
2795 | ('0', '1', '2')
2796 | >>> elements = it.elements()
2797 | >>> elements
2798 | SequenceView(['0', '1', '2'])
2799 | >>> next(it)
2800 | '3'
2801 | >>> elements
2802 | SequenceView(['0', '1', '2', '3'])
2803 |
2804 | By default, the cache grows as the source iterable progresses, so beware of
2805 | wrapping very large or infinite iterables. Supply *maxlen* to limit the
2806 | size of the cache (this of course limits how far back you can seek).
2807 |
2808 | >>> from itertools import count
2809 | >>> it = seekable((str(n) for n in count()), maxlen=2)
2810 | >>> next(it), next(it), next(it), next(it)
2811 | ('0', '1', '2', '3')
2812 | >>> list(it.elements())
2813 | ['2', '3']
2814 | >>> it.seek(0)
2815 | >>> next(it), next(it), next(it), next(it)
2816 | ('2', '3', '4', '5')
2817 | >>> next(it)
2818 | '6'
2819 |
2820 | """
2821 |
2822 | def __init__(self, iterable, maxlen=None):
2823 | self._source = iter(iterable)
2824 | if maxlen is None:
2825 | self._cache = []
2826 | else:
2827 | self._cache = deque([], maxlen)
2828 | self._index = None
2829 |
2830 | def __iter__(self):
2831 | return self
2832 |
2833 | def __next__(self):
2834 | if self._index is not None:
2835 | try:
2836 | item = self._cache[self._index]
2837 | except IndexError:
2838 | self._index = None
2839 | else:
2840 | self._index += 1
2841 | return item
2842 |
2843 | item = next(self._source)
2844 | self._cache.append(item)
2845 | return item
2846 |
2847 | def __bool__(self):
2848 | try:
2849 | self.peek()
2850 | except StopIteration:
2851 | return False
2852 | return True
2853 |
2854 | def peek(self, default=_marker):
2855 | try:
2856 | peeked = next(self)
2857 | except StopIteration:
2858 | if default is _marker:
2859 | raise
2860 | return default
2861 | if self._index is None:
2862 | self._index = len(self._cache)
2863 | self._index -= 1
2864 | return peeked
2865 |
2866 | def elements(self):
2867 | return SequenceView(self._cache)
2868 |
2869 | def seek(self, index):
2870 | self._index = index
2871 | remainder = index - len(self._cache)
2872 | if remainder > 0:
2873 | consume(self, remainder)
2874 |
2875 | def relative_seek(self, count):
2876 | index = len(self._cache)
2877 | self.seek(max(index + count, 0))
2878 |
2879 |
2880 | class run_length:
2881 | """
2882 | :func:`run_length.encode` compresses an iterable with run-length encoding.
2883 | It yields groups of repeated items with the count of how many times they
2884 | were repeated:
2885 |
2886 | >>> uncompressed = 'abbcccdddd'
2887 | >>> list(run_length.encode(uncompressed))
2888 | [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
2889 |
2890 | :func:`run_length.decode` decompresses an iterable that was previously
2891 | compressed with run-length encoding. It yields the items of the
2892 | decompressed iterable:
2893 |
2894 | >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
2895 | >>> list(run_length.decode(compressed))
2896 | ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd']
2897 |
2898 | """
2899 |
2900 | @staticmethod
2901 | def encode(iterable):
2902 | return ((k, ilen(g)) for k, g in groupby(iterable))
2903 |
2904 | @staticmethod
2905 | def decode(iterable):
2906 | return chain.from_iterable(repeat(k, n) for k, n in iterable)
2907 |
2908 |
2909 | def exactly_n(iterable, n, predicate=bool):
2910 | """Return ``True`` if exactly ``n`` items in the iterable are ``True``
2911 | according to the *predicate* function.
2912 |
2913 | >>> exactly_n([True, True, False], 2)
2914 | True
2915 | >>> exactly_n([True, True, False], 1)
2916 | False
2917 | >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3)
2918 | True
2919 |
2920 | The iterable will be advanced until ``n + 1`` truthy items are encountered,
2921 | so avoid calling it on infinite iterables.
2922 |
2923 | """
2924 | return len(take(n + 1, filter(predicate, iterable))) == n
2925 |
2926 |
2927 | def circular_shifts(iterable):
2928 | """Return a list of circular shifts of *iterable*.
2929 |
2930 | >>> circular_shifts(range(4))
2931 | [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]
2932 | """
2933 | lst = list(iterable)
2934 | return take(len(lst), windowed(cycle(lst), len(lst)))
2935 |
2936 |
2937 | def make_decorator(wrapping_func, result_index=0):
2938 | """Return a decorator version of *wrapping_func*, which is a function that
2939 | modifies an iterable. *result_index* is the position in that function's
2940 | signature where the iterable goes.
2941 |
2942 | This lets you use itertools on the "production end," i.e. at function
2943 | definition. This can augment what the function returns without changing the
2944 | function's code.
2945 |
2946 | For example, to produce a decorator version of :func:`chunked`:
2947 |
2948 | >>> from more_itertools import chunked
2949 | >>> chunker = make_decorator(chunked, result_index=0)
2950 | >>> @chunker(3)
2951 | ... def iter_range(n):
2952 | ... return iter(range(n))
2953 | ...
2954 | >>> list(iter_range(9))
2955 | [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
2956 |
2957 | To only allow truthy items to be returned:
2958 |
2959 | >>> truth_serum = make_decorator(filter, result_index=1)
2960 | >>> @truth_serum(bool)
2961 | ... def boolean_test():
2962 | ... return [0, 1, '', ' ', False, True]
2963 | ...
2964 | >>> list(boolean_test())
2965 | [1, ' ', True]
2966 |
2967 | The :func:`peekable` and :func:`seekable` wrappers make for practical
2968 | decorators:
2969 |
2970 | >>> from more_itertools import peekable
2971 | >>> peekable_function = make_decorator(peekable)
2972 | >>> @peekable_function()
2973 | ... def str_range(*args):
2974 | ... return (str(x) for x in range(*args))
2975 | ...
2976 | >>> it = str_range(1, 20, 2)
2977 | >>> next(it), next(it), next(it)
2978 | ('1', '3', '5')
2979 | >>> it.peek()
2980 | '7'
2981 | >>> next(it)
2982 | '7'
2983 |
2984 | """
2985 |
2986 | # See https://sites.google.com/site/bbayles/index/decorator_factory for
2987 | # notes on how this works.
2988 | def decorator(*wrapping_args, **wrapping_kwargs):
2989 | def outer_wrapper(f):
2990 | def inner_wrapper(*args, **kwargs):
2991 | result = f(*args, **kwargs)
2992 | wrapping_args_ = list(wrapping_args)
2993 | wrapping_args_.insert(result_index, result)
2994 | return wrapping_func(*wrapping_args_, **wrapping_kwargs)
2995 |
2996 | return inner_wrapper
2997 |
2998 | return outer_wrapper
2999 |
3000 | return decorator
3001 |
3002 |
3003 | def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None):
3004 | """Return a dictionary that maps the items in *iterable* to categories
3005 | defined by *keyfunc*, transforms them with *valuefunc*, and
3006 | then summarizes them by category with *reducefunc*.
3007 |
3008 | *valuefunc* defaults to the identity function if it is unspecified.
3009 | If *reducefunc* is unspecified, no summarization takes place:
3010 |
3011 | >>> keyfunc = lambda x: x.upper()
3012 | >>> result = map_reduce('abbccc', keyfunc)
3013 | >>> sorted(result.items())
3014 | [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])]
3015 |
3016 | Specifying *valuefunc* transforms the categorized items:
3017 |
3018 | >>> keyfunc = lambda x: x.upper()
3019 | >>> valuefunc = lambda x: 1
3020 | >>> result = map_reduce('abbccc', keyfunc, valuefunc)
3021 | >>> sorted(result.items())
3022 | [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])]
3023 |
3024 | Specifying *reducefunc* summarizes the categorized items:
3025 |
3026 | >>> keyfunc = lambda x: x.upper()
3027 | >>> valuefunc = lambda x: 1
3028 | >>> reducefunc = sum
3029 | >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc)
3030 | >>> sorted(result.items())
3031 | [('A', 1), ('B', 2), ('C', 3)]
3032 |
3033 | You may want to filter the input iterable before applying the map/reduce
3034 | procedure:
3035 |
3036 | >>> all_items = range(30)
3037 | >>> items = [x for x in all_items if 10 <= x <= 20] # Filter
3038 | >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1
3039 | >>> categories = map_reduce(items, keyfunc=keyfunc)
3040 | >>> sorted(categories.items())
3041 | [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])]
3042 | >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum)
3043 | >>> sorted(summaries.items())
3044 | [(0, 90), (1, 75)]
3045 |
3046 | Note that all items in the iterable are gathered into a list before the
3047 | summarization step, which may require significant storage.
3048 |
3049 | The returned object is a :obj:`collections.defaultdict` with the
3050 | ``default_factory`` set to ``None``, such that it behaves like a normal
3051 | dictionary.
3052 |
3053 | """
3054 | valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc
3055 |
3056 | ret = defaultdict(list)
3057 | for item in iterable:
3058 | key = keyfunc(item)
3059 | value = valuefunc(item)
3060 | ret[key].append(value)
3061 |
3062 | if reducefunc is not None:
3063 | for key, value_list in ret.items():
3064 | ret[key] = reducefunc(value_list)
3065 |
3066 | ret.default_factory = None
3067 | return ret
3068 |
3069 |
3070 | def rlocate(iterable, pred=bool, window_size=None):
3071 | """Yield the index of each item in *iterable* for which *pred* returns
3072 | ``True``, starting from the right and moving left.
3073 |
3074 | *pred* defaults to :func:`bool`, which will select truthy items:
3075 |
3076 | >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4
3077 | [4, 2, 1]
3078 |
3079 | Set *pred* to a custom function to, e.g., find the indexes for a particular
3080 | item:
3081 |
3082 | >>> iterable = iter('abcb')
3083 | >>> pred = lambda x: x == 'b'
3084 | >>> list(rlocate(iterable, pred))
3085 | [3, 1]
3086 |
3087 | If *window_size* is given, then the *pred* function will be called with
3088 | that many items. This enables searching for sub-sequences:
3089 |
3090 | >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
3091 | >>> pred = lambda *args: args == (1, 2, 3)
3092 | >>> list(rlocate(iterable, pred=pred, window_size=3))
3093 | [9, 5, 1]
3094 |
3095 | Beware, this function won't return anything for infinite iterables.
3096 | If *iterable* is reversible, ``rlocate`` will reverse it and search from
3097 | the right. Otherwise, it will search from the left and return the results
3098 | in reverse order.
3099 |
3100 | See :func:`locate` to for other example applications.
3101 |
3102 | """
3103 | if window_size is None:
3104 | try:
3105 | len_iter = len(iterable)
3106 | return (len_iter - i - 1 for i in locate(reversed(iterable), pred))
3107 | except TypeError:
3108 | pass
3109 |
3110 | return reversed(list(locate(iterable, pred, window_size)))
3111 |
3112 |
3113 | def replace(iterable, pred, substitutes, count=None, window_size=1):
3114 | """Yield the items from *iterable*, replacing the items for which *pred*
3115 | returns ``True`` with the items from the iterable *substitutes*.
3116 |
3117 | >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1]
3118 | >>> pred = lambda x: x == 0
3119 | >>> substitutes = (2, 3)
3120 | >>> list(replace(iterable, pred, substitutes))
3121 | [1, 1, 2, 3, 1, 1, 2, 3, 1, 1]
3122 |
3123 | If *count* is given, the number of replacements will be limited:
3124 |
3125 | >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0]
3126 | >>> pred = lambda x: x == 0
3127 | >>> substitutes = [None]
3128 | >>> list(replace(iterable, pred, substitutes, count=2))
3129 | [1, 1, None, 1, 1, None, 1, 1, 0]
3130 |
3131 | Use *window_size* to control the number of items passed as arguments to
3132 | *pred*. This allows for locating and replacing subsequences.
3133 |
3134 | >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5]
3135 | >>> window_size = 3
3136 | >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred
3137 | >>> substitutes = [3, 4] # Splice in these items
3138 | >>> list(replace(iterable, pred, substitutes, window_size=window_size))
3139 | [3, 4, 5, 3, 4, 5]
3140 |
3141 | """
3142 | if window_size < 1:
3143 | raise ValueError('window_size must be at least 1')
3144 |
3145 | # Save the substitutes iterable, since it's used more than once
3146 | substitutes = tuple(substitutes)
3147 |
3148 | # Add padding such that the number of windows matches the length of the
3149 | # iterable
3150 | it = chain(iterable, [_marker] * (window_size - 1))
3151 | windows = windowed(it, window_size)
3152 |
3153 | n = 0
3154 | for w in windows:
3155 | # If the current window matches our predicate (and we haven't hit
3156 | # our maximum number of replacements), splice in the substitutes
3157 | # and then consume the following windows that overlap with this one.
3158 | # For example, if the iterable is (0, 1, 2, 3, 4...)
3159 | # and the window size is 2, we have (0, 1), (1, 2), (2, 3)...
3160 | # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2)
3161 | if pred(*w):
3162 | if (count is None) or (n < count):
3163 | n += 1
3164 | yield from substitutes
3165 | consume(windows, window_size - 1)
3166 | continue
3167 |
3168 | # If there was no match (or we've reached the replacement limit),
3169 | # yield the first item from the window.
3170 | if w and (w[0] is not _marker):
3171 | yield w[0]
3172 |
3173 |
3174 | def partitions(iterable):
3175 | """Yield all possible order-preserving partitions of *iterable*.
3176 |
3177 | >>> iterable = 'abc'
3178 | >>> for part in partitions(iterable):
3179 | ... print([''.join(p) for p in part])
3180 | ['abc']
3181 | ['a', 'bc']
3182 | ['ab', 'c']
3183 | ['a', 'b', 'c']
3184 |
3185 | This is unrelated to :func:`partition`.
3186 |
3187 | """
3188 | sequence = list(iterable)
3189 | n = len(sequence)
3190 | for i in powerset(range(1, n)):
3191 | yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))]
3192 |
3193 |
3194 | def set_partitions(iterable, k=None):
3195 | """
3196 | Yield the set partitions of *iterable* into *k* parts. Set partitions are
3197 | not order-preserving.
3198 |
3199 | >>> iterable = 'abc'
3200 | >>> for part in set_partitions(iterable, 2):
3201 | ... print([''.join(p) for p in part])
3202 | ['a', 'bc']
3203 | ['ab', 'c']
3204 | ['b', 'ac']
3205 |
3206 |
3207 | If *k* is not given, every set partition is generated.
3208 |
3209 | >>> iterable = 'abc'
3210 | >>> for part in set_partitions(iterable):
3211 | ... print([''.join(p) for p in part])
3212 | ['abc']
3213 | ['a', 'bc']
3214 | ['ab', 'c']
3215 | ['b', 'ac']
3216 | ['a', 'b', 'c']
3217 |
3218 | """
3219 | L = list(iterable)
3220 | n = len(L)
3221 | if k is not None:
3222 | if k < 1:
3223 | raise ValueError(
3224 | "Can't partition in a negative or zero number of groups"
3225 | )
3226 | elif k > n:
3227 | return
3228 |
3229 | def set_partitions_helper(L, k):
3230 | n = len(L)
3231 | if k == 1:
3232 | yield [L]
3233 | elif n == k:
3234 | yield [[s] for s in L]
3235 | else:
3236 | e, *M = L
3237 | for p in set_partitions_helper(M, k - 1):
3238 | yield [[e], *p]
3239 | for p in set_partitions_helper(M, k):
3240 | for i in range(len(p)):
3241 | yield p[:i] + [[e] + p[i]] + p[i + 1 :]
3242 |
3243 | if k is None:
3244 | for k in range(1, n + 1):
3245 | yield from set_partitions_helper(L, k)
3246 | else:
3247 | yield from set_partitions_helper(L, k)
3248 |
3249 |
3250 | class time_limited:
3251 | """
3252 | Yield items from *iterable* until *limit_seconds* have passed.
3253 | If the time limit expires before all items have been yielded, the
3254 | ``timed_out`` parameter will be set to ``True``.
3255 |
3256 | >>> from time import sleep
3257 | >>> def generator():
3258 | ... yield 1
3259 | ... yield 2
3260 | ... sleep(0.2)
3261 | ... yield 3
3262 | >>> iterable = time_limited(0.1, generator())
3263 | >>> list(iterable)
3264 | [1, 2]
3265 | >>> iterable.timed_out
3266 | True
3267 |
3268 | Note that the time is checked before each item is yielded, and iteration
3269 | stops if the time elapsed is greater than *limit_seconds*. If your time
3270 | limit is 1 second, but it takes 2 seconds to generate the first item from
3271 | the iterable, the function will run for 2 seconds and not yield anything.
3272 | As a special case, when *limit_seconds* is zero, the iterator never
3273 | returns anything.
3274 |
3275 | """
3276 |
3277 | def __init__(self, limit_seconds, iterable):
3278 | if limit_seconds < 0:
3279 | raise ValueError('limit_seconds must be positive')
3280 | self.limit_seconds = limit_seconds
3281 | self._iterable = iter(iterable)
3282 | self._start_time = monotonic()
3283 | self.timed_out = False
3284 |
3285 | def __iter__(self):
3286 | return self
3287 |
3288 | def __next__(self):
3289 | if self.limit_seconds == 0:
3290 | self.timed_out = True
3291 | raise StopIteration
3292 | item = next(self._iterable)
3293 | if monotonic() - self._start_time > self.limit_seconds:
3294 | self.timed_out = True
3295 | raise StopIteration
3296 |
3297 | return item
3298 |
3299 |
3300 | def only(iterable, default=None, too_long=None):
3301 | """If *iterable* has only one item, return it.
3302 | If it has zero items, return *default*.
3303 | If it has more than one item, raise the exception given by *too_long*,
3304 | which is ``ValueError`` by default.
3305 |
3306 | >>> only([], default='missing')
3307 | 'missing'
3308 | >>> only([1])
3309 | 1
3310 | >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL
3311 | Traceback (most recent call last):
3312 | ...
3313 | ValueError: Expected exactly one item in iterable, but got 1, 2,
3314 | and perhaps more.'
3315 | >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL
3316 | Traceback (most recent call last):
3317 | ...
3318 | TypeError
3319 |
3320 | Note that :func:`only` attempts to advance *iterable* twice to ensure there
3321 | is only one item. See :func:`spy` or :func:`peekable` to check
3322 | iterable contents less destructively.
3323 | """
3324 | it = iter(iterable)
3325 | first_value = next(it, default)
3326 |
3327 | try:
3328 | second_value = next(it)
3329 | except StopIteration:
3330 | pass
3331 | else:
3332 | msg = (
3333 | 'Expected exactly one item in iterable, but got {!r}, {!r}, '
3334 | 'and perhaps more.'.format(first_value, second_value)
3335 | )
3336 | raise too_long or ValueError(msg)
3337 |
3338 | return first_value
3339 |
3340 |
3341 | def _ichunk(iterable, n):
3342 | cache = deque()
3343 | chunk = islice(iterable, n)
3344 |
3345 | def generator():
3346 | while True:
3347 | if cache:
3348 | yield cache.popleft()
3349 | else:
3350 | try:
3351 | item = next(chunk)
3352 | except StopIteration:
3353 | return
3354 | else:
3355 | yield item
3356 |
3357 | def materialize_next(n=1):
3358 | # if n not specified materialize everything
3359 | if n is None:
3360 | cache.extend(chunk)
3361 | return len(cache)
3362 |
3363 | to_cache = n - len(cache)
3364 |
3365 | # materialize up to n
3366 | if to_cache > 0:
3367 | cache.extend(islice(chunk, to_cache))
3368 |
3369 | # return number materialized up to n
3370 | return min(n, len(cache))
3371 |
3372 | return (generator(), materialize_next)
3373 |
3374 |
3375 | def ichunked(iterable, n):
3376 | """Break *iterable* into sub-iterables with *n* elements each.
3377 | :func:`ichunked` is like :func:`chunked`, but it yields iterables
3378 | instead of lists.
3379 |
3380 | If the sub-iterables are read in order, the elements of *iterable*
3381 | won't be stored in memory.
3382 | If they are read out of order, :func:`itertools.tee` is used to cache
3383 | elements as necessary.
3384 |
3385 | >>> from itertools import count
3386 | >>> all_chunks = ichunked(count(), 4)
3387 | >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks)
3388 | >>> list(c_2) # c_1's elements have been cached; c_3's haven't been
3389 | [4, 5, 6, 7]
3390 | >>> list(c_1)
3391 | [0, 1, 2, 3]
3392 | >>> list(c_3)
3393 | [8, 9, 10, 11]
3394 |
3395 | """
3396 | iterable = iter(iterable)
3397 | while True:
3398 | # Create new chunk
3399 | chunk, materialize_next = _ichunk(iterable, n)
3400 |
3401 | # Check to see whether we're at the end of the source iterable
3402 | if not materialize_next():
3403 | return
3404 |
3405 | yield chunk
3406 |
3407 | # Fill previous chunk's cache
3408 | materialize_next(None)
3409 |
3410 |
3411 | def iequals(*iterables):
3412 | """Return ``True`` if all given *iterables* are equal to each other,
3413 | which means that they contain the same elements in the same order.
3414 |
3415 | The function is useful for comparing iterables of different data types
3416 | or iterables that do not support equality checks.
3417 |
3418 | >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc"))
3419 | True
3420 |
3421 | >>> iequals("abc", "acb")
3422 | False
3423 |
3424 | Not to be confused with :func:`all_equal`, which checks whether all
3425 | elements of iterable are equal to each other.
3426 |
3427 | """
3428 | return all(map(all_equal, zip_longest(*iterables, fillvalue=object())))
3429 |
3430 |
3431 | def distinct_combinations(iterable, r):
3432 | """Yield the distinct combinations of *r* items taken from *iterable*.
3433 |
3434 | >>> list(distinct_combinations([0, 0, 1], 2))
3435 | [(0, 0), (0, 1)]
3436 |
3437 | Equivalent to ``set(combinations(iterable))``, except duplicates are not
3438 | generated and thrown away. For larger input sequences this is much more
3439 | efficient.
3440 |
3441 | """
3442 | if r < 0:
3443 | raise ValueError('r must be non-negative')
3444 | elif r == 0:
3445 | yield ()
3446 | return
3447 | pool = tuple(iterable)
3448 | generators = [unique_everseen(enumerate(pool), key=itemgetter(1))]
3449 | current_combo = [None] * r
3450 | level = 0
3451 | while generators:
3452 | try:
3453 | cur_idx, p = next(generators[-1])
3454 | except StopIteration:
3455 | generators.pop()
3456 | level -= 1
3457 | continue
3458 | current_combo[level] = p
3459 | if level + 1 == r:
3460 | yield tuple(current_combo)
3461 | else:
3462 | generators.append(
3463 | unique_everseen(
3464 | enumerate(pool[cur_idx + 1 :], cur_idx + 1),
3465 | key=itemgetter(1),
3466 | )
3467 | )
3468 | level += 1
3469 |
3470 |
3471 | def filter_except(validator, iterable, *exceptions):
3472 | """Yield the items from *iterable* for which the *validator* function does
3473 | not raise one of the specified *exceptions*.
3474 |
3475 | *validator* is called for each item in *iterable*.
3476 | It should be a function that accepts one argument and raises an exception
3477 | if that item is not valid.
3478 |
3479 | >>> iterable = ['1', '2', 'three', '4', None]
3480 | >>> list(filter_except(int, iterable, ValueError, TypeError))
3481 | ['1', '2', '4']
3482 |
3483 | If an exception other than one given by *exceptions* is raised by
3484 | *validator*, it is raised like normal.
3485 | """
3486 | for item in iterable:
3487 | try:
3488 | validator(item)
3489 | except exceptions:
3490 | pass
3491 | else:
3492 | yield item
3493 |
3494 |
3495 | def map_except(function, iterable, *exceptions):
3496 | """Transform each item from *iterable* with *function* and yield the
3497 | result, unless *function* raises one of the specified *exceptions*.
3498 |
3499 | *function* is called to transform each item in *iterable*.
3500 | It should accept one argument.
3501 |
3502 | >>> iterable = ['1', '2', 'three', '4', None]
3503 | >>> list(map_except(int, iterable, ValueError, TypeError))
3504 | [1, 2, 4]
3505 |
3506 | If an exception other than one given by *exceptions* is raised by
3507 | *function*, it is raised like normal.
3508 | """
3509 | for item in iterable:
3510 | try:
3511 | yield function(item)
3512 | except exceptions:
3513 | pass
3514 |
3515 |
3516 | def map_if(iterable, pred, func, func_else=lambda x: x):
3517 | """Evaluate each item from *iterable* using *pred*. If the result is
3518 | equivalent to ``True``, transform the item with *func* and yield it.
3519 | Otherwise, transform the item with *func_else* and yield it.
3520 |
3521 | *pred*, *func*, and *func_else* should each be functions that accept
3522 | one argument. By default, *func_else* is the identity function.
3523 |
3524 | >>> from math import sqrt
3525 | >>> iterable = list(range(-5, 5))
3526 | >>> iterable
3527 | [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
3528 | >>> list(map_if(iterable, lambda x: x > 3, lambda x: 'toobig'))
3529 | [-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig']
3530 | >>> list(map_if(iterable, lambda x: x >= 0,
3531 | ... lambda x: f'{sqrt(x):.2f}', lambda x: None))
3532 | [None, None, None, None, None, '0.00', '1.00', '1.41', '1.73', '2.00']
3533 | """
3534 | for item in iterable:
3535 | yield func(item) if pred(item) else func_else(item)
3536 |
3537 |
3538 | def _sample_unweighted(iterable, k):
3539 | # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li:
3540 | # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))".
3541 |
3542 | # Fill up the reservoir (collection of samples) with the first `k` samples
3543 | reservoir = take(k, iterable)
3544 |
3545 | # Generate random number that's the largest in a sample of k U(0,1) numbers
3546 | # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic
3547 | W = exp(log(random()) / k)
3548 |
3549 | # The number of elements to skip before changing the reservoir is a random
3550 | # number with a geometric distribution. Sample it using random() and logs.
3551 | next_index = k + floor(log(random()) / log(1 - W))
3552 |
3553 | for index, element in enumerate(iterable, k):
3554 | if index == next_index:
3555 | reservoir[randrange(k)] = element
3556 | # The new W is the largest in a sample of k U(0, `old_W`) numbers
3557 | W *= exp(log(random()) / k)
3558 | next_index += floor(log(random()) / log(1 - W)) + 1
3559 |
3560 | return reservoir
3561 |
3562 |
3563 | def _sample_weighted(iterable, k, weights):
3564 | # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. :
3565 | # "Weighted random sampling with a reservoir".
3566 |
3567 | # Log-transform for numerical stability for weights that are small/large
3568 | weight_keys = (log(random()) / weight for weight in weights)
3569 |
3570 | # Fill up the reservoir (collection of samples) with the first `k`
3571 | # weight-keys and elements, then heapify the list.
3572 | reservoir = take(k, zip(weight_keys, iterable))
3573 | heapify(reservoir)
3574 |
3575 | # The number of jumps before changing the reservoir is a random variable
3576 | # with an exponential distribution. Sample it using random() and logs.
3577 | smallest_weight_key, _ = reservoir[0]
3578 | weights_to_skip = log(random()) / smallest_weight_key
3579 |
3580 | for weight, element in zip(weights, iterable):
3581 | if weight >= weights_to_skip:
3582 | # The notation here is consistent with the paper, but we store
3583 | # the weight-keys in log-space for better numerical stability.
3584 | smallest_weight_key, _ = reservoir[0]
3585 | t_w = exp(weight * smallest_weight_key)
3586 | r_2 = uniform(t_w, 1) # generate U(t_w, 1)
3587 | weight_key = log(r_2) / weight
3588 | heapreplace(reservoir, (weight_key, element))
3589 | smallest_weight_key, _ = reservoir[0]
3590 | weights_to_skip = log(random()) / smallest_weight_key
3591 | else:
3592 | weights_to_skip -= weight
3593 |
3594 | # Equivalent to [element for weight_key, element in sorted(reservoir)]
3595 | return [heappop(reservoir)[1] for _ in range(k)]
3596 |
3597 |
3598 | def sample(iterable, k, weights=None):
3599 | """Return a *k*-length list of elements chosen (without replacement)
3600 | from the *iterable*. Like :func:`random.sample`, but works on iterables
3601 | of unknown length.
3602 |
3603 | >>> iterable = range(100)
3604 | >>> sample(iterable, 5) # doctest: +SKIP
3605 | [81, 60, 96, 16, 4]
3606 |
3607 | An iterable with *weights* may also be given:
3608 |
3609 | >>> iterable = range(100)
3610 | >>> weights = (i * i + 1 for i in range(100))
3611 | >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP
3612 | [79, 67, 74, 66, 78]
3613 |
3614 | The algorithm can also be used to generate weighted random permutations.
3615 | The relative weight of each item determines the probability that it
3616 | appears late in the permutation.
3617 |
3618 | >>> data = "abcdefgh"
3619 | >>> weights = range(1, len(data) + 1)
3620 | >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP
3621 | ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f']
3622 | """
3623 | if k == 0:
3624 | return []
3625 |
3626 | iterable = iter(iterable)
3627 | if weights is None:
3628 | return _sample_unweighted(iterable, k)
3629 | else:
3630 | weights = iter(weights)
3631 | return _sample_weighted(iterable, k, weights)
3632 |
3633 |
3634 | def is_sorted(iterable, key=None, reverse=False, strict=False):
3635 | """Returns ``True`` if the items of iterable are in sorted order, and
3636 | ``False`` otherwise. *key* and *reverse* have the same meaning that they do
3637 | in the built-in :func:`sorted` function.
3638 |
3639 | >>> is_sorted(['1', '2', '3', '4', '5'], key=int)
3640 | True
3641 | >>> is_sorted([5, 4, 3, 1, 2], reverse=True)
3642 | False
3643 |
3644 | If *strict*, tests for strict sorting, that is, returns ``False`` if equal
3645 | elements are found:
3646 |
3647 | >>> is_sorted([1, 2, 2])
3648 | True
3649 | >>> is_sorted([1, 2, 2], strict=True)
3650 | False
3651 |
3652 | The function returns ``False`` after encountering the first out-of-order
3653 | item. If there are no out-of-order items, the iterable is exhausted.
3654 | """
3655 |
3656 | compare = (le if reverse else ge) if strict else (lt if reverse else gt)
3657 | it = iterable if key is None else map(key, iterable)
3658 | return not any(starmap(compare, pairwise(it)))
3659 |
3660 |
3661 | class AbortThread(BaseException):
3662 | pass
3663 |
3664 |
3665 | class callback_iter:
3666 | """Convert a function that uses callbacks to an iterator.
3667 |
3668 | Let *func* be a function that takes a `callback` keyword argument.
3669 | For example:
3670 |
3671 | >>> def func(callback=None):
3672 | ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]:
3673 | ... if callback:
3674 | ... callback(i, c)
3675 | ... return 4
3676 |
3677 |
3678 | Use ``with callback_iter(func)`` to get an iterator over the parameters
3679 | that are delivered to the callback.
3680 |
3681 | >>> with callback_iter(func) as it:
3682 | ... for args, kwargs in it:
3683 | ... print(args)
3684 | (1, 'a')
3685 | (2, 'b')
3686 | (3, 'c')
3687 |
3688 | The function will be called in a background thread. The ``done`` property
3689 | indicates whether it has completed execution.
3690 |
3691 | >>> it.done
3692 | True
3693 |
3694 | If it completes successfully, its return value will be available
3695 | in the ``result`` property.
3696 |
3697 | >>> it.result
3698 | 4
3699 |
3700 | Notes:
3701 |
3702 | * If the function uses some keyword argument besides ``callback``, supply
3703 | *callback_kwd*.
3704 | * If it finished executing, but raised an exception, accessing the
3705 | ``result`` property will raise the same exception.
3706 | * If it hasn't finished executing, accessing the ``result``
3707 | property from within the ``with`` block will raise ``RuntimeError``.
3708 | * If it hasn't finished executing, accessing the ``result`` property from
3709 | outside the ``with`` block will raise a
3710 | ``more_itertools.AbortThread`` exception.
3711 | * Provide *wait_seconds* to adjust how frequently the it is polled for
3712 | output.
3713 |
3714 | """
3715 |
3716 | def __init__(self, func, callback_kwd='callback', wait_seconds=0.1):
3717 | self._func = func
3718 | self._callback_kwd = callback_kwd
3719 | self._aborted = False
3720 | self._future = None
3721 | self._wait_seconds = wait_seconds
3722 | # Lazily import concurrent.future
3723 | self._executor = __import__(
3724 | 'concurrent.futures'
3725 | ).futures.ThreadPoolExecutor(max_workers=1)
3726 | self._iterator = self._reader()
3727 |
3728 | def __enter__(self):
3729 | return self
3730 |
3731 | def __exit__(self, exc_type, exc_value, traceback):
3732 | self._aborted = True
3733 | self._executor.shutdown()
3734 |
3735 | def __iter__(self):
3736 | return self
3737 |
3738 | def __next__(self):
3739 | return next(self._iterator)
3740 |
3741 | @property
3742 | def done(self):
3743 | if self._future is None:
3744 | return False
3745 | return self._future.done()
3746 |
3747 | @property
3748 | def result(self):
3749 | if not self.done:
3750 | raise RuntimeError('Function has not yet completed')
3751 |
3752 | return self._future.result()
3753 |
3754 | def _reader(self):
3755 | q = Queue()
3756 |
3757 | def callback(*args, **kwargs):
3758 | if self._aborted:
3759 | raise AbortThread('canceled by user')
3760 |
3761 | q.put((args, kwargs))
3762 |
3763 | self._future = self._executor.submit(
3764 | self._func, **{self._callback_kwd: callback}
3765 | )
3766 |
3767 | while True:
3768 | try:
3769 | item = q.get(timeout=self._wait_seconds)
3770 | except Empty:
3771 | pass
3772 | else:
3773 | q.task_done()
3774 | yield item
3775 |
3776 | if self._future.done():
3777 | break
3778 |
3779 | remaining = []
3780 | while True:
3781 | try:
3782 | item = q.get_nowait()
3783 | except Empty:
3784 | break
3785 | else:
3786 | q.task_done()
3787 | remaining.append(item)
3788 | q.join()
3789 | yield from remaining
3790 |
3791 |
3792 | def windowed_complete(iterable, n):
3793 | """
3794 | Yield ``(beginning, middle, end)`` tuples, where:
3795 |
3796 | * Each ``middle`` has *n* items from *iterable*
3797 | * Each ``beginning`` has the items before the ones in ``middle``
3798 | * Each ``end`` has the items after the ones in ``middle``
3799 |
3800 | >>> iterable = range(7)
3801 | >>> n = 3
3802 | >>> for beginning, middle, end in windowed_complete(iterable, n):
3803 | ... print(beginning, middle, end)
3804 | () (0, 1, 2) (3, 4, 5, 6)
3805 | (0,) (1, 2, 3) (4, 5, 6)
3806 | (0, 1) (2, 3, 4) (5, 6)
3807 | (0, 1, 2) (3, 4, 5) (6,)
3808 | (0, 1, 2, 3) (4, 5, 6) ()
3809 |
3810 | Note that *n* must be at least 0 and most equal to the length of
3811 | *iterable*.
3812 |
3813 | This function will exhaust the iterable and may require significant
3814 | storage.
3815 | """
3816 | if n < 0:
3817 | raise ValueError('n must be >= 0')
3818 |
3819 | seq = tuple(iterable)
3820 | size = len(seq)
3821 |
3822 | if n > size:
3823 | raise ValueError('n must be <= len(seq)')
3824 |
3825 | for i in range(size - n + 1):
3826 | beginning = seq[:i]
3827 | middle = seq[i : i + n]
3828 | end = seq[i + n :]
3829 | yield beginning, middle, end
3830 |
3831 |
3832 | def all_unique(iterable, key=None):
3833 | """
3834 | Returns ``True`` if all the elements of *iterable* are unique (no two
3835 | elements are equal).
3836 |
3837 | >>> all_unique('ABCB')
3838 | False
3839 |
3840 | If a *key* function is specified, it will be used to make comparisons.
3841 |
3842 | >>> all_unique('ABCb')
3843 | True
3844 | >>> all_unique('ABCb', str.lower)
3845 | False
3846 |
3847 | The function returns as soon as the first non-unique element is
3848 | encountered. Iterables with a mix of hashable and unhashable items can
3849 | be used, but the function will be slower for unhashable items.
3850 | """
3851 | seenset = set()
3852 | seenset_add = seenset.add
3853 | seenlist = []
3854 | seenlist_add = seenlist.append
3855 | for element in map(key, iterable) if key else iterable:
3856 | try:
3857 | if element in seenset:
3858 | return False
3859 | seenset_add(element)
3860 | except TypeError:
3861 | if element in seenlist:
3862 | return False
3863 | seenlist_add(element)
3864 | return True
3865 |
3866 |
3867 | def nth_product(index, *args):
3868 | """Equivalent to ``list(product(*args))[index]``.
3869 |
3870 | The products of *args* can be ordered lexicographically.
3871 | :func:`nth_product` computes the product at sort position *index* without
3872 | computing the previous products.
3873 |
3874 | >>> nth_product(8, range(2), range(2), range(2), range(2))
3875 | (1, 0, 0, 0)
3876 |
3877 | ``IndexError`` will be raised if the given *index* is invalid.
3878 | """
3879 | pools = list(map(tuple, reversed(args)))
3880 | ns = list(map(len, pools))
3881 |
3882 | c = reduce(mul, ns)
3883 |
3884 | if index < 0:
3885 | index += c
3886 |
3887 | if not 0 <= index < c:
3888 | raise IndexError
3889 |
3890 | result = []
3891 | for pool, n in zip(pools, ns):
3892 | result.append(pool[index % n])
3893 | index //= n
3894 |
3895 | return tuple(reversed(result))
3896 |
3897 |
3898 | def nth_permutation(iterable, r, index):
3899 | """Equivalent to ``list(permutations(iterable, r))[index]```
3900 |
3901 | The subsequences of *iterable* that are of length *r* where order is
3902 | important can be ordered lexicographically. :func:`nth_permutation`
3903 | computes the subsequence at sort position *index* directly, without
3904 | computing the previous subsequences.
3905 |
3906 | >>> nth_permutation('ghijk', 2, 5)
3907 | ('h', 'i')
3908 |
3909 | ``ValueError`` will be raised If *r* is negative or greater than the length
3910 | of *iterable*.
3911 | ``IndexError`` will be raised if the given *index* is invalid.
3912 | """
3913 | pool = list(iterable)
3914 | n = len(pool)
3915 |
3916 | if r is None or r == n:
3917 | r, c = n, factorial(n)
3918 | elif not 0 <= r < n:
3919 | raise ValueError
3920 | else:
3921 | c = perm(n, r)
3922 | assert c > 0 # factortial(n)>0, and r<n so perm(n,r) is never zero
3923 |
3924 | if index < 0:
3925 | index += c
3926 |
3927 | if not 0 <= index < c:
3928 | raise IndexError
3929 |
3930 | result = [0] * r
3931 | q = index * factorial(n) // c if r < n else index
3932 | for d in range(1, n + 1):
3933 | q, i = divmod(q, d)
3934 | if 0 <= n - d < r:
3935 | result[n - d] = i
3936 | if q == 0:
3937 | break
3938 |
3939 | return tuple(map(pool.pop, result))
3940 |
3941 |
3942 | def nth_combination_with_replacement(iterable, r, index):
3943 | """Equivalent to
3944 | ``list(combinations_with_replacement(iterable, r))[index]``.
3945 |
3946 |
3947 | The subsequences with repetition of *iterable* that are of length *r* can
3948 | be ordered lexicographically. :func:`nth_combination_with_replacement`
3949 | computes the subsequence at sort position *index* directly, without
3950 | computing the previous subsequences with replacement.
3951 |
3952 | >>> nth_combination_with_replacement(range(5), 3, 5)
3953 | (0, 1, 1)
3954 |
3955 | ``ValueError`` will be raised If *r* is negative or greater than the length
3956 | of *iterable*.
3957 | ``IndexError`` will be raised if the given *index* is invalid.
3958 | """
3959 | pool = tuple(iterable)
3960 | n = len(pool)
3961 | if (r < 0) or (r > n):
3962 | raise ValueError
3963 |
3964 | c = comb(n + r - 1, r)
3965 |
3966 | if index < 0:
3967 | index += c
3968 |
3969 | if (index < 0) or (index >= c):
3970 | raise IndexError
3971 |
3972 | result = []
3973 | i = 0
3974 | while r:
3975 | r -= 1
3976 | while n >= 0:
3977 | num_combs = comb(n + r - 1, r)
3978 | if index < num_combs:
3979 | break
3980 | n -= 1
3981 | i += 1
3982 | index -= num_combs
3983 | result.append(pool[i])
3984 |
3985 | return tuple(result)
3986 |
3987 |
3988 | def value_chain(*args):
3989 | """Yield all arguments passed to the function in the same order in which
3990 | they were passed. If an argument itself is iterable then iterate over its
3991 | values.
3992 |
3993 | >>> list(value_chain(1, 2, 3, [4, 5, 6]))
3994 | [1, 2, 3, 4, 5, 6]
3995 |
3996 | Binary and text strings are not considered iterable and are emitted
3997 | as-is:
3998 |
3999 | >>> list(value_chain('12', '34', ['56', '78']))
4000 | ['12', '34', '56', '78']
4001 |
4002 | Pre- or postpend a single element to an iterable:
4003 |
4004 | >>> list(value_chain(1, [2, 3, 4, 5, 6]))
4005 | [1, 2, 3, 4, 5, 6]
4006 | >>> list(value_chain([1, 2, 3, 4, 5], 6))
4007 | [1, 2, 3, 4, 5, 6]
4008 |
4009 | Multiple levels of nesting are not flattened.
4010 |
4011 | """
4012 | for value in args:
4013 | if isinstance(value, (str, bytes)):
4014 | yield value
4015 | continue
4016 | try:
4017 | yield from value
4018 | except TypeError:
4019 | yield value
4020 |
4021 |
4022 | def product_index(element, *args):
4023 | """Equivalent to ``list(product(*args)).index(element)``
4024 |
4025 | The products of *args* can be ordered lexicographically.
4026 | :func:`product_index` computes the first index of *element* without
4027 | computing the previous products.
4028 |
4029 | >>> product_index([8, 2], range(10), range(5))
4030 | 42
4031 |
4032 | ``ValueError`` will be raised if the given *element* isn't in the product
4033 | of *args*.
4034 | """
4035 | index = 0
4036 |
4037 | for x, pool in zip_longest(element, args, fillvalue=_marker):
4038 | if x is _marker or pool is _marker:
4039 | raise ValueError('element is not a product of args')
4040 |
4041 | pool = tuple(pool)
4042 | index = index * len(pool) + pool.index(x)
4043 |
4044 | return index
4045 |
4046 |
4047 | def combination_index(element, iterable):
4048 | """Equivalent to ``list(combinations(iterable, r)).index(element)``
4049 |
4050 | The subsequences of *iterable* that are of length *r* can be ordered
4051 | lexicographically. :func:`combination_index` computes the index of the
4052 | first *element*, without computing the previous combinations.
4053 |
4054 | >>> combination_index('adf', 'abcdefg')
4055 | 10
4056 |
4057 | ``ValueError`` will be raised if the given *element* isn't one of the
4058 | combinations of *iterable*.
4059 | """
4060 | element = enumerate(element)
4061 | k, y = next(element, (None, None))
4062 | if k is None:
4063 | return 0
4064 |
4065 | indexes = []
4066 | pool = enumerate(iterable)
4067 | for n, x in pool:
4068 | if x == y:
4069 | indexes.append(n)
4070 | tmp, y = next(element, (None, None))
4071 | if tmp is None:
4072 | break
4073 | else:
4074 | k = tmp
4075 | else:
4076 | raise ValueError('element is not a combination of iterable')
4077 |
4078 | n, _ = last(pool, default=(n, None))
4079 |
4080 | # Python versions below 3.8 don't have math.comb
4081 | index = 1
4082 | for i, j in enumerate(reversed(indexes), start=1):
4083 | j = n - j
4084 | if i <= j:
4085 | index += comb(j, i)
4086 |
4087 | return comb(n + 1, k + 1) - index
4088 |
4089 |
4090 | def combination_with_replacement_index(element, iterable):
4091 | """Equivalent to
4092 | ``list(combinations_with_replacement(iterable, r)).index(element)``
4093 |
4094 | The subsequences with repetition of *iterable* that are of length *r* can
4095 | be ordered lexicographically. :func:`combination_with_replacement_index`
4096 | computes the index of the first *element*, without computing the previous
4097 | combinations with replacement.
4098 |
4099 | >>> combination_with_replacement_index('adf', 'abcdefg')
4100 | 20
4101 |
4102 | ``ValueError`` will be raised if the given *element* isn't one of the
4103 | combinations with replacement of *iterable*.
4104 | """
4105 | element = tuple(element)
4106 | l = len(element)
4107 | element = enumerate(element)
4108 |
4109 | k, y = next(element, (None, None))
4110 | if k is None:
4111 | return 0
4112 |
4113 | indexes = []
4114 | pool = tuple(iterable)
4115 | for n, x in enumerate(pool):
4116 | while x == y:
4117 | indexes.append(n)
4118 | tmp, y = next(element, (None, None))
4119 | if tmp is None:
4120 | break
4121 | else:
4122 | k = tmp
4123 | if y is None:
4124 | break
4125 | else:
4126 | raise ValueError(
4127 | 'element is not a combination with replacement of iterable'
4128 | )
4129 |
4130 | n = len(pool)
4131 | occupations = [0] * n
4132 | for p in indexes:
4133 | occupations[p] += 1
4134 |
4135 | index = 0
4136 | cumulative_sum = 0
4137 | for k in range(1, n):
4138 | cumulative_sum += occupations[k - 1]
4139 | j = l + n - 1 - k - cumulative_sum
4140 | i = n - k
4141 | if i <= j:
4142 | index += comb(j, i)
4143 |
4144 | return index
4145 |
4146 |
4147 | def permutation_index(element, iterable):
4148 | """Equivalent to ``list(permutations(iterable, r)).index(element)```
4149 |
4150 | The subsequences of *iterable* that are of length *r* where order is
4151 | important can be ordered lexicographically. :func:`permutation_index`
4152 | computes the index of the first *element* directly, without computing
4153 | the previous permutations.
4154 |
4155 | >>> permutation_index([1, 3, 2], range(5))
4156 | 19
4157 |
4158 | ``ValueError`` will be raised if the given *element* isn't one of the
4159 | permutations of *iterable*.
4160 | """
4161 | index = 0
4162 | pool = list(iterable)
4163 | for i, x in zip(range(len(pool), -1, -1), element):
4164 | r = pool.index(x)
4165 | index = index * i + r
4166 | del pool[r]
4167 |
4168 | return index
4169 |
4170 |
4171 | class countable:
4172 | """Wrap *iterable* and keep a count of how many items have been consumed.
4173 |
4174 | The ``items_seen`` attribute starts at ``0`` and increments as the iterable
4175 | is consumed:
4176 |
4177 | >>> iterable = map(str, range(10))
4178 | >>> it = countable(iterable)
4179 | >>> it.items_seen
4180 | 0
4181 | >>> next(it), next(it)
4182 | ('0', '1')
4183 | >>> list(it)
4184 | ['2', '3', '4', '5', '6', '7', '8', '9']
4185 | >>> it.items_seen
4186 | 10
4187 | """
4188 |
4189 | def __init__(self, iterable):
4190 | self._it = iter(iterable)
4191 | self.items_seen = 0
4192 |
4193 | def __iter__(self):
4194 | return self
4195 |
4196 | def __next__(self):
4197 | item = next(self._it)
4198 | self.items_seen += 1
4199 |
4200 | return item
4201 |
4202 |
4203 | def chunked_even(iterable, n):
4204 | """Break *iterable* into lists of approximately length *n*.
4205 | Items are distributed such the lengths of the lists differ by at most
4206 | 1 item.
4207 |
4208 | >>> iterable = [1, 2, 3, 4, 5, 6, 7]
4209 | >>> n = 3
4210 | >>> list(chunked_even(iterable, n)) # List lengths: 3, 2, 2
4211 | [[1, 2, 3], [4, 5], [6, 7]]
4212 | >>> list(chunked(iterable, n)) # List lengths: 3, 3, 1
4213 | [[1, 2, 3], [4, 5, 6], [7]]
4214 |
4215 | """
4216 | iterable = iter(iterable)
4217 |
4218 | # Initialize a buffer to process the chunks while keeping
4219 | # some back to fill any underfilled chunks
4220 | min_buffer = (n - 1) * (n - 2)
4221 | buffer = list(islice(iterable, min_buffer))
4222 |
4223 | # Append items until we have a completed chunk
4224 | for _ in islice(map(buffer.append, iterable), n, None, n):
4225 | yield buffer[:n]
4226 | del buffer[:n]
4227 |
4228 | # Check if any chunks need addition processing
4229 | if not buffer:
4230 | return
4231 | length = len(buffer)
4232 |
4233 | # Chunks are either size `full_size <= n` or `partial_size = full_size - 1`
4234 | q, r = divmod(length, n)
4235 | num_lists = q + (1 if r > 0 else 0)
4236 | q, r = divmod(length, num_lists)
4237 | full_size = q + (1 if r > 0 else 0)
4238 | partial_size = full_size - 1
4239 | num_full = length - partial_size * num_lists
4240 |
4241 | # Yield chunks of full size
4242 | partial_start_idx = num_full * full_size
4243 | if full_size > 0:
4244 | for i in range(0, partial_start_idx, full_size):
4245 | yield buffer[i : i + full_size]
4246 |
4247 | # Yield chunks of partial size
4248 | if partial_size > 0:
4249 | for i in range(partial_start_idx, length, partial_size):
4250 | yield buffer[i : i + partial_size]
4251 |
4252 |
4253 | def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False):
4254 | """A version of :func:`zip` that "broadcasts" any scalar
4255 | (i.e., non-iterable) items into output tuples.
4256 |
4257 | >>> iterable_1 = [1, 2, 3]
4258 | >>> iterable_2 = ['a', 'b', 'c']
4259 | >>> scalar = '_'
4260 | >>> list(zip_broadcast(iterable_1, iterable_2, scalar))
4261 | [(1, 'a', '_'), (2, 'b', '_'), (3, 'c', '_')]
4262 |
4263 | The *scalar_types* keyword argument determines what types are considered
4264 | scalar. It is set to ``(str, bytes)`` by default. Set it to ``None`` to
4265 | treat strings and byte strings as iterable:
4266 |
4267 | >>> list(zip_broadcast('abc', 0, 'xyz', scalar_types=None))
4268 | [('a', 0, 'x'), ('b', 0, 'y'), ('c', 0, 'z')]
4269 |
4270 | If the *strict* keyword argument is ``True``, then
4271 | ``UnequalIterablesError`` will be raised if any of the iterables have
4272 | different lengths.
4273 | """
4274 |
4275 | def is_scalar(obj):
4276 | if scalar_types and isinstance(obj, scalar_types):
4277 | return True
4278 | try:
4279 | iter(obj)
4280 | except TypeError:
4281 | return True
4282 | else:
4283 | return False
4284 |
4285 | size = len(objects)
4286 | if not size:
4287 | return
4288 |
4289 | new_item = [None] * size
4290 | iterables, iterable_positions = [], []
4291 | for i, obj in enumerate(objects):
4292 | if is_scalar(obj):
4293 | new_item[i] = obj
4294 | else:
4295 | iterables.append(iter(obj))
4296 | iterable_positions.append(i)
4297 |
4298 | if not iterables:
4299 | yield tuple(objects)
4300 | return
4301 |
4302 | zipper = _zip_equal if strict else zip
4303 | for item in zipper(*iterables):
4304 | for i, new_item[i] in zip(iterable_positions, item):
4305 | pass
4306 | yield tuple(new_item)
4307 |
4308 |
4309 | def unique_in_window(iterable, n, key=None):
4310 | """Yield the items from *iterable* that haven't been seen recently.
4311 | *n* is the size of the lookback window.
4312 |
4313 | >>> iterable = [0, 1, 0, 2, 3, 0]
4314 | >>> n = 3
4315 | >>> list(unique_in_window(iterable, n))
4316 | [0, 1, 2, 3, 0]
4317 |
4318 | The *key* function, if provided, will be used to determine uniqueness:
4319 |
4320 | >>> list(unique_in_window('abAcda', 3, key=lambda x: x.lower()))
4321 | ['a', 'b', 'c', 'd', 'a']
4322 |
4323 | The items in *iterable* must be hashable.
4324 |
4325 | """
4326 | if n <= 0:
4327 | raise ValueError('n must be greater than 0')
4328 |
4329 | window = deque(maxlen=n)
4330 | counts = defaultdict(int)
4331 | use_key = key is not None
4332 |
4333 | for item in iterable:
4334 | if len(window) == n:
4335 | to_discard = window[0]
4336 | if counts[to_discard] == 1:
4337 | del counts[to_discard]
4338 | else:
4339 | counts[to_discard] -= 1
4340 |
4341 | k = key(item) if use_key else item
4342 | if k not in counts:
4343 | yield item
4344 | counts[k] += 1
4345 | window.append(k)
4346 |
4347 |
4348 | def duplicates_everseen(iterable, key=None):
4349 | """Yield duplicate elements after their first appearance.
4350 |
4351 | >>> list(duplicates_everseen('mississippi'))
4352 | ['s', 'i', 's', 's', 'i', 'p', 'i']
4353 | >>> list(duplicates_everseen('AaaBbbCccAaa', str.lower))
4354 | ['a', 'a', 'b', 'b', 'c', 'c', 'A', 'a', 'a']
4355 |
4356 | This function is analogous to :func:`unique_everseen` and is subject to
4357 | the same performance considerations.
4358 |
4359 | """
4360 | seen_set = set()
4361 | seen_list = []
4362 | use_key = key is not None
4363 |
4364 | for element in iterable:
4365 | k = key(element) if use_key else element
4366 | try:
4367 | if k not in seen_set:
4368 | seen_set.add(k)
4369 | else:
4370 | yield element
4371 | except TypeError:
4372 | if k not in seen_list:
4373 | seen_list.append(k)
4374 | else:
4375 | yield element
4376 |
4377 |
4378 | def duplicates_justseen(iterable, key=None):
4379 | """Yields serially-duplicate elements after their first appearance.
4380 |
4381 | >>> list(duplicates_justseen('mississippi'))
4382 | ['s', 's', 'p']
4383 | >>> list(duplicates_justseen('AaaBbbCccAaa', str.lower))
4384 | ['a', 'a', 'b', 'b', 'c', 'c', 'a', 'a']
4385 |
4386 | This function is analogous to :func:`unique_justseen`.
4387 |
4388 | """
4389 | return flatten(g for _, g in groupby(iterable, key) for _ in g)
4390 |
4391 |
4392 | def classify_unique(iterable, key=None):
4393 | """Classify each element in terms of its uniqueness.
4394 |
4395 | For each element in the input iterable, return a 3-tuple consisting of:
4396 |
4397 | 1. The element itself
4398 | 2. ``False`` if the element is equal to the one preceding it in the input,
4399 | ``True`` otherwise (i.e. the equivalent of :func:`unique_justseen`)
4400 | 3. ``False`` if this element has been seen anywhere in the input before,
4401 | ``True`` otherwise (i.e. the equivalent of :func:`unique_everseen`)
4402 |
4403 | >>> list(classify_unique('otto')) # doctest: +NORMALIZE_WHITESPACE
4404 | [('o', True, True),
4405 | ('t', True, True),
4406 | ('t', False, False),
4407 | ('o', True, False)]
4408 |
4409 | This function is analogous to :func:`unique_everseen` and is subject to
4410 | the same performance considerations.
4411 |
4412 | """
4413 | seen_set = set()
4414 | seen_list = []
4415 | use_key = key is not None
4416 | previous = None
4417 |
4418 | for i, element in enumerate(iterable):
4419 | k = key(element) if use_key else element
4420 | is_unique_justseen = not i or previous != k
4421 | previous = k
4422 | is_unique_everseen = False
4423 | try:
4424 | if k not in seen_set:
4425 | seen_set.add(k)
4426 | is_unique_everseen = True
4427 | except TypeError:
4428 | if k not in seen_list:
4429 | seen_list.append(k)
4430 | is_unique_everseen = True
4431 | yield element, is_unique_justseen, is_unique_everseen
4432 |
4433 |
4434 | def minmax(iterable_or_value, *others, key=None, default=_marker):
4435 | """Returns both the smallest and largest items in an iterable
4436 | or the largest of two or more arguments.
4437 |
4438 | >>> minmax([3, 1, 5])
4439 | (1, 5)
4440 |
4441 | >>> minmax(4, 2, 6)
4442 | (2, 6)
4443 |
4444 | If a *key* function is provided, it will be used to transform the input
4445 | items for comparison.
4446 |
4447 | >>> minmax([5, 30], key=str) # '30' sorts before '5'
4448 | (30, 5)
4449 |
4450 | If a *default* value is provided, it will be returned if there are no
4451 | input items.
4452 |
4453 | >>> minmax([], default=(0, 0))
4454 | (0, 0)
4455 |
4456 | Otherwise ``ValueError`` is raised.
4457 |
4458 | This function is based on the
4459 | `recipe <http://code.activestate.com/recipes/577916/>`__ by
4460 | Raymond Hettinger and takes care to minimize the number of comparisons
4461 | performed.
4462 | """
4463 | iterable = (iterable_or_value, *others) if others else iterable_or_value
4464 |
4465 | it = iter(iterable)
4466 |
4467 | try:
4468 | lo = hi = next(it)
4469 | except StopIteration as exc:
4470 | if default is _marker:
4471 | raise ValueError(
4472 | '`minmax()` argument is an empty iterable. '
4473 | 'Provide a `default` value to suppress this error.'
4474 | ) from exc
4475 | return default
4476 |
4477 | # Different branches depending on the presence of key. This saves a lot
4478 | # of unimportant copies which would slow the "key=None" branch
4479 | # significantly down.
4480 | if key is None:
4481 | for x, y in zip_longest(it, it, fillvalue=lo):
4482 | if y < x:
4483 | x, y = y, x
4484 | if x < lo:
4485 | lo = x
4486 | if hi < y:
4487 | hi = y
4488 |
4489 | else:
4490 | lo_key = hi_key = key(lo)
4491 |
4492 | for x, y in zip_longest(it, it, fillvalue=lo):
4493 | x_key, y_key = key(x), key(y)
4494 |
4495 | if y_key < x_key:
4496 | x, y, x_key, y_key = y, x, y_key, x_key
4497 | if x_key < lo_key:
4498 | lo, lo_key = x, x_key
4499 | if hi_key < y_key:
4500 | hi, hi_key = y, y_key
4501 |
4502 | return lo, hi
4503 |
4504 |
4505 | def constrained_batches(
4506 | iterable, max_size, max_count=None, get_len=len, strict=True
4507 | ):
4508 | """Yield batches of items from *iterable* with a combined size limited by
4509 | *max_size*.
4510 |
4511 | >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
4512 | >>> list(constrained_batches(iterable, 10))
4513 | [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')]
4514 |
4515 | If a *max_count* is supplied, the number of items per batch is also
4516 | limited:
4517 |
4518 | >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']
4519 | >>> list(constrained_batches(iterable, 10, max_count = 2))
4520 | [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)]
4521 |
4522 | If a *get_len* function is supplied, use that instead of :func:`len` to
4523 | determine item size.
4524 |
4525 | If *strict* is ``True``, raise ``ValueError`` if any single item is bigger
4526 | than *max_size*. Otherwise, allow single items to exceed *max_size*.
4527 | """
4528 | if max_size <= 0:
4529 | raise ValueError('maximum size must be greater than zero')
4530 |
4531 | batch = []
4532 | batch_size = 0
4533 | batch_count = 0
4534 | for item in iterable:
4535 | item_len = get_len(item)
4536 | if strict and item_len > max_size:
4537 | raise ValueError('item size exceeds maximum size')
4538 |
4539 | reached_count = batch_count == max_count
4540 | reached_size = item_len + batch_size > max_size
4541 | if batch_count and (reached_size or reached_count):
4542 | yield tuple(batch)
4543 | batch.clear()
4544 | batch_size = 0
4545 | batch_count = 0
4546 |
4547 | batch.append(item)
4548 | batch_size += item_len
4549 | batch_count += 1
4550 |
4551 | if batch:
4552 | yield tuple(batch)
4553 |
4554 |
4555 | def gray_product(*iterables):
4556 | """Like :func:`itertools.product`, but return tuples in an order such
4557 | that only one element in the generated tuple changes from one iteration
4558 | to the next.
4559 |
4560 | >>> list(gray_product('AB','CD'))
4561 | [('A', 'C'), ('B', 'C'), ('B', 'D'), ('A', 'D')]
4562 |
4563 | This function consumes all of the input iterables before producing output.
4564 | If any of the input iterables have fewer than two items, ``ValueError``
4565 | is raised.
4566 |
4567 | For information on the algorithm, see
4568 | `this section <https://www-cs-faculty.stanford.edu/~knuth/fasc2a.ps.gz>`__
4569 | of Donald Knuth's *The Art of Computer Programming*.
4570 | """
4571 | all_iterables = tuple(tuple(x) for x in iterables)
4572 | iterable_count = len(all_iterables)
4573 | for iterable in all_iterables:
4574 | if len(iterable) < 2:
4575 | raise ValueError("each iterable must have two or more items")
4576 |
4577 | # This is based on "Algorithm H" from section 7.2.1.1, page 20.
4578 | # a holds the indexes of the source iterables for the n-tuple to be yielded
4579 | # f is the array of "focus pointers"
4580 | # o is the array of "directions"
4581 | a = [0] * iterable_count
4582 | f = list(range(iterable_count + 1))
4583 | o = [1] * iterable_count
4584 | while True:
4585 | yield tuple(all_iterables[i][a[i]] for i in range(iterable_count))
4586 | j = f[0]
4587 | f[0] = 0
4588 | if j == iterable_count:
4589 | break
4590 | a[j] = a[j] + o[j]
4591 | if a[j] == 0 or a[j] == len(all_iterables[j]) - 1:
4592 | o[j] = -o[j]
4593 | f[j] = f[j + 1]
4594 | f[j + 1] = j + 1
4595 |
4596 |
4597 | def partial_product(*iterables):
4598 | """Yields tuples containing one item from each iterator, with subsequent
4599 | tuples changing a single item at a time by advancing each iterator until it
4600 | is exhausted. This sequence guarantees every value in each iterable is
4601 | output at least once without generating all possible combinations.
4602 |
4603 | This may be useful, for example, when testing an expensive function.
4604 |
4605 | >>> list(partial_product('AB', 'C', 'DEF'))
4606 | [('A', 'C', 'D'), ('B', 'C', 'D'), ('B', 'C', 'E'), ('B', 'C', 'F')]
4607 | """
4608 |
4609 | iterators = list(map(iter, iterables))
4610 |
4611 | try:
4612 | prod = [next(it) for it in iterators]
4613 | except StopIteration:
4614 | return
4615 | yield tuple(prod)
4616 |
4617 | for i, it in enumerate(iterators):
4618 | for prod[i] in it:
4619 | yield tuple(prod)
4620 |
4621 |
4622 | def takewhile_inclusive(predicate, iterable):
4623 | """A variant of :func:`takewhile` that yields one additional element.
4624 |
4625 | >>> list(takewhile_inclusive(lambda x: x < 5, [1, 4, 6, 4, 1]))
4626 | [1, 4, 6]
4627 |
4628 | :func:`takewhile` would return ``[1, 4]``.
4629 | """
4630 | for x in iterable:
4631 | yield x
4632 | if not predicate(x):
4633 | break
4634 |
4635 |
4636 | def outer_product(func, xs, ys, *args, **kwargs):
4637 | """A generalized outer product that applies a binary function to all
4638 | pairs of items. Returns a 2D matrix with ``len(xs)`` rows and ``len(ys)``
4639 | columns.
4640 | Also accepts ``*args`` and ``**kwargs`` that are passed to ``func``.
4641 |
4642 | Multiplication table:
4643 |
4644 | >>> list(outer_product(mul, range(1, 4), range(1, 6)))
4645 | [(1, 2, 3, 4, 5), (2, 4, 6, 8, 10), (3, 6, 9, 12, 15)]
4646 |
4647 | Cross tabulation:
4648 |
4649 | >>> xs = ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B']
4650 | >>> ys = ['X', 'X', 'X', 'Y', 'Z', 'Z', 'Y', 'Y', 'Z', 'Z']
4651 | >>> rows = list(zip(xs, ys))
4652 | >>> count_rows = lambda x, y: rows.count((x, y))
4653 | >>> list(outer_product(count_rows, sorted(set(xs)), sorted(set(ys))))
4654 | [(2, 3, 0), (1, 0, 4)]
4655 |
4656 | Usage with ``*args`` and ``**kwargs``:
4657 |
4658 | >>> animals = ['cat', 'wolf', 'mouse']
4659 | >>> list(outer_product(min, animals, animals, key=len))
4660 | [('cat', 'cat', 'cat'), ('cat', 'wolf', 'wolf'), ('cat', 'wolf', 'mouse')]
4661 | """
4662 | ys = tuple(ys)
4663 | return batched(
4664 | starmap(lambda x, y: func(x, y, *args, **kwargs), product(xs, ys)),
4665 | n=len(ys),
4666 | )
4667 |
4668 |
4669 | def iter_suppress(iterable, *exceptions):
4670 | """Yield each of the items from *iterable*. If the iteration raises one of
4671 | the specified *exceptions*, that exception will be suppressed and iteration
4672 | will stop.
4673 |
4674 | >>> from itertools import chain
4675 | >>> def breaks_at_five(x):
4676 | ... while True:
4677 | ... if x >= 5:
4678 | ... raise RuntimeError
4679 | ... yield x
4680 | ... x += 1
4681 | >>> it_1 = iter_suppress(breaks_at_five(1), RuntimeError)
4682 | >>> it_2 = iter_suppress(breaks_at_five(2), RuntimeError)
4683 | >>> list(chain(it_1, it_2))
4684 | [1, 2, 3, 4, 2, 3, 4]
4685 | """
4686 | try:
4687 | yield from iterable
4688 | except exceptions:
4689 | return
4690 |
4691 |
4692 | def filter_map(func, iterable):
4693 | """Apply *func* to every element of *iterable*, yielding only those which
4694 | are not ``None``.
4695 |
4696 | >>> elems = ['1', 'a', '2', 'b', '3']
4697 | >>> list(filter_map(lambda s: int(s) if s.isnumeric() else None, elems))
4698 | [1, 2, 3]
4699 | """
4700 | for x in iterable:
4701 | y = func(x)
4702 | if y is not None:
4703 | yield y
4704 |
4705 |
4706 | def powerset_of_sets(iterable):
4707 | """Yields all possible subsets of the iterable.
4708 |
4709 | >>> list(powerset_of_sets([1, 2, 3])) # doctest: +SKIP
4710 | [set(), {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}]
4711 | >>> list(powerset_of_sets([1, 1, 0])) # doctest: +SKIP
4712 | [set(), {1}, {0}, {0, 1}]
4713 |
4714 | :func:`powerset_of_sets` takes care to minimize the number
4715 | of hash operations performed.
4716 | """
4717 | sets = tuple(map(set, dict.fromkeys(map(frozenset, zip(iterable)))))
4718 | for r in range(len(sets) + 1):
4719 | yield from starmap(set().union, combinations(sets, r))
4720 |
4721 |
4722 | def join_mappings(**field_to_map):
4723 | """
4724 | Joins multiple mappings together using their common keys.
4725 |
4726 | >>> user_scores = {'elliot': 50, 'claris': 60}
4727 | >>> user_times = {'elliot': 30, 'claris': 40}
4728 | >>> join_mappings(score=user_scores, time=user_times)
4729 | {'elliot': {'score': 50, 'time': 30}, 'claris': {'score': 60, 'time': 40}}
4730 | """
4731 | ret = defaultdict(dict)
4732 |
4733 | for field_name, mapping in field_to_map.items():
4734 | for key, value in mapping.items():
4735 | ret[key][field_name] = value
4736 |
4737 | return dict(ret)
4738 |
4739 |
4740 | def _complex_sumprod(v1, v2):
4741 | """High precision sumprod() for complex numbers.
4742 | Used by :func:`dft` and :func:`idft`.
4743 | """
4744 |
4745 | r1 = chain((p.real for p in v1), (-p.imag for p in v1))
4746 | r2 = chain((q.real for q in v2), (q.imag for q in v2))
4747 | i1 = chain((p.real for p in v1), (p.imag for p in v1))
4748 | i2 = chain((q.imag for q in v2), (q.real for q in v2))
4749 | return complex(_fsumprod(r1, r2), _fsumprod(i1, i2))
4750 |
4751 |
4752 | def dft(xarr):
4753 | """Discrete Fourier Tranform. *xarr* is a sequence of complex numbers.
4754 | Yields the components of the corresponding transformed output vector.
4755 |
4756 | >>> import cmath
4757 | >>> xarr = [1, 2-1j, -1j, -1+2j]
4758 | >>> Xarr = [2, -2-2j, -2j, 4+4j]
4759 | >>> all(map(cmath.isclose, dft(xarr), Xarr))
4760 | True
4761 |
4762 | See :func:`idft` for the inverse Discrete Fourier Transform.
4763 | """
4764 | N = len(xarr)
4765 | roots_of_unity = [e ** (n / N * tau * -1j) for n in range(N)]
4766 | for k in range(N):
4767 | coeffs = [roots_of_unity[k * n % N] for n in range(N)]
4768 | yield _complex_sumprod(xarr, coeffs)
4769 |
4770 |
4771 | def idft(Xarr):
4772 | """Inverse Discrete Fourier Tranform. *Xarr* is a sequence of
4773 | complex numbers. Yields the components of the corresponding
4774 | inverse-transformed output vector.
4775 |
4776 | >>> import cmath
4777 | >>> xarr = [1, 2-1j, -1j, -1+2j]
4778 | >>> Xarr = [2, -2-2j, -2j, 4+4j]
4779 | >>> all(map(cmath.isclose, idft(Xarr), xarr))
4780 | True
4781 |
4782 | See :func:`dft` for the Discrete Fourier Transform.
4783 | """
4784 | N = len(Xarr)
4785 | roots_of_unity = [e ** (n / N * tau * 1j) for n in range(N)]
4786 | for k in range(N):
4787 | coeffs = [roots_of_unity[k * n % N] for n in range(N)]
4788 | yield _complex_sumprod(Xarr, coeffs) / N
4789 |
4790 |
4791 | def doublestarmap(func, iterable):
4792 | """Apply *func* to every item of *iterable* by dictionary unpacking
4793 | the item into *func*.
4794 |
4795 | The difference between :func:`itertools.starmap` and :func:`doublestarmap`
4796 | parallels the distinction between ``func(*a)`` and ``func(**a)``.
4797 |
4798 | >>> iterable = [{'a': 1, 'b': 2}, {'a': 40, 'b': 60}]
4799 | >>> list(doublestarmap(lambda a, b: a + b, iterable))
4800 | [3, 100]
4801 |
4802 | ``TypeError`` will be raised if *func*'s signature doesn't match the
4803 | mapping contained in *iterable* or if *iterable* does not contain mappings.
4804 | """
4805 | for item in iterable:
4806 | yield func(**item)
4807 |
```