PageRenderTime 76ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/PythonFile.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 1734 lines | 1297 code | 266 blank | 171 comment | 370 complexity | 1a9429a986bab22d0ce284430ccb6798 MD5 | raw file

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

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