Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.1.0: pytest coverage of 100.0% not reached #29

Closed
kloczek opened this issue Jan 15, 2022 · 14 comments
Closed

3.1.0: pytest coverage of 100.0% not reached #29

kloczek opened this issue Jan 15, 2022 · 14 comments

Comments

@kloczek
Copy link

kloczek commented Jan 15, 2022

Yep .. 😄

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.1.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.1.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra python_utils
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: hypothesis-6.35.0, cov-3.0.0, flake8-1.0.7, mypy-0.8.1
collected 31 items

python_utils/__init__.py ..                                                                                                                                          [  6%]
python_utils/__about__.py .                                                                                                                                          [  9%]
python_utils/aio.py .                                                                                                                                                [ 12%]
python_utils/compat.py .                                                                                                                                             [ 16%]
python_utils/containers.py ...                                                                                                                                       [ 25%]
python_utils/converters.py .......                                                                                                                                   [ 48%]
python_utils/decorators.py ...                                                                                                                                       [ 58%]
python_utils/formatters.py ...                                                                                                                                       [ 67%]
python_utils/import_.py .                                                                                                                                            [ 70%]
python_utils/logger.py ..                                                                                                                                            [ 77%]
python_utils/terminal.py .                                                                                                                                           [ 80%]
python_utils/time.py .....                                                                                                                                           [ 96%]
python_utils/types.py .                                                                                                                                              [100%]

---------- coverage: platform linux, python 3.8.12-final-0 -----------
Name                         Stmts   Miss Branch BrPart  Cover   Missing
------------------------------------------------------------------------
python_utils/__about__.py        6      0      0      0   100%
python_utils/__init__.py        28      0      0      0   100%
python_utils/aio.py              6      3      2      0    62%   7-9
python_utils/compat.py           0      0      0      0   100%
python_utils/containers.py      55      0     28      0   100%
python_utils/converters.py      82      0     44      0   100%
python_utils/decorators.py      18      0      4      0   100%
python_utils/formatters.py      30      0     20      0   100%
python_utils/import_.py         34     30     22      0    11%   31-85
python_utils/logger.py          36      0      4      0   100%
python_utils/terminal.py         3      0      0      0   100%
python_utils/time.py            74     17     32      2    78%   175, 200-222, 253
python_utils/types.py           13      0      0      0   100%
------------------------------------------------------------------------
TOTAL                          385     50    156      2    86%
Coverage HTML written to dir htmlcov

FAIL Required test coverage of 100.0% not reached. Total coverage: 85.95%
=================================================================================== mypy ===================================================================================

Success: no issues found in 13 source files
=========================================================================== 31 passed in 16.24s ============================================================================
@wolph
Copy link
Owner

wolph commented Jan 15, 2022

I don't see how or why... that code and those tests haven't even been touched the past 6 months.

And the automated tests are working without issue: https://github.com/WoLpH/python-utils/runs/4819234204?check_suite_focus=true

@wolph
Copy link
Owner

wolph commented Jan 15, 2022

Actually, looking at the output from the automated tests and your tests I can see at least 1 issue. It seems your tests are not running the tests in _python_utils_tests:

Github actions tests:

setup.py ..                                                              [  3%]
_python_utils_tests/__init__.py .                                        [  5%]
_python_utils_tests/test_import.py .......                               [ 18%]
_python_utils_tests/test_python_utils.py ..                              [ 22%]
_python_utils_tests/test_time.py ..........                              [ 41%]
docs/conf.py .                                                           [ 43%]
python_utils/__init__.py .                                               [ 45%]
python_utils/__about__.py .                                              [ 47%]
python_utils/aio.py .                                                    [ 49%]
python_utils/compat.py .                                                 [ 50%]
python_utils/containers.py ...                                           [ 56%]
python_utils/converters.py .......                                       [ 69%]
python_utils/decorators.py ...                                           [ 75%]
python_utils/formatters.py ...                                           [ 81%]
python_utils/import_.py .                                                [ 83%]
python_utils/logger.py ..                                                [ 86%]
python_utils/terminal.py .                                               [ 88%]
python_utils/time.py .....                                               [ 98%]
python_utils/types.py .                                                  [100%]

@wolph
Copy link
Owner

wolph commented Jan 15, 2022

The tests paths are configured in pytest.ini, do you have any idea why it's not reading that? https://github.com/WoLpH/python-utils/blob/c70f3b8b05686e84bc191995b7cce44b99f53c42/pytest.ini#L2-L4

@kloczek
Copy link
Author

kloczek commented Jan 15, 2022

I have no idea 🤔
Nevertheless when I'm running pytest without params pytest is scanning not only files specified in python_files (as same as in your case) and it shows as well some warnings.

+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: hypothesis-6.35.0, cov-3.0.0, flake8-1.0.7, mypy-0.8.1
collected 53 items

setup.py FF                                                                                                                                                          [  3%]
_python_utils_tests/__init__.py .                                                                                                                                    [  5%]
_python_utils_tests/test_import.py .......                                                                                                                           [ 18%]
_python_utils_tests/test_python_utils.py ..                                                                                                                          [ 22%]
_python_utils_tests/test_time.py .ssss.....                                                                                                                          [ 41%]
docs/conf.py .                                                                                                                                                       [ 43%]
python_utils/__init__.py .                                                                                                                                           [ 45%]
python_utils/__about__.py .                                                                                                                                          [ 47%]
python_utils/aio.py .                                                                                                                                                [ 49%]
python_utils/compat.py .                                                                                                                                             [ 50%]
python_utils/containers.py ...                                                                                                                                       [ 56%]
python_utils/converters.py .......                                                                                                                                   [ 69%]
python_utils/decorators.py ...                                                                                                                                       [ 75%]
python_utils/formatters.py ...                                                                                                                                       [ 81%]
python_utils/import_.py .                                                                                                                                            [ 83%]
python_utils/logger.py ..                                                                                                                                            [ 86%]
python_utils/terminal.py .                                                                                                                                           [ 88%]
python_utils/time.py .....                                                                                                                                           [ 98%]
python_utils/types.py .                                                                                                                                              [100%]

================================================================================= FAILURES =================================================================================
_________________________________________________________________________________ setup.py _________________________________________________________________________________
4: error: Skipping analyzing "setuptools": module is installed, but missing library stubs or py.typed marker
4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
_______________________________________________________________________________ test session _______________________________________________________________________________
mypy exited with status 1.
============================================================================= warnings summary =============================================================================
_python_utils_tests/test_time.py:18
  /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0/_python_utils_tests/test_time.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.asyncio - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.asyncio

_python_utils_tests/test_time.py::test_aio_timeout_generator[0.1-0.06-0.5-0.1-acount-2]
_python_utils_tests/test_time.py::test_aio_timeout_generator[0.2-0.06-0.5-0.1-acount-4]
_python_utils_tests/test_time.py::test_aio_timeout_generator[0.3-0.06-1.0-None-acount-5]
_python_utils_tests/test_time.py::test_aio_timeout_generator[timeout3-interval3-2.0-maximum_interval3-acount-2]
  /usr/lib/python3.8/site-packages/_pytest/python.py:172: PytestUnhandledCoroutineWarning: async def functions are not natively supported and have been skipped.
  You need to install a suitable plugin for your async framework, for example:
    - anyio
    - pytest-asyncio
    - pytest-tornasync
    - pytest-trio
    - pytest-twisted
    warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))

-- Docs: https://docs.pytest.org/en/stable/warnings.html

---------- coverage: platform linux, python 3.8.12-final-0 -----------
Name                         Stmts   Miss Branch BrPart  Cover   Missing
------------------------------------------------------------------------
python_utils/__about__.py        6      0      0      0   100%
python_utils/__init__.py        28      0      0      0   100%
python_utils/aio.py              6      3      2      0    62%   7-9
python_utils/compat.py           0      0      0      0   100%
python_utils/containers.py      55      0     28      0   100%
python_utils/converters.py      82      0     44      0   100%
python_utils/decorators.py      18      0      4      0   100%
python_utils/formatters.py      30      0     20      0   100%
python_utils/import_.py         34      0     22      0   100%
python_utils/logger.py          36      0      4      0   100%
python_utils/terminal.py         3      0      0      0   100%
python_utils/time.py            74     15     32      0    82%   200-222
python_utils/types.py           13      0      0      0   100%
------------------------------------------------------------------------
TOTAL                          385     18    156      0    96%
Coverage HTML written to dir htmlcov

FAIL Required test coverage of 100.0% not reached. Total coverage: 95.93%
=================================================================================== mypy ===================================================================================
Found 1 error in 1 file (checked 19 source files)
========================================================================= short test summary info ==========================================================================
SKIPPED [4] ../../../../../usr/lib/python3.8/site-packages/_pytest/python.py:173: async def function and no async plugin installed (see warnings)
FAILED setup.py::mypy
FAILED setup.py::mypy-status
====================================================== 2 failed, 47 passed, 4 skipped, 5 warnings in 62.24s (0:01:02) ======================================================

pytest and coverity used in my env are listed in pytest headr.
Because pytest have been scanning setup.py Ive decided to add python_utils as pytest param.
Temporary in my rpm build procedure I'm using --no-cov

+ /usr/bin/pytest -ra python_utils --no-cov
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: hypothesis-6.35.0, cov-3.0.0, flake8-1.0.7, mypy-0.8.1
collected 31 items

python_utils/__init__.py ..                                                                                                                                          [  6%]
python_utils/__about__.py .                                                                                                                                          [  9%]
python_utils/aio.py .                                                                                                                                                [ 12%]
python_utils/compat.py .                                                                                                                                             [ 16%]
python_utils/containers.py ...                                                                                                                                       [ 25%]
python_utils/converters.py .......                                                                                                                                   [ 48%]
python_utils/decorators.py ...                                                                                                                                       [ 58%]
python_utils/formatters.py ...                                                                                                                                       [ 67%]
python_utils/import_.py .                                                                                                                                            [ 70%]
python_utils/logger.py ..                                                                                                                                            [ 77%]
python_utils/terminal.py .                                                                                                                                           [ 80%]
python_utils/time.py .....                                                                                                                                           [ 96%]
python_utils/types.py .                                                                                                                                              [100%]
=================================================================================== mypy ===================================================================================

Success: no issues found in 13 source files
=========================================================================== 31 passed in 11.20s ============================================================================

@wolph
Copy link
Owner

wolph commented Jan 15, 2022

The setuptools warning and the asyncio warnings indicate missing requirements. These are part of the tests extras in the requirements: https://github.com/WoLpH/python-utils/blob/c70f3b8b05686e84bc191995b7cce44b99f53c42/setup.py#L37-L46

I think it should run fine once those are installed

