Repository: apytypes/apytypes (language: python)

Issue:
windows-latest runner failing to build apytypes
Since about yesterday, June 6th, the windows-latest runners are failing to build the apytypes project with an MSVC internal compiler error, for all versions of Python.

It's likely a problem upstream. Possibly in the GitHub windows-server runner, but I'm not 100 % sure about this. 

Related issues:
* https://github.com/wjakob/nanobind/issues/613
* https://github.com/actions/runner-images/issues/10004
windows-latest runner failing to build apytypes
Since about yesterday, June 6th, the windows-latest runners are failing to build the apytypes project with an MSVC internal compiler error, for all versions of Python.

It's likely a problem upstream. Possibly in the GitHub windows-server runner, but I'm not 100 % sure about this. 

Related issues:
* https://github.com/wjakob/nanobind/issues/613
* https://github.com/actions/runner-images/issues/10004


Discussion / hints:

Fix patch:
diff --git a/src/python_util.h b/src/python_util.h
index c5028ec1d..c82128a42 100644
--- a/src/python_util.h
+++ b/src/python_util.h
@@ -358,9 +358,62 @@ python_sequence_extract_shape(const nanobind::sequence& bit_pattern_sequence)
  * `bit_pattern_sequence` does not match `<T>` or another Python sequence a
  * `std::domain_error` exception to be raised.
  */
+// template <typename... PyTypes>
+//[[maybe_unused]] static std::vector<nanobind::object>
+// python_sequence_walk(const nanobind::sequence& py_seq)
+//{
+//     namespace nb = nanobind;
+//
+//     // Result output iterator
+//     std::vector<nb::object> result {};
+//
+//     // Walk the Python sequences and extract the data
+//     struct seq_it_pair {
+//         decltype(std::begin(py_seq)) iterator;
+//         decltype(std::end(py_seq)) sentinel;
+//     };
+//     std::stack<seq_it_pair, std::vector<seq_it_pair>> it_stack;
+//     it_stack.push({ py_seq.begin(), py_seq.end() });
+//
+//     while (!it_stack.empty()) {
+//         if (it_stack.top().iterator == it_stack.top().sentinel) {
+//             // End of current iterator/sentinel pair. Pop it.
+//             it_stack.pop();
+//         } else {
+//             if (nb::isinstance<nb::sequence>(*it_stack.top().iterator)) {
+//                 // New sequence found. We need to go deeper
+//                 auto new_sequence =
+//                 nb::cast<nb::sequence>(*it_stack.top().iterator++); it_stack.push({
+//                 new_sequence.begin(), new_sequence.end() });
+//             } else if ((nb::isinstance<PyTypes>(*it_stack.top().iterator) || ...)) {
+//                 // Element matching one of the PyTypes found, store it in container
+//                 result.push_back(nb::cast<nb::object>(*it_stack.top().iterator++));
+//             } else {
+//                 nb::object obj = nb::cast<nb::object>(*it_stack.top().iterator);
+//                 nb::type_object type = nb::cast<nb::type_object>(obj.type());
+//                 nb::str type_string = nb::str(type);
+//                 nb::str repr = nb::repr(obj);
+//                 std::string repr_string = repr.c_str();
+//                 throw std::domain_error(
+//                     std::string("Non <type>/sequence found when walking: '")
+//                     + repr_string + "' of type: '" + type_string.c_str()
+//                 );
+//             }
+//         }
+//     }
+//
+//     // Return the result
+//     return result;
+// }
+//
+//  MSVC2019 hot-fix:
+//
 template <typename... PyTypes>
