| 2458 | " None."; |
| 2459 | |
| 2460 | static PyObject *Str_write_to(PyObject *self, PyObject *const *args, Py_ssize_t positional_args_count, |
| 2461 | PyObject *args_names_tuple) { |
| 2462 | |
| 2463 | int is_member = self != NULL && PyObject_TypeCheck(self, &StrType); |
| 2464 | if (positional_args_count != !is_member + 1) { |
| 2465 | PyErr_SetString(PyExc_TypeError, "Invalid number of arguments"); |
| 2466 | return NULL; |
| 2467 | } |
| 2468 | |
| 2469 | PyObject *text_obj = is_member ? self : args[0]; |
| 2470 | PyObject *path_obj = args[!is_member + 0]; |
| 2471 | |
| 2472 | // Parse keyword arguments |
| 2473 | if (args_names_tuple) { |
| 2474 | PyErr_Format(PyExc_TypeError, "Got an unexpected keyword argument"); |
| 2475 | return NULL; |
| 2476 | } |
| 2477 | |
| 2478 | sz_string_view_t text; |
| 2479 | sz_string_view_t path; |
| 2480 | |
| 2481 | // Validate and convert `text` and `path` |
| 2482 | if (!sz_py_export_string_like(text_obj, &text.start, &text.length) || |
| 2483 | !sz_py_export_string_like(path_obj, &path.start, &path.length)) { |
| 2484 | wrap_current_exception("Text and path must be string-like"); |
| 2485 | return NULL; |
| 2486 | } |
| 2487 | |
| 2488 | // There is a chance, the path isn't NULL-terminated, so copy it to a new buffer. |
| 2489 | // Many OSes have fairly low limit for the maximum path length. |
| 2490 | // On Windows its 260, but up to __around__ 32,767 characters are supported in extended API. |
| 2491 | // But it's better to be safe than sorry and use malloc :) |
| 2492 | // |
| 2493 | // https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry |
| 2494 | // https://doc.owncloud.com/server/next/admin_manual/troubleshooting/path_filename_length.html |
| 2495 | sz_ptr_t path_buffer = (sz_ptr_t)malloc(path.length + 1); |
| 2496 | if (path_buffer == NULL) { |
| 2497 | PyErr_SetString(PyExc_MemoryError, "Unable to allocate memory for the path"); |
| 2498 | return NULL; |
| 2499 | } |
| 2500 | sz_copy(path_buffer, path.start, path.length); |
| 2501 | path_buffer[path.length] = '\0'; |
| 2502 | |
| 2503 | // Unlock the Global Interpreter Lock (GIL) to allow other threads to run |
| 2504 | // while the current thread is waiting for the file to be written. |
| 2505 | PyThreadState *gil_state = PyEval_SaveThread(); |
| 2506 | FILE *file_pointer = fopen(path_buffer, "wb"); |
| 2507 | if (file_pointer == NULL) { |
| 2508 | PyEval_RestoreThread(gil_state); |
| 2509 | PyErr_SetFromErrnoWithFilename(PyExc_OSError, path_buffer); |
| 2510 | free(path_buffer); |
| 2511 | PyEval_RestoreThread(gil_state); |
| 2512 | return NULL; |
| 2513 | } |
| 2514 | |
| 2515 | setbuf(file_pointer, NULL); // Set the stream to unbuffered |
| 2516 | int status = fwrite(text.start, 1, text.length, file_pointer); |
| 2517 | PyEval_RestoreThread(gil_state); |
nothing calls this directly
no test coverage detected
searching dependent graphs…