@kloczek
Copy link
Author

kloczek commented Jan 15, 2022

pytest output after add pytest-asyncio to build env

=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: hypothesis-6.35.0, cov-3.0.0, flake8-1.0.7, mypy-0.8.1, asyncio-0.17.0
collected 31 items

python_utils/__init__.py ..                                                                                                                                          [  6%]
python_utils/__about__.py .                                                                                                                                          [  9%]
python_utils/aio.py .                                                                                                                                                [ 12%]
python_utils/compat.py .                                                                                                                                             [ 16%]
python_utils/containers.py ...                                                                                                                                       [ 25%]
python_utils/converters.py .......                                                                                                                                   [ 48%]
python_utils/decorators.py ...                                                                                                                                       [ 58%]
python_utils/formatters.py ...                                                                                                                                       [ 67%]
python_utils/import_.py .                                                                                                                                            [ 70%]
python_utils/logger.py ..                                                                                                                                            [ 77%]
python_utils/terminal.py .                                                                                                                                           [ 80%]
python_utils/time.py .....                                                                                                                                           [ 96%]
python_utils/types.py .                                                                                                                                              [100%]

============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:112
  /usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:112: DeprecationWarning: The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.
    config.issue_config_time_warning(LEGACY_MODE, stacklevel=2)

-- Docs: https://docs.pytest.org/en/stable/warnings.html
=================================================================================== mypy ===================================================================================
Success: no issues found in 13 source files
====================================================================== 31 passed, 1 warning in 11.15s ======================================================================

So looks OK now however still is one pytest warning.

@kloczek
Copy link
Author

kloczek commented Jan 15, 2022

Nevertheless after remove --no-cov still coverity dost not show 100% 🤔

+ /usr/bin/pytest -ra python_utils
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: hypothesis-6.35.0, cov-3.0.0, flake8-1.0.7, mypy-0.8.1, asyncio-0.17.0
collected 31 items

python_utils/__init__.py ..                                                                                                                                          [  6%]
python_utils/__about__.py .                                                                                                                                          [  9%]
python_utils/aio.py .                                                                                                                                                [ 12%]
python_utils/compat.py .                                                                                                                                             [ 16%]
python_utils/containers.py ...                                                                                                                                       [ 25%]
python_utils/converters.py .......                                                                                                                                   [ 48%]
python_utils/decorators.py ...                                                                                                                                       [ 58%]
python_utils/formatters.py ...                                                                                                                                       [ 67%]
python_utils/import_.py .                                                                                                                                            [ 70%]
python_utils/logger.py ..                                                                                                                                            [ 77%]
python_utils/terminal.py .                                                                                                                                           [ 80%]
python_utils/time.py .....                                                                                                                                           [ 96%]
python_utils/types.py .                                                                                                                                              [100%]

============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:112
  /usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:112: DeprecationWarning: The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.
    config.issue_config_time_warning(LEGACY_MODE, stacklevel=2)

-- Docs: https://docs.pytest.org/en/stable/warnings.html

---------- coverage: platform linux, python 3.8.12-final-0 -----------
Name                         Stmts   Miss Branch BrPart  Cover   Missing
------------------------------------------------------------------------
python_utils/__about__.py        6      0      0      0   100%
python_utils/__init__.py        28      0      0      0   100%
python_utils/aio.py              6      3      2      0    62%   7-9
python_utils/compat.py           0      0      0      0   100%
python_utils/containers.py      55      0     28      0   100%
python_utils/converters.py      82      0     44      0   100%
python_utils/decorators.py      18      0      4      0   100%
python_utils/formatters.py      30      0     20      0   100%
python_utils/import_.py         34     30     22      0    11%   31-85
python_utils/logger.py          36      0      4      0   100%
python_utils/terminal.py         3      0      0      0   100%
python_utils/time.py            74     17     32      2    78%   175, 200-222, 253
python_utils/types.py           13      0      0      0   100%
------------------------------------------------------------------------
TOTAL                          385     50    156      2    86%
Coverage HTML written to dir htmlcov

FAIL Required test coverage of 100.0% not reached. Total coverage: 85.95%
=================================================================================== mypy ===================================================================================
Success: no issues found in 13 source files
====================================================================== 31 passed, 1 warning in 16.12s ======================================================================

@wolph
Copy link
Owner

wolph commented Jan 15, 2022

Did you see my previous message about installing the extra tests packages?

It looks like those might be missing.

@kloczek
Copy link
Author

kloczek commented Jan 15, 2022

Did you see my previous message about installing the extra tests packages?

Yes however I've lost somehow pytest-asyncio (my fault).

It looks like those might be missing.

Looking one more time on that list I still don't have packaged types-setuptools.
Do you think that that eooe may be caused by missing that module? 🤔

@kloczek
Copy link
Author

kloczek commented May 4, 2022

Looks like latest pytest is failing

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.1.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.1.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra python_utils
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.1.0, configfile: pytest.ini
plugins: mypy-0.9.1, flake8-1.1.1, asyncio-0.18.2
asyncio: mode=legacy
collected 31 items

python_utils/__init__.py .F                                                                                                                                          [  6%]
python_utils/__about__.py .                                                                                                                                          [  9%]
python_utils/aio.py .                                                                                                                                                [ 12%]
python_utils/compat.py .                                                                                                                                             [ 16%]
python_utils/containers.py F..                                                                                                                                       [ 25%]
python_utils/converters.py .......                                                                                                                                   [ 48%]
python_utils/decorators.py ...                                                                                                                                       [ 58%]
python_utils/formatters.py ...                                                                                                                                       [ 67%]
python_utils/import_.py .                                                                                                                                            [ 70%]
python_utils/logger.py ..                                                                                                                                            [ 77%]
python_utils/terminal.py .                                                                                                                                           [ 80%]
python_utils/time.py .....                                                                                                                                           [ 96%]
python_utils/types.py .                                                                                                                                              [100%]

================================================================================= FAILURES =================================================================================
_______________________________________________________________________________ test session _______________________________________________________________________________
mypy exited with status 1.
________________________________________________________________________ python_utils/containers.py ________________________________________________________________________
37: error: Signature of "update" incompatible with supertype "MutableMapping"
37: note:      Superclass:
37: note:          @overload
37: note:          def update(self, SupportsKeysAndGetItem[KT, VT], **kwargs: VT) -> None
37: note:          @overload
37: note:          def update(self, Iterable[Tuple[KT, VT]], **kwargs: VT) -> None
37: note:          @overload
37: note:          def update(self, **kwargs: VT) -> None
37: note:      Subclass:
37: note:          def update(self, *args: Union[Mapping[Any, Any], Iterable[Union[Tuple[Any, Any], Mapping[Any, Any]]]], **kwargs: Any) -> None
============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:191
  /usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:191: DeprecationWarning: The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.
    config.issue_config_time_warning(LEGACY_MODE, stacklevel=2)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================== mypy ===================================================================================
Found 1 error in 1 file (checked 13 source files)
========================================================================= short test summary info ==========================================================================
FAILED python_utils/__init__.py::mypy-status
FAILED python_utils/containers.py::mypy
================================================================= 2 failed, 29 passed, 1 warning in 7.21s ==================================================================

asyncio_mode warning can be fixed by add asyncio_mode=auto to pytest.ini

@wolph wolph closed this as completed in b34e299 May 12, 2022
@wolph
Copy link
Owner

wolph commented May 12, 2022

Thank you for letting me know :)

I've fixed all the issues and enabled nit-picky mode by default for the documentation tests

@kloczek
Copy link
Author

kloczek commented May 29, 2022

Just tested new version and generally test suite look better now.
Tis time I've tested as well pytest without specify python_utils as param.
Looks like mypy complains about content of the setup.py

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.3.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.3.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0, configfile: pytest.ini
plugins: mypy-0.9.1, asyncio-0.18.2
asyncio: mode=strict
collected 71 items

setup.py FF                                                                                                                                                          [  2%]
_python_utils_tests/__init__.py .                                                                                                                                    [  4%]
_python_utils_tests/test_decorators.py ...                                                                                                                           [  8%]
_python_utils_tests/test_generators.py ....                                                                                                                          [ 14%]
_python_utils_tests/test_import.py .......                                                                                                                           [ 23%]
_python_utils_tests/test_logger.py ..                                                                                                                                [ 26%]
_python_utils_tests/test_python_utils.py ..                                                                                                                          [ 29%]
_python_utils_tests/test_time.py ............                                                                                                                        [ 46%]
docs/conf.py .                                                                                                                                                       [ 47%]
python_utils/__init__.py .                                                                                                                                           [ 49%]
python_utils/__about__.py .                                                                                                                                          [ 50%]
python_utils/aio.py .                                                                                                                                                [ 52%]
python_utils/compat.py .                                                                                                                                             [ 53%]
python_utils/containers.py ...                                                                                                                                       [ 57%]
python_utils/converters.py .......                                                                                                                                   [ 67%]
python_utils/decorators.py ....                                                                                                                                      [ 73%]
python_utils/exceptions.py ..                                                                                                                                        [ 76%]
python_utils/formatters.py ....                                                                                                                                      [ 81%]
python_utils/generators.py .                                                                                                                                         [ 83%]
python_utils/import_.py .                                                                                                                                            [ 84%]
python_utils/logger.py ...                                                                                                                                           [ 88%]
python_utils/loguru.py .                                                                                                                                             [ 90%]
python_utils/terminal.py .                                                                                                                                           [ 91%]
python_utils/time.py .....                                                                                                                                           [ 98%]
python_utils/types.py .                                                                                                                                              [100%]

================================================================================= FAILURES =================================================================================
_________________________________________________________________________________ setup.py _________________________________________________________________________________
4: error: Skipping analyzing "setuptools": module is installed, but missing library stubs or py.typed marker
4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
_______________________________________________________________________________ test session _______________________________________________________________________________
mypy exited with status 1.
============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1252
  /usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1252: PytestConfigWarning: Unknown config option: flake8-ignore

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================== mypy ===================================================================================
Found 1 error in 1 file (checked 25 source files)
========================================================================= short test summary info ==========================================================================
FAILED setup.py::mypy
FAILED setup.py::mypy-status
================================================================= 2 failed, 69 passed, 1 warning in 37.77s =================================================================

I'm not sure how to do that but IMO it would be good to make flake8 optional without emitting that warning.
If you don't want to make it optional I would be glad to learn how to supress that exact warning because in my build procedure I don't need 'flake8` as obligatory rpm spec file BuildRequires.

@kloczek
Copy link
Author

kloczek commented May 29, 2022

Also I've manually tested pytest output with pytest-black installed and I think that it would be good to apply patch generated by that extension

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.3.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-utils-3.3.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra --black
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0, configfile: pytest.ini
plugins: mypy-0.9.1, asyncio-0.18.2, black-0.3.12
asyncio: mode=strict
collected 96 items

