Skip to main content

A lightweight wrapper around numexpr that adds support for registered callables and automatic type normalization

Project description

funcexpr

A lightweight wrapper around numexpr that adds support for registered callables and automatic type normalization.

import numpy as np
import funcexpr as fe

a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])

def clip_positive(x):
    return np.clip(x, 0, None)

result = fe.evaluate("clip_positive(a - b) + b", ctx={"a": a, "b": b}, funcs={"clip_positive": clip_positive})
# array([4., 5., 6.])

Motivation

numexpr is fast, but it only accepts plain ndarrays and supports a limited set of built-in functions. funcexpr solves this by pre-processing the expression AST before handing it off to numexpr:

  • Registered callables are evaluated eagerly via NumPy and replaced with temporary variables before the expression reaches numexpr
  • numexpr built-ins (sin, cos, exp, etc.) at the top level of an expression are passed through to numexpr as-is; when they appear as arguments to a registered callable, they are resolved via NumPy during eager argument evaluation
  • Type normalization handles Python scalars, numpy scalars, and any object implementing __array__

Installation

pip install funcexpr

Usage

Basic

import numpy as np
import funcexpr as fe

a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])

fe.evaluate("a + b * 2", ctx={"a": a, "b": b})
# array([ 9., 12., 15.])

Registered callables

Any Python callable can be registered via funcs. Arguments are evaluated eagerly before the callable is invoked, so nested calls and expressions as arguments work naturally.

def double(x):
    return x * 2

fe.evaluate("double(a) + b", ctx={"a": a, "b": b}, funcs={"double": double})
# array([ 6.,  9., 12.])

Nested calls:

fe.evaluate("double(double(a))", ctx={"a": a}, funcs={"double": double})
# array([ 4.,  8., 12.])

Expression as argument:

fe.evaluate("double(a + b)", ctx={"a": a, "b": b}, funcs={"double": double})
# array([10., 14., 18.])

numexpr built-ins

numexpr built-ins used at the top level of an expression are passed through to numexpr directly and require no registration.

fe.evaluate("sin(a) + cos(b)", ctx={"a": a, "b": b})

When a built-in appears as an argument to a registered callable, it is resolved via NumPy during eager argument evaluation.

def my_func(x):
    return x * 2

fe.evaluate("my_func(sin(a))", ctx={"a": a}, funcs={"my_func": my_func})
# equivalent to my_func(np.sin(a))

Type normalization

ctx values and callable return values are normalized automatically.

Type Behavior
int, float, complex passed through as-is
np.generic (numpy scalar) passed through as-is
np.ndarray passed through as-is
object with __array__ converted via np.asarray()
anything else TypeError

API reference

def evaluate(
    expr: str,
    ctx: dict[str, np.ndarray | int | float | complex | np.generic],
    funcs: dict[str, Callable] | None = None,
) -> np.ndarray:
Parameter Type Default Description
expr str Python expression string to evaluate
ctx dict Variable context
funcs dict[str, Callable] | None None Registered callables

Returns np.ndarray.

Error handling

Condition Exception
Invalid expression SyntaxError
Variable missing from ctx KeyError (raised by numexpr)
Unnormalizable type in ctx or callable return value TypeError
Unrecognized function name TypeError (raised by numexpr)
Any other numexpr or callable error propagated as-is

Limitations

Callable arguments support a subset of AST node types: Constant, Name, BinOp, UnaryOp, and Call. Passing unsupported node types (e.g. Compare, BoolOp, IfExp) as callable arguments will raise TypeError. This may be lifted in a future version.

Design

funcexpr is intentionally minimal. It does one thing: let you use arbitrary callables inside numexpr expressions. More advanced features such as xarray DataArray support and axis alignment are out of scope and belong in a higher-level layer.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

funcexpr-0.1.0.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

funcexpr-0.1.0-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file funcexpr-0.1.0.tar.gz.

File metadata

  • Download URL: funcexpr-0.1.0.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.13.12 HTTPX/0.28.1

File hashes

Hashes for funcexpr-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7e469ce7ac7bb5d1c7501214f9dba3d5683057c7f585906ec4d7cf7882419602
MD5 8c651444f9a1ef532d27b4913ad1603a
BLAKE2b-256 7229db0385df93d5b8be90bf2520d378c0bb89e9b1edc4b654dc6a1210427896

See more details on using hashes here.

File details

Details for the file funcexpr-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: funcexpr-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.13.12 HTTPX/0.28.1

File hashes

Hashes for funcexpr-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 53d73efc0f7bede85c557b274591275d44d39ec16e6dc65e4b41ba29977e0c1e
MD5 3d3a14b3acca3f2f66b02c8c7b0ac83a
BLAKE2b-256 d4f5b19e23df611ba1bef3a5086cf9e5424f1369bc24d4d429fc283052448780

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page