Metadata-Version: 2.4
Name: Pyrunall
Version: 0.1.0
Summary: Execute functions and class methods written in multiple programming languages directly from Python.
Author-email: Pavan S <pavans26122000@gmail.com>
Maintainer-email: Pavan S <pavans26122000@gmail.com>
License-Expression: MIT
Keywords: python,runner,cross-language,interpreter,compiler,javascript,java,go,php,ruby,c,cpp,rust,bash,automation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Interpreters
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Utilities
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file
Dynamic: requires-python

# Pyrunall

Call a function or class method written in **another language**, from Python,
and get the result back as a native Python value (int, float, str, bool,
list, dict, None).

```python
from Pyrunall import Runner

r = Runner()

result = r.call(
    language="javascript",
    code="function add(a, b) { return a + b; }",
    function="add",
    args=[2, 3],
)
print(result)  # 5
```

It works by generating a small "driver" wrapper in the target language that
decodes your arguments, calls your function/method, JSON-encodes the return
value, and prints it between sentinel markers that Python then parses back
out of the subprocess's stdout.

## Supported languages

| Language   | Functions | Classes/methods | Requires on PATH   | Arg/return types |
|------------|-----------|------------------|---------------------|-------------------|
| Python     | ✅        | ✅               | (uses your interpreter) | any JSON-serializable value |
| JavaScript | ✅        | ✅               | `node`              | any JSON-serializable value |
| Ruby       | ✅        | ✅               | `ruby`              | any JSON-serializable value |
| PHP        | ✅        | ✅ (via reflection) | `php`             | any JSON-serializable value |
| Bash       | ✅ (no classes) | —          | `bash`               | args: strings/numbers; result: captured stdout (parsed as JSON if valid) |
| Java       | ✅ (static methods) | ✅        | `javac`, `java`     | any JSON-serializable value (uses a small embedded JSON encoder/decoder + reflection — no external jar needed) |
| Go         | ✅        | ✅ (`New<Type>()` convention) | `go`     | any JSON-serializable value (uses `encoding/json` + `reflect`) |
| C          | ✅ (no classes) | —          | `gcc`                | scalars only, **requires `return_type`** |
| C++        | ✅        | ✅               | `g++`                | scalars/strings, **requires `return_type`** |
| Rust       | ✅        | ✅ (`::new()` convention) | `rustc`      | scalars/strings, **requires `return_type`** |

Dynamic languages (Python/JS/Ruby/PHP/Go/Java) marshal arguments and return
values through JSON, so nested lists/dicts work transparently. C, C++, and
Rust don't ship a JSON library by default, so for those three the call site
is generated with your arguments embedded directly as source literals, and
you must tell the adapter what type to expect back:

```python
r.call(language="c", code="int add(int a, int b){ return a+b; }",
       function="add", args=[2, 3], return_type="int")
```

Valid `return_type` values: `int`, `long`, `double`, `float`, `bool`,
`string`, `void` (Rust uses `i32`/`i64`/`f64`/`f32`/`bool`/`string`/`void`).

## API

```python
from Pyrunall import Runner

r = Runner()

# Call a top-level function
r.call(language, code, function, args=None, kwargs=None, **options)

# Instantiate a class and call a method on it
r.call_method(language, code, class_name, method,
              init_args=None, method_args=None, **options)

r.supported_languages()  # -> list of accepted language identifiers
```

- `language` — e.g. `"python"`, `"javascript"`/`"js"`, `"ruby"`, `"php"`,
  `"bash"`, `"java"`, `"go"`, `"c"`, `"cpp"`, `"rust"`.
- `code` — a string containing the source that defines your function/class.
- `function` — the function name. For **Java**, use `"ClassName.methodName"`
  since Java has no free-floating functions (it must be `static`).
- `args` / `kwargs` — positional/keyword arguments (JSON-serializable).
- `**options` — currently just `return_type` for C/C++/Rust.

## Errors

All errors are raised as Python exceptions from `Pyrunall.exceptions`:

- `LanguageNotSupportedError` — unknown `language` value.
- `RuntimeNotFoundError` — the interpreter/compiler binary isn't on PATH.
- `CompilationError` — a compiled language failed to compile (`.stderr`
  has the compiler output).
- `ExecutionError` — the target code threw/raised, or the process exited
  non-zero (`.stdout` / `.stderr` / `.returncode` available).
- `ResultParseError` — the process ran but its output wasn't valid JSON.

```python
from Pyrunall import Runner, ExecutionError

try:
    r.call("javascript", "function boom(){ throw new Error('kaboom'); }", "boom")
except ExecutionError as e:
    print(e)  # "javascript code raised an error: kaboom"
```

## Conventions for languages without JSON/classes

- **Bash** has no classes; only `call()` is supported. The function's
  captured stdout becomes the result (parsed as JSON if it looks like JSON,
  otherwise returned as a plain string).
- **C** has no classes; only `call()` is supported.
- **Go** has no classes. For `call_method`, define a constructor named
  `New<ClassName>` returning the type, e.g. `func NewCounter(start int) *Counter`.
- **Rust** has no classes. For `call_method`, define `impl <ClassName> { fn new(...) -> Self }`.

## Security note

This library executes the source code you pass it, using the interpreters/
compilers installed on your machine. Never pass it code from an untrusted
source — it has the same privileges as any other subprocess you'd launch
yourself.

## Installation

```bash
pip install -e .
```

(Or just copy the `Pyrunall/` package into your project — it has no
third-party dependencies, only the target language's own runtime.)

## Layout

```
Pyrunall/
  __init__.py         # public exports: Runner, exceptions
  core.py              # Runner class (main entry point)
  exceptions.py
  adapters/
    base.py            # shared subprocess + marker-extraction machinery
    python_adapter.py
    javascript_adapter.py
    ruby_adapter.py
    php_adapter.py
    bash_adapter.py
    java_adapter.py
    go_adapter.py
    c_adapter.py
    cpp_adapter.py
    rust_adapter.py
examples/demo.py
```
