Compare commits
	
		
			69 Commits
		
	
	
		
	
	| Author | SHA256 | Date | |
|---|---|---|---|
| b47f69f09d | |||
| dfb9de9042 | |||
| ce64aa0405 | |||
| 9441fbba5a | |||
| 78f1d3c8a5 | |||
| fcf4f968b8 | |||
| 690348986b | |||
| 97e0411112 | |||
| fc79112197 | |||
| 10c7a335e7 | |||
| f8e8e5f5a6 | |||
| 844cea57bf | |||
| 0e67cb4c62 | |||
| 59750e7e5a | |||
| 4632f56ec3 | |||
| 60eb19dd17 | |||
| 27c961e4d8 | |||
| b256864458 | |||
| c6c155c765 | |||
| 2b4732641b | |||
| a9d7418085 | |||
| ec3fa17564 | |||
| 3a4347dd93 | |||
| adca54398c | |||
| bd6726f1f6 | |||
| 9a7f0d1c68 | |||
| 7b9a150396 | |||
| 9289882dcd | |||
| 108c3339d7 | |||
| ce579c406b | |||
| 57d2f49c64 | |||
| 42a8035c46 | |||
| fabf7106e6 | |||
| 3a1f027049 | |||
| 94c4bccce5 | |||
| fc9cf6cfea | |||
| 395fe8544a | |||
| dc82b5318c | |||
| c8a8af201f | |||
| b876032470 | |||
| 3fe1d60041 | |||
| 99ff7f3e99 | |||
| e6bd6c692a | |||
| 48cbabdad7 | |||
| 695821b9b3 | |||
| 57c49b2fca | |||
| a3ec43e016 | |||
| 83df97fda2 | |||
| e5871db16d | |||
| cbb6e21bc1 | |||
| 4fed4eb6fe | |||
| 2212963a8a | |||
| 17ee322361 | |||
| 3b913b8990 | |||
| 5c1938bb2e | |||
| 4cb5ff030c | |||
| 75fe01d8a9 | |||
| a0a7b00ead | |||
| 9b82a25a73 | |||
| f7a3262d62 | |||
| 579bd59446 | |||
| 69fb40c00c | |||
| 8f9543b5fd | |||
| 7c69c9c95f | |||
| f2fe90f983 | |||
| 8d2b055f70 | |||
| ab0570a88a | |||
| 3a6ba38613 | |||
| de3ed981e5 | 
							
								
								
									
										343
									
								
								np.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								np.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| From f78b07d7648a1efe543c01cc4019928791eb39e9 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Sun, 8 Jun 2025 19:00:10 +0200 | ||||
