PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/IronPython/IronPython/Runtime/PythonFile.cs

http://github.com/IronLanguages/main
C# | 2184 lines | 1650 code | 336 blank | 198 comment | 440 complexity | 565e255874d15c6a09d234f88e4bd36c MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.ComponentModel;
  19. using System.Diagnostics;
  20. using System.IO;
  21. using System.Reflection;
  22. using System.Runtime.InteropServices;
  23. using System.Text;
  24. using System.Threading;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Runtime;
  27. using Microsoft.Scripting.Utils;
  28. using Microsoft.Win32.SafeHandles;
  29. using IronPython.Runtime.Exceptions;
  30. using IronPython.Runtime.Operations;
  31. using IronPython.Runtime.Types;
  32. #if FEATURE_PIPES
  33. using System.IO.Pipes;
  34. #endif
  35. #if FEATURE_NUMERICS
  36. using System.Numerics;
  37. #else
  38. using Microsoft.Scripting.Math;
  39. #endif
  40. #if NETCOREAPP1_0
  41. using Environment = System.FakeEnvironment;
  42. #endif
  43. namespace IronPython.Runtime {
  44. #region Readers
  45. // The following set of classes is used to translate between pythonic file stream semantics and those of
  46. // the runtime and the underlying system.
  47. //
  48. // Python supports opening files in binary and text mode. Binary is fairly obvious: we want to preserve
  49. // the data as is, to the point where it should be possible to round-trip an arbitrary binary file without
  50. // introducing corruptions.
  51. //
  52. // Text mode is more complex. Python further subdivides this class into the regular text mode where the
  53. // newline convention is defined by the underlying system, and universal newline mode where python will
  54. // treat '\n', '\r' and '\r\n' as equivalently terminating a line. In all these text modes reading from
  55. // the file will translate the associated newline format into '\n' and writing will convert '\n' back to
  56. // the original newline format.
  57. //
  58. // We want to support all these modes and also not tie ourselves to a particular platform. So although
  59. // Win32 always terminates lines with '\r\n' we want to support running on platforms where '\r' or '\n' is
  60. // the terminator as well. Further, we don't wish to bog down the performance of the implementation by
  61. // checking the newline semantics throughout the code. So instead we define abstract reader and writer
  62. // classes that roughly support the APIs and semantics that python needs and provide a set of
  63. // implementations of those classes that match the mode selected at runtime.
  64. //
  65. // The classes defined below have the following hierarchy:
  66. //
  67. // PythonStreamReader :: Abstract reader APIs
  68. // PythonBinaryReader :: Read binary data
  69. // PythonTextCRLFReader :: Read text data with lines terminated with '\r\n'
  70. // PythonTextCRReader :: Read text data with lines terminated with '\r'
  71. // PythonTextLFReader :: Read text data with lines terminated with '\n'
  72. // PythonUniversalReader :: Read text data with lines terminated with '\r\n', '\r' or '\n'
  73. // PythonStreamWriter :: Abstract writer APIs
  74. // PythonBinaryWriter :: Write binary data
  75. // PythonTextCRLFWriter :: Write text data with lines terminated with '\r\n'
  76. // PythonTextCRWriter :: Write text data with lines terminated with '\r'
  77. // PythonTextLFWriter :: Write text data with lines terminated with '\n'
  78. //
  79. // Note that there is no universal newline write mode since there's no reasonable way to define this.
  80. // The abstract reader API.
  81. internal abstract class PythonStreamReader {
  82. protected Encoding _encoding;
  83. public Encoding Encoding { get { return _encoding; } }
  84. public abstract TextReader TextReader { get; }
  85. public PythonStreamReader(Encoding encoding) {
  86. _encoding = encoding;
  87. }
  88. // Read at most size characters and return the result as a string.
  89. public abstract String Read(int size);
  90. // Read until the end of the stream and return the result as a single string.
  91. public abstract String ReadToEnd();
  92. // Read characters up to and including the mode defined newline (or until EOF, in which case the
  93. // string will not be newline terminated).
  94. public abstract String ReadLine();
  95. // Read characters up to and including the mode defined newline (or until EOF or the given size, in
  96. // which case the string will not be newline terminated).
  97. public abstract String ReadLine(int size);
  98. // Discard any data we may have buffered based on the current stream position. Called after seeking in
  99. // the stream.
  100. public abstract void DiscardBufferedData();
  101. public abstract long Position {
  102. get;
  103. internal set; // update position bookkeeping
  104. }
  105. }
  106. // Read data as binary. We encode binary data in the low order byte of each character of the strings
  107. // returned so there will be a X2 expansion in space required (but normal string indexing can be used to
  108. // inspect the data).
  109. internal class PythonBinaryReader : PythonStreamReader {
  110. private readonly Stream/*!*/ _stream;
  111. public override TextReader TextReader { get { return null; } }
  112. // Buffer size (in bytes) used when reading until the end of the stream.
  113. private const int BufferSize = 4096;
  114. private byte[] _buffer;
  115. public PythonBinaryReader(Stream/*!*/ stream)
  116. : base(null) {
  117. Assert.NotNull(stream);
  118. _stream = stream;
  119. }
  120. // Read at most size characters (bytes in this case) and return the result as a string.
  121. public override String Read(int size) {
  122. // If size is zero return empty string
  123. if (size == 0)
  124. return String.Empty;
  125. byte[] data;
  126. if (size <= BufferSize) {
  127. if (_buffer == null)
  128. _buffer = new byte[BufferSize];
  129. data = _buffer;
  130. } else
  131. data = new byte[size];
  132. int leftCount = size;
  133. int offset = 0;
  134. while (true) {
  135. int count = _stream.Read(data, offset, leftCount);
  136. if (count <= 0) break;
  137. leftCount -= count;
  138. if (leftCount <= 0) break;
  139. offset += count;
  140. }
  141. System.Diagnostics.Debug.Assert(leftCount >= 0);
  142. return PackDataIntoString(data, size - leftCount);
  143. }
  144. // Read until the end of the stream and return the result as a single string.
  145. public override String ReadToEnd() {
  146. StringBuilder sb = new StringBuilder();
  147. if (_buffer == null)
  148. _buffer = new byte[BufferSize];
  149. while (true) {
  150. int count = _stream.Read(_buffer, 0, BufferSize);
  151. if (count == 0)
  152. break;
  153. sb.Append(PackDataIntoString(_buffer, count));
  154. }
  155. if (sb.Length == 0)
  156. return String.Empty;
  157. return sb.ToString();
  158. }
  159. // Read characters up to and including a '\n' (or until EOF, in which case the string will not be
  160. // newline terminated).
  161. public override String ReadLine() {
  162. StringBuilder sb = new StringBuilder(80);
  163. while (true) {
  164. int b = _stream.ReadByte();
  165. if (b == -1)
  166. break;
  167. sb.Append((char)b);
  168. if (b == '\n')
  169. break;
  170. }
  171. if (sb.Length == 0)
  172. return String.Empty;
  173. return sb.ToString();
  174. }
  175. // Read characters up to and including a '\n' (or until EOF or the given size, in which case the
  176. // string will not be newline terminated).
  177. public override String ReadLine(int size) {
  178. StringBuilder sb = new StringBuilder(80);
  179. while (size-- > 0) {
  180. int b = _stream.ReadByte();
  181. if (b == -1)
  182. break;
  183. sb.Append((char)b);
  184. if (b == '\n')
  185. break;
  186. }
  187. if (sb.Length == 0)
  188. return String.Empty;
  189. return sb.ToString();
  190. }
  191. // Discard any data we may have buffered based on the current stream position. Called after seeking in
  192. // the stream.
  193. public override void DiscardBufferedData() {
  194. // No buffering is performed.
  195. }
  196. public override long Position {
  197. get {
  198. return _stream.Position;
  199. }
  200. internal set {
  201. }
  202. }
  203. // Convert a byte array into a string by casting each byte into a character.
  204. internal static String PackDataIntoString(byte[] data, int count) {
  205. if (count == 1) {
  206. return ScriptingRuntimeHelpers.CharToString((char)data[0]);
  207. }
  208. StringBuilder sb = new StringBuilder(count);
  209. for (int i = 0; i < count; i++)
  210. sb.Append((char)data[i]);
  211. return sb.ToString();
  212. }
  213. }
  214. internal abstract class PythonTextReader : PythonStreamReader {
  215. // We read the stream through a StreamReader to take advantage of stream buffering and encoding to
  216. // translate incoming bytes into characters. This requires us to keep control of our own position.
  217. protected readonly TextReader/*!*/ _reader;
  218. protected long _position;
  219. public override TextReader TextReader { get { return _reader; } }
  220. public override long Position {
  221. get {
  222. return _position;
  223. }
  224. internal set {
  225. _position = value;
  226. }
  227. }
  228. public PythonTextReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long position)
  229. : base(encoding) {
  230. _reader = reader;
  231. _position = position;
  232. }
  233. // Discard any data we may have buffered based on the current stream position. Called after seeking in
  234. // the stream.
  235. public override void DiscardBufferedData() {
  236. StreamReader streamReader = _reader as StreamReader;
  237. if (streamReader != null) {
  238. streamReader.DiscardBufferedData();
  239. }
  240. }
  241. }
  242. // Read data as text with lines terminated with '\r\n' (the Windows convention). Such terminators will be
  243. // translated to '\n' in the strings returned.
  244. internal class PythonTextCRLFReader : PythonTextReader {
  245. // We read the stream through a StreamReader to take advantage of stream buffering and encoding to
  246. // translate incoming bytes into characters. This requires us to keep track of our own position.
  247. // the size of this buffer is optimized for reading at least one full line of text and avoding
  248. // creating StringBuilder's in that case - we therefore want something larger than common widths
  249. // for lines in files. This results in reading lines being about 4/5ths of the cost vs. a smaller
  250. // buffer
  251. private char[] _buffer = new char[160];
  252. private int _bufPos, _bufLen;
  253. public PythonTextCRLFReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long position)
  254. : base(reader, encoding, position) {
  255. }
  256. private int Read() {
  257. if (_bufPos >= _bufLen && ReadBuffer() == 0) {
  258. return -1;
  259. }
  260. _position++;
  261. return _buffer[_bufPos++];
  262. }
  263. private int Peek() {
  264. if (_bufPos >= _bufLen && ReadBuffer() == 0) {
  265. return -1;
  266. }
  267. return _buffer[_bufPos];
  268. }
  269. private int ReadBuffer() {
  270. _bufLen = _reader.Read(_buffer, 0, _buffer.Length);
  271. _bufPos = 0;
  272. return _bufLen;
  273. }
  274. // Read at most size characters and return the result as a string.
  275. public override String Read(int size) {
  276. if (size == 1) {
  277. int c = Read();
  278. if (c == -1) {
  279. return String.Empty;
  280. }
  281. if (c == '\r' && Peek() == '\n') {
  282. c = Read();
  283. }
  284. return ScriptingRuntimeHelpers.CharToString((char)c);
  285. }
  286. StringBuilder sb = new StringBuilder(size);
  287. while (size-- > 0) {
  288. int c = Read();
  289. if (c == -1)
  290. break;
  291. if (c == '\r' && Peek() == '\n') {
  292. c = Read();
  293. }
  294. sb.Append((char)c);
  295. }
  296. if (sb.Length == 0) {
  297. return String.Empty;
  298. }
  299. return sb.ToString();
  300. }
  301. // Read until the end of the stream and return the result as a single string.
  302. public override String ReadToEnd() {
  303. StringBuilder sb = new StringBuilder();
  304. while (true) {
  305. int c = Read();
  306. if (c == -1)
  307. break;
  308. if (c == '\r' && Peek() == '\n') {
  309. c = Read();
  310. }
  311. sb.Append((char)c);
  312. }
  313. if (sb.Length == 0)
  314. return String.Empty;
  315. return sb.ToString();
  316. }
  317. // Read characters up to and including a '\r\n', converted to '\n' (or until EOF, in which case the
  318. // string will not be newline terminated).
  319. public override String ReadLine() {
  320. return ReadLine(Int32.MaxValue);
  321. }
  322. // Read characters up to and including a '\r\n', converted to '\n' (or until EOF or the given size, in
  323. // which case the string will not be newline terminated).
  324. public override String ReadLine(int size) {
  325. StringBuilder sb = null;
  326. // start off w/ some text
  327. if (_bufPos >= _bufLen) ReadBuffer();
  328. if (_bufLen == 0) return String.Empty;
  329. int curIndex = _bufPos;
  330. int bytesWritten = 0;
  331. int lenAdj = 0;
  332. while (true) {
  333. if (curIndex >= _bufLen) {
  334. // need more text...
  335. if (sb == null) {
  336. sb = new StringBuilder((curIndex - _bufPos) * 2);
  337. }
  338. sb.Append(_buffer, _bufPos, curIndex - _bufPos);
  339. if (ReadBuffer() == 0) {
  340. return sb.ToString();
  341. }
  342. curIndex = 0;
  343. }
  344. char c = _buffer[curIndex++];
  345. if (c == '\r') {
  346. if (curIndex < _bufLen) {
  347. if (_buffer[curIndex] == '\n') {
  348. _position++;
  349. c = _buffer[curIndex++];
  350. lenAdj = 2;
  351. }
  352. } else if (_reader.Peek() == '\n') {
  353. c = (char)_reader.Read();
  354. lenAdj = 1;
  355. }
  356. }
  357. _position++;
  358. if (c == '\n') {
  359. break;
  360. }
  361. if (++bytesWritten >= size) break;
  362. }
  363. return FinishString(sb, curIndex, lenAdj);
  364. }
  365. private string FinishString(StringBuilder sb, int curIndex, int lenAdj) {
  366. int len = curIndex - _bufPos;
  367. int pos = _bufPos;
  368. _bufPos = curIndex;
  369. if (sb != null) {
  370. if (lenAdj != 0) {
  371. sb.Append(_buffer, pos, len - lenAdj);
  372. sb.Append('\n');
  373. } else {
  374. sb.Append(_buffer, pos, len);
  375. }
  376. return sb.ToString();
  377. } else if (lenAdj != 0) {
  378. return new String(_buffer, pos, len - lenAdj) + "\n";
  379. } else {
  380. return new String(_buffer, pos, len);
  381. }
  382. }
  383. // Discard any data we may have buffered based on the current stream position. Called after seeking in
  384. // the stream.
  385. public override void DiscardBufferedData() {
  386. _bufPos = _bufLen = 0;
  387. base.DiscardBufferedData();
  388. }
  389. }
  390. // Read data as text with lines terminated with '\r' (the Macintosh convention). Such terminators will be
  391. // translated to '\n' in the strings returned.
  392. internal class PythonTextCRReader : PythonTextReader {
  393. public PythonTextCRReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long position)
  394. : base(reader, encoding, position) {
  395. }
  396. // Read at most size characters and return the result as a string.
  397. public override String Read(int size) {
  398. if (size == 1) {
  399. int c = _reader.Read();
  400. if (c == -1) {
  401. return String.Empty;
  402. } else {
  403. _position++;
  404. }
  405. if (c == '\r') c = '\n';
  406. return ScriptingRuntimeHelpers.CharToString((char)c);
  407. }
  408. StringBuilder sb = new StringBuilder(size);
  409. while (size-- > 0) {
  410. int c = _reader.Read();
  411. if (c == -1)
  412. break;
  413. _position++;
  414. if (c == '\r')
  415. c = '\n';
  416. sb.Append((char)c);
  417. }
  418. if (sb.Length == 0)
  419. return String.Empty;
  420. return sb.ToString();
  421. }
  422. // Read until the end of the stream and return the result as a single string.
  423. public override String ReadToEnd() {
  424. StringBuilder sb = new StringBuilder();
  425. while (true) {
  426. int c = _reader.Read();
  427. if (c == -1)
  428. break;
  429. _position++;
  430. if (c == '\r')
  431. c = '\n';
  432. sb.Append((char)c);
  433. }
  434. if (sb.Length == 0)
  435. return String.Empty;
  436. return sb.ToString();
  437. }
  438. // Read characters up to and including a '\r', converted to '\n' (or until EOF, in which case the
  439. // string will not be newline terminated).
  440. public override String ReadLine() {
  441. StringBuilder sb = new StringBuilder(80);
  442. while (true) {
  443. int c = _reader.Read();
  444. if (c == -1)
  445. break;
  446. _position++;
  447. if (c == '\r')
  448. c = '\n';
  449. sb.Append((char)c);
  450. if (c == '\n')
  451. break;
  452. }
  453. if (sb.Length == 0)
  454. return String.Empty;
  455. return sb.ToString();
  456. }
  457. // Read characters up to and including a '\r', converted to '\n' (or until EOF or the given size, in
  458. // which case the string will not be newline terminated).
  459. public override String ReadLine(int size) {
  460. StringBuilder sb = new StringBuilder(80);
  461. while (size-- > 0) {
  462. int c = _reader.Read();
  463. if (c == -1)
  464. break;
  465. _position++;
  466. if (c == '\r')
  467. c = '\n';
  468. sb.Append((char)c);
  469. if (c == '\n')
  470. break;
  471. }
  472. if (sb.Length == 0)
  473. return String.Empty;
  474. return sb.ToString();
  475. }
  476. }
  477. // Read data as text with lines terminated with '\n' (the Unix convention).
  478. internal class PythonTextLFReader : PythonTextReader {
  479. public PythonTextLFReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long position)
  480. : base(reader, encoding, position) {
  481. }
  482. // Read at most size characters and return the result as a string.
  483. public override String Read(int size) {
  484. if (size == 1) {
  485. int c = _reader.Read();
  486. if (c == -1) {
  487. return String.Empty;
  488. } else {
  489. _position++;
  490. }
  491. return ScriptingRuntimeHelpers.CharToString((char)c);
  492. }
  493. StringBuilder sb = new StringBuilder(size);
  494. while (size-- > 0) {
  495. int c = _reader.Read();
  496. if (c == -1)
  497. break;
  498. _position++;
  499. sb.Append((char)c);
  500. }
  501. if (sb.Length == 0)
  502. return String.Empty;
  503. return sb.ToString();
  504. }
  505. // Read until the end of the stream and return the result as a single string.
  506. public override String ReadToEnd() {
  507. return _reader.ReadToEnd();
  508. }
  509. // Read characters up to and including a '\n' (or until EOF, in which case the string will not be
  510. // newline terminated).
  511. public override String ReadLine() {
  512. StringBuilder sb = new StringBuilder(80);
  513. while (true) {
  514. int c = _reader.Read();
  515. if (c == -1)
  516. break;
  517. _position++;
  518. sb.Append((char)c);
  519. if (c == '\n')
  520. break;
  521. }
  522. if (sb.Length == 0)
  523. return String.Empty;
  524. return sb.ToString();
  525. }
  526. // Read characters up to and including a '\n' (or until EOF or the given size, in which case the
  527. // string will not be newline terminated).
  528. public override String ReadLine(int size) {
  529. StringBuilder sb = new StringBuilder(80);
  530. while (size-- > 0) {
  531. int c = _reader.Read();
  532. if (c == -1)
  533. break;
  534. _position++;
  535. sb.Append((char)c);
  536. if (c == '\n')
  537. break;
  538. }
  539. if (sb.Length == 0)
  540. return String.Empty;
  541. return sb.ToString();
  542. }
  543. }
  544. // Read data as text with lines terminated with any of '\n', '\r' or '\r\n'. Such terminators will be
  545. // translated to '\n' in the strings returned. This class also records whcih of these have been seen so
  546. // far in the stream to support python semantics (see the Terminators property).
  547. internal class PythonUniversalReader : PythonTextReader {
  548. private int _lastChar = -1;
  549. // Symbols for the different styles of newline terminator we might have seen in this stream so far.
  550. public enum TerminatorStyles {
  551. None = 0x0,
  552. CrLf = 0x1, // '\r\n'
  553. Cr = 0x2, // '\r'
  554. Lf = 0x4 // '\n'
  555. }
  556. // We read the stream through a StreamReader to take advantage of stream buffering and encoding to
  557. // translate incoming bytes into characters. This requires that we keep track of our own position.
  558. private TerminatorStyles _terminators;
  559. public PythonUniversalReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long position)
  560. : base(reader, encoding, position) {
  561. _terminators = TerminatorStyles.None;
  562. }
  563. private int ReadOne() {
  564. if (_lastChar != -1) {
  565. var res = _lastChar;
  566. _lastChar = -1;
  567. return res;
  568. }
  569. return _reader.Read();
  570. }
  571. // Private helper used to check for newlines and transform and record as necessary. Returns the
  572. // possibly translated character read.
  573. private int ReadChar() {
  574. int c = ReadOne();
  575. if (c != -1) _position++;
  576. if (c == '\r') {
  577. Debug.Assert(_lastChar == -1);
  578. // we can't Peek here because Peek() won't block for more input
  579. int next = _reader.Read();
  580. if (next == '\n') {
  581. _position++;
  582. _terminators |= TerminatorStyles.CrLf;
  583. } else {
  584. _lastChar = next;
  585. _terminators |= TerminatorStyles.Cr;
  586. }
  587. c = '\n';
  588. } else if (c == '\n') {
  589. _terminators |= TerminatorStyles.Lf;
  590. }
  591. return c;
  592. }
  593. // Read at most size characters and return the result as a string.
  594. public override String Read(int size) {
  595. if (size == 1) {
  596. int c = ReadChar();
  597. if (c == -1) {
  598. return String.Empty;
  599. }
  600. return ScriptingRuntimeHelpers.CharToString((char)c);
  601. }
  602. StringBuilder sb = new StringBuilder(size);
  603. while (size-- > 0) {
  604. int c = ReadChar();
  605. if (c == -1)
  606. break;
  607. sb.Append((char)c);
  608. }
  609. if (sb.Length == 0)
  610. return String.Empty;
  611. return sb.ToString();
  612. }
  613. // Read until the end of the stream and return the result as a single string.
  614. public override String ReadToEnd() {
  615. StringBuilder sb = new StringBuilder();
  616. while (true) {
  617. int c = ReadChar();
  618. if (c == -1)
  619. break;
  620. sb.Append((char)c);
  621. }
  622. if (sb.Length == 0)
  623. return String.Empty;
  624. return sb.ToString();
  625. }
  626. // Read characters up to and including a '\r\n', '\r' or '\n' converted to '\n' (or until EOF, in
  627. // which case the string will not be newline terminated).
  628. public override String ReadLine() {
  629. StringBuilder sb = new StringBuilder(80);
  630. while (true) {
  631. int c = ReadChar();
  632. if (c == -1)
  633. break;
  634. sb.Append((char)c);
  635. if (c == '\n')
  636. break;
  637. }
  638. if (sb.Length == 0)
  639. return String.Empty;
  640. return sb.ToString();
  641. }
  642. // Read characters up to and including a '\r\n', '\r' or '\n' converted to '\n' (or until EOF or the
  643. // given size, in which case the string will not be newline terminated).
  644. public override String ReadLine(int size) {
  645. StringBuilder sb = new StringBuilder(80);
  646. while (size-- > 0) {
  647. int c = ReadChar();
  648. if (c == -1)
  649. break;
  650. sb.Append((char)c);
  651. if (c == '\n')
  652. break;
  653. }
  654. if (sb.Length == 0)
  655. return String.Empty;
  656. return sb.ToString();
  657. }
  658. // PythonUniversalReader specific property that returns a bitmask of all the newline termination
  659. // styles seen in the stream so far.
  660. public TerminatorStyles Terminators { get { return _terminators; } }
  661. }
  662. #endregion
  663. #region Writers
  664. // The abstract writer API.
  665. internal abstract class PythonStreamWriter {
  666. protected Encoding _encoding;
  667. public Encoding Encoding { get { return _encoding; } }
  668. public abstract TextWriter TextWriter { get; }
  669. public PythonStreamWriter(Encoding encoding) {
  670. _encoding = encoding;
  671. }
  672. // Write the data in the input string to the output stream, converting line terminators ('\n') into
  673. // the output format as necessary. Returns the number of bytes written
  674. public abstract int Write(String/*!*/ data);
  675. // Write the raw input data to the output stream
  676. public abstract int WriteBytes(IList<byte> data);
  677. // Flush any buffered data to the file.
  678. public abstract void Flush();
  679. public abstract void FlushToDisk();
  680. public void FlushToDiskWorker(Stream stream) {
  681. var fs = stream as FileStream;
  682. if (fs != null) {
  683. #if CLR4
  684. fs.Flush(true);
  685. #elif FEATURE_NATIVE
  686. if (!NativeMethods.FlushFileBuffers(fs.SafeFileHandle)) {
  687. throw new IOException();
  688. }
  689. #else
  690. throw new NotImplementedException();
  691. #endif
  692. }
  693. }
  694. }
  695. // Write binary data embedded in the low-order byte of each string character to the output stream with no
  696. // other translation.
  697. internal class PythonBinaryWriter : PythonStreamWriter {
  698. private Stream/*!*/ _stream;
  699. public override TextWriter TextWriter { get { return null; } }
  700. public PythonBinaryWriter(Stream/*!*/ stream)
  701. : base(null) {
  702. _stream = stream;
  703. }
  704. // Write the data in the input string to the output stream. No newline conversion is performed.
  705. public override int Write(string/*!*/ data) {
  706. byte[] bytes = PythonAsciiEncoding.Instance.GetBytes(data);
  707. Debug.Assert(bytes.Length == data.Length);
  708. _stream.Write(bytes, 0, bytes.Length);
  709. return bytes.Length;
  710. }
  711. // Write the raw input data to the output stream. No newline conversion is performed.
  712. public override int WriteBytes(IList<byte> data) {
  713. int count = data.Count;
  714. for (int i = 0; i < count; i++) {
  715. _stream.WriteByte(data[i]);
  716. }
  717. return count;
  718. }
  719. // Flush any buffered data to the file.
  720. public override void Flush() {
  721. _stream.Flush();
  722. }
  723. public override void FlushToDisk() {
  724. FlushToDiskWorker(_stream);
  725. }
  726. }
  727. // Write data with '\r', '\n' or '\r\n' line termination.
  728. internal class PythonTextWriter : PythonStreamWriter {
  729. // We write the stream through a StreamWriter to take advantage of stream buffering and encoding to
  730. // translate outgoing characters into bytes.
  731. private TextWriter/*!*/ _writer;
  732. private readonly string _eoln;
  733. public override TextWriter TextWriter { get { return _writer; } }
  734. public PythonTextWriter(TextWriter/*!*/ writer, string eoln)
  735. : base(writer.Encoding) {
  736. _writer = writer;
  737. _eoln = eoln;
  738. }
  739. // Write the data in the input string to the output stream, converting line terminators ('\n') into
  740. // _eoln as necessary.
  741. public override int Write(string/*!*/ data) {
  742. if (_eoln != null) {
  743. data = data.Replace("\n", _eoln);
  744. }
  745. _writer.Write(data);
  746. return data.Length;
  747. }
  748. // Write the input data to the output stream, converting line terminators ('\n') into _eoln as necessary.
  749. public override int WriteBytes(IList<byte> data) {
  750. // Result is equivalent to "return Write(data.MakeString());" but more efficient because
  751. // MakeString() and Replace() are done at the same time.
  752. int count = data.Count;
  753. StringBuilder sb = new StringBuilder(_eoln.Length > 1 ? (int)(count * 1.2) : count);
  754. for (int i = 0; i < count; i++) {
  755. char c = (char)data[i];
  756. if (c == '\n') {
  757. sb.Append(_eoln);
  758. } else {
  759. sb.Append(c);
  760. }
  761. }
  762. _writer.Write(sb.ToString());
  763. return count;
  764. }
  765. // Flush any buffered data to the file.
  766. public override void Flush() {
  767. _writer.Flush();
  768. }
  769. public override void FlushToDisk() {
  770. var streamWriter = _writer as StreamWriter;
  771. if (streamWriter != null) {
  772. streamWriter.Flush();
  773. FlushToDiskWorker(streamWriter.BaseStream);
  774. }
  775. }
  776. }
  777. #endregion
  778. #region File Manager
  779. internal class PythonFileManager {
  780. private HybridMapping<object> mapping = new HybridMapping<object>(3);
  781. public int AddToStrongMapping(PythonFile pf, int pos) {
  782. return mapping.StrongAdd(pf, pos);
  783. }
  784. public int AddToStrongMapping(PythonFile pf) {
  785. return mapping.StrongAdd(pf, -1);
  786. }
  787. public int AddToStrongMapping(object o, int pos) {
  788. return mapping.StrongAdd(o, pos);
  789. }
  790. public int AddToStrongMapping(object o) {
  791. return mapping.StrongAdd(o, -1);
  792. }
  793. public void Remove(PythonFile pf) {
  794. mapping.RemoveOnObject(pf);
  795. }
  796. public void RemovePythonFileOnId(int id) {
  797. mapping.RemoveOnId(id);
  798. }
  799. public void Remove(object o) {
  800. mapping.RemoveOnObject(o);
  801. }
  802. public void RemoveObjectOnId(int id) {
  803. mapping.RemoveOnId(id);
  804. }
  805. public PythonFile GetFileFromId(PythonContext context, int id) {
  806. PythonFile pf;
  807. if (!TryGetFileFromId(context, id, out pf)) {
  808. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  809. }
  810. return pf;
  811. }
  812. public bool TryGetFileFromId(PythonContext context, int id, out PythonFile pf) {
  813. // TODO:
  814. // the meaning of 0, 1 and 2 can be changed by open/close/dup
  815. // stdin/out/err should be also in the dynamic mapping
  816. switch (id) {
  817. case 0:
  818. pf = (context.GetSystemStateValue("__stdin__") as PythonFile);
  819. break;
  820. case 1:
  821. pf = (context.GetSystemStateValue("__stdout__") as PythonFile);
  822. break;
  823. case 2:
  824. pf = (context.GetSystemStateValue("__stderr__") as PythonFile);
  825. break;
  826. default:
  827. pf = mapping.GetObjectFromId(id) as PythonFile;
  828. break;
  829. }
  830. return pf != null;
  831. }
  832. public bool TryGetObjectFromId(PythonContext context, int id, out object o) {
  833. o = mapping.GetObjectFromId(id);
  834. return o != null;
  835. }
  836. public object GetObjectFromId(int id) {
  837. object o = mapping.GetObjectFromId(id);
  838. if (o == null) {
  839. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  840. }
  841. return o;
  842. }
  843. public int GetIdFromFile(PythonFile pf) {
  844. return mapping.GetIdFromObject(pf);
  845. }
  846. public void CloseIfLast(int fd, PythonFile pf) {
  847. mapping.RemoveOnId(fd);
  848. if (-1 == mapping.GetIdFromObject(pf)) {
  849. pf.close();
  850. }
  851. }
  852. public void CloseIfLast(int fd, Stream stream) {
  853. mapping.RemoveOnId(fd);
  854. if (-1 == mapping.GetIdFromObject(stream)) {
  855. ((IDisposable)stream).Dispose();
  856. }
  857. }
  858. public int GetOrAssignIdForFile(PythonFile pf) {
  859. // TODO: again logic fixed on 0, 1 and 2
  860. if (pf.IsConsole) {
  861. for (int i = 0; i < 3; i++) {
  862. if (pf == GetFileFromId(pf.Context, i)) {
  863. return i;
  864. }
  865. }
  866. }
  867. int res = mapping.GetIdFromObject(pf);
  868. if (res == -1) {
  869. // lazily created weak mapping
  870. res = mapping.WeakAdd(pf);
  871. }
  872. return res;
  873. }
  874. public int GetIdFromObject(object o) {
  875. return mapping.GetIdFromObject(o);
  876. }
  877. public int GetOrAssignIdForObject(object o) {
  878. int res = mapping.GetIdFromObject(o);
  879. if (res == -1) {
  880. // lazily created weak mapping
  881. res = mapping.WeakAdd(o);
  882. }
  883. return res;
  884. }
  885. public bool ValidateFdRange(int fd) {
  886. return fd >= 0 && fd < HybridMapping<object>.SIZE;
  887. }
  888. }
  889. #endregion
  890. [PythonType("file")]
  891. [DontMapIEnumerableToContains]
  892. public class PythonFile : IDisposable, ICodeFormattable, IEnumerator<string>, IEnumerator, IWeakReferenceable {
  893. private ConsoleStreamType _consoleStreamType;
  894. private SharedIO _io; // null for non-console
  895. internal Stream _stream; // null for console
  896. private string _mode;
  897. private string _name, _encoding;
  898. private PythonFileMode _fileMode;
  899. private PythonStreamReader _reader;
  900. private PythonStreamWriter _writer;
  901. [PythonHidden] protected bool _isOpen;
  902. private Nullable<long> _reseekPosition; // always null for console
  903. private WeakRefTracker _weakref;
  904. private string _enumValue;
  905. internal readonly PythonContext/*!*/ _context;
  906. private bool _softspace;
  907. internal bool IsConsole {
  908. get {
  909. return _stream == null;
  910. }
  911. }
  912. internal bool IsOutput {
  913. get {
  914. return _writer != null;
  915. }
  916. }
  917. internal PythonFile(PythonContext/*!*/ context) {
  918. _context = context;
  919. }
  920. public PythonFile(CodeContext/*!*/ context)
  921. : this(PythonContext.GetContext(context)) {
  922. }
  923. internal static PythonFile/*!*/ Create(CodeContext/*!*/ context, Stream/*!*/ stream, string/*!*/ name, string/*!*/ mode) {
  924. return Create(context, stream, PythonContext.GetContext(context).DefaultEncoding, name, mode);
  925. }
  926. internal static PythonFile/*!*/ Create(CodeContext/*!*/ context, Stream/*!*/ stream, Encoding/*!*/ encoding, string/*!*/ name, string/*!*/ mode) {
  927. PythonFile res = new PythonFile(PythonContext.GetContext(context));
  928. res.__init__(stream, encoding, name, mode);
  929. return res;
  930. }
  931. internal static PythonFile/*!*/ CreateConsole(PythonContext/*!*/ context, SharedIO/*!*/ io, ConsoleStreamType type, string/*!*/ name) {
  932. PythonFile res = new PythonFile(context);
  933. res.InitializeConsole(io, type, name);
  934. return res;
  935. }
  936. #if FEATURE_PIPES
  937. internal static PythonFile[] CreatePipe(CodeContext/*!*/ context) {
  938. var pythonContext = PythonContext.GetContext(context);
  939. var encoding = pythonContext.DefaultEncoding;
  940. var inPipe = new AnonymousPipeServerStream(PipeDirection.In);
  941. var inPipeFile = new PythonFile(context);
  942. inPipeFile.InitializePipe(inPipe, "r", encoding);
  943. var outPipe = new AnonymousPipeClientStream(PipeDirection.Out, inPipe.ClientSafePipeHandle);
  944. var outPipeFile = new PythonFile(context);
  945. outPipeFile.InitializePipe(outPipe, "w", encoding);
  946. return new [] {inPipeFile, outPipeFile};
  947. }
  948. [PythonHidden]
  949. public static PythonTuple CreatePipeAsFd(CodeContext context) {
  950. var pipeFiles = CreatePipe(context);
  951. return PythonTuple.MakeTuple(
  952. PythonContext.GetContext(context).FileManager.AddToStrongMapping(pipeFiles[0]),
  953. PythonContext.GetContext(context).FileManager.AddToStrongMapping(pipeFiles[1]));
  954. }
  955. #endif
  956. ~PythonFile() {
  957. try {
  958. Dispose(false);
  959. } catch (ObjectDisposedException) {
  960. } catch (EncoderFallbackException) {
  961. // flushing could fail due to encoding, ignore it
  962. } catch (IOException) {
  963. // flushing could fail, especially if one half of a pipe is closed
  964. }
  965. }
  966. #region Python initialization
  967. public void __init__(CodeContext/*!*/ context, string name, [DefaultParameterValue("r")]string mode, double buffering) {
  968. throw PythonOps.TypeError("integer argument expected, got float");
  969. }
  970. public void __init__(CodeContext/*!*/ context, string name, [DefaultParameterValue("r")]string mode, BigInteger buffering) {
  971. __init__(context, name, mode, (int)buffering);
  972. }
  973. //
  974. // Here are the mode rules for IronPython "file":
  975. // (r|a|w|rU|U|Ur) [ [+][b|t] | [b|t][+] ]
  976. //
  977. // Seems C-Python allows "b|t" at the beginning too.
  978. //
  979. public void __init__(CodeContext/*!*/ context, string name, [DefaultParameterValue("r")]string mode, [DefaultParameterValue(-1)]int buffering) {
  980. FileShare fshare = FileShare.ReadWrite;
  981. FileMode fmode;
  982. FileAccess faccess;
  983. if (name == null) {
  984. throw PythonOps.TypeError("file name must be string, found NoneType");
  985. }
  986. if (mode == null) {
  987. throw PythonOps.TypeError("mode must be string, not None");
  988. }
  989. if (mode == "") {
  990. throw PythonOps.ValueError("empty mode string");
  991. }
  992. bool seekEnd;
  993. TranslateAndValidateMode(mode, out fmode, out faccess, out seekEnd);
  994. try {
  995. Stream stream;
  996. try {
  997. if (Environment.OSVersion.Platform == PlatformID.Win32NT && name == "nul") {
  998. stream = Stream.Null;
  999. } else if (buffering <= 0) {
  1000. stream = PythonContext.GetContext(context).DomainManager.Platform.OpenInputFileStream(name, fmode, faccess, fshare);
  1001. } else {
  1002. stream = PythonContext.GetContext(context).DomainManager.Platform.OpenInputFileStream(name, fmode, faccess, fshare, buffering);
  1003. }
  1004. } catch (IOException e) {
  1005. AddFilename(context, name, e);
  1006. throw;
  1007. }
  1008. // we want to own the lifetime of the stream so we can flush & dispose in our finalizer...
  1009. GC.SuppressFinalize(stream);
  1010. if (seekEnd) stream.Seek(0, SeekOrigin.End);
  1011. __init__(stream, PythonContext.GetContext(context).DefaultEncoding, name, mode);
  1012. this._isOpen = true;
  1013. } catch (UnauthorizedAccessException e) {
  1014. throw ToIoException(context, name, e);
  1015. }
  1016. }
  1017. internal static Exception ToIoException(CodeContext context, string name, UnauthorizedAccessException e) {
  1018. Exception excp = new IOException(e.Message, e);
  1019. AddFilename(context, name, excp);
  1020. return excp;
  1021. }
  1022. internal static void AddFilename(CodeContext context, string name, Exception ioe) {
  1023. var pyExcep = PythonExceptions.ToPython(ioe);
  1024. PythonOps.SetAttr(context, pyExcep, "filename", name);
  1025. }
  1026. internal static void ValidateMode(string mode) {
  1027. FileMode fmode;
  1028. FileAccess access;
  1029. bool seekEnd;
  1030. TranslateAndValidateMode(mode, out fmode, out access, out seekEnd);
  1031. }
  1032. private static void TranslateAndValidateMode(string mode, out FileMode fmode, out FileAccess faccess, out bool seekEnd) {
  1033. if (mode.Length == 0) {
  1034. throw PythonOps.ValueError("empty mode string");
  1035. }
  1036. // remember the original mode for error reporting
  1037. string inMode = mode;
  1038. if (mode.IndexOf('U') != -1) {
  1039. mode = mode.Replace("U", String.Empty);
  1040. if (mode.Length == 0) {
  1041. mode = "r";
  1042. } else if (mode == "+") {
  1043. mode = "r+";
  1044. } else if (mode[0] == 'w' || mode[0] == 'a') {
  1045. throw PythonOps.ValueError("universal newline mode can only be used with modes starting with 'r'");
  1046. } else if (mode[0] != 'r') {
  1047. mode = "r" + mode;
  1048. }
  1049. }
  1050. // process read/write/append and remove char from mode
  1051. seekEnd = false;
  1052. switch (mode[0]) {
  1053. case 'r': fmode = FileMode.Open; mode = mode.Remove(0, 1); break;
  1054. case 'w': fmode = FileMode.Create; mode = mode.Remove(0, 1); break;
  1055. case 'a': fmode = FileMode.Append; mode = mode.Remove(0, 1); break;
  1056. default:
  1057. throw PythonOps.ValueError("mode string must begin with one of 'r', 'w', 'a' or 'U', not '{0}'", inMode);
  1058. }
  1059. // process +
  1060. if (mode.IndexOf('+') != -1) {
  1061. mode = mode.Remove(mode.IndexOf('+'), 1);
  1062. faccess = FileAccess.ReadWrite;
  1063. if (fmode == FileMode.Append) {
  1064. fmode = FileMode.OpenOrCreate;
  1065. seekEnd = true;
  1066. }
  1067. } else {
  1068. switch (fmode) {
  1069. case FileMode.Create: faccess = FileAccess.Write; break;
  1070. case FileMode.Open: faccess = FileAccess.Read; break;
  1071. case FileMode.Append: faccess = FileAccess.Write; break;
  1072. default: throw new InvalidOperationException();
  1073. }
  1074. }
  1075. // Make some additional mode check after processing all valid modes.
  1076. if (mode.Length > 0) {
  1077. if (mode[0] != 'U' && mode[0] != 'b' && mode[0] != 't') {
  1078. throw PythonOps.ValueError("Invalid mode ('{0}')", inMode);
  1079. }
  1080. }
  1081. }
  1082. public void __init__(CodeContext/*!*/ context, [NotNull]Stream/*!*/ stream) {
  1083. ContractUtils.RequiresNotNull(stream, "stream");
  1084. string mode;
  1085. if (stream.CanRead && stream.CanWrite) mode = "w+";
  1086. else if (stream.CanWrite) mode = "w";
  1087. else mode = "r";
  1088. __init__(stream, PythonContext.GetContext(context).DefaultEncoding, mode);
  1089. }
  1090. public void __init__(CodeContext/*!*/ context, [NotNull]Stream/*!*/ stream, string mode) {
  1091. __init__(stream, PythonContext.GetContext(context).DefaultEncoding, mode);
  1092. }
  1093. public void __init__([NotNull]Stream/*!*/ stream, Encoding encoding, string mode) {
  1094. InternalInitialize(stream, encoding, mode);
  1095. }
  1096. public void __init__([NotNull]Stream/*!*/ stream, [NotNull]Encoding/*!*/ encoding, string name, string mode) {
  1097. ContractUtils.RequiresNotNull(stream, "stream");
  1098. ContractUtils.RequiresNotNull(encoding, "encoding");
  1099. InternalInitialize(stream, encoding, name, mode);
  1100. }
  1101. private PythonTextReader/*!*/ CreateTextReader(TextReader/*!*/ reader, Encoding/*!*/ encoding, long initPosition) {
  1102. switch (_fileMode) {
  1103. case PythonFileMode.TextCrLf:
  1104. return new PythonTextCRLFReader(reader, encoding, initPosition);
  1105. case PythonFileMode.TextCr:
  1106. return new PythonTextCRReader(reader, encoding, initPosition);
  1107. case PythonFileMode.TextLf:
  1108. return new PythonTextLFReader(reader, encoding, initPosition);
  1109. case PythonFileMode.UniversalNewline:
  1110. return new PythonUniversalReader(reader, encoding, initPosition);
  1111. }
  1112. throw Assert.Unreachable;
  1113. }
  1114. private PythonTextReader/*!*/ CreateConsoleReader() {
  1115. Debug.Assert(_io != null);
  1116. Encoding encoding;
  1117. return CreateTextReader(_io.GetReader(out encoding), encoding, 0);
  1118. }
  1119. private PythonTextWriter/*!*/ CreateTextWriter(TextWriter/*!*/ writer) {
  1120. PythonFileMode fileMode = _fileMode;
  1121. if (_fileMode == PythonFileMode.UniversalNewline) {
  1122. if (Environment.OSVersion.Platform == PlatformID.Unix) {
  1123. fileMode = PythonFileMode.TextLf;
  1124. } else {
  1125. fileMode = PythonFileMode.TextCrLf;
  1126. }
  1127. // TODO: Identify Mac?
  1128. }
  1129. switch (fileMode) {
  1130. case PythonFileMode.TextCrLf:
  1131. return new PythonTextWriter(writer, "\r\n");
  1132. case PythonFileMode.TextCr:
  1133. return new PythonTextWriter(writer, "\r");
  1134. case PythonFileMode.TextLf:
  1135. return new PythonTextWriter(writer, "\n");
  1136. }
  1137. throw Assert.Unreachable;
  1138. }
  1139. /// <summary>
  1140. /// Sets the mode to text or binary. Returns true if previously set to text, false if previously set to binary.
  1141. /// </summary>
  1142. internal bool SetMode(CodeContext context, bool text) {
  1143. lock (this) {
  1144. var mode = MapFileMode(_mode);
  1145. if (text) {
  1146. if (mode == PythonFileMode.Binary) {
  1147. _fileMode = PythonFileMode.UniversalNewline;
  1148. } else {
  1149. _fileMode = mode;
  1150. }
  1151. } else {
  1152. _fileMode = PythonFileMode.Binary;
  1153. }
  1154. if (_stream != null) {
  1155. Encoding enc;
  1156. if (!StringOps.TryGetEncoding(_encoding, out enc)) {

Large files files are truncated, but you can click here to view the full file