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.1.tar.gz (8.3 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.1-py3-none-any.whl (8.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: funcexpr-0.1.1.tar.gz
  • Upload date:
  • Size: 8.3 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.1.tar.gz
Algorithm Hash digest
SHA256 5496dca61c58a88f2dba8304cc55075016dac4a6f3882770f84b214d1dcb7ffe
MD5 c92cfdbab2bbc872d8f11ef082eab4de
BLAKE2b-256 f5d8766d2602450607831b9da2cc458bcf2ae5dcd8d4d1a102b3779129df709d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: funcexpr-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 572186d00382bdcef9da3339feec4095602ac356ec31396478848ffa8a3d663c
MD5 ac69f32d117604483f9b933dab74deeb
BLAKE2b-256 2ad25e59474fee02f22e8ac9a288d1bf41d2daed52097591c80c34086115beed

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