pax_global_header00006660000000000000000000000064125075706440014524gustar00rootroot0000000000000052 comment=da0c30de9f64801eaeec96f0cbfd57ae35522bab snuggs-1.3.1/000077500000000000000000000000001250757064400130345ustar00rootroot00000000000000snuggs-1.3.1/.travis.yml000066400000000000000000000003241250757064400151440ustar00rootroot00000000000000language: 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 snuggs-1.3.1/CHANGES.txt000066400000000000000000000006341250757064400146500ustar00rootroot00000000000000Changes ======= 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. snuggs-1.3.1/LICENSE000066400000000000000000000020621250757064400140410ustar00rootroot00000000000000The 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. snuggs-1.3.1/README.rst000066400000000000000000000045141250757064400145270ustar00rootroot00000000000000====== 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 `__ (multithreading, elimination of temporary data, etc) are currently available. If you're looking to combine Numpy with a more complete Lisp, see `Hy `__: .. code-block:: clojure => (import numpy) => (* 2 (.asarray numpy [1 2 3])) array([2, 4, 6]) snuggs-1.3.1/setup.py000066400000000000000000000014721250757064400145520ustar00rootroot00000000000000from 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@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'], }) snuggs-1.3.1/snuggs/000077500000000000000000000000001250757064400143425ustar00rootroot00000000000000snuggs-1.3.1/snuggs/__init__.py000066400000000000000000000111561250757064400164570ustar00rootroot00000000000000""" 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 = "" 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) snuggs-1.3.1/test_snuggs.py000066400000000000000000000124701250757064400157570ustar00rootroot00000000000000import 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. @pytest.fixture def ones(): return numpy.ones((2, 2)) @pytest.fixture def truetrue(): return numpy.array([True, True]) @pytest.fixture def truefalse(): return numpy.array([True, False])