diff --git a/.travis.yml b/.travis.yml index 64cf253c88cbe2c4ff997e8e88167dd34560d3c6..0835bafe4270352d50fb95854b55292f6de8bb67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ python: cache: pip install: - pip install -r requirements.txt - - pip install pyyaml toml - pip install --force-reinstall "coverage>=4.0,<4.4" codeclimate-test-reporter # for codeclimate-test-reporter script: - bash tests/test_posix/test_hang_until_sig.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index ff642575c0149c8fdced8fe6091c3e75981c8cb8..49fd0d32b5255bddcf33ab42a40308b8239588bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # v2.2.17 -* _TBA_ +* added [choose](satella/coding/sequences/choose.py) # v2.2.16 diff --git a/docs/coding/sequences.rst b/docs/coding/sequences.rst new file mode 100644 index 0000000000000000000000000000000000000000..b08ecbf6cb883274dd009ef6edb65a0db8480aa4 --- /dev/null +++ b/docs/coding/sequences.rst @@ -0,0 +1,6 @@ +choose +====== + +To return the single element that returns true on given callable, use the following function: + +.. autofunction:: satella.coding.sequences.choose diff --git a/docs/index.rst b/docs/index.rst index 3168e26af403c6b7a8dcd1591cf786b1d8f47ecf..222e5ca42b0d12af7966471596e3324269c5c618 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,6 +2,10 @@ Welcome to satella's documentation! =================================== +Visit the project's page at GitHub_! + +.. _GitHub: https://github.com/piotrmaslanka/satella + .. toctree:: :maxdepth: 2 :caption: Contents @@ -11,6 +15,7 @@ Welcome to satella's documentation! coding/functions coding/structures coding/concurrent + coding/sequences instrumentation/traceback instrumentation/metrics exception_handling diff --git a/requirements.txt b/requirements.txt index a4d92cc08db6a0d8bfedbbbd620d1fb11f84677b..d36e77a2cf5accf12f8f22e842bfb5d8f2be0291 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ psutil +pyyaml +toml diff --git a/satella/__init__.py b/satella/__init__.py index 2b8a694a5795cc5c6dd4fbfbc56a330590fb3a30..342df280d318f2925ee80147d8078c3a87a40af1 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1,2 +1,2 @@ # coding=UTF-8 -__version__ = '2.2.17a1' +__version__ = '2.2.17' diff --git a/satella/coding/sequences/__init__.py b/satella/coding/sequences/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7dfca4cc8ccd57b8c01f2f2dca4a21191ca71e56 --- /dev/null +++ b/satella/coding/sequences/__init__.py @@ -0,0 +1,4 @@ +from .choose import choose + + +__all__ = ['choose'] diff --git a/satella/coding/sequences/choose.py b/satella/coding/sequences/choose.py new file mode 100644 index 0000000000000000000000000000000000000000..f323506cea1aa2bd1caab0dd52221bc810f1fafe --- /dev/null +++ b/satella/coding/sequences/choose.py @@ -0,0 +1,30 @@ +import typing as tp + +T = tp.TypeVar('T') + +__all__ = ['choose'] + + +def choose(filter_fun: tp.Callable[[T], bool], iterable: tp.Iterable[T]) -> T: + """ + Return a single value that exists in given iterable + + :param filter_fun: function that returns bool on the single value + :param iterable: iterable to examine + :return: single element in the iterable that matches given input + :raises ValueError: on multiple elements matching, or none at all + """ + elem_candidate = None + found = False + for elem in iterable: + if filter_fun(elem): + if found: + raise ValueError( + 'Multiple values (%s, %s) seen' % (repr(elem_candidate), repr(elem))) + elem_candidate = elem + found = True + + if not found: + raise ValueError('No elements matching given filter seen') + + return elem_candidate diff --git a/tests/test_coding/test_sequences.py b/tests/test_coding/test_sequences.py new file mode 100644 index 0000000000000000000000000000000000000000..307b756bc6258d627222e6b3f0db4fbfcfbee0cc --- /dev/null +++ b/tests/test_coding/test_sequences.py @@ -0,0 +1,14 @@ +import logging +import typing as tp +import unittest + +from satella.coding.sequences import choose + +logger = logging.getLogger(__name__) + + +class TestSequences(unittest.TestCase): + def test_choose(self): + self.assertEqual(choose(lambda x: x == 2, [1, 2, 3, 4, 5]), 2) + self.assertRaises(ValueError, lambda: choose(lambda x: x % 2 == 0, [1, 2, 3, 4, 5])) + self.assertRaises(ValueError, lambda: choose(lambda x: x == 0, [1, 2, 3, 4, 5]))