PyExpression 1.1.0
See the version list below for details.
dotnet add package PyExpression --version 1.1.0
NuGet\Install-Package PyExpression -Version 1.1.0
<PackageReference Include="PyExpression" Version="1.1.0" />
<PackageVersion Include="PyExpression" Version="1.1.0" />
<PackageReference Include="PyExpression" />
paket add PyExpression --version 1.1.0
#r "nuget: PyExpression, 1.1.0"
#:package PyExpression@1.1.0
#addin nuget:?package=PyExpression&version=1.1.0
#tool nuget:?package=PyExpression&version=1.1.0
PyExpression
PyExpression is a small, controlled, Python-like expression and mini-script engine for C#.
It is designed for sandboxed execution with a deliberately limited language surface:
- no Python runtime embedding
- no IronPython
- no Roslyn scripting
- no arbitrary .NET object access
- no file, network, system, or reflection access from scripts
The repository contains three main projects:
PyExpression- core class libraryPyExpressionSandbox- WPF test applicationPyExpression.Tests- xUnit test project
An example plugin project is also included:
PyFilter.Methods- sample provider assembly
What PyExpression Is
PyExpression parses a small Python-like language with its own lexer, parser, AST, and interpreter. It is intentionally smaller than Python and focuses on predictable behavior, explicit control over the callable surface, and easy extension.
Supported language features
- literals:
- integers
- doubles
- booleans
- strings
- expressions:
+,-,*,/,%==,!=,<,<=,>,>=and,or,not
- statements:
- assignment
returnif/elsewhilefor ... in range(...)breakcontinueyieldyield from
- collections:
- list literals
- dictionary literals
- index access and indexed assignment
- built-in functions:
absminmaxsumsortedkeysvaluesroundfloorceilpowsqrtlenclamp
Explicitly unsupported
import- class definitions
- function definitions inside the script
- arbitrary reflection
- file system access
- network access
- system access
- dynamic assembly execution
- access to arbitrary .NET objects
Quick Start
Execute a script
using PyExpression;
var result = PyExpression.PyExpression.Execute(
"""
x = n * 2
return x
""",
new PyVar("n", 9));
Console.WriteLine(result.Value); // 18
Use the instance API
using PyExpression;
var expression = new PyExpression.PyExpression("""
if n > 10:
return 100
else:
return 50
""")
{
Vars = new[]
{
new PyVar("n", 11)
}
};
var result = expression.Execute();
Console.WriteLine(result.Value); // 100
Async and streaming execution
using PyExpression;
var expression = new PyExpression.PyExpression("""
i = 0
while i < 3:
i = i + 1
yield i
""");
await foreach (var item in expression.StreamAsync())
{
Console.WriteLine(item);
}
Architecture
The interpreter is split into clear layers:
flowchart LR
Script[Script Text] --> Lexer[Lexer / Tokenizer]
Lexer --> Parser[Parser]
Parser --> AST[AST]
AST --> Interpreter[Interpreter]
Vars[PyVar Collection] --> Context[ExecutionContext]
Lib[PyLib + Providers] --> Interpreter
Context --> Interpreter
Interpreter --> Result[PyVar Result]
Interpreter --> Stream[Yield Stream]
Internal components
Lexerconverts the source text into tokensParserturns tokens into an ASTInterpreterevaluates statements and expressionsExecutionContextstores variables and local scopePyLibexposes the approved callable methodsPyVarholds typed input/output values
Public API Overview
PyExpression
Main class used to execute a script.
var expression = new PyExpression.PyExpression("return n * 2")
{
Vars = new[] { new PyVar("n", 9) }
};
PyVar result = expression.Execute();
Useful members:
string ScriptICollection<PyVar> VarsPyLib Libraryint MaxIterationsPyVar Execute()Task<PyVar> ExecuteAsync(...)IAsyncEnumerable<object?> StreamAsync(...)bool ContainsYield()static PyVar Execute(string script, params PyVar[] vars)
PyVar
Represents a named value passed to or returned from the engine.
var value = new PyVar("n", 9);
PyLib
Registry of permitted methods and plugin providers.
var library = new PyLib();
library.Register(new MathMethods());
Useful members:
Register(string name, int minArgs, int? maxArgs, Func<IReadOnlyList<object?>, object?> handler)Register(IPyMethods methods)Register(Assembly assembly)RegisterDirectory(string directoryPath)LoadPluginsFromFolder(string directoryPath)MethodNamesProvidersManifestsDefault
IPyMethods
Contract for providers that register methods into PyLib.
public sealed class MyMethods : PyMethods
{
public override IPyPluginManifest Manifest => new PyPluginManifest(
"MyMethods",
new Version(1, 0, 0),
"Custom helper methods");
public override void Register(PyLib library)
{
library.Register("hello", 0, 0, _ => "world");
}
}
IPyPluginManifest
Metadata contract used for plugin discovery and reporting.
public sealed record PyPluginManifest(
string Name,
Version Version,
string Description) : IPyPluginManifest;
Default Methods
The built-in default provider is MathMethods.
It is always registered in PyLib.Default and provides the standard helper set:
- arithmetic helpers
- math functions
- collection helpers
Example:
var result = PyExpression.PyExpression.Execute("return clamp(n * 3, 0, 100)", new PyVar("n", 7));
Console.WriteLine(result.Value); // 21
Sandbox
PyExpressionSandbox is a WPF demo application for live testing.
It includes:
- typed variable editor
- script editor
- sync / async mode selection
- output view
- yield stream view
- cancel button for async execution
- generated C# snippet preview
- copy button for the snippet
The sandbox is intended for interactive experimentation, not for production use.
Plugin Support
Plugins are controlled method provider assemblies that implement IPyMethods.
The platform supports:
- direct registration of a provider instance
- assembly scanning
- folder-based plugin loading
var library = new PyLib();
library.Register(new PyFilter.Methods.PyFilterMethods());
library.Register(typeof(PyFilter.Methods.PyFilterMethods).Assembly);
var results = library.LoadPluginsFromFolder(@"C:\plugins");
Plugin metadata is separated through IPyPluginManifest.
flowchart TD
DLL[Plugin DLL] --> Scan[Assembly Scan]
Scan --> Provider[IPyMethods]
Provider --> Manifest[IPyPluginManifest]
Provider --> Register[PyLib Register]
Register --> Methods[Allowed Methods]
Manifest --> Result[PluginLoadResult]
Error Handling
The engine uses explicit exception types so callers can distinguish between:
- parse errors
- runtime errors
- unknown variables
- unknown methods
- invalid argument counts
- iteration limit violations
Examples:
try
{
var result = PyExpression.PyExpression.Execute("return missing");
}
catch (PyUnknownVariableException ex)
{
Console.WriteLine(ex.Message);
}
Examples
Variables
n = 5
x = n * 2
return x
Conditions
if n > 10:
return 100
else:
return 50
While loop
i = 0
sum = 0
while i < 5:
sum = sum + i
i = i + 1
return sum
For loop
sum = 0
for i in range(1, 6, 2):
sum = sum + i
return sum
Yield stream
i = 0
while i < 3:
i = i + 1
yield i
Test Coverage
The xUnit test project covers:
- variable access and overwriting
- arithmetic and precedence
- comparisons and boolean logic
- conditions
- loops
break/continue- collections and indexing
- built-in methods
- plugin loading
- async execution
- generator streaming
- error cases
Repository Layout
PyExpressionSandbox/
PyExpression/
PyExpressionSandbox/
PyExpression.Tests/
PyFilter.Methods/
README.md
how-to-create-a-plugin.md
Building and Testing
dotnet build PyExpression\PyExpression.csproj -nologo
dotnet build PyFilter.Methods\PyFilter.Methods.csproj -nologo
dotnet build PyExpressionSandbox\PyExpressionSandbox.csproj -nologo
dotnet test PyExpression.tests\PyExpression.tests.csproj --no-build --no-restore -nologo
Next Steps
Good future extensions would be:
- dictionaries with richer helpers
- list and dictionary comprehensions
break/continuediagnostics in the sandbox UI- plugin manifest discovery from custom attributes
- plugin manifest files or package metadata
- more built-in collection helpers
Documentation
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.