Skip to content

Commit

Permalink
Add package source and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
dgilland committed Aug 15, 2018
1 parent 64636f7 commit 8601419
Show file tree
Hide file tree
Showing 16 changed files with 3,061 additions and 0 deletions.
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-e .
-e .[dev]
43 changes: 43 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[metadata]
name = fnc
author = Derrick Gilland
author_email = dgilland@gmail.com
url = https://github.com/dgilland/fnc
description = Functional utilities
long_description = file: README.rst, CHANGELOG.rst, LICENSE.rst
keywords = fnc
license = MIT License
classifiers =
Development Status :: 3 - Alpha
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Topic :: Software Development :: Libraries
Topic :: Software Development :: Libraries :: Python Modules


[options]
install_requires =


[options.extras_require]
dev =
coverage
flake8
pylint
pytest
pytest-cov
Sphinx
sphinx-rtd-theme
tox
twine
wheel


[bdist_wheel]
python-tag = py3
19 changes: 19 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python

from setuptools import setup, find_packages


package_dir = 'src'
packages = find_packages(package_dir)

version = {}
with open('{}/{}/__version__.py'.format(package_dir, packages[0])) as f:
exec(f.read(), version)


setup(
version=version['__version__'],
package_dir={'': package_dir},
packages=packages,
include_package_data=True
)
71 changes: 71 additions & 0 deletions src/fnc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""The fnc package.
Functional utilities
"""

from .__version__ import __version__


from .mappings import (
at,
defaults,
get,
has,
invert,
mapkeys,
mapvalues,
merge,
omit,
pick
)

from .sequences import (
chunk,
compact,
concat,
countby,
difference,
duplicates,
filter,
find,
findindex,
findlast,
findlastindex,
flatten,
flattendeep,
groupall,
groupby,
intercalate,
interleave,
intersection,
intersperse,
keyby,
map,
mapcat,
mapflat,
mapflatdeep,
partition,
reject,
union,
unzip,
without,
xor
)

from .utilities import (
after,
aspath,
atgetter,
before,
compose,
constant,
identity,
ismatch,
iteratee,
noop,
matches,
pathgetter,
pickgetter,
random,
retry
)
4 changes: 4 additions & 0 deletions src/fnc/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Project version information.
"""

__version__ = '0.0.0'
87 changes: 87 additions & 0 deletions src/fnc/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

from collections.abc import Iterable, Mapping, Sequence
from decimal import Decimal
import types


number_types = (int, float, Decimal)

Sentinel = object()


class _NotSet(object):
"""Represents an unset value. Used to differeniate between an explicit
``None`` and an unset value.
"""
def __bool__(self): # pragma: no cover
return False


NotSet = _NotSet()


class Seen(object):
"""A "seen" container for keeping track of elements of a sequence that have
been encountered before. It is optimized to work with both hashable and
unhashable values by storing hashable items in a ``set`` and unhashable
items in a ``list`` and then checking both containers for existence.
"""
def __init__(self):
self.hashable = set()
self.unhashable = []

def __contains__(self, value):
try:
return value in self.hashable
except TypeError:
return value in self.unhashable

def add(self, value):
if value in self:
return

try:
self.hashable.add(value)
except TypeError:
self.unhashable.append(value)


def iscollection(value):
"""Return whether `value` is iterable but not string or bytes."""
return isinstance(value, Iterable) and not isinstance(value, (str, bytes))


def isgenerator(value):
"""Return whether `value` is a generator or generator-like. The purpose
being to determine whether `value` will be exhausted if it is iterated
over.
"""
return (isinstance(value, types.GeneratorType) or
(hasattr(value, '__iter__') and hasattr(value, '__next__') and
not hasattr(value, '__getitem__')))


def iterate(mapping):
"""Attempt to iterate over `mapping` such that key-values pairs are yielded
per iteration. For dictionaries and other mappings, this would be the keys
and values. For lists and other sequences, this would be the indexes and
values. For other non-standard object types, some duck-typing will be used:
- If `mapping` has callable ``mapping.items()`` attribute, it will be
used.
- If `mapping` has callable ``mapping.keys()`` and ``__getitem__``
attributes, then ``(key, mapping[key])`` will be used.
- Otherwise, `iter(mapping)` will be returned.
"""
if (isinstance(mapping, Mapping) or
callable(getattr(mapping, 'items', None))):
return mapping.items()

if isinstance(mapping, Sequence):
return enumerate(mapping)

if (callable(getattr(mapping, 'keys', None)) and
hasattr(mapping, '__getitem__')):
return ((key, mapping[key]) for key in mapping.keys())

return iter(mapping)

0 comments on commit 8601419

Please sign in to comment.