diff --git a/docs/unity/bindings.md b/docs/unity/bindings.md deleted file mode 100644 index c09d96d1..00000000 --- a/docs/unity/bindings.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -label: Bindings -icon: dot -order: 10 ---- - -Bindings are methods and variables that are defined in C# and can be accessed from Python. -We provide two types of bindings: static bindings and dynamic bindings. - -## Static Bindings - -Static bindings wrap a C# class or struct and expose its methods and variables to Python. -This is the most common way to define bindings. -Static bindings are initialized at compile time. - -### Manual Static Bindings - -Manual static bindings directly create a Python equivalent of `def f(a, b, *args)` in C#. -To use it, you need to create a class that inherits from `PyTypeObject`. -And implement some abstract methods to specify the name and type of the Python type. -For example, to make `UnityEngine.Vector2` available in Python, you can write a `PyVector2Type` -class like the following. - -```csharp -public class PyVector2Type: PyTypeObject{ - // The name of the type in Python - public override string Name => "Vector2"; - - // The corresponding C# type - public override System.Type CSType => typeof(Vector2); -} -``` - -Next, you need to define each method and variable to be exposed to Python, -by using `[PythonBinding]` attribute. - -!!! -We assume that you have necessary knowledge about -[Python's data model](https://docs.python.org/3/reference/datamodel.html). -Such as magic methods, `__new__`, `__init__`, `__add__` and so on. -Otherwise, you may have trouble understanding the following code. -!!! - -Let's define a magic method `__add__`, it is used to implement the `+` operator in Python. -With `__add__`, `Vector2` object in Python can be added with another `Vector2` object. - -```csharp -public class PyVector2Type: PyTypeObject{ - public override string Name => "Vector2"; - public override System.Type CSType => typeof(Vector2); - - [PythonBinding] - public object __add__(Vector2 self, object other){ - // If the other object is not a Vector2, return NotImplemented - if(!(other is Vector2)) return VM.NotImplemented; - // Otherwise, return the result of addition - return self + (Vector2)other; - } -} -``` - -This is easy to understand. -Let's see another example, `__mul__`, it is used to implement the `*` operator in Python. -`Vector2` object in C# can be multiplied with a `float` object in Python. -The following code shows this usage. - -```csharp -Vector2 a = new Vector2(1, 2); -Vector2 b = a * 2.0f; -Vector2 c = 2.0f * a; -``` - -As you can see, things are slightly different from `__add__`. -Because the `float` operand can be on the left or right side of the `*` operator. -In this case, you need to define `__mul__` and `__rmul__` at the same time. - -```csharp -public class PyVector2Type: PyTypeObject{ - public override string Name => "Vector2"; - public override System.Type CSType => typeof(Vector2); - - // ... - - [PythonBinding] - public object __mul__(Vector2 self, object other){ - if(!(other is float)) return VM.NotImplemented; - return self * (float)other; - } - - [PythonBinding] - public object __rmul__(Vector2 self, object other){ - if(!(other is float)) return VM.NotImplemented; - return self * (float)other; - } -} -``` - -Finally, let's implement the constructor of `Vector2`. -`__new__` magic method must be defined. - -```csharp -public class PyVector2Type: PyTypeObject{ - public override string Name => "Vector2"; - public override System.Type CSType => typeof(Vector2); - - [PythonBinding] - public object __new__(PyTypeObject cls, params object[] args){ - if(args.Length == 0) return new Vector2(); - if(args.Length == 2){ - float x = vm.PyCast(args[0]); - float y = vm.PyCast(args[1]); - return new Vector2(x, y); - } - vm.TypeError("Vector2.__new__ takes 0 or 2 arguments"); - return null; - } -} -``` - -Here we use `params object[] args` to tell the bindings that the constructor can take any number of arguments. -It is equivalent to `def __new__(cls, *args)` in Python. -Note that Python does not support method overloading. -So we manually check the number of arguments and their types to determine which constructor to call. - -For fields, we can form a Python property by defining a getter and a setter. -By using `[PythonBinding(BindingType.Getter)]` and `[PythonBinding(BindingType.Setter)]` attributes. - -!!! -However, this has certain limitations for value types. Because `Vector2` is a struct, it is passed by value. -So our setter will not be able to modify the original `Vector2` object. -!!! - -```csharp -public class PyVector2Type: PyTypeObject{ - public override string Name => "Vector2"; - public override System.Type CSType => typeof(Vector2); - - [PythonBinding(BindingType.Getter)] - public object x(Vector2 self) => self.x; - - [PythonBinding(BindingType.Setter)] - public void x(Vector2 self, object value) => self.x = vm.PyCast(value); - - [PythonBinding(BindingType.Getter)] - public object y(Vector2 self) => self.y; - - [PythonBinding(BindingType.Setter)] - public void y(Vector2 self, object value) => self.y = vm.PyCast(value); -} -``` - -Once you have done all the above, you must register the type to the VM. -Here we set it into `builtins` module, so that it can be accessed from anywhere. - -```csharp -vm.RegisterType(new PyVector2Type(), vm.builtins); -``` - -To summarize, manual static bindings provide detailed control for exposing a C# class to Python. -You decide which methods and variables to expose, and how to expose them. -This is our recommended way to define bindings. Also it is the most performant way. - -### Automatic Static Bindings - -Automatic static bindings use C# reflection to automatically generate bindings for a C# class. -It is convenient for testing and prototyping, but it is slow and unsafe since the user can access any member of the class. - -```csharp -vm.RegisterAutoType(vm.builtins); -``` - -That's all you need to do. The `RegisterAutoType` method will automatically generate bindings for `Vector2`. - - -## Dynamic Bindings - -Dynamic bindings allow you to add a single C# lambda function to an object at runtime. - -```csharp -delegate object NativeFuncC(VM vm, object[] args); -``` - -+ `CSharpLambda BindFunc(PyObject obj, string name, int argc, NativeFuncC f)` - -It is similar to `bind_func` in [C++ API](../bindings/). diff --git a/docs/unity/console.md b/docs/unity/console.md deleted file mode 100644 index 362747ce..00000000 --- a/docs/unity/console.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -label: Python console -icon: dot -order: 5 ---- - -You can open the Python console in Unity by clicking the `Window/Python Console` menu item. - -By default, the console creates a unmodified `VM` instance to execute your code. -You may want to provide an enhanced `VM` instance for the console in Unity Editor. -For example, adding some class bindings in `UnityEngine` namespace. - -To do this, you need to create a class derived from `VM` and put it in `Assets/Editor/` folder. -By adding `[EditorVM]` attribute to the class, -the console will use it instead of the default `VM` instance. - - -```csharp -using UnityEngine; -using PocketPython; - -[EditorVM] // this attribute is required -public class EnhancedVM: VM{ - public EnhancedVM() { - RegisterAutoType(builtins); - RegisterAutoType(builtins); - RegisterAutoType(builtins); - RegisterAutoType(builtins); - // ... - } -} -``` \ No newline at end of file diff --git a/docs/unity/examples.md b/docs/unity/examples.md deleted file mode 100644 index 1e0a1312..00000000 --- a/docs/unity/examples.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -label: Examples -icon: dot -order: 4 ---- - -See `Assets/PocketPython/Examples` after you import the plugin. - - -### Primes Example - -```csharp -using UnityEngine; - -namespace PocketPython -{ - - /// - /// Example of using PocketPython to find prime numbers. - /// - public class PrimesExample : MonoBehaviour - { - // Start is called before the first frame update - void Start() - { - var vm = new VM(); - const string source = @" -def is_prime(x): - if x < 2: - return False - for i in range(2, x): - if x % i == 0: - return False - return True - -primes = [i for i in range(2, 20) if is_prime(i)] -print(primes) -"; - CodeObject code = vm.Compile(source, "main.py", CompileMode.EXEC_MODE); - vm.Exec(code); // [2, 3, 5, 7, 11, 13, 17, 19] - } - } - -} -``` - - - -### Vector2 Example - -```csharp -using UnityEngine; - -namespace PocketPython -{ - - /// - /// Example of making UnityEngine.Vector2 available to Python. - /// - public class Vector2Example : MonoBehaviour - { - // Start is called before the first frame update - void Start() - { - var vm = new VM(); - // register UnityEngine.Vector2 type into the builtins module - vm.RegisterAutoType(vm.builtins); - - vm.Exec("print(Vector2)", "main.py"); // - vm.Exec("v = Vector2(1, 2)", "main.py"); - vm.Exec("print(v)", "main.py"); // (1.0, 2.0) - vm.Exec("print(v.x)", "main.py"); // 1.0 - vm.Exec("print(v.y)", "main.py"); // 2.0 - vm.Exec("print(v.magnitude)", "main.py"); // 2.236068 - vm.Exec("print(v.normalized)", "main.py"); // (0.4472136, 0.8944272) - vm.Exec("print(Vector2.Dot(v, v))", "main.py"); // 5.0 - vm.Exec("print(Vector2.get_up())", "main.py"); // (0.0, 1.0) - - Vector2 v = (Vector2)vm.Eval("Vector2(3, 4) + v"); - Debug.Log(v); // (4.0, 6.0) - } - } - -} -``` - - - -### MyClass Example - -```csharp -using UnityEngine; -using System; - -namespace PocketPython -{ - - public class MyClass - { - public string title; - public string msg; - - public void Print() - { - Debug.Log(title + ": " + msg); - } - } - - public class PyMyclassType : PyTypeObject - { - public override string Name => "my_class"; - public override Type CSType => typeof(MyClass); - - [PythonBinding] - public object __new__(PyTypeObject cls) - { - return new MyClass(); - } - - [PythonBinding(BindingType.Getter)] - public string title(MyClass value) => value.title; - - [PythonBinding(BindingType.Getter)] - public string msg(MyClass value) => value.msg; - - [PythonBinding(BindingType.Setter)] - public void title(MyClass value, string title) => value.title = title; - - [PythonBinding(BindingType.Setter)] - public void msg(MyClass value, string msg) => value.msg = msg; - - [PythonBinding] - public void print(MyClass value) => value.Print(); - } - - - /// - /// Example of binding a custom C# class to Python. - /// - public class MyClassExample : MonoBehaviour - { - // Start is called before the first frame update - void Start() - { - var vm = new VM(); - - // register MyClass type into the builtins module - vm.RegisterType(new PyMyclassType(), vm.builtins); - - vm.Exec("print(my_class)", "main.py"); // - vm.Exec("c = my_class()", "main.py"); - vm.Exec("c.title = 'Greeting'", "main.py"); - vm.Exec("c.msg = 'Hello, world!'", "main.py"); - - string title = vm.Eval("c.title").ToString(); - string msg = vm.Eval("c.msg").ToString(); - - Debug.Log(title + ": " + msg); // Greeting: Hello, world! - - vm.Exec("c.print()", "main.py"); // Greeting: Hello, world! - } - } - -} -``` - diff --git a/docs/unity/index.yml b/docs/unity/index.yml deleted file mode 100644 index a0024f8e..00000000 --- a/docs/unity/index.yml +++ /dev/null @@ -1,3 +0,0 @@ -label: Unity Plugin -icon: code -order: 0 \ No newline at end of file diff --git a/docs/unity/introduction.md b/docs/unity/introduction.md deleted file mode 100644 index b76117f6..00000000 --- a/docs/unity/introduction.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -label: Introduction -icon: dot -order: 30 ---- - -# Welcome to PocketPython - -PocketPython is a C# plugin that allows you to do Python scripting in Unity. It provides a sandboxed Python environment, which adds dynamic capabilities to your game, which can be used for dynamic game logic, modding, hot fixing, and more. - -The virtual machine is written in pure C#, which means you can fully control the internal state of the Python interpreter. - -!!! -PocketPython is designed for game scripting, not for scientific computing. -You cannot use it to run NumPy, OpenCV, or any other CPython extension modules. -!!! - - -## Features - -### Python 3.x Syntax - -PocketPython uses [pocketpy](https://github.com/pocketpy/pocketpy) -as frontend to parse and compile Python source code. -It supports most of the Python 3.x syntax. - -The following table shows a feature comparison of PocketPython -with respect to the original [pocketpy](https://github.com/pocketpy/pocketpy). -The features marked with `YES` are supported, and the features marked with `NO` are not supported. - -| Name | Example | Cpp | Unity | -| --------------- | ------------------------------- | --------- | --- | -| If Else | `if..else..elif` | YES | YES | -| Loop | `for/while/break/continue` | YES | YES | -| Function | `def f(x,*args,y=1):` | YES | YES | -| Subclass | `class A(B):` | YES | YES | -| List | `[1, 2, 'a']` | YES | YES | -| ListComp | `[i for i in range(5)]` | YES | YES | -| Slice | `a[1:2], a[:2], a[1:]` | YES | YES | -| Tuple | `(1, 2, 'a')` | YES | YES | -| Dict | `{'a': 1, 'b': 2}` | YES | YES | -| F-String | `f'value is {x}'` | YES | YES | -| Unpacking | `a, b = 1, 2` | YES | YES | -| Star Unpacking | `a, *b = [1, 2, 3]` | YES | YES | -| Exception | `raise/try..catch` | YES | NO | -| Dynamic Code | `eval()/exec()` | YES | YES | -| Reflection | `hasattr()/getattr()/setattr()` | YES | YES | -| Import | `import/from..import` | YES | YES | -| Context Block | `with as :` | YES | NO | -| Type Annotation | `def f(a:int, b:float=1)` | YES | YES | -| Generator | `yield i` | YES | NO | -| Decorator | `@cache` | YES | YES | - -### Sandboxed Python Environment - -PocketPython provides a sandboxed Python environment. -All python code is executed in a C# virtual machine. -The user cannot access the file system, network, or any other resources of the host machine. - -### Seamless Interop with C# - -PocketPython uses `object` in C# to represent dynamic typed Python objects. -Most of the basic Python types correspond to a C# type, -which means passing arguments between C# and Python is extremely easy and intuitive. - -| Python Type | C# Type | -| ----------- | ------- | -| `None` | `NoneType` | -| `object` | `System.Object` | -| `bool` | `System.Boolean` | -| `int` | `System.Int32` | -| `float` | `System.Single` | -| `str` | `System.String` | -| `tuple` | `System.Object[]` | -| `list` | `System.Collections.Generic.List` | -| `dict` | `System.Collections.Generic.Dictionary` | -| ... | ... | - -### Python Console in Editor - -PocketPython provides a Python console in Unity editor, -which allows you to do quick debugging and testing. \ No newline at end of file diff --git a/docs/unity/vm.md b/docs/unity/vm.md deleted file mode 100644 index 2564c561..00000000 --- a/docs/unity/vm.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -label: Virtual machine -icon: dot -order: 20 ---- - -The `VM` class provides a sandboxed Python environment and a set of APIs for interacting with it. -Using the namespace `PocketPython` before any operations. - -```csharp -using PocketPython; -``` - -### Construction - -+ `VM()` - - Create a new Python virtual machine. - -### Code Execution - -+ `CodeObject Compile(string source, string filename, CompileMode mode)` - - Compile Python source code into a `CodeObject` that can be executed later. - The `filename` parameter is used for error reporting, you can set it to `main.py` if you don't need it. - The `mode` parameter specifies the compile mode, see [CompileMode](../quick-start/exec/#compile-mode) for details. - -+ `object Exec(CodeObject co, PyModule mod = null)` - - Execute a `CodeObject` in the given module. - The `mod` parameter specifies the module in which the code will be executed. - If it is `null`, the code will be executed in the main module. - -+ `object Exec(string source, string filename, CompileMode mode = CompileMode.EXEC_MODE, PyModule mod = null)` - - Compile and execute Python source code in the given module. It is equivalent to `Exec(Compile(source, filename, mode), mod)`. - -+ `object Eval(string source, PyModule mod = null)` - - Evaluate an expression in the given module. - -+ `object Call(object callable, object[] args, Dictionary kwargs)` - - Call a Python callable object with the given arguments and keyword arguments. It is equivalent to `callable(*args, **kwargs)` in Python. - -+ `object CallMethod(object obj, string name, params object[] args)` - - Call a method of a Python object with the given arguments. It is equivalent to `obj.name(*args)` in Python. - - -### Attribute Access - -+ `object GetAttr(object obj, string name, bool throwErr = true)` - - Get an attribute of a Python object. It is equivalent to `obj.name` in Python. - If `throwErr` is `true`, it will throw an exception if the attribute does not exist. - Otherwise, it will return `null`. - -+ `NoneType SetAttr(object obj, string name, object value)` - - Set an attribute of a Python object. It is equivalent to `obj.name = value` in Python. - -+ `bool HasAttr(object obj, string name)` - - Check if a Python object has the given attribute. It is equivalent to `hasattr(obj, name)` in Python. - -### Module Access - -+ `Dictionary modules` - - A dictionary that maps module names to `PyModule` objects. - You can use it to access the modules that have been imported. - -+ `Dictionary lazyModules` - - A dictionary stores all unimported modules. You can add Python source into this dictionary. - It will be initialized and moved to `modules` when it is first imported. - -+ `PyModule NewModule(string name)` - - Create a new module with the given name at runtime. The module will be added to `modules` automatically. - -+ `PyModule PyImport(string name)` - - Import a Python module. It is equivalent to `import name` in Python. It first checks if the module has been imported, if not, it will try to load the module from `lazyModules`. - - -### Type Conversion - -+ `T PyCast(object obj)` - - Convert a Python object to a C# object. It is equivalent to `obj as T` in C#. - Raise `TypeError` if the conversion fails. - -+ `bool IsInstance(object obj, PyTypeObject type)` - - Check if a Python object is an instance of the given type. It is equivalent to `isinstance(obj, type)` in Python. - -+ `void CheckType(object t)` - - Check if `t is T`. Raise `TypeError` if the check fails. - -+ `bool PyEquals(object lhs, object rhs)` - - Check if two Python objects are equal. It is equivalent to `lhs == rhs` in Python. This is different from `==` or `object.ReferenceEquals` in C#. You should always use this method to compare Python objects. - -+ `object PyIter(object obj)` - - Get an iterator of a Python object. It is equivalent to `iter(obj)` in Python. - -+ `object PyNext(object obj)` - - Get the next element of a Python iterator. It is equivalent to `next(obj)` in Python. - -+ `bool PyBool(object obj)` - - Convert a Python object to a boolean value. It is equivalent to `bool(obj)` in Python. - -+ `string PyStr(object obj)` - - Convert a Python object to a string. It is equivalent to `str(obj)` in Python. - -+ `string PyRepr(object obj)` - - Convert a Python object to a string representation. It is equivalent to `repr(obj)` in Python. - -+ `int PyHash(object obj)` - - Get the hash value of a Python object. It is equivalent to `hash(obj)` in Python. - -+ `List PyList(object obj)` - - Convert an `Iterable` Python object to a list. It is equivalent to `list(obj)` in Python. - -### Callbacks - -+ `System.Action stdout = Debug.Log` - - A callback that will be called when the Python code invokes `print` function. - By default, it will print the message to Unity console. - -+ `System.Action stderr = null` - - A callback that will be called when the Python code emits an error message. - By default, an Exception will be raised. - You can set it to `Debug.LogError` for printing to the Unity Console. - -### Debug Flag - -+ `bool debug = false` - - A flag that controls whether to print debug messages to Unity console. - You can set it to `true` to enable debug messages, or `false` to disable them. diff --git a/scripts/build_references.py b/scripts/build_references.py index e2743df5..6c11532e 100644 --- a/scripts/build_references.py +++ b/scripts/build_references.py @@ -28,7 +28,8 @@ for line in lines: output.append('```cpp\n') with open('docs/references.md', 'w', encoding='utf-8') as f: - f.write('''---label: References + f.write('''--- +label: References icon: code order: 2 ---