diff --git a/py/misc.h b/py/misc.h
index 013b5f123ecfac6a6e8d07f7323706a58acc2c95..fd54147efdb3f0523f53855312082d9e129d3284 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -96,6 +96,8 @@ bool unichar_isalpha(unichar c);
 bool unichar_isprint(unichar c);
 bool unichar_isdigit(unichar c);
 bool unichar_isxdigit(unichar c);
+bool unichar_isupper(unichar c);
+bool unichar_islower(unichar c);
 unichar unichar_tolower(unichar c);
 unichar unichar_toupper(unichar c);
 
diff --git a/py/objstr.c b/py/objstr.c
index d095c8b471b0315ded4a7020762de8a7b32ce060..729ed7a85531af683517b3542718002aba8f2bd5 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -1502,6 +1502,71 @@ STATIC mp_obj_t str_upper(mp_obj_t self_in) {
     return str_caseconv(CASE_UPPER, self_in);
 }
 
+enum { IS_SPACE, IS_ALPHA, IS_DIGIT, IS_UPPER, IS_LOWER };
+
+STATIC mp_obj_t str_uni_istype(int type, mp_obj_t self_in) {
+    GET_STR_DATA_LEN(self_in, self_data, self_len);
+    
+    if (self_len == 0) return mp_const_false; // default to False for empty str
+        
+    typedef bool (*check_function)(unichar);
+    check_function f;
+    
+    if (type != IS_UPPER && type != IS_LOWER) {
+        switch (type) {
+            case IS_SPACE: f = &unichar_isspace; break;
+            case IS_ALPHA: f = &unichar_isalpha; break;
+            case IS_DIGIT: f = &unichar_isdigit; break;
+            default:
+                nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "unknown type provided for str_uni_istype"));
+        }
+    
+        for (int i = 0; i < self_len; i++) {
+            if (!f(*self_data++)) return mp_const_false;
+        }
+    } else {
+        switch (type) {
+            case IS_UPPER: f = &unichar_isupper; break;
+            case IS_LOWER: f = &unichar_islower; break;
+            default:
+                nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "unknown type provided for str_uni_istype"));
+        }
+        
+        bool contains_alpha = false;
+        
+        for (int i = 0; i < self_len; i++) { // only check alphanumeric characters
+            if (unichar_isalpha(*self_data++)) {
+                contains_alpha = true;
+                if (!f(*(self_data-1))) return mp_const_false; // we already incremented
+            }
+        }
+        
+        if (!(contains_alpha)) return mp_const_false;
+    }
+
+    return mp_const_true;
+}
+
+STATIC mp_obj_t str_isspace(mp_obj_t self_in) {
+    return str_uni_istype(IS_SPACE, self_in);
+}
+
+STATIC mp_obj_t str_isalpha(mp_obj_t self_in) {
+    return str_uni_istype(IS_ALPHA, self_in);
+}
+
+STATIC mp_obj_t str_isdigit(mp_obj_t self_in) {
+    return str_uni_istype(IS_DIGIT, self_in);
+}
+
+STATIC mp_obj_t str_isupper(mp_obj_t self_in) {
+    return str_uni_istype(IS_UPPER, self_in);
+}
+
+STATIC mp_obj_t str_islower(mp_obj_t self_in) {
+    return str_uni_istype(IS_LOWER, self_in);
+}
+
 #if MICROPY_CPYTHON_COMPAT
 // These methods are superfluous in the presense of str() and bytes()
 // constructors.
@@ -1569,6 +1634,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(str_partition_obj, str_partition);
 STATIC MP_DEFINE_CONST_FUN_OBJ_2(str_rpartition_obj, str_rpartition);
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_lower_obj, str_lower);
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_upper_obj, str_upper);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_isspace_obj, str_isspace);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_isalpha_obj, str_isalpha);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_isdigit_obj, str_isdigit);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_isupper_obj, str_isupper);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(str_islower_obj, str_islower);
 
 STATIC const mp_map_elem_t str_locals_dict_table[] = {
 #if MICROPY_CPYTHON_COMPAT
@@ -1594,6 +1664,11 @@ STATIC const mp_map_elem_t str_locals_dict_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_rpartition), (mp_obj_t)&str_rpartition_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_lower), (mp_obj_t)&str_lower_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_upper), (mp_obj_t)&str_upper_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_isspace), (mp_obj_t)&str_isspace_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_isalpha), (mp_obj_t)&str_isalpha_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_isdigit), (mp_obj_t)&str_isdigit_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_isupper), (mp_obj_t)&str_isupper_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_islower), (mp_obj_t)&str_islower_obj },
 };
 
 STATIC MP_DEFINE_CONST_DICT(str_locals_dict, str_locals_dict_table);
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 2d4ddfea2980dc7a52d2ca4fa3380604b9730cdd..4ee56db909fc78a0cbcca7fdb81a31be00685e16 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -246,6 +246,11 @@ Q(partition)
 Q(rpartition)
 Q(lower)
 Q(upper)
+Q(isspace)
+Q(isalpha)
+Q(isdigit)
+Q(isupper)
+Q(islower)
 Q(iterable)
 Q(start)
 
diff --git a/tests/basics/string_istest.py b/tests/basics/string_istest.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ea6c4508fcc6a9c8e6f0575b552d137bf2e3a0b
--- /dev/null
+++ b/tests/basics/string_istest.py
@@ -0,0 +1,20 @@
+print("".isspace())
+print(" \t\n\r\v\f".isspace())
+print("a".isspace())
+print(" \t\n\r\v\fa".isspace())
+print("".isalpha())
+print("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".isalpha())
+print("0abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".isalpha())
+print("this ".isalpha())
+print("".isdigit())
+print("0123456789".isdigit())
+print("0123456789a".isdigit())
+print("0123456789 ".isdigit())
+print("".isupper())
+print("CHEESE-CAKE WITH ... _FROSTING_*99".isupper())
+print("aB".isupper())
+print("".islower())
+print("cheese-cake with ... _frosting_*99".islower())
+print("aB".islower())
+print("123".islower())
+print("123a".islower())