setup.py FFF                                                                                                                                                         [  3%]
_python_utils_tests/__init__.py ..                                                                                                                                   [  5%]
_python_utils_tests/test_decorators.py F...                                                                                                                          [  9%]
_python_utils_tests/test_generators.py F....                                                                                                                         [ 14%]
_python_utils_tests/test_import.py F.......                                                                                                                          [ 22%]
_python_utils_tests/test_logger.py F..                                                                                                                               [ 26%]
_python_utils_tests/test_python_utils.py F..                                                                                                                         [ 29%]
_python_utils_tests/test_time.py F............                                                                                                                       [ 42%]
docs/conf.py F.                                                                                                                                                      [ 44%]
python_utils/__init__.py F.                                                                                                                                          [ 46%]
python_utils/__about__.py F.                                                                                                                                         [ 48%]
python_utils/aio.py F.                                                                                                                                               [ 51%]
python_utils/compat.py ..                                                                                                                                            [ 53%]
python_utils/containers.py F...                                                                                                                                      [ 57%]
python_utils/converters.py F.......                                                                                                                                  [ 65%]
python_utils/decorators.py F....                                                                                                                                     [ 70%]
python_utils/exceptions.py F..                                                                                                                                       [ 73%]
python_utils/formatters.py F....                                                                                                                                     [ 79%]
python_utils/generators.py F.                                                                                                                                        [ 81%]
python_utils/import_.py F.                                                                                                                                           [ 83%]
python_utils/logger.py F...                                                                                                                                          [ 87%]
python_utils/loguru.py F.                                                                                                                                            [ 89%]
python_utils/terminal.py F.                                                                                                                                          [ 91%]
python_utils/time.py F.....                                                                                                                                          [ 97%]
python_utils/types.py F.                                                                                                                                             [100%]

================================================================================= FAILURES =================================================================================
____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/setup.py   2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/setup.py   2022-05-29 04:46:43.153873 +0000
@@ -4,50 +4,51 @@
 import setuptools

 # To prevent importing about and thereby breaking the coverage info we use this
 # exec hack
 about: typing.Dict[str, str] = {}
-with open('python_utils/__about__.py') as fp:
+with open("python_utils/__about__.py") as fp:
     exec(fp.read(), about)

-if os.path.isfile('README.rst'):
-    long_description = open('README.rst').read()
+if os.path.isfile("README.rst"):
+    long_description = open("README.rst").read()
 else:
-    long_description = 'See http://pypi.python.org/pypi/python-utils/'
+    long_description = "See http://pypi.python.org/pypi/python-utils/"

-if __name__ == '__main__':
+if __name__ == "__main__":
     setuptools.setup(
-        python_requires='>3.6.0',
-        name='python-utils',
-        version=about['__version__'],
-        author=about['__author__'],
-        author_email=about['__author_email__'],
-        description=about['__description__'],
-        url=about['__url__'],
-        license='BSD',
-        packages=setuptools.find_packages(exclude=[
-            '_python_utils_tests', '*.__pycache__']),
+        python_requires=">3.6.0",
+        name="python-utils",
+        version=about["__version__"],
+        author=about["__author__"],
+        author_email=about["__author_email__"],
+        description=about["__description__"],
+        url=about["__url__"],
+        license="BSD",
+        packages=setuptools.find_packages(
+            exclude=["_python_utils_tests", "*.__pycache__"]
+        ),
         long_description=long_description,
-        tests_require=['pytest'],
+        tests_require=["pytest"],
         extras_require={
-            'loguru': [
-                'loguru',
+            "loguru": [
+                "loguru",
             ],
-            'docs': [
-                'mock',
-                'sphinx',
-                'python-utils',
+            "docs": [
+                "mock",
+                "sphinx",
+                "python-utils",
             ],
-            'tests': [
-                'flake8',
-                'pytest',
-                'pytest-cov',
-                'pytest-mypy',
-                'pytest-flake8',
-                'pytest-asyncio',
-                'sphinx',
-                'types-setuptools',
-                'loguru',
+            "tests": [
+                "flake8",
+                "pytest",
+                "pytest-cov",
+                "pytest-mypy",
+                "pytest-flake8",
+                "pytest-asyncio",
+                "sphinx",
+                "types-setuptools",
+                "loguru",
             ],
         },
-        classifiers=['License :: OSI Approved :: BSD License'],
+        classifiers=["License :: OSI Approved :: BSD License"],
     )

_________________________________________________________________________________ setup.py _________________________________________________________________________________
4: error: Skipping analyzing "setuptools": module is installed, but missing library stubs or py.typed marker
4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
_______________________________________________________________________________ test session _______________________________________________________________________________
mypy exited with status 1.
____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_decorators.py     2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_decorators.py     2022-05-29 04:47:17.147670 +0000
@@ -12,11 +12,11 @@
     return mock


 def test_sample_called(random):
     demo_function = MagicMock()
-    decorated = sample(0.5)(demo_function)
+    decorated = sample(0.5)(demo_function)
     random.return_value = 0.4
     decorated()
     random.return_value = 0.0
     decorated()
     args = [1, 2]
@@ -31,6 +31,6 @@
     decorated = sample(0.5)(demo_function)
     random.return_value = 0.5
     decorated()
     random.return_value = 1.0
     decorated()
-    assert demo_function.call_count == 0
\ No newline at end of file
+    assert demo_function.call_count == 0

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_generators.py     2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_generators.py     2022-05-29 04:47:17.379777 +0000
@@ -14,12 +14,11 @@

 @pytest.mark.asyncio
 async def test_abatcher_timed():
     batches = []
     async for batch in python_utils.abatcher(
-        python_utils.acount(stop=10, delay=0.08),
-        interval=0.2
+        python_utils.acount(stop=10, delay=0.08), interval=0.2
     ):
         batches.append(batch)

     assert len(batches) == 3
     assert sum(len(batch) for batch in batches) == 10

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_import.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_import.py 2022-05-29 04:47:18.439799 +0000
@@ -6,43 +6,44 @@
         relative_import(i)


 def relative_import(level):
     locals_ = {}
-    globals_ = {'__name__': 'python_utils.import_'}
-    import_.import_global('.formatters', locals_=locals_, globals_=globals_)
-    assert 'camel_to_underscore' in globals_
+    globals_ = {"__name__": "python_utils.import_"}
+    import_.import_global(".formatters", locals_=locals_, globals_=globals_)
+    assert "camel_to_underscore" in globals_


 def test_import_globals_without_inspection():
     locals_ = {}
-    globals_ = {'__name__': __name__}
-    import_.import_global(
-        'python_utils.formatters', locals_=locals_, globals_=globals_)
-    assert 'camel_to_underscore' in globals_
+    globals_ = {"__name__": __name__}
+    import_.import_global("python_utils.formatters", locals_=locals_, globals_=globals_)
+    assert "camel_to_underscore" in globals_


 def test_import_globals_single_method():
     locals_ = {}
-    globals_ = {'__name__': __name__}
+    globals_ = {"__name__": __name__}
     import_.import_global(
-        'python_utils.formatters', ['camel_to_underscore'], locals_=locals_,
-        globals_=globals_)
-    assert 'camel_to_underscore' in globals_
+        "python_utils.formatters",
+        ["camel_to_underscore"],
+        locals_=locals_,
+        globals_=globals_,
+    )
+    assert "camel_to_underscore" in globals_


 def test_import_globals_with_inspection():
-    import_.import_global('python_utils.formatters')
-    assert 'camel_to_underscore' in globals()
+    import_.import_global("python_utils.formatters")
+    assert "camel_to_underscore" in globals()


 def test_import_globals_missing_module():
-    import_.import_global(
-        'python_utils.spam', exceptions=ImportError, locals_=locals())
-    assert 'camel_to_underscore' in globals()
+    import_.import_global("python_utils.spam", exceptions=ImportError, locals_=locals())
+    assert "camel_to_underscore" in globals()


 def test_import_locals_missing_module():
     import_.import_global(
-        'python_utils.spam', exceptions=ImportError, globals_=globals())
-    assert 'camel_to_underscore' in globals()
-
+        "python_utils.spam", exceptions=ImportError, globals_=globals()
+    )
+    assert "camel_to_underscore" in globals()

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_logger.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_logger.py 2022-05-29 04:47:18.686776 +0000
@@ -1,19 +1,19 @@
 import pytest

 from python_utils.loguru import Logurud


-loguru = pytest.importorskip('loguru')
+loguru = pytest.importorskip("loguru")


 def test_logurud():
     class MyClass(Logurud):
         pass

     my_class = MyClass()
-    my_class.debug('debug')
-    my_class.info('info')
-    my_class.warning('warning')
-    my_class.error('error')
-    my_class.exception('exception')
-    my_class.log(0, 'log')
+    my_class.debug("debug")
+    my_class.info("info")
+    my_class.warning("warning")
+    my_class.error("error")
+    my_class.exception("exception")
+    my_class.log(0, "log")

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_python_utils.py   2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_python_utils.py   2022-05-29 04:47:18.886611 +0000
@@ -5,6 +5,5 @@
     # The setup.py requires this so we better make sure they exist :)
     assert __about__.__version__
     assert __about__.__author__
     assert __about__.__author_email__
     assert __about__.__description__
-

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_time.py   2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/_python_utils_tests/test_time.py   2022-05-29 04:47:19.226729 +0000
@@ -6,43 +6,58 @@

 import python_utils


 @pytest.mark.parametrize(
-    'timeout,interval,interval_multiplier,maximum_interval,iterable,result', [
+    "timeout,interval,interval_multiplier,maximum_interval,iterable,result",
+    [
         (0.2, 0.1, 0.4, 0.2, python_utils.acount, 2),
         (0.3, 0.1, 0.4, 0.2, python_utils.acount(), 3),
         (0.3, 0.06, 1.0, None, python_utils.acount, 5),
-        (timedelta(seconds=0.1), timedelta(seconds=0.06),
-         2.0, timedelta(seconds=0.1), python_utils.acount, 2),
-    ])
+        (
+            timedelta(seconds=0.1),
+            timedelta(seconds=0.06),
+            2.0,
+            timedelta(seconds=0.1),
+            python_utils.acount,
+            2,
+        ),
+    ],
+)
 @pytest.mark.asyncio
