Skip to content

Commit 61c121d

Browse files
committed
Merge branch main into refactor (finally!!)
2 parents f72cdc3 + a687be1 commit 61c121d

File tree

118 files changed

+4246
-1668
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+4246
-1668
lines changed

.github/workflows/main.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,15 @@ jobs:
1010
strategy:
1111
fail-fast: false
1212
matrix:
13-
os: [windows-latest, ubuntu-latest, macos-latest-large]
13+
os: [windows-latest, ubuntu-22.04, macos-latest-large]
1414

1515
steps:
1616
- name: Install tools
17-
if: matrix.os == 'ubuntu-latest'
17+
if: matrix.os == 'ubuntu-22.04'
1818
run: sudo apt-get -yq install mono-vbnc dos2unix
1919
- uses: actions/checkout@v4
2020
with:
2121
submodules: true
22-
- name: Setup .NET Core 3.1
23-
uses: actions/setup-dotnet@v4
24-
with:
25-
dotnet-version: '3.1.x'
2622
- name: Setup .NET 6.0
2723
uses: actions/setup-dotnet@v4
2824
with:
@@ -31,6 +27,10 @@ jobs:
3127
uses: actions/setup-dotnet@v4
3228
with:
3329
dotnet-version: '8.0.x'
30+
- name: Setup .NET 9.0
31+
uses: actions/setup-dotnet@v4
32+
with:
33+
dotnet-version: '9.0.x'
3434
- name: Version Information
3535
run: |
3636
dotnet --info
@@ -48,9 +48,6 @@ jobs:
4848
- name: Test (net462)
4949
run: ./make.ps1 -frameworks net462 test-all
5050
shell: pwsh
51-
- name: Test (netcoreapp3.1)
52-
run: ./make.ps1 -frameworks netcoreapp3.1 test-all
53-
shell: pwsh
5451
- name: Test (net6.0)
5552
run: ./make.ps1 -frameworks net6.0 test-all
5653
shell: pwsh

CurrentVersion.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<MajorVersion>3</MajorVersion>
55
<MinorVersion>4</MinorVersion>
6-
<MicroVersion>1</MicroVersion>
6+
<MicroVersion>2</MicroVersion>
77
<ReleaseLevel>final</ReleaseLevel>
88
<ReleaseSerial>0</ReleaseSerial><!-- this should only be 0 when final -->
99

