Initial commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 2
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
name: Python Lint CI
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lint-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/lint.yml'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'uv.lock'
|
||||||
|
- '.python-version'
|
||||||
|
- '**/*.py'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ruff:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Set up UV
|
||||||
|
uses: actions/setup-uv@v1
|
||||||
|
- name: Run Ruff check
|
||||||
|
run: uv run ruff check --output-format=github
|
||||||
|
|
||||||
|
ruff-format-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Set up UV
|
||||||
|
uses: actions/setup-uv@v1
|
||||||
|
- name: Run Ruff format check
|
||||||
|
run: uv run ruff format --check --output-format=github
|
||||||
|
|
||||||
|
ty:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Set up UV
|
||||||
|
uses: actions/setup-uv@v1
|
||||||
|
- name: Run ty check
|
||||||
|
run: uv run ty check --output-format=github
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: Python Test CI
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: test-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/test.yml'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'uv.lock'
|
||||||
|
- '.python-version'
|
||||||
|
- '**/*.py'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Set up UV
|
||||||
|
uses: actions/setup-uv@v1
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: uv run coverage run -m unittest -v
|
||||||
|
- name: Generate coverage XML report
|
||||||
|
run: uv run coverage xml
|
||||||
|
- name: Create coverage summary
|
||||||
|
uses: actions/code-coverage-summary@v1.3.0
|
||||||
|
with:
|
||||||
|
filename: coverage.xml
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
.venv
|
||||||
|
.vscode
|
||||||
|
.ruff_cache
|
||||||
|
.env
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
|
coverage.xml
|
||||||
|
*.egg-info
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
.pytest_cache
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.14
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# ENV
|
||||||
|
|
||||||
|
ifeq (,$(shell which uv))
|
||||||
|
ifeq (,$(shell python3 -m uv --help||python3 -m pip install --user uv)) # detect uv install
|
||||||
|
UV ?= false
|
||||||
|
endif
|
||||||
|
UV ?= python3 -m uv
|
||||||
|
endif
|
||||||
|
|
||||||
|
UV ?= uv
|
||||||
|
RUFF ?= $(UV) run --active ruff
|
||||||
|
TY ?= $(UV) run --active ty
|
||||||
|
UNITTEST ?= $(UV) run --active -m unittest
|
||||||
|
COVERAGE ?= $(UV) run --active coverage
|
||||||
|
OPEN ?= xdg-open
|
||||||
|
|
||||||
|
# DOCS
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
help: ## show this message
|
||||||
|
@echo "Usage: $(MAKE) [target1] [target2] ..."
|
||||||
|
@echo ""
|
||||||
|
@echo "Commands/Targets:"
|
||||||
|
@cat $(MAKEFILE_LIST) | grep -E '(^[a-zA-Z0-9_%-]+:.*?##.*$$)|(^##)' | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
|
||||||
|
@echo ""
|
||||||
|
@echo "Environment:"
|
||||||
|
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z0-9_-]+\s*\??=.*$$' | grep -Eo '^[a-zA-Z0-9_-]+' | xargs -I {} $(MAKE) -s print-{} 2> /dev/null
|
||||||
|
|
||||||
|
.PHONY: print-%
|
||||||
|
print-%:
|
||||||
|
@echo -e '\033[32m$*\033[0m = $($*)'
|
||||||
|
|
||||||
|
# FILES
|
||||||
|
|
||||||
|
.venv: uv.lock
|
||||||
|
@$(MAKE) -s uv-sync
|
||||||
|
|
||||||
|
# ACTIONS
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: uv-sync ## install project
|
||||||
|
|
||||||
|
.PHONY: update
|
||||||
|
update: uv-upgrade ## update project dependencies
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format: ruff-fix ruff-format ## format project
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: ruff ruff-format-check ty ## lint project
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: docker-build ## build project
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: unittest ## test project
|
||||||
|
|
||||||
|
.PHONY: test-%
|
||||||
|
test-%: ## test project with specific test
|
||||||
|
@$(MAKE) -s unittest-$*
|
||||||
|
|
||||||
|
.PHONY: coverage
|
||||||
|
coverage: coverage-unittest coverage-xml coverage-report ## test project with coverage
|
||||||
|
|
||||||
|
# TOOLS
|
||||||
|
|
||||||
|
.PHONY: uv-sync
|
||||||
|
uv-sync: ## uv sync
|
||||||
|
@$(UV) sync --active
|
||||||
|
|
||||||
|
.PHONY: uv-update
|
||||||
|
uv-upgrade: ## uv sync upgrade
|
||||||
|
@$(UV) sync --active --upgrade
|
||||||
|
|
||||||
|
.PHONY: ruff
|
||||||
|
ruff: .venv ## ruff check
|
||||||
|
@$(RUFF) check
|
||||||
|
|
||||||
|
.PHONY: ruff-fix
|
||||||
|
ruff-fix: .venv ## ruff check (and fix)
|
||||||
|
@$(RUFF) check --fix --unsafe-fixes
|
||||||
|
|
||||||
|
.PHONY: ruff-format
|
||||||
|
ruff-format: .venv ## ruff format
|
||||||
|
@$(RUFF) format
|
||||||
|
|
||||||
|
.PHONY: ruff-format-check
|
||||||
|
ruff-format-check: .venv ## ruff format (check only)
|
||||||
|
@$(RUFF) format --check
|
||||||
|
|
||||||
|
.PHONY: ty
|
||||||
|
ty: .venv ## ty check
|
||||||
|
@$(TY) check
|
||||||
|
|
||||||
|
.PHONY: unittest
|
||||||
|
unittest: .venv ## unittest
|
||||||
|
@$(UNITTEST) -v
|
||||||
|
|
||||||
|
.PHONY: unittest-%
|
||||||
|
unittest-%: .venv ## unittest -k [filter]
|
||||||
|
@$(UNITTEST) -v -k $*
|
||||||
|
|
||||||
|
.PHONY: coverage-unittest
|
||||||
|
coverage-unittest: .venv ## coverage run -m unittest
|
||||||
|
@$(COVERAGE) run -m unittest -v
|
||||||
|
|
||||||
|
.PHONY: coverage-report
|
||||||
|
coverage-report: .venv ## coverage report
|
||||||
|
@$(COVERAGE) report
|
||||||
|
|
||||||
|
.PHONY: coverage-html
|
||||||
|
coverage-html: .venv ## coverage html
|
||||||
|
@$(COVERAGE) html
|
||||||
|
@$(OPEN) htmlcov/index.html || true
|
||||||
|
|
||||||
|
.PHONY: coverage-xml
|
||||||
|
coverage-xml: .venv ## coverage xml
|
||||||
|
@$(COVERAGE) xml
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[](https://git.klemek.fr/klemek/python-template/actions?workflow=lint.yml) [](https://git.klemek.fr/klemek/python-template/actions?workflow=test.yml)
|
||||||
|
|
||||||
|
|
||||||
|
# Python Template
|
||||||
|
|
||||||
|
## Use this template
|
||||||
|
|
||||||
|
- [ ] 1. rename foler `sample` to match project
|
||||||
|
- [ ] 2. rename project in `pyproject.toml`
|
||||||
|
- [ ] 3. update `README.md`
|
||||||
|
- [ ] 4. enjoy
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
[project]
|
||||||
|
name = "sample" # TODO: 2. rename project
|
||||||
|
version = "0.0.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
sample = "sample.__main__:main" # TODO: 2. rename project
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["uv_build>=0.11.7,<0.12"]
|
||||||
|
build-backend = "uv_build"
|
||||||
|
|
||||||
|
[tool.uv.build-backend]
|
||||||
|
module-root = ""
|
||||||
|
module-name = "sample" # TODO: 2. rename project
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"coverage>=7.13.5",
|
||||||
|
"pytest>=9.0.3",
|
||||||
|
"ruff>=0.15.10",
|
||||||
|
"ty>=0.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["ALL"]
|
||||||
|
ignore = ["D", "E501", "ANN401", "BLE001", "COM812", "S101", "PT"]
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["sample"] # TODO: 2. rename project
|
||||||
|
omit = ["sample/logs.py", "sample/__main__.py"]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import importlib.metadata
|
||||||
|
|
||||||
|
PKG_NAME = __name__
|
||||||
|
PKG_VERSION = importlib.metadata.version(PKG_NAME)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from . import PKG_NAME, PKG_VERSION
|
||||||
|
from .logs import setup_logs
|
||||||
|
from .params import parse_parameters
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
params = parse_parameters(sys.argv[1:])
|
||||||
|
setup_logs(params)
|
||||||
|
logging.getLogger().info("%s %s", PKG_NAME, PKG_VERSION)
|
||||||
|
return 0
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import enum
|
||||||
|
import logging
|
||||||
|
import typing
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from .params import Parameters
|
||||||
|
|
||||||
|
|
||||||
|
class TermColor(enum.StrEnum):
|
||||||
|
RESET = "0"
|
||||||
|
BOLD = "1"
|
||||||
|
FEINT = "2"
|
||||||
|
UNDERLINE = "4"
|
||||||
|
|
||||||
|
BLACK = "30"
|
||||||
|
RED = "31"
|
||||||
|
GREEN = "32"
|
||||||
|
YELLOW = "33"
|
||||||
|
BLUE = "34"
|
||||||
|
MAGENTA = "35"
|
||||||
|
CYAN = "36"
|
||||||
|
WHITE = "37"
|
||||||
|
GREY = "38"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"\033[{self.value}m"
|
||||||
|
|
||||||
|
@typing.override
|
||||||
|
def __add__(self, value: typing.Any, /) -> str: # ty:ignore[invalid-method-override]
|
||||||
|
return str(self) + str(value)
|
||||||
|
|
||||||
|
def __radd__(self, value: typing.Any, /) -> str:
|
||||||
|
return str(value) + str(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ColoredLoggingFormatter(logging.Formatter):
|
||||||
|
pre_format = "%(asctime)s | "
|
||||||
|
level_format = "%(levelname)-8s"
|
||||||
|
post_format = " | [%(name)s] %(message)s"
|
||||||
|
trace_format = (
|
||||||
|
TermColor.FEINT
|
||||||
|
+ TermColor.GREY
|
||||||
|
+ " (%(filename)s:%(lineno)d)"
|
||||||
|
+ TermColor.RESET
|
||||||
|
)
|
||||||
|
|
||||||
|
FORMAT_COLORS: typing.ClassVar[dict[int, TermColor | str]] = {
|
||||||
|
logging.DEBUG: TermColor.FEINT + TermColor.GREY,
|
||||||
|
logging.INFO: TermColor.GREEN,
|
||||||
|
logging.WARNING: TermColor.YELLOW,
|
||||||
|
logging.ERROR: TermColor.RED,
|
||||||
|
logging.CRITICAL: TermColor.RED,
|
||||||
|
}
|
||||||
|
|
||||||
|
@typing.override
|
||||||
|
def __init__(self, trace: bool) -> None:
|
||||||
|
self.trace: bool = trace
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@typing.override
|
||||||
|
def format(self, record: logging.LogRecord) -> str:
|
||||||
|
log_color: TermColor | str = self.FORMAT_COLORS.get(
|
||||||
|
record.levelno,
|
||||||
|
TermColor.MAGENTA,
|
||||||
|
)
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
self.pre_format
|
||||||
|
+ log_color
|
||||||
|
+ TermColor.BOLD
|
||||||
|
+ self.level_format
|
||||||
|
+ TermColor.RESET
|
||||||
|
+ self.post_format
|
||||||
|
+ (self.trace_format if self.trace else ""),
|
||||||
|
)
|
||||||
|
return formatter.format(record)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logs(params: Parameters) -> None:
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
stream_handler.setFormatter(ColoredLoggingFormatter(trace=params.debug))
|
||||||
|
log_level = logging.INFO
|
||||||
|
if params.debug:
|
||||||
|
log_level = logging.DEBUG
|
||||||
|
logging.basicConfig(level=log_level, handlers=[stream_handler])
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import argparse
|
||||||
|
import dataclasses
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from . import PKG_NAME
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True, slots=True)
|
||||||
|
class Parameters:
|
||||||
|
debug: bool = False
|
||||||
|
quiet: bool = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_namespace(cls, args: argparse.Namespace) -> Parameters:
|
||||||
|
return Parameters(**vars(args))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_parameters(args: typing.Sequence[str]) -> Parameters:
|
||||||
|
default_values = Parameters()
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
PKG_NAME,
|
||||||
|
suggest_on_error=True,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug", action=argparse.BooleanOptionalAction, default=default_values.debug
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--quiet", action=argparse.BooleanOptionalAction, default=default_values.quiet
|
||||||
|
)
|
||||||
|
parsed_args = parser.parse_args(args)
|
||||||
|
return Parameters.from_namespace(parsed_args)
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
import contextlib
|
||||||
|
import pathlib
|
||||||
|
import tempfile
|
||||||
|
import typing
|
||||||
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
|
|
||||||
|
__import__("sys").modules["unittest.util"]._MAX_LENGTH = 999999999 # ty:ignore[unresolved-attribute] # noqa: SLF001
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTestCase(unittest.TestCase):
|
||||||
|
@typing.override
|
||||||
|
def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
||||||
|
self.mocks: list[unittest.mock.Mock] = []
|
||||||
|
self.tmp_dir: tempfile.TemporaryDirectory | None = None
|
||||||
|
self.tmp_path: pathlib.Path = pathlib.Path()
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@typing.override
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
if self.tmp_dir is not None:
|
||||||
|
self.tmp_dir.cleanup()
|
||||||
|
self.tmp_dir = None
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def get_tmp_dir(self) -> str:
|
||||||
|
self.tmp_dir = tempfile.TemporaryDirectory(delete=False)
|
||||||
|
self.tmp_path = pathlib.Path(self.tmp_dir.name)
|
||||||
|
return self.tmp_dir.name
|
||||||
|
|
||||||
|
def new_mock(self, spec: type | None = None) -> unittest.mock.Mock:
|
||||||
|
mock = unittest.mock.Mock(spec)
|
||||||
|
self.mocks += [mock]
|
||||||
|
return mock
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def patch(
|
||||||
|
self, target: str, return_value: typing.Any = None, count: int = 1
|
||||||
|
) -> typing.Iterator[unittest.mock.Mock]:
|
||||||
|
with unittest.mock.patch(
|
||||||
|
target, return_value=return_value, create=True
|
||||||
|
) as mock:
|
||||||
|
yield mock
|
||||||
|
self.assertEqual(mock.call_count, count, mock)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def patch_calls(
|
||||||
|
self,
|
||||||
|
target: str,
|
||||||
|
args: list[typing.Iterable[typing.Any]] | None = None,
|
||||||
|
return_values: list[typing.Any] | None = None,
|
||||||
|
kwargs: list[dict[str, typing.Any]] | None = None,
|
||||||
|
) -> typing.Iterator[unittest.mock.Mock]:
|
||||||
|
if args is None:
|
||||||
|
args = [[]]
|
||||||
|
if return_values is None:
|
||||||
|
return_values = [None] * len(args)
|
||||||
|
if kwargs is None:
|
||||||
|
kwargs = [{}] * len(args)
|
||||||
|
with unittest.mock.patch(
|
||||||
|
target, side_effect=return_values, create=True
|
||||||
|
) as mock:
|
||||||
|
yield mock
|
||||||
|
self.__check_calls(mock, args, kwargs)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def patch_call(
|
||||||
|
self,
|
||||||
|
target: str,
|
||||||
|
args: typing.Iterable[typing.Any] | None = None,
|
||||||
|
return_value: typing.Any = None,
|
||||||
|
kwargs: dict[str, typing.Any] | None = None,
|
||||||
|
) -> typing.Iterator[unittest.mock.Mock]:
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if kwargs is None:
|
||||||
|
kwargs = {}
|
||||||
|
with self.patch_calls(target, [args], [return_value], [kwargs]) as mock:
|
||||||
|
yield mock
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def seal_mocks(self, *extra_mocks: unittest.mock.Mock) -> typing.Iterator[None]:
|
||||||
|
for mock in self.mocks:
|
||||||
|
unittest.mock.seal(mock)
|
||||||
|
for mock in extra_mocks:
|
||||||
|
unittest.mock.seal(mock)
|
||||||
|
yield
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def mock_calls(
|
||||||
|
self,
|
||||||
|
mock: unittest.mock.Mock,
|
||||||
|
args: list[typing.Iterable[typing.Any]] | None = None,
|
||||||
|
return_values: list[typing.Any] | None = None,
|
||||||
|
kwargs: list[dict[str, typing.Any]] | None = None,
|
||||||
|
) -> typing.Iterator[None]:
|
||||||
|
if args is None:
|
||||||
|
args = [[]]
|
||||||
|
if return_values is None:
|
||||||
|
return_values = [None] * len(args)
|
||||||
|
if kwargs is None:
|
||||||
|
kwargs = [{}] * len(args)
|
||||||
|
mock.side_effect = return_values
|
||||||
|
mock.reset_mock()
|
||||||
|
yield
|
||||||
|
self.__check_calls(mock, args, kwargs)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def mock_call(
|
||||||
|
self,
|
||||||
|
mock: unittest.mock.Mock,
|
||||||
|
args: typing.Iterable[typing.Any] | None = None,
|
||||||
|
return_value: typing.Any = None,
|
||||||
|
kwargs: dict[str, typing.Any] | None = None,
|
||||||
|
) -> typing.Iterator[None]:
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if kwargs is None:
|
||||||
|
kwargs = {}
|
||||||
|
with self.mock_calls(mock, [args], [return_value], [kwargs]):
|
||||||
|
yield
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def mock_calls_unchecked(
|
||||||
|
self,
|
||||||
|
mock: unittest.mock.Mock,
|
||||||
|
count: int = 1,
|
||||||
|
return_values: list[typing.Any] | None = None,
|
||||||
|
) -> typing.Iterator[None]:
|
||||||
|
if return_values is None:
|
||||||
|
return_values = [None] * count
|
||||||
|
mock.side_effect = return_values
|
||||||
|
mock.reset_mock()
|
||||||
|
yield
|
||||||
|
self.assertEqual(mock.call_count, count, mock)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def mock_call_unchecked(
|
||||||
|
self,
|
||||||
|
mock: unittest.mock.Mock,
|
||||||
|
return_value: typing.Any = None,
|
||||||
|
) -> typing.Iterator[None]:
|
||||||
|
with self.mock_calls_unchecked(mock, 1, [return_value]):
|
||||||
|
yield
|
||||||
|
|
||||||
|
def assert_file_content(self, file: pathlib.Path, *expected_content: str) -> None:
|
||||||
|
assert file.parent.is_dir(), file
|
||||||
|
assert file.exists(), file
|
||||||
|
assert file.is_file(), file
|
||||||
|
with file.open() as file_content:
|
||||||
|
self.assertEqual(file_content.read(), "\n".join(expected_content))
|
||||||
|
|
||||||
|
def __check_calls(
|
||||||
|
self,
|
||||||
|
mock: unittest.mock.Mock,
|
||||||
|
args: list[typing.Iterable[typing.Any]],
|
||||||
|
kwargs: list[dict[str, typing.Any]],
|
||||||
|
) -> None:
|
||||||
|
total_rows = max(len(args), len(mock.method_calls), len(kwargs))
|
||||||
|
missing_calls = max(0, total_rows - len(mock.mock_calls))
|
||||||
|
missing_args = max(0, total_rows - len(args))
|
||||||
|
missing_kwargs = max(0, total_rows - len(kwargs))
|
||||||
|
for i, values in enumerate(
|
||||||
|
zip(
|
||||||
|
mock.mock_calls + [None] * missing_calls,
|
||||||
|
args + [[]] * missing_args,
|
||||||
|
kwargs + [{}] * missing_kwargs,
|
||||||
|
strict=False,
|
||||||
|
)
|
||||||
|
):
|
||||||
|
real_call, expected_args, expected_kwargs = values
|
||||||
|
self.assertEqual(
|
||||||
|
real_call,
|
||||||
|
unittest.mock.call(*expected_args, **expected_kwargs),
|
||||||
|
f"{i + 1}: {mock}",
|
||||||
|
)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from . import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestSample(BaseTestCase):
|
||||||
|
def test_sample(self) -> None:
|
||||||
|
self.skipTest("TODO")
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coverage"
|
||||||
|
version = "7.13.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "26.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.20.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "9.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.15.12"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sample"
|
||||||
|
version = "0.0.0"
|
||||||
|
source = { editable = "." }
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "coverage" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "ruff" },
|
||||||
|
{ name = "ty" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = [
|
||||||
|
{ name = "coverage", specifier = ">=7.13.5" },
|
||||||
|
{ name = "pytest", specifier = ">=9.0.3" },
|
||||||
|
{ name = "ruff", specifier = ">=0.15.10" },
|
||||||
|
{ name = "ty", specifier = ">=0.0.29" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ty"
|
||||||
|
version = "0.0.34"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c4/69/e24eefe2c35c0fdbdec9b60e162727af669bb76d64d993d982eb67b24c38/ty-0.0.34.tar.gz", hash = "sha256:a6efe66b0f13c03a65e6c72ec9abfe2792e2fd063c74fa67e2c4930e29d661be", size = 5585933, upload-time = "2026-05-01T23:06:46.388Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/7b/8b85003d6639ef17a97dcbb31f4511cfe78f1c81a964470db100c8c883e7/ty-0.0.34-py3-none-linux_armv6l.whl", hash = "sha256:9ecc3d14f07a95a6ceb88e07f8e62358dbd37325d3d5bd56da7217ff1fef7fb8", size = 11067094, upload-time = "2026-05-01T23:06:21.133Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/25/b0098f65b020b015c40567c763fc66fffbec88b2ba6f584bca1e92f05ebb/ty-0.0.34-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0dccffd8a9d02321cd2dee3249df205e26d62694e741f4eeca36b157fd8b419f", size = 10840909, upload-time = "2026-05-01T23:06:18.409Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/55/5e4adcf7d2a1006b844903b27cb81244a9b748d850433a46a6c21776c401/ty-0.0.34-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b0ea47a2998e167ab3b21d2f4b5309a9cf33c297809f6d7e3e753252223174d0", size = 10279378, upload-time = "2026-05-01T23:06:37.962Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/91/f537dca0db8fe2558e8ab04d8941d687b384fcc1df5eb9023b2db75ac26c/ty-0.0.34-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b37da00b41a118a459ae56d8947e70651073fb33ebfbceb820e4a10b22d5023", size = 10817423, upload-time = "2026-05-01T23:06:26.247Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/c4/55a3ad1da2815af1009bdc1b8c90dc11a364cd314e4b48c5128ba9d38859/ty-0.0.34-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81cbbb93c2342fe3de43e625d3a9eb149633e9f485e816ebf6395d08685355d8", size = 10851826, upload-time = "2026-05-01T23:06:24.198Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/8c/9c7606af22d73fb43ea4369472d9c66ece11231be73b0efe8e3c61655559/ty-0.0.34-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c5b4dea1594a021289e172582df9cde7089dce14b276fc650e7b212b1772e12", size = 11356318, upload-time = "2026-05-01T23:06:51.139Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/54/bb423f663721ab4138b216425c6b55eaefd3a068243b24d6d8fe988f4e13/ty-0.0.34-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:030fb00aa2d2a5b5ae9d9183d574e0c82dae80566700a7490c43669d8ece40cd", size = 11902968, upload-time = "2026-05-01T23:06:35.82Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/22/01122b21ab6b534a2f618c6bbe5f1f7f49fd56f4b2ec8887cd6d40d08fb3/ty-0.0.34-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ae9555e24e36c63a8218e037a5a63f15579eb6aa94f41017e57cd41d335cfb5", size = 11548860, upload-time = "2026-05-01T23:06:42.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/50/86008b1392ec64bed1957bbcc7aaa43b466b50dfc91bb131841c21d7c5c3/ty-0.0.34-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99eb23df9ed129fc26d1ab00d6f0b8dfe5253b09c2ac6abdb11523fa70d67f10", size = 11457097, upload-time = "2026-05-01T23:06:53.477Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/3e/4558b2296963ba99c58d8409c57d7db4f3061b656c3613cb21c02c1ef4c2/ty-0.0.34-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85de45382016eceae69e104815eb2cfa200787df104002e262a86cbd43ed2c02", size = 10798192, upload-time = "2026-05-01T23:06:40.004Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/bf/650d24402be2ef678528d60caac1d9477a40fc37e3792ecef07834fd7a4a/ty-0.0.34-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:14cb575fb8fa5131f5129d100cfe23c1575d23faf5dfc5158432749a3e38c9b5", size = 10890390, upload-time = "2026-05-01T23:06:33.076Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/ef/ccd2ca13906079f7935fd7e067661b24233017f57d987d51d6a121d85bb5/ty-0.0.34-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c6fc0b69d8450e6910ba9db34572b959b81329a97ae273c391f70e9fb6c1aade", size = 11031564, upload-time = "2026-05-01T23:06:55.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/2d/d27b72005b6f43599e3bcabab0d7135ac0c230b7a307bb99f9eea02c1cda/ty-0.0.34-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:30dfcec2f0fde3993f4f912ed0e057dcbebc8615299f610a4c2ddb7b5a3e1e06", size = 11553430, upload-time = "2026-05-01T23:06:31.096Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/12/20812e1ad930b8d4af70eebf19ad23cff6e31efcfa613ef884531fcdbaa1/ty-0.0.34-py3-none-win32.whl", hash = "sha256:97b77ddf007271b812a313a8f0a14929bc5590958433e1fb83ef585676f53342", size = 10436048, upload-time = "2026-05-01T23:06:49.108Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/6a/afa095c5987868fbda27c0f731146ac8e3d07b357adfa83daccaee5b1a16/ty-0.0.34-py3-none-win_amd64.whl", hash = "sha256:1f543968accb952705134028d1fda8656882787dbbc667ad4d6c3ba23791d604", size = 11462526, upload-time = "2026-05-01T23:06:28.514Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/8f/bf041a06260d77662c0605e56dacfe90b786bf824cbe1aed238d15fe5e84/ty-0.0.34-py3-none-win_arm64.whl", hash = "sha256:ea09108cbcb16b6b06d7596312b433bf49681e78d30e4dc7fb3c1b248a95e09a", size = 10846945, upload-time = "2026-05-01T23:06:44.428Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user