aboutsummaryrefslogtreecommitdiff
blob: ac29e606132c14976a43e65cbcf28d9c7b2f9407 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
==================================================
CarbonPython, aka C# considered harmful
==================================================

CarbonPython overview
=====================

CarbonPython is an experimental RPython to .NET compiler. Its main
focus is to produce DLLs to be used by other .NET programs, not
standalone executables; if you want to compile an RPython standalone
program, have a look to `translate.py`_.

Compiled RPython programs are much faster (up to 250x) than
interpreted IronPython programs, hence it might be a convenient
replacement for C# when more speed is needed. RPython programs can be
as fast as C# programs.

RPython is a restrict subset of Python, static enough to be analyzed
and compiled efficiently to lower level languages.  To read more about
the RPython limitations read the `RPython description`_.

**Disclaimer**: RPython is a much less convenient language than Python
to program with. If you do not need speed, there is no reason to look
at RPython.

**Big disclaimer**: CarbonPython is still in a pre-alpha stage: it's
not meant to be used for production code, and the API might change in
the future. Despite this, it might be useful in some situations and
you are encouraged to try it by yourself. Suggestions, bug-reports and
even better patches are welcome.

.. _`RPython description`: coding-guide.html#restricted-python
.. _`translate.py`: faq.html#how-do-i-compile-my-own-interpreters


Quick start
===========

Suppose you want to write a little DLL in RPython and call its
function from C#.

Here is the file mylibrary.py::

    from pypy.translator.cli.carbonpython import export

    @export(int, int)
    def add(x, y):
        return x+y

    @export(int, int)
    def sub(x, y):
        return x-y


And here the C# program main.cs::

    using System;
    public class CarbonPythonTest
    {
        public static void Main()
        {
            Console.WriteLine(mylibrary.add(40, 2));
            Console.WriteLine(mylibrary.sub(44, 2));
        }
    }

Once the files have been created, you can compile ``mylibrary.py``
with CarbonPython to get the corresponding DLL::

    $ python carbonpython.py mylibrary.py
    ... lot of stuff

Then, we compile main.cs into an executable, being sure to add a
reference to the newly created ``mylibrary.dll``::

    # with mono on linux
    $ gmcs /r:mylibrary.dll main.cs

    # with Microsoft CLR on windows
    c:\> csc /r:mylibrary main.cs

Now we can run the executable to see whether the answers are right::

    $ mono main.exe
    42
    42


Multiple entry-points
=====================

In RPython, the type of each variable is inferred by the `Annotator`_:
the annotator analyzed the whole program top-down starting from an
entry-point, i.e. a function whose we specified the types of the
parameters.

This approach works for a standalone executables, but not for a
library that by definition is composed by more than one
entry-point. Thus, you need to explicitly specify which functions you
want to include in your DLL, together with the expected input types.

To mark a function as an entry-point, you use the ``@export``
decorator, which is defined in ``pypy.translator.cli.carbonpython``,
as shown by the previous example.  Note that you do not need to
specify the return type, because it is automatically inferenced by the
annotator.

.. _`Annotator`: translation.html#annotator


Namespaces
==========

Since `CLS`_ (Common Language Specification) does not support module
level static methods, RPython functions marked as entry-points are
compiled to static methods of a class, in order to be accessible by
every CLS-compliant language such as C# or VB.NET.

The class which each function is placed in depends on its
**namespace**; for example, if the namespace of a function ``foo`` is
``A.B.C``, the function will be rendered as a static method of the
``C`` class inside the ``A.B`` namespace. This allows C# and
IronPython code to call the function using the intuitive ``A.B.C.foo``
syntax.

By default, the default namespace for exported function is the same as
the name of the module. Thus in the previous example the default
namespace is ``mylibrary`` and the functions are placed inside the
corresponding class in the global namespace.

You can change the default namespace by setting the ``_namespace_``
variable in the module you are compiling::

    _namespace_ = 'Foo.Bar'

    @export(int, int)
    def f(x, y):
        pass

Finally, you can also set a specific namespace on a per-function
basis, using the appropriate keyword argument of the ``@export``
decorator::

    @export(int, int, namespace='Foo.Bar')
    def f(x, y):
        pass


.. _`CLS`: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf


Exporting classes
=================

RPython libraries can also export classes: to export a class, add the
``@export`` decorator to its ``__init__`` method; similarly, you can
also export any methods of the class::

    class MyClass:

        @export(int)
        def __init__(self, x):
            self.x = x

        @export
        def getx(self):
            return self.x


Note that the type of ``self`` must not be speficied: it will
automatically assumed to be ``MyClass``.

The ``__init__`` method is not automatically mapped to the .NET
constructor; to properly initialize an RPython object from C# or
IronPython code you need to explicitly call ``__init__``; for example,
in C#::

    MyClass obj = new MyClass();
    obj.__init__(x);

Note that this is needed only when calling RPython code from 
outside; the RPython compiler automatically calls ``__init__``
whenever an RPython class is instantiated.

In the future this discrepacy will be fixed and the ``__init__``
method will be automatically mapped to the constructor.


Accessing .NET libraries
========================

**Warning**: the API for accessing .NET classes from RPython is highly
experimental and will probably change in the future.

In RPython you can access native .NET classes through the ``CLR``
object defined in ``translator.cli.dotnet``: from there, you can
navigate through namespaces using the usual dot notation; for example,
``CLR.System.Collections.ArrayList`` refers to the ``ArrayList`` class
in the ``System.Collections`` namespace.

To instantiate a .NET class, simply call it::

    ArrayList = CLR.System.Collections.ArrayList
    def foo():
        obj = ArrayList()
        obj.Add(42)
        return obj

At the moment there is no special syntax support for indexers and
properties: for example, you can't access ArrayList's elements using
the square bracked notation, but you have to call the call the
``get_Item`` and ``set_Item`` methods; similarly, to access a property
``XXX`` you need to call ``get_XXX`` and ``set_XXX``::

    def foo():
        obj = ArrayList()
        obj.Add(42)
        print obj.get_Item(0)
        print obj.get_Count()

Static methods and are also supported, as well as overloadings::

    Math = CLR.System.Math
    def foo():
        print Math.Abs(-42)
        print Math.Abs(-42.0)


At the moment, it is not possible to reference assemblies other than
mscorlib. This will be fixed soon.