UniPy

Manual

Go Product Page »


What UniPy can't do

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.

Description specification for correct conversion with UniPy

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.

It is written as follows:

An example of defining a string type variable hoge:

This translates to:


An example of defining a function (method) that takes an int argument and returns a string:

This translates to:

How to write a variable declaration without an initial value (Dummy.init)
In UniPy, variable definitions without initial values cannot be converted properly as is.
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.
It is defined in unipy_stubs/__init__.py. It is a class variable of Any type.
hoge: str = Dummy.init  # Write as a dummy initial value

This translates to:

How to write variable declarations when using type inference
Use Var type to specify the type hint.
It is defined in unipy_stubs/__init__.py as a TypeAlias of Any.

This translates to:

An example of just defining an empty constructor and destructor for class Hoge:

This translates to:

Define it as a variable by adding self inside __init__ as usual.

When converting to Swift/C#, it will be moved outside __init__.

This translates to:

Static members are defined as class variables.

Static methods are created by adding the @staticmethod decorator when defining the function.

This translates to:

Following the PEP8 naming convention, define a name that starts with an _(underscore).

This translates to:

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.

This translates to:

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.

This translates to:

The code is different when converting to Swift and when converting to C#.

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.

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.

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:

This translates to:

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:

This translates to:

This translates to:

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:

This translates to:

It will be converted as follows:

  • elif to else if
  • and to &&
  • or to ||
  • not to !
  • in to calling contain method

==,!=,<=,>=,<,> The syntax for these comparison operators does not differ between languages, so no conversion takes place.

This translates to:

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:

This translates to:

An example of defining and accessing a tuple with two elements of type int and str:

This translates to:

Restrictions on accessing tuple elements (index specification)
  • Supports 0 to 9. Conversion of indexes greater than this is not supported.
  • Must be a static value. Dynamic values cannot be converted.
  • In Swift or C#, it is not possible to iterate over the elements of a tuple using a for statement like in Python, Currently, it is not possible to convert it properly using UniPy.

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:

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:

This translates to:

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:

This translates to:

This translates to:

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)

This translates to:

An example of defining a function params_world with a variable number of arguments in class Hoge and calling that function:

This translates to:

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:

This translates to:

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:

This translates to:

How to write overloads

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'.

This error does not affect UniPy conversion, but to avoid this error, write it using the @overload decorator in the typing module, as in the Python example above.

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.

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"

This translates to:

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#.

This translates to:

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:

This translates to:

# #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:

Exceptions are caught in the same way as in standard Python.

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.

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.

Specifications that are relevant only to conversion to Swift

Use Final from the typing module.

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.

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.

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.

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].

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.

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.

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.

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.

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.

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.
Since this is not supported in Python, you may see an error message such as
'invalid syntax Mypy(syntax)' in your IDE (Linter).
To avoid errors in the IDE,
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.

However, conversion with UniPy will not be affected even if an error occurs in the IDE.
Even if you use implicit member expressions, they will be written directly into Swift code.
If you don't mind IDE errors, you can leave it as is.

Specifications that are relevant only to conversion to C#

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.

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;

Other

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).

  • with
  • match(switch)

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.