summaryrefslogtreecommitdiff
path: root/modules/language/python
diff options
context:
space:
mode:
Diffstat (limited to 'modules/language/python')
-rw-r--r--modules/language/python/compile.scm1
-rw-r--r--modules/language/python/exceptions.scm29
-rw-r--r--modules/language/python/module.scm10
-rw-r--r--modules/language/python/module/_python.scm1
-rw-r--r--modules/language/python/module/decimal.scm116
-rw-r--r--modules/language/python/module/fnmatch.py110
-rw-r--r--modules/language/python/module/functools.scm3
-rw-r--r--modules/language/python/module/genericpath.py153
-rw-r--r--modules/language/python/module/glob.py173
-rw-r--r--modules/language/python/module/posixpath.py521
-rw-r--r--modules/language/python/number.scm3
-rw-r--r--modules/language/python/range.scm2
12 files changed, 1051 insertions, 71 deletions
diff --git a/modules/language/python/compile.scm b/modules/language/python/compile.scm
index ba07241..b2f5ea7 100644
--- a/modules/language/python/compile.scm
+++ b/modules/language/python/compile.scm
@@ -379,6 +379,7 @@
((__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))
diff --git a/modules/language/python/exceptions.scm b/modules/language/python/exceptions.scm
index 4ce744a..990f9c3 100644
--- a/modules/language/python/exceptions.scm
+++ b/modules/language/python/exceptions.scm
@@ -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)))
@@ -31,6 +32,22 @@
(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)
@@ -77,3 +94,13 @@
+
+(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)
diff --git a/modules/language/python/module.scm b/modules/language/python/module.scm
index 87f9207..52ec88b 100644
--- a/modules/language/python/module.scm
+++ b/modules/language/python/module.scm
@@ -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?)
@@ -123,7 +123,7 @@
(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
@@ -133,10 +133,10 @@
(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__
diff --git a/modules/language/python/module/_python.scm b/modules/language/python/module/_python.scm
index 47df9fc..2fac0a1 100644
--- a/modules/language/python/module/_python.scm
+++ b/modules/language/python/module/_python.scm
@@ -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
diff --git a/modules/language/python/module/decimal.scm b/modules/language/python/module/decimal.scm
index 4f2f6fe..0778da1 100644
--- a/modules/language/python/module/decimal.scm
+++ b/modules/language/python/module/decimal.scm
@@ -50,7 +50,13 @@
;; 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
index 0000000..07c9924
--- /dev/null
+++ b/modules/language/python/module/fnmatch.py
@@ -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
diff --git a/modules/language/python/module/functools.scm b/modules/language/python/module/functools.scm
index f0ddf29..c065900 100644
--- a/modules/language/python/module/functools.scm
+++ b/modules/language/python/module/functools.scm
@@ -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
index 0000000..226e0b3
--- /dev/null
+++ b/modules/language/python/module/genericpath.py
@@ -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
index 0000000..9b3efe2
--- /dev/null
+++ b/modules/language/python/module/glob.py
@@ -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
index 0000000..1a3198b
--- /dev/null
+++ b/modules/language/python/module/posixpath.py
@@ -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
diff --git a/modules/language/python/number.scm b/modules/language/python/number.scm
index 95965b8..b51cd6b 100644
--- a/modules/language/python/number.scm
+++ b/modules/language/python/number.scm
@@ -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))
@@ -244,7 +245,7 @@
(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))))
diff --git a/modules/language/python/range.scm b/modules/language/python/range.scm
index 5941469..8cc2741 100644
--- a/modules/language/python/range.scm
+++ b/modules/language/python/range.scm
@@ -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)