py: Make print() accept "file" argument, and actually print to stream.

And not system printf(), like it was before. For this, move pfenv_printf()
from stmhal port to py/.
diff --git a/py/builtin.c b/py/builtin.c
index e723fad..7256133 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -36,6 +36,8 @@
 #include "runtime0.h"
 #include "runtime.h"
 #include "builtin.h"
+#include "stream.h"
+#include "pfenv.h"
 
 #if MICROPY_PY_BUILTINS_FLOAT
 #include <math.h>
@@ -424,13 +426,36 @@
     if (end_elem != NULL && end_elem->value != mp_const_none) {
         end_data = mp_obj_str_get_data(end_elem->value, &end_len);
     }
+    #if MICROPY_PY_IO
+    mp_obj_t stream_obj = &mp_sys_stdout_obj;
+    mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP);
+    if (file_elem != NULL && file_elem->value != mp_const_none) {
+        stream_obj = file_elem->value;
+    }
+
+    pfenv_t pfenv;
+    pfenv.data = stream_obj;
+    pfenv.print_strn = (void (*)(void *, const char *, unsigned int))mp_stream_write;
+    #endif
     for (int i = 0; i < n_args; i++) {
         if (i > 0) {
+            #if MICROPY_PY_IO
+            mp_stream_write(stream_obj, sep_data, sep_len);
+            #else
             printf("%.*s", sep_len, sep_data);
+            #endif
         }
+        #if MICROPY_PY_IO
+        mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, args[i], PRINT_STR);
+        #else
         mp_obj_print(args[i], PRINT_STR);
+        #endif
     }
+    #if MICROPY_PY_IO
+    mp_stream_write(stream_obj, end_data, end_len);
+    #else
     printf("%.*s", end_len, end_data);
+    #endif
     return mp_const_none;
 }