-async def test_aio_timeout_generator(timeout, interval, interval_multiplier,
-                                     maximum_interval, iterable, result):
+async def test_aio_timeout_generator(
+    timeout, interval, interval_multiplier, maximum_interval, iterable, result
+):
     i = None
     async for i in python_utils.aio_timeout_generator(
-        timeout, interval, iterable,
-        maximum_interval=maximum_interval
+        timeout, interval, iterable, maximum_interval=maximum_interval
     ):
         pass

     assert i == result


 @pytest.mark.parametrize(
-    'timeout,interval,interval_multiplier,maximum_interval,iterable,result', [
-        (0.01, 0.006, 0.5, 0.01, 'abc', 'c'),
+    "timeout,interval,interval_multiplier,maximum_interval,iterable,result",
+    [
+        (0.01, 0.006, 0.5, 0.01, "abc", "c"),
         (0.01, 0.006, 0.5, 0.01, itertools.count, 2),
         (0.01, 0.006, 0.5, 0.01, itertools.count(), 2),
-        (0.01, 0.006, 1.0, None, 'abc', 'c'),
-        (timedelta(seconds=0.01),
-         timedelta(seconds=0.006),
-         2.0, timedelta(seconds=0.01),
-         itertools.count, 2),
-    ])
-def test_timeout_generator(timeout, interval, interval_multiplier,
-                           maximum_interval, iterable, result):
+        (0.01, 0.006, 1.0, None, "abc", "c"),
+        (
+            timedelta(seconds=0.01),
+            timedelta(seconds=0.006),
+            2.0,
+            timedelta(seconds=0.01),
+            itertools.count,
+            2,
+        ),
+    ],
+)
+def test_timeout_generator(
+    timeout, interval, interval_multiplier, maximum_interval, iterable, result
+):
     i = None
     for i in python_utils.timeout_generator(
         timeout=timeout,
         interval=interval,
         interval_multiplier=interval_multiplier,
@@ -102,12 +117,11 @@
         async for i in generator():
             pass

     # Test regular timeout with clean exit
     @python_utils.aio_generator_timeout_detector_decorator(
-        timeout=0.05,
-        on_timeout=None
+        timeout=0.05, on_timeout=None
     )
     async def generator():
         for i in range(10):
             await asyncio.sleep(i / 100.0)
             yield i
@@ -128,12 +142,11 @@
         async for i in generator():
             pass

     # Test total timeout with clean exit
     @python_utils.aio_generator_timeout_detector_decorator(
-        total_timeout=0.1,
-        on_timeout=None
+        total_timeout=0.1, on_timeout=None
     )
     async def generator():
         for i in range(10):
             await asyncio.sleep(i / 100.0)
             yield i

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/docs/conf.py       2022-05-29 04:46:35.637674 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/docs/conf.py       2022-05-29 04:47:21.984024 +0000
@@ -11,17 +11,18 @@
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 #
 from datetime import date
 import os
 import sys
-sys.path.insert(0, os.path.abspath('..'))
+
+sys.path.insert(0, os.path.abspath(".."))

 from python_utils import __about__

 # -- Project information -----------------------------------------------------

-project = 'Python Utils'
+project = "Python Utils"
 author = __about__.__author__
 copyright = f'{date.today().year}, <a href="http://wol.ph/">{author}</a>'

 # The full version, including alpha/beta/rc tags
 release = __about__.__version__
@@ -41,36 +42,35 @@

 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
-    'sphinx.ext.autodoc',
-    'sphinx.ext.doctest',
-    'sphinx.ext.intersphinx',
-    'sphinx.ext.todo',
-    'sphinx.ext.coverage',
-    'sphinx.ext.viewcode',
+    "sphinx.ext.autodoc",
+    "sphinx.ext.doctest",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.todo",
+    "sphinx.ext.coverage",
+    "sphinx.ext.viewcode",
 ]

 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]

 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]


 # -- Options for HTML output -------------------------------------------------

 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'alabaster'
+html_theme = "alabaster"

 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 # html_static_path = ['_static']

-intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
-
+intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/__init__.py   2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/__init__.py   2022-05-29 04:47:22.216832 +0000
@@ -30,44 +30,44 @@
     timedelta_to_seconds,
     timeout_generator,
 )

 __all__ = [
-    'aio',
-    'generators',
-    'compat',
-    'converters',
-    'decorators',
-    'formatters',
-    'import_',
-    'logger',
-    'terminal',
-    'time',
-    'types',
-    'to_int',
-    'to_float',
-    'to_unicode',
-    'to_str',
-    'scale_1024',
-    'remap',
-    'set_attributes',
-    'listify',
-    'camel_to_underscore',
-    'timesince',
-    'import_global',
-    'get_terminal_size',
-    'timedelta_to_seconds',
-    'format_time',
-    'timeout_generator',
-    'acount',
-    'abatcher',
-    'batcher',
-    'aio_timeout_generator',
-    'aio_generator_timeout_detector_decorator',
-    'aio_generator_timeout_detector',
-    'delta_to_seconds',
-    'delta_to_seconds_or_none',
-    'reraise',
-    'raise_exception',
-    'Logged',
-    'LoggerBase',
+    "aio",
+    "generators",
+    "compat",
+    "converters",
+    "decorators",
+    "formatters",
+    "import_",
+    "logger",
+    "terminal",
+    "time",
+    "types",
+    "to_int",
+    "to_float",
+    "to_unicode",
+    "to_str",
+    "scale_1024",
+    "remap",
+    "set_attributes",
+    "listify",
+    "camel_to_underscore",
+    "timesince",
+    "import_global",
+    "get_terminal_size",
+    "timedelta_to_seconds",
+    "format_time",
+    "timeout_generator",
+    "acount",
+    "abatcher",
+    "batcher",
+    "aio_timeout_generator",
+    "aio_generator_timeout_detector_decorator",
+    "aio_generator_timeout_detector",
+    "delta_to_seconds",
+    "delta_to_seconds_or_none",
+    "reraise",
+    "raise_exception",
+    "Logged",
+    "LoggerBase",
 ]

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/__about__.py  2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/__about__.py  2022-05-29 04:47:22.418395 +0000
@@ -1,9 +1,10 @@
-__package_name__: str = 'python-utils'
-__author__: str = 'Rick van Hattem'
-__author_email__: str = 'Wolph@wol.ph'
+__package_name__: str = "python-utils"
+__author__: str = "Rick van Hattem"
+__author_email__: str = "Wolph@wol.ph"
 __description__: str = (
-    'Python Utils is a module with some convenient utilities not included '
-    'with the standard Python install')
-__url__: str = 'https://github.com/WoLpH/python-utils'
+    "Python Utils is a module with some convenient utilities not included "
+    "with the standard Python install"
+)
+__url__: str = "https://github.com/WoLpH/python-utils"
 # Omit type info due to automatic versioning script
-__version__ = '3.3.0'
+__version__ = "3.3.0"

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/aio.py        2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/aio.py        2022-05-29 04:47:22.608639 +0000
@@ -1,15 +1,15 @@
-'''
+"""
 Asyncio equivalents to regular Python functions.

-'''
+"""
 import asyncio
 import itertools


 async def acount(start=0, step=1, delay=0, stop=None):
-    '''Asyncio version of itertools.count()'''
+    """Asyncio version of itertools.count()"""
     for item in itertools.count(start, step):  # pragma: no branch
         if stop is not None and item >= stop:
             break

         yield item

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/containers.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/containers.py 2022-05-29 04:47:23.077700 +0000
@@ -7,44 +7,36 @@
 from . import types

 if typing.TYPE_CHECKING:
     import _typeshed  # noqa: F401

-KT = types.TypeVar('KT')
-VT = types.TypeVar('VT')
+KT = types.TypeVar("KT")
+VT = types.TypeVar("VT")
 DT = types.Dict[KT, VT]
 KT_cast = types.Optional[types.Callable[[Any], KT]]
 VT_cast = types.Optional[types.Callable[[Any], VT]]

 # Using types.Union instead of | since Python 3.7 doesn't fully support it
 DictUpdateArgs = types.Union[
     types.Mapping,
     types.Iterable[types.Union[types.Tuple[Any, Any], types.Mapping]],
-    '_typeshed.SupportsKeysAndGetItem[KT, VT]',
+    "_typeshed.SupportsKeysAndGetItem[KT, VT]",
 ]


 class CastedDictBase(types.Dict[KT, VT], abc.ABC):
     _key_cast: KT_cast
     _value_cast: VT_cast

     def __init__(
-            self,
-            key_cast: KT_cast = None,
-            value_cast: VT_cast = None,
-            *args,
-            **kwargs
+        self, key_cast: KT_cast = None, value_cast: VT_cast = None, *args, **kwargs
     ) -> None:
         self._value_cast = value_cast
         self._key_cast = key_cast
         self.update(*args, **kwargs)

-    def update(
-            self,
-            *args: DictUpdateArgs,
-            **kwargs: VT
-    ) -> None:
+    def update(self, *args: DictUpdateArgs, **kwargs: VT) -> None:
         if args:
             kwargs.update(*args)

         if kwargs:
             for key, value in kwargs.items():
@@ -56,11 +48,11 @@

         return super().__setitem__(key, value)


 class CastedDict(CastedDictBase):
-    '''
+    """
     Custom dictionary that casts keys and values to the specified typing.

     Note that you can specify the types for mypy and type hinting with:
     CastedDict[int, int](int, int)

@@ -88,21 +80,21 @@
     >>> d['3'] = '4'
     >>> d.update({'5': '6'})
     >>> d.update([('7', '8')])
     >>> d
     {1: 2, '3': '4', '5': '6', '7': '8'}
-    '''
+    """

     def __setitem__(self, key, value):
         if self._value_cast is not None:
             value = self._value_cast(value)

         super().__setitem__(key, value)


 class LazyCastedDict(CastedDictBase):
-    '''
+    """
     Custom dictionary that casts keys and lazily casts values to the specified
     typing. Note that the values are cast only when they are accessed and
     are not cached between executions.

     Note that you can specify the types for mypy and type hinting with:
@@ -141,11 +133,11 @@

     >>> list(d.items())
     [(1, 2), ('3', '4'), ('5', '6'), ('7', '8')]
     >>> d['3']
     '4'
-    '''
+    """

     def __setitem__(self, key, value):
         if self._key_cast is not None:
             key = self._key_cast(key)

@@ -175,9 +167,9 @@
         else:
             for value in super().values():
                 yield self._value_cast(value)


-if __name__ == '__main__':
+if __name__ == "__main__":
     import doctest

     doctest.testmod()

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/converters.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/converters.py 2022-05-29 04:47:23.468462 +0000
@@ -5,16 +5,16 @@

 from . import types


 def to_int(
-        input_: typing.Optional[str] = None,
-        default: int = 0,
-        exception: types.ExceptionsType = (ValueError, TypeError),
-        regexp: types.O[types.Pattern] = None,
+    input_: typing.Optional[str] = None,
+    default: int = 0,
+    exception: types.ExceptionsType = (ValueError, TypeError),
+    regexp: types.O[types.Pattern] = None,
 ) -> int:
