| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -216,6 +216,38 @@ Operating System Utilities | |||
| 216 | 216 | The function now uses the UTF-8 encoding on Windows if | |
| 217 | 217 | :c:member:`PyPreConfig.legacy_windows_fs_encoding` is zero. | |
| 218 | 218 | ||
| 219 | + .. c:function:: FILE* Py_fopen(PyObject *path, const char *mode) | ||
| 220 | + | ||
| 221 | + Similar to :c:func:`!fopen`, but *path* is a Python object and | ||
| 222 | + an exception is set on error. | ||
| 223 | + | ||
| 224 | + *path* must be a :class:`str` object, a :class:`bytes` object, | ||
| 225 | + or a :term:`path-like object`. | ||
| 226 | + | ||
| 227 | + On success, return the new file pointer. | ||
| 228 | + On error, set an exception and return ``NULL``. | ||
| 229 | + | ||
| 230 | + The file must be closed by :c:func:`Py_fclose` rather than calling directly | ||
| 231 | + :c:func:`!fclose`. | ||
| 232 | + | ||
| 233 | + The file descriptor is created non-inheritable (:pep:`446`). | ||
| 234 | + | ||
| 235 | + The caller must hold the GIL. | ||
| 236 | + | ||
| 237 | + .. versionadded:: next | ||
| 238 | + | ||
| 239 | + | ||
| 240 | + .. c:function:: int Py_fclose(FILE *file) | ||
| 241 | + | ||
| 242 | + Close a file that was opened by :c:func:`Py_fopen`. | ||
| 243 | + | ||
| 244 | + On success, return ``0``. | ||
| 245 | + On error, return ``EOF`` and ``errno`` is set to indicate the error. | ||
| 246 | + In either case, any further access (including another call to | ||
| 247 | + :c:func:`Py_fclose`) to the stream results in undefined behavior. | ||
| 248 | + | ||
| 249 | + .. versionadded:: next | ||
| 250 | + | ||
| 219 | 251 | ||
| 220 | 252 | .. _systemfunctions: | |
| 221 | 253 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1237,6 +1237,12 @@ New features | |||
| 1237 | 1237 | :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT` | |
| 1238 | 1238 | events, respectively. | |
| 1239 | 1239 | ||
| 1240 | + * Add :c:func:`Py_fopen` function to open a file. Similar to the | ||
| 1241 | + :c:func:`!fopen` function, but the *path* parameter is a Python object and an | ||
| 1242 | + exception is set on error. Add also :c:func:`Py_fclose` function to close a | ||
| 1243 | + file. | ||
| 1244 | + (Contributed by Victor Stinner in :gh:`127350`.) | ||
| 1245 | + | ||
| 1240 | 1246 | ||
| 1241 | 1247 | Porting to Python 3.14 | |
| 1242 | 1248 | ---------------------- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,13 @@ | |||
| 2 | 2 | # error "this header file must not be included directly" | |
| 3 | 3 | #endif | |
| 4 | 4 | ||
| 5 | - // Used by _testcapi which must not use the internal C API | ||
| 6 | - PyAPI_FUNC(FILE*) _Py_fopen_obj( | ||
| 5 | + PyAPI_FUNC(FILE*) Py_fopen( | ||
| 7 | 6 | PyObject *path, | |
| 8 | 7 | const char *mode); | |
| 8 | + | ||
| 9 | + // Deprecated alias to Py_fopen() kept for backward compatibility | ||
| 10 | + Py_DEPRECATED(3.14) PyAPI_FUNC(FILE*) _Py_fopen_obj( | ||
| 11 | + PyObject *path, | ||
| 12 | + const char *mode); | ||
| 13 | + | ||
| 14 | + PyAPI_FUNC(int) Py_fclose(FILE *file); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,67 @@ | |||
| 1 | + import os | ||
| 2 | + import unittest | ||
| 3 | + from test import support | ||
| 4 | + from test.support import import_helper, os_helper | ||
| 5 | + | ||
| 6 | + _testcapi = import_helper.import_module('_testcapi') | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + class CAPIFileTest(unittest.TestCase): | ||
| 10 | + def test_py_fopen(self): | ||
| 11 | + # Test Py_fopen() and Py_fclose() | ||
| 12 | + | ||
| 13 | + with open(__file__, "rb") as fp: | ||
| 14 | + source = fp.read() | ||
| 15 | + | ||
| 16 | + for filename in (__file__, os.fsencode(__file__)): | ||
| 17 | + with self.subTest(filename=filename): | ||
| 18 | + data = _testcapi.py_fopen(filename, "rb") | ||
| 19 | + self.assertEqual(data, source[:256]) | ||
| 20 | + | ||
| 21 | + data = _testcapi.py_fopen(os_helper.FakePath(filename), "rb") | ||
| 22 | + self.assertEqual(data, source[:256]) | ||
| 23 | + | ||
| 24 | + filenames = [ | ||
| 25 | + os_helper.TESTFN, | ||
| 26 | + os.fsencode(os_helper.TESTFN), | ||
| 27 | + ] | ||
| 28 | + # TESTFN_UNDECODABLE cannot be used to create a file on macOS/WASI. | ||
| 29 | + if os_helper.TESTFN_UNENCODABLE is not None: | ||
| 30 | + filenames.append(os_helper.TESTFN_UNENCODABLE) | ||
| 31 | + for filename in filenames: | ||
| 32 | + with self.subTest(filename=filename): | ||
| 33 | + try: | ||
| 34 | + with open(filename, "wb") as fp: | ||
| 35 | + fp.write(source) | ||
| 36 | + | ||
| 37 | + data = _testcapi.py_fopen(filename, "rb") | ||
| 38 | + self.assertEqual(data, source[:256]) | ||
| 39 | + finally: | ||
| 40 | + os_helper.unlink(filename) | ||
| 41 | + | ||
| 42 | + # embedded null character/byte in the filename | ||
| 43 | + with self.assertRaises(ValueError): | ||
| 44 | + _testcapi.py_fopen("a\x00b", "rb") | ||
| 45 | + with self.assertRaises(ValueError): | ||
| 46 | + _testcapi.py_fopen(b"a\x00b", "rb") | ||
| 47 | + | ||
| 48 | + # non-ASCII mode failing with "Invalid argument" | ||
| 49 | + with self.assertRaises(OSError): | ||
| 50 | + _testcapi.py_fopen(__file__, "\xe9") | ||
| 51 | + | ||
| 52 | + # invalid filename type | ||
| 53 | + for invalid_type in (123, object()): | ||
| 54 | + with self.subTest(filename=invalid_type): | ||
| 55 | + with self.assertRaises(TypeError): | ||
| 56 | + _testcapi.py_fopen(invalid_type, "rb") | ||
| 57 | + | ||
| 58 | + if support.MS_WINDOWS: | ||
| 59 | + with self.assertRaises(OSError): | ||
| 60 | + # On Windows, the file mode is limited to 10 characters | ||
| 61 | + _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8") | ||
| 62 | + | ||
| 63 | + # CRASHES py_fopen(__file__, None) | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + if __name__ == "__main__": | ||
| 67 | + unittest.main() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1325,8 +1325,7 @@ def test_load_verify_cadata(self): | |||
| 1325 | 1325 | def test_load_dh_params(self): | |
| 1326 | 1326 | ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | |
| 1327 | 1327 | ctx.load_dh_params(DHFILE) | |
| 1328 | - if os.name != 'nt': | ||
| 1329 | - ctx.load_dh_params(BYTES_DHFILE) | ||
| 1328 | + ctx.load_dh_params(BYTES_DHFILE) | ||
| 1330 | 1329 | self.assertRaises(TypeError, ctx.load_dh_params) | |
| 1331 | 1330 | self.assertRaises(TypeError, ctx.load_dh_params, None) | |
| 1332 | 1331 | with self.assertRaises(FileNotFoundError) as cm: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + Add :c:func:`Py_fopen` function to open a file. Similar to the :c:func:`!fopen` | ||
| 2 | + function, but the *path* parameter is a Python object and an exception is set | ||
| 3 | + on error. Add also :c:func:`Py_fclose` function to close a file, function | ||
| 4 | + needed for Windows support. | ||
| 5 | + Patch by Victor Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4377,7 +4377,7 @@ _ssl__SSLContext_load_dh_params_impl(PySSLContext *self, PyObject *filepath) | |||
| 4377 | 4377 | FILE *f; | |
| 4378 | 4378 | DH *dh; | |
| 4379 | 4379 | ||
| 4380 | - f = _Py_fopen_obj(filepath, "rb"); | ||
| 4380 | + f = Py_fopen(filepath, "rb"); | ||
| 4381 | 4381 | if (f == NULL) | |
| 4382 | 4382 | return NULL; | |
| 4383 | 4383 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -180,8 +180,8 @@ _PySSLContext_set_keylog_filename(PySSLContext *self, PyObject *arg, void *c) { | |||
| 180 | 180 | return 0; | |
| 181 | 181 | } | |
| 182 | 182 | ||
| 183 | - /* _Py_fopen_obj() also checks that arg is of proper type. */ | ||
| 184 | - fp = _Py_fopen_obj(arg, "a" PY_STDIOTEXTMODE); | ||
| 183 | + /* Py_fopen() also checks that arg is of proper type. */ | ||
| 184 | + fp = Py_fopen(arg, "a" PY_STDIOTEXTMODE); | ||
| 185 | 185 | if (fp == NULL) | |
| 186 | 186 | return -1; | |
| 187 | 187 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,8 +1,43 @@ | |||
| 1 | + // clinic/file.c.h uses internal pycore_modsupport.h API | ||
| 2 | + #define PYTESTCAPI_NEED_INTERNAL_API | ||
| 3 | + | ||
| 1 | 4 | #include "parts.h" | |
| 2 | 5 | #include "util.h" | |
| 6 | + #include "clinic/file.c.h" | ||
| 7 | + | ||
| 8 | + /*[clinic input] | ||
| 9 | + module _testcapi | ||
| 10 | + [clinic start generated code]*/ | ||
| 11 | + /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ | ||
| 12 | + | ||
| 13 | + /*[clinic input] | ||
| 14 | + _testcapi.py_fopen | ||
| 15 | + | ||
| 16 | + path: object | ||
| 17 | + mode: str | ||
| 18 | + / | ||
| 19 | + | ||
| 20 | + Call Py_fopen(), fread(256) and Py_fclose(). Return read bytes. | ||
| 21 | + [clinic start generated code]*/ | ||
| 3 | 22 | ||
| 23 | + static PyObject * | ||
| 24 | + _testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode) | ||
| 25 | + /*[clinic end generated code: output=5a900af000f759de input=d7e7b8f0fd151953]*/ | ||
| 26 | + { | ||
| 27 | + FILE *fp = Py_fopen(path, mode); | ||
| 28 | + if (fp == NULL) { | ||
| 29 | + return NULL; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + char buffer[256]; | ||
| 33 | + size_t size = fread(buffer, 1, Py_ARRAY_LENGTH(buffer), fp); | ||
| 34 | + Py_fclose(fp); | ||
| 35 | + | ||
| 36 | + return PyBytes_FromStringAndSize(buffer, size); | ||
| 37 | + } | ||
| 4 | 38 | ||
| 5 | 39 | static PyMethodDef test_methods[] = { | |
| 40 | + _TESTCAPI_PY_FOPEN_METHODDEF | ||
| 6 | 41 | {NULL}, | |
| 7 | 42 | }; | |
| 8 | 43 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments