[python-snuggs] 01/02: Imported Upstream version 1.3.1
Johan Van de Wauw
johanvdw-guest at moszumanska.debian.org
Wed Apr 29 12:26:29 UTC 2015
This is an automated email from the git hooks/post-receive script.
johanvdw-guest pushed a commit to branch master
in repository python-snuggs.
commit e59942de7c216a5364d5321167a3c9773e55042f
Author: Johan Van de Wauw <johan.vandewauw at gmail.com>
Date: Fri Apr 24 21:26:13 2015 +0200
Imported Upstream version 1.3.1
---
.travis.yml | 12 +++
CHANGES.txt | 22 ++++++
LICENSE | 22 ++++++
README.rst | 106 +++++++++++++++++++++++++++
setup.py | 30 ++++++++
snuggs/__init__.py | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++
test_snuggs.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 609 insertions(+)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..673c733
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,12 @@
+language: python
+python:
+ - "2.7"
+ - "3.4"
+install:
+ - "pip install pytest"
+ - "pip install coveralls"
+ - "pip install -e ."
+script:
+ - coverage run --source=snuggs -m py.test
+after_success:
+ - coveralls
diff --git a/CHANGES.txt b/CHANGES.txt
new file mode 100644
index 0000000..8cd9018
--- /dev/null
+++ b/CHANGES.txt
@@ -0,0 +1,22 @@
+Changes
+=======
+
+1.3.1 (2015-04-03)
+------------------
+- Revert to operator functions in op_map (#4).
+
+1.3.0 (2015-03-30)
+------------------
+- Added nil keyword (#2).
+
+1.2.0 (2015-03-25)
+------------------
+- Helpfully formatted error messages, as Python SyntaxError.
+
+1.1.0 (2015-02-17)
+------------------
+- Add `map` and `partial` higher-order functions.
+
+1.0.0 (2015-02-13)
+------------------
+- First release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3e1c06d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Mapbox
+
+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.
+
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..67e8ade
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,106 @@
+======
+snuggs
+======
+
+.. image:: https://travis-ci.org/mapbox/snuggs.svg?branch=master
+ :target: https://travis-ci.org/mapbox/snuggs
+
+.. image:: https://coveralls.io/repos/mapbox/snuggs/badge.svg
+ :target: https://coveralls.io/r/mapbox/snuggs
+
+Snuggs are s-expressions for Numpy
+
+.. code-block:: python
+
+ >>> snuggs.eval("(+ (asarray 1 1) (asarray 2 2))")
+ array([3, 3])
+
+Syntax
+======
+
+Snuggs wraps Numpy in expressions with the following syntax:
+
+.. code-block::
+
+ expression = "(" (operator | function) *arg ")"
+ arg = expression | name | number | string
+
+Examples
+========
+
+Addition of two numbers
+-----------------------
+
+.. code-block:: python
+
+ import snuggs
+ snuggs.eval('(+ 1 2)')
+ # 3
+
+Multiplication of a number and an array
+---------------------------------------
+
+Arrays can be created using ``asarray``.
+
+.. code-block:: python
+
+ snuggs.eval("(* 3.5 (asarray 1 1))")
+ # array([ 3.5, 3.5])
+
+Evaluation context
+------------------
+
+Expressions can also refer by name to arrays in a local context.
+
+.. code-block:: python
+
+ snuggs.eval("(+ (asarray 1 1) b)", b=np.array([2, 2]))
+ # array([3, 3])
+
+Functions and operators
+=======================
+
+Arithmetic (``* + / -``) and logical (``< <= == != >= > & |``) operators are
+available. Members of the ``numpy`` module such as ``asarray()``, ``mean()``,
+and ``where()`` are also available.
+
+.. code-block:: python
+
+ snuggs.eval("(mean (asarray 1 2 4))")
+ # 2.3333333333333335
+
+.. code-block:: python
+
+ snuggs.eval("(where (& tt tf) 1 0)",
+ tt=numpy.array([True, True]),
+ tf=numpy.array([True, False]))
+ # array([1, 0])
+
+Higher-order functions
+======================
+
+New in snuggs 1.1 are higher-order functions ``map`` and ``partial``.
+
+.. code-block:: python
+
+ snuggs.eval("((partial * 2) 2)")
+ # 4
+
+ snuggs.eval('(asarray (map (partial * 2) (asarray 1 2 3)))')
+ # array([2, 4, 6])
+
+Performance notes
+=================
+
+Snuggs makes simple calculator programs possible. None of the optimizations
+of, e.g., `numexpr <https://github.com/pydata/numexpr>`__ (multithreading,
+elimination of temporary data, etc) are currently available.
+
+If you're looking to combine Numpy with a more complete Lisp, see
+`Hy <https://github.com/hylang/hy>`__:
+
+.. code-block:: clojure
+
+ => (import numpy)
+ => (* 2 (.asarray numpy [1 2 3]))
+ array([2, 4, 6])
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..dbb588a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,30 @@
+from codecs import open as codecs_open
+from setuptools import setup, find_packages
+
+
+# Get the long description from the relevant file
+with codecs_open('README.rst', encoding='utf-8') as f:
+ long_description = f.read()
+
+
+setup(name='snuggs',
+ version='1.3.1',
+ description=u"Snuggs are s-expressions for Numpy",
+ long_description=long_description,
+ classifiers=[],
+ keywords='',
+ author=u"Sean Gillies",
+ author_email='sean at mapbox.com',
+ url='https://github.com/mapbox/snuggs',
+ license='MIT',
+ packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ 'click',
+ 'numpy',
+ 'pyparsing'
+ ],
+ extras_require={
+ 'test': ['pytest'],
+ })
diff --git a/snuggs/__init__.py b/snuggs/__init__.py
new file mode 100644
index 0000000..e91cf98
--- /dev/null
+++ b/snuggs/__init__.py
@@ -0,0 +1,210 @@
+"""
+Snuggs are s-expressions for Numpy.
+"""
+
+import functools
+import itertools
+import operator
+import re
+import sys
+
+from pyparsing import (
+ alphanums, ZeroOrMore, nums, oneOf, Word, Literal, Combine, QuotedString,
+ ParseException, Forward, Group, CaselessLiteral, Optional, alphas,
+ OneOrMore, ParseResults, ParseException)
+
+import numpy
+
+
+__all__ = ['eval']
+__version__ = "1.3.1"
+
+# Python 2-3 compatibility
+string_types = (str,) if sys.version_info[0] >= 3 else (basestring,)
+
+
+class Context(object):
+
+ def __init__(self):
+ self._data = {}
+
+ def add(self, name, val):
+ self._data[name] = val
+
+ def get(self, name):
+ return self._data[name]
+
+ def lookup(self, index, subindex=None):
+ s = list(self._data.values())[int(index)-1]
+ if subindex:
+ return s[int(subindex)-1]
+ else:
+ return s
+
+ def clear(self):
+ self._data = {}
+
+_ctx = Context()
+
+
+class ctx(object):
+
+ def __init__(self, **kwds):
+ self.kwds = kwds
+
+ def __enter__(self):
+ _ctx.clear()
+ for k, v in self.kwds.items():
+ _ctx.add(k, v)
+ return self
+
+ def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
+ self.kwds = None
+ _ctx.clear()
+
+
+class ExpressionError(SyntaxError):
+ """Snuggs specific syntax errors"""
+ filename = "<string>"
+ lineno = 1
+
+op_map = {
+ '*': operator.mul,
+ '+': operator.add,
+ '/': operator.truediv,
+ '-': operator.sub,
+ '<': operator.lt,
+ '<=': operator.le,
+ '==': operator.eq,
+ '!=': operator.ne,
+ '>=': operator.ge,
+ '>': operator.gt,
+ '&': operator.and_,
+ '|': operator.or_,
+ }
+
+def asarray(*args):
+ if len(args) == 1 and hasattr(args[0], '__iter__'):
+ return numpy.asanyarray(list(args[0]))
+ else:
+ return numpy.asanyarray(list(args))
+
+func_map = {
+ 'asarray': asarray,
+ 'read': _ctx.lookup,
+ 'take': lambda a, idx: numpy.take(a, idx-1, axis=0),
+ }
+
+higher_func_map = {
+ 'map': map if sys.version_info[0] >= 3 else itertools.imap,
+ 'partial': functools.partial,
+ }
+
+# Definition of the grammar.
+decimal = Literal('.')
+e = CaselessLiteral('E')
+sign = Literal('+') | Literal('-')
+number = Word(nums)
+name = Word(alphas)
+nil = Literal('nil').setParseAction(lambda s, l, t: [None])
+
+def resolve_var(s, l, t):
+ try:
+ return _ctx.get(t[0])
+ except KeyError:
+ err = ExpressionError(
+ "name '%s' is not defined" % t[0])
+ err.text = s
+ err.offset = l + 1
+ raise err
+
+var = name.setParseAction(resolve_var)
+
+integer = Combine(
+ Optional(sign) +
+ number
+ ).setParseAction(lambda s, l, t: int(t[0]))
+
+real = Combine(
+ integer +
+ decimal + Optional(number) +
+ Optional(e + integer)
+ ).setParseAction(lambda s, l, t: float(t[0]))
+
+string = QuotedString("'") | QuotedString('"')
+
+lparen = Literal('(').suppress()
+rparen = Literal(')').suppress()
+
+op = oneOf(' '.join(op_map.keys())).setParseAction(
+ lambda s, l, t: op_map[t[0]])
+
+
+def resolve_func(s, l, t):
+ try:
+ return func_map[t[0]] if t[0] in func_map else getattr(numpy, t[0])
+ except AttributeError:
+ err = ExpressionError(
+ "'%s' is not a function or operator" % t[0])
+ err.text = s
+ err.offset = l + 1
+ raise err
+
+func = Word(alphanums + '_').setParseAction(resolve_func)
+
+higher_func = oneOf('map partial').setParseAction(
+ lambda s, l, t: higher_func_map[t[0]])
+
+func_expr = Forward()
+higher_func_expr = Forward()
+expr = higher_func_expr | func_expr
+
+operand = higher_func_expr | func_expr | nil | var | real | integer | string
+
+func_expr << Group(
+ lparen +
+ (higher_func_expr | op | func) +
+ operand +
+ ZeroOrMore(operand) +
+ rparen)
+
+higher_func_expr << Group(
+ lparen +
+ higher_func +
+ (nil | higher_func_expr | op | func) +
+ ZeroOrMore(operand) +
+ rparen)
+
+
+def processArg(arg):
+ if not isinstance(arg, ParseResults):
+ return arg
+ else:
+ return processList(arg)
+
+
+def processList(lst):
+ args = [processArg(x) for x in lst[1:]]
+ func = processArg(lst[0])
+ return func(*args)
+
+
+def handleLine(line):
+ try:
+ result = expr.parseString(line)
+ return processList(result[0])
+ except ParseException as exc:
+ text = str(exc)
+ m = re.search(r'(Expected .+) \(at char (\d+)\), \(line:(\d+)', text)
+ msg = m.group(1)
+ if 'map|partial' in msg:
+ msg = "expected a function or operator"
+ err = ExpressionError(msg)
+ err.text = line
+ err.offset = int(m.group(2)) + 1
+ raise err
+
+
+def eval(source, **kwds):
+ with ctx(**kwds):
+ return handleLine(source)
diff --git a/test_snuggs.py b/test_snuggs.py
new file mode 100644
index 0000000..ca5d074
--- /dev/null
+++ b/test_snuggs.py
@@ -0,0 +1,207 @@
+import numpy
+import pytest
+
+import snuggs
+
+# Fixtures follow the tests. See the end of the file.
+
+
+def test_integer():
+ assert list(snuggs.integer.parseString('1')) == [1]
+
+
+def test_real():
+ assert list(snuggs.real.parseString('1.1')) == [1.1]
+
+
+def test_int_expr():
+ assert snuggs.eval('(+ 1 2)') == 3
+
+
+def test_real_expr():
+ assert round(snuggs.eval('(* 0.1 0.2)'), 3) == 0.02
+
+
+def test_int_real_expr():
+ assert snuggs.eval('(+ 2 1.1)') == 3.1
+
+
+def test_real_int_expr():
+ assert snuggs.eval('(+ 1.1 2)') == 3.1
+
+
+def test_arr_var(ones):
+ r = snuggs.eval('(+ foo 0)', foo=ones)
+ assert list(r.flatten()) == [1, 1, 1, 1]
+
+
+def test_arr_lookup(ones):
+ r = snuggs.eval('(read 1)', foo=ones)
+ assert list(r.flatten()) == [1, 1, 1, 1]
+
+
+def test_arr_lookup_2(ones):
+ r = snuggs.eval('(read 1 1)', foo=ones)
+ assert list(r.flatten()) == [1, 1]
+
+
+def test_arr_take(ones):
+ r = snuggs.eval('(take foo 1)', foo=ones)
+ assert list(r.flatten()) == [1, 1]
+ r = snuggs.eval('(take foo 2)', foo=ones)
+ assert list(r.flatten()) == [1, 1]
+
+
+def test_int_arr_expr(ones):
+ result = snuggs.eval('(+ foo 1)', foo=ones)
+ assert list(result.flatten()) == [2, 2, 2, 2]
+
+
+def test_int_arr_expr_by_name(ones):
+ result = snuggs.eval('(+ (read 1) 1.5)', foo=ones)
+ assert list(result.flatten()) == [2.5, 2.5, 2.5, 2.5]
+
+
+def test_int_arr_read(ones):
+ result = snuggs.eval('(+ (read 1 1) 1.5)', foo=ones)
+ assert list(result.flatten()) == [2.5, 2.5]
+
+
+def test_list(ones):
+ result = snuggs.eval(
+ '(asarray (take foo 1) (take foo 1) (take bar 1) (take bar 1))',
+ foo=ones, bar=ones)
+ assert list(result.flatten()) == [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
+
+
+def test_eq(ones):
+ ones[0][0] = 2
+ result = snuggs.eval('(== foo 1)', foo=ones)
+ assert list(result.flatten()) == [False, True, True, True]
+
+
+def test_or(truetrue, truefalse):
+ result = snuggs.eval(
+ '(| foo bar)', foo=truetrue, bar=truefalse)
+ assert list(result.flatten()) == [True, True]
+
+
+def test_and(truetrue, truefalse):
+ result = snuggs.eval(
+ '(& foo bar)', foo=truetrue, bar=truefalse)
+ assert list(result.flatten()) == [True, False]
+
+
+def test_ones_like(truefalse):
+ result = snuggs.eval("(ones_like foo 'uint8')", foo=truefalse)
+ assert list(result.flatten()) == [1.0, 1.0]
+
+
+def test_full_like(truefalse):
+ result = snuggs.eval("(full_like foo 3.14 'float64')", foo=truefalse)
+ assert list(result.flatten()) == [3.14, 3.14]
+ result = snuggs.eval('(full_like foo 3.14 "float64")', foo=truefalse)
+ assert list(result.flatten()) == [3.14, 3.14]
+
+
+def test_ufunc(truetrue, truefalse):
+ result = snuggs.eval(
+ '(where (& foo bar) 1 0)', foo=truetrue, bar=truefalse)
+ assert list(result.flatten()) == [1.0, 0.0]
+
+
+def test_partial():
+ result = snuggs.eval('((partial * 2) 2)')
+ assert result == 4
+
+
+def test_map_func():
+ result = snuggs.eval('(map sqrt (asarray 1 4 9))')
+ assert list(result) == [1, 2, 3]
+
+
+def test_map_partial():
+ result = snuggs.eval('(map (partial * 2) (asarray 1 2 3))')
+ assert list(result) == [2, 4, 6]
+
+
+def test_map_asarray():
+ result = snuggs.eval('(asarray (map (partial * 2) (asarray 1 2 3)))')
+ assert list(result) == [2, 4, 6]
+
+
+def test_nil():
+ assert snuggs.eval('(== nil nil)')
+ assert not snuggs.eval('(== 1 nil)')
+ assert not snuggs.eval('(== nil 1)')
+ assert snuggs.eval('(!= 1 nil)')
+ assert snuggs.eval('(!= nil 1)')
+
+
+def test_masked_arr():
+ foo = numpy.ma.masked_equal(numpy.array([0, 0, 0, 1], dtype='uint8'), 0)
+ r = snuggs.eval('(+ foo 1)', foo=foo)
+ assert list(r.data.flatten()) == [0, 0, 0, 2]
+ assert list(r.flatten()) == [numpy.ma.masked, numpy.ma.masked, numpy.ma.masked, 2]
+
+
+# Parse and syntax error testing.
+def test_missing_closing_paren():
+ with pytest.raises(SyntaxError) as excinfo:
+ result = snuggs.eval("(+ 1 2")
+ assert excinfo.value.lineno == 1
+ assert excinfo.value.offset == 7
+ assert str(excinfo.value) == 'Expected ")"'
+
+
+def test_missing_func():
+ with pytest.raises(SyntaxError) as excinfo:
+ result = snuggs.eval("(0 1 2)")
+ assert excinfo.value.lineno == 1
+ assert excinfo.value.offset == 2
+ assert str(excinfo.value) == "'0' is not a function or operator"
+
+
+def test_missing_func2():
+ with pytest.raises(SyntaxError) as excinfo:
+ result = snuggs.eval("(# 1 2)")
+ assert excinfo.value.lineno == 1
+ assert excinfo.value.offset == 2
+ assert str(excinfo.value) == "expected a function or operator"
+
+
+def test_undefined_var():
+ with pytest.raises(SyntaxError) as excinfo:
+ result = snuggs.eval("(+ 1 bogus)")
+ assert excinfo.value.lineno == 1
+ assert excinfo.value.offset == 6
+ assert str(excinfo.value) == "name 'bogus' is not defined"
+
+
+def test_bogus_higher_order_func():
+ with pytest.raises(SyntaxError) as excinfo:
+ result = snuggs.eval("((bogus * 2) 2)")
+ assert excinfo.value.lineno == 1
+ assert excinfo.value.offset == 3
+ assert str(excinfo.value) == "expected a function or operator"
+
+
+def test_type_error():
+ with pytest.raises(TypeError) as excinfo:
+ result = snuggs.eval("(+ 1 'bogus')")
+
+
+# Fixtures.
+ at pytest.fixture
+def ones():
+ return numpy.ones((2, 2))
+
+
+ at pytest.fixture
+def truetrue():
+ return numpy.array([True, True])
+
+
+ at pytest.fixture
+def truefalse():
+ return numpy.array([True, False])
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/python-snuggs.git
More information about the Pkg-grass-devel
mailing list