pax_global_header 0000666 0000000 0000000 00000000064 13440164350 0014512 g ustar 00root root 0000000 0000000 52 comment=294964dd2b7a68930ff520a58cdb314e58ecb1e7
first-2.0.2/ 0000775 0000000 0000000 00000000000 13440164350 0012642 5 ustar 00root root 0000000 0000000 first-2.0.2/.gitignore 0000664 0000000 0000000 00000000102 13440164350 0014623 0 ustar 00root root 0000000 0000000 *.pyc
__pycache__
build
dist
first.egg-info
.coverage
.cache
.tox
first-2.0.2/.travis.yml 0000664 0000000 0000000 00000000530 13440164350 0014751 0 ustar 00root root 0000000 0000000 dist: 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.rst 0000664 0000000 0000000 00000000253 13440164350 0014521 0 ustar 00root root 0000000 0000000 Credits
=======
“first” is written and maintained by Hynek Schlawack and various contributors:
- Artem Bezsmertnyi
- Łukasz Langa
- Nick Coghlan
- Vincent Driessen
first-2.0.2/CONTRIBUTING.rst 0000664 0000000 0000000 00000001570 13440164350 0015306 0 ustar 00root root 0000000 0000000 How 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.rst 0000664 0000000 0000000 00000002173 13440164350 0014540 0 ustar 00root root 0000000 0000000 .. :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/LICENSE 0000664 0000000 0000000 00000002043 13440164350 0013646 0 ustar 00root root 0000000 0000000 Copyright (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.in 0000664 0000000 0000000 00000000101 13440164350 0014370 0 ustar 00root root 0000000 0000000 include *.rst LICENSE test_first.py tox.ini Makefile .travis.yml
first-2.0.2/Makefile 0000664 0000000 0000000 00000000245 13440164350 0014303 0 ustar 00root root 0000000 0000000 test:
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.rst 0000664 0000000 0000000 00000010612 13440164350 0014331 0 ustar 00root root 0000000 0000000 first: 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.py 0000664 0000000 0000000 00000003276 13440164350 0014353 0 ustar 00root root 0000000 0000000 ## -*- 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.cfg 0000664 0000000 0000000 00000000123 13440164350 0014457 0 ustar 00root root 0000000 0000000 [bdist_wheel]
# we're pure-python
universal = 1
[metadata]
license_file = LICENSE
first-2.0.2/setup.py 0000664 0000000 0000000 00000003173 13440164350 0014360 0 ustar 00root root 0000000 0000000 from 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.py 0000664 0000000 0000000 00000001765 13440164350 0015413 0 ustar 00root root 0000000 0000000 import 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.ini 0000664 0000000 0000000 00000000744 13440164350 0014162 0 ustar 00root root 0000000 0000000 [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