-    r'''
+    r"""
     Convert the given input to an integer or return default

     When trying to convert the exceptions given in the exception parameter
     are automatically catched and the default will be returned.

@@ -70,19 +70,19 @@
     1
     >>> to_int('abc', regexp=123)
     Traceback (most recent call last):
     ...
     TypeError: unknown argument for regexp parameter: 123
-    '''
+    """
     if regexp is True:
-        regexp = re.compile(r'(\d+)')
+        regexp = re.compile(r"(\d+)")
     elif isinstance(regexp, str):
         regexp = re.compile(regexp)
-    elif hasattr(regexp, 'search'):
+    elif hasattr(regexp, "search"):
         pass
     elif regexp is not None:
-        raise TypeError('unknown argument for regexp parameter: %r' % regexp)
+        raise TypeError("unknown argument for regexp parameter: %r" % regexp)

     try:
         if regexp and input_:
             match = regexp.search(input_)
             if match:
@@ -95,16 +95,16 @@
     except exception:  # type: ignore
         return default


 def to_float(
-        input_: str,
-        default: int = 0,
-        exception: types.ExceptionsType = (ValueError, TypeError),
-        regexp: types.O[types.Pattern] = None,
+    input_: str,
+    default: int = 0,
+    exception: types.ExceptionsType = (ValueError, TypeError),
+    regexp: types.O[types.Pattern] = None,
 ) -> types.Number:
-    r'''
+    r"""
     Convert the given `input_` to an integer or return default

     When trying to convert the exceptions given in the exception parameter
     are automatically catched and the default will be returned.

@@ -150,20 +150,20 @@
     '1.00'
     >>> '%.2f' % to_float('abc', regexp=123)
     Traceback (most recent call last):
     ...
     TypeError: unknown argument for regexp parameter
-    '''
+    """

     if regexp is True:
-        regexp = re.compile(r'(\d+(\.\d+|))')
+        regexp = re.compile(r"(\d+(\.\d+|))")
     elif isinstance(regexp, str):
         regexp = re.compile(regexp)
-    elif hasattr(regexp, 'search'):
+    elif hasattr(regexp, "search"):
         pass
     elif regexp is not None:
-        raise TypeError('unknown argument for regexp parameter')
+        raise TypeError("unknown argument for regexp parameter")

     try:
         if regexp:
             match = regexp.search(input_)
             if match:
@@ -172,15 +172,15 @@
     except exception:
         return default


 def to_unicode(
-        input_: types.StringTypes,
-        encoding: str = 'utf-8',
-        errors: str = 'replace',
+    input_: types.StringTypes,
+    encoding: str = "utf-8",
+    errors: str = "replace",
 ) -> str:
-    '''Convert objects to unicode, if needed decodes string with the given
+    """Convert objects to unicode, if needed decodes string with the given
     encoding and errors settings.

     :rtype: str

     >>> to_unicode(b'a')
@@ -192,24 +192,24 @@
     >>> class Foo(object): __str__ = lambda s: u'a'
     >>> to_unicode(Foo())
     'a'
     >>> to_unicode(Foo)
     "<class 'python_utils.converters.Foo'>"
-    '''
+    """
     if isinstance(input_, bytes):
         input_ = input_.decode(encoding, errors)
     else:
         input_ = str(input_)
     return input_


 def to_str(
-        input_: types.StringTypes,
-        encoding: str = 'utf-8',
-        errors: str = 'replace',
+    input_: types.StringTypes,
+    encoding: str = "utf-8",
+    errors: str = "replace",
 ) -> bytes:
-    '''Convert objects to string, encodes to the given encoding
+    """Convert objects to string, encodes to the given encoding

     :rtype: str

     >>> to_str('a')
     b'a'
@@ -220,25 +220,26 @@
     >>> class Foo(object): __str__ = lambda s: u'a'
     >>> to_str(Foo())
     'a'
     >>> to_str(Foo)
     "<class 'python_utils.converters.Foo'>"
-    '''
+    """
     if isinstance(input_, bytes):
         pass
     else:
-        if not hasattr(input_, 'encode'):
+        if not hasattr(input_, "encode"):
             input_ = str(input_)

         input_ = input_.encode(encoding, errors)
     return input_


 def scale_1024(
-        x: types.Number, n_prefixes: int,
+    x: types.Number,
+    n_prefixes: int,
 ) -> types.Tuple[types.Number, types.Number]:
-    '''Scale a number down to a suitable size, based on powers of 1024.
+    """Scale a number down to a suitable size, based on powers of 1024.

     Returns the scaled number and the power of 1024 used.

     Use to format numbers of bytes to KiB, MiB, etc.

@@ -250,25 +251,27 @@
     (0.0, 0)
     >>> scale_1024(0.5, 2)
     (0.5, 0)
     >>> scale_1024(1, 2)
     (1.0, 0)
-    '''
+    """
     if x <= 0:
         power = 0
     else:
         power = min(int(math.log(x, 2) / 10), n_prefixes - 1)
     scaled = float(x) / (2 ** (10 * power))
     return scaled, power


 def remap(
-        value: types.DecimalNumber,
-        old_min: types.DecimalNumber, old_max: types.DecimalNumber,
-        new_min: types.DecimalNumber, new_max: types.DecimalNumber,
+    value: types.DecimalNumber,
+    old_min: types.DecimalNumber,
+    old_max: types.DecimalNumber,
+    new_min: types.DecimalNumber,
+    new_max: types.DecimalNumber,
 ) -> types.DecimalNumber:
-    '''
+    """
     remap a value from one range into another.

     >>> remap(500, 0, 1000, 0, 100)
     50
     >>> remap(250.0, 0.0, 1000.0, 0.0, 100.0)
@@ -335,26 +338,26 @@
         of any of the passed parameters ar `decimal.Decimal`, the return type
         will be `float` if any of the passed parameters are a `float` otherwise
         the returned type will be `int`.

     :rtype: int, float, decimal.Decimal
-    '''
+    """
     type_: types.Type[types.DecimalNumber]
     if (
-            isinstance(value, decimal.Decimal) or
-            isinstance(old_min, decimal.Decimal) or
-            isinstance(old_max, decimal.Decimal) or
-            isinstance(new_min, decimal.Decimal) or
-            isinstance(new_max, decimal.Decimal)
+        isinstance(value, decimal.Decimal)
+        or isinstance(old_min, decimal.Decimal)
+        or isinstance(old_max, decimal.Decimal)
+        or isinstance(new_min, decimal.Decimal)
+        or isinstance(new_max, decimal.Decimal)
     ):
         type_ = decimal.Decimal
     elif (
-            isinstance(value, float) or
-            isinstance(old_min, float) or
-            isinstance(old_max, float) or
-            isinstance(new_min, float) or
-            isinstance(new_max, float)
+        isinstance(value, float)
+        or isinstance(old_min, float)
+        or isinstance(old_max, float)
+        or isinstance(new_min, float)
+        or isinstance(new_max, float)
     ):
         type_ = float

     else:
         type_ = int
@@ -367,16 +370,14 @@

     old_range = old_max - old_min  # type: ignore
     new_range = new_max - new_min  # type: ignore

     if old_range == 0:
-        raise ValueError('Input range ({}-{}) is empty'.format(
-            old_min, old_max))
+        raise ValueError("Input range ({}-{}) is empty".format(old_min, old_max))

     if new_range == 0:
-        raise ValueError('Output range ({}-{}) is empty'.format(
-            new_min, new_max))
+        raise ValueError("Output range ({}-{}) is empty".format(new_min, new_max))

     new_value = (value - old_min) * new_range  # type: ignore

     if type_ == int:
         new_value //= old_range  # type: ignore

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/decorators.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/decorators.py 2022-05-29 04:47:23.725721 +0000
@@ -3,11 +3,11 @@
 import random
 from . import types


 def set_attributes(**kwargs):
-    '''Decorator to set attributes on functions and classes
+    """Decorator to set attributes on functions and classes

     A common usage for this pattern is the Django Admin where
     functions can get an optional short_description. To illustrate:

     Example from the Django admin using this decorator:
@@ -24,22 +24,22 @@
     >>> def upper_case_name(obj):
     ...     return ("%s %s" % (obj.first_name, obj.last_name)).upper()

     >>> upper_case_name.short_description = 'Name'

-    '''
+    """

     def _set_attributes(function):
         for key, value in kwargs.items():
             setattr(function, key, value)
         return function

     return _set_attributes


 def listify(collection: types.Callable = list, allow_empty: bool = True):
-    '''
+    """
     Convert any generator to a list or other type of collection.

     >>> @listify()
     ... def generator():
     ...     yield 1
@@ -79,11 +79,11 @@
     ...     yield 'a', 1
     ...     yield 'b', 2

     >>> dict_generator()
     {'a': 1, 'b': 2}
-    '''
+    """

     def _listify(function):
         @functools.wraps(function)
         def __listify(*args, **kwargs):
             result = function(*args, **kwargs)
@@ -95,11 +95,11 @@

     return _listify


 def sample(sample_rate: float):
-    '''
+    """
     Limit calls to a function based on given sample rate.
     Number of calls to the function will be roughly equal to
     sample_rate percentage.

     Usage:
@@ -108,16 +108,23 @@
     ... def demo_function(*args, **kwargs):
     ...     return 1

     Calls to *demo_function* will be limited to 50% approximatly.

-    '''
+    """
+
     def _sample(function):
         @functools.wraps(function)
         def __sample(*args, **kwargs):
             if random.random() < sample_rate:
                 return function(*args, **kwargs)
             else:
-                logging.debug('Skipped execution of %r(%r, %r) due to sampling', function, args, kwargs)  # noqa: E501
+                logging.debug(
+                    "Skipped execution of %r(%r, %r) due to sampling",
+                    function,
+                    args,
+                    kwargs,
+                )  # noqa: E501

         return __sample
+
     return _sample

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/exceptions.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/exceptions.py 2022-05-29 04:47:23.936041 +0000
@@ -4,19 +4,19 @@
 def raise_exception(
     exception_class: typing.Type[Exception],
     *args: typing.Any,
     **kwargs: typing.Any,
 ) -> typing.Callable:
-    '''
+    """
     Returns a function that raises an exception of the given type with the
     given arguments.

     >>> raise_exception(ValueError, 'spam')('eggs')
     Traceback (most recent call last):
         ...
     ValueError: spam
-    '''
+    """

     def raise_(*args_: typing.Any, **kwargs_: typing.Any) -> typing.Any:
         raise exception_class(*args, **kwargs)

     return raise_

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/formatters.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/formatters.py 2022-05-29 04:47:24.224922 +0000
@@ -2,11 +2,11 @@

 from python_utils import types


 def camel_to_underscore(name: str) -> str:
-    '''Convert camel case style naming to underscore/snake case style naming
+    """Convert camel case style naming to underscore/snake case style naming

     If there are existing underscores they will be collapsed with the
     to-be-added underscores. Multiple consecutive capital letters will not be
     split except for the last one.

@@ -18,37 +18,35 @@
     'spam_and_bacon'
     >>> camel_to_underscore('__SpamAndBacon__')
     '__spam_and_bacon__'
     >>> camel_to_underscore('__SpamANDBacon__')
     '__spam_and_bacon__'
-    '''
+    """
     output = []
     for i, c in enumerate(name):
         if i > 0:
             pc = name[i - 1]
-            if c.isupper() and not pc.isupper() and pc != '_':
+            if c.isupper() and not pc.isupper() and pc != "_":
                 # Uppercase and the previous character isn't upper/underscore?
                 # Add the underscore
-                output.append('_')
+                output.append("_")
             elif i > 3 and not c.isupper():
                 # Will return the last 3 letters to check if we are changing
                 # case
-                previous = name[i - 3:i]
+                previous = name[i - 3 : i]
                 if previous.isalpha() and previous.isupper():
-                    output.insert(len(output) - 1, '_')
+                    output.insert(len(output) - 1, "_")

         output.append(c.lower())

-    return ''.join(output)
+    return "".join(output)


 def apply_recursive(
-    function: types.Callable[[str], str],
-    data: types.OptionalScope = None,
-    **kwargs
+    function: types.Callable[[str], str], data: types.OptionalScope = None, **kwargs
 ) -> types.OptionalScope:
-    '''
+    """
     Apply a function to all keys in a scope recursively

     >>> apply_recursive(camel_to_underscore, {'SpamEggsAndBacon': 'spam'})
     {'spam_eggs_and_bacon': 'spam'}
     >>> apply_recursive(camel_to_underscore, {'SpamEggsAndBacon': {
@@ -60,11 +58,11 @@
     >>> b = apply_recursive(camel_to_underscore, a)
     >>> b
     {'a_b_c': 123, 'def': {'de_f': 456}}

     >>> apply_recursive(camel_to_underscore, None)
-    '''
+    """
     if data is None:
         return None

     elif isinstance(data, dict):
         return {
@@ -74,14 +72,13 @@
     else:
         return data


 def timesince(
-    dt: types.Union[datetime.datetime, datetime.timedelta],
-    default: str = 'just now'
+    dt: types.Union[datetime.datetime, datetime.timedelta], default: str = "just now"
 ) -> str:
-    '''
+    """
     Returns string representing 'time since' e.g.
     3 days ago, 5 hours ago etc.

     >>> now = datetime.datetime.now()
     >>> timesince(now)
@@ -118,34 +115,34 @@
     '1 hour and 2 minutes ago'
     >>> timesince(now - datetime.timedelta(seconds=3721))
     '1 hour and 2 minutes ago'
     >>> timesince(datetime.timedelta(seconds=3721))
     '1 hour and 2 minutes ago'
-    '''
+    """
     if isinstance(dt, datetime.timedelta):
         diff = dt
     else:
         now = datetime.datetime.now()
         diff = abs(now - dt)

     periods = (
-        (diff.days / 365, 'year', 'years'),
-        (diff.days % 365 / 30, 'month', 'months'),
-        (diff.days % 30 / 7, 'week', 'weeks'),
-        (diff.days % 7, 'day', 'days'),
-        (diff.seconds / 3600, 'hour', 'hours'),
-        (diff.seconds % 3600 / 60, 'minute', 'minutes'),
-        (diff.seconds % 60, 'second', 'seconds'),
+        (diff.days / 365, "year", "years"),
+        (diff.days % 365 / 30, "month", "months"),
+        (diff.days % 30 / 7, "week", "weeks"),
+        (diff.days % 7, "day", "days"),
+        (diff.seconds / 3600, "hour", "hours"),
+        (diff.seconds % 3600 / 60, "minute", "minutes"),
+        (diff.seconds % 60, "second", "seconds"),
     )

     output = []
     for period, singular, plural in periods:
         if int(period):
             if int(period) == 1:
-                output.append('%d %s' % (period, singular))
+                output.append("%d %s" % (period, singular))
             else:
-                output.append('%d %s' % (period, plural))
+                output.append("%d %s" % (period, plural))

     if output:
-        return '%s ago' % ' and '.join(output[:2])
+        return "%s ago" % " and ".join(output[:2])

     return default

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/generators.py 2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/generators.py 2022-05-29 04:47:24.478103 +0000
@@ -7,17 +7,17 @@
 async def abatcher(
     generator: types.AsyncGenerator,
     batch_size: types.Optional[int] = None,
     interval: types.Optional[types.delta_type] = None,
 ):
-    '''
+    """
     Asyncio generator wrapper that returns items with a given batch size or
     interval (whichever is reached first).
-    '''
+    """
     batch: list = []

-    assert batch_size or interval, 'Must specify either batch_size or interval'
+    assert batch_size or interval, "Must specify either batch_size or interval"

     if interval:
         interval_s = python_utils.delta_to_seconds(interval)
         next_yield = time.perf_counter() + interval_s
     else:
@@ -46,13 +46,13 @@
             # want to burst too much
             next_yield = time.perf_counter() + interval_s


 def batcher(iterable, batch_size):
-    '''
+    """
     Generator wrapper that returns items with a given batch size
-    '''
+    """
     batch = []
     for item in iterable:
         batch.append(item)
         if len(batch) == batch_size:
             yield batch

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/import_.py    2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/import_.py    2022-05-29 04:47:24.739549 +0000
@@ -4,18 +4,18 @@
 class DummyException(Exception):
     pass


 def import_global(
-        name: str,
-        modules: types.Optional[types.List[str]] = None,
-        exceptions: types.ExceptionsType = DummyException,
-        locals_: types.OptionalScope = None,
-        globals_: types.OptionalScope = None,
-        level: int = -1,
+    name: str,
+    modules: types.Optional[types.List[str]] = None,
+    exceptions: types.ExceptionsType = DummyException,
+    locals_: types.OptionalScope = None,
+    globals_: types.OptionalScope = None,
+    level: int = -1,
 ) -> types.Any:
-    '''Import the requested items into the global scope
+    """Import the requested items into the global scope

     WARNING! this method _will_ overwrite your global scope
     If you have a variable named "path" and you call import_global('sys')
     it will be overwritten with sys.path

@@ -25,19 +25,20 @@
         exception (Exception): the exception to catch, e.g. ImportError
         `locals_`: the `locals()` method (in case you need a different scope)
         `globals_`: the `globals()` method (in case you need a different scope)
         level (int): the level to import from, this can be used for
         relative imports
-    '''
+    """
     frame = None
-    name_parts: types.List[str] = name.split('.')
+    name_parts: types.List[str] = name.split(".")
     modules_set: types.Set[str] = set()
     try:
         # If locals_ or globals_ are not given, autodetect them by inspecting
         # the current stack
         if locals_ is None or globals_ is None:
             import inspect
+
             frame = inspect.stack()[1][0]

             if locals_ is None:
                 locals_ = frame.f_locals

@@ -50,11 +51,11 @@
                 name_parts = name_parts[1:]
                 level = 1

             # raise IOError((name, level))
             module = __import__(
-                name=name_parts[0] or '.',
+                name=name_parts[0] or ".",
                 globals=globals_,
                 locals=locals_,
                 fromlist=name_parts[1:],
                 level=max(level, 0),
             )
@@ -63,24 +64,23 @@
             # spam.eggs should return eggs, not spam)
             try:
                 for attr in name_parts[1:]:
                     module = getattr(module, attr)
             except AttributeError:
-                raise ImportError('No module named ' + '.'.join(name_parts))
+                raise ImportError("No module named " + ".".join(name_parts))

             # If no list of modules is given, autodetect from either __all__
             # or a dir() of the module
             if not modules:
-                modules_set = set(getattr(module, '__all__', dir(module)))
+                modules_set = set(getattr(module, "__all__", dir(module)))
             else:
                 modules_set = set(modules).intersection(dir(module))

             # Add all items in modules to the global scope
             for k in set(dir(module)).intersection(modules_set):
-                if k and k[0] != '_':
+                if k and k[0] != "_":
                     globals_[k] = getattr(module, k)
         except exceptions as e:
             return e
     finally:
         # Clean up, just to be sure
-        del name, name_parts, modules, modules_set, exceptions, locals_, \
-            globals_, frame
+        del name, name_parts, modules, modules_set, exceptions, locals_, globals_, frame

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/logger.py     2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/logger.py     2022-05-29 04:47:25.016549 +0000
@@ -1,16 +1,16 @@
 import abc
 import functools
 import logging

-__all__ = ['Logged']
+__all__ = ["Logged"]

 import typing


 class LoggerBase(abc.ABC):
-    '''Class which automatically adds logging utilities to your class when
+    """Class which automatically adds logging utilities to your class when
     interiting. Expects `logger` to be a logging.Logger or compatible instance.

     Adds easy access to debug, info, warning, error, exception and log methods

     >>> class MyClass(LoggerBase):
@@ -24,18 +24,19 @@
     >>> my_class.info('info')
     >>> my_class.warning('warning')
     >>> my_class.error('error')
     >>> my_class.exception('exception')
     >>> my_class.log(0, 'log')
-    '''
+    """
+
     # Being a tad lazy here and not creating a Protocol.
     # The actual classes define the correct type anyway
     logger: typing.Any

     @classmethod
     def __get_name(cls, *name_parts: str) -> str:
-        return '.'.join(n.strip() for n in name_parts if n.strip())
+        return ".".join(n.strip() for n in name_parts if n.strip())

     @classmethod
     @functools.wraps(logging.debug)
     def debug(cls, msg: str, *args: typing.Any, **kwargs: typing.Any):
         cls.logger.debug(msg, *args, **kwargs)
@@ -65,11 +66,11 @@
     def log(cls, lvl: int, msg: str, *args: typing.Any, **kwargs: typing.Any):
         cls.logger.log(lvl, msg, *args, **kwargs)


 class Logged(LoggerBase):
-    '''Class which automatically adds a named logger to your class when
+    """Class which automatically adds a named logger to your class when
     interiting

     Adds easy access to debug, info, warning, error, exception and log methods

     >>> class MyClass(Logged):
@@ -84,18 +85,16 @@
     >>> my_class.exception('exception')
     >>> my_class.log(0, 'log')

     >>> my_class._Logged__get_name('spam')
     'spam'
-    '''
+    """

     logger: logging.Logger  # pragma: no cover

     @classmethod
     def __get_name(cls, *name_parts: str) -> str:
         return LoggerBase._LoggerBase__get_name(*name_parts)  # type: ignore

     def __new__(cls, *args, **kwargs):
-        cls.logger = logging.getLogger(
-            cls.__get_name(cls.__module__, cls.__name__)
-        )
+        cls.logger = logging.getLogger(cls.__get_name(cls.__module__, cls.__name__))
         return super(Logged, cls).__new__(cls)

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/loguru.py     2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/loguru.py     2022-05-29 04:47:25.228765 +0000
@@ -2,11 +2,11 @@

 from . import logger

 import loguru

-__all__ = ['Logurud']
+__all__ = ["Logurud"]


 class Logurud(logger.LoggerBase):
     logger: loguru.Logger


____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/terminal.py   2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/terminal.py   2022-05-29 04:47:25.572693 +0000
@@ -3,54 +3,58 @@

 from . import converters


 def get_terminal_size() -> typing.Tuple[int, int]:  # pragma: no cover
-    '''Get the current size of your terminal
+    """Get the current size of your terminal

     Multiple returns are not always a good idea, but in this case it greatly
     simplifies the code so I believe it's justified. It's not the prettiest
     function but that's never really possible with cross-platform code.

     Returns:
         width, height: Two integers containing width and height
-    '''
+    """
     w: typing.Optional[int]
     h: typing.Optional[int]

     try:
         # Default to 79 characters for IPython notebooks
         from IPython import get_ipython  # type: ignore
+
         ipython = get_ipython()
         from ipykernel import zmqshell  # type: ignore
+
         if isinstance(ipython, zmqshell.ZMQInteractiveShell):
             return 79, 24
     except Exception:  # pragma: no cover
         pass

     try:
         # This works for Python 3, but not Pypy3. Probably the best method if
         # it's supported so let's always try
         import shutil
+
         w, h = shutil.get_terminal_size()
         if w and h:
             # The off by one is needed due to progressbars in some cases, for
             # safety we'll always substract it.
             return w - 1, h
     except Exception:  # pragma: no cover
         pass

     try:
-        w = converters.to_int(os.environ.get('COLUMNS'))
-        h = converters.to_int(os.environ.get('LINES'))
+        w = converters.to_int(os.environ.get("COLUMNS"))
+        h = converters.to_int(os.environ.get("LINES"))
         if w and h:
             return w, h
     except Exception:  # pragma: no cover
         pass

     try:
         import blessings  # type: ignore
+
         terminal = blessings.Terminal()
         w = terminal.width
         h = terminal.height
         if w and h:
             return w, h
@@ -98,12 +102,14 @@
     except Exception:
         return None

     if res:
         import struct
-        (_, _, _, _, _, left, top, right, bottom, _, _) = \
-            struct.unpack("hhhhHhhhhhh", csbi.raw)
+
+        (_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack(
+            "hhhhHhhhhhh", csbi.raw
+        )
         w = right - left
         h = bottom - top
         return w, h
     else:
         return None
@@ -111,18 +117,25 @@

 def _get_terminal_size_tput():  # pragma: no cover
     # get terminal width src: http://stackoverflow.com/questions/263890/
     try:
         import subprocess
+
         proc = subprocess.Popen(
-            ['tput', 'cols'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
+            ["tput", "cols"],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+        )
         output = proc.communicate(input=None)
         w = int(output[0])
         proc = subprocess.Popen(
-            ['tput', 'lines'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
+            ["tput", "lines"],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+        )
         output = proc.communicate(input=None)
         h = int(output[0])
         return w, h
     except Exception:
         return None
@@ -132,12 +145,12 @@
     def ioctl_GWINSZ(fd):
         try:
             import fcntl
             import termios
             import struct
-            size = struct.unpack(
-                'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+
+            size = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234"))
         except Exception:
             return None
         return size

     size = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
@@ -149,10 +162,10 @@
             os.close(fd)
         except Exception:
             pass
     if not size:
         try:
-            size = os.environ['LINES'], os.environ['COLUMNS']
+            size = os.environ["LINES"], os.environ["COLUMNS"]
         except Exception:
             return None

     return int(size[1]), int(size[0])

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/time.py       2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/time.py       2022-05-29 04:47:26.011248 +0000
@@ -11,11 +11,11 @@
 # a pull request if you know a better way that functions for Python 2 and 3
 epoch = datetime.datetime(year=1970, month=1, day=1)


 def timedelta_to_seconds(delta: datetime.timedelta) -> types.Number:
-    '''Convert a timedelta to seconds with the microseconds as fraction
+    """Convert a timedelta to seconds with the microseconds as fraction

     Note that this method has become largely obsolete with the
     `timedelta.total_seconds()` method introduced in Python 2.7.

     >>> from datetime import timedelta
@@ -25,11 +25,11 @@
     '1'
     >>> '%.6f' % timedelta_to_seconds(timedelta(seconds=1, microseconds=1))
     '1.000001'
     >>> '%.6f' % timedelta_to_seconds(timedelta(microseconds=1))
     '0.000001'
-    '''
+    """
     # Only convert to float if needed
     if delta.microseconds:
         total = delta.microseconds * 1e-6
     else:
         total = 0
@@ -37,11 +37,11 @@
     total += delta.days * 60 * 60 * 24
     return total


 def delta_to_seconds(interval: types.delta_type) -> float:
-    '''
+    """
     Convert a timedelta to seconds

     >>> delta_to_seconds(datetime.timedelta(seconds=1))
     1
     >>> delta_to_seconds(datetime.timedelta(seconds=1, microseconds=1))
@@ -50,33 +50,33 @@
     1
     >>> delta_to_seconds('whatever')  # doctest: +ELLIPSIS
     Traceback (most recent call last):
         ...
     TypeError: Unknown type ...
-    '''
+    """
     if isinstance(interval, datetime.timedelta):
         return timedelta_to_seconds(interval)
     elif isinstance(interval, (int, float)):
         return interval
     else:
-        raise TypeError('Unknown type %s: %r' % (type(interval), interval))
+        raise TypeError("Unknown type %s: %r" % (type(interval), interval))


 def delta_to_seconds_or_none(
-    interval: types.Optional[types.delta_type]
+    interval: types.Optional[types.delta_type],
 ) -> types.Optional[float]:
     if interval is None:
         return None
     else:
         return delta_to_seconds(interval)


 def format_time(
     timestamp: types.timestamp_type,
-    precision: datetime.timedelta = datetime.timedelta(seconds=1)
+    precision: datetime.timedelta = datetime.timedelta(seconds=1),
 ) -> str:
-    '''Formats timedelta/datetime/seconds
+    """Formats timedelta/datetime/seconds

     >>> format_time('1')
     '0:00:01'
     >>> format_time(1.234)
     '0:00:01'
@@ -93,11 +93,11 @@
     >>> format_time(format_time)  # doctest: +ELLIPSIS
     Traceback (most recent call last):
         ...
     TypeError: Unknown type ...

-    '''
+    """
     precision_seconds = precision.total_seconds()

     if isinstance(timestamp, str):
         timestamp = float(timestamp)

@@ -113,11 +113,11 @@
         seconds = seconds - (seconds % precision_seconds)

         return str(datetime.timedelta(seconds=seconds))
     elif isinstance(timestamp, datetime.datetime):  # pragma: no cover
         # Python 2 doesn't have the timestamp method
-        if hasattr(timestamp, 'timestamp'):
+        if hasattr(timestamp, "timestamp"):
             seconds = timestamp.timestamp()
         else:
             seconds = timedelta_to_seconds(timestamp - epoch)

         # Truncate the number to the given precision
@@ -129,24 +129,23 @@
             dt = datetime.datetime.max
         return str(dt)
     elif isinstance(timestamp, datetime.date):
         return str(timestamp)
     elif timestamp is None:
-        return '--:--:--'
-    else:
-        raise TypeError('Unknown type %s: %r' % (type(timestamp), timestamp))
+        return "--:--:--"
+    else:
+        raise TypeError("Unknown type %s: %r" % (type(timestamp), timestamp))


 def timeout_generator(
     timeout: types.delta_type,
     interval: types.delta_type = datetime.timedelta(seconds=1),
-    iterable: types.Union[types.Iterable, types.Callable] =
-    itertools.count,
+    iterable: types.Union[types.Iterable, types.Callable] = itertools.count,
     interval_multiplier: float = 1.0,
     maximum_interval: types.Optional[types.delta_type] = None,
 ):
-    '''
+    """
     Generator that walks through the given iterable (a counter by default)
     until the float_timeout is reached with a configurable float_interval
     between items

     >>> for i in timeout_generator(0.1, 0.06):
@@ -171,11 +170,11 @@
     >>> for i in timeout_generator(timeout, interval, interval_multiplier=2):
     ...     print(i)
     0
     1
     2
-    '''
+    """
     float_timeout: float = delta_to_seconds(timeout)
     float_interval: float = delta_to_seconds(interval)
     float_maximum_interval: types.Optional[float] = delta_to_seconds_or_none(
         maximum_interval
     )
@@ -201,16 +200,15 @@


 async def aio_timeout_generator(
     timeout: types.delta_type,
     interval: types.delta_type = datetime.timedelta(seconds=1),
-    iterable: types.Union[
-        types.AsyncIterable, types.Callable] = aio.acount,
+    iterable: types.Union[types.AsyncIterable, types.Callable] = aio.acount,
     interval_multiplier: float = 1.0,
     maximum_interval: types.Optional[types.delta_type] = None,
 ):
-    '''
+    """
     Aync generator that walks through the given iterable (a counter by
     default) until the float_timeout is reached with a configurable
     float_interval between items

     The interval_exponent automatically increases the float_timeout with each
@@ -219,11 +217,11 @@
     float_interval with each run, specify 2.

     Doctests and asyncio are not friends, so no examples. But this function is
     effectively the same as the `timeout_generator` but it uses `async for`
     instead.
-    '''
+    """
     float_timeout: float = delta_to_seconds(timeout)
     float_interval: float = delta_to_seconds(interval)
     float_maximum_interval: types.Optional[float] = delta_to_seconds_or_none(
         maximum_interval
     )
@@ -253,49 +251,41 @@
     timeout: types.Optional[types.delta_type] = None,
     total_timeout: types.Optional[types.delta_type] = None,
     on_timeout: types.Optional[types.Callable] = exceptions.reraise,
     **kwargs,
 ):
