pax_global_header00006660000000000000000000000064134401643500014512gustar00rootroot0000000000000052 comment=294964dd2b7a68930ff520a58cdb314e58ecb1e7 first-2.0.2/000077500000000000000000000000001344016435000126425ustar00rootroot00000000000000first-2.0.2/.gitignore000066400000000000000000000001021344016435000146230ustar00rootroot00000000000000*.pyc __pycache__ build dist first.egg-info .coverage .cache .tox first-2.0.2/.travis.yml000066400000000000000000000005301344016435000147510ustar00rootroot00000000000000dist: xenial group: travis_latest cache: directories: - $HOME/.cache/pip language: python matrix: include: - python: "2.6" dist: trusty - python: "2.7" - python: "3.3" dist: trusty - python: "3.4" - python: "3.5" - python: "3.6" - python: "3.7" install: - "pip install ." script: make test first-2.0.2/AUTHORS.rst000066400000000000000000000002531344016435000145210ustar00rootroot00000000000000Credits ======= “first” is written and maintained by Hynek Schlawack and various contributors: - Artem Bezsmertnyi - Łukasz Langa - Nick Coghlan - Vincent Driessen first-2.0.2/CONTRIBUTING.rst000066400000000000000000000015701344016435000153060ustar00rootroot00000000000000How To Contribute ================= Although very small, “first” is always open for suggestions and contributions by generous developers. I’ve collected a few tipps to get you started. Please: - Obey `PEP 8`_ and `PEP 257`_. - *Always* add tests and docs for your code. - Add yourself to the AUTHORS.rst_ file in an alphabetical fashion. - Write `good commit messages`_. - Ideally, squash_ your commits, i.e. make your pull requests just one commit. Thank you for considering to contribute to “first”! .. _`squash`: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html .. _`PEP 8`: http://www.python.org/dev/peps/pep-0008/ .. _`PEP 257`: http://www.python.org/dev/peps/pep-0257/ .. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html .. _`AUTHORS.rst`: https://github.com/hynek/first/blob/master/AUTHORS.rst first-2.0.2/HISTORY.rst000066400000000000000000000021731344016435000145400ustar00rootroot00000000000000.. :changelog: History ------- 2.0.2 (2019-03-07) ++++++++++++++++++ - Package tests as part of the dist. - Update docs. - Drop unsupported Python versions from CI. N.B. The code hasn't changed and ``first`` continues to work as before. 2.0.1 (2013-08-04) ++++++++++++++++++ - Make installable on systems that don’t support UTF-8 by default. - *Backward incompatible*: Drop support for Python older than 2.6, the previous fix gets too convoluted otherwise. Please don’t use Python < 2.6 anyway. I beg you. N.B. that this is a *pure packaging/QA matter*: the module still works perfectly with ancient Python versions. 2.0.0 (2012-10-13) ++++++++++++++++++ - `pred` proved to be rather useless. Changed to `key` which is just a selector. This is a *backward incompatible* change and the reason for going 2.0. - Add `default` argument which is returned instead of `None` if no true element is found. 1.0.2 (2012-10-09) ++++++++++++++++++ - Fix packaging. I get this never right the first time. :-/ 1.0.1 (2012-10-09) ++++++++++++++++++ - Documentation fixes only. 1.0.0 (2012-10-09) ++++++++++++++++++ - Initial release. first-2.0.2/LICENSE000066400000000000000000000020431344016435000136460ustar00rootroot00000000000000Copyright (c) 2012 Hynek Schlawack Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. first-2.0.2/MANIFEST.in000066400000000000000000000001011344016435000143700ustar00rootroot00000000000000include *.rst LICENSE test_first.py tox.ini Makefile .travis.yml first-2.0.2/Makefile000066400000000000000000000002451344016435000143030ustar00rootroot00000000000000test: python -m pytest --doctest-glob='*.rst' --doctest-modules --ignore=setup.py cov: python -m pytest --cov first --cov-report=term-missing . .PHONY: test cov first-2.0.2/README.rst000066400000000000000000000106121344016435000143310ustar00rootroot00000000000000first: The function you always missed in Python =============================================== .. image:: https://travis-ci.org/hynek/first.svg?branch=master :target: https://travis-ci.org/hynek/first :alt: CI Status ``first`` is an MIT-licensed Python package with a simple function that returns the first true value from an iterable, or ``None`` if there is none. If you need more power, you can also supply a ``key`` function that is used to judge the truth value of the element or a ``default`` value if ``None`` doesn’t fit your use case. N.B. I’m using the term “true” consistently with Python docs for ``any()`` and ``all()`` — it means that the value evaluates to true like: ``True``, ``1``, ``"foo"``, or ``[None]``. But **not**: ``None``, ``False``, ``[]``, or ``0``. In JavaScript, they call this “truthy”. Examples ======== A simple example to get started: .. code-block:: pycon >>> from first import first >>> first([0, None, False, [], (), 42]) 42 However, it’s especially useful for dealing with regular expressions in ``if/elif/else`` branches: .. code-block:: python import re from first import first re1 = re.compile('b(.*)') re2 = re.compile('a(.*)') m = first(regexp.match('abc') for regexp in [re1, re2]) if not m: print('no match!') elif m.re is re1: print('re1', m.group(1)) elif m.re is re2: print('re2', m.group(1)) The optional ``key`` function gives you even *more* selection power. If you want to return the first even number from a list, just do the following: .. code-block:: pycon >>> from first import first >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0) 4 ``default`` on the other hand allows you to specify a value that is returned if none of the elements is true: .. code-block:: pycon >>> from first import first >>> first([0, None, False, [], ()], default=42) 42 Usage ===== The package consists of one module consisting of one function: .. code-block:: python from first import first first(iterable, default=None, key=None) This function returns the first element of ``iterable`` that is true if ``key`` is ``None``. If there is no true element, the value of ``default`` is returned, which is ``None`` by default. If a callable is supplied in ``key``, the result of ``key(element)`` is used to judge the truth value of the element, but the element itself is returned. ``first`` has no dependencies and should work with any Python available. Alternatives ============ ``first`` brings nothing to the table that wasn’t possible before. However the existing solutions aren’t very idiomatic for such a common and simple problem. The following constructs are equivalent to ``first(seq)`` and work since Python 2.6: .. code-block:: python next(itertools.ifilter(None, seq), None) next(itertools.ifilter(bool, seq), None) next((x for x in seq if x), None) None of them is as pretty as I’d like them to be. The ``re`` example from above would look like the following: .. code-block:: python next(itertools.ifilter(None, (regexp.match('abc') for regexp in [re1, re2])), None) next((regexp.match('abc') for regexp in [re1, re2] if regexp.match('abc')), None) next((match for match in itertools.imap( operator.methodcaller('match', 'abc'), [re1, re2]) if match), None) Note that in the second case you have to call ``regexp.match()`` *twice*. The third example "fixes" that problem but also summons Cthulhu. For comparison, one more time the ``first``-version: .. code-block:: python first(regexp.match('abc') for regexp in [re1, re2]) Idiomatic, clear and readable. Pythonic. :) ---- As of version 0.6.5 from 2015, the excellent `boltons package `_ contains a ``first``-like function as part of its `iterutils module `_. Background ========== The idea for ``first`` goes back to a discussion I had with `Łukasz Langa`_ about how the ``re`` example above is painful in Python. We figured such a function is missing Python, however it’s rather unlikely we’d get it in and even if, it wouldn’t get in before 3.4 anyway, which is years away as of yours truly is writing this. So I decided to release it as a package for now. If it proves popular enough, it may even make it into Python’s stdlib in the end. .. _`Łukasz Langa`: https://github.com/ambv first-2.0.2/first.py000066400000000000000000000032761344016435000143530ustar00rootroot00000000000000## -*- coding: utf-8 -*- """ first ===== first is the function you always missed in Python. In the simplest case, it returns the first true element from an iterable: >>> from first import first >>> first([0, False, None, [], (), 42]) 42 Or None if there is none: >>> from first import first >>> first([]) is None True >>> first([0, False, None, [], ()]) is None True It also supports the passing of a key argument to help selecting the first match in a more advanced way. >>> from first import first >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0) 4 :copyright: (c) 2012 by Hynek Schlawack. :license: MIT, see LICENSE for more details. """ __title__ = 'first' __version__ = '2.0.2' __author__ = 'Hynek Schlawack' __license__ = 'MIT' __copyright__ = 'Copyright 2012 Hynek Schlawack' def first(iterable, default=None, key=None): """ Return first element of `iterable` that evaluates true, else return None (or an optional default value). >>> first([0, False, None, [], (), 42]) 42 >>> first([0, False, None, [], ()]) is None True >>> first([0, False, None, [], ()], default='ohai') 'ohai' >>> import re >>> m = first(re.match(regex, 'abc') for regex in ['b.*', 'a(.*)']) >>> m.group(1) 'bc' The optional `key` argument specifies a one-argument predicate function like that used for `filter()`. The `key` argument, if supplied, must be in keyword form. For example: >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0) 4 """ if key is None: for el in iterable: if el: return el else: for el in iterable: if key(el): return el return default first-2.0.2/setup.cfg000066400000000000000000000001231344016435000144570ustar00rootroot00000000000000[bdist_wheel] # we're pure-python universal = 1 [metadata] license_file = LICENSE first-2.0.2/setup.py000066400000000000000000000031731344016435000143600ustar00rootroot00000000000000from setuptools import setup import first import sys if sys.version_info[0] >= 3: openf = open else: import codecs openf = codecs.open def read(fn): with openf(fn, encoding='utf-8') as fp: return fp.read() setup( name='first', version=first.__version__, description='Return the first true value of an iterable.', long_description=(read('README.rst') + '\n\n' + read('HISTORY.rst') + '\n\n' + read('AUTHORS.rst')), long_description_content_type="text/x-rst", url='http://github.com/hynek/first/', license=first.__license__, author=first.__author__, author_email='hs@ox.cx', py_modules=['first'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries :: Python Modules', ], ) first-2.0.2/test_first.py000066400000000000000000000017651344016435000154130ustar00rootroot00000000000000import unittest from first import first isbool = lambda x: isinstance(x, bool) isint = lambda x: isinstance(x, int) odd = lambda x: isint(x) and x % 2 != 0 even = lambda x: isint(x) and x % 2 == 0 is_meaning_of_life = lambda x: x == 42 class TestFirst(unittest.TestCase): def test_empty_iterables(self): s = set() l = [] assert first(s) is None assert first(l) is None def test_default_value(self): s = set() l = [] assert first(s, default=42) == 42 assert first(l, default=3.14) == 3.14 l = [0, False, []] assert first(l, default=3.14) == 3.14 def test_selection(self): l = [(), 0, False, 3, []] assert first(l, default=42) == 3 assert first(l, key=isint) == 0 assert first(l, key=isbool) is False assert first(l, key=odd) == 3 assert first(l, key=even) == 0 assert first(l, key=is_meaning_of_life) is None if __name__ == '__main__': unittest.main() first-2.0.2/tox.ini000066400000000000000000000007441344016435000141620ustar00rootroot00000000000000[tox] envlist = py{27,34,35,36,37,py,pi-description},manifest [testenv] deps=pytest commands=python -m pytest --doctest-glob='*.rst' --doctest-modules --ignore=setup.py [testenv:pypi-description] basepython = python3.7 skip_install = true deps = twine pip >= 18.0.0 commands = pip wheel -w {envtmpdir}/build --no-deps . twine check {envtmpdir}/build/* [testenv:manifest] basepython = python3.7 deps = check-manifest skip_install = true commands = check-manifest