diff --git a/ports/bare-arm/mpconfigport.h b/ports/bare-arm/mpconfigport.h
index 3fbd3769f1e90203587447f19c6d14a0db3bb6d5..5734bc648e26e8936774ee608b132bd3311bcc31 100644
--- a/ports/bare-arm/mpconfigport.h
+++ b/ports/bare-arm/mpconfigport.h
@@ -29,6 +29,7 @@
 #define MICROPY_PY_BUILTINS_SET     (0)
 #define MICROPY_PY_BUILTINS_SLICE   (0)
 #define MICROPY_PY_BUILTINS_PROPERTY (0)
+#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0)
 #define MICROPY_PY___FILE__         (0)
 #define MICROPY_PY_GC               (0)
 #define MICROPY_PY_ARRAY            (0)
diff --git a/ports/minimal/mpconfigport.h b/ports/minimal/mpconfigport.h
index 8744ca95081f409007eea4d4c5abe28c3757fa9c..20a21ce839a22921b786edcac7afc3678f9753d8 100644
--- a/ports/minimal/mpconfigport.h
+++ b/ports/minimal/mpconfigport.h
@@ -40,6 +40,7 @@
 #define MICROPY_PY_BUILTINS_SLICE   (0)
 #define MICROPY_PY_BUILTINS_PROPERTY (0)
 #define MICROPY_PY_BUILTINS_MIN_MAX (0)
+#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0)
 #define MICROPY_PY___FILE__         (0)
 #define MICROPY_PY_GC               (0)
 #define MICROPY_PY_ARRAY            (0)
diff --git a/ports/unix/mpconfigport_minimal.h b/ports/unix/mpconfigport_minimal.h
index ef7a1a09a0e564b63ed7a813e7f6964147b27345..95311618d9e6e250f1e63b7f7cdb34d0bd06359c 100644
--- a/ports/unix/mpconfigport_minimal.h
+++ b/ports/unix/mpconfigport_minimal.h
@@ -65,6 +65,7 @@
 #define MICROPY_PY_BUILTINS_REVERSED (0)
 #define MICROPY_PY_BUILTINS_SET     (0)
 #define MICROPY_PY_BUILTINS_SLICE   (0)
+#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0)
 #define MICROPY_PY_BUILTINS_STR_UNICODE (0)
 #define MICROPY_PY_BUILTINS_PROPERTY (0)
 #define MICROPY_PY_BUILTINS_MIN_MAX (0)
diff --git a/py/mpconfig.h b/py/mpconfig.h
index e0a0f0d5af8c588e824ac650a35038ba5bfe8a82..8f141140576648baac91e1aca4cbca7d2f7c2003 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -759,6 +759,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_BUILTINS_STR_CENTER (0)
 #endif
 
+// Whether str % (...) formatting operator provided
+#ifndef MICROPY_PY_BUILTINS_STR_OP_MODULO
+#define MICROPY_PY_BUILTINS_STR_OP_MODULO (1)
+#endif
+
 // Whether str.partition()/str.rpartition() method provided
 #ifndef MICROPY_PY_BUILTINS_STR_PARTITION
 #define MICROPY_PY_BUILTINS_STR_PARTITION (0)
diff --git a/py/objstr.c b/py/objstr.c
index 397e5ccdee5b66efba12d83f79ea3d46490b442c..f9dcb28c5dd631f6417a22efeb3c5045f0e72d9d 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -34,7 +34,9 @@
 #include "py/runtime.h"
 #include "py/stackctrl.h"
 
+#if MICROPY_PY_BUILTINS_STR_OP_MODULO
 STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict);
+#endif
 
 STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf);
 STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in);
@@ -301,6 +303,7 @@ const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle,
 mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     // check for modulo
     if (op == MP_BINARY_OP_MODULO) {
+        #if MICROPY_PY_BUILTINS_STR_OP_MODULO
         mp_obj_t *args = &rhs_in;
         size_t n_args = 1;
         mp_obj_t dict = MP_OBJ_NULL;
@@ -311,6 +314,9 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
             dict = rhs_in;
         }
         return str_modulo_format(lhs_in, n_args, args, dict);
+        #else
+        return MP_OBJ_NULL;
+        #endif
     }
 
     // from now on we need lhs type and data, so extract them
@@ -915,6 +921,7 @@ STATIC bool arg_looks_numeric(mp_obj_t arg) {
     ;
 }
 
+#if MICROPY_PY_BUILTINS_STR_OP_MODULO
 STATIC mp_obj_t arg_as_int(mp_obj_t arg) {
 #if MICROPY_PY_BUILTINS_FLOAT
     if (mp_obj_is_float(arg)) {
@@ -923,6 +930,7 @@ STATIC mp_obj_t arg_as_int(mp_obj_t arg) {
 #endif
     return arg;
 }
+#endif
 
 #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
 STATIC NORETURN void terse_str_format_value_error(void) {
@@ -1383,6 +1391,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
 }
 MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format);
 
+#if MICROPY_PY_BUILTINS_STR_OP_MODULO
 STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict) {
     mp_check_self(MP_OBJ_IS_STR_OR_BYTES(pattern));
 
@@ -1578,6 +1587,7 @@ not_enough_args:
 
     return mp_obj_new_str_from_vstr(is_bytes ? &mp_type_bytes : &mp_type_str, &vstr);
 }
+#endif
 
 // The implementation is optimized, returning the original string if there's
 // nothing to replace.