Metadata-Version: 2.4
Name: dilemma
Version: 0.3.2
Summary: This is an expression language python package.
Author-email: Patrick Dobbs <patrick.dobbs@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Patrick Dobbs
        
        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.
        
Platform: unix
Platform: linux
Platform: osx
Platform: cygwin
Platform: win32
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: POSIX
Classifier: Operating System :: Unix
Classifier: Operating System :: MacOS
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: lark
Requires-Dist: jq
Requires-Dist: click
Requires-Dist: pyyaml
Requires-Dist: jsonpath-ng>=1.7.0
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: codecov; extra == "test"
Requires-Dist: coverage[toml]; extra == "test"
Requires-Dist: pre-commit; extra == "test"
Requires-Dist: pyyaml; extra == "test"
Requires-Dist: hypothesis>=6.136.4; extra == "test"
Provides-Extra: doc
Requires-Dist: mkdocs; extra == "doc"
Requires-Dist: mkdocstrings; extra == "doc"
Requires-Dist: mkdocstrings[python]; extra == "doc"
Requires-Dist: mkdocs-material; extra == "doc"
Requires-Dist: Pygments; extra == "doc"
Provides-Extra: dev
Requires-Dist: click; extra == "dev"
Requires-Dist: isort; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Provides-Extra: all
Requires-Dist: dilemma[dev,doc,test]; extra == "all"
Dynamic: license-file

# Dilemma Expression Language

[![CI](https://github.com/patrickcd/dilemma/workflows/CI/badge.svg)](https://github.com/patrickcd/dilemma/actions)
[![codecov](https://codecov.io/gh/patrickcd/dilemma/branch/main/graph/badge.svg)](https://codecov.io/gh/patrickcd/dilemma)
[![PyPI version](https://badge.fury.io/py/dilemma.svg)](https://badge.fury.io/py/dilemma)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)

A secure, powerful expression evaluation engine for Python applications that makes complex logical expressions readable and maintainable.

## Why Dilemma?

Instead of writing complex Python conditionals like this:
```python
if (user.get('profile', {}).get('age', 0) >= 18 and 
    user.get('subscription', {}).get('status') == 'active' and
    datetime.now() - user.get('last_login', datetime.min) < timedelta(days=30)):
    # grant access
    pass
```

Write this:
```python
from dilemma import evaluate

expr = "user.profile.age >= 18 and user.subscription.status == 'active' and user.last_login upcoming within 30 days"

if evaluate(expr, context):
    # grant access
    pass
```

## Features

- **Secure evaluation** - No arbitrary code execution, only safe expressions
- **Rich data access** - Navigate nested dictionaries and lists with ease
- **Date/time operations** - Natural language date comparisons
- **Multiple resolvers** - JsonPath, JQ, and basic dictionary lookup
- **Performance optimized** - Compile expressions once, evaluate many times
- **Type safe** - Built-in type checking and validation

## Quick Start

```bash
pip install dilemma
```

```python
from dilemma import evaluate

# Basic arithmetic and logic
result = evaluate("2 * (3 + 4)")  # Returns 14
result = evaluate("age >= 18 and status == 'active'", {"age": 25, "status": "active"})

# Date operations
result = evaluate("user.last_login upcoming within 7 days", context)
result = evaluate("subscription.end_date is $future", context)

# Complex data access
result = evaluate("user.permissions contains 'admin'", context)
result = evaluate("`[.users[] | select(.active == true) | .name] | length` > 0", context)
```

## Language Features

All [Language Features](https://github.com/patrickcd/dilemma/blob/main/docs/language.md).
Extensive [Examples](https://github.com/patrickcd/dilemma/blob/main/docs/examples.md).


### Data Access Patterns

```python
# Dot notation for nested objects
"user.profile.settings.theme == 'dark'"

# Natural possessive syntax  
"user's subscription's status == 'premium'"

# Array/list access
"users[0].name == 'Alice'"

# Check membership
"'admin' in user.roles"
"user.permissions contains 'read'"
```

### Date and Time Operations

```python
# Relative time checks
"user.created_at upcoming within 30 days"
"order.shipped_date older than 1 week"

# State comparisons
"subscription.expires is $future"
"last_backup is $past"
"meeting.date is $today"

# Date comparisons
"start_date before end_date"
"event.date same_day_as $now"
```


### Advanced JQ Integration

For complex data manipulation, use JQ expressions in backticks:

```python
# Filter and transform arrays - working with provided context
evaluate('`[.users[] | select(.active == true) | .name]`', context)

# Mathematical operations on arrays  
evaluate('`[.sales[].amount] | add` > 10000', context)

# Complex conditionals
evaluate('`[.products[] | select(.price > 100 and .category == "electronics")] | length` > 1', context)
```

## Performance Optimization

For repeated evaluations, compile expressions once:

```python
from dilemma import compile_expression

# Compile once
eligibility_check = compile_expression(
    "user.age >= 18 and user.subscription.active and user.last_login upcoming within 30 days"
)

# Evaluate many times with different contexts
for user_data in users:
    if eligibility_check.evaluate(user_data):
        # send_premium_content(user_data)
        pass
```

## Error Handling

Dilemma provides clear, actionable error messages:

```python
try:
    result = evaluate("user.invalidfield == 'test'", context)
except VariableError as e:
    print(f"Expression error: {e}")
    # Suggests available fields and common fixes
```

## Use Cases

- **Form validation rules** - `"email like '*@*' and age >= 13"`
- **Business logic** - `"order.total > 100 and customer.tier == 'premium'"`
- **Access control** - `"user.roles contains 'admin' or resource.owner == user.id"`
- **Data filtering** - `"created_at upcoming within 24 hours and status == 'pending'"`
- **Workflow conditions** - `"approval.status == 'approved' and budget.remaining >= cost"`

## Safety & Security

- ✅ No arbitrary Python code execution
- ✅ No access to imports or builtins  
- ✅ Sandboxed evaluation environment
- ✅ Input validation and sanitization
- ✅ Memory and complexity limits


## License

MIT License - see [LICENSE](https://github.com/patrickcd/dilemma/blob/main/LICENSE) file for details.
