Since UniPy is basically a transcompiler that only converts grammar,
it is not possible to use Python's rich library (API), which is a major factor when adopting Python.
The libraries (APIs) must be Swift (Foundation, etc.) or C# (.NET) libraries
to match the language to which the code is being converted.
It does have the ability to automatically convert
some built-in functions and
methods of the str object,
etc., to the corresponding Swift/C# (.NET) methods.
Grammar analysis is not performed in accordance with PEG,
so there may be cases in which a grammar analysis error prevents successful conversion.
In particular, extra white space in expressions or sentences will prevent proper parsing.
Basically, by writing in accordance with PEP8, you can avoid patterns that cannot be parsed by UniPy.
Also, instead of putting multiple expressions on a single line,
try dividing the processing into multiple lines by assigning them to variables once,
and simplifying the statements on each line. This may enable the code to be parsed correctly, so please try this.
Python is a dynamically typed language, but in order to convert code to Swift/C# with UniPy,
you need to write the code while being aware of the variable types,
just like with Swift/C#, which are statically typed languages.
Specifically, type hinting is required.
An example of defining a string type variable hoge:
hoge: str = "Hello"
hoge = "Hello" # type: str
This translates to:
var hoge: String = "Hello";
string hoge = "Hello";
An example of defining a function (method) that takes an int argument and returns a string:
def number_to_string(self, _value: int, message: str) -> str:
return str(_value) + message
def number_to_string(self, _value, message):
# type: (int, str) -> str
return str(_value) + message
This translates to:
func number_to_string(_ _value: Int, message: String) -> String
{
return _value.description + message;
}
public string number_to_string(int _value, string message)
{
return _value.ToString() + message;
}
hoge: str # This doesn't translate well
It would be easy to specify an empty string or similar as the initial value,
but if you absolutely need to convert a variable declaration without an initial value,
use Dummy.init as a temporary assignment value.hoge: str = Dummy.init # Write as a dummy initial value
This translates to:
var hoge: String;
string hoge;
x: Var = ""
x = "" # type: Var
This translates to:
var x = "";
var x = "";
An example of just defining an empty constructor and destructor for class Hoge:
class Hoge(object):
def __init__(self) -> None:
pass
def __del__(self) -> None:
pass
class Hoge(object):
def __init__(self):
# type: () -> None
pass
def __del__(self):
# type: () -> None
pass
This translates to:
class Hoge
{
init()
{
}
deinit
{
}
}
public class Hoge
{
public Hoge()
{
}
~Hoge()
{
}
}
Define it as a variable by adding self inside __init__ as usual.
When converting to Swift/C#, it will be moved outside __init__.
class Hoge():
def __init__(self) -> None:
self.aa: str = "AA"
class Hoge():
def __init__(self):
# type: () -> None
self.aa = "AA" # type: str
This translates to:
class Hoge
{
var aa: String;
init()
{
self.aa = "AA";
}
}
public class Hoge
{
public string aa;
public Hoge()
{
this.aa = "AA";
}
}
Static members are defined as class variables.
Static methods are created by adding the @staticmethod decorator when defining the function.
class Hoge(object):
Hello: str = "Static Variable"
@staticmethod
def World() -> str:
return "Static Function"
class Hoge(object):
Hello = "Static Variable" # type: str
@staticmethod
def World():
# type: () -> str
return "Static Function"
This translates to:
class Hoge
{
static var Hello: String = "Static Variable";
static func World() -> String
{
return "Static Function";
}
}
public static class Hoge
{
public static string Hello = "Static Variable";
public static string World()
{
return "Static Function";
}
}
Following the PEP8 naming convention, define a name that starts with an _(underscore).
class Hoge(object):
def __init__(self) -> None:
self._aa: str = "AA"
def _bb(self) -> None:
pass
class Hoge(object):
def __init__(self):
# type: () -> None
self._aa: str = "AA"
def _bb(self):
# type: () -> None
pass
This translates to:
class Hoge
{
private var _aa: String;
init()
{
self._aa = "AA";
}
private func _bb() -> Void
{
}
}
public class Hoge
{
private string _aa;
public Hoge()
{
this._aa = "AA";
}
private void _bb()
{
}
}
Use the property function.
It also supports specifying only a getter or only a setter, but it does not support a deleter.
If you specify a function name that begins with __(two underscores),
the private keyword will be added.
Alternatively, you can use the @property decorator, but this only translates to a Swift getter.
class Hoge(object):
def __init__(self) -> None:
self._aa: str = ""
self._bb: int = 1
def __set_aa(self, value: str) -> None:
self._aa = value
def get_aa(self) -> str:
return self._aa
Aa = property(get_aa, __set_aa)
@property
def Bb(self) -> int:
return self._bb
class Hoge(object):
def __init__(self):
# type: () -> None
self._aa = "" # type: str
self._bb = 1 # type: int
def __set_aa(self, value):
# type: (str) -> None
self._aa = value
def get_aa(self):
# type: () -> str
return self._aa
Aa = property(get_aa, __set_aa)
@property
def Bb(self):
# type: () -> int
return self._bb
This translates to:
class Hoge
{
private var _aa: String;
private var _bb: Int;
init()
{
self._aa = "";
self._bb = 1;
}
public private(set) var Aa: String
{
get
{
return _aa;
}
set(value)
{
_aa = value;
}
}
var Bb: Int
{
return _bb;
}
}
public class Hoge
{
private string _aa;
private int _bb;
public Hoge()
{
this._aa = "";
this._bb = 1;
}
public string Aa
{
get
{
return _aa;
}
private set
{
_aa = value;
}
}
public int Bb()
{
return _bb;
}
}
The method of writing [T] directly after the function name, which is used in Python 3.12 and later, is not supported.
UniPy corresponds to the method of using TypeVar, which was used in Python 3.11 and earlier.
from typing import TypeVar, Any
class Hoge(object):
T = TypeVar('T')
def g1_hoge(self, _a: T) -> None:
pass
V = TypeVar('V', bound=Any)
def g2_hoge(self, _a: T, _b: V) -> None:
pass
from typing import TypeVar, Any
class Hoge(object):
T = TypeVar('T')
def g1_hoge(self, _a):
# type: (T) -> None
pass
V = TypeVar('V', bound=Any)
def g2_hoge(self, _a, _b):
# type: (T, V) -> None
pass
This translates to:
class Hoge
{
func g1_hoge<T>(_ _a: T) -> Void
{
}
func g2_hoge<T, V: Any>(_ _a: T, _ _b: V) -> Void
{
}
}
public class Hoge
{
public void g1_hoge<T>(T _a)
{
}
public void g2_hoge<T, V>(T _a, V _b)
{
}
}
The code is different when converting to Swift and when converting to C#.
In the case of Swift, type inference is performed in Swift, so no special writing is required; just write it in the same way as a normal method call.
Code omitted
For C#, use a generic function.
That is UniPy-specific function defined in unipy_stubs/__init__.py.
Specify the "type" in the first argument and the "method call statement" in the second argument.
Let's say you define an additional method named call_g to the Hoge class defined in the previous example of defining a generic method, and then call g1_hoge and g2_hoge, which are in the same class, from there.
from unipy_stubs import *
def call_g(self) -> None:
generic(int, self.g1_hoge(1))
generic([int, str], self.g2_hoge(1, "a"))
This translates to:
public void call_g()
{
g1_hoge<int>(1);
g2_hoge<int, string>(1, "a");
}
In the above example, if you run the check with Mypy, Mypy will probably give you the following error:
This error does not affect UniPy conversion, but to avoid this error,
generic(int, self.g1_hoge(1)) # type: ignore
Either add ignore for Mypy as above, or
generic(int, lambda: self.g1_hoge(1))
Please add lambda as shown above.
In this case lambda is ignored and the conversion results are identical.
Use the cast function from the typing module.
An example of defining a class BaseClass and its subclass SubClass, downcasting and upcasting its objects and assigning them to variables:
Upcasting is implicit, so you don't actually need to explicitly use the cast function.
The way to write the cast itself is the same whether you are converting to Swift or C#, but the required code in other parts of the example is different.
In Swift, the cast function is converted to as.
Furthermore, by enclosing it in the unwrap function, it is converted to as!
The unwrap function is a UniPy-specific function defined in unipy_stubs/__init__.py.
It is also used to remove Optional.
Also, if you specify the Optional type as the type of the first argument to the cast function, it will be converted to as?
from typing import Optional, cast
from unipy_stubs import *
class BaseClass():
pass
class SubClass(BaseClass):
pass
bc: BaseClass = SubClass()
sc: SubClass = unwrap(cast(SubClass, bc))
"""Forced Downcasting"""
bc = cast(BaseClass, sc)
"""Upcasting"""
opt_sc: Optional[SubClass] = cast(Optional[SubClass], bc)
"""Optional Downcasting"""
This translates to:
class BaseClass
{
}
class SubClass : BaseClass
{
}
var bc: BaseClass = SubClass();
var sc: SubClass = (bc as! SubClass);
bc = (sc as BaseClass);
var opt_sc: Optional<SubClass> = (bc as? SubClass);
In C#, the cast function is converted to a cast using () (parentheses).
Also, if you specify the Optional type as the type of the first argument to the cast function, it will be converted to a cast using as
from typing import Optional, cast
from unipy_stubs import *
class BaseClass():
pass
class SubClass(BaseClass):
pass
bc: BaseClass = Dummy.init
bc = new(SubClass())
sc: SubClass = Dummy.init
sc = cast(SubClass, bc)
"""Downcasting"""
bc = cast(BaseClass, sc)
"""Upcasting"""
opt_sc: Optional[SubClass] = cast(Optional[SubClass], bc)
"""Optional Downcasting"""
This translates to:
public class BaseClass
{
}
public class SubClass : BaseClass
{
}
BaseClass bc;
bc = new SubClass();
SubClass sc;
sc = ((SubClass)bc);
bc = ((BaseClass)sc);
SubClass opt_sc = (bc as SubClass);
Write the @attribute decorator or the attribute function on the line before the definition to which you want to add an attribute.
Decorators (functions) are UniPy-specific functions defined in unipy_stubs/__init__.py.
The way to write attributes is the same whether you are converting to Swift or C#.
The following examples are different for Swift and C# as they each provide specific examples of attribute usage.
from unipy_stubs import *
from unipy_stubs.swift_api.swift_ui import *
@attribute('available(macOS 10.12, *)')
class AttrClass(ObservableObject):
def __init__(self) -> None:
attribute(Published)
self.all_text: list[str] = []
@attribute('objc')
def native_method(self) -> None:
pass
This translates to:
@available(macOS 10.12, *)
class AttrClass : ObservableObject
{
@Published
var all_text: [String];
init()
{
self.all_text = [];
}
@objc
func native_method() -> Void
{
}
}
from unipy_stubs import *
from unipy_stubs.unity_api.unity_engine import *
@attribute(AddComponentMenu("Transform/Follow Transform"))
class FollowTransform(MonoBehaviour):
def __init__(self) -> None:
attribute(SerializeField)
self._has_health_potion: bool = Dummy.init
attribute(ContextMenuItemAttribute("Reset", "ResetBiography"))
attribute(MultilineAttribute(8))
self.player_biography: str = Dummy.init
This translates to:
[AddComponentMenu("Transform/Follow Transform")]
public class FollowTransform : MonoBehaviour
{
[SerializeField]
private bool _has_health_potion;
[ContextMenuItem("Reset", "ResetBiography")]
[Multiline(8)]
public string player_biography;
public FollowTransform()
{
}
}
Only some conversion of the methods of the listings is supported.
Slice conversion is also supported.
Specifying the step [start:stop:step] is not supported.
An example of defining a list of int types and slicing and printing part of the contents:
lst: list[int] = [1, 2, 3, 4, 5, 6]
print(lst[:2]) # [1, 2]
print(lst[2:4]) # [3, 4]
print(lst[-2:]) # [5, 6]
lst = [1, 2, 3, 4, 5, 6] # type: list[int]
print(lst[:2]) # [1, 2]
print(lst[2:4]) # [3, 4]
print(lst[-2:]) # [5, 6]
This translates to:
var lst: [Int] = [1, 2, 3, 4, 5, 6];
print(lst.slice(0, 2));
print(lst.slice(2, 4));
print(lst.slice(-2));
lst.shuffle()
if you write the above in Python, you will get the error:var lst = new List<int> { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(lst.Slice(0, 2));
Console.WriteLine(lst.Slice(2, 4));
Console.WriteLine(lst.Slice(-2));
lst.LastIndexOf(1)
if you write the above in Python, you will get the error:Define it as an array type instead of a list type.
ary: array[int] = [1, 2, 3, 4, 5, 6]
This translates to:
var ary = new int[] { 1, 2, 3, 4, 5, 6 };
Only some conversions of dictionary methods are supported.
An example of defining an empty dictionary (a_dic) with ints as keys and strings as values, adding an item, and printing one of them, and an example of defining a dictionary (b_dic) with initial values, with strings as keys and ints as values, and printing the keys and values as lists:
a_dic: dict[int, str] = {}
a_dic[0] = "A"
a_dic[1] = "B"
print(a_dic[0])
b_dic: dict[str, int] = {"A": 0, "B": 1}
b_keys: list[str] = list(b_dic.keys())
b_values: list[int] = list(b_dic.values())
print(b_keys)
print(b_values)
a_dic = {} # type: dict[int, str]
a_dic[0] = "A"
a_dic[1] = "B"
print(a_dic[0])
b_dic = {"A": 0, "B": 1} # type: dict[str, int]
b_keys = list(b_dic.keys()) # type: list[str]
b_values = list(b_dic.values()) # type: list[int]
print(b_keys)
print(b_values)
This translates to:
var a_dic: Dictionary<Int, String> = [:];
a_dic[0] = "A";
a_dic[1] = "B";
print(a_dic[0]);
var b_dic: Dictionary<String, Int> = ["A": 0, "B": 1];
var b_keys: [String] = list(b_dic.keys);
var b_values: [Int] = list(b_dic.values);
print(b_keys);
print(b_values);
var a_dic = new Dictionary<int, string>();
a_dic[0] = "A";
a_dic[1] = "B";
Console.WriteLine(a_dic[0]);
var b_dic = new Dictionary<string, int> { {"A", 0}, {"B", 1} };
var b_keys = new List<string>();b_keys.AddRange(b_dic.Keys.Cast<string>());
var b_values = new List<int>();b_values.AddRange(b_dic.Values.Cast<int>());
Console.WriteLine(b_keys);
Console.WriteLine(b_values);
lst: list[str] = ["1", "2", "3", "4", "5", "6"]
del lst[5:] # delete 6
print(lst)
del lst[:1] # delete 1
print(lst)
del lst[1:-1] # delete 3, 4
print(lst)
del lst[0] # delete 2
print(lst)
del lst[:] # delete all
print(lst)
dic: dict[str, str] = {"a": "A", "b": "B"}
del dic["a"]
print(dic)
lst = ["1", "2", "3", "4", "5", "6"] # type: list[str]
del lst[5:] # delete 6
print(lst)
del lst[:1] # delete 1
print(lst)
del lst[1:-1] # delete 3, 4
print(lst)
del lst[0] # delete 2
print(lst)
del lst[:] # delete all
print(lst)
dic = {"a": "A", "b": "B"} # type: dict[str, str]
del dic["a"]
print(dic)
This translates to:
var lst: [Int] = [1, 2, 3, 4, 5, 6];
lst.removeSubrange(5..<(len(lst)));
print(lst);
lst.removeSubrange(0, 1);
print(lst);
lst.removeSubrange(1, -1);
print(lst);
lst.remove(at: 0);
print(lst);
lst.removeAll();
print(lst);
var dic: Dictionary<String, String> = ["a": "A", "b": "B"];
dic.removeValue(forKey: "a");
print(dic);
var lst = new List<string> { "1", "2", "3", "4", "5", "6" };
lst.RemoveRange(5, lst.Count - 5);
Console.WriteLine(lst);
lst.RemoveRange(0, (1 < 0) ? (lst.Count + 1 - 0) : (1 - 0));
Console.WriteLine(lst);
lst.RemoveRange(1, (-1 < 0) ? (lst.Count + -1 - 1) : (-1 - 1));
Console.WriteLine(lst);
lst.RemoveAt(0);
Console.WriteLine(lst);
lst.Clear();
Console.WriteLine(lst);
var dic = new Dictionary<string, string> { {"a", "A"}, {"b", "B"} };
dic.Remove("a");
Console.WriteLine(dic);
Please note that the scope of loop variables is different between Python and Swift/C#.
Here's an example of defining list and dictionary variables and looping over them directly, and an example of looping over the number of elements using the range function:
lst_for: list[str] = ["a", "b"]
for n in lst_for:
print(str(n))
dic_for: dict[str, str] = {"a": "A", "b": "B"}
for m in dic_for:
print("{0}: {1}".format(m, dic_for[m]))
for key, val in dic_for.items():
print("{0}: {1}".format(key, val))
for i in range(len(lst_for)):
print(str(i))
for i in range(len(dic_for)):
print(str(i))
for i in range(2):
print(str(i))
lst_for = ["a", "b"] # type: list[str]
for n in lst_for:
print(str(n))
dic_for = {"a": "A", "b": "B"} # type: dict[str, str]
for m in dic_for:
print("{0}: {1}".format(m, dic_for[m]))
for key, val in dic_for.items():
print("{0}: {1}".format(key, val))
for i in range(len(lst_for)):
print(str(i))
for i in range(len(dic_for)):
print(str(i))
for i in range(2):
print(str(i))
This translates to:
var lst_for: [String] = ["a", "b"];
for n in lst_for
{
print(n.description);
}
var dic_for: Dictionary<String, String> = ["a": "A", "b": "B"];
for m in dic_for.keys
{
print("\(m): \(dic_for[m])");
}
for (key, val) in dic_for
{
print("\(key): \(val)");
}
for i in 0 ..< len(lst_for)
{
print(i.description);
}
for i in 0 ..< len(dic_for)
{
print(i.description);
}
for i in 0 ..< 2
{
print(i.description);
}
var lst_for = new List<string> { "a", "b" };
foreach (string n in lst_for)
{
Console.WriteLine(n.ToString());
}
var dic_for = new Dictionary<string, string> { {"a", "A"}, {"b", "B"} };
foreach (var m in dic_for.Keys.Cast<string>())
{
Console.WriteLine(string.Format("{0}: {1}", m, dic_for[m]));
}
foreach (var (key, val) in dic_for)
{
Console.WriteLine(string.Format("{0}: {1}", key, val));
}
for (var i = 0; i < lst_for.Count; i++)
{
Console.WriteLine(i.ToString());
}
for (var i = 0; i < dic_for.Keys.Count; i++)
{
Console.WriteLine(i.ToString());
}
for (var i = 0; i < 2; i++)
{
Console.WriteLine(i.ToString());
}
ary_keys: array[str] = list(dic_for.keys())
for i in range(len(ary_keys)):
print(ary_keys[i])
This translates to:
string[] ary_keys = dic_for.Keys.Cast<string>().ToArray();
for (var i = 0; i < ary_keys.Length; i++)
{
Console.WriteLine(ary_keys[i]);
}
The loop variable of an OrderedDictionary in System.Collections.Specialized is of type DictionaryEntry, but UniPy supports special conversions to make it easier to handle in a more Pythonic way.
As in the following example, on the line after the for statement, specify the key type using the cast function, and on the line after that specify the value type.
from typing import cast
from unipy_stubs.dotnet_api.system_collections_specialized import OrderedDictionary
od_for: OrderedDictionary = {"a": "A", "b": "B"}
for key, val in od_for.items():
key = cast(str, key)
val = cast(str, val)
print("{0}: {1}".format(key, val))
This translates to:
var od_for = new OrderedDictionary { {"a", "A"}, {"b", "B"} };
foreach (DictionaryEntry kv3 in od_for) {
string key = (string)kv3.Key;
string val = (string)kv3.Value;
Console.WriteLine(string.Format("{0}: {1}", key, val));
}
It will be converted as follows:
==,!=,<=,>=,<,> The syntax for these comparison operators does not differ between languages, so no conversion takes place.
s: str = str(datetime.now())
if "2024" in s and ("9/" in s or "-09-" in s):
if len(s) >= 25:
print("1-1")
else:
print("1-2")
elif "2025" not in s:
print("2")
s = str(datetime.now()) # type: str
if "2024" in s and ("9/" in s or "-09-" in s):
if len(s) >= 25:
print("1-1")
else:
print("1-2")
elif "2025" not in s:
print("2")
This translates to:
var s: String = datetime.now().description;
if (s.contains("2024") && (s.contains("9/") || s.contains("-09-")))
{
if (len(s) >= 25)
{
print("1-1");
}
else
{
print("1-2");
}
}
else if (!s.contains("2025"))
{
print("2");
}
string s = DateTime.Now.ToString();
if (s.Contains("2024") && (s.Contains("9/") || s.Contains("-09-")))
{
if (s.Length >= 25)
{
Console.WriteLine("1-1");
}
else
{
Console.WriteLine("1-2");
}
}
else if (!s.Contains("2025"))
{
Console.WriteLine("2");
}
Nested or other complex statements cannot be converted correctly. Please make the statements as simple as possible.
An example that loops from 0 to 4 and prints different strings for even and odd numbers:
for i in range(5):
print("odd" if (i % 2) != 0 else "even")
This translates to:
for i in 0 ..< 5
{
print((i % 2) != 0 ? "odd" : "even");
}
for (var i = 0; i < 5; i++)
{
Console.WriteLine((i % 2) != 0 ? "odd" : "even");
}
n: int = 0
while n < 5:
print(str(n))
n += 1
n = 0 # type: int
while n < 5:
print(str(n))
n += 1
This translates to:
var n: Int = 0;
while (n < 5)
{
print(n.description);
n += 1;
}
var n = 0;
while (n < 5)
{
Console.WriteLine(n.ToString());
n += 1;
}
An example of defining and accessing a tuple with two elements of type int and str:
t: tuple[int, str] = (1, "a")
print(str(t[0]))
print(t[1])
t = (1, "a") # type: tuple[int, str]
print(str(t[0]))
print(t[1])
This translates to:
var t: (Int, String) = (1, "a");
print(t.0.description);
print(t.1);
(int, string) t = (1, "a");
Console.WriteLine(t.Item1.ToString());
Console.WriteLine(t.Item2);
Define it as a child class of the Enum class or IntEnum class in the enum module.
from enum import Enum, IntEnum, auto
class Season(IntEnum):
Spring = 1
Summer = 2
Autumn = 3
Winter = 4
class ColorRGB(Enum):
Red = auto()
Green = auto()
Blue = auto()
This translates to:
enum Season : Int
{
case Spring = 1;
case Summer = 2;
case Autumn = 3;
case Winter = 4;
}
enum ColorRGB
{
case Red
case Green
case Blue
}
public enum Season : int
{
Spring = 1,
Summer = 2,
Autumn = 3,
Winter = 4,
}
public enum ColorRGB
{
Red,
Green,
Blue,
}
In the Hoge class, define an action_world that receives a function as an argument, and then call the argument by writing it as a lambda expression:
from typing import Callable
class Hoge(object):
def __init__(self) -> None:
self.action_world(lambda x: print("Hello lambda {0}".format(x)))
def action_world(self, _f: Callable[[str], None]) -> None:
_f("World!")
from typing import Callable
class Hoge(object):
def __init__(self):
# type: () -> None
self.action_world(lambda x: print("Hello lambda {0}".format(x)))
def action_world(self, _f):
# type: (Callable[[str], None]) -> None
_f("World!")
This translates to:
class Hoge
{
init()
{
action_world({(x) in print("Hello lambda \(x)")});
}
func action_world(_ _f: (String) -> Void) -> Void
{
_f("World!");
}
}
public class Hoge
{
public Hoge()
{
action_world((x) => Console.WriteLine(string.Format("Hello lambda {0}", x)));
}
public void action_world(Action<string> _f)
{
_f("World!");
}
}
Nested or other complex statements cannot be converted correctly. Please make the statements as simple as possible.
An example of using list comprehension to create lst2 by extracting only elements containing "o" from lst1 and converting them to uppercase:
lst1: list[str] = ["one", "two", "three", "for"]
lst2: list[str] = [x.upper() for x in lst1 if "o" in x]
print(lst2) # ["ONE", "TWO", "FOR"]
lst1 = ["one", "two", "three", "for"] # type: list[str]
lst2 = [x.upper() for x in lst1 if "o" in x] # type: list[str]
print(lst2) # ["ONE", "TWO", "FOR"]
This translates to:
var lst1: [String] = ["one", "two", "three", "for"];
var lst2: [String] = lst1.comprehension(({(x) in x.upper()}), ({(x) in x.contains("o")}));
print(lst2);
var lst1 = new List<string> { "one", "two", "three", "for" };
List<string> lst2 = lst1.comprehension(((x) => x.ToUpper()), ((x) => x.Contains("o")));
Console.WriteLine(lst2);
class Hoge():
def __init__(self) -> None:
self.default_method()
self.default_method(name="Taro", num=2)
def default_method(self, name: str = "Jone", num: int = 1) -> None:
print("{0}: {1}".format(name, str(num)))
class Hoge():
def __init__(self):
# type: () -> None
self.default_method()
self.default_method(name="Taro", num=2)
def default_method(self, name="Jone", num=1) -> None:
# type: (str, int) -> None
print("{0}: {1}".format(name, str(num)))
This translates to:
class Hoge
{
init()
{
default_method();
default_method(name: "Taro", num: 2);
}
func default_method(name: String = "Jone", num: Int = 1) -> Void
{
print("\(name): \(num.description)");
}
}
public class Hoge
{
public Hoge()
{
default_method();
default_method(name: "Taro", num: 2);
}
public void default_method(string name = "Jone", int num = 1)
{
Console.WriteLine(string.Format("{0}: {1}", name, num.ToString()));
}
}
To define a function that takes arguments by reference, define the argument with a name that starts with _ref_ or ref_ .
To pass arguments by reference, use the ref function.
That is UniPy-specific function defined in unipy_stubs/__init__.py.
An example of defining a function inout_world in class Hoge that receives two arguments
by reference and calling that function:
(The arguments for ref_bb are written as
'named arguments (keyword arguments)'
calls)
class Hoge():
def __init__(self) -> None:
aa: str = "Hello"
bb: str = "Hello"
self.inout_world(ref(aa), ref_bb=ref(bb))
print(aa)
print(bb)
def inout_world(self, _ref_aa: str, ref_bb: str) -> None:
_ref_aa += " World A"
ref_bb += " World B"
class Hoge():
def __init__(self):
# type: () -> None
aa = "Hello" # type: str
bb = "Hello" # type: str
self.inout_world(ref(aa), ref_bb=ref(bb))
print(aa)
print(bb)
def inout_world(self, _ref_aa, ref_bb):
# type: (str, str) -> None
_ref_aa += " World A"
ref_bb += " World B"
This translates to:
class Hoge
{
init()
{
var aa: String = "Hello";
var bb: String = "Hello";
inout_world(&aa, ref_bb: &bb);
print(aa);
print(bb);
}
func inout_world(_ _ref_aa: inout String, ref_bb: inout String) -> Void
{
_ref_aa += " World A";
ref_bb += " World B";
}
}
public class Hoge
{
public Hoge()
{
var aa = "Hello";
var bb = "Hello";
inout_world(ref aa, ref_bb: ref bb);
Console.WriteLine(aa);
Console.WriteLine(bb);
}
public void inout_world(ref string _ref_aa, ref string ref_bb)
{
_ref_aa += " World A";
ref_bb += " World B";
}
}
An example of defining a function params_world with a variable number of arguments in class Hoge and calling that function:
class Hoge():
def __init__(self) -> None:
self.params_world("a", "b", "c")
def params_world(self, *_abc: str) -> None:
for x in _abc:
print(x)
class Hoge():
def __init__(self):
# type: () -> None
self.params_world("a", "b", "c")
def params_world(self, *_abc):
# type: (str) -> None
for x in _abc:
print(x)
This translates to:
class Hoge
{
init()
{
params_world("a", "b", "c");
}
func params_world(_ _abc: String...) -> Void
{
for x in _abc
{
print(x);
}
}
}
public class Hoge
{
public Hoge()
{
params_world("a", "b", "c");
}
public void params_world(params string[] _abc)
{
foreach (var x in _abc)
{
Console.WriteLine(x);
}
}
}
Define it as a Python comment as follows, and write Swift/C# code inside:
"""native
...
"""
You must start with the word
native
and write the content on a new line.
The content written in native is not converted by UniPy at all and is written directly to the converted Swift/C# source.
Please use this when you are using a notation that UniPy does not support or when conversion is not possible.
An example of defining a function call_from_native in the class BaseClass and calling that function without going through UniPy conversion:
class BaseClass():
def __init__(self) -> None:
"""native
// Write swift/cs code here.
call_from_native();
"""
"""native_swift
// Write swift code here.
"""
"""native_cs
// Write cs code here.
"""
pass
def call_from_native(self) -> None:
print("Hello World")
class BaseClass():
def __init__(self):
# type: () -> None
"""native
// Write swift/cs code here.
call_from_native();
"""
"""native_swift
// Write swift code here.
"""
"""native_cs
// Write cs code here.
"""
pass
def call_from_native(self):
# type: () -> None
print("Hello World")
This translates to:
class BaseClass
{
init()
{
// Write swift/cs code here.
call_from_native();
// Write swift code here.
}
func call_from_native() -> Void
{
print("Hello World");
}
}
public class BaseClass
{
public BaseClass()
{
// Write swift/cs code here.
call_from_native();
// Write cs code here.
}
public void call_from_native()
{
Console.WriteLine("Hello World");
}
}
Define it as a Python comment as follows, and write the Python code you don't want to be converted between
# no_unipy start
...
# no_unipy end
Anything written within the two comments will not be converted by UniPy, and will not be written to the converted Swift/C# source.
Please use this when you want to prepare a dummy definition to avoid errors in the IDE.
An example of defining three over_world functions (one of which is a dummy definition for Mypy that is not converted by UniPy) and calling them:
from typing import Any, overload
class Hoge():
def __init__(self) -> None:
print(self.over_world(99))
print(self.over_world("Bye-bye"))
@overload
def over_world(self, _i: int) -> str:
return "Hello Over World" + str(_i)
@overload
def over_world(self, _s: str) -> str:
return _s + " Over World"
# no_unipy start
def over_world(self, *args: Any, **kwargs: Any) -> str:
pass
# no_unipy end
from typing import Any, overload
class Hoge():
def __init__(self):
# type: () -> None
print(self.over_world(99))
print(self.over_world("Bye-bye"))
@overload
def over_world(self, _i):
# type: (int) -> str
return "Hello Over World" + str(_i)
@overload
def over_world(self, _s):
# type: (str) -> str
return _s + " Over World"
# no_unipy start
def over_world(self, *args, **kwargs):
# type: (Any, Any) -> str
pass
# no_unipy end
This translates to:
class Hoge
{
init()
{
print(over_world(99));
print(over_world("Bye-bye"));
}
func over_world(_ _i: Int) -> String
{
return "Hello Over World" + _i.description;
}
func over_world(_ _s: String) -> String
{
return _s + " Over World";
}
}
public class Hoge
{
public Hoge()
{
Console.WriteLine(over_world(99));
Console.WriteLine(over_world("Bye-bye"));
}
public string over_world(int _i)
{
return "Hello Over World" + _i.ToString();
}
public string over_world(string _s)
{
return _s + " Over World";
}
}
Swift/C# do not require any special keywords to define overloads.
However, in Python, if you check with Mypy, you will get the following 'duplicate definition error'.
For modules that have stub files in unipy_stubs/,
importing that module will import/use the corresponding Swift/C# module.
For modules that do not have stub files, use the """native~""" syntax.
When converting to C#, you can also use a function that will remove comments during conversion by writing using statements as comments.
from unipy_stubs.swift_api.swift_ui import *
"""native_swift
import Foundation
"""
This translates to:
import SwiftUI
import Foundation
from unipy_stubs.unity_api.unity_engine import *
"""native_cs
using UnityEditor;
"""
# using OpenCvSharp;
This translates to:
using UnityEngine;
using UnityEditor;
using OpenCvSharp;
Use the @override decorator.
If the IDE's execution environment is Python 3.12 or later,
it is included in the typing module.
It is defined in unipy_stubs/__init__.py for when the IDE execution environment is Python 3.11 or earlier.
(*The Python version of the IDE's execution environment has no effect on UniPy conversion.
It is merely a matter of specifying it to resolve reference relationships in the IDE's editor.)
When converting to C#,
you must also specify the @virtual decorator on the overridden method,
which will be converted to the virtual keyword.
It is defined in unipy_stubs/__init__.py.
This is not necessary when converting to Swift, but even if you specify it, it will be removed so there is no problem.
An example of overriding the function ride_world defined in the class BaseClass
in its subclass SubClass and calling it from the class Hoge to check:
In order to make Python code examples common between Swift and C#,
the new function is inserted when instantiating the class.
This is not necessary when converting to Swift, but even if specified, it will be removed so there is no problem.
For more information,
see "Specifications that are relevant only to conversion to C# - Class Instantiation"
from unipy_stubs import *
class BaseClass():
@virtual
def ride_world(self) -> None:
print("I'm BaseClass")
class SubClass(BaseClass):
@override
def ride_world(self) -> None:
print("I'm SubClass")
class Hoge():
def __init__(self) -> None:
ride1: BaseClass = new(BaseClass())
ride1.ride_world()
ride2: SubClass = new(SubClass())
ride2.ride_world()
from unipy_stubs import *
class BaseClass():
@virtual
def ride_world(self):
# type: () -> None
print("I'm BaseClass")
class SubClass(BaseClass):
@override
def ride_world(self):
# type: () -> None
print("I'm SubClass")
class Hoge():
def __init__(self):
# type: () -> None
ride1: BaseClass = new(BaseClass())
ride1.ride_world()
ride2: SubClass = new(SubClass())
ride2.ride_world()
This translates to:
class BaseClass
{
func ride_world() -> Void
{
print("I'm BaseClass");
}
}
class SubClass : BaseClass
{
override func ride_world() -> Void
{
print("I'm SubClass");
}
}
class Hoge
{
init()
{
var ride1: BaseClass = BaseClass();
ride1.ride_world();
var ride2: SubClass = SubClass();
ride2.ride_world();
}
}
public class BaseClass
{
public virtual void ride_world()
{
Console.WriteLine("I'm BaseClass");
}
}
public class SubClass : BaseClass
{
public override void ride_world()
{
Console.WriteLine("I'm SubClass");
}
}
public class Hoge
{
public Hoge()
{
BaseClass ride1 = new BaseClass();
ride1.ride_world();
SubClass ride2 = new SubClass();
ride2.ride_world();
}
}
Define it as an internal function with a name starting with _tc.
The call is converted to a trailing closure in Swift and an anonymous closure in C#.
class Hoge():
def __init__(self) -> None:
def _tc0() -> None:
print("Unname0")
self.unname_func_world(_tc0)
def _tc1() -> None:
print("Unname1")
def _tc2() -> None:
print("Unname2")
self.unname_func_world2(_tc1, action2=_tc2)
def _tc3(_s: str) -> None:
print("Unname3 {0}".format(_s))
def _tc4(_s: str) -> None:
print("Unname4 {0}".format(_s))
self.unname_func_world3(action1=_tc3, action2=_tc4)
def unname_func_world(self, action: Callable[[], None]) -> None:
action()
def unname_func_world2(self, action1: Callable[[], None], action2: Callable[[], None]) -> None:
action1()
action2()
def unname_func_world3(self, action1: Callable[[str], None], action2: Callable[[str], None]) -> None:
action1("Hello")
action2("World")
class Hoge():
def __init__(self):
# type: () -> None
def _tc0():
# type: () -> None
print("Unname0")
self.unname_func_world(_tc0)
def _tc1():
# type: () -> None
print("Unname1")
def _tc2():
# type: () -> None
print("Unname2")
self.unname_func_world2(_tc1, action2=_tc2)
def _tc3(_s):
# type: (str) -> None
print("Unname3 {0}".format(_s))
def _tc4(_s):
# type: (str) -> None
print("Unname4 {0}".format(_s))
self.unname_func_world3(action1=_tc3, action2=_tc4)
def unname_func_world(self, action):
# type: (Callable[[], None]) -> None
action()
def unname_func_world2(self, action1, action2):
# type: (Callable[[], None], Callable[[], None]) -> None
action1()
action2()
def unname_func_world3(self, action1, action2):
# type: (Callable[[str], None], Callable[[str], None]) -> None
action1("Hello")
action2("World")
This translates to:
class Hoge
{
init()
{
unname_func_world() {
print("Unname0");
}
unname_func_world2() {
print("Unname1");
} action2: {
print("Unname2");
}
unname_func_world3() { _s in
print("Unname3 \(_s)");
} action2: { _s in
print("Unname4 \(_s)");
}
}
func unname_func_world(action: () -> Void) -> Void
{
action();
}
func unname_func_world2(action1: () -> Void, action2: () -> Void) -> Void
{
action1();
action2();
}
func unname_func_world3(action1: (String) -> Void, action2: (String) -> Void) -> Void
{
action1("Hello");
action2("World");
}
}
public class Hoge
{
public Hoge()
{
unname_func_world(() => {
Console.WriteLine("Unname0");
});
unname_func_world2(() => {
Console.WriteLine("Unname1");
}, action2: () => {
Console.WriteLine("Unname2");
});
unname_func_world3(action1: (string _s) => {
Console.WriteLine(string.Format("Unname3 {0}", _s));
}, action2: (string _s) => {
Console.WriteLine(string.Format("Unname4 {0}", _s));
});
}
public void unname_func_world(Action action)
{
action();
}
public void unname_func_world2(Action action1, Action action2)
{
action1();
action2();
}
public void unname_func_world3(Action<string> action1, Action<string> action2)
{
action1("Hello");
action2("World");
}
}
You define an asynchronous function using the async keyword and call it using the await keyword.
To call asynchronous functions in parallel, use the create_task function from the asyncio module.
Also, the run function from the asyncio module is converted to an asynchronous function call using Task in Swift and Task.Run in C#.
Define two asynchronous functions, async_loop and async_world, in the class Hoge,
call async_world in the init of the class Hoge,
run two async_loops in parallel from async_world, wait for the two results, return them, and print them:
from typing import Awaitable
from asyncio import create_task, run, sleep
class Hoge():
def __init__(self) -> None:
print("before async_world()")
async def _tc_task() -> None:
print(await self.async_world())
run(_tc_task())
print("after async_world() - printed before 'Done async_loop'")
async def async_loop(self, _loop: int) -> int:
ret: int = 0
for i in range(_loop):
ret += 1
print("Lopping in async_loop({0}): {1}".format(_loop, i))
try:
await sleep(1.0)
except Exception:
pass
print("Done async_loop({0})".format(_loop))
return ret
async def async_world(self) -> int:
a: Awaitable[int] = create_task(self.async_loop(5))
b = create_task(self.async_loop(3))
c: list[int] = [await a, await b]
return c[0] + c[1]
from typing import Awaitable
from asyncio import create_task, run, sleep
class Hoge():
def __init__(self):
# type: () -> None
print("before async_world()")
async def _tc_task() -> None:
print(await self.async_world())
run(_tc_task())
print("after async_world() - printed before 'Done async_loop'")
async def async_loop(self, _loop):
# type: (int) -> int
ret = 0 # type: int
for i in range(_loop):
ret += 1
print("Lopping in async_loop({0}): {1}".format(_loop, i))
try:
await sleep(1.0)
except Exception:
pass
print("Done async_loop({0})".format(_loop))
return ret
async def async_world(self):
# type: () -> int
a = create_task(self.async_loop(5)) # type: Awaitable[int]
b = create_task(self.async_loop(3))
c = [await a, await b] # type: list[int]
return c[0] + c[1]
This translates to:
class Hoge
{
init()
{
print("before async_world()");
Task() {
print(await self.async_world());
}
print("after async_world() - printed before 'Done async_loop'");
}
func async_loop(_ _loop: Int) async -> Int
{
var ret: Int = 0;
for i in 0 ..< _loop
{
ret += 1;
print("Lopping in async_loop(\(_loop)): \(i)");
do
{
try await Task.sleep(nanoseconds: UInt64(1.0 * 1_000_000_000));
}
catch
{
}
}
print("Done async_loop(\(_loop))");
return ret;
}
func async_world() async -> Int
{
async let a: Int = async_loop(5);
async let b = async_loop(3);
var c: [Int] = [await a, await b];
return c[0] + c[1];
}
}
public class Hoge
{
public Hoge()
{
Console.WriteLine("before async_world()");
Task.Run(async () => {
Console.WriteLine(await this.async_world());
});
Console.WriteLine("after async_world() - printed before 'Done async_loop'");
}
public async Task<int> async_loop(int _loop)
{
var ret = 0;
for (var i = 0; i < _loop; i++)
{
ret += 1;
Console.WriteLine(string.Format("Lopping in async_loop({0}): {1}", _loop, i));
try
{
await Task.Delay(Convert.ToInt32(1.0 * 1000));
}
catch
{
}
}
Console.WriteLine(string.Format("Done async_loop({0})", _loop));
return ret;
}
public async Task<int> async_world()
{
Task<int> a = Task.Run(() => async_loop(5));
var b = Task.Run(() => async_loop(3));
var c = new List<int> { await a, await b };
return c[0] + c[1];
}
}
# #if will be converted by removing the first # when written as a comment.
#if #else #endif is supported. (Alternatively, consider using """native~""")
# #if DEBUG
print("Debug version")
# #else
print("Release version")
# #endif
This translates to:
#if DEBUG
print("Debug version");
#else
print("Release version");
#endif
#if DEBUG
Console.WriteLine("Debug version");
#else
Console.WriteLine("Release version");
#endif
Exceptions are caught in the same way as in standard Python.
When converting to Swift,
it also supports conversion to the following keywords that can be processed with one-liners.
To convert an error to a try? keyword that is processed by converting it to an optional value,
use the try9 function.
If you want to convert it to a try! keyword that disables error propagation and handles it,
use the try1 function.
Both are UniPy-specific functions defined in unipy_stubs/__init__.py.
from unipy_stubs import *
a = "" # type: str
try:
a = open("a.txt", "r").read()
except Exception as error:
print(error)
b = try9(open("b.txt", "r")) # type: Var
c = try1(open("c.txt", "r")).read() # type: str
This translates to:
var a: String = "";
do
{
a = try open("a.txt", "r").read();
}
catch
{
print(error);
}
var b = try? open("b.txt", "r");
var c: String = try! open("c.txt", "r").read();
except is converted to catch.
from unipy_stubs.unity_api.unity_engine import *
class Hoge(MonoBehaviour):
def __init__(self) -> None:
s = None # type: str
try:
_ = len(s) # type: int
except NullReferenceException as e:
print(e)
finally:
s = ""
Debug.Log(s)
This translates to:
public class Hoge : MonoBehaviour
{
public Hoge()
{
string s = null;
try
{
var _ = s.Length;
}
catch (NullReferenceException e)
{
Console.WriteLine(e);
}
finally
{
s = "";
}
Debug.Log(s);
}
}
Conversion of built-in functions other than those listed below is not supported.
Even the built-in functions listed below do not necessarily have a conversion that provides equivalent functionality as Python.
abs()
This is addressed by providing compatible functions in unipy.swift.
bytes()
In unipy.swift, it is specified as a typealias of [byte].
dict()
It will be converted to [:]. Initialization using a constructor is not supported.
If you want to specify an initial value, use a description using {}.
float()
It is specified as a typealias for Float in unipy.swift.
Also, float('inf') is converted to Float.infinity.
int()
It is specified as a typealias of Int in unipy.swift.
isinstance()
It is translated into the is keyword.
len()
This is addressed by providing compatible functions in unipy.swift.
list()
It is specified as a typealias for Array in unipy.swift.
max()
This is addressed by providing compatible functions in unipy.swift.
min()
This is addressed by providing compatible functions in unipy.swift.
open()
This is addressed by providing compatible functions in unipy.swift. The functionality is limited.
print()
No conversion is performed, but Swift has an equivalent function, so it can be used.
property()
It is converted to a property definition.
See the "Writing properties" section for details.
range()
It is converted to the Half-Open (..<) operator.
If you need the Closed(...) operator, use the closedrange function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
reversed()
This is addressed by providing compatible functions in unipy.swift.
sorted()
This is addressed by providing compatible functions in unipy.swift. Options are not supported.
staticmethod()
It is converted to a static definition.
See the "Writing Static" section for details.
str()
Convert to .description.
It is specified as a typealias of String in unipy.swift.
super()
super(C, self) is converted to super.
(Specify the class name in C)
Conversion when arguments are omitted is not supported.
abs()
Convert to Math.Abs().
bytearray()
Convert to byte[].
dict()
It is converted to new Dictionary<TKey, TValue>(). Initialization using a constructor is not supported.
If you want to specify an initial value, use a statement using {}.
float()
If the argument is only a number, it will be appended with f, otherwise it will be converted to float.Parse().
Also, float('inf') will be converted to float.MaxValue.
int()
It will be converted to int.Parse().
If the destination type is recognized as short/long, it will be converted to short.Parse()/long.Parse().
isinstance()
It is translated into the is keyword.
len()
It will be converted to .Count or .Length depending on the type of the variable specified in the argument.
list()
The conversion differs depending on whether the argument is not specified,
if the argument is a fixed string, if the argument is an array, or if the argument is a list.
lst1 = list() # type: list[str]
lst2 = list("Hello World") # type: list[char]
ary1 = [1, 2, 3] # type: array[int]
lst3 = list(ary1) # type: list[int]
ary2 = list(lst3) # type: array[int]
This translates to:
var lst1 = new List<string>();
var lst2 = new List<char>();lst2.AddRange("Hello World");
var ary1 = new int[] { 1, 2, 3 };
var lst3 = new List<int>();lst3.AddRange(ary1.Cast<int>());
int[] ary2 = lst3.Cast<int>().ToArray();
print()
This is translated to Console.WriteLine().
property()
It is converted to a property definition. See the "Writing properties" section for details.
range()
If used outside of a for statement, it will be converted to Enumerable.Range().
reversed()
It is converted to Enumerable.Reverse(X).ToList().
sorted()
Convert to X.OrderBy(i => i).ToList().
staticmethod()
It is converted to a static definition. See the "Writing Static" section for details.
str()
Convert to .ToString().
super()
super(C, self) is converted to base.(Specify the class name in C)
Conversion when arguments are omitted is not supported.
Conversion of str object methods not listed below is not supported.
Even the str object methods listed below do not necessarily have a conversion that provides equivalent functionality as Python.
.count()
This is addressed by providing compatible functions in unipy.swift.
.lstrip(), .rstrip(), .strip()
This is addressed by providing compatible functions in unipy.swift.
.startswith(), .endswith()
This is addressed by providing compatible functions in unipy.swift.
.replace()
This is addressed by providing compatible functions in unipy.swift.
.format()
It will be converted to a description using \().
.split()
This is addressed by providing compatible functions in unipy.swift.
.ljust(), .rjust()
This is addressed by providing compatible functions in unipy.swift.
.join()
This is addressed by providing compatible functions in unipy.swift.
.rfind(), .find()
This is addressed by providing compatible functions in unipy.swift.
.isupper(), .isdigit()
This is addressed by providing compatible functions in unipy.swift.
.encode()
This is addressed by providing compatible functions in unipy.swift.
.lstrip(), .rstrip(), .strip()
Convert to .TrimStart(), .TrimEnd(), .Trim()
.upper(), .lower()
Convert to .ToUpper(), .ToLower()
.startswith(), .endswith()
Convert to .StartsWith(), .EndsWith()
.replace()
Convert to .Replace()
.format()
Convert to string.Format()
.split()
Convert to .Split()
The separator character must be specified as '' (single quote).
.ljust(), .rjust()
Convert to .PadLeft(), .PadRight()
The fill character must be specified using '' (single quotes).
.join()
Convert to string.Join()
Use Final from the typing module.
from typing import Final
hoge: Final[str] = "Hello"
from typing import Final
hoge = "Hello" # type: Final[str]
This translates to:
let hoge: String = "Hello";
To define a variable as optional, use the Optional from the typing module.
To unwrap an optional variable, use the unwrap function.
That is UniPy-specific function defined in unipy_stubs/__init__.py.
from typing import Final
hoge: Optional[str] = None
print(unwrap(hoge)) # This will result in a run-time error.
from typing import Final
hoge = None # type: Optional[str]
print(unwrap(hoge)) # This will result in a run-time error.
This translates to:
var hoge: Optional<String> = nil;
print(hoge!);
To define a data class (struct), use the @dataclass decorator from the dataclasses module.
from dataclasses import dataclass
from typing import ClassVar
@dataclass
class HogeData():
aa: str = ""
Bb: ClassVar[int] = 0
hd: HogeData = HogeData(aa="AA")
This translates to:
struct HogeData
{
var aa: String = "";
static var Bb: Int = 0;
}
var hd: HogeData = HogeData(aa: "AA");
Use the @mutating decorator.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
from unipy_stubs.swift_api.swift import *
@dataclass
class HogeData():
def __init__(self) -> None:
self.count: int = 0
@mutating
def add_count(self) -> None:
self.count += 1
from unipy_stubs.swift_api.swift import *
@dataclass
class HogeData():
def __init__(self):
# type: () -> None
self.count = 0 # type: int
@mutating
def add_count(self):
# type: () -> None
self.count += 1
This translates to:
struct HogeData
{
var count: Int = 0;
init()
{
}
mutating func add_count() -> Void
{
count += 1;
}
}
Use the sharp_selector function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
"""native
import Foundation
"""
class Hoge():
def __init__(self) -> None:
Timer.scheduledTimer(timeInterval=1,
target=self,
selector=sharp_selector(Hoge.sel_method),
userInfo=None,
repeats=False)
@attribute('objc')
def sel_method(self) -> None:
print("Hello #selector World")
"""native
import Foundation
"""
class Hoge():
def __init__(self):
# type: () -> None
Timer.scheduledTimer(timeInterval=1,
target=self,
selector=sharp_selector(Hoge.sel_method),
userInfo=None,
repeats=False)
@attribute('objc')
def sel_method(self):
# type: () -> None
print("Hello #selector World")
This translates to:
class Hoge
{
init()
{
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(Hoge.sel_method), userInfo: nil, repeats: false);
}
@objc
func sel_method() -> Void
{
print("Hello #selector World");
}
}
Define it by wrapping it in some type.
That is a UniPy-specific type defined in unipy_stubs/swift_api/swift.py as a TypeAlias of Any[T].
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self) -> some[View]:
return EmptyView()
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self):
# type: () -> some[View]
return EmptyView()
This translates to:
struct HogeView : View
{
var body: some View
{
return EmptyView();
}
}
There are two ways to write it.
One is,
Define a function with a return type of viewbuilder.
That is a UniPy-specific type defined in unipy_stubs/swift_api/swift.py as a TypeAlias of Any.
The other one is,
Specify a ViewBuilder class in the @attribute decorator and specify some[View] as the return value.
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self) -> some[View]:
def _tc_view() -> viewbuilder:
self.my_content1()
self.my_content2()
return VStack(content=_tc_view)
def my_content1(self) -> viewbuilder:
Text("Hello World 1")
@attribute(ViewBuilder)
def my_content2(self) -> some[View]:
Text("Hello World 2")
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeView(View):
@property
def body(self):
# type: () -> some[View]
def _tc_view():
# type: () -> viewbuilder
self.my_content1()
self.my_content2()
return VStack(content=_tc_view)
def my_content1(self):
# type: () -> viewbuilder
Text("Hello World 1")
@attribute(ViewBuilder)
def my_content2(self):
# type: () -> some[View]
Text("Hello World 2")
This translates to:
struct HogeView : View
{
var body: some View
{
return VStack() {
self.my_content1();
self.my_content2();
}
}
@ViewBuilder func my_content1() -> some View
{
Text("Hello World 1");
}
@ViewBuilder
func my_content2() -> some View
{
Text("Hello World 2");
}
}
If you write SwiftUIList it will be converted to List.
The unipy_stubs/swift_api/swift_ui stubs also define List as a SwiftUIList.
This is a measure to prevent names from overlapping with the typing module List.
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeListView(View):
@property
def body(self) -> some[View]:
def _tc_list() -> viewbuilder:
def _tc_foreach(i: int) -> viewbuilder:
Text(str(i))
ForEach(range(5), content=_tc_foreach)
return SwiftUIList(content=_tc_list)
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class HogeListView(View):
@property
def body(self):
# type: () -> some[View]
def _tc_list():
# type: () -> viewbuilder
def _tc_foreach(i):
# type: (int) -> viewbuilder
Text(str(i))
ForEach(range(5), content=_tc_foreach)
return SwiftUIList(content=_tc_list)
This translates to:
struct HogeListView : View
{
var body: some View
{
return List() {
ForEach(0..<5) { i in
Text(i.description);
}
}
}
}
Use the keypath function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
It is converted in three different ways depending on the arguments.
When defining a KeyPath object,
specify the type name and path as a string in the first argument.
When reading a value using keyPath,
specify the target object as the first argument and the KeyPath object as the second argument.
When assigning a value with keyPath,
specify the target object as the first argument, the KeyPath object as the second argument, and the value as the third argument.
hd: HogeData = HogeData()
kp: KeyPath[HogeData, Int] = keypath("HogeData.count")
c: int = keypath(hd, kp)
print("Read by keypath: {}".format(c))
kp2: WritableKeyPath[HogeData, Int] = keypath("HogeData.count")
keypath(hd, kp2, 99)
print("Written by keypath: {}".format(hd.count))
hd = HogeData() # type: HogeData
kp = keypath("HogeData.count") # type: KeyPath[HogeData, Int]
c = keypath(hd, kp) # type: int
print("Read by keypath: {}".format(c))
kp2 = keypath("HogeData.count") # type: WritableKeyPath[HogeData, Int]
keypath(hd, kp2, 99)
print("Written by keypath: {}".format(hd.count))
This translates to:
var hd: HogeData = HogeData();
var kp: KeyPath<HogeData, Int> = \HogeData.count;
var c: Int = hd[keyPath: kp];
print("Read by keypath: \(c)");
var kp2: WritableKeyPath<HogeData, Int> = \HogeData.count;
hd[keyPath: kp2] = 99;
print("Written by keypath: \(hd.count)");
Use the self_ function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
Also, if you specify type[X] as a type hint, it will be converted to X.Type.
t: type[HogeData] = self_(HogeData)
print(t)
t = self_(HogeData) # type: type[HogeData]
print(t)
This translates to:
var t: HogeData.Type = HogeData.self;
print(t);
To pass variables through binding, use the binding function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
The variable specified as an argument to the binding function will be converted to a name with $ appended.
To assign a value to a variable with the @Binding attribute, use the unbinding function.
That is UniPy-specific function defined in unipy_stubs/swift_api/swift.py.
The destination variable will be converted to a name with an _ (underscore) attached.
Please note that this is a special conversion method compared to other UniPy functions.
from dataclasses import dataclass
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class Binding_SheetView(View):
def __init__(self, text: Binding[str]):
attribute(Binding)
self.text: str = unbinding(text)
@property
def body(self) -> some[View]:
def _tc_action() -> None:
self.text = "Hello Binding!"
return Button("Change Text", action=_tc_action)
@dataclass
class Binding_ContentView(View):
attribute(State)
text: str = "Hello World!"
attribute(State)
is_show_sheet: bool = False
@property
def body(self) -> some[View]:
def _tc_vstack() -> viewbuilder:
Text(self.text)
Spacer()
def _tc_action() -> None:
self.is_show_sheet = True
Button("Show Sheet", action=_tc_action)
def _tc_sheet() -> viewbuilder:
Binding_SheetView(text=binding(self.text))
return VStack(content=_tc_vstack)\
.sheet(isPresented=binding(self.is_show_sheet), content=_tc_sheet)
from dataclasses import dataclass
from unipy_stubs.swift_api.swift_ui import *
@dataclass
class Binding_SheetView(View):
def __init__(self, text):
# type: (Binding[str]) -> None
attribute(Binding)
self.text = unbinding(text) # type: str
@property
def body(self):
# type: () -> some[View]
def _tc_action():
# type: () -> None
self.text = "Hello Binding!"
return Button("Change Text", action=_tc_action)
@dataclass
class Binding_ContentView(View):
attribute(State)
text = "Hello World!" # type: str
attribute(State)
is_show_sheet = False # type: bool
@property
def body(self):
# type: () -> some[View]
def _tc_vstack():
# type: () -> viewbuilder
Text(self.text)
Spacer()
def _tc_action():
# type: () -> None
self.is_show_sheet = True
Button("Show Sheet", action=_tc_action)
def _tc_sheet():
# type: () -> viewbuilder
Binding_SheetView(text=binding(self.text))
return VStack(content=_tc_vstack)\
.sheet(isPresented=binding(self.is_show_sheet), content=_tc_sheet)
This translates to:
struct Binding_SheetView : View
{
@Binding
var text: String;
init(text: Binding<String>)
{
self._text = text;
}
var body: some View
{
return Button("Change Text") {
self.text = "Hello Binding!";
}
}
}
struct Binding_ContentView : View
{
@State
var text: String = "Hello World!";
@State
var is_show_sheet: Bool = false;
var body: some View
{
return VStack() {
Text(self.text);
Spacer();
Button("Show Sheet") {
self.is_show_sheet = true;
}
}.sheet(isPresented: $is_show_sheet) {
Binding_SheetView(text: self.$text);
}
}
}
This is about shorthand notation starting with . (dot) in contexts where the type can be implicitly determined by type inference.
For example,
Image(systemName="bold").imageScale(.large)
It is written as shown in the (.large) part above.Image(systemName="bold").imageScale(Image.Scale.large)
As shown above, it is necessary to write the expression without using implicit member expressions and without omitting any of them.Unlike Python, C# requires the new operator to instantiate a class.
Even in places where the new operator is required, it is not automatically added during conversion, so you must explicitly use the new function.
That is UniPy-specific function defined in unipy_stubs/__init__.py.
Write the first argument in the same way as when you normally instantiate it in Python.
The second and subsequent arguments are converted to object initializers. Optional.
from unipy_stubs import *
class PointStruct():
def __init__(self, msg: str) -> None:
print("init msg={0}".format(msg))
self.X: int = 0
self.Y: int = 0
class Hoge():
def __init__(self) -> None:
xy1: PointStruct = new(PointStruct("Hello"))
print("xy1 X={0}, Y={1}".format(xy1.X, xy1.Y))
xy2: PointStruct = new(PointStruct("World"), X=1, Y=2)
print("xy2 X={0}, Y={1}".format(xy2.X, xy2.Y))
from unipy_stubs import *
class PointStruct():
def __init__(self, msg) -> None:
# type: (str) -> None
print("init msg={0}".format(msg))
self.X = 0 # type: int
self.Y = 0 # type: int
class Hoge():
def __init__(self):
# type: () -> None
xy1 = new(PointStruct("Hello")) # type: PointStruct
print("xy1 X={0}, Y={1}".format(xy1.X, xy1.Y))
xy2 = new(PointStruct("World"), X=1, Y=2) # type: PointStruct
print("xy2 X={0}, Y={1}".format(xy2.X, xy2.Y))
This translates to:
public class PointStruct
{
public int X;
public int Y;
public PointStruct(string msg)
{
Console.WriteLine(string.Format("init msg={0}", msg));
}
}
public class Hoge
{
public Hoge()
{
var xy1 = new PointStruct("Hello");
Console.WriteLine(string.Format("xy1 X={0}, Y={1}", xy1.X, xy1.Y));
var xy2 = new PointStruct("World") { X=1, Y=2 };
Console.WriteLine(string.Format("xy2 X={0}, Y={1}", xy2.X, xy2.Y));
}
}
Write it as a comment starting with # namespace .
From then on, the nesting of the entire file will be one level deeper and the entire file will be surrounded by {} until the end of the file.
Nested conversion is not supported. (If nesting is required, consider using """native~""".)
# namespace HogeSpace
class Hoge():
pass
This translates to:
namespace HogeSpace
{
public class Hoge
{
}
}
Or, if you want to use "file scope namespace declarations" from C#10 onwards:
Please add ; at the end.
# namespace HogeSpace;
Lines with Python assert statements are ignored and are not output to the converted source.
However, writing assert statements in Python can be useful when enabling type hints in an IDE (Linter).
Even if it is not in the above list, conversion to Swift/C# grammar or expressions not explained on this page or in the sample source is not supported.
(However, if the syntax (formula) is exactly the same in Python and Swift/C#, it will work.)
If you need to use grammar or expressions that UniPy does not support conversion, you can either use the """native~""" syntax, or extract that part into a Swift/C# source file and write it directly as Swift/C# code, and simply have the code written in UniPy call the function, etc., to handle the situation in your operation.