Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update notes on upgrading from ipy2 to cover PEP 237 #1423

Merged
merged 3 commits into from
Apr 27, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions Documentation/upgrading-from-ipy2.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,19 @@ False
'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). For example, `System.Collections.Generic.List[int]` in IronPython 3 is equivalent to `System.Collections.Generic.List[System.Numerics.BigInteger]`. To retain IronPython 2 semantics, replace `int` with `System.Int32`.
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:

```python
# IronPython 2
System.Collections.Generic.List[int]
```

```python
# IronPython 3
System.Collections.Generic.List[System.Int32]
```

Overview of `int` type equivalency:

Expand All @@ -52,51 +64,51 @@ 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 for the presence of `MaxValue` or `MinValue`. For those properties to be visible, `System` has to be imported first.
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.)

```pycon
>>> import System
>>> type(i)
<class 'int'>
>>> hasattr(i, 'MaxValue') # Int32
>>> isinstance(i, System.Int32)
True
>>> hex(i.MaxValue)
'0x7fffffff'
>>> type(j)
<class 'int'>
>>> hasattr(j, 'MaxValue') # BigInteger
>>> 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.
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.

```pycon
>>> bi = i.ToBigInteger()
>>> hasattr(bi, 'MaxValue')
>>> isinstance(j, System.Int32)
False
```

In the opposite direction, if it is essential to create `Int32` objects, either use constructors for `int` or `Int32`. 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 is the conversion is not possible.
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 is 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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The declaration about the constructor always producing the canonical form is debatable. Although a documented and reliable way of producing a canonical representation is necessary, it doesn't have to be the constructor. Looking at the example of Pyhon Decimal, it uses three operations to produce some form of a normalized value:

  • __pos__(), +x (on instance), plus(x) (on context) — "This operation applies the context precision and rounding, so it is not an identity operation."
  • normalize() — "Used for producing canonical values for attributes of an equivalence class."
  • canonical() — "Return the canonical encoding of the argument." It also comes with test is_canonical().

According tho the documentation, current implementation of Decimal keeps all instances canonical at all times anyway, although it does not explain what that means. To make the matter more confusing:

Q. There are many ways to express the same value. The numbers 200, 200.000, 2E2, and 02E+4 all have the same value at various precisions. Is there a way to transform them to a single recognizable canonical value?

A. The normalize() method maps all equivalent values to a single representative:

Perhaps canonical() refers only to some internal representation. If so, this is exactly what we are looking for in the case of our int.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine (in particular since it's quoted). If someone has an issue with the wording they can file an issue! 😄


```pycon
>>> # k is a BigInteger that fits in 32 bits
>>> hasattr(k, 'MaxValue')
>>> isinstance(j, System.Int32)
False
>>> hex(k)
'0x7fffffff'
>>> ki = int(k) # converts k to Int32
>>> hasattr(ki, 'MaxValue')
>>> isinstance(ki, System.Int32)
True
>>> ki = System.Int32(k) # also converts k to Int32
>>> hasattr(ki, 'MaxValue')
>>> isinstance(ki, System.Int32)
True
>>> # j is a BigInteger that does not fit in 32 bits
>>> hasattr(j, 'MaxValue')
>>> isinstance(j, System.Int32)
False
>>> hex(j)
'0x80000000'
>>> j = int(j) # no type change, j stays BigInteger
>>> hasattr(j, 'MaxValue')
>>> isinstance(j, System.Int32)
False
>>> j = System.Int32(j) # conversion fails
Traceback (most recent call last):
Expand Down Expand Up @@ -157,7 +169,7 @@ AttributeError: 'int' object has no attribute 'GetWords'
Array[UInt32]((1))
```

Another set of Python-hidden methods on `long` in IronPython 2 that 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.
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.

```python
# IronPython 2
Expand Down