- Notifications
You must be signed in to change notification settings - Fork301
Upgrading from IronPython2
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.
The IronPython 3 binaries are not compatible with the IronPython 2 binaries. Modules compiled withclr.CompileModules
using IronPython 2 are not compatible and will need to be recompiled using IronPython 3.
In an effort to improve compatibility,sys.platform
no longer returnscli
. If you wish to check if you're running on IronPython the recommended pattern is to check thatsys.implementation.name
is equal toironpython
:
ifsys.implementation.name=="ironpython":print("IronPython!")
None
is a keyword in Python 3 and trying to access a member calledNone
will raise aSyntaxError
. 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 examplegetattr(x, "None")
or an accessor for enumsMyEnum["None"]
.
# IronPython 2System.StringSplitOptions.None
# IronPython 3System.StringSplitOptions["None"]
Similarly,True
andFalse
are also keywords in Python 3.
With IronPython 2, standard output was written to the runtime'sSharedIO.OutputWriter
(which wasConsole.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'sSharedIO.OutputStream
. Similarly, standard input and error are now usingSharedIO.InputStream
andSharedIO.ErrorStream
respectively.
Because of this, using aTextWriter
to capture output will no longer work. As a workaround, in order to use aTextWriter
as the main method of redirection, one could wrap the writer inside a stream (for example, seeTextStream
).
// IronPython 2varengine=Python.CreateEngine();vartextWriter=newMyTextWriter();// no longer works in IronPython 3!engine.Runtime.IO.RedirectToConsole();Console.SetOut(textWriter);
// IronPython 3varengine=Python.CreateEngine();vartextWriter=newMyTextWriter();engine.Runtime.IO.SetOutput(newTextStream(textWriter),textWriter);
Another way of achieving the redirection behavior similar to IronPython 2 is to set engine optionConsoleSupportLevel
toSupportLevel.Basic
. IronPython 3 still uses binary streams for standard input and output butall three standard streams are set to useTextStream
, forwarding to the corresponding writers/reader.
// IronPython 3varengine=Python.CreateEngine(newDictionary<string,object>{{"ConsoleSupportLevel",Microsoft.Scripting.Runtime.SharedIO.SupportLevel.Basic},});vartextWriter=newMyTextWriter();// 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 inLINQPad).
// IronPython 3 in LINQPadvarengine=Python.CreateEngine(newDictionary<string,object>{{"ConsoleSupportLevel",Microsoft.Scripting.Runtime.SharedIO.SupportLevel.Basic},});engine.Execute("print('abc')");// shows output in the "Results" panedynamicans=engine.Execute("input()");// pauses the script and asks for input at the bottom of the "Results" pane; terminate your input with Ctrl+Z, Enter
One of the major backward incompatible changes in Python 3 isPEP 237 – Unifying Long Integers and Integers: Essentially,long
renamed toint
. That is, there is only one built-in integral type, namedint
; but it behaves mostly like the oldlong
type. From the pure Python perspective this means thatint
should be used wherever previouslylong
was used. More consideration has to be applied in interop cases with .NET.
The Pythonint
type in IronPython 3 is implemented asSystem.Numerics.BigInteger
(and not asSystem.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>>>intis System.Numerics.BigIntegerTrue>>>intis System.Int32False>>> clr.GetClrType(int).Name'BigInteger'
This means that in interop cases, when theint
type is used (think generics), it will meanBigInteger
and notInt32
(which was the case in IronPython 2). To retain IronPython 2 semantics, replaceint
withSystem.Int32
.
Example:
# IronPython 2System.Collections.Generic.List[int]
# IronPython 3System.Collections.Generic.List[System.Int32]
Overview ofint
type equivalency:
IronPython 2 | IronPython 3 | .NET |
---|---|---|
long | int | System.Numerics.BigInteger |
int | N/A | System.Int32 |
As for instances ofint
, mostly for performance reasons, IronPython may use instances ofSystem.Int32
to hold smaller integers, whileBigInteger
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 Int32j=1<<31# instance of BigIntegerk=j-1# still BigInteger, as one of the arguments makes the result type BigInteger
This means that the type ofInt32
objects is always reported asint
(which is the same asBigInteger
). If it is important to check what is the actual type of a given integer object, test if the object is an instance ofSystem.Int32
. (An alternative way is a test for the presence ofMaxValue
orMinValue
. 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 eitherInt32
orBigInteger
instances happens automatically by theint
constructor. If for interop purposes it is important to create aBigInteger
(despite the value fitting in 32 bits), use methodToBigInteger
. It convertsInt32
values toBigInteger
and leavesBigInteger
values unaffected.
>>> bi= i.ToBigInteger()>>>isinstance(j, System.Int32)False
In the opposite direction, if it is essential to createInt32
objects, either use constructors forint
orInt32
. In the current implementation, the former converts an integer toInt32
if the value fits in 32 bits, otherwise it leaves it asBigInteger
. The latter throws an exception if the conversion is not possible. Although the behavior of the constructorint
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 failsTraceback (most recent call last): File "<stdin>", line 1, in <module>OverflowError: Arithmetic operation resulted in an overflow.
Such explicit conversions are in most cases unnecessary since the runtime recognizesint
/Int32
equivalence of instances and performs necessary conversions automatically.
>>>import System>>> int_list= System.Collections.Generic.List[int]()>>> int_list.Add(1)# Int32 instance converted to BigInteger>>> int32_list= System.Collections.Generic.List[System.Int32]()>>> int32_list.Add((1).ToBigInteger())# BigInteger instance converted to Int32>>> int_list[0]== int32_list[0]True
When anint
object is serialized usingpickle.dump(x, myfile)
and subsequently unpickled withx = pickle.load(myfile)
(orpickle.loads(pickle.dumps(x))
, this has the same effect as reconstructing the object using theint
constructor, i.e.x = int(x)
. In other words, if thex
instance wasBigInteger
but the value fits inInt32
, it will be reconstructed asInt32
.
In IronPython 2,long
type carries an obsoleteBigIntegerV2
API, accessible after importingSystem
. In IronPython 3 this API is not available directly onint
instances (regardless of whether the instance isInt32
orBigInteger
), but is still accessible in some form throughMicrosoft.Scripting.Utils.MathUtils
inMicrosoft.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 "<stdin>", line 1, in <module>AttributeError: 'int' object has no attribute 'GetWords'>>>long.GetWords(i)Array[UInt32]((1))
>>># 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 "<stdin>", line 1, in <module>AttributeError: 'int' object has no attribute 'GetWords'>>> Microsoft.Scripting.Utils.MathUtils.GetWords(i)Array[UInt32]((1))
Another set of Python-hidden methods onlong
in IronPython 2 that are not available onint
in IronPython 3 are conversion methods with names likeToXxx
. The recommended way to perform type conversions like those is to use type constructors. The exception is the conversion toBigInteger
itself, for the reasons explained above.
# IronPython 2j=long(1)i64=j.ToInt64()
# IronPython 3importSystemj= (1).ToBigInteger()i64=System.Int64(j)
IronPython'srange
is a generator that produces a sequence ofint
values. The values are instances ofInt32
orBigInteger
, depending on the actual integer value they represent. Whenrange
is used in a LINQ context, it exposes interfaceIEnumerable<Int32>
and all values generated are of typeInt32
. This limits the possible value to the rangeInt32.MinValue
toInt32.MaxValue
.
Still looking for more? Browse theDiscussions tab, where you can ask questions to the IronPython community.
🐍IronPython