IronPython.sln

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.9.34714.143
@@ -38,7 +38,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{17737ACB-40C
3838
eng\net8.0.props = eng\net8.0.props
3939
eng\net9.0-windows.props = eng\net9.0-windows.props
4040
eng\net9.0.props = eng\net9.0.props
41-
eng\netcoreapp3.1.props = eng\netcoreapp3.1.props
4241
eng\netstandard2.0.props = eng\netstandard2.0.props
4342
eng\steps.yml = eng\steps.yml
4443
eng\Tasks.Targets = eng\Tasks.Targets

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,4 @@ Binaries of IronPython 3 can be downloaded from the [release page](https://githu
8686
See the [building document](docs/building.md). Since the main development is on Windows, bugs on other platforms may inadvertently be introduced - please report them!
8787

8888
## Supported Platforms
89-
IronPython 3 targets .NET Framework 4.6.2, .NET Standard 2.0 and .NET 6.0. The support for .NET and .NET Core follow the lifecycle defined on [.NET and .NET Core Support Policy](https://dotnet.microsoft.com/platform/support/policy/dotnet-core).
89+
IronPython 3 targets .NET Framework 4.6.2, .NET Standard 2.0, .NET 6.0 and .NET 8.0. The support for .NET and .NET Core follow the lifecycle defined on [.NET and .NET Core Support Policy](https://dotnet.microsoft.com/platform/support/policy/dotnet-core).

WhatsNewInPython34.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ https://docs.python.org/3/whatsnew/3.4.html
44

55
PEPs
66
====
7-
- [ ] [PEP 453][]: Explicit Bootstrapping of PIP in Python Installations
7+
- [x] [PEP 453][]: Explicit Bootstrapping of PIP in Python Installations
88
- [ ] [PEP 446][]: Newly created file descriptors are non-inheritable
99
- [ ] [PEP 451][]: A `ModuleSpec` Type for the Import System
1010

@@ -21,7 +21,7 @@ Other Language Changes
2121
- [ ] All the UTF-* codecs (except UTF-7) now reject surrogates during both encoding and decoding unless the surrogatepass error handler is used, with the exception of the UTF-16 decoder and the UTF-16 encoder
2222
- [ ] New German EBCDIC codec `cp273`.
2323
- [ ] New Ukrainian codec `cp1125`.
24-
- [ ] `bytes.join()` and `bytearray.join()` now accept arbitrary buffer objects as arguments.
24+
- [x] `bytes.join()` and `bytearray.join()` now accept arbitrary buffer objects as arguments.
2525
- [ ] The `int` constructor now accepts any object that has an `__index__` method for its base argument.
2626
- [ ] Frame objects now have a `clear()` method that clears all references to local variables from the frame.
2727
- [ ] `memoryview` is now registered as a `Sequence`, and supports the `reversed()` builtin.

docs/file-descriptors.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# File Descriptors in IronPython
2+
3+
## Windows
4+
5+
The conceptual picture of file descriptors (FDs) usage on Windows, for the most interesting case of `FileStream`:
6+
7+
```mermaid
8+
graph LR;
9+
10+
FileIO --> StreamBox --> FileStream --> Handle(Handle) --> OSFile[OS File];
11+
FD(FD) <--> StreamBox;
12+
```
13+
14+
Conceptually, the relationship between `FD` (a number) and `StreamBox` (a class) is bidirectional because `PythonFileManager` (global singleton) maintains the association between the two so it is cost-free to obtaining the one having the other. FD is not the same as the handle, which is created by the OS. FD is an emulated (fake) file descriptor, assigned by the `PythonFileManager`, for the purpose of supporting the Python API. The descriptors are allocated lazily, i.e. only if the user code makes an API call that accesses it. Once assigned, the descriptor does not change. The FD number is released once the FD is closed (or the associated `FileIO` is closed and had `closefd` set to true.)
15+
16+
It is possible to have the structure above without `FileIO`; for instance when an OS file is opened with one of the low-level functions in `os`, or when an existing FD is duplicated. It is also possible to associate an FD with several `FileIO`. In such cases it is the responsibility of the user code to take care that the FD is closed at the right time.
17+
18+
When FD is duplicated (using `dup` or `dup2`), the associated `StreamBox` is duplicated (there is always a 1-to-1 relationship between FD and `StreamBox`), but the underlying `FileStream` objects remain the same, and so are the underlying OS handles. The new FD may be used to create a `FileIO` (or several, just as the original FD). All read/seek/write operations on both descriptors go though the same `FileStream` object and the same OS handle.
19+
20+
```mermaid
21+
graph LR;
22+
23+
FD1(FD1) <--> StreamBox --> FileStream --> Handle(Handle) --> OSFile[OS File];
24+
FD2(FD2) <--> StreamBox2[StreamBox] --> FileStream;
25+
```
26+
27+
The descriptors can be closed independently, and the underlying `FileStream` is closed when the last `StreamBox` using it is closed.
28+
29+
## Posix
30+
31+
On Unix-like systems (Linux, maxOS), `FileStream` uses the actual file descriptor as the handle. In the past. IronPython was ignoring this and still issuing its own fake file descriptors as it is in the case of Windows. Now, however, the genuine FD is extracted from the handle and used as FD at the `PythonFileManager` level, ensuring that clients of Python API obtain the genuine FD.
32+
33+
```mermaid
34+
graph LR;
35+
36+
FileIO --> StreamBox --> FileStream --> FDH(FD) --> OSFile[OS File];
37+
FD(FD) <--> StreamBox;
38+
```
39+
40+
When descriptor FD is duplicated, the actual OS call is made to create the duplicate FD2. In order to use FD2 directly, a new `Stream` object has to be created around it.
41+
42+
### Optimal Mechanism
43+
44+
The optimal solution is to create another `FileStream` using the constructor that accepts an already opened file descriptor.
45+
46+
```mermaid
47+
graph LR;
48+
49+
FD1(FD1) <--> StreamBox --> FileStream --> FDH1(FD1) --> OSFile[OS File];
50+
FD2(FD2) <--> StreamBox2[StreamBox] --> FileStream2[FileStream] --> FDH2(FD2) --> OSFile;
51+
```
52+
53+
In this way, the file descriptor on the `PythonFileManager` level is the same as the file descriptor used by `FileStream`.
54+
55+
Unfortunately, on .NET, somehow, two `FileStream` instances using the same file descriptor will have the two independent read/write positions. This is not how duplicated file descriptors should work: both descriptors should point to the same file description structure and share the read/seek/write position. In practice, on .NET, writing through the second file object will overwrite data already written through the first file object. In regular Unix applications (incl. CPython), the subsequent writes append data, regardless which file object is used. The same principle should apply to reads.
56+
57+
Also unfortunately, on Mono, the `FileStream` constructor accepts only descriptors opened by another call to a `FileStream` constructor[[1]]. So descriptors obtained from direct OS calls, like `open`, `creat`, `dup`, `dup2` are being rejected.
58+
59+
### Mono Workaround
60+
61+
To use system-opened file descriptors on Mono `UnixStream` can be used instead of `FileStream`.
62+
63+
```mermaid
64+
graph LR;
65+
66+
FD1(FD1) <--> StreamBox --> FileStream --> FDH1(FD1) --> OSFile[OS File];
67+
FD2(FD2) <--> StreamBox2[StreamBox] --> UnixStream --> FDH2(FD2) --> OSFile;
68+
```
69+
70+
Since FileIO works with various types of the underlying `Stream`, using `UnixStream` should be OK.
71+
72+
Although `UnixStream` is available in .NET through package `Mono.Posix`, this solution still does not work around desynchronized read/write position, which `FileStream` using the original FD1 must somehow maintain independently.
73+
74+
### .NET Workaround
75+
76+
To ensure proper R/W behavior on .NET, operations on both file descriptions have to go though the same `FileStream` object. Since the duplicated file descriptor is basically just a number, pointing to the same file description as the original descriptor, on the OS level it doesn't matter which descriptor is used for operations. The only difference between those descriptors is flag `O_CLOEXEC`, which determines whether the descriptor stays open or not when child processed are executed.
77+
78+
```mermaid
79+
graph LR;
80+
81+
FD1(FD1) <--> StreamBox --> FileStream --> FDH1(FD1) --> OSFile[OS File];
82+
FD2(FD2) <--> StreamBox2[StreamBox] --> FileStream;
83+
FDH2(FD2) --> OSFile;
84+
```
85+
86+
This actually works OK, until `dup2` is used. When the FD1 descriptor (or the associated `FileIO`) is closed on the Python API level, the underlying OS descriptor is not released but still being used by `FileStream`. A small side effect is that it will not be reused until FD2 is closed, but other than that, the behaviour is as expected.
87+
88+
```mermaid
89+
graph LR;
90+
91+
FileStream --> FDH1(FD1) --> OSFile[OS File];
92+
FD2(FD2) <--> StreamBox2[StreamBox] --> FileStream;
93+
FDH2(FD2) --> OSFile;
94+
```
95+
96+
The problem arises when `dup2` is used with the target being FD1. This will forcibly close the descriptor used by `FileStream`, rendering the stream broken, despite having FD2 available. Perhaps closing `FileStream` using FD1 and opening a replacement around FD2 could be a solution, but this would have to be done atomically. If so, this would lead to a healthy structure.
97+
98+
```mermaid
99+
graph LR;
100+
101+
FileStream --> FDH2(FD2);
102+
FD2(FD2) <--> StreamBox2[StreamBox] --> FileStream;
103+
FDH2(FD2) --> OSFile;
104+
```
105+
106+
107+
## Practical Scenarios
108+
109+
None of the above solutions is fully satisfactory for .NET. Ideally, .NET would behave consistently with Posix, because even the most elaborate workarounds (like juggling various `FileStream` objects around the descriptors) only work within IronPython, and break down when a descriptor is passed to a 3rd party library that uses C extension and creates its own `FILE*` struct around it. The `FileStream` object in .NET knows nothing about it and will not adjust its R/W position.
110+
111+
In the meantime, let's look at some practical cases when `dup`/`dup2` are used and try to support just these. For what I have seen, `dup`/`dup2` are commonly used to redirect some of the standard descriptors. For example, to redirect standard output to a file:
112+
1. Open a file for writing, it will get assigned descriptor FD1.
113+
2. Copy descriptor 1 aside using `dup`. The copy will get assigned descriptor FD2.
114+
3. Copy the open file descriptor FD1 onto descriptor 1 using `dup2`. This will forcibly close the existing descriptor 1, but not the output stream, which is sill accessible through descriptor FD2.
115+
4. Code writing to "standard output", i.e. descriptor 1, will now write to the open file.
116+
5. If needed, the application can still write to the original output stream by writing to descriptor FD2.
117+
6. When done, close descriptor FD1.
118+
7. Copy descriptor FD2 onto descriptor 1 using `dup2`. Since the is the last one pointing to the open file, the file will be closed as well.
119+
8. Close descriptor FD2, the copy is not needed anymore.
120+
121+
The same scenario is commonly done for standard input and sometimes standard error.
122+
123+
The problem of .NET manifests itself when there are two descriptors open that refer to the same open file description and used concurrently. In the above scenario it is descriptor 1 and FD1. Assuming that the application is not using FD1 (typical use), the _Optimal Mechanism_ described above is sufficient.
124+
125+
If the application does insist on using both descriptors 1 and FD1, the first .NET workaround is needed. This will lead to the following structure:
126+
127+
```mermaid
128+
graph LR;
129+
130+
FD1(FD1) <--> StreamBox --> FileStream --> FDH1(FD1) --> OSFile[OS File];
131+
D1(1) <--> StreamBox2[StreamBox] --> FileStream;
132+
DH1(1) --> OSFile;
133+
FD2(FD2) <--> StreamBox3[StreamBox] --> FileStream2[FileStream] --> FDH2(FD2) --> stdout
134+
```
135+
136+
The problem of closing FD1 and then overwriting it is not an issue, since only standard descriptors (0, 1, 2) are being overwritten with `dup2`. There is still a problem of overwriting data written by C extension code writing though descriptor 1. Perhaps replacing `FileStream` utilizing FD1 with `UnixStream` from Mono would make it more cooperative.
137+
138+
In the end, the implementation of genuine file descriptors in IronPython starts with the simple solution (the simple workarounds described above) and will be adjusted as needed to support the 3rd party Python packages.
139+
140+
## Special Case: Double Stream
141+
142+
In Python, a file can be opened with mode "ab+". The file is opened for appending to the end (created if not exists), and the `+` means that it is also opened for updating. i.e. reading and writing. The file pointer is initially set at the end of the file (ready to write to append) but can be moved around to read already existing data. However, each write will append data to the end and reset the read/write pointer at the end again. In IronPython this is simulated by using two file streams, one for reading and one fore writing. Both are maintained in a single `StreamBox` but will have different file descriptors. This is subject to change.
143+
144+
[1]: https://github.com/mono/mono/issues/12783

eng/dotnettool/IronPython.Console.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Project Sdk="Microsoft.Build.NoTargets/3.3.0">
33

44
<PropertyGroup>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<OutputType>Exe</OutputType>
77
<AssemblyName>ipy</AssemblyName>
88
<UseAppHost>false</UseAppHost>
@@ -40,7 +40,7 @@ This package contains a standalone Python interpreter, invokable from the comman
4040
<ItemGroup>
4141
<PackageReference Include="Mono.Unix" Version="7.1.0-final.1.21458.1" />
4242
<!-- must match PackageReferences for the DLR -->
43-
<PackageReference Include="System.CodeDom" Version="6.0.0" />
43+
<PackageReference Include="System.CodeDom" Version="8.0.0" />
4444
</ItemGroup>
4545

4646
<ItemGroup>

eng/dotnettool/runtimeconfig.template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"framework": {
33
"name": "Microsoft.NETCore.App",
4-
"version": "6.0.0"
4+
"version": "8.0.0"
55
},
66
"configProperties": {
77
"Microsoft.NETCore.DotNetHostPolicy.SetAppPaths": true

eng/netcoreapp3.1.props

Lines changed: 0 additions & 38 deletions
This file was deleted.

eng/nuget/IronPython.nuspec

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,26 @@ This package contains the IronPython interpreter engine.</description>
1919
<tags>ironpython python dynamic dlr</tags>
2020
<dependencies>
2121
<group targetFramework="net462">
22-
<dependency id="DynamicLanguageRuntime" version="1.3.4" />
22+
<dependency id="DynamicLanguageRuntime" version="1.3.5" />
2323
<dependency id="System.Memory" version="4.5.5" />
2424
</group>
2525
<group targetFramework="netstandard2.0">
26-
<dependency id="DynamicLanguageRuntime" version="1.3.4" />
26+
<dependency id="DynamicLanguageRuntime" version="1.3.5" />
2727
<dependency id="System.Memory" version="4.5.5" />
2828
<dependency id="System.Text.Encoding.CodePages" version="4.7.0" />
2929
<dependency id="Microsoft.Win32.Registry" version="4.7.0" />
3030
<dependency id="Mono.Unix" version="7.1.0-final.1.21458.1" />
3131
</group>
32-
<group targetFramework="net6.0">
33-
<dependency id="DynamicLanguageRuntime" version="1.3.4" />
34-
<dependency id="Mono.Unix" version="7.1.0-final.1.21458.1" />
35-
</group>
3632
<group targetFramework="net8.0">
37-
<dependency id="DynamicLanguageRuntime" version="1.3.4" />
33+
<dependency id="DynamicLanguageRuntime" version="1.3.5" />
3834
<dependency id="Mono.Unix" version="7.1.0-final.1.21458.1" />
3935
</group>
4036
</dependencies>
4137
</metadata>
4238
<files>
43-
<file src="**\IronPython*.dll" target="lib" exclude="**\IronPythonTest.dll;**\DLLs\*.dll;netcoreapp3.1\**\*;net9.0*\**\*" />
44-
<file src="**\IronPython*.pdb" target="lib" exclude="**\IronPythonTest.pdb;**\DLLs\*.pdb;netcoreapp3.1\**\*;net9.0*\**\*" />
45-
<file src="**\IronPython*.xml" target="lib" exclude="**\IronPythonTest.xml;**\DLLs\*.xml;netcoreapp3.1\**\*;net9.0*\**\*" />
39+
<file src="**\IronPython*.dll" target="lib" exclude="**\IronPythonTest.dll;**\DLLs\*.dll;net6.0\**\*;net9.0*\**\*" />
40+
<file src="**\IronPython*.pdb" target="lib" exclude="**\IronPythonTest.pdb;**\DLLs\*.pdb;net6.0\**\*;net9.0*\**\*" />
41+
<file src="**\IronPython*.xml" target="lib" exclude="**\IronPythonTest.xml;**\DLLs\*.xml;net6.0\**\*;net9.0*\**\*" />
4642
<file src="LICENSE" />
4743
<file src="..\..\..\nuget\README.md" target="" />
4844
<file src="..\..\..\..\docs\logo.png" target="" />

0 commit comments

Comments
 (0)