glob
authorStefan Israelsson Tampe <stefan.itampe@gmail.com>
Thu, 16 Aug 2018 15:13:32 +0000 (17:13 +0200)
committerStefan Israelsson Tampe <stefan.itampe@gmail.com>
Thu, 16 Aug 2018 15:13:32 +0000 (17:13 +0200)
12 files changed:
modules/language/python/compile.scm
modules/language/python/exceptions.scm
modules/language/python/module.scm
modules/language/python/module/_python.scm
modules/language/python/module/decimal.scm
modules/language/python/module/fnmatch.py [new file with mode: 0644]
modules/language/python/module/functools.scm
modules/language/python/module/genericpath.py [new file with mode: 0644]
modules/language/python/module/glob.py [new file with mode: 0644]
modules/language/python/module/posixpath.py [new file with mode: 0644]
modules/language/python/number.scm
modules/language/python/range.scm

index ba0724173e00bfce1270afe4596a7e27506ca24b..b2f5ea78c164fe097d245456dde40c9c076ed3cd 100644 (file)
    ((__rshift__)   (N 'py-rshift))
    ((__rlshift__)  (N 'py-rlshift))
    ((__rrshift__)  (N 'py-rrshift))
+   ((bit_length)   (N 'py-bit-length))
    ((as_integer_ratio) (N 'py-as-integer-ratio))
    ((conjugate)    (N 'py-conjugate))
    ((denominator)  (N 'py-denominator))
index 4ce744a72539eceb3a33dae92868e595f3edeb2d..990f9c31bc3254438629c0156eabf5f5997bc532 100644 (file)
@@ -11,7 +11,8 @@
                           ModuleNotFoundError BlockingIOError
                           InterruptedError BaseException
                          ZeroDivisionError ArithmeticError
-                         OverflowError RecursionError))
+                         OverflowError RecursionError
+                         Warning DeprecationWarning BytesWarning))
 
 (define-syntax-rule (aif it p x y) (let ((it p)) (if it x y)))
 
            (format #f "~a"
                    (rawref self '__name__))))))
 
+(define-python-class Warning ()
+  (define __init__
+    (case-lambda
+      ((self)
+       (values))
+      ((self val . l)
+       (set self 'value val))))
+                 
+  (define __repr__
+    (lambda (self)
+      (aif it (rawref self 'value #f)
+           (format #f "~a:~a"
+                   (rawref self '__name__) it)
+           (format #f "~a"
+                   (rawref self '__name__))))))
+
 (define-syntax define-er
   (syntax-rules ()
     ((_ nm k)
 
 
             
+
+(define-syntax define-wr
+  (syntax-rules ()
+    ((_ nm k)
+     (define-python-class nm (Warning)))
+    ((_ nm w k)
+     (define-python-class nm w))))
+
+(define-wr BytesWarning       'BytesWarning)
+(define-wr DepricationWarning 'DeprecationWarning)
index 87f9207c63c210e08d08954f6876e0fa805b2f64..52ec88b06e163c5859e8697b30ba1cbae64be8e5 100644 (file)
@@ -42,7 +42,7 @@
   (define _modules (make-hash-table))
   (define __setprivate__
     (lambda (self p)
-      (rawset self '_isprivate p)))
+      (rawset self '_private p)))
 
   (define _cont
     (lambda (self id pre l nm skip-error?)
   
   (define _make
     (lambda (self l nm skip-error?)
-      (rawset self '_private #f)
+      (rawset self '_private #t)
       (if (not (rawref self '_module))
          (begin
            (rawset self '__name__ (string-join
               (if (and (not skip-error?) (not public-i))
                   (raise (ModuleNotFoundError
                           (format #f "No module named ~a"
-                                  (ref self '__name__)))))
+                                  (rawref self '__name__)))))
               
-             (rawset self '_export (module-public-interface _module))
-             (rawset self '_module _module)
+             (rawset self '_export   (module-public-interface _module))
+             (rawset self '_module   _module)
              (hash-set! _modules l self))))))
       
   (define __getattribute__
index 47df9fc22d4b40a446d78a16eb9af14f2afad0c4..2fac0a1e149f29ac014f6647159ceafcbcdf01cb 100644 (file)
@@ -40,6 +40,7 @@
                              len dir next dict None property range
                              tuple bytes bytearray eval locals globals
                              exec type object open __import__
+                            Warning BytesWarning DeprecationWarning
                              )
   
   #:export (print repr complex float int str
index 4f2f6fe8436f3791db4822380e408f102a9f577e..0778da187398c979649fde1c5e16fb34d5ea16b7 100644 (file)
           ;; Limits for the C version for compatibility
           MAX_PREC  MAX_EMAX MIN_EMIN MIN_ETINY))
 
+(define (abool x)
+  (if (eq? x None)
+      None
+      (bool x)))
+
 (define-syntax-rule (aif it p . l) (let ((it p)) (if (bool it) . l)))
+(define-syntax-rule (aaif it p . l) (let ((it p)) (if (abool it) . l)))
 
 (define-syntax-rule (D x) (lambda () x))
 
@@ -620,7 +626,7 @@ This is the copyright information of the file ported over to scheme
     ((_ (when . u) . l)
      (begin (when . u) (twix . l)))
     ((_ (a it code ...) . l)
-     (aif it a (begin code ...) (twix . l)))))
+     (aaif it a (begin code ...) (twix . l)))))
 
 (define-syntax-rule (norm-op self op)
   (begin
@@ -1196,21 +1202,19 @@ This is the copyright information of the file ported over to scheme
 
         Rounds, if it has reason.
         "
-       (pk '__neg__)
        (twix 
         ((un-special self context) it it)
-        (let () (pk 1))
         (let* ((context (if (eq? context None)
                             (getcontext)
                             context))
-               (ans     (if (pk (and (not (bool self))
+               (ans     (if (and (not (bool self))
                                  (not (eq? (cx-rounding context)
-                                           ROUND_FLOOR))))
+                                           ROUND_FLOOR)))
                             ;; -Decimal('0') is Decimal('0'),
                             ;; not Decimal('-0'), except
                             ;; in ROUND_FLOOR rounding mode.
-                            ((pk (ref self 'copy_abs)))
-                            ((pk (ref self 'copy_negate))))))      
+                            ((ref self 'copy_abs))
+                            ((ref self 'copy_negate)))))
           ((ref ans '_fix) context)))))
 
     (define __pos__
@@ -1473,7 +1477,6 @@ This is the copyright information of the file ported over to scheme
                                         (set! exp exp-)
                                         coeff-))))))))))
                              
-          (pk 'div sign coeff exp)
           (let ((ans (_dec_from_triple sign (str coeff) exp)))
             ((ref ans '_fix) context))))))
 
@@ -1864,7 +1867,7 @@ This is the copyright information of the file ported over to scheme
                  (set! ans
                        ((cx-error context) Overflow "above Emax"
                         (ref self '_sign)))
-                 (set! ans (_dec_from_triple (ref self '_sign) (pk 'c coeff) (pk 'e exp_min))))
+                 (set! ans (_dec_from_triple (ref self '_sign) coeff exp_min)))
              ;; raise the appropriate signals, taking care to respect
              ;; the precedence described in the specification
              (if (and changed self_is_subnormal)
@@ -2122,13 +2125,13 @@ This is the copyright information of the file ported over to scheme
         ((norm-op self other ) it it)
         ((norm-op self modulo) it it)
         (let (get-context context))
-
+        (let () (pk 1))
         ;; deal with NaNs: if there are any sNaNs then first one wins,
         ;; (i.e. behaviour for NaNs is identical to that of fma)
         (let ((self_is_nan   (ref self   '_isnan))
               (other_is_nan  (ref other  '_isnan))
               (modulo_is_nan (ref modulo '_isnan))))
-        
+        (let () (pk 2))
         ((or (bool self_is_nan) (bool other_is_nan) (bool modulo_is_nan)) it
          (cond
           ((= self_is_nan 2)
@@ -2143,7 +2146,7 @@ This is the copyright information of the file ported over to scheme
            (_fix_nan other context))
           (else
            (_fix_nan modulo context))))
-         
+        (let () (pk 3))
         ;;check inputs: we apply same restrictions as Python's pow()
         ((not (and ((ref self   '_isinteger))
                    ((ref other  '_isinteger))
@@ -2151,16 +2154,16 @@ This is the copyright information of the file ported over to scheme
          ((cx-error context) InvalidOperation
           (+ "pow() 3rd argument not allowed "
              "unless all arguments are integers")))
-        
+        (let () (pk 4))
         ((< other 0) it
          ((cx-error context) InvalidOperation
           (+ "pow() 2nd argument cannot be "
              "negative when 3rd argument specified")))
-
+        (let () (pk 5))
         ((not (bool modulo)) it
             ((cx-error context) InvalidOperation
              "pow() 3rd argument cannot be 0"))
-
+        (let () (pk 6))
         ;; additional restriction for decimal: the modulus must be less
         ;; than 10**prec in absolute value
         ((>= ((ref modulo 'adjusted)) (cx-prec context)) it
@@ -2168,7 +2171,7 @@ This is the copyright information of the file ported over to scheme
           (+ "insufficient precision: pow() 3rd "
              "argument must not have more than "
              "precision digits")))
-
+        (let () (pk 7))
         ;; define 0**0 == NaN, for consistency with two-argument pow
         ;; (even though it hurts!)
         ((and (not (bool other)) (not (bool self))) it
@@ -2176,7 +2179,7 @@ This is the copyright information of the file ported over to scheme
           (+ "at least one of pow() 1st argument "
              "and 2nd argument must be nonzero ;"
              "0**0 is not defined")))
-
+        (let () (pk 8))
         ;; compute sign of result
         (let ((sign     (if ((ref other '_iseven))
                             0
@@ -2184,23 +2187,23 @@ This is the copyright information of the file ported over to scheme
               (base     (_WorkRep ((ref self  'to_integral_value))))
               (exponent (_WorkRep ((ref other 'to_integral_value)))))
 
-
+          (let () (pk 9))
           ;; convert modulo to a Python integer, and self and other to
           ;; Decimal integers (i.e. force their exponents to be >= 0)
           (set! modulo (abs (int modulo)))
-
+          (let () (pk 10))
           ;; compute result using integer pow()
           (set! base (guile:modulo
                       (* (guile:modulo (ref base 'int) modulo)
                          (modulo-expt 10 (ref base 'exp) modulo))
                       modulo))
-          
+          (let () (pk 11))
           (let lp ((i (ref exponent 'exp)))
             (if (> i 0)
                 (begin
                   (set! base (modulo-expt base 10 modulo))
                   (lp (- i 1)))))
-          
+          (let () (pk 12))
           (set! base (modulo-expt base (ref exponent 'int) modulo))
           
           (_dec_from_triple sign (str base) 0)))))
@@ -2263,7 +2266,6 @@ This is the copyright information of the file ported over to scheme
         ;; so |y| < 1/xe and the result is not representable.
         ;; Similarly, len(str(abs(yc)*xc_bits)) <= -ye implies |y|
         ;; < 1/nbits(xc).
-
        (twix
         (let ()
           (define-syntax-rule (clean xc xe n +)
@@ -2273,7 +2275,6 @@ This is the copyright information of the file ported over to scheme
                     (set! xc (/ xc n))
                     (set! xe (+ xe 1))
                     (lp))))))
-
         (let* ((x  (_WorkRep self))
                (xc (ref x 'int))
                (xe (ref x 'exp)))
@@ -2283,15 +2284,13 @@ This is the copyright information of the file ported over to scheme
                (yc (ref y 'int))
                (ye (ref y 'exp)))
           (clean yc ye 10 +))
-        
         ;; case where xc == 1: result is 10**(xe*y), with xe*y
         ;; required to be an integer
-        ((= xc 1) it
-        (set! xe (* xe yc))
+        ((= xc 1) it
+         (set! xe (* xe yc))
 
         ;; result is now 10**(xe * 10**ye);  xe * 10**ye must be integral
         (clean xe ye 10 +)
-        
         (if (< ye 0)
             None
             (let ((exponent (* xe (expt 10 ye)))
@@ -2308,8 +2307,8 @@ This is the copyright information of the file ported over to scheme
 
               (_dec_from_triple 0 (+ "1" (* "0" zeros)) (- exponent zeros)))))
 
-        ;; case where y is negative: xc must be either a power
-        ;; of 2 or a power of 5.
+        ;; case where y is negative: xc must be either a power
+        ;; of 2 or a power of 5.
         ((= (ref y 'sign) 1) it
         (let ((last_digit (modulo xc 10)))
           (twix
@@ -2364,7 +2363,7 @@ This is the copyright information of the file ported over to scheme
 
                ((> e emax) it
                 None)
-
+               
                (begin
                  (set! xc (expt 5 e))
                  #f)))
@@ -2381,7 +2380,6 @@ This is the copyright information of the file ported over to scheme
                
                ((not (= remainder 0)) it
                 None)
-               
                (let () (clean xc e 5 -))
                 
                ;; Guard against large values of ye, using the same logic as in
@@ -2417,6 +2415,7 @@ This is the copyright information of the file ported over to scheme
 
         ;; now y is positive; find m and n such that y = m/n
        (let ((m #f) (n #f) (xc_bits (_nbits xc))))
+
        ((if (>= ye 0)
             (begin
               (set! m (* yc (expt 10 ye)))
@@ -2454,7 +2453,7 @@ This is the copyright information of the file ported over to scheme
                ((and (not (= xc 1)) (<= xc_bits n)) it
                 None)
 
-               ((not (= (modulo xe n) 0)) it
+               ((not (= (guile:modulo xe n) 0)) it
                 None)
 
                (begin
@@ -2465,7 +2464,7 @@ This is the copyright information of the file ported over to scheme
                    (let lp ()
                      (let* ((x (expt a (- n 1)))
                             (q (quotient xc x))
-                            (r (modulo   xc x)))
+                            (r (guile:modulo   xc x)))
                        (if (<= a q)
                            (if (not (and (= a q) (= r 0)))
                                None
@@ -2587,7 +2586,7 @@ This is the copyright information of the file ported over to scheme
             (pylist-ref _SignedInfinity result_sign)))
 
         ;; Inf**(+ve or Inf) = Inf; Inf**(-ve or -Inf) = 0
-        ((bool ((self '_isinfinity))) it
+        ((bool ((ref self '_isinfinity))) it
         (if (= (ref other '_sign) 0)
             (pylist-ref _SignedInfinity result_sign)
             (_dec_from_triple result_sign "0" 0)))
@@ -2596,7 +2595,7 @@ This is the copyright information of the file ported over to scheme
         ;; depend on the exponent of self, and on whether other is a
         ;; positive integer, a negative integer, or neither
        (let ((prec (cx-prec context))))
-       
+
         ((equal? self _One) it
         (let ((exp  #f))             
           (if ((ref other '_isinteger))
@@ -2666,6 +2665,7 @@ This is the copyright information of the file ported over to scheme
         ;; try for an exact result with precision +1
         (when (eq? ans None)
          (set! ans ((ref self '_power_exact) other (+ prec 1)))
+         (let () (pk 2 0))
          (when (not (eq? ans None))          
            (if (= result_sign 1)
                (set! ans (_dec_from_triple 1  (ref ans '_int)
@@ -2695,7 +2695,7 @@ This is the copyright information of the file ported over to scheme
                    (call-with-values
                        (lambda () (_dpower xc xe yc ye (+ p extra)))
                      (lambda (coeff exp)                 
-                       (if (modulo coeff
+                       (if (guile:modulo coeff
                                    (* 5 (expt 10 (- (len (str coeff)) p 1))))
                            (values coeff exp)
                            (lp (+ extra 3)))))))
@@ -3396,10 +3396,8 @@ This is the copyright information of the file ported over to scheme
 
        (twix
         (let (get-context context))
-
         ;; exp(NaN) = NaN
         (let ((ans ((ref self '_check_nans) #:context context))))
-            
         (ans it it)
 
         ;; exp(-Infinity) = 0
@@ -3472,7 +3470,6 @@ This is the copyright information of the file ported over to scheme
 
         ;; at this stage, ans should round correctly with *any*
         ;; rounding mode, not just with ROUND_HALF_EVEN
-       
         (let* ((context  ((ref context '_shallow_copy)))
                (rounding ((ref context '_set_rounding) ROUND_HALF_EVEN))
                (ans      ((ref ans '_fix) context)))
@@ -3585,36 +3582,33 @@ This is the copyright information of the file ported over to scheme
 
        (twix
         (let (get-context context))
-        (let () (pk 4 1))
         ;; ln(NaN) = NaN
         (let ((ans ((ref self '_check_nans) #:context context))))
-        (let () (pk 4 1.2 ans))
+
         (ans it it)
 
-        (let () (pk 4 2))
         ;; ln(0.0) == -Infinity
          ((not (bool self)) it
          _NegativeInfinity)
-        (let () (pk 4))
+
         ;; ln(Infinity) = Infinity
         ((= ((ref self '_isinfinity)) 1) it
           _Infinity)
-        (let () (pk 4 3))
+
         ;; ln(1.0) == 0.0
         ((equal? self _One) it
             _Zero)
-        (let () (pk 4 4))
+
         ;; ln(negative) raises InvalidOperation
         (if (= (ref self '_sign) 1)
          ((cx-error context) InvalidOperation
           "ln of a negative value"))
-        (let () (pk 4 5))
+
         ;; result is irrational, so necessarily inexact
         (let* ((op (_WorkRep self))
                (c  (ref op 'int))
                (e  (ref op 'exp))
                (p  (cx-prec context))))
-        (let () (pk 4 6))
 
         ;; correctly rounded result: repeatedly increase precision by 3
         ;; until we get an unambiguously roundable result
@@ -3623,10 +3617,8 @@ This is the copyright information of the file ported over to scheme
                          2)) ;; at least p+3 places
               (ans #f))
           (let lp ((places places))
-            (pk 'places places)
             (let ((coeff (_dlog c  e places)))
               ;; assert len(str(abs(coeff)))-p >= 1
-              (pk 'coeff coeff)
               (if (not (= (modulo coeff
                                   (* 5 (expt 10 (- (len (str (abs coeff)))
                                                    p 1))))
@@ -6228,7 +6220,7 @@ This is the copyright information of the file ported over to scheme
 
 ;;##### Integer arithmetic functions used by ln, log10, exp and __pow__ #####
 
-(define _nbits (ref int 'bit_length))
+(define _nbits py-bit-length)
 
 (define _decimal_lshift_exact
   (lambda (n e)
@@ -6322,7 +6314,7 @@ This is the copyright information of the file ported over to scheme
     ;; is actually an integer approximation to 2**R*y*M, where R is the
     ;; number of reductions performed so far.
 
-    ;; argument reduction; R = number of reductions performed
+    ;; argument reduction; R = number of reductions performed    
     (call-with-values
        (lambda ()
          (let lp ((y (- x M)) (R 0))
@@ -6380,7 +6372,6 @@ This is the copyright information of the file ported over to scheme
     "Given integers c, e and p with c > 0, compute an integer
     approximation to 10**p * log(c*10**e), with an absolute error of
     at most 1.  Assumes that c*10**e is not exactly 1."
-    (pk '_dlog)
     ;; Increase precision by 2. The precision increase is compensated
     ;; for at the end with a division by 100.
     (set! p (+ p 2))
@@ -6405,10 +6396,10 @@ This is the copyright information of the file ported over to scheme
                ;; p <= 0: just approximate the whole thing by 0; error < 2.31
                0))
        (lambda (log_d)
-         (pk 'log_d)
          (call-with-values
              (lambda ()
                ;; compute approximation to f*10**p*log(10), with error < 11.
+               (pk 'log_d log_d)
                (if (not (= f 0))
                    (let ((extra (- (len (str (abs f))) 1)))
                      (if (>= (+ p extra) 0)
@@ -6419,6 +6410,7 @@ This is the copyright information of the file ported over to scheme
                    0))
            (lambda (f_log_ten)
              ;; error in sum < 11+27 = 38; error after division < 0.38 + 0.5 < 1
+             (pk 'log_ten f_log_ten)
              (_div_nearest (+ f_log_ten log_d) 100))))))))
 
 (define-python-class _Log10Memoize ()
@@ -6484,11 +6476,13 @@ This is the copyright information of the file ported over to scheme
     ;; expm1(z/2**(R-1)), ... , exp(z/2), exp(z).
 
     ;; Find R such that x/2**R/M <= 2**-L
+
     (let ((R (_nbits (floor-quotient (ash x L) M))))
       ;; Taylor series.  (2**L)**T > M
       (let* ((T  (- (int (floor-quotient (* -10 (len (str M))) (* 3 L)))))
             (y1 (let ((Mshift (ash M R)))
                   (for ((i : (range (- T 1) 0 -1))) ((y (_div_nearest x T)))
+                       (pk 'y i y)
                        (_div_nearest (* x (+ Mshift y)) (* Mshift i))
                        #:final y)))
                                   
@@ -6498,7 +6492,7 @@ This is the copyright information of the file ported over to scheme
                      (let ((Mshift (ash M (+ k 2))))
                        (_div_nearest (* y (+ y Mshift)) Mshift))
                      #:final y)))
-
+       (pk '_iexp x M (+ M y2) R T y1)
        (+ M y2)))))
 
 (define _dexp
@@ -6515,14 +6509,13 @@ This is the copyright information of the file ported over to scheme
     digits of precision and with an error in d of at most 1.  This is
     almost, but not quite, the same as the error being < 1ulp: when d
     = 10**(p-1) the error could be up to 10 ulp."
-
     ;; we'll call iexp with M = 10**(p+2), giving p+3 digits of precision
     (set! p (+ p 2))
-
+    (pk '_dexp c e p)
     ;; compute log(10) with extra precision = adjusted exponent of c*10**e
     (let* ((extra (max 0 (+ e (len (str c)) -1)))
           (q     (+ p extra)))
-
+      
       ;; compute quotient c*10**e/(log(10)) = c*10**(e+q)/(log(10)*10**q),
       ;; rounding down
       (let* ((shift  (+ e q))
@@ -6555,7 +6548,7 @@ This is the copyright information of the file ported over to scheme
 
     We assume that: x is positive and not equal to 1, and y is nonzero.
     "
-
+    (pk xc xe yc ye p)
     (let*
        ;; Find b such that 10**(b-1) <= |y| <= 10**b
        ((b   (+ (len (str (abs yc))) ye))
@@ -6579,10 +6572,9 @@ This is the copyright information of the file ported over to scheme
          (call-with-values
              (lambda ()
                (_dexp ps (- (+ p 1)) (+ p 1)))
-           (lambda (coeff exp)
-             (values (_div_nearest coeff 10)
-                     (+ exp 1))))))))
-
+           (lambda (coeff exp)       
+             (values (pk 1 (_div_nearest coeff 10))
+                     (pk 2 (+ exp 1)))))))))
 (define _corr (dict '(("1" . 100) ("2" . 70) ("3" . 53) ("4" . 40) ("5" . 31)
                      ("6" . 23 ) ("7" . 16) ("8" . 10) ("9" . 5))))
 (define _log10_lb
diff --git a/modules/language/python/module/fnmatch.py b/modules/language/python/module/fnmatch.py
new file mode 100644 (file)
index 0000000..07c9924
--- /dev/null
@@ -0,0 +1,110 @@
+module(fnmatch)
+"""Filename matching with shell patterns.
+
+fnmatch(FILENAME, PATTERN) matches according to the local convention.
+fnmatchcase(FILENAME, PATTERN) always takes case in account.
+
+The functions operate by translating the pattern into a regular
+expression.  They cache the compiled regular expressions for speed.
+
+The function translate(PATTERN) returns a regular expression
+corresponding to PATTERN.  (It does not compile it.)
+"""
+import os
+import posixpath
+import re
+import functools
+
+__all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
+
+def fnmatch(name, pat):
+    """Test whether FILENAME matches PATTERN.
+
+    Patterns are Unix shell style:
+
+    *       matches everything
+    ?       matches any single character
+    [seq]   matches any character in seq
+    [!seq]  matches any char not in seq
+
+    An initial period in FILENAME is not special.
+    Both FILENAME and PATTERN are first case-normalized
+    if the operating system requires it.
+    If you don't want this, use fnmatchcase(FILENAME, PATTERN).
+    """
+    name = os.path.normcase(name)
+    pat = os.path.normcase(pat)
+    return fnmatchcase(name, pat)
+
+@functools.lru_cache(maxsize=256, typed=True)
+def _compile_pattern(pat):
+    if isinstance(pat, bytes):
+        pat_str = str(pat, 'ISO-8859-1')
+        res_str = translate(pat_str)
+        res = bytes(res_str, 'ISO-8859-1')
+    else:
+        res = translate(pat)
+    return re.compile(res).match
+
+def filter(names, pat):
+    """Return the subset of the list NAMES that match PAT."""
+    result = []
+    pat = os.path.normcase(pat)
+    match = _compile_pattern(pat)
+    if os.path is posixpath:
+        # normcase on posix is NOP. Optimize it away from the loop.
+        for name in names:
+            if match(name):
+                result.append(name)
+    else:
+        for name in names:
+            if match(os.path.normcase(name)):
+                result.append(name)
+    return result
+
+def fnmatchcase(name, pat):
+    """Test whether FILENAME matches PATTERN, including case.
+
+    This is a version of fnmatch() which doesn't case-normalize
+    its arguments.
+    """
+    match = _compile_pattern(pat)
+    return match(name) is not None
+
+
+def translate(pat):
+    """Translate a shell PATTERN to a regular expression.
+
+    There is no way to quote meta-characters.
+    """
+
+    i, n = 0, len(pat)
+    res = ''
+    while i < n:
+        c = pat[i]
+        i = i+1
+        if c == '*':
+            res = res + '.*'
+        elif c == '?':
+            res = res + '.'
+        elif c == '[':
+            j = i
+            if j < n and pat[j] == '!':
+                j = j+1
+            if j < n and pat[j] == ']':
+                j = j+1
+            while j < n and pat[j] != ']':
+                j = j+1
+            if j >= n:
+                res = res + '\\['
+            else:
+                stuff = pat[i:j].replace('\\','\\\\')
+                i = j+1
+                if stuff[0] == '!':
+                    stuff = '^' + stuff[1:]
+                elif stuff[0] == '^':
+                    stuff = '\\' + stuff
+                res = '%s[%s]' % (res, stuff)
+        else:
+            res = res + re.escape(c)
+    return r'(?s:%s)\Z' % res
index f0ddf299c7169abad526eeb257fb58a012351eb8..c065900412e7b86217fa90dfae3aab58a91534e6 100644 (file)
@@ -63,7 +63,8 @@
         (lambda ()
           (let ((value (getattr wrapped attr)))
             (setattr wrapper attr value)))
-        (#:except AttributeError => values)))
+        (#:except #t
+         (setattr wrapper attr None))))
                 
     (for ((attr : updated)) ()
         (py-update (getattr wrapper attr) (getattr wrapped attr (dict))))
diff --git a/modules/language/python/module/genericpath.py b/modules/language/python/module/genericpath.py
new file mode 100644 (file)
index 0000000..226e0b3
--- /dev/null
@@ -0,0 +1,153 @@
+module(genericpath)
+
+"""
+Path operations common to more than one OS
+Do not use directly.  The OS specific modules import the appropriate
+functions from this module themselves.
+"""
+import os
+import stat
+
+__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
+           'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
+           'samestat']
+
+
+# Does a path exist?
+# This is false for dangling symbolic links on systems that support them.
+def exists(path):
+    """Test whether a path exists.  Returns False for broken symbolic links"""
+    try:
+        os.stat(path)
+    except OSError:
+        return False
+    return True
+
+
+# This follows symbolic links, so both islink() and isdir() can be true
+# for the same path on systems that support symlinks
+def isfile(path):
+    """Test whether a path is a regular file"""
+    try:
+        st = os.stat(path)
+    except OSError:
+        return False
+    return stat.S_ISREG(st.st_mode)
+
+
+# Is a path a directory?
+# This follows symbolic links, so both islink() and isdir()
+# can be true for the same path on systems that support symlinks
+def isdir(s):
+    """Return true if the pathname refers to an existing directory."""
+    try:
+        st = os.stat(s)
+    except OSError:
+        return False
+    return stat.S_ISDIR(st.st_mode)
+
+
+def getsize(filename):
+    """Return the size of a file, reported by os.stat()."""
+    return os.stat(filename).st_size
+
+
+def getmtime(filename):
+    """Return the last modification time of a file, reported by os.stat()."""
+    return os.stat(filename).st_mtime
+
+
+def getatime(filename):
+    """Return the last access time of a file, reported by os.stat()."""
+    return os.stat(filename).st_atime
+
+
+def getctime(filename):
+    """Return the metadata change time of a file, reported by os.stat()."""
+    return os.stat(filename).st_ctime
+
+
+# Return the longest prefix of all list elements.
+def commonprefix(m):
+    "Given a list of pathnames, returns the longest common leading component"
+    if not m: return ''
+    # Some people pass in a list of pathname parts to operate in an OS-agnostic
+    # fashion; don't try to translate in that case as that's an abuse of the
+    # API and they are already doing what they need to be OS-agnostic and so
+    # they most likely won't be using an os.PathLike object in the sublists.
+    if not isinstance(m[0], (list, tuple)):
+        m = tuple(map(os.fspath, m))
+    s1 = min(m)
+    s2 = max(m)
+    for i, c in enumerate(s1):
+        if c != s2[i]:
+            return s1[:i]
+    return s1
+
+# Are two stat buffers (obtained from stat, fstat or lstat)
+# describing the same file?
+def samestat(s1, s2):
+    """Test whether two stat buffers reference the same file"""
+    return (s1.st_ino == s2.st_ino and
+            s1.st_dev == s2.st_dev)
+
+
+# Are two filenames really pointing to the same file?
+def samefile(f1, f2):
+    """Test whether two pathnames reference the same actual file"""
+    s1 = os.stat(f1)
+    s2 = os.stat(f2)
+    return samestat(s1, s2)
+
+
+# Are two open files really referencing the same file?
+# (Not necessarily the same file descriptor!)
+def sameopenfile(fp1, fp2):
+    """Test whether two open file objects reference the same file"""
+    s1 = os.fstat(fp1)
+    s2 = os.fstat(fp2)
+    return samestat(s1, s2)
+
+
+# Split a path in root and extension.
+# The extension is everything starting at the last dot in the last
+# pathname component; the root is everything before that.
+# It is always true that root + ext == p.
+
+# Generic implementation of splitext, to be parametrized with
+# the separators
+def _splitext(p, sep, altsep, extsep):
+    """Split the extension from a pathname.
+
+    Extension is everything from the last dot to the end, ignoring
+    leading dots.  Returns "(root, ext)"; ext may be empty."""
+    # NOTE: This code must work for text and bytes strings.
+
+    sepIndex = p.rfind(sep)
+    if altsep:
+        altsepIndex = p.rfind(altsep)
+        sepIndex = max(sepIndex, altsepIndex)
+
+    dotIndex = p.rfind(extsep)
+    if dotIndex > sepIndex:
+        # skip all leading dots
+        filenameIndex = sepIndex + 1
+        while filenameIndex < dotIndex:
+            if p[filenameIndex:filenameIndex+1] != extsep:
+                return p[:dotIndex], p[dotIndex:]
+            filenameIndex += 1
+
+    return p, p[:0]
+
+def _check_arg_types(funcname, *args):
+    hasstr = hasbytes = False
+    for s in args:
+        if isinstance(s, str):
+            hasstr = True
+        elif isinstance(s, bytes):
+            hasbytes = True
+        else:
+            raise TypeError('%s() argument must be str or bytes, not %r' %
+                            (funcname, s.__class__.__name__)) from None
+    if hasstr and hasbytes:
+        raise TypeError("Can't mix strings and bytes in path components") from None
diff --git a/modules/language/python/module/glob.py b/modules/language/python/module/glob.py
new file mode 100644 (file)
index 0000000..9b3efe2
--- /dev/null
@@ -0,0 +1,173 @@
+module(glob)
+
+"""Filename globbing utility."""
+
+import os
+import re
+import fnmatch
+
+__all__ = ["glob", "iglob", "escape"]
+
+def glob(pathname, *, recursive=False):
+    """Return a list of paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    return list(iglob(pathname, recursive=recursive))
+
+def iglob(pathname, *, recursive=False):
+    """Return an iterator which yields the paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    it = _iglob(pathname, recursive, False)
+    if recursive and _isrecursive(pathname):
+        s = next(it)  # skip empty string
+        assert not s
+    return it
+
+def _iglob(pathname, recursive, dironly):
+    dirname, basename = os.path.split(pathname)
+    if not has_magic(pathname):
+        assert not dironly
+        if basename:
+            if os.path.lexists(pathname):
+                yield pathname
+        else:
+            # Patterns ending with a slash should match only directories
+            if os.path.isdir(dirname):
+                yield pathname
+        return
+    if not dirname:
+        if recursive and _isrecursive(basename):
+            yield from _glob2(dirname, basename, dironly)
+        else:
+            yield from _glob1(dirname, basename, dironly)
+        return
+    # `os.path.split()` returns the argument itself as a dirname if it is a
+    # drive or UNC path.  Prevent an infinite recursion if a drive or UNC path
+    # contains magic characters (i.e. r'\\?\C:').
+    if dirname != pathname and has_magic(dirname):
+        dirs = _iglob(dirname, recursive, True)
+    else:
+        dirs = [dirname]
+    if has_magic(basename):
+        if recursive and _isrecursive(basename):
+            glob_in_dir = _glob2
+        else:
+            glob_in_dir = _glob1
+    else:
+        glob_in_dir = _glob0
+    for dirname in dirs:
+        for name in glob_in_dir(dirname, basename, dironly):
+            yield os.path.join(dirname, name)
+
+# These 2 helper functions non-recursively glob inside a literal directory.
+# They return a list of basenames.  _glob1 accepts a pattern while _glob0
+# takes a literal basename (so it only has to check for its existence).
+
+def _glob1(dirname, pattern, dironly):
+    names = list(_iterdir(dirname, dironly))
+    if not _ishidden(pattern):
+        names = (x for x in names if not _ishidden(x))
+    return fnmatch.filter(names, pattern)
+
+def _glob0(dirname, basename, dironly):
+    if not basename:
+        # `os.path.split()` returns an empty basename for paths ending with a
+        # directory separator.  'q*x/' should match only directories.
+        if os.path.isdir(dirname):
+            return [basename]
+    else:
+        if os.path.lexists(os.path.join(dirname, basename)):
+            return [basename]
+    return []
+
+# Following functions are not public but can be used by third-party code.
+
+def glob0(dirname, pattern):
+    return _glob0(dirname, pattern, False)
+
+def glob1(dirname, pattern):
+    return _glob1(dirname, pattern, False)
+
+# This helper function recursively yields relative pathnames inside a literal
+# directory.
+
+def _glob2(dirname, pattern, dironly):
+    assert _isrecursive(pattern)
+    yield pattern[:0]
+    yield from _rlistdir(dirname, dironly)
+
+# If dironly is false, yields all file names inside a directory.
+# If dironly is true, yields only directory names.
+def _iterdir(dirname, dironly):
+    if not dirname:
+        if isinstance(dirname, bytes):
+            dirname = bytes(os.curdir, 'ASCII')
+        else:
+            dirname = os.curdir
+    try:
+        with os.scandir(dirname) as it:
+            for entry in it:
+                try:
+                    if not dironly or entry.is_dir():
+                        yield entry.name
+                except OSError:
+                    pass
+    except OSError:
+        return
+
+# Recursively yields relative pathnames inside a literal directory.
+def _rlistdir(dirname, dironly):
+    names = list(_iterdir(dirname, dironly))
+    for x in names:
+        if not _ishidden(x):
+            yield x
+            path = os.path.join(dirname, x) if dirname else x
+            for y in _rlistdir(path, dironly):
+                yield os.path.join(x, y)
+
+
+magic_check = re.compile('([*?[])')
+magic_check_bytes = re.compile(b'([*?[])')
+
+def has_magic(s):
+    if isinstance(s, bytes):
+        match = magic_check_bytes.search(s)
+    else:
+        match = magic_check.search(s)
+    return match is not None
+
+def _ishidden(path):
+    return path[0] in ('.', b'.'[0])
+
+def _isrecursive(pattern):
+    if isinstance(pattern, bytes):
+        return pattern == b'**'
+    else:
+        return pattern == '**'
+
+def escape(pathname):
+    """Escape all special characters.
+    """
+    # Escaping is done by wrapping any of "*?[" between square brackets.
+    # Metacharacters do not work in the drive part and shouldn't be escaped.
+    drive, pathname = os.path.splitdrive(pathname)
+    if isinstance(pathname, bytes):
+        pathname = magic_check_bytes.sub(br'[\1]', pathname)
+    else:
+        pathname = magic_check.sub(r'[\1]', pathname)
+    return drive + pathname
diff --git a/modules/language/python/module/posixpath.py b/modules/language/python/module/posixpath.py
new file mode 100644 (file)
index 0000000..1a3198b
--- /dev/null
@@ -0,0 +1,521 @@
+module(posixpath)
+"""Common operations on Posix pathnames.
+
+Instead of importing this module directly, import os and refer to
+this module as os.path.  The "os.path" name is an alias for this
+module on Posix systems; on other systems (e.g. Mac, Windows),
+os.path provides the same operations in a manner specific to that
+platform, and is an alias to another module (e.g. macpath, ntpath).
+
+Some of this can actually be useful on non-Posix systems too, e.g.
+for manipulation of the pathname component of URLs.
+"""
+
+import os
+import sys
+import stat
+import genericpath
+from genericpath import *
+
+__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
+           "basename","dirname","commonprefix","getsize","getmtime",
+           "getatime","getctime","islink","exists","lexists","isdir","isfile",
+           "ismount", "expanduser","expandvars","normpath","abspath",
+           "samefile","sameopenfile","samestat",
+           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
+           "devnull","realpath","supports_unicode_filenames","relpath",
+           "commonpath"]
+
+# Strings representing various path-related bits and pieces.
+# These are primarily for export; internally, they are hardcoded.
+curdir = '.'
+pardir = '..'
+extsep = '.'
+sep = '/'
+pathsep = ':'
+defpath = ':/bin:/usr/bin'
+altsep = None
+devnull = '/dev/null'
+
+def _get_sep(path):
+    if isinstance(path, bytes):
+        return b'/'
+    else:
+        return '/'
+
+# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
+# On MS-DOS this may also turn slashes into backslashes; however, other
+# normalizations (such as optimizing '../' away) are not allowed
+# (another function should be defined to do that).
+
+def normcase(s):
+    """Normalize case of pathname.  Has no effect under Posix"""
+    s = os.fspath(s)
+    if not isinstance(s, (bytes, str)):
+        raise TypeError("normcase() argument must be str or bytes, "
+                        "not '{}'".format(s.__class__.__name__))
+    return s
+
+
+# Return whether a path is absolute.
+# Trivial in Posix, harder on the Mac or MS-DOS.
+
+def isabs(s):
+    """Test whether a path is absolute"""
+    s = os.fspath(s)
+    sep = _get_sep(s)
+    return s.startswith(sep)
+
+
+# Join pathnames.
+# Ignore the previous parts if a part is absolute.
+# Insert a '/' unless the first part is empty or already ends in '/'.
+
+def join(a, *p):
+    """Join two or more pathname components, inserting '/' as needed.
+    If any component is an absolute path, all previous path components
+    will be discarded.  An empty last part will result in a path that
+    ends with a separator."""
+    a = os.fspath(a)
+    sep = _get_sep(a)
+    path = a
+    try:
+        if not p:
+            path[:0] + sep  #23780: Ensure compatible data type even if p is null.
+        for b in map(os.fspath, p):
+            if b.startswith(sep):
+                path = b
+            elif not path or path.endswith(sep):
+                path += b
+            else:
+                path += sep + b
+    except (TypeError, AttributeError, BytesWarning):
+        genericpath._check_arg_types('join', a, *p)
+        raise
+    return path
+
+
+# Split a path in head (everything up to the last '/') and tail (the
+# rest).  If the path ends in '/', tail will be empty.  If there is no
+# '/' in the path, head  will be empty.
+# Trailing '/'es are stripped from head unless it is the root.
+
+def split(p):
+    """Split a pathname.  Returns tuple "(head, tail)" where "tail" is
+    everything after the final slash.  Either part may be empty."""
+    p = os.fspath(p)
+    sep = _get_sep(p)
+    i = p.rfind(sep) + 1
+    head, tail = p[:i], p[i:]
+    if head and head != sep*len(head):
+        head = head.rstrip(sep)
+    return head, tail
+
+
+# Split a path in root and extension.
+# The extension is everything starting at the last dot in the last
+# pathname component; the root is everything before that.
+# It is always true that root + ext == p.
+
+def splitext(p):
+    p = os.fspath(p)
+    if isinstance(p, bytes):
+        sep = b'/'
+        extsep = b'.'
+    else:
+        sep = '/'
+        extsep = '.'
+    return genericpath._splitext(p, sep, None, extsep)
+#splitext.__doc__ = genericpath._splitext.__doc__
+
+# Split a pathname into a drive specification and the rest of the
+# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.
+
+def splitdrive(p):
+    """Split a pathname into drive and path. On Posix, drive is always
+    empty."""
+    p = os.fspath(p)
+    return p[:0], p
+
+
+# Return the tail (basename) part of a path, same as split(path)[1].
+
+def basename(p):
+    """Returns the final component of a pathname"""
+    p = os.fspath(p)
+    sep = _get_sep(p)
+    i = p.rfind(sep) + 1
+    return p[i:]
+
+
+# Return the head (dirname) part of a path, same as split(path)[0].
+
+def dirname(p):
+    """Returns the directory component of a pathname"""
+    p = os.fspath(p)
+    sep = _get_sep(p)
+    i = p.rfind(sep) + 1
+    head = p[:i]
+    if head and head != sep*len(head):
+        head = head.rstrip(sep)
+    return head
+
+
+# Is a path a symbolic link?
+# This will always return false on systems where os.lstat doesn't exist.
+
+def islink(path):
+    """Test whether a path is a symbolic link"""
+    try:
+        st = os.lstat(path)
+    except (OSError, AttributeError):
+        return False
+    return stat.S_ISLNK(st.st_mode)
+
+# Being true for dangling symbolic links is also useful.
+
+def lexists(path):
+    """Test whether a path exists.  Returns True for broken symbolic links"""
+    try:
+        os.lstat(path)
+    except OSError:
+        return False
+    return True
+
+
+# Is a path a mount point?
+# (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)
+
+def ismount(path):
+    """Test whether a path is a mount point"""
+    try:
+        s1 = os.lstat(path)
+    except OSError:
+        # It doesn't exist -- so not a mount point. :-)
+        return False
+    else:
+        # A symlink can never be a mount point
+        if stat.S_ISLNK(s1.st_mode):
+            return False
+
+    if isinstance(path, bytes):
+        parent = join(path, b'..')
+    else:
+        parent = join(path, '..')
+    parent = realpath(parent)
+    try:
+        s2 = os.lstat(parent)
+    except OSError:
+        return False
+
+    dev1 = s1.st_dev
+    dev2 = s2.st_dev
+    if dev1 != dev2:
+        return True     # path/.. on a different device as path
+    ino1 = s1.st_ino
+    ino2 = s2.st_ino
+    if ino1 == ino2:
+        return True     # path/.. is the same i-node as path
+    return False
+
+
+# Expand paths beginning with '~' or '~user'.
+# '~' means $HOME; '~user' means that user's home directory.
+# If the path doesn't begin with '~', or if the user or $HOME is unknown,
+# the path is returned unchanged (leaving error reporting to whatever
+# function is called with the expanded path as argument).
+# See also module 'glob' for expansion of *, ? and [...] in pathnames.
+# (A function should also be defined to do full *sh-style environment
+# variable expansion.)
+
+def expanduser(path):
+    """Expand ~ and ~user constructions.  If user or $HOME is unknown,
+    do nothing."""
+    path = os.fspath(path)
+    if isinstance(path, bytes):
+        tilde = b'~'
+    else:
+        tilde = '~'
+    if not path.startswith(tilde):
+        return path
+    sep = _get_sep(path)
+    i = path.find(sep, 1)
+    if i < 0:
+        i = len(path)
+    if i == 1:
+        if 'HOME' not in os.environ:
+            import pwd
+            userhome = pwd.getpwuid(os.getuid()).pw_dir
+        else:
+            userhome = os.environ['HOME']
+    else:
+        import pwd
+        name = path[1:i]
+        if isinstance(name, bytes):
+            name = str(name, 'ASCII')
+        try:
+            pwent = pwd.getpwnam(name)
+        except KeyError:
+            return path
+        userhome = pwent.pw_dir
+    if isinstance(path, bytes):
+        userhome = os.fsencode(userhome)
+        root = b'/'
+    else:
+        root = '/'
+    userhome = userhome.rstrip(root)
+    return (userhome + path[i:]) or root
+
+
+# Expand paths containing shell variable substitutions.
+# This expands the forms $variable and ${variable} only.
+# Non-existent variables are left unchanged.
+
+_varprog = None
+_varprogb = None
+
+def expandvars(path):
+    """Expand shell variables of form $var and ${var}.  Unknown variables
+    are left unchanged."""
+    path = os.fspath(path)
+    global _varprog, _varprogb
+    if isinstance(path, bytes):
+        if b'$' not in path:
+            return path
+        if not _varprogb:
+            import re
+            _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
+        search = _varprogb.search
+        start = b'{'
+        end = b'}'
+        environ = getattr(os, 'environb', None)
+    else:
+        if '$' not in path:
+            return path
+        if not _varprog:
+            import re
+            _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
+        search = _varprog.search
+        start = '{'
+        end = '}'
+        environ = os.environ
+    i = 0
+    while True:
+        m = search(path, i)
+        if not m:
+            break
+        i, j = m.span(0)
+        name = m.group(1)
+        if name.startswith(start) and name.endswith(end):
+            name = name[1:-1]
+        try:
+            if environ is None:
+                value = os.fsencode(os.environ[os.fsdecode(name)])
+            else:
+                value = environ[name]
+        except KeyError:
+            i = j
+        else:
+            tail = path[j:]
+            path = path[:i] + value
+            i = len(path)
+            path += tail
+    return path
+
+
+# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
+# It should be understood that this may change the meaning of the path
+# if it contains symbolic links!
+
+def normpath(path):
+    """Normalize path, eliminating double slashes, etc."""
+    path = os.fspath(path)
+    if isinstance(path, bytes):
+        sep = b'/'
+        empty = b''
+        dot = b'.'
+        dotdot = b'..'
+    else:
+        sep = '/'
+        empty = ''
+        dot = '.'
+        dotdot = '..'
+    if path == empty:
+        return dot
+    initial_slashes = path.startswith(sep)
+    # POSIX allows one or two initial slashes, but treats three or more
+    # as single slash.
+    if (initial_slashes and
+        path.startswith(sep*2) and not path.startswith(sep*3)):
+        initial_slashes = 2
+    comps = path.split(sep)
+    new_comps = []
+    for comp in comps:
+        if comp in (empty, dot):
+            continue
+        if (comp != dotdot or (not initial_slashes and not new_comps) or
+             (new_comps and new_comps[-1] == dotdot)):
+            new_comps.append(comp)
+        elif new_comps:
+            new_comps.pop()
+    comps = new_comps
+    path = sep.join(comps)
+    if initial_slashes:
+        path = sep*initial_slashes + path
+    return path or dot
+
+
+def abspath(path):
+    """Return an absolute path."""
+    path = os.fspath(path)
+    if not isabs(path):
+        if isinstance(path, bytes):
+            cwd = os.getcwdb()
+        else:
+            cwd = os.getcwd()
+        path = join(cwd, path)
+    return normpath(path)
+
+
+# Return a canonical path (i.e. the absolute location of a file on the
+# filesystem).
+
+def realpath(filename):
+    """Return the canonical path of the specified filename, eliminating any
+symbolic links encountered in the path."""
+    filename = os.fspath(filename)
+    path, ok = _joinrealpath(filename[:0], filename, {})
+    return abspath(path)
+
+# Join two paths, normalizing and eliminating any symbolic links
+# encountered in the second path.
+def _joinrealpath(path, rest, seen):
+    if isinstance(path, bytes):
+        sep = b'/'
+        curdir = b'.'
+        pardir = b'..'
+    else:
+        sep = '/'
+        curdir = '.'
+        pardir = '..'
+
+    if isabs(rest):
+        rest = rest[1:]
+        path = sep
+
+    while rest:
+        name, _, rest = rest.partition(sep)
+        if not name or name == curdir:
+            # current dir
+            continue
+        if name == pardir:
+            # parent dir
+            if path:
+                path, name = split(path)
+                if name == pardir:
+                    path = join(path, pardir, pardir)
+            else:
+                path = pardir
+            continue
+        newpath = join(path, name)
+        if not islink(newpath):
+            path = newpath
+            continue
+        # Resolve the symbolic link
+        if newpath in seen:
+            # Already seen this path
+            path = seen[newpath]
+            if path is not None:
+                # use cached value
+                continue
+            # The symlink is not resolved, so we must have a symlink loop.
+            # Return already resolved part + rest of the path unchanged.
+            return join(newpath, rest), False
+        seen[newpath] = None # not resolved symlink
+        path, ok = _joinrealpath(path, os.readlink(newpath), seen)
+        if not ok:
+            return join(path, rest), False
+        seen[newpath] = path # resolved symlink
+
+    return path, True
+
+
+supports_unicode_filenames = (sys.platform == 'darwin')
+
+def relpath(path, start=None):
+    """Return a relative version of a path"""
+
+    if not path:
+        raise ValueError("no path specified")
+
+    path = os.fspath(path)
+    if isinstance(path, bytes):
+        curdir = b'.'
+        sep = b'/'
+        pardir = b'..'
+    else:
+        curdir = '.'
+        sep = '/'
+        pardir = '..'
+
+    if start is None:
+        start = curdir
+    else:
+        start = os.fspath(start)
+
+    try:
+        start_list = [x for x in abspath(start).split(sep) if x]
+        path_list = [x for x in abspath(path).split(sep) if x]
+        # Work out how much of the filepath is shared by start and path.
+        i = len(commonprefix([start_list, path_list]))
+
+        rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+        if not rel_list:
+            return curdir
+        return join(*rel_list)
+    except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
+        genericpath._check_arg_types('relpath', path, start)
+        raise
+
+
+# Return the longest common sub-path of the sequence of paths given as input.
+# The paths are not normalized before comparing them (this is the
+# responsibility of the caller). Any trailing separator is stripped from the
+# returned path.
+
+def commonpath(paths):
+    """Given a sequence of path names, returns the longest common sub-path."""
+
+    if not paths:
+        raise ValueError('commonpath() arg is an empty sequence')
+
+    paths = tuple(map(os.fspath, paths))
+    if isinstance(paths[0], bytes):
+        sep = b'/'
+        curdir = b'.'
+    else:
+        sep = '/'
+        curdir = '.'
+
+    try:
+        split_paths = [path.split(sep) for path in paths]
+
+        try:
+            isabs, = set(p[:1] == sep for p in paths)
+        except ValueError:
+            raise ValueError("Can't mix absolute and relative paths") from None
+
+        split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
+        s1 = min(split_paths)
+        s2 = max(split_paths)
+        common = s1
+        for i, c in enumerate(s1):
+            if c != s2[i]:
+                common = s1[:i]
+                break
+
+        prefix = sep if isabs else sep[:0]
+        return prefix + sep.join(common)
+    except (TypeError, AttributeError):
+        genericpath._check_arg_types('commonpath', *paths)
+        raise
index 95965b8070f89b4d7b168ecc0d5435b792374162..b51cd6b2f999abe5d1db23a82b72b50225d91ad2 100644 (file)
@@ -15,6 +15,7 @@
                    py-as-integer-ratio py-conjugate py-fromhex py-hex py-imag
                    py-is-integer py-real hex py-bin py-index
                   py-ifloordiv py-ilshift py-imod py-imul py-imatmul
+                  py-bit-length
                   py-ilogior py-ilogand py-ipow py-isub py-i/
                   py-irshift py-ilogxor))
 
 (mk-unop i0 py-lognot __invert__)
 
 (define-method (py-bit-length (i <integer>))
-  (logcount i))
+  (integer-length (abs i)))
 
 (define-method (py-conjugate (i <complex>))
   (make-rectangular (real-part i) (- (imag-part i))))
index 5941469697131c1d952fe6acb0ca9446ec8b4059..8cc2741008efd820f078668c27e41be24f8d598d 100644 (file)
@@ -47,7 +47,7 @@
         (let* ((a  (ref self '_a))
                (b  (ref self '_b))
                (c  (ref self '_c))
-               (aa (if (> c 0) a (- a 1)))
+               (aa (if (> c 0) a a))
                (op (if (> c 0) < >)))
           (let lp ((i aa))
             (if (op i b)