Issue #14153 Create _Py_device_encoding() to prevent _io from having to import
the os module.
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
index 9aad479..31eea3c 100644
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -1,9 +1,9 @@
 /*
     An implementation of the new I/O lib as defined by PEP 3116 - "New I/O"
-    
+
     Classes defined here: UnsupportedOperation, BlockingIOError.
     Functions defined here: open().
-    
+
     Mostly written by Amaury Forgeot d'Arc
 */
 
@@ -510,7 +510,7 @@
 
 
 /* Basically the "n" format code with the ability to turn None into -1. */
-int 
+int
 _PyIO_ConvertSsize_t(PyObject *obj, void *result) {
     Py_ssize_t limit;
     if (obj == Py_None) {
@@ -537,7 +537,6 @@
     _PyIO_State *state = IO_MOD_STATE(mod);
     if (!state->initialized)
         return 0;
-    Py_VISIT(state->os_module);
     if (state->locale_module != NULL) {
         Py_VISIT(state->locale_module);
     }
@@ -551,7 +550,6 @@
     _PyIO_State *state = IO_MOD_STATE(mod);
     if (!state->initialized)
         return 0;
-    Py_CLEAR(state->os_module);
     if (state->locale_module != NULL)
         Py_CLEAR(state->locale_module);
     Py_CLEAR(state->unsupported_operation);
@@ -595,11 +593,6 @@
     state = IO_MOD_STATE(m);
     state->initialized = 0;
 
-    /* put os in the module state */
-    state->os_module = PyImport_ImportModule("os");
-    if (state->os_module == NULL)
-        goto fail;
-
 #define ADD_TYPE(type, name) \
     if (PyType_Ready(type) < 0) \
         goto fail; \
@@ -725,7 +718,6 @@
     return m;
 
   fail:
-    Py_XDECREF(state->os_module);
     Py_XDECREF(state->unsupported_operation);
     Py_DECREF(m);
     return NULL;
diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h
index b3a8471..987aac8 100644
--- a/Modules/_io/_iomodule.h
+++ b/Modules/_io/_iomodule.h
@@ -50,8 +50,8 @@
    `*consumed`.
    If not found, returns -1 and sets `*consumed` to the number of characters
    which can be safely put aside until another search.
-   
-   NOTE: for performance reasons, `end` must point to a NUL character ('\0'). 
+
+   NOTE: for performance reasons, `end` must point to a NUL character ('\0').
    Otherwise, the function will scan further and return garbage. */
 extern Py_ssize_t _PyIO_find_line_ending(
     int translated, int universal, PyObject *readnl,
@@ -124,7 +124,6 @@
 
 typedef struct {
     int initialized;
-    PyObject *os_module;
     PyObject *locale_module;
 
     PyObject *unsupported_operation;
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 79c64ba..833a527 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -14,7 +14,6 @@
 _Py_IDENTIFIER(close);
 _Py_IDENTIFIER(_dealloc_warn);
 _Py_IDENTIFIER(decode);
-_Py_IDENTIFIER(device_encoding);
 _Py_IDENTIFIER(fileno);
 _Py_IDENTIFIER(flush);
 _Py_IDENTIFIER(getpreferredencoding);
@@ -875,9 +874,13 @@
             }
         }
         else {
-            self->encoding = _PyObject_CallMethodId(state->os_module,
-                                                    &PyId_device_encoding,
-                                                    "N", fileno);
+            int fd = (int) PyLong_AsLong(fileno);
+            Py_DECREF(fileno);
+            if (fd == -1 && PyErr_Occurred()) {
+                goto error;
+            }
+
+            self->encoding = _Py_device_encoding(fd);
             if (self->encoding == NULL)
                 goto error;
             else if (!PyUnicode_Check(self->encoding))