From 6ab1402a897e23bd32ab97ad841a8d2615ff78e1 Mon Sep 17 00:00:00 2001 From: Stefan Israelsson Tampe Date: Wed, 11 Apr 2018 21:10:33 +0200 Subject: io module finished --- modules/language/python/compile.scm | 10 +- modules/language/python/exceptions.scm | 43 +-- modules/language/python/module.scm | 22 +- modules/language/python/module/io.scm | 481 +++++++++++++++++++++++++++ modules/language/python/module/os.scm | 7 +- modules/language/python/module/python.scm | 56 +--- modules/language/python/module/subprocess.py | 5 +- modules/language/python/with.scm | 2 +- 8 files changed, 538 insertions(+), 88 deletions(-) create mode 100644 modules/language/python/module/io.scm diff --git a/modules/language/python/compile.scm b/modules/language/python/compile.scm index e5bc219..fe3533b 100644 --- a/modules/language/python/compile.scm +++ b/modules/language/python/compile.scm @@ -1407,7 +1407,8 @@ ,(C 'clear-warning-data) (fluid-set! (@@ (system base message) %dont-warn-list) '()) ,@(map (lambda (s) `(,(C 'var) ,s)) globs) - ,@(map (g globs exp) x)))))) + ,@(map (g globs exp) x) + (,(C 'export-all))))))) (define-syntax-parameter break (lambda (x) #'(values))) @@ -1932,3 +1933,10 @@ ((_ #t) #t) ((_ #f) #f) ((_ x ) (bool x)))) + +(define (export-all) + (define mod (current-module)) + (if (module-defined? mod '__all__) + (for ((x : (module-ref mod '__all__))) () + (module-export! mod (string->symbol (scm-str x)))))) + diff --git a/modules/language/python/exceptions.scm b/modules/language/python/exceptions.scm index 60b850e..99e8b97 100644 --- a/modules/language/python/exceptions.scm +++ b/modules/language/python/exceptions.scm @@ -7,28 +7,11 @@ SyntaxError SystemException OSError ProcessLookupError PermissionError None NotImplemented NotImplementedError - RunTimeError AssertionError)) + RunTimeError AssertionError + ModuleNotFoundError BlockingIOError)) (define-syntax-rule (aif it p x y) (let ((it p)) (if it x y))) -(define StopIteration 'StopIteration) -(define GeneratorExit 'GeneratorExit) -(define SystemException 'SystemException) -(define RuntimeError 'RuntimeError) -(define IndexError 'IndexError) -(define ValueError 'ValueError) -(define None 'None) -(define KeyError 'KeyError) -(define TypeError 'TypeError) -(define AttributeError 'AttributeError) -(define SyntaxError 'SyntaxError) -(define OSError 'OSError) -(define ProcessLookupError 'ProcessLookupError) -(define PermissionError 'PermissionError) -(define NotImplementedError 'NotImplementedError) -(define RunTimeError 'RunTimeError) -(define AssertionError 'AssertionError) - (define-python-class Exception () (define __init__ (case-lambda @@ -45,6 +28,28 @@ (format #f "~a" (ref self '__name__)))))) +(define-syntax-rule (define-er nm k) + (define-python-class nm (Exception))) + +(define StopIteration 'StopIteration) +(define GeneratorExit 'GeneratorExit) +(define-er SystemException 'SystemException) +(define-er RuntimeError 'RuntimeError) +(define-er IndexError 'IndexError) +(define-er ValueError 'ValueError) +(define None 'None) +(define-er KeyError 'KeyError) +(define-er TypeError 'TypeError) +(define-er AttributeError 'AttributeError) +(define-er SyntaxError 'SyntaxError) +(define-er OSError 'OSError) +(define-er ProcessLookupError 'ProcessLookupError) +(define-er PermissionError 'PermissionError) +(define-er NotImplementedError 'NotImplementedError) +(define-er RunTimeError 'RunTimeError) +(define AssertionError 'AssertionError) +(define-er ModuleNotFoundError 'ModuleNotFoundError) +(define-er BlockingIOError 'BlockingIOError) (define NotImplemented (list 'NotImplemented)) diff --git a/modules/language/python/module.scm b/modules/language/python/module.scm index 2940a9a..53aa1c2 100644 --- a/modules/language/python/module.scm +++ b/modules/language/python/module.scm @@ -44,14 +44,14 @@ (rawset self '_isprivate p))) (define _cont - (lambda (self id pre l nm) + (lambda (self id pre l nm skip-error?) (if id (aif it (rawref self id) ((ref it '__init__) pre l nm) (begin (rawset self id (Module pre l nm)) - (_make self pre nm))) - (_make self pre nm)))) + (_make self pre nm skip-error?))) + (_make self pre nm skip-error?)))) (define _contupdate (lambda (self id pre l nm) @@ -67,15 +67,15 @@ (match l ((name) (set self '_path (reverse (cons name pre))) - (_cont self #f (cons name pre) #f (cons name nm))) + (_cont self #f (cons name pre) #f (cons name nm) #f)) ((name . (and l (name2 . _))) (set self '_path (reverse (cons name pre))) - (_cont self name2 (cons name pre) l (cons name nm))))) + (_cont self name2 (cons name pre) l (cons name nm) #t)))) ((self l nm) - (_cont self #f l #f nm)) + (_cont self #f l #f nm #f)) ((self l) (if (pair? l) @@ -117,14 +117,20 @@ (string-split l #\.))))))) (define _make - (lambda (self l nm) + (lambda (self l nm skip-error?) (rawset self '_private #f) (if (not (rawref self '_module)) (begin (set self '__dict__ self) (set self '__name__ (string-join (map symbol->string (reverse nm)) ".")) - (let ((_module (in-scheme (resolve-module (reverse l))))) + (let* ((_module (in-scheme (resolve-module (reverse l)))) + (public-i (and _module (module-public-interface _module)))) + (if (and (not skip-error?) (not public-i)) + (raise (ModuleNotFoundError + (format #f "No module named ~a" + (ref self '__name__))))) + (set self '_export (module-public-interface _module)) (set self '_module _module) (hash-set! _modules l self)))))) diff --git a/modules/language/python/module/io.scm b/modules/language/python/module/io.scm new file mode 100644 index 0000000..b2f9342 --- /dev/null +++ b/modules/language/python/module/io.scm @@ -0,0 +1,481 @@ +(define-module (language python module io) + #:use-module (language python module exceptions) + #:use-module ((language python module os) + #:select (get_blocking)) + #:re-export (BlockingIOError) + #:export (UnsupportedOperation scm-port open DEFAULT_BUFFER_SIZE + IOBase RawIOBase BufferedIOBase FileIO + BytesIO BufferedReader BufferedWriter + BufferedRandom TextIOBase TextIOWrapper + StringIO)) + + + +(define (scm-port x) + (if (port? x) + x + (aif it (ref x '_port) + it + (raise ValueError "no port in scm-port")))) + +(define-python-class UnsupportedOperation (OSError ValueError)) + +(define DEFAULT_BUFFER_SIZE 4096) + +(define (path-it path) + (aif it (ref path '__fspath__) + (it) + path)) + +(def (open- path + (= mode "r") + (= buffering -1 ) + (= encoding None) + (= errors None) + (= newline None) + (= closefd #t) + (= opener None)) + + (define modelist (string->list mode)) + (define path (path-it path)) + (define (clean ch l) + (filter (lambda (c) (not (eq? ch c))) l)) + (let ((port (if (number? path) + (begin + (if (member #\a modelist) + (seek path 0 SEEK_END)) + (if (member #\x modelist) + (error "cannot use mode 'x' for fd input")) + (cond + ((member #\r modelist) + (fdes->inport path)) + ((member #\w modelist) + (fdes->outport path)))) + (begin + (if (member #\x modelist) + (if (file-exists? path) + (raise OSError "mode='x' and file exists") + (set mode (list->string + (clean #\x modelist))))) + ((@ (guile) open-file) (path-it path) mode)))) + + (errors (if (bool errors) + (scm-str errors) + (let ((s (port-conversion-strategy port))) + (cond + ((eq? s 'error) "strict") + ((eq? s 'substitute) "replace") + ((eq? s 'escape) "basckslashreplace"))))) + + (encoding (if (eq? encoding None) + (port-encoding port) + encoding))) + + + ;; encoding + (set self 'encoding encoding) + (set-port-encoding! port encoding) + + (case buffering + ((-1) + (setvbuf port 'block DEFAULT_BUFFER_SIZE)) + ((0) + (setvbuf port 'none)) + ((1) + (setvbuf port 'line)) + (else + (setvbuf port 'block buffering))) + + (cond + ((equal? errors "strict") + (set-port-conversion-strategy! port 'error)) + ((equal? errors "replace") + (set-port-conversion-strategy! port 'substitute)) + ((equal? errors "basckslashreplace") + (set-port-conversion-strategy! port 'escape)) + (else + (set-port-conversion-strategy! port 'escape))) + + port)) + + +(def (open path + (= mode "r") + (= buffering -1 ) + (= encoding None) + (= errors None) + (= newline None) + (= closefd #t) + (= opener None)) + + (let ((F + (FileIO (cons + (open path mode buffering encoding errors + newline closefd opener) + path) + mode))) + (if (member #\b (string->list mode)) + F + (TextIOWrapper F encoding errors)))) + + +;;ABC + +(define-syntax-rule (check self . l) + (aif it (ref self 'raw) + (let ((self it)) + (if (ref self 'closed) + (raise ValueError "IO operation on closed port")) + . l) + (begin + (if (ref self 'closed) + (raise ValueError "IO operation on closed port")) + . l))) + +(define-python-class IOBase () + (define __init__ + (lambda (self port) + (set self '_port port) + (set self 'closed (port-closed? port)))) + + (define __getport__ + (lambda (self) + (check self + (ref self _port)))) + + (define close + (lambda (self) + (check self + (close-port (ref self '_port)) + (set self 'closed #t)))) + + (define __enter__ + (lambda (self) + (check self) + self)) + + (define __exit__ + (lambda (self . x) + (check self + ((ref self 'close))))) + + (define flush + (lambda (self) + (check self + (if ((ref self readable)) (drain-input (ref self '_port))) + (if ((ref self writeable)) (force-output (ref self '_port)))))) + + (define isatty + (lambda (self) + (check self + (isatty? (ref self '_port))))) + + (define __iter__ + (lambda (self) + (check self) + self)) + + (define __next__ + (lambda (self) + (check self + (raise StopIteration)))) + + (define readable + (lambda (self) + (check self + (output-port? (ref self '_port))))) + + (define readline + (lam (self (= size -1)) + (check self + (raise UnsupportedOperation)))) + + (define readlines + (lam (self (= hint -1)) + (check self + (raise UnsupportedOperation)))) + + (define seekable + (lambda (self) + (check self + (catch #t + (lambda () (seek (ref self '_port) 0 SEEK_CUR) #t) + (lambda x #f))))) + + (define seek + (lambda* (self offset #:optional (whence SEEK_SET)) + (check self + (if (not ((ref self seekable))) + (raise (ValueError "Not seekable"))) + (seek (ref self '_port) offset whence)))) + + + (define tell + (lambda (self) + (check self + (ftell (ref self '_port))))) + + (define truncate + (lam (self (size None)) + (check self + (if (eq? size None) + (truncate-file (ref self '_port)) + (truncate-file (ref self '_port) size))))) + + + (define writable + (lambda (self) + (check self + (input-port? (ref self '_port))))) + + (define writelines + (lambda (self lines) + (check self + (raise UnsupportedOperation)))) + + (define __del__ + (lambda (self + ((ref self 'close)))))) + + + + + + +(define-python-class RawIOBase (IOBase) + (define read + (lambda (self #:optional (size -1)) + (check self + (bytes + (if (< size 0) + ((ref self 'readall)) + (get-bytevector-n (ref self '_port) size)))))) + + + (define readall + (lambda (self) + (check self + (bytes + (get-bytevector-all (ref self '_port)))))) + + (define readinto + (lambda (self b) + (check self + (let* ((n (len b)) + (b (scm-bytevector b)) + (m (get-bytevector-n! (ref self '_port) b 0 n))) + (if (eof? m) + (if (get_blocking port) + 0 + None)))))) + + (define write + (lambda (self b) + (check self + (let ((n (len b)) + (b (scm-bytevector b))) + (put-bytevector (ref self '_port) b 0 n) + n))))) + + +(define-python-class BufferedIOBase (RawIOBase) + (define detach + (lambda (self) + (check self + (raise UnsupportedOperation "detach")))) + + (define read1 + (lambda* (self #:optional (size -1)) + (check self + ((ref self 'read) size)))) + + (define readinto1 + (lambda (self b) + (check self + ((ref self 'readinto) b))))) + +(define-python-class FileIO (RawIOBase) + (define __init__ + (lam (self name (= mode 'r') (= closefd #t) (= opener None)) + (if (pair? name) + (set self '_port (car name)) + (set self '_port + (open_ it + #:mode mode + #:closefd closefd + #:opener opener))) + (set self 'mode mode) + (set self 'name (cdr name))))) + + +(define-python-class BytesIO (BufferedIOBase) + (define __init__ + (lambda* (self #:optional (initial_bytes None)) + (if (eq? initial_bytes None) + (call-with-values open-bytevector-output-port + (lambda (port get-bytevector) + (set self '_port port) + (set self '_gtbv get-bytevector))) + (set self '_port + (open-bytevector-input-port initial_bytes))))) + + (define getvalue + (lambda (self) + (check self + (bytes ((ref self '_gtbv))))))) + +(define-python-class BufferedReader (BufferedIOBase) + (define __init__ + (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE)) + (let ((port (ref raw '_port))) + (case buffer_size + ((0) + (setvbuf port 'none)) + ((1) + (setvbuf port 'line)) + (else + (setvbuf port 'block buffer_size)))) + (set self 'raw raw))) + + (define peek + (lambda (self) + (raise UnsupportedOperation peek)))) + +(define-python-class BufferedWriter (BufferedIOBase) + (define __init__ + (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE)) + (let ((port (ref raw '_port))) + (case buffer_size + ((0) + (setvbuf port 'none)) + ((1) + (setvbuf port 'line)) + (else + (setvbuf port 'block buffer_size)))) + (set self 'raw raw)))) + +(define-python-class BufferedRandom (BufferedIOBase) + (define __init__ + (lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE)) + (let ((port (ref raw '_port))) + (case buffer_size + ((0) + (setvbuf port 'none)) + ((1) + (setvbuf port 'line)) + (else + (setvbuf port 'block buffer_size)))) + (set self 'raw raw))) + + (define peek + (lambda (self) + (raise UnsupportedOperation peek)))) + +(use-modules (ice-9 textual-ports)) +(use-modules (ice-9 rdelim)) + +(define-python-class TextIOBase (IOBase) + (define read + (lambda (self size) + (check self + (let ((port (ref self '_port))) + (get-string-n port size))))) + + (define readline + (lam (self (= size -1)) + (check self + (let ((port (ref self '_port))) + (read-line port 'concat))))) + + (define write + (lambda (self s) + (check self + (let ((port (ref self '_port))) + (put-string port (scm-str s) 0 (len s)) + (len s)))))) + +(define (get-port x) + (aif it (ref x '_port) + it + (aif it (ref x 'raw) + (get-port it) + (raise (ValueError "No port associated to IO wrapper"))))) + +(define-python-class TextIOWrapper (TextIOBase) + (define __init__ + (lam (self buffer + (= encoding None) + (= errors None) + (= newline None) + (= line_buffering #f) + (= write_through #f)) + (set self 'raw buffer) + (let* ((port (get-port buffer)) + (errors (if (bool errors) + (scm-str errors) + (let ((s (port-conversion-strategy port))) + (cond + ((eq? s 'error) "strict") + ((eq? s 'substitute) "replace") + ((eq? s 'escape) "basckslashreplace"))))) + (encoding (if (eq? encoding None) + (port-encoding port) + encoding))) + ;; encoding + (set self 'encoding encoding) + (set-port-encoding! port encoding) + + ;; buffering + (if line_buffering + (setvbuf port 'line)) + + (set self 'line_buffering line_buffering) + + ;; errors + (set self 'error errors) + (cond + ((equal? errors "strict") + (set-port-conversion-strategy! port 'error)) + ((equal? errors "replace") + (set-port-conversion-strategy! port 'substitute)) + ((equal? errors "basckslashreplace") + (set-port-conversion-strategy! port 'escape)) + (else + (set-port-conversion-strategy! port 'escape))) + + ;; write trough + (set self 'write_trough write_trough))))) + +(define-python-class StringIO (TextIOBase) + (define __init__ + (lam (self (= initial_value "") (= newline "\n")) + (set self 'newline newline) + (if (equal? initial_value "") + (set self '_port (open-output-str)) + (set self '_port (open-input-str initial_value))))) + + (define getvalue + (lambda (self) + (check self + (get-output-string (ref self port)))))) + + + + + + + + + + + + + + + + + + +(define-python-class TextIOWrapper (TextIOBase)) +(define-python-class StringIO (TextIOBase)) + diff --git a/modules/language/python/module/os.scm b/modules/language/python/module/os.scm index 863287d..757d549 100644 --- a/modules/language/python/module/os.scm +++ b/modules/language/python/module/os.scm @@ -694,9 +694,10 @@ (rm (fcntl3 fd F_GETFL (logand o (lognot O_NONBLOCK))))))) (define (get_blocking fd) - (if (= (logand O_NONBLOCK (rm (fcntl2 fd F_GETFL))) 0) - #f - #t)) + (let ((fd (if (port? fd) (port->fdes fd) fd))) + (if (= (logand O_NONBLOCK (rm (fcntl2 fd F_GETFL))) 0) + #f + #t))) (define (readv fd buffers) (error "not implemented")) diff --git a/modules/language/python/module/python.scm b/modules/language/python/module/python.scm index 30434af..96b0aa7 100644 --- a/modules/language/python/module/python.scm +++ b/modules/language/python/module/python.scm @@ -8,6 +8,7 @@ object-method)) #:use-module (language python exceptions ) #:use-module ((language python module string ) #:select ()) + #:use-module ((language python module io ) #:select (open)) #:use-module (language python def ) #:use-module (language python for ) #:use-module (language python try ) @@ -38,13 +39,13 @@ SyntaxError bool len dir next dict None property range tuple bytes bytearray eval locals globals - compile exec type object + compile exec type object open ) #:export (print repr complex float int str set all any bin callable reversed chr classmethod staticmethod objectmethod - divmod enumerate filter open + divmod enumerate filter getattr hasattr setattr hex isinstance issubclass iter map sum id input oct ord pow super sorted zip @@ -52,11 +53,6 @@ (define-syntax-rule (aif it p x y) (let ((it p)) (if it x y))) -(define (path-it path) - (aif it (ref path '__fspath__) - (it) - path)) - (define print (case-lambda (() ((@ (guile) format) #t "~%")) @@ -263,52 +259,6 @@ (yield (reverse r)) (lp)))))))))) -(define DEFAULT_BUFFER_SIZE 4096) -(def (open path - (= mode "r") - (= buffering -1 ) - (= encoding None) - (= errors None) - (= newline None) - (= closefd #t) - (= opener None)) - - (define modelist (string->list mode)) - (define path (path-it path)) - (define (clean ch l) - (filter (lambda (c) (not (eq? ch c))) l)) - (let ((port (if (number? path) - (begin - (if (member #\a modelist) - (seek path 0 SEEK_END)) - (if (member #\x modelist) - (error "cannot use mode 'x' for fd input")) - (cond - ((member #\r modelist) - (fdes->inport path)) - ((member #\w modelist) - (fdes->outport path)))) - (begin - (if (member #\x modelist) - (if (file-exists? path) - (raise OSError "mode='x' and file exists") - (set mode (list->string - (clean #\x modelist))))) - ((@ (guile) open-file) (path-it path) mode))))) - - (case buffering - ((-1) - (setvbuf port 'block DEFAULT_BUFFER_SIZE)) - ((0) - (setvbuf port 'none)) - ((1) - (setvbuf port 'line)) - (else - (setvbuf port 'block buffering))) - - port)) - - (define-python-class ClassMethod ()) (define-python-class StaticMethod ()) (define-python-class Funcobj ()) diff --git a/modules/language/python/module/subprocess.py b/modules/language/python/module/subprocess.py index 0d8ee7e..d4dd259 100644 --- a/modules/language/python/module/subprocess.py +++ b/modules/language/python/module/subprocess.py @@ -86,7 +86,6 @@ class CalledProcessError(SubprocessError): # .stdout is a transparent alias for .output self.output = value - class TimeoutExpired(SubprocessError): """This exception is raised when the timeout expires while waiting for a child process. @@ -133,12 +132,12 @@ else: import threading except ImportError: import dummy_threading as threading - + pk(1) # When select or poll has indicated that the file is writable, # we can write up to _PIPE_BUF bytes without risk of blocking. # POSIX defines PIPE_BUF as >= 512. _PIPE_BUF = getattr(select, 'PIPE_BUF', 512) - + pk(2) # poll/select have the advantage of not requiring any extra file # descriptor, contrarily to epoll/kqueue (also, they require a single # syscall). diff --git a/modules/language/python/with.scm b/modules/language/python/with.scm index eab5c55..0193189 100644 --- a/modules/language/python/with.scm +++ b/modules/language/python/with.scm @@ -25,7 +25,7 @@ (lambda () (let ((id (enter))) . code)) (#:except #t => - (lambda (tag l) + (lambda (tag l) (set! type (if (pyclass? tag) tag (aif it (ref tag '__class__) -- cgit v1.2.3