-    '''
+    """
     This function is used to detect if an asyncio generator has not yielded
     an element for a set amount of time.

     The `on_timeout` argument is called with the `generator`, `timeout`,
     `total_timeout`, `exception` and the extra `**kwargs` to this function as
     arguments.
     If `on_timeout` is not specified, the exception is reraised.
     If `on_timeout` is `None`, the exception is silently ignored and the
     generator will finish as normal.
-    '''
+    """
     if total_timeout is None:
         total_timeout_end = None
     else:
-        total_timeout_end = time.perf_counter() + delta_to_seconds(
-            total_timeout
-        )
+        total_timeout_end = time.perf_counter() + delta_to_seconds(total_timeout)

     timeout_s = python_utils.delta_to_seconds_or_none(timeout)

     while True:
         try:
             if total_timeout_end and time.perf_counter() >= total_timeout_end:
-                raise asyncio.TimeoutError('Total timeout reached')
+                raise asyncio.TimeoutError("Total timeout reached")

             if timeout_s:
                 yield await asyncio.wait_for(generator.__anext__(), timeout_s)
             else:
                 yield await generator.__anext__()

         except asyncio.TimeoutError as exception:
             if on_timeout is not None:
-                await on_timeout(
-                    generator,
-                    timeout,
-                    total_timeout,
-                    exception,
-                    **kwargs
-                )
+                await on_timeout(generator, timeout, total_timeout, exception, **kwargs)
             break

         except StopAsyncIteration:
             break

@@ -304,18 +294,18 @@
     timeout: types.Optional[types.delta_type] = None,
     total_timeout: types.Optional[types.delta_type] = None,
     on_timeout: types.Optional[types.Callable] = exceptions.reraise,
     **kwargs,
 ):
-    '''
+    """
     A decorator wrapper for aio_generator_timeout_detector.
