# flexible-matchers
[](https://badge.fury.io/py/flexible-matchers)
[](https://pypi.org/project/flexible-matchers/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/psf/black)
[](https://pycqa.github.io/isort/)
[](https://github.com/astral-sh/ruff)
[](https://github.com/skippdot/flexible-matchers/actions)
[](https://codecov.io/gh/skippdot/flexible-matchers)
**Lightweight, zero-dependency mock assertion helpers with flexible numeric and string matching.**
`flexible-matchers` provides intuitive matcher objects for use with Python's `unittest.mock` and general test assertions. Unlike other matcher libraries, it uses Python's native equality operators, making it work seamlessly with standard assertions and mock calls.
## โจ Key Features
- **๐ฏ Zero Dependencies** - No external packages required
- **๏ฟฝ๏ฟฝ Numeric Matchers** - Range-based and tolerance-based number matching
- **๐ String Matchers** - Flexible length constraints for string validation
- **๐ Collection Matchers** - List validation with length constraints
- **๐ซ None Handling** - Special matcher for non-None values
- **๐ Pythonic API** - Uses standard `==` operator, works with any assertion library
- **โก Lightweight** - Simple, focused implementation
- **๐งช Well-Tested** - Comprehensive test suite with 100% coverage
- **๐ฆ Type-Hinted** - Full type annotations for better IDE support
## ๐ Installation
```bash
pip install flexible-matchers
```
## ๐ Quick Start
```python
from flexible_matchers import NUMBER, STRING, IS_NUMBER, ANY_NOT_NONE
# In mock assertions
mock_api.assert_called_with(
user_id=NUMBER(min_value=1),
name=STRING(min_length=1),
age=NUMBER(min_value=0, max_value=150)
)
# In data structure comparisons
response = {"id": 123, "name": "Alice", "created_at": "2024-01-01"}
assert response == {
"id": IS_NUMBER,
"name": STRING(min_length=1),
"created_at": ANY_NOT_NONE
}
```
## ๐ Documentation
### NUMBER
Matches numeric values (int or float) with optional min/max constraints.
```python
from flexible_matchers import NUMBER, IS_NUMBER
# Match any number
assert 42 == NUMBER()
assert 3.14 == NUMBER()
# Match with minimum value
assert 42 == NUMBER(min_value=0)
assert -5 != NUMBER(min_value=0)
# Match with maximum value
assert 42 == NUMBER(max_value=100)
assert 150 != NUMBER(max_value=100)
# Match within range
assert 50 == NUMBER(min_value=0, max_value=100)
assert -1 != NUMBER(min_value=0, max_value=100)
# Pre-instantiated matcher for any number
assert 42 == IS_NUMBER
```
### CLOSE_NUMBER
Matches numbers within a tolerance range (useful for floating-point comparisons).
```python
from flexible_matchers import CLOSE_NUMBER
# Default tolerance of 0.5
assert 42.3 == CLOSE_NUMBER(42)
assert 41.7 == CLOSE_NUMBER(42)
assert 42.6 != CLOSE_NUMBER(42)
# Custom tolerance
assert 3.14 == CLOSE_NUMBER(3.1, tolerance=0.1)
assert 100 == CLOSE_NUMBER(99, tolerance=1)
```
**Unique Feature**: Unlike other libraries, `CLOSE_NUMBER` provides tolerance-based matching which is essential for floating-point comparisons in scientific computing and financial applications.
### STRING
Matches strings with optional length constraints.
```python
from flexible_matchers import STRING, IS_STRING
# Match any string
assert "hello" == STRING()
assert "" == STRING()
# Match exact length
assert "hello" == STRING(length=5)
assert "hi" != STRING(length=5)
# Match minimum length
assert "hello" == STRING(min_length=3)
assert "hi" != STRING(min_length=3)
# Match maximum length
assert "hello" == STRING(max_length=10)
assert "very long string" != STRING(max_length=10)
# Match length range
assert "hello" == STRING(min_length=3, max_length=10)
# Pre-instantiated matcher for any string
assert "hello" == IS_STRING
```
**Unique Feature**: Flexible string length constraints (`min_length`, `max_length`) are not available in most other matcher libraries.
### LIST
Matches lists with optional length constraint.
```python
from flexible_matchers import LIST, IS_LIST
# Match any list
assert [1, 2, 3] == LIST()
assert [] == LIST()
# Match exact length
assert [1, 2, 3] == LIST(3)
assert [1, 2] != LIST(3)
# Pre-instantiated matcher for any list
assert [1, 2, 3] == IS_LIST
```
### ANY_NOT_NONE
Matches any value except `None`.
```python
from flexible_matchers import ANY_NOT_NONE
# Matches any non-None value
assert 42 == ANY_NOT_NONE
assert "hello" == ANY_NOT_NONE
assert [] == ANY_NOT_NONE
assert 0 == ANY_NOT_NONE
assert False == ANY_NOT_NONE
# Does not match None
assert None != ANY_NOT_NONE
```
**Use Case**: Perfect for API responses where you want to ensure a field exists but don't care about its specific value.
## ๐ Comparison with Other Libraries
### vs. unittest.mock.ANY
```python
from unittest.mock import ANY
from flexible_matchers import NUMBER, STRING
# unittest.mock.ANY - too permissive
assert {"age": -100} == {"age": ANY} # Passes, but age is invalid!
# flexible-matchers - precise validation
assert {"age": 30} == {"age": NUMBER(min_value=0, max_value=150)} # โ
assert {"age": -100} == {"age": NUMBER(min_value=0, max_value=150)} # โ
```
### vs. PyHamcrest
```python
# PyHamcrest - requires special syntax
from hamcrest import assert_that, instance_of, greater_than
assert_that(value, instance_of(int))
assert_that(value, greater_than(0))
# flexible-matchers - natural Python syntax
from flexible_matchers import NUMBER
assert value == NUMBER(min_value=0)
```
### vs. dirty-equals
```python
# dirty-equals - close, but missing key features
from dirty_equals import IsPositiveInt
assert 42 == IsPositiveInt
# flexible-matchers - more flexible with ranges and tolerance
from flexible_matchers import NUMBER, CLOSE_NUMBER
assert 42 == NUMBER(min_value=0, max_value=100)
assert 3.14 == CLOSE_NUMBER(3.1, tolerance=0.1) # Not available in dirty-equals
```
### vs. pychoir
```python
# pychoir - similar approach, but less intuitive
from pychoir import LessThan, GreaterThan, And
assert value == And(GreaterThan(0), LessThan(100))
# flexible-matchers - simpler, more intuitive
from flexible_matchers import NUMBER
assert value == NUMBER(min_value=0, max_value=100)
```
## ๐ฏ Real-World Examples
### API Testing
```python
from flexible_matchers import NUMBER, STRING, ANY_NOT_NONE
def test_create_user_api():
response = api.create_user(name="Alice", email="alice@example.com")
assert response == {
"id": NUMBER(min_value=1),
"name": STRING(min_length=1, max_length=100),
"email": STRING(min_length=5),
"created_at": ANY_NOT_NONE,
"updated_at": ANY_NOT_NONE,
"is_active": True,
}
```
### Mock Assertions
```python
from unittest.mock import Mock
from flexible_matchers import NUMBER, STRING
def test_user_service():
mock_db = Mock()
service = UserService(mock_db)
service.create_user(name="Alice", age=30)
mock_db.insert.assert_called_once_with(
table="users",
data={
"name": STRING(min_length=1),
"age": NUMBER(min_value=0, max_value=150),
"created_at": ANY_NOT_NONE,
}
)
```
### Nested Data Structures
```python
from flexible_matchers import NUMBER, STRING, LIST, IS_NUMBER
def test_complex_response():
response = {
"users": [
{"id": 1, "name": "Alice", "scores": [95, 87, 92]},
{"id": 2, "name": "Bob", "scores": [88, 91, 85]},
],
"total": 2,
"page": 1,
}
assert response == {
"users": [
{
"id": IS_NUMBER,
"name": STRING(min_length=1),
"scores": LIST(3),
},
{
"id": IS_NUMBER,
"name": STRING(min_length=1),
"scores": LIST(3),
},
],
"total": NUMBER(min_value=0),
"page": NUMBER(min_value=1),
}
```
### Floating-Point Comparisons
```python
from flexible_matchers import CLOSE_NUMBER
def test_scientific_calculation():
result = calculate_pi()
assert result == CLOSE_NUMBER(3.14159, tolerance=0.00001)
def test_financial_calculation():
total = calculate_total([10.10, 20.20, 30.30])
assert total == CLOSE_NUMBER(60.60, tolerance=0.01)
```
## ๐ ๏ธ Development
### Setup
```bash
# Clone the repository
git clone https://github.com/skippdot/flexible-matchers.git
cd flexible-matchers
# Install development dependencies
pip install -e ".[dev]"
```
### Running Tests
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=flexible_matchers --cov-report=html
# Run specific test file
pytest tests/test_matchers.py
# Run specific test
pytest tests/test_matchers.py::TestNUMBER::test_range
```
### Code Quality
```bash
# Format code
black src tests
isort src tests
# Lint code
ruff check src tests
flake8 src tests
pylint src tests
# Type checking
mypy src
```
### Running All Checks
```bash
# Format
black src tests && isort src tests
# Lint
ruff check src tests && flake8 src tests && pylint src tests
# Test
pytest --cov=flexible_matchers --cov-report=term-missing
# Type check
mypy src
```
## ๐ Requirements
- Python >= 3.7
- No runtime dependencies!
## ๐ License
MIT License - see [LICENSE](LICENSE) file for details.
## ๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## ๐ Acknowledgments
Inspired by:
- [pychoir](https://github.com/kajaste/pychoir) - Modern matcher library
- [dirty-equals](https://github.com/samuelcolvin/dirty-equals) - Flexible equality testing
- [PyHamcrest](https://github.com/hamcrest/PyHamcrest) - Mature matcher framework
- [callee](https://github.com/Xion/callee) - Argument matchers (now abandoned)
## ๐ Project Stats
- **Zero Dependencies**: No external packages required
- **100% Test Coverage**: Comprehensive test suite
- **Type Hinted**: Full type annotations
- **Python 3.7+**: Modern Python support
- **Active Maintenance**: Regular updates and improvements
## ๐ Links
- **PyPI**: https://pypi.org/project/flexible-matchers/
- **GitHub**: https://github.com/skippdot/flexible-matchers
- **Issues**: https://github.com/skippdot/flexible-matchers/issues
- **Changelog**: https://github.com/skippdot/flexible-matchers/releases
---
Made with โค๏ธ by [Stepan Shamaiev](https://github.com/skippdot)
Raw data
{
"_id": null,
"home_page": null,
"name": "flexible-matchers",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "testing, mock, unittest, pytest, matchers, assertions, test-helpers",
"author": null,
"author_email": "Stepan Shamaiev <59963936+skippdot@users.noreply.github.com>",
"download_url": "https://files.pythonhosted.org/packages/fa/d9/7c7c38533e471c8485815b238cde526c2587c6a7f19c9dd8484aa22403de/flexible_matchers-0.1.0.tar.gz",
"platform": null,
"description": "# flexible-matchers\n\n[](https://badge.fury.io/py/flexible-matchers)\n[](https://pypi.org/project/flexible-matchers/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/psf/black)\n[](https://pycqa.github.io/isort/)\n[](https://github.com/astral-sh/ruff)\n[](https://github.com/skippdot/flexible-matchers/actions)\n[](https://codecov.io/gh/skippdot/flexible-matchers)\n\n**Lightweight, zero-dependency mock assertion helpers with flexible numeric and string matching.**\n\n`flexible-matchers` provides intuitive matcher objects for use with Python's `unittest.mock` and general test assertions. Unlike other matcher libraries, it uses Python's native equality operators, making it work seamlessly with standard assertions and mock calls.\n\n## \u2728 Key Features\n\n- **\ud83c\udfaf Zero Dependencies** - No external packages required\n- **\ufffd\ufffd Numeric Matchers** - Range-based and tolerance-based number matching\n- **\ud83d\udcdd String Matchers** - Flexible length constraints for string validation\n- **\ud83d\udccb Collection Matchers** - List validation with length constraints\n- **\ud83d\udeab None Handling** - Special matcher for non-None values\n- **\ud83d\udc0d Pythonic API** - Uses standard `==` operator, works with any assertion library\n- **\u26a1 Lightweight** - Simple, focused implementation\n- **\ud83e\uddea Well-Tested** - Comprehensive test suite with 100% coverage\n- **\ud83d\udce6 Type-Hinted** - Full type annotations for better IDE support\n\n## \ud83d\ude80 Installation\n\n```bash\npip install flexible-matchers\n```\n\n## \ud83d\udcd6 Quick Start\n\n```python\nfrom flexible_matchers import NUMBER, STRING, IS_NUMBER, ANY_NOT_NONE\n\n# In mock assertions\nmock_api.assert_called_with(\n user_id=NUMBER(min_value=1),\n name=STRING(min_length=1),\n age=NUMBER(min_value=0, max_value=150)\n)\n\n# In data structure comparisons\nresponse = {\"id\": 123, \"name\": \"Alice\", \"created_at\": \"2024-01-01\"}\nassert response == {\n \"id\": IS_NUMBER,\n \"name\": STRING(min_length=1),\n \"created_at\": ANY_NOT_NONE\n}\n```\n\n## \ud83d\udcda Documentation\n\n### NUMBER\n\nMatches numeric values (int or float) with optional min/max constraints.\n\n```python\nfrom flexible_matchers import NUMBER, IS_NUMBER\n\n# Match any number\nassert 42 == NUMBER()\nassert 3.14 == NUMBER()\n\n# Match with minimum value\nassert 42 == NUMBER(min_value=0)\nassert -5 != NUMBER(min_value=0)\n\n# Match with maximum value\nassert 42 == NUMBER(max_value=100)\nassert 150 != NUMBER(max_value=100)\n\n# Match within range\nassert 50 == NUMBER(min_value=0, max_value=100)\nassert -1 != NUMBER(min_value=0, max_value=100)\n\n# Pre-instantiated matcher for any number\nassert 42 == IS_NUMBER\n```\n\n### CLOSE_NUMBER\n\nMatches numbers within a tolerance range (useful for floating-point comparisons).\n\n```python\nfrom flexible_matchers import CLOSE_NUMBER\n\n# Default tolerance of 0.5\nassert 42.3 == CLOSE_NUMBER(42)\nassert 41.7 == CLOSE_NUMBER(42)\nassert 42.6 != CLOSE_NUMBER(42)\n\n# Custom tolerance\nassert 3.14 == CLOSE_NUMBER(3.1, tolerance=0.1)\nassert 100 == CLOSE_NUMBER(99, tolerance=1)\n```\n\n**Unique Feature**: Unlike other libraries, `CLOSE_NUMBER` provides tolerance-based matching which is essential for floating-point comparisons in scientific computing and financial applications.\n\n### STRING\n\nMatches strings with optional length constraints.\n\n```python\nfrom flexible_matchers import STRING, IS_STRING\n\n# Match any string\nassert \"hello\" == STRING()\nassert \"\" == STRING()\n\n# Match exact length\nassert \"hello\" == STRING(length=5)\nassert \"hi\" != STRING(length=5)\n\n# Match minimum length\nassert \"hello\" == STRING(min_length=3)\nassert \"hi\" != STRING(min_length=3)\n\n# Match maximum length\nassert \"hello\" == STRING(max_length=10)\nassert \"very long string\" != STRING(max_length=10)\n\n# Match length range\nassert \"hello\" == STRING(min_length=3, max_length=10)\n\n# Pre-instantiated matcher for any string\nassert \"hello\" == IS_STRING\n```\n\n**Unique Feature**: Flexible string length constraints (`min_length`, `max_length`) are not available in most other matcher libraries.\n\n### LIST\n\nMatches lists with optional length constraint.\n\n```python\nfrom flexible_matchers import LIST, IS_LIST\n\n# Match any list\nassert [1, 2, 3] == LIST()\nassert [] == LIST()\n\n# Match exact length\nassert [1, 2, 3] == LIST(3)\nassert [1, 2] != LIST(3)\n\n# Pre-instantiated matcher for any list\nassert [1, 2, 3] == IS_LIST\n```\n\n### ANY_NOT_NONE\n\nMatches any value except `None`.\n\n```python\nfrom flexible_matchers import ANY_NOT_NONE\n\n# Matches any non-None value\nassert 42 == ANY_NOT_NONE\nassert \"hello\" == ANY_NOT_NONE\nassert [] == ANY_NOT_NONE\nassert 0 == ANY_NOT_NONE\nassert False == ANY_NOT_NONE\n\n# Does not match None\nassert None != ANY_NOT_NONE\n```\n\n**Use Case**: Perfect for API responses where you want to ensure a field exists but don't care about its specific value.\n\n## \ud83c\udd9a Comparison with Other Libraries\n\n### vs. unittest.mock.ANY\n\n```python\nfrom unittest.mock import ANY\nfrom flexible_matchers import NUMBER, STRING\n\n# unittest.mock.ANY - too permissive\nassert {\"age\": -100} == {\"age\": ANY} # Passes, but age is invalid!\n\n# flexible-matchers - precise validation\nassert {\"age\": 30} == {\"age\": NUMBER(min_value=0, max_value=150)} # \u2713\nassert {\"age\": -100} == {\"age\": NUMBER(min_value=0, max_value=150)} # \u2717\n```\n\n### vs. PyHamcrest\n\n```python\n# PyHamcrest - requires special syntax\nfrom hamcrest import assert_that, instance_of, greater_than\nassert_that(value, instance_of(int))\nassert_that(value, greater_than(0))\n\n# flexible-matchers - natural Python syntax\nfrom flexible_matchers import NUMBER\nassert value == NUMBER(min_value=0)\n```\n\n### vs. dirty-equals\n\n```python\n# dirty-equals - close, but missing key features\nfrom dirty_equals import IsPositiveInt\nassert 42 == IsPositiveInt\n\n# flexible-matchers - more flexible with ranges and tolerance\nfrom flexible_matchers import NUMBER, CLOSE_NUMBER\nassert 42 == NUMBER(min_value=0, max_value=100)\nassert 3.14 == CLOSE_NUMBER(3.1, tolerance=0.1) # Not available in dirty-equals\n```\n\n### vs. pychoir\n\n```python\n# pychoir - similar approach, but less intuitive\nfrom pychoir import LessThan, GreaterThan, And\nassert value == And(GreaterThan(0), LessThan(100))\n\n# flexible-matchers - simpler, more intuitive\nfrom flexible_matchers import NUMBER\nassert value == NUMBER(min_value=0, max_value=100)\n```\n\n## \ud83c\udfaf Real-World Examples\n\n### API Testing\n\n```python\nfrom flexible_matchers import NUMBER, STRING, ANY_NOT_NONE\n\ndef test_create_user_api():\n response = api.create_user(name=\"Alice\", email=\"alice@example.com\")\n \n assert response == {\n \"id\": NUMBER(min_value=1),\n \"name\": STRING(min_length=1, max_length=100),\n \"email\": STRING(min_length=5),\n \"created_at\": ANY_NOT_NONE,\n \"updated_at\": ANY_NOT_NONE,\n \"is_active\": True,\n }\n```\n\n### Mock Assertions\n\n```python\nfrom unittest.mock import Mock\nfrom flexible_matchers import NUMBER, STRING\n\ndef test_user_service():\n mock_db = Mock()\n service = UserService(mock_db)\n \n service.create_user(name=\"Alice\", age=30)\n \n mock_db.insert.assert_called_once_with(\n table=\"users\",\n data={\n \"name\": STRING(min_length=1),\n \"age\": NUMBER(min_value=0, max_value=150),\n \"created_at\": ANY_NOT_NONE,\n }\n )\n```\n\n### Nested Data Structures\n\n```python\nfrom flexible_matchers import NUMBER, STRING, LIST, IS_NUMBER\n\ndef test_complex_response():\n response = {\n \"users\": [\n {\"id\": 1, \"name\": \"Alice\", \"scores\": [95, 87, 92]},\n {\"id\": 2, \"name\": \"Bob\", \"scores\": [88, 91, 85]},\n ],\n \"total\": 2,\n \"page\": 1,\n }\n \n assert response == {\n \"users\": [\n {\n \"id\": IS_NUMBER,\n \"name\": STRING(min_length=1),\n \"scores\": LIST(3),\n },\n {\n \"id\": IS_NUMBER,\n \"name\": STRING(min_length=1),\n \"scores\": LIST(3),\n },\n ],\n \"total\": NUMBER(min_value=0),\n \"page\": NUMBER(min_value=1),\n }\n```\n\n### Floating-Point Comparisons\n\n```python\nfrom flexible_matchers import CLOSE_NUMBER\n\ndef test_scientific_calculation():\n result = calculate_pi()\n assert result == CLOSE_NUMBER(3.14159, tolerance=0.00001)\n \ndef test_financial_calculation():\n total = calculate_total([10.10, 20.20, 30.30])\n assert total == CLOSE_NUMBER(60.60, tolerance=0.01)\n```\n\n## \ud83d\udee0\ufe0f Development\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/skippdot/flexible-matchers.git\ncd flexible-matchers\n\n# Install development dependencies\npip install -e \".[dev]\"\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npytest\n\n# Run with coverage\npytest --cov=flexible_matchers --cov-report=html\n\n# Run specific test file\npytest tests/test_matchers.py\n\n# Run specific test\npytest tests/test_matchers.py::TestNUMBER::test_range\n```\n\n### Code Quality\n\n```bash\n# Format code\nblack src tests\nisort src tests\n\n# Lint code\nruff check src tests\nflake8 src tests\npylint src tests\n\n# Type checking\nmypy src\n```\n\n### Running All Checks\n\n```bash\n# Format\nblack src tests && isort src tests\n\n# Lint\nruff check src tests && flake8 src tests && pylint src tests\n\n# Test\npytest --cov=flexible_matchers --cov-report=term-missing\n\n# Type check\nmypy src\n```\n\n## \ud83d\udccb Requirements\n\n- Python >= 3.7\n- No runtime dependencies!\n\n## \ud83d\udcc4 License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## \ud83d\ude4f Acknowledgments\n\nInspired by:\n- [pychoir](https://github.com/kajaste/pychoir) - Modern matcher library\n- [dirty-equals](https://github.com/samuelcolvin/dirty-equals) - Flexible equality testing\n- [PyHamcrest](https://github.com/hamcrest/PyHamcrest) - Mature matcher framework\n- [callee](https://github.com/Xion/callee) - Argument matchers (now abandoned)\n\n## \ud83d\udcca Project Stats\n\n- **Zero Dependencies**: No external packages required\n- **100% Test Coverage**: Comprehensive test suite\n- **Type Hinted**: Full type annotations\n- **Python 3.7+**: Modern Python support\n- **Active Maintenance**: Regular updates and improvements\n\n## \ud83d\udd17 Links\n\n- **PyPI**: https://pypi.org/project/flexible-matchers/\n- **GitHub**: https://github.com/skippdot/flexible-matchers\n- **Issues**: https://github.com/skippdot/flexible-matchers/issues\n- **Changelog**: https://github.com/skippdot/flexible-matchers/releases\n\n---\n\nMade with \u2764\ufe0f by [Stepan Shamaiev](https://github.com/skippdot)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Lightweight, zero-dependency mock assertion helpers with flexible numeric and string matching",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/skippdot/flexible-matchers",
"Issues": "https://github.com/skippdot/flexible-matchers/issues",
"Repository": "https://github.com/skippdot/flexible-matchers"
},
"split_keywords": [
"testing",
" mock",
" unittest",
" pytest",
" matchers",
" assertions",
" test-helpers"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "112582e24e55ac3228a6f508072c0d6d14d22ab1220c52bc48766d6aa5586b6d",
"md5": "84d8283bb3d7fb550c2849e2a4c003bd",
"sha256": "b8d88be8bb406dfd22adae0b4bd4287855da836b46956a1b619bf369325b3037"
},
"downloads": -1,
"filename": "flexible_matchers-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "84d8283bb3d7fb550c2849e2a4c003bd",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 8206,
"upload_time": "2025-10-06T14:50:25",
"upload_time_iso_8601": "2025-10-06T14:50:25.935241Z",
"url": "https://files.pythonhosted.org/packages/11/25/82e24e55ac3228a6f508072c0d6d14d22ab1220c52bc48766d6aa5586b6d/flexible_matchers-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "fad97c7c38533e471c8485815b238cde526c2587c6a7f19c9dd8484aa22403de",
"md5": "cc9d3edc3065b5d780c81b279d7796b6",
"sha256": "86191acecaf34d87aa4f5b016738c61de709931df5f31e29f5320e4b1776966e"
},
"downloads": -1,
"filename": "flexible_matchers-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "cc9d3edc3065b5d780c81b279d7796b6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 13901,
"upload_time": "2025-10-06T14:50:27",
"upload_time_iso_8601": "2025-10-06T14:50:27.023421Z",
"url": "https://files.pythonhosted.org/packages/fa/d9/7c7c38533e471c8485815b238cde526c2587c6a7f19c9dd8484aa22403de/flexible_matchers-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-06 14:50:27",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "skippdot",
"github_project": "flexible-matchers",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "flexible-matchers"
}