Skip to content

Commit b2486ab

Browse files
authored
Enable test_file (#1039)
* Enable test_file * Fix test on posix * More cleanup * Fix test_fd * Set windows eol * IOException to OSError * Don't map EndOfStreamException to IOError * Fix test failures * Fix test * Cleanup * fix typo
1 parent 7840403 commit b2486ab

13 files changed

Lines changed: 301 additions & 385 deletions

File tree

.gitattributes

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
*.html text
1414
*.ps1 text
1515

16+
# Explicitly declare files that should not have line endings modified, ever
17+
Tests/file_with_BOM.txt -text
18+
Tests/file_without_BOM.txt -text
19+
1620
# Declare files that will always have CRLF line endings on checkout.
1721
*.bat text eol=crlf
1822
*.sln text eol=crlf
@@ -23,7 +27,6 @@
2327
*.vcxproj text eol=crlf
2428
*.vdproj text eol=crlf
2529

26-
2730
# Declare files that will always have LF line endings on checkout
2831
.gitmodules text eol=lf
2932
*.sh text eol=lf

Src/IronPython.Modules/nt.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,9 +1571,9 @@ public static int WSTOPSIG(int status) {
15711571

15721572
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke")]
15731573
private static Exception ToPythonException(Exception e, string filename = null) {
1574-
if (e is IPythonAwareException) {
1574+
// already a Python Exception
1575+
if (e.GetPythonException() is not null)
15751576
return e;
1576-
}
15771577

15781578
if (e is ArgumentException || e is ArgumentNullException || e is ArgumentTypeException) {
15791579
// rethrow reasonable exceptions

Src/IronPython/Modules/_fileio.cs

Lines changed: 104 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,10 @@ public FileIO(CodeContext/*!*/ context, int fd, string mode = "r", bool closefd
7171
throw PythonOps.ValueError("fd must be >= 0");
7272
}
7373

74-
PythonContext pc = context.LanguageContext;
75-
76-
_context = pc;
77-
switch (StandardizeMode(mode)) {
78-
case "r": this.mode = "rb"; break;
79-
case "w": this.mode = "wb"; break;
80-
case "a": this.mode = "w"; break;
81-
case "r+":
82-
case "+r": this.mode = "rb+"; break;
83-
case "w+":
84-
case "+w": this.mode = "rb+"; break;
85-
case "a+":
86-
case "+a": this.mode = "r+"; break;
87-
default:
88-
BadMode(mode);
89-
break;
90-
}
74+
_context = context.LanguageContext;
75+
this.mode = NormalizeMode(mode, out _);
9176

92-
object fileObject = pc.FileManager.GetObjectFromId(fd); // OSError here if no such fd
77+
object fileObject = _context.FileManager.GetObjectFromId(fd); // OSError here if no such fd
9378

9479
// FileManager interface guarantees fileObject is
9580
// one of these two types
@@ -117,55 +102,38 @@ public FileIO(CodeContext/*!*/ context, string name, string mode = "r", bool clo
117102
this.name = name;
118103
PlatformAdaptationLayer pal = context.LanguageContext.DomainManager.Platform;
119104

120-
int flags = 0;
121-
switch (StandardizeMode(mode)) {
122-
case "r":
123-
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
124-
this.mode = "rb";
125-
flags |= O_RDONLY;
126-
break;
127-
case "w":
128-
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
129-
this.mode = "wb";
130-
flags |= O_CREAT | O_TRUNC | O_WRONLY;
131-
break;
132-
case "a":
133-
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
134-
_readStream.Seek(0L, SeekOrigin.End);
135-
this.mode = "ab";
136-
flags |= O_APPEND | O_CREAT;
137-
break;
138-
case "r+":
139-
case "+r":
140-
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
141-
this.mode = "rb+";
142-
flags |= O_RDWR;
143-
break;
144-
case "w+":
145-
case "+w":
146-
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
147-
this.mode = "rb+";
148-
flags |= O_CREAT | O_TRUNC | O_RDWR;
149-
break;
150-
case "a+":
151-
case "+a":
152-
_readStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
153-
_writeStream = OpenFile(context, pal, name, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
154-
_readStream.Seek(0L, SeekOrigin.End);
155-
_writeStream.Seek(0L, SeekOrigin.End);
156-
this.mode = "ab+";
157-
flags |= O_APPEND | O_CREAT | O_RDWR;
158-
break;
159-
default:
160-
BadMode(mode);
161-
break;
162-
}
163-
164-
_closefd = true;
165-
166105
_context = context.LanguageContext;
167-
168-
if (opener != null) {
106+
this.mode = NormalizeMode(mode, out int flags);
107+
108+
if (opener is null) {
109+
switch (this.mode) {
110+
case "rb":
111+
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
112+
break;
113+
case "wb":
114+
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
115+
break;
116+
case "ab":
117+
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
118+
_readStream.Seek(0L, SeekOrigin.End);
119+
break;
120+
case "rb+":
121+
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
122+
break;
123+
case "wb+":
124+
_readStream = _writeStream = OpenFile(context, pal, name, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
125+
break;
126+
case "ab+":
127+
_readStream = OpenFile(context, pal, name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
128+
_writeStream = OpenFile(context, pal, name, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
129+
_readStream.Seek(0L, SeekOrigin.End);
130+
_writeStream.Seek(0L, SeekOrigin.End);
131+
break;
132+
default:
133+
throw new InvalidOperationException();
134+
}
135+
}
136+
else {
169137
object fdobj = PythonOps.CallWithContext(context, opener, name, flags);
170138
if (fdobj is int fd) {
171139
if (fd < 0) {
@@ -183,58 +151,86 @@ public FileIO(CodeContext/*!*/ context, string name, string mode = "r", bool clo
183151
throw PythonOps.TypeError("expected integer from opener");
184152
}
185153
}
154+
155+
_closefd = true;
186156
}
187157

188-
/// <summary>
189-
/// Remove all 'b's from mode string to simplify parsing
190-
/// </summary>
191-
private static string StandardizeMode(string mode) {
192-
int index = mode.IndexOf('b');
158+
private static string NormalizeMode(string mode, out int flags) {
159+
switch (StandardizeMode(mode)) {
160+
case "r":
161+
flags = O_RDONLY;
162+
return "rb";
163+
case "w":
164+
flags = O_CREAT | O_TRUNC | O_WRONLY;
165+
return "wb";
166+
case "a":
167+
flags = O_APPEND | O_CREAT;
168+
return "ab";
169+
case "r+":
170+
case "+r":
171+
flags = O_RDWR;
172+
return "rb+";
173+
case "w+":
174+
case "+w":
175+
flags = O_CREAT | O_TRUNC | O_RDWR;
176+
return "wb+";
177+
case "a+":
178+
case "+a":
179+
flags = O_APPEND | O_CREAT | O_RDWR;
180+
return "ab+";
181+
default:
182+
throw BadMode(mode);
183+
}
193184

194-
if (index == mode.Length - 1) {
195-
mode = mode.Substring(0, index);
196-
} else if (index >= 0) {
197-
StringBuilder sb = new StringBuilder(mode.Substring(0, index), mode.Length - 1);
198-
for (int pos = index + 1; pos < mode.Length; pos++) {
199-
if (mode[pos] != 'b') {
200-
sb.Append(mode[pos]);
185+
// remove all 'b's from mode string to simplify parsing
186+
static string StandardizeMode(string mode) {
187+
int index = mode.IndexOf('b');
188+
189+
if (index == mode.Length - 1) {
190+
mode = mode.Substring(0, index);
191+
} else if (index >= 0) {
192+
StringBuilder sb = new StringBuilder(mode.Substring(0, index), mode.Length - 1);
193+
for (int pos = index + 1; pos < mode.Length; pos++) {
194+
if (mode[pos] != 'b') {
195+
sb.Append(mode[pos]);
196+
}
201197
}
198+
mode = sb.ToString();
202199
}
203-
mode = sb.ToString();
204-
}
205200

206-
return mode;
207-
}
201+
return mode;
202+
}
208203

209-
private static void BadMode(string mode) {
210-
bool foundMode = false, foundPlus = false;
211-
foreach (char c in mode) {
212-
switch (c) {
213-
case 'r':
214-
case 'w':
215-
case 'a':
216-
if (foundMode) {
217-
throw PythonOps.ValueError("Must have exactly one of read/write/append mode");
218-
} else {
219-
foundMode = true;
204+
static Exception BadMode(string mode) {
205+
bool foundMode = false, foundPlus = false;
206+
foreach (char c in mode) {
207+
switch (c) {
208+
case 'r':
209+
case 'w':
210+
case 'a':
211+
if (foundMode) {
212+
return PythonOps.ValueError("Must have exactly one of read/write/append mode");
213+
} else {
214+
foundMode = true;
215+
continue;
216+
}
217+
case '+':
218+
if (foundPlus) {
219+
return PythonOps.ValueError("Must have exactly one of read/write/append mode");
220+
} else {
221+
foundPlus = true;
222+
continue;
223+
}
224+
case 'b':
225+
// any number of 'b's is acceptable
220226
continue;
221-
}
222-
case '+':
223-
if (foundPlus) {
224-
throw PythonOps.ValueError("Must have exactly one of read/write/append mode");
225-
} else {
226-
foundPlus = true;
227-
continue;
228-
}
229-
case 'b':
230-
// any number of 'b's is acceptable
231-
continue;
232-
default:
233-
throw PythonOps.ValueError("invalid mode: {0}", mode);
227+
default:
228+
return PythonOps.ValueError("invalid mode: {0}", mode);
229+
}
234230
}
235-
}
236231

237-
throw PythonOps.ValueError("Must have exactly one of read/write/append mode");
232+
return PythonOps.ValueError("Must have exactly one of read/write/append mode");
233+
}
238234
}
239235

240236
#endregion

Src/IronPython/Modules/_io.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public void _checkReadable() {
9292

9393
public void _checkReadable(string msg) {
9494
if (!readable(context)) {
95-
throw PythonOps.ValueError(msg ?? "File or stream is not readable.");
95+
throw UnsupportedOperationWithMessage(context, msg ?? "File or stream is not readable.");
9696
}
9797
}
9898

@@ -112,7 +112,7 @@ public void _checkWritable() {
112112

113113
public void _checkWritable(string msg) {
114114
if (!writable(context)) {
115-
throw PythonOps.ValueError(msg ?? "File or stream is not writable.");
115+
throw UnsupportedOperationWithMessage(context, msg ?? "File or stream is not writable.");
116116
}
117117
}
118118

@@ -397,6 +397,9 @@ internal Exception UnsupportedOperation(CodeContext/*!*/ context, string attr) {
397397
);
398398
}
399399

400+
internal Exception UnsupportedOperationWithMessage(CodeContext/*!*/ context, string msg)
401+
=> PythonExceptions.CreateThrowable((PythonType)context.LanguageContext.GetModuleState(_unsupportedOperationKey), msg);
402+
400403
internal Exception AttributeError(string attrName) {
401404
throw PythonOps.AttributeError("'{0}' object has no attribute '{1}'", PythonTypeOps.GetName(this), attrName);
402405
}
@@ -2452,7 +2455,7 @@ public override BigInteger seek(CodeContext/*!*/ context, BigInteger cookie, [Op
24522455
public override object read(CodeContext/*!*/ context, object length=null) {
24532456
_checkClosed();
24542457
if (!readable(context)) {
2455-
throw PythonOps.IOError("not readable");
2458+
throw UnsupportedOperationWithMessage(context, "not readable");
24562459
}
24572460

24582461
int size = GetInt(length, -1);
@@ -2497,6 +2500,9 @@ public override object read(CodeContext/*!*/ context, object length=null) {
24972500

24982501
public override object readline(CodeContext/*!*/ context, int limit=-1) {
24992502
_checkClosed("read from closed file");
2503+
if (!readable(context)) {
2504+
throw UnsupportedOperationWithMessage(context, "not readable");
2505+
}
25002506

25012507
string line = GetDecodedChars();
25022508

@@ -2635,13 +2641,19 @@ IEnumerator IEnumerable.GetEnumerator() {
26352641

26362642
#region ICodeFormattable Members
26372643

2644+
#nullable enable
26382645
public string __repr__(CodeContext/*!*/ context) {
2639-
if (PythonOps.TryGetBoundAttr(buffer, "name", out object nameObj)) {
2640-
return $"<_io.TextIOWrapper name={PythonOps.Repr(context, nameObj)} encoding='{_encoding}'>";
2641-
}
2646+
string name = string.Empty;
2647+
if (PythonOps.TryGetBoundAttr(buffer, "name", out var nameObj))
2648+
name = $" name={PythonOps.Repr(context, nameObj)}";
2649+
2650+
string mode = string.Empty;
2651+
if (PythonOps.TryGetBoundAttr(this, "mode", out var modeObj))
2652+
mode = $" mode={PythonOps.Repr(context, modeObj)}";
26422653

2643-
return $"<_io.TextIOWrapper encoding='{_encoding}'>";
2654+
return $"<_io.TextIOWrapper{name}{mode} encoding='{_encoding}'>";
26442655
}
2656+
#nullable restore
26452657

26462658
#endregion
26472659

@@ -2806,7 +2818,7 @@ public static _IOBase open(
28062818

28072819
HashSet<char> modes = MakeSet(mode);
28082820
if (modes.Count < mode.Length || !_validModes.IsSupersetOf(modes)) {
2809-
throw PythonOps.ValueError("invalid mode: {0}", mode);
2821+
throw PythonOps.ValueError("invalid mode: '{0}'", mode);
28102822
}
28112823

28122824
bool reading = modes.Contains('r');

Src/IronPython/Runtime/Exceptions/PythonExceptions.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -781,22 +781,22 @@ internal static object ToPython(System.Exception/*!*/ clrException) {
781781
/// Creates a new style Python exception from the .NET exception
782782
/// </summary>
783783
private static BaseException/*!*/ ToPythonNewStyle(System.Exception/*!*/ clrException) {
784+
// EndOfStreamException is not part of the OSError hierarchy
785+
if (clrException is Win32Exception || clrException is IOException && clrException is not EndOfStreamException) {
786+
int errorCode = clrException.HResult;
787+
788+
if ((errorCode & ~0xfff) == unchecked((int)0x80070000)) {
789+
errorCode &= 0xfff;
790+
}
791+
792+
// TODO: can we get filename and such?
793+
return CreatePythonThrowable(OSError, errorCode, clrException.Message, null, errorCode);
794+
}
795+
784796
BaseException pyExcep;
785797
if (clrException is InvalidCastException || clrException is ArgumentNullException) {
786798
// explicit extra conversions outside the generated hierarchy
787799
pyExcep = new BaseException(TypeError);
788-
} else if (clrException is Win32Exception) {
789-
Win32Exception win32 = (Win32Exception)clrException;
790-
int errorCode = win32.ErrorCode;
791-
792-
pyExcep = new _OSError();
793-
if ((errorCode & 0x80070000) == 0x80070000) {
794-
errorCode &= 0xffff;
795-
}
796-
pyExcep.__init__(errorCode, win32.Message, null, errorCode);
797-
return pyExcep;
798-
} else if (clrException is DirectoryNotFoundException) {
799-
pyExcep = new _OSError(FileNotFoundError);
800800
} else {
801801
// conversions from generated code (in the generated hierarchy)...
802802
pyExcep = ToPythonHelper(clrException);

0 commit comments

Comments
 (0)