-[[maybe_unused]] static APY_INLINE std::vector<nanobind::object>
-python_sequence_walk(const nanobind::sequence& py_seq)
+[[maybe_unused]] static std::vector<nanobind::object>
+python_sequence_walk(const nanobind::sequence& py_seq);
+template <>
+[[maybe_unused]] std::vector<nanobind::object>
+python_sequence_walk<nanobind::int_>(const nanobind::sequence& py_seq)
 {
     namespace nb = nanobind;
 
@@ -384,7 +437,101 @@ python_sequence_walk(const nanobind::sequence& py_seq)
                 // New sequence found. We need to go deeper
                 auto new_sequence = nb::cast<nb::sequence>(*it_stack.top().iterator++);
                 it_stack.push({ new_sequence.begin(), new_sequence.end() });
-            } else if ((nb::isinstance<PyTypes>(*it_stack.top().iterator) || ...)) {
+            } else if ((nb::isinstance<nb::int_>(*it_stack.top().iterator))) {
+                // Element matching one of the PyTypes found, store it in container
+                result.push_back(nb::cast<nb::object>(*it_stack.top().iterator++));
+            } else {
+                nb::object obj = nb::cast<nb::object>(*it_stack.top().iterator);
+                nb::type_object type = nb::cast<nb::type_object>(obj.type());
+                nb::str type_string = nb::str(type);
+                nb::str repr = nb::repr(obj);
+                std::string repr_string = repr.c_str();
+                throw std::domain_error(
+                    std::string("Non <type>/sequence found when walking: '")
+                    + repr_string + "' of type: '" + type_string.c_str()
+                );
+            }
+        }
+    }
+
+    // Return the result
+    return result;
+}
+template <>
+[[maybe_unused]] std::vector<nanobind::object>
+python_sequence_walk<nanobind::float_, nanobind::int_>(const nanobind::sequence& py_seq)
+{
+    namespace nb = nanobind;
+
+    // Result output iterator
+    std::vector<nb::object> result {};
+
+    // Walk the Python sequences and extract the data
+    struct seq_it_pair {
+        decltype(std::begin(py_seq)) iterator;
+        decltype(std::end(py_seq)) sentinel;
+    };
+    std::stack<seq_it_pair, std::vector<seq_it_pair>> it_stack;
+    it_stack.push({ py_seq.begin(), py_seq.end() });
+
+    while (!it_stack.empty()) {
+        if (it_stack.top().iterator == it_stack.top().sentinel) {
+            // End of current iterator/sentinel pair. Pop it.
+            it_stack.pop();
+        } else {
+            if (nb::isinstance<nb::sequence>(*it_stack.top().iterator)) {
+                // New sequence found. We need to go deeper
+                auto new_sequence = nb::cast<nb::sequence>(*it_stack.top().iterator++);
+                it_stack.push({ new_sequence.begin(), new_sequence.end() });
+            } else if ((nb::isinstance<nb::int_>(*it_stack.top().iterator)
+                        || nb::isinstance<nb::float_>(*it_stack.top().iterator))) {
+                // Element matching one of the PyTypes found, store it in container
+                result.push_back(nb::cast<nb::object>(*it_stack.top().iterator++));
+            } else {
+                nb::object obj = nb::cast<nb::object>(*it_stack.top().iterator);
+                nb::type_object type = nb::cast<nb::type_object>(obj.type());
+                nb::str type_string = nb::str(type);
+                nb::str repr = nb::repr(obj);
+                std::string repr_string = repr.c_str();
+                throw std::domain_error(
+                    std::string("Non <type>/sequence found when walking: '")
+                    + repr_string + "' of type: '" + type_string.c_str()
+                );
+            }
+        }
+    }
+
+    // Return the result
+    return result;
+}
+template <>
+[[maybe_unused]] std::vector<nanobind::object>
+python_sequence_walk<nanobind::int_, nanobind::bool_>(const nanobind::sequence& py_seq)
+{
+    namespace nb = nanobind;
+
+    // Result output iterator
+    std::vector<nb::object> result {};
+
+    // Walk the Python sequences and extract the data
+    struct seq_it_pair {
+        decltype(std::begin(py_seq)) iterator;
+        decltype(std::end(py_seq)) sentinel;
+    };
+    std::stack<seq_it_pair, std::vector<seq_it_pair>> it_stack;
+    it_stack.push({ py_seq.begin(), py_seq.end() });
+
+    while (!it_stack.empty()) {
+        if (it_stack.top().iterator == it_stack.top().sentinel) {
+            // End of current iterator/sentinel pair. Pop it.
+            it_stack.pop();
+        } else {
+            if (nb::isinstance<nb::sequence>(*it_stack.top().iterator)) {
+                // New sequence found. We need to go deeper
+                auto new_sequence = nb::cast<nb::sequence>(*it_stack.top().iterator++);
+                it_stack.push({ new_sequence.begin(), new_sequence.end() });
+            } else if ((nb::isinstance<nb::int_>(*it_stack.top().iterator)
+                        || nb::isinstance<nb::bool_>(*it_stack.top().iterator))) {
                 // Element matching one of the PyTypes found, store it in container
                 result.push_back(nb::cast<nb::object>(*it_stack.top().iterator++));
             } else {


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1e0c7c559..4f5ca364d 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -23,7 +23,7 @@ jobs:
       fail-fast: false
       matrix:
         python-version: ["3.9", "3.10", "3.11", "3.12", "3.13-dev"]
-        os: ["macos-12", "ubuntu-latest", "windows-latest", "macos-14"]
+        os: ["macos-12", "ubuntu-latest", "windows-2019", "macos-14"]
         numpy-version: ["1.26.4", "2.0.0rc2"]
         exclude:
           # Only test NumPy2 on 3.12 for now