-    '''
+    """

     def _timeout_detector_decorator(generator: types.Callable):
-        '''
+        """
         The decorator itself.
-        '''
+        """

         @functools.wraps(generator)
         def wrapper(*args, **wrapper_kwargs):
             return aio_generator_timeout_detector(
                 generator(*args, **wrapper_kwargs),

____________________________________________________________________________ Black format check ____________________________________________________________________________
--- /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/types.py      2022-05-29 02:07:44 +0000
+++ /home/tkloczko/rpmbuild/BUILD/python-utils-3.3.0/python_utils/types.py      2022-05-29 04:47:26.879586 +0000
@@ -1,15 +1,17 @@
 import datetime
 import decimal
 from typing import *  # pragma: no cover
+
 # import * does not import Pattern
 from typing import Pattern

 # Quickhand for optional because it gets so much use. If only Python had
 # support for an optional type shorthand such as `SomeType?` instead of
 # `Optional[SomeType]`.
 from typing import Optional as O
+
 # Since the Union operator is only supported for Python 3.10, we'll create a
 # shorthand for it.
 from typing import Union as U

 Scope = Dict[str, Any]
@@ -32,109 +34,102 @@
 ]

 assert Pattern

 __all__ = [
-    'OptionalScope',
-    'Number',
-    'DecimalNumber',
-    'delta_type',
-    'timestamp_type',
-
+    "OptionalScope",
+    "Number",
+    "DecimalNumber",
+    "delta_type",
+    "timestamp_type",
     # The types from the typing module.
-
     # Super-special typing primitives.
-    'Annotated',
-    'Any',
-    'Callable',
-    'ClassVar',
-    'Concatenate',
-    'Final',
-    'ForwardRef',
-    'Generic',
-    'Literal',
-    'Optional',
-    'ParamSpec',
-    'Protocol',
-    'Tuple',
-    'Type',
-    'TypeVar',
-    'Union',
-
+    "Annotated",
+    "Any",
+    "Callable",
+    "ClassVar",
+    "Concatenate",
+    "Final",
+    "ForwardRef",
+    "Generic",
+    "Literal",
+    "Optional",
+    "ParamSpec",
+    "Protocol",
+    "Tuple",
+    "Type",
+    "TypeVar",
+    "Union",
     # ABCs (from collections.abc).
-    'AbstractSet',  # collections.abc.Set.
-    'ByteString',
-    'Container',
-    'ContextManager',
-    'Hashable',
-    'ItemsView',
-    'Iterable',
-    'Iterator',
-    'KeysView',
-    'Mapping',
-    'MappingView',
-    'MutableMapping',
-    'MutableSequence',
-    'MutableSet',
-    'Sequence',
-    'Sized',
-    'ValuesView',
-    'Awaitable',
-    'AsyncIterator',
-    'AsyncIterable',
-    'Coroutine',
-    'Collection',
-    'AsyncGenerator',
-    'AsyncContextManager',
-
+    "AbstractSet",  # collections.abc.Set.
+    "ByteString",
+    "Container",
+    "ContextManager",
+    "Hashable",
+    "ItemsView",
+    "Iterable",
+    "Iterator",
+    "KeysView",
+    "Mapping",
+    "MappingView",
+    "MutableMapping",
+    "MutableSequence",
+    "MutableSet",
+    "Sequence",
+    "Sized",
+    "ValuesView",
+    "Awaitable",
+    "AsyncIterator",
+    "AsyncIterable",
+    "Coroutine",
+    "Collection",
+    "AsyncGenerator",
+    "AsyncContextManager",
     # Structural checks, a.k.a. protocols.
-    'Reversible',
-    'SupportsAbs',
-    'SupportsBytes',
-    'SupportsComplex',
-    'SupportsFloat',
-    'SupportsIndex',
-    'SupportsInt',
-    'SupportsRound',
-
+    "Reversible",
+    "SupportsAbs",
+    "SupportsBytes",
+    "SupportsComplex",
+    "SupportsFloat",
+    "SupportsIndex",
+    "SupportsInt",
+    "SupportsRound",
     # Concrete collection types.
-    'ChainMap',
-    'Counter',
-    'Deque',
-    'Dict',
-    'DefaultDict',
-    'List',
-    'OrderedDict',
-    'Set',
-    'FrozenSet',
-    'NamedTuple',  # Not really a type.
-    'TypedDict',  # Not really a type.
-    'Generator',
-
+    "ChainMap",
+    "Counter",
+    "Deque",
+    "Dict",
+    "DefaultDict",
+    "List",
+    "OrderedDict",
+    "Set",
+    "FrozenSet",
+    "NamedTuple",  # Not really a type.
+    "TypedDict",  # Not really a type.
+    "Generator",
     # Other concrete types.
-    'BinaryIO',
-    'IO',
-    'Match',
-    'Pattern',
-    'TextIO',
-
+    "BinaryIO",
+    "IO",
+    "Match",
+    "Pattern",
+    "TextIO",
     # One-off things.
-    'AnyStr',
-    'cast',
-    'final',
-    'get_args',
-    'get_origin',
-    'get_type_hints',
-    'is_typeddict',
-    'NewType',
-    'no_type_check',
-    'no_type_check_decorator',
-    'NoReturn',
-    'overload',
-    'ParamSpecArgs',
-    'ParamSpecKwargs',
-    'runtime_checkable',
-    'Text',
-    'TYPE_CHECKING',
-    'TypeAlias',
-    'TypeGuard',
+    "AnyStr",
+    "cast",
+    "final",
+    "get_args",
+    "get_origin",
+    "get_type_hints",
+    "is_typeddict",
+    "NewType",
+    "no_type_check",
+    "no_type_check_decorator",
+    "NoReturn",
+    "overload",
+    "ParamSpecArgs",
+    "ParamSpecKwargs",
+    "runtime_checkable",
+    "Text",
+    "TYPE_CHECKING",
+    "TypeAlias",
+    "TypeGuard",
 ]

============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1252
  /usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1252: PytestConfigWarning: Unknown config option: flake8-ignore

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

../../../../../usr/lib/python3.8/site-packages/_pytest/nodes.py:146: 25 warnings
  /usr/lib/python3.8/site-packages/_pytest/nodes.py:146: PytestDeprecationWarning: <class 'pytest_black.BlackItem'> is not using a cooperative constructor and only takes {'fspath', 'parent'}.
  See https://docs.pytest.org/en/stable/deprecations.html#constructors-of-custom-pytest-node-subclasses-should-take-kwargs for more details.
    warnings.warn(

../../../../../usr/lib/python3.8/site-packages/_pytest/nodes.py:264: 25 warnings
  /usr/lib/python3.8/site-packages/_pytest/nodes.py:264: PytestRemovedIn8Warning: The (fspath: py.path.local) argument to BlackItem is deprecated. Please use the (path: pathlib.Path) argument instead.
  See https://docs.pytest.org/en/latest/deprecations.html#fspath-argument-for-node-constructors-replaced-with-pathlib-path
    return cls._create(parent=parent, **kw)

../../../../../usr/lib/python3.8/site-packages/_pytest/nodes.py:708
  /usr/lib/python3.8/site-packages/_pytest/nodes.py:708: PytestWarning: BlackItem is an Item subclass and should not be a collector, however its bases File are collectors.
  Please split the Collectors and the Item into separate node types.
  Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html
  example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================== mypy ===================================================================================
Found 1 error in 1 file (checked 25 source files)
========================================================================= short test summary info ==========================================================================
FAILED setup.py::BLACK
FAILED setup.py::mypy
FAILED setup.py::mypy-status
FAILED _python_utils_tests/test_decorators.py::BLACK
FAILED _python_utils_tests/test_generators.py::BLACK
FAILED _python_utils_tests/test_import.py::BLACK
FAILED _python_utils_tests/test_logger.py::BLACK
FAILED _python_utils_tests/test_python_utils.py::BLACK
FAILED _python_utils_tests/test_time.py::BLACK
FAILED docs/conf.py::BLACK
FAILED python_utils/__init__.py::BLACK
FAILED python_utils/__about__.py::BLACK
FAILED python_utils/aio.py::BLACK
FAILED python_utils/containers.py::BLACK
FAILED python_utils/converters.py::BLACK
FAILED python_utils/decorators.py::BLACK
FAILED python_utils/exceptions.py::BLACK
FAILED python_utils/formatters.py::BLACK
FAILED python_utils/generators.py::BLACK
FAILED python_utils/import_.py::BLACK
FAILED python_utils/logger.py::BLACK
FAILED python_utils/loguru.py::BLACK
FAILED python_utils/terminal.py::BLACK
FAILED python_utils/time.py::BLACK
FAILED python_utils/types.py::BLACK
=============================================================== 25 failed, 71 passed, 52 warnings in 44.22s ================================================================

@wolph
Copy link
Owner

wolph commented May 29, 2022

Thank you for letting me know @kloczek

That setting actually isn't needed anymore since the last migration to flake8 from pytest-flake8.

I've run black on the code as well but I'm not going to use the pytest-black plugin. I just moved away from pytest-flake8 because those kinds of plugins tend not to last (i've had to replace multiple over the years)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants