Upgrading from IronPython2 (original) (raw)
IronPython 3.4 uses Python 3.4 syntax and standard libraries and so your Python code will need to be updated accordingly. There are numerous tools and guides available on the web to help porting from Python 2 to 3.
Binary compatibility
The IronPython 3 binaries are not compatible with the IronPython 2 binaries. Modules compiled with clr.CompileModules
using IronPython 2 are not compatible and will need to be recompiled using IronPython 3.
Checking for IronPython
In an effort to improve compatibility, sys.platform
no longer returns cli
. If you wish to check if you're running on IronPython the recommended pattern is to check that sys.implementation.name
is equal to ironpython
:
if sys.implementation.name == "ironpython": print("IronPython!")
None
is a keyword
None
is a keyword in Python 3 and trying to access a member called None
will raise a SyntaxError
. Since this name is frequently used in .NET code (e.g. in enums), code trying to use it is going to throw. You can use alternate syntax in order to access the .NET member, for example getattr(x, "None")
or an accessor for enums MyEnum["None"]
.
IronPython 2
System.StringSplitOptions.None
IronPython 3
System.StringSplitOptions["None"]
Similarly, True
and False
are also keywords in Python 3.
Redirecting output
With IronPython 2, standard output was written to the runtime's SharedIO.OutputWriter
(which was Console.Out
by default). This is no longer the case with IronPython 3 where the standard output is a binary stream. The output is now written to runtime's SharedIO.OutputStream
. Similarly, standard input and error are now using SharedIO.InputStream
and SharedIO.ErrorStream
respectively.
Because of this, using a TextWriter
to capture output will no longer work. As a workaround, in order to use a TextWriter
as the main method of redirection, one could wrap the writer inside a stream (for example, see TextStream).
// IronPython 2 var engine = Python.CreateEngine(); var textWriter = new MyTextWriter(); // no longer works in IronPython 3! engine.Runtime.IO.RedirectToConsole(); Console.SetOut(textWriter);
// IronPython 3 var engine = Python.CreateEngine(); var textWriter = new MyTextWriter(); engine.Runtime.IO.SetOutput(new TextStream(textWriter), textWriter);
Another way of achieving the redirection behavior similar to IronPython 2 is to set engine option ConsoleSupportLevel
to SupportLevel.Basic
. IronPython 3 still uses binary streams for standard input and output but all three standard streams are set to use TextStream
, forwarding to the corresponding writers/reader.
// IronPython 3 var engine = Python.CreateEngine(new Dictionary<string, object> { { "ConsoleSupportLevel", Microsoft.Scripting.Runtime.SharedIO.SupportLevel.Basic }, }); var textWriter = new MyTextWriter(); // works again! engine.Runtime.IO.RedirectToConsole(); Console.SetOut(textWriter);
This method is particularly useful when embedding the IronPython 3 engine in a host that sets console's writers/reader before the engine is created and the host's code is not accessible to the user (for instance, scripting in LINQPad).
// IronPython 3 in LINQPad var engine = Python.CreateEngine(new Dictionary<string, object> { { "ConsoleSupportLevel", Microsoft.Scripting.Runtime.SharedIO.SupportLevel.Basic }, }); engine.Execute("print('abc')"); // shows output in the "Results" pane dynamic ans = engine.Execute("input()"); // pauses the script and asks for input at the bottom of the "Results" pane; terminate your input with Ctrl+Z, Enter
int
Type
One of the major backward incompatible changes in Python 3 is PEP 237 – Unifying Long Integers and Integers: Essentially, long
renamed to int
. That is, there is only one built-in integral type, named int
; but it behaves mostly like the old long
type. From the pure Python perspective this means that int
should be used wherever previously long
was used. More consideration has to be applied in interop cases with .NET.
The Python int
type in IronPython 3 is implemented as System.Numerics.BigInteger
(and not as System.Int32
as it was in IronPython 2). It can contain in theory an arbitrarily large integer (only limited by the 2 GByte memory boundary).
import clr clr.AddReference("System.Numerics") import System int is System.Numerics.BigInteger True int is System.Int32 False clr.GetClrType(int).Name 'BigInteger'
This means that in interop cases, when the int
type is used (think generics), it will mean BigInteger
and not Int32
(which was the case in IronPython 2). To retain IronPython 2 semantics, replace int
with System.Int32
.
Example:
IronPython 2
System.Collections.Generic.List[int]
IronPython 3
System.Collections.Generic.List[System.Int32]
Overview of int
type equivalency:
IronPython 2 | IronPython 3 | .NET |
---|---|---|
long | int | System.Numerics.BigInteger |
int | N/A | System.Int32 |
Instances of int
As for instances of int
, mostly for performance reasons, IronPython may use instances of System.Int32
to hold smaller integers, while BigInteger
instances are used for large integers. This is done transparently from the Python side, but again the distinction may become relevant for interop cases. Examples:
i = 1 # instance of Int32 j = 1 << 31 # instance of BigInteger k = j - 1 # still BigInteger, as one of the arguments makes the result type BigInteger
This means that the type of Int32
objects is always reported as int
(which is the same as BigInteger
). If it is important to check what is the actual type of a given integer object, test if the object is an instance of System.Int32
. (An alternative way is a test for the presence of MaxValue
or MinValue
. For those properties to be visible, System
has to be imported first.)
import System type(i) <class 'int'> isinstance(i, System.Int32) True type(j) <class 'int'> isinstance(j, System.Int32) False hex(i.MaxValue) '0x7fffffff'
The creation of either Int32
or BigInteger
instances happens automatically by the int
constructor. If for interop purposes it is important to create a BigInteger
(despite the value fitting in 32 bits), use method ToBigInteger
. It converts Int32
values to BigInteger
and leaves BigInteger
values unaffected.
bi = i.ToBigInteger() isinstance(j, System.Int32) False
In the opposite direction, if it is essential to create Int32
objects, either use constructors for int
or Int32
. In the current implementation, the former converts an integer to Int32
if the value fits in 32 bits, otherwise it leaves it as BigInteger
. The latter throws an exception if the conversion is not possible. Although the behavior of the constructor int
may or may not change in the future, it is always guaranteed to convert the value to the "canonical form" adopted for that version of IronPython.
k is a BigInteger that fits in 32 bits
isinstance(k, System.Int32) False hex(k) '0x7fffffff' ki = int(k) # converts k to Int32 isinstance(ki, System.Int32) True ki = System.Int32(k) # also converts k to Int32 isinstance(ki, System.Int32) True
j is a BigInteger that does not fit in 32 bits
isinstance(j, System.Int32) False hex(j) '0x80000000' j = int(j) # no type change, j stays BigInteger isinstance(j, System.Int32) False j = System.Int32(j) # conversion fails Traceback (most recent call last): File "", line 1, in OverflowError: Arithmetic operation resulted in an overflow.
Such explicit conversions are in most cases unnecessary since the runtime recognizes int
/Int32
equivalence of instances and performs necessary conversions automatically.
import System int_list = System.Collections.Generic.Listint int_list.Add(1) # Int32 instance converted to BigInteger int32_list = System.Collections.Generic.ListSystem.Int32 int32_list.Add((1).ToBigInteger()) # BigInteger instance converted to Int32 int_list[0] == int32_list[0] True
Pickling and unpickling of int
When an int
object is serialized using pickle.dump(x, myfile)
and subsequently unpickled with x = pickle.load(myfile)
(or pickle.loads(pickle.dumps(x))
, this has the same effect as reconstructing the object using the int
constructor, i.e. x = int(x)
. In other words, if the x
instance was BigInteger
but the value fits in Int32
, it will be reconstructed as Int32
.
BigIntegerV2 API
In IronPython 2, long
type carries an obsolete BigIntegerV2
API, accessible after importing System
. In IronPython 3 this API is not available directly on int
instances (regardless of whether the instance is Int32
or BigInteger
), but is still accessible in some form through Microsoft.Scripting.Utils.MathUtils
in Microsoft.Dynamic.dll
.
IronPython 2
i = 1 # instance of Int32 (int) j = 1 << 64 # instance of BigInteger (long) import System j.GetWords() Array[UInt32]((0, 0, 1)) i.GetWords() Traceback (most recent call last): File "", line 1, in AttributeError: 'int' object has no attribute 'GetWords' long.GetWords(i) ArrayUInt32
IronPython 3
i = 1 # instance of Int32 (int) j = 1 << 64 # instance of BigInteger (int) import clr clr.AddReference("Microsoft.Dynamic") import Microsoft.Scripting.Utils.MathUtils clr.ImportExtensions(Microsoft.Scripting.Utils.MathUtils) j.GetWords() Array[UInt32]((0, 0, 1)) i.GetWords() Traceback (most recent call last): File "", line 1, in AttributeError: 'int' object has no attribute 'GetWords' Microsoft.Scripting.Utils.MathUtils.GetWords(i) ArrayUInt32
Another set of Python-hidden methods on long
in IronPython 2 that are not available on int
in IronPython 3 are conversion methods with names like ToXxx
. The recommended way to perform type conversions like those is to use type constructors. The exception is the conversion to BigInteger
itself, for the reasons explained above.
IronPython 2
j = long(1) i64 = j.ToInt64()
IronPython 3
import System j = (1).ToBigInteger() i64 = System.Int64(j)
range
IronPython's range
is a generator that produces a sequence of int
values. The values are instances of Int32
or BigInteger
, depending on the actual integer value they represent. When range
is used in a LINQ context, it exposes interface IEnumerable<Int32>
and all values generated are of type Int32
. This limits the possible value to the range Int32.MinValue
to Int32.MaxValue
.