From b4a777ec60bee2fecdbeb0396afcfde30d4e5e62 Mon Sep 17 00:00:00 2001 From: "E. Madison Bray" Date: Fri, 17 Jan 2020 16:59:59 +0000 Subject: [PATCH] Allow Sage to work with a system Python 3.6. Currently sage-the-distribution is tested against a minimum of Python 3.7, but we can support more system Pythons by supporting down to 3.6 with some minimal fixes to tests. --- build/bin/sage-spkg | 4 +++- build/pkgs/python3/spkg-configure.m4 | 8 ++++---- src/bin/sage-env-config.in | 8 ++++++++ src/sage/all.py | 2 +- src/sage/combinat/subset.py | 17 ++++++++++++++++- src/sage/graphs/views.pyx | 1 + src/sage/misc/sagedoc.py | 2 +- src/sage/misc/sageinspect.py | 6 ++---- src/sage/symbolic/expression.pyx | 8 ++------ 9 files changed, 38 insertions(+), 18 deletions(-) diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 94c22cf715e..ac5166b777a 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -64,7 +64,9 @@ #***************************************************************************** # Avoid surprises with character ranges [a-z] in regular expressions -export LC_ALL=C +# See Trac #15791; some locales can produce different results for +# character ranges (use C.UTF-8 to ensure UTF-8 default encoding in Python) +export LC_ALL=C.UTF-8 usage() { diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 39baa4ec64c..5fbc3161eb0 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,14 +1,14 @@ SAGE_SPKG_CONFIGURE([python3], [ SAGE_SPKG_DEPCHECK([sqlite libpng bzip2 xz libffi], [ check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" - AC_CACHE_CHECK([for python3 >= 3.7, < 3.8 with sqlite3 module], [ac_cv_path_PYTHON3], [ + AC_CACHE_CHECK([for python3 >= 3.6, < 3.8 with sqlite3 module], [ac_cv_path_PYTHON3], [ AC_MSG_RESULT([]) - AC_PATH_PROGS_FEATURE_CHECK([PYTHON3], [python3.7 python3], [ + AC_PATH_PROGS_FEATURE_CHECK([PYTHON3], [python3.7 python3.6 python3], [ AC_MSG_CHECKING([... whether $ac_path_PYTHON3 is good]) python3_version=`"$ac_path_PYTHON3" --version 2>&1 \ | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` AS_IF([test -n "$python3_version"], [ - AX_COMPARE_VERSION([$python3_version], [ge], [3.7.0], [ + AX_COMPARE_VERSION([$python3_version], [ge], [3.6.0], [ AX_COMPARE_VERSION([$python3_version], [lt], [3.8.0], [ AS_IF(["$ac_path_PYTHON3" -S -c "import $check_modules"], [ AC_LANG_PUSH([C]) @@ -51,7 +51,7 @@ EOF ac_path_PYTHON3_found=: AC_MSG_RESULT([yes]) dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK - AC_MSG_CHECKING([for python3 >= 3.7, < 3.8 with modules $check_modules]) + AC_MSG_CHECKING([for python3 >= 3.6, < 3.8 with modules $check_modules]) ], [ AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build an extension]) ]) diff --git a/src/bin/sage-env-config.in b/src/bin/sage-env-config.in index 4319a1d8962..b9f38deebb8 100644 --- a/src/bin/sage-env-config.in +++ b/src/bin/sage-env-config.in @@ -38,6 +38,14 @@ fi export SAGE_SYSTEM_PYTHON="@SAGE_SYSTEM_PYTHON@" +if [ -n "$SAGE_SYSTEM_PYTHON" ]; then + # Force LC_CTYPE to a UTF-8 locale to ensure that Python uses UTF-8 by + # default (on Python 3.7+ this is not necessary as UTF-8 is always the + # default encoding, so we only need this if using a system Python that + # is less than 3.7, since Sage's Python is 3.7+) + export LC_CTYPE="C.UTF-8" +fi + # This is usually blank if the system GMP is used, or $SAGE_LOCAL otherwise export SAGE_GMP_PREFIX="@SAGE_GMP_PREFIX@" export SAGE_GMP_INCLUDE="@SAGE_GMP_INCLUDE@" diff --git a/src/sage/all.py b/src/sage/all.py index 460adc79968..a72cd0478a1 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -22,7 +22,7 @@ ....: 'IPython', 'prompt_toolkit', 'jedi', # sage dependencies ....: 'threading', 'multiprocessing', # doctest dependencies ....: '__main__', 'sage.doctest', # doctesting - ....: 'signal', 'enum', # may appear in Python 3 + ....: 'signal', 'enum', 'types' # may appear in Python 3 ....: ] sage: def is_not_allowed(frame): ....: module = inspect.getmodule(frame) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index b4278891e23..d63bef12c95 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -359,7 +359,22 @@ def cardinality(self): """ return Integer(1) << self._s.cardinality() - __len__ = cardinality + def __len__(self): + r""" + Equivalent to ``self.cardinality()``. + + TESTS:: + + ``__len__`` should return a Python int; in Python 3.7+ this happens + automatically, but not on Python 3.6. + + sage: S = Subsets(Set([1,2,3])) + sage: len(S) + 8 + sage: type(len(S)) is int + True + """ + return int(self.cardinality()) def first(self): """ diff --git a/src/sage/graphs/views.pyx b/src/sage/graphs/views.pyx index 22e82619b1e..3a8877d3900 100644 --- a/src/sage/graphs/views.pyx +++ b/src/sage/graphs/views.pyx @@ -601,6 +601,7 @@ cdef class EdgesView: elif i < 0: return list(self)[i] else: + i = int(i) # For Python < 3.7 where islice doesn't support non-int try: return next(islice(self, i, i + 1, 1)) except StopIteration: diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 9a9f3523eae..a10daf8acb1 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -1035,7 +1035,7 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: print(search_src(" fetch(", "def", interact=False)) # py3 Traceback (most recent call last): ... - re.error: missing ), unterminated subpattern at position 6 + error: missing ), unterminated subpattern at position 6 To fix this, *escape* the parenthesis with a backslash:: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index c5310a456ae..d22b4547088 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -1752,10 +1752,8 @@ def sage_formatargspec(args, varargs=None, varkw=None, defaults=None, sage: defaults = [3] sage: sage_formatargspec(args, defaults=defaults) '(a, b, c=3)' - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) # py2 - True - sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) # py3 - doctest:...: DeprecationWarning: `formatargspec` is deprecated since Python 3.5. Use `signature` and the `Signature` object directly + sage: import warnings; warnings.simplefilter('ignore') # py3: ignore DeprecationWarning + sage: formatargspec(args, defaults=defaults) == sage_formatargspec(args, defaults=defaults) True """ def formatargandannotation(arg): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f05be8b42b4..c8dfb27e478 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -5879,14 +5879,10 @@ cdef class Expression(CommutativeRingElement): Indexing directly with ``t[1]`` causes problems with numpy types. - sage: t[1] # py2 + sage: t[1] Traceback (most recent call last): ... - TypeError: 'sage.symbolic.expression.Expression' object does not support indexing - sage: t[1] # py3 - Traceback (most recent call last): - ... - TypeError: 'sage.symbolic.expression.Expression' object is not subscriptable + TypeError: 'sage.symbolic.expression.Expression' object ... """ if (is_a_symbol(self._gobj) or is_a_constant(self._gobj) or is_a_numeric(self._gobj)):