| Subject: [PATCH 1/4] Use more generic way of checking bool value of an object | ||||
|  | ||||
| This avoid bad interaction with numpy.bool_ as reported in #2322 | ||||
| --- | ||||
|  pythran/pythonic/types/bool.hpp | 2 +- | ||||
|  1 file changed, 1 insertion(+), 1 deletion(-) | ||||
|  | ||||
| diff --git a/pythran/pythonic/types/bool.hpp b/pythran/pythonic/types/bool.hpp | ||||
| index 35ac01d37..1436871f1 100644 | ||||
| --- a/pythran/pythonic/types/bool.hpp | ||||
| +++ b/pythran/pythonic/types/bool.hpp | ||||
| @@ -28,7 +28,7 @@ inline bool from_python<bool>::convert(PyObject *obj) | ||||
|    else if (obj == Py_False) | ||||
|      return false; | ||||
|    else | ||||
| -    return PyInt_AsLong(obj); | ||||
| +    return PyObject_IsTrue(obj); | ||||
|  } | ||||
|   | ||||
|  PYTHONIC_NS_END | ||||
|  | ||||
| From 2847f56dbbb388aeab5f3cab18c70fe26298d254 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Tue, 10 Jun 2025 07:23:03 +0200 | ||||
| Subject: [PATCH 2/4] Add support for numpy.frombuffer | ||||
|  | ||||
| Even though we have very partial support for bytes in Pythran | ||||
| --- | ||||
|  pythran/analyses/dependencies.py              |  2 ++ | ||||
|  pythran/backend.py                            |  8 +++++ | ||||
|  pythran/pythonic/include/numpy/frombuffer.hpp | 26 ++++++++++++++ | ||||
|  pythran/pythonic/numpy/frombuffer.hpp         | 35 +++++++++++++++++++ | ||||
|  pythran/tables.py                             |  1 + | ||||
|  pythran/tests/test_numpy_func0.py             | 14 +++++++- | ||||
|  pythran/tests/test_numpy_random.py            |  4 +-- | ||||
|  pythran/types/conversion.py                   |  1 + | ||||
|  8 files changed, 88 insertions(+), 3 deletions(-) | ||||
|  create mode 100644 pythran/pythonic/include/numpy/frombuffer.hpp | ||||
|  create mode 100644 pythran/pythonic/numpy/frombuffer.hpp | ||||
|  | ||||
| diff --git a/pythran/analyses/dependencies.py b/pythran/analyses/dependencies.py | ||||
| index f153558f8..e0988e103 100644 | ||||
| --- a/pythran/analyses/dependencies.py | ||||
| +++ b/pythran/analyses/dependencies.py | ||||
| @@ -127,6 +127,8 @@ def visit_Yield(self, node): | ||||
|      def visit_Constant(self, node): | ||||
|          if node.value is None: | ||||
|              self.result.add(('builtins', 'None')) | ||||
| +        elif isinstance(node.value, bytes): | ||||
| +            self.result.add(('types', 'str'))  # FIXME: using str as backend | ||||
|          elif isinstance(node.value, str): | ||||
|              self.result.add(('types', 'str')) | ||||
|          elif isinstance(node.value, complex): | ||||
| diff --git a/pythran/backend.py b/pythran/backend.py | ||||
| index e5728ab3e..7b12773fa 100644 | ||||
| --- a/pythran/backend.py | ||||
| +++ b/pythran/backend.py | ||||
| @@ -1012,6 +1012,14 @@ def visit_Constant(self, node): | ||||
|              ret = 'pythonic::builtins::None' | ||||
|          elif isinstance(node.value, bool): | ||||
|              ret = str(node.value).lower() | ||||
| +        elif isinstance(node.value, bytes): | ||||
| +            quoted = "".join('\\' + hex(b)[1:] for b in node.value) | ||||
| +            # FIXME: using str type as backend | ||||
| +            if len(node.value) == 1: | ||||
| +                quoted = quoted.replace("'", r"\'") | ||||
| +                ret = 'pythonic::types::chr(\'' + quoted + '\')' | ||||
| +            else: | ||||
| +                ret = 'pythonic::types::str("' + quoted + '")' | ||||
|          elif isinstance(node.value, str): | ||||
|              quoted = quote_cxxstring(node.value) | ||||
|              if len(node.value) == 1: | ||||
| diff --git a/pythran/pythonic/include/numpy/frombuffer.hpp b/pythran/pythonic/include/numpy/frombuffer.hpp | ||||
| new file mode 100644 | ||||
| index 000000000..bb685d8b5 | ||||
| --- /dev/null | ||||
| +++ b/pythran/pythonic/include/numpy/frombuffer.hpp | ||||
| @@ -0,0 +1,26 @@ | ||||
| +#ifndef PYTHONIC_INCLUDE_NUMPY_FROMBUFFER_HPP | ||||
| +#define PYTHONIC_INCLUDE_NUMPY_FROMBUFFER_HPP | ||||
| + | ||||
| +#include "pythonic/include/numpy/float64.hpp" | ||||
| +#include "pythonic/include/types/list.hpp" | ||||
| +#include "pythonic/include/types/ndarray.hpp" | ||||
| +#include "pythonic/include/types/str.hpp" | ||||
| +#include "pythonic/include/utils/functor.hpp" | ||||
| + | ||||
| +#include <limits> | ||||
| +#include <sstream> | ||||
| + | ||||
| +PYTHONIC_NS_BEGIN | ||||
| + | ||||
| +namespace numpy | ||||
| +{ | ||||
| +  template <class dtype = functor::float64> | ||||
| +  types::ndarray<typename dtype::type, types::pshape<long>> | ||||
| +  frombuffer(types::str const &string, dtype d = dtype(), long count = -1, | ||||
| +             long offset = 0); | ||||
| + | ||||
| +  DEFINE_FUNCTOR(pythonic::numpy, frombuffer); | ||||
| +} // namespace numpy | ||||
| +PYTHONIC_NS_END | ||||
| + | ||||
| +#endif | ||||
| diff --git a/pythran/pythonic/numpy/frombuffer.hpp b/pythran/pythonic/numpy/frombuffer.hpp | ||||
| new file mode 100644 | ||||
| index 000000000..c0f625142 | ||||
| --- /dev/null | ||||
| +++ b/pythran/pythonic/numpy/frombuffer.hpp | ||||
| @@ -0,0 +1,35 @@ | ||||
| +#ifndef PYTHONIC_NUMPY_FROMBUFFER_HPP | ||||
| +#define PYTHONIC_NUMPY_FROMBUFFER_HPP | ||||
| + | ||||
| +#include "pythonic/include/numpy/frombuffer.hpp" | ||||
| + | ||||
| +#include "pythonic/types/list.hpp" | ||||
| +#include "pythonic/types/ndarray.hpp" | ||||
| +#include "pythonic/types/str.hpp" | ||||
| +#include "pythonic/utils/functor.hpp" | ||||
| + | ||||
| +#include <limits> | ||||
| +#include <sstream> | ||||
| + | ||||
| +PYTHONIC_NS_BEGIN | ||||
| + | ||||
| +namespace numpy | ||||
| +{ | ||||
| +  template <class dtype> | ||||
| +  types::ndarray<typename dtype::type, types::pshape<long>> | ||||
| +  frombuffer(types::str const &string, dtype d, long count, long offset) | ||||
| +  { | ||||
| +    if (count < 0) | ||||
| +      count = string.size() / sizeof(typename dtype::type); | ||||
| +    types::pshape<long> shape = count; | ||||
| +    utils::shared_ref<types::raw_array<typename dtype::type>> buffer( | ||||
| +        std::get<0>(shape)); | ||||
| +    auto const *tstring = | ||||
| +        reinterpret_cast<typename dtype::type const *>(string.c_str()) + offset; | ||||
| +    std::copy(tstring, tstring + std::get<0>(shape), buffer->data); | ||||
| +    return {buffer, shape}; | ||||
| +  } | ||||
| +} // namespace numpy | ||||
| +PYTHONIC_NS_END | ||||
| + | ||||
| +#endif | ||||
| diff --git a/pythran/tables.py b/pythran/tables.py | ||||
| index a61709d19..bf0d9b220 100644 | ||||
| --- a/pythran/tables.py | ||||
| +++ b/pythran/tables.py | ||||
| @@ -3752,6 +3752,7 @@ def expand_numpy_2_args(args, defaults=None, force=False): | ||||
|          "fmin": UFunc(REDUCED_BINARY_UFUNC), | ||||
|          "fmod": UFunc(BINARY_UFUNC), | ||||
|          "frexp": ConstFunctionIntr(), | ||||
| +        "frombuffer": ConstFunctionIntr(), | ||||
|          "fromfunction": ConstFunctionIntr(), | ||||
|          "fromiter": ConstFunctionIntr(args=("iterable", "dtype", "count"), | ||||
|                                        defaults=(-1,)), | ||||
| diff --git a/pythran/tests/test_numpy_func0.py b/pythran/tests/test_numpy_func0.py | ||||
| index 41f716d90..a97a43c50 100644 | ||||
| --- a/pythran/tests/test_numpy_func0.py | ||||
| +++ b/pythran/tests/test_numpy_func0.py | ||||
| @@ -560,11 +560,23 @@ def test_fromstring1(self): | ||||
|          self.run_test("def np_fromstring1(a): from numpy import fromstring, uint8 ; a = '\x01\x02\x03\x04' ; return fromstring(a, uint8,3)", '\x01\x02\x03\x04', np_fromstring1=[str]) | ||||
|   | ||||
|      def test_fromstring2(self): | ||||
| -        self.run_test("def np_fromstring2(a): from numpy import fromstring, uint32 ; return fromstring(a, uint32,-1, ' ')", '1 2 3 4', np_fromstring2=[str]) | ||||
| +        self.run_test("def np_fromstring2(a): from numpy import fromstring, uint32 ; return fromstring(a, uint32,-1, ' ')", '1 20 3 40', np_fromstring2=[str]) | ||||
|   | ||||
|      def test_fromstring3(self): | ||||
|          self.run_test("def np_fromstring3(a): from numpy import fromstring, uint32 ; return fromstring(a, uint32,2, ',')", '1,2, 3, 4', np_fromstring3=[str]) | ||||
|   | ||||
| +    def test_frombuffer0(self): | ||||
| +        self.run_test("def np_frombuffer0(a): from numpy import frombuffer, uint8 ; return frombuffer(b'\x01\x02' * a, uint8)", 1, np_frombuffer0=[int]) | ||||
| + | ||||
| +    def test_frombuffer1(self): | ||||
| +        self.run_test("def np_frombuffer1(a): from numpy import frombuffer, uint8 ; return frombuffer(b'\x01\x02\x03\x04' * a, uint8, 3)", 1, np_frombuffer1=[int]) | ||||
| + | ||||
| +    def test_frombuffer2(self): | ||||
| +        self.run_test("def np_frombuffer2(a): from numpy import frombuffer, uint16 ; return frombuffer(a * b'\x01\x02', uint16)", 1, np_frombuffer2=[int]) | ||||
| + | ||||
| +    def test_frombuffer3(self): | ||||
| +        self.run_test("def np_frombuffer3(a): from numpy import frombuffer, int8 ; return frombuffer(a * b'\x01\x02\x03\x04', int8, 3, 1)", 1, np_frombuffer3=[int]) | ||||
| + | ||||
|      def test_outer0(self): | ||||
|          self.run_test("def np_outer0(x): from numpy import outer ; return outer(x, x+2)", numpy.arange(6).reshape(2,3), np_outer0=[NDArray[int,:,:]]) | ||||
|   | ||||
| diff --git a/pythran/tests/test_numpy_random.py b/pythran/tests/test_numpy_random.py | ||||
| index aa3f22815..6547a2e92 100644 | ||||
| --- a/pythran/tests/test_numpy_random.py | ||||
| +++ b/pythran/tests/test_numpy_random.py | ||||
| @@ -638,9 +638,9 @@ def test_numpy_random_bytes1(self): | ||||
|          self.run_test(""" | ||||
|              def numpy_random_bytes1(n): | ||||
|                  from numpy.random import bytes | ||||
| -                from numpy import mean, fromstring, uint8, asarray | ||||
| +                from numpy import mean, frombuffer, uint8, asarray | ||||
|                  a = bytes(n) | ||||
| -                return (abs(mean(asarray(fromstring(a, uint8), dtype=float)) - 127.5) < .05)""", | ||||
| +                return (abs(mean(asarray(frombuffer(a, uint8), dtype=float)) - 127.5) < .05)""", | ||||
|                        10 ** 8, numpy_random_bytes1=[int]) | ||||
|   | ||||
|      ########################################################################### | ||||
| diff --git a/pythran/types/conversion.py b/pythran/types/conversion.py | ||||
| index e149b7fb4..b3c86b5fd 100644 | ||||
| --- a/pythran/types/conversion.py | ||||
| +++ b/pythran/types/conversion.py | ||||
| @@ -9,6 +9,7 @@ | ||||
|  PYTYPE_TO_CTYPE_TABLE = { | ||||
|      numpy.uint: 'npy_uint', | ||||
|      # | ||||
| +    bytes: 'pythonic::types::str',  # FIXME: using types::str as backend | ||||
|      complex: 'std::complex<double>', | ||||
|      bool: 'bool', | ||||
|      int: 'long', | ||||
|  | ||||
| From c01d4224ca0c34af6157ee4e82893a82f3e9bb43 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Sun, 8 Jun 2025 19:15:28 +0200 | ||||
| Subject: [PATCH 3/4] Do not test binary mode of numpy.fromstring for recent | ||||
|  numpy version | ||||
|  | ||||
| It's no longer supported as of numpy 2.3, see | ||||
| https://numpy.org/devdocs/release/2.3.0-notes.html and | ||||
| https://github.com/numpy/numpy/pull/28254 | ||||
|  | ||||
| Fix #2322 | ||||
| --- | ||||
|  pythran/tests/test_numpy_func0.py | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
|  | ||||
| diff --git a/pythran/tests/test_numpy_func0.py b/pythran/tests/test_numpy_func0.py | ||||
| index a97a43c50..91c37e979 100644 | ||||
| --- a/pythran/tests/test_numpy_func0.py | ||||
| +++ b/pythran/tests/test_numpy_func0.py | ||||
| @@ -553,9 +553,11 @@ def test_fromfile5(self): | ||||
|          finally: | ||||
|              os.remove(temp_name) | ||||
|   | ||||
| +    @unittest.skipIf(np_version > version.Version("2.2"), reason="np.fromstring no longer supports binary mode") | ||||
|      def test_fromstring0(self): | ||||
|          self.run_test("def np_fromstring0(a): from numpy import fromstring, uint8 ; return fromstring(a, uint8)", '\x01\x02', np_fromstring0=[str]) | ||||
|   | ||||
| +    @unittest.skipIf(np_version > version.Version("2.2"), reason="np.fromstring no longer supports binary mode") | ||||
|      def test_fromstring1(self): | ||||
|          self.run_test("def np_fromstring1(a): from numpy import fromstring, uint8 ; a = '\x01\x02\x03\x04' ; return fromstring(a, uint8,3)", '\x01\x02\x03\x04', np_fromstring1=[str]) | ||||
|   | ||||
|  | ||||
| From ef869953fbb16a31c5eb06c72296d4f23cf73e55 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Sun, 8 Jun 2025 19:22:49 +0200 | ||||
| Subject: [PATCH 4/4] Adjust doc validation to recent python 3.13 | ||||
|  | ||||
| - force COLUMNS width for reproducible argparse help output | ||||
| - gast Constant pretty printing has changed | ||||
|  | ||||
| Fix #2317 | ||||
| --- | ||||
|  .github/workflows/core.yml |  2 +- | ||||
|  docs/TUTORIAL.rst          |  6 +++--- | ||||
|  pythran/tests/test_xdoc.py | 14 +++++++++++++- | ||||
|  3 files changed, 17 insertions(+), 5 deletions(-) | ||||
|  | ||||
| diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml | ||||
| index 5dca25973..8609be87e 100644 | ||||
| --- a/.github/workflows/core.yml | ||||
| +++ b/.github/workflows/core.yml | ||||
| @@ -12,7 +12,7 @@ jobs: | ||||
|      runs-on: ubuntu-22.04 | ||||
|      strategy: | ||||
|        matrix: | ||||
| -        python-version: [3.7, 3.9, 3.11, 3.12, pypy-3.9] | ||||
| +        python-version: [3.7, 3.9, 3.11, 3.12, 3.13, pypy-3.9] | ||||
|          cpp-version: [g++-12, clang-13] | ||||
|      steps: | ||||
|      - uses: actions/checkout@v2 | ||||
| diff --git a/docs/TUTORIAL.rst b/docs/TUTORIAL.rst | ||||
| index e1dd80c4f..31ff4fffc 100644 | ||||
| --- a/docs/TUTORIAL.rst | ||||
| +++ b/docs/TUTORIAL.rst | ||||
| @@ -20,7 +20,7 @@ Python ships a standard module, ``ast`` to turn Python code into an AST. For ins | ||||
|    >>> code = "a=1" | ||||
|    >>> tree = ast.parse(code)  # turn the code into an AST | ||||
|    >>> print(ast.dump(tree))  # view it as a string | ||||
| -  Module(body=[Assign(targets=[Name(id='a', ctx=Store())], value=Constant(value=1, kind=None))]) | ||||
| +  Module(body=[Assign(targets=[Name(id='a', ctx=Store())], value=Constant(value=1))]) | ||||
|   | ||||
|  Deciphering the above line, one learns that the single assignment is parsed as | ||||
|  a module containing a single statement, which is an assignment to a single | ||||
| @@ -33,7 +33,7 @@ Eventually, one needs to parse more complex codes, and things get a bit more cry | ||||
|    ...     return n if n< 2 else fib(n-1) + fib(n-2)""" | ||||
|    >>> tree = ast.parse(fib_src) | ||||
|    >>> print(ast.dump(tree)) | ||||
| -  Module(body=[FunctionDef(name='fib', args=arguments(args=[Name(id='n', ctx=Param())]), body=[Return(value=IfExp(test=Compare(left=Name(id='n', ctx=Load()), ops=[Lt()], comparators=[Constant(value=2, kind=None)]), body=Name(id='n', ctx=Load()), orelse=BinOp(left=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=1, kind=None))]), op=Add(), right=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=2, kind=None))]))))])]) | ||||
| +  Module(body=[FunctionDef(name='fib', args=arguments(args=[Name(id='n', ctx=Param())]), body=[Return(value=IfExp(test=Compare(left=Name(id='n', ctx=Load()), ops=[Lt()], comparators=[Constant(value=2)]), body=Name(id='n', ctx=Load()), orelse=BinOp(left=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=1))]), op=Add(), right=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=2))]))))])]) | ||||
|   | ||||
|  The idea remains the same. The whole Python syntax is described in | ||||
|  http://docs.python.org/2/library/ast.html and is worth a glance, otherwise | ||||
| @@ -199,7 +199,7 @@ constant expressions. In the previous code, there is only two constant | ||||
|   | ||||
|    >>> ce = pm.gather(analyses.ConstantExpressions, tree) | ||||
|    >>> sorted(map(ast.dump, ce)) | ||||
| -  ["Attribute(value=Name(id='math', ctx=Load()), attr='cos', ctx=Load())", 'Constant(value=3, kind=None)'] | ||||
| +  ["Attribute(value=Name(id='math', ctx=Load()), attr='cos', ctx=Load())", 'Constant(value=3)'] | ||||
|   | ||||
|  One of the most critical analyse of Pythran is the points-to analysis. There | ||||
|  are two flavors of this analyse, one that computes an over-set of the aliased | ||||
| diff --git a/pythran/tests/test_xdoc.py b/pythran/tests/test_xdoc.py | ||||
| index 862dec1fa..1dddf36ca 100644 | ||||
| --- a/pythran/tests/test_xdoc.py | ||||
| +++ b/pythran/tests/test_xdoc.py | ||||
| @@ -20,6 +20,8 @@ class TestDoctest(unittest.TestCase): | ||||
|   | ||||
|      @pytest.mark.skipif(sys.platform == "win32", | ||||
|                          reason="We should create a file for windows.") | ||||
| +    @pytest.mark.skipif(sys.version_info < (3, 13), | ||||
| +                        reason="ast output changed with 3.13") | ||||
|      def test_tutorial(self): | ||||
|          failed, _ = doctest.testfile('../../docs/TUTORIAL.rst') | ||||
|          self.assertEqual(failed, 0) | ||||
| @@ -34,9 +36,19 @@ def test_internal(self): | ||||
|   | ||||
|      @pytest.mark.skipif(sys.platform == "win32", | ||||
|                          reason="We should create a file for windows.") | ||||
| +    @pytest.mark.skipif(sys.version_info <= (3, 12), | ||||
| +                        reason="argparse output changed with 3.13") | ||||
|      def test_cli(self): | ||||
|          tmpfile = self.adapt_rst('../../docs/CLI.rst') | ||||
| -        failed, _ = doctest.testfile(tmpfile, False) | ||||
| +        columns = os.environ.get('COLUMNS', None) | ||||
| +        os.environ['COLUMNS'] = '80' | ||||
| +        try: | ||||
| +            failed, _ = doctest.testfile(tmpfile, False) | ||||
| +        finally: | ||||
| +            if columns is None: | ||||
| +                del os.environ['COLUMNS'] | ||||
| +            else: | ||||
| +                os.environ['COLUMNS'] = columns | ||||
|          self.assertEqual(failed, 0) | ||||
|          os.remove(tmpfile) | ||||
|   | ||||
| @@ -1,33 +0,0 @@ | ||||
| From 6b61e8a6b3dddab13b88e51309cbdf2f28247960 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Thu, 22 Aug 2024 08:20:25 +0200 | ||||
| Subject: [PATCH] Fix docstring and implementation of Interval.power | ||||
|  | ||||
| This makes the code more resilient to future numpy changes. | ||||
| --- | ||||
|  pythran/interval.py | 8 +++++--- | ||||
|  1 file changed, 5 insertions(+), 3 deletions(-) | ||||
|  | ||||
| diff --git a/pythran/interval.py b/pythran/interval.py | ||||
| index 4e5dff8fd..b8ef42e69 100644 | ||||
| --- a/pythran/interval.py | ||||
| +++ b/pythran/interval.py | ||||
| @@ -196,13 +196,15 @@ def __pow__(range1, range2): | ||||
|          >>> Interval(1, 5) ** Interval(-5, -4) | ||||
|          Interval(low=1.0, high=1.0) | ||||
|          >>> Interval(-1, 5) ** Interval(-5, 3) | ||||
| -        Interval(low=-1.0, high=125.0) | ||||
| +        Interval(low=-1.0, high=125) | ||||
|          >>> Interval(1, 5) ** Interval(3, 8) | ||||
| -        Interval(low=1.0, high=390625.0) | ||||
| +        Interval(low=1, high=390625) | ||||
|          """ | ||||
|          res = [v1 ** v2 for v1, v2 in | ||||
|                 itertools.product(range1.bounds(), range2.bounds())] | ||||
| -        return Interval(numpy.ceil(min(res)), numpy.floor(max(res))) | ||||
| +        minres, maxres = min(res), max(res) | ||||
| +        return Interval(type(minres)(numpy.ceil(minres)), | ||||
| +                        type(maxres)(numpy.floor(maxres))) | ||||
|   | ||||
|      def __lshift__(range1, range2): | ||||
|          """ | ||||
| @@ -1,105 +0,0 @@ | ||||
| From 9261d30aa9618cb2a5a698d39752263b076f2d4b Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Tue, 20 Aug 2024 23:50:55 +0200 | ||||
| Subject: [PATCH] Fix numpy.fix output type | ||||
|  | ||||
| This one changed with recent numpy upgrade, see | ||||
| https://github.com/numpy/numpy/pull/26766 | ||||
| --- | ||||
|  pythran/pythonic/include/numpy/fix.hpp | 17 ++++++++++++++--- | ||||
|  pythran/pythonic/numpy/fix.hpp         |  6 +++--- | ||||
|  pythran/tests/test_numpy_func0.py      |  5 +++++ | ||||
|  3 files changed, 22 insertions(+), 6 deletions(-) | ||||
|  | ||||
| diff --git a/pythran/pythonic/include/numpy/fix.hpp b/pythran/pythonic/include/numpy/fix.hpp | ||||
| index 2708930d6c..e4a85a5049 100644 | ||||
| --- a/pythran/pythonic/include/numpy/fix.hpp | ||||
| +++ b/pythran/pythonic/include/numpy/fix.hpp | ||||
| @@ -1,18 +1,29 @@ | ||||
|  #ifndef PYTHONIC_INCLUDE_NUMPY_FIX_HPP | ||||
|  #define PYTHONIC_INCLUDE_NUMPY_FIX_HPP | ||||
|   | ||||
| -#include "pythonic/include/utils/functor.hpp" | ||||
|  #include "pythonic/include/types/ndarray.hpp" | ||||
| +#include "pythonic/include/utils/functor.hpp" | ||||
|  #include "pythonic/include/utils/numpy_traits.hpp" | ||||
|   | ||||
|  PYTHONIC_NS_BEGIN | ||||
|   | ||||
|  namespace numpy | ||||
|  { | ||||
| +  namespace wrapper | ||||
| +  { | ||||
| +    template <class E> | ||||
| +    E fix(E const &e) | ||||
| +    { | ||||
| +      if (std::is_integral<E>::value) | ||||
| +        return e; | ||||
| +      else | ||||
| +        return std::trunc(e); | ||||
| +    } | ||||
| +  } // namespace wrapper | ||||
|  #define NUMPY_NARY_FUNC_NAME fix | ||||
| -#define NUMPY_NARY_FUNC_SYM std::trunc | ||||
| +#define NUMPY_NARY_FUNC_SYM wrapper::fix | ||||
|  #include "pythonic/include/types/numpy_nary_expr.hpp" | ||||
| -} | ||||
| +} // namespace numpy | ||||
|  PYTHONIC_NS_END | ||||
|   | ||||
|  #endif | ||||
| diff --git a/pythran/pythonic/numpy/fix.hpp b/pythran/pythonic/numpy/fix.hpp | ||||
| index 5b1b020dc2..84773b61cf 100644 | ||||
| --- a/pythran/pythonic/numpy/fix.hpp | ||||
| +++ b/pythran/pythonic/numpy/fix.hpp | ||||
| @@ -3,8 +3,8 @@ | ||||
|   | ||||
|  #include "pythonic/include/numpy/fix.hpp" | ||||
|   | ||||
| -#include "pythonic/utils/functor.hpp" | ||||
|  #include "pythonic/types/ndarray.hpp" | ||||
| +#include "pythonic/utils/functor.hpp" | ||||
|  #include "pythonic/utils/numpy_traits.hpp" | ||||
|   | ||||
|  PYTHONIC_NS_BEGIN | ||||
| @@ -13,9 +13,9 @@ namespace numpy | ||||
|  { | ||||
|   | ||||
|  #define NUMPY_NARY_FUNC_NAME fix | ||||
| -#define NUMPY_NARY_FUNC_SYM std::trunc | ||||
| +#define NUMPY_NARY_FUNC_SYM wrapper::fix | ||||
|  #include "pythonic/types/numpy_nary_expr.hpp" | ||||
| -} | ||||
| +} // namespace numpy | ||||
|  PYTHONIC_NS_END | ||||
|   | ||||
|  #endif | ||||
| diff --git a/pythran/tests/test_numpy_func0.py b/pythran/tests/test_numpy_func0.py | ||||
| index 3e11133fec..41f716d900 100644 | ||||
| --- a/pythran/tests/test_numpy_func0.py | ||||
| +++ b/pythran/tests/test_numpy_func0.py | ||||
| @@ -1,12 +1,16 @@ | ||||
|  import unittest | ||||
|  from pythran.tests import TestEnv | ||||
|  import numpy | ||||
| +from packaging import version | ||||
|  import tempfile | ||||
|  import os | ||||
|   | ||||
|  from pythran.typing import NDArray, List, Tuple | ||||
|   | ||||
|   | ||||
| +np_version = version.parse(numpy.version.version) | ||||
| + | ||||
| + | ||||
|  class TestNumpyFunc0(TestEnv): | ||||
|   | ||||
|      def test_extended_sum0(self): | ||||
| @@ -910,6 +914,7 @@ def test_flatnonzero1(self): | ||||
|      def test_fix0(self): | ||||
|          self.run_test("def np_fix0(x): from numpy import fix ; return fix(x)", 3.14, np_fix0=[float]) | ||||
|   | ||||
| +    @unittest.skipIf(np_version <= version.Version("2.1"), reason="np.fix used to return float on integral input") | ||||
|      def test_fix1(self): | ||||
|          self.run_test("def np_fix1(x): from numpy import fix ; return fix(x)", 3, np_fix1=[int]) | ||||
|   | ||||
| @@ -1,3 +1,21 @@ | ||||
| ------------------------------------------------------------------- | ||||
| Mon Sep 29 21:32:36 UTC 2025 - Dirk Müller <dmueller@suse.com> | ||||
|  | ||||
| - prepare for py314 tests | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Tue Aug 19 07:47:56 UTC 2025 - Markéta Machová <mmachova@suse.com> | ||||
|  | ||||
| - Drop np-frombuffer.patch and np-fromstring.patch and substitute | ||||
|   them with np.patch, which is a PR which contains both of them | ||||
|   and also other fixes, unskip the previously skipped test | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Thu Aug 14 08:37:43 UTC 2025 - Markéta Machová <mmachova@suse.com> | ||||
|  | ||||
| - Add upstream np-fromstring.patch and np-frombuffer.patch to fix | ||||
|   tests with NumPy 2.3, skip one additional failing test | ||||
|  | ||||
| ------------------------------------------------------------------- | ||||
| Mon Jul  7 18:31:44 UTC 2025 - Ben Greiner <code@bnavigator.de> | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # | ||||
| # spec file for package python-pythran | ||||
| # | ||||
| # Copyright (c) 2025 SUSE LLC | ||||
| # Copyright (c) 2025 SUSE LLC and contributors | ||||
| # | ||||
| # All modifications and additions to the file contributed by third parties | ||||
| # remain the property of their copyright owners, unless otherwise agreed | ||||
| @@ -34,6 +34,9 @@ | ||||
| %if "%{flavor}" != "test-py313" | ||||
| %define skip_python313 1 | ||||
| %endif | ||||
| %if "%{flavor}" != "test-py314" | ||||
| %define skip_python314 1 | ||||
| %endif | ||||
| # Skip empty buildsets on tumbleweed, or non py311 flavors on sle15_python_module_pythons, or any testing on leap 16 | ||||
| %if "%{shrink:%{pythons}}" == "" || ("%pythons" == "python311" && 0%{?skip_python311}) || (0%{?is_opensuse} && 0%{?suse_version} == 1600) | ||||
| ExclusiveArch:  donotbuild | ||||
| @@ -56,6 +59,8 @@ Source99:       python-pythran-rpmlintrc | ||||
| Patch0:         GCC15_fix_Add-missing-operators-to-nditerator.patch | ||||
| # PATCH-FIX-UPSTREAM: https://github.com/serge-sans-paille/pythran/commit/623fa5031df7ec5c3dfe6789bf608cf11ac95c36 | ||||
| Patch1:         GCC15_pythran-PR2325-missing-operators.patch | ||||
| # PATCH-FIX-UPSTREAM https://github.com/serge-sans-paille/pythran/pull/2323 Various fixes with recent numpy | ||||
| Patch2:         np.patch | ||||
| BuildRequires:  %{python_module pip} | ||||
| BuildRequires:  %{python_module setuptools} | ||||
| BuildRequires:  %{python_module wheel} | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:a2510f370a7d62761844daa112a455785e5a6a216cf9ae704c3926fe68eb65ce | ||||
| size 3680817 | ||||
| @@ -1,3 +0,0 @@ | ||||
| version https://git-lfs.github.com/spec/v1 | ||||
| oid sha256:b1f13ca239625579a92bc915bd0abae3747d96063ce55790eead2a072667fcb3 | ||||
| size 3697173 | ||||
| @@ -1,76 +0,0 @@ | ||||
| From 840a0e706ec39963aec6bcd1f118bf33177c20b4 Mon Sep 17 00:00:00 2001 | ||||
| From: serge-sans-paille <serge.guelton@telecom-bretagne.eu> | ||||
| Date: Sat, 29 Jun 2024 19:13:02 +0200 | ||||
| Subject: [PATCH] Bump gast requirement to 0.6.0 | ||||
|  | ||||
| This mostly helps for harmonious behavior wrt. gast.dump | ||||
| --- | ||||
|  docs/TUTORIAL.rst | 8 ++++---- | ||||
|  pythran/utils.py  | 2 +- | ||||
|  requirements.txt  | 2 +- | ||||
|  3 files changed, 6 insertions(+), 6 deletions(-) | ||||
|  | ||||
| diff --git a/docs/TUTORIAL.rst b/docs/TUTORIAL.rst | ||||
| index 09f6902f9..7692547eb 100644 | ||||
| --- a/docs/TUTORIAL.rst | ||||
| +++ b/docs/TUTORIAL.rst | ||||
| @@ -20,7 +20,7 @@ Python ships a standard module, ``ast`` to turn Python code into an AST. For ins | ||||
|    >>> code = "a=1" | ||||
|    >>> tree = ast.parse(code)  # turn the code into an AST | ||||
|    >>> print(ast.dump(tree))  # view it as a string | ||||
| -  Module(body=[Assign(targets=[Name(id='a', ctx=Store(), annotation=None, type_comment=None)], value=Constant(value=1, kind=None), type_comment=None)], type_ignores=[]) | ||||
| +  Module(body=[Assign(targets=[Name(id='a', ctx=Store())], value=Constant(value=1, kind=None))]) | ||||
|   | ||||
|  Deciphering the above line, one learns that the single assignment is parsed as | ||||
|  a module containing a single statement, which is an assignment to a single | ||||
| @@ -33,7 +33,7 @@ Eventually, one needs to parse more complex codes, and things get a bit more cry | ||||
|    ...     return n if n< 2 else fib(n-1) + fib(n-2)""" | ||||
|    >>> tree = ast.parse(fib_src) | ||||
|    >>> print(ast.dump(tree)) | ||||
| -  Module(body=[FunctionDef(name='fib', args=arguments(args=[Name(id='n', ctx=Param(), annotation=None, type_comment=None)], posonlyargs=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Return(value=IfExp(test=Compare(left=Name(id='n', ctx=Load(), annotation=None, type_comment=None), ops=[Lt()], comparators=[Constant(value=2, kind=None)]), body=Name(id='n', ctx=Load(), annotation=None, type_comment=None), orelse=BinOp(left=Call(func=Name(id='fib', ctx=Load(), annotation=None, type_comment=None), args=[BinOp(left=Name(id='n', ctx=Load(), annotation=None, type_comment=None), op=Sub(), right=Constant(value=1, kind=None))], keywords=[]), op=Add(), right=Call(func=Name(id='fib', ctx=Load(), annotation=None, type_comment=None), args=[BinOp(left=Name(id='n', ctx=Load(), annotation=None, type_comment=None), op=Sub(), right=Constant(value=2, kind=None))], keywords=[]))))], decorator_list=[], returns=None, type_comment=None)], type_ignores=[]) | ||||
| +  Module(body=[FunctionDef(name='fib', args=arguments(args=[Name(id='n', ctx=Param())]), body=[Return(value=IfExp(test=Compare(left=Name(id='n', ctx=Load()), ops=[Lt()], comparators=[Constant(value=2, kind=None)]), body=Name(id='n', ctx=Load()), orelse=BinOp(left=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=1, kind=None))]), op=Add(), right=Call(func=Name(id='fib', ctx=Load()), args=[BinOp(left=Name(id='n', ctx=Load()), op=Sub(), right=Constant(value=2, kind=None))]))))])]) | ||||
|   | ||||
|  The idea remains the same. The whole Python syntax is described in | ||||
|  http://docs.python.org/2/library/ast.html and is worth a glance, otherwise | ||||
| @@ -199,7 +199,7 @@ constant expressions. In the previous code, there is only two constant | ||||
|   | ||||
|    >>> ce = pm.gather(analyses.ConstantExpressions, tree) | ||||
|    >>> sorted(map(ast.dump, ce)) | ||||
| -  ["Attribute(value=Name(id='math', ctx=Load(), annotation=None, type_comment=None), attr='cos', ctx=Load())", 'Constant(value=3, kind=None)'] | ||||
| +  ["Attribute(value=Name(id='math', ctx=Load()), attr='cos', ctx=Load())", 'Constant(value=3, kind=None)'] | ||||
|   | ||||
|  One of the most critical analyse of Pythran is the points-to analysis. There | ||||
|  are two flavors of this analyse, one that computes an over-set of the aliased | ||||
| @@ -210,7 +210,7 @@ variable, and one that computes an under set. ``Aliases`` computes an over-set:: | ||||
|    >>> al = pm.gather(analyses.Aliases, tree) | ||||
|    >>> returned = tree.body[-1].body[-1].value | ||||
|    >>> print(ast.dump(returned)) | ||||
| -  Name(id='b', ctx=Load(), annotation=None, type_comment=None) | ||||
| +  Name(id='b', ctx=Load()) | ||||
|    >>> sorted(a.id for a in al[returned]) | ||||
|    ['c', 'd'] | ||||
|   | ||||
| diff --git a/pythran/utils.py b/pythran/utils.py | ||||
| index 2d7a67327..55a7e8ad6 100644 | ||||
| --- a/pythran/utils.py | ||||
| +++ b/pythran/utils.py | ||||
| @@ -106,7 +106,7 @@ def get_variable(assignable): | ||||
|      ...     slice=ast.Name('j', ast.Load(), None, None), | ||||
|      ...     ctx=ast.Load()) | ||||
|      >>> ast.dump(get_variable(ref)) | ||||
| -    "Name(id='a', ctx=Load(), annotation=None, type_comment=None)" | ||||
| +    "Name(id='a', ctx=Load())" | ||||
|      """ | ||||
|      msg = "Only name and subscript can be assigned." | ||||
|      assert isinstance(assignable, (ast.Name, ast.Subscript)), msg | ||||
| diff --git a/requirements.txt b/requirements.txt | ||||
| index fd6a738e5..c7a25c52a 100644 | ||||
| --- a/requirements.txt | ||||
| +++ b/requirements.txt | ||||
| @@ -1,5 +1,5 @@ | ||||
|  ply>=3.4 | ||||
|  setuptools | ||||
| -gast~=0.5.0 | ||||
| +gast~=0.6.0 | ||||
|  numpy | ||||
|  beniget~=0.4.0 | ||||
		Reference in New Issue
	
	Block a user