diff --git a/miltermodule.c b/miltermodule.c
index c6810057f28ba2fb78d815fb2d7707687d650bae..3700d0fd859cfefe57233b438acc5e4dfb3317d3 100644
--- a/miltermodule.c
+++ b/miltermodule.c
@@ -343,7 +343,11 @@ static struct MilterCallback {
       { NULL , NULL }
     };
 
-staticforward struct smfiDesc description; /* forward declaration */
+#if PY_MAJOR_VERSION >= 3
+        static struct smfiDesc description; /* forward declaration */
+#else
+        staticforward struct smfiDesc description; /* forward declaration */
+#endif
 
 static PyObject *MilterError;
 /* The interpreter instance that called milter.main */
@@ -355,7 +359,11 @@ typedef struct {
 
 static milter_Diag diag;
 
-staticforward PyTypeObject milter_ContextType;
+#if PY_MAJOR_VERSION >= 3
+        static PyTypeObject milter_ContextType;
+#else
+        staticforward PyTypeObject milter_ContextType;
+#endif
 
 typedef struct {
   PyObject_HEAD
@@ -700,7 +708,11 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
   result = PyEval_CallObject(cb, arglist);
   Py_DECREF(arglist);
   if (result == NULL) return _report_exception(self);
+#if PY_MAJOR_VERSION >= 3
+  if (!PyLong_Check(result)) {
+#else
   if (!PyInt_Check(result)) {
+#endif
     const struct MilterCallback *p;
     const char *cbname = "milter";
     char buf[40];
@@ -715,7 +727,11 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
     PyErr_SetString(MilterError,buf);
     return _report_exception(self);
   }
+#if PY_MAJOR_VERSION >= 3
+  retval = PyLong_AS_LONG(result);
+#else
   retval = PyInt_AS_LONG(result);
+#endif
   Py_DECREF(result);
   _release_thread(self->t);
   return retval;
@@ -732,7 +748,11 @@ makeipaddr(struct sockaddr_in *addr) {
 	sprintf(buf, "%d.%d.%d.%d",
 		(int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
 		(int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
+#if PY_MAJOR_VERSION >= 3
+	return PyUnicode_FromString(buf);
+#else
 	return PyString_FromString(buf);
+#endif
 }
 
 #ifdef HAVE_IPV6_SUPPORT
@@ -740,8 +760,13 @@ static PyObject *
 makeip6addr(struct sockaddr_in6 *addr) {
 	char buf[100]; /* must be at least INET6_ADDRSTRLEN + 1 */
 	const char *s = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof buf);
-	if (s) return PyString_FromString(s);
-	return PyString_FromString("inet6:unknown");
+#if PY_MAJOR_VERSION >= 3
+       if (s) return PyUnicode_FromString(s);
+       return PyUnicode_FromString("inet6:unknown");
+#else
+       if (s) return PyString_FromString(s);
+       return PyString_FromString("inet6:unknown");
+#endif
 }
 #endif
 
@@ -832,7 +857,11 @@ generic_env_wrapper(SMFICTX *ctx, PyObject*cb, char **argv) {
    for (i=0;i<count;i++) {
      /* There's some error checking performed in do_mkvalue() for a string */
      /* that's not currently done here - it probably should be */
+#if PY_MAJOR_VERSION >= 3
+     PyObject *o = PyUnicode_FromStringAndSize(argv[i], strlen(argv[i]));
+#else
      PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i]));
+#endif
      if (o == NULL) {	/* out of memory */
        Py_DECREF(arglist);
        return _report_exception(self);
@@ -889,7 +918,11 @@ milter_wrap_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen) {
    c = _get_context(ctx);
    if (!c) return SMFIS_TEMPFAIL;
    /* Unclear whether this should be s#, z#, or t# */
+#if PY_MAJOR_VERSION >= 3
+   arglist = Py_BuildValue("(Oy#)", c, bodyp, bodylen);
+#else
    arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
+#endif
    return _generic_wrapper(c, body_callback, arglist);
 }
 
@@ -963,7 +996,11 @@ milter_wrap_negotiate(SMFICTX *ctx,
     int i;
     for (i = 0; i < 4; ++i) {
       *pa[i] = (i <= len)
-      	? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
+#if PY_MAJOR_VERSION >= 3
+       ? PyLong_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
+#else
+       ? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
+#endif
 	: fa[i];
     }
     if (PyErr_Occurred()) {
@@ -1551,10 +1588,12 @@ static PyMethodDef context_methods[] = {
   { NULL, NULL }
 };
 
+#if PY_MAJOR_VERSION < 3
 static PyObject *
 milter_Context_getattr(PyObject *self, char *name) {
   return Py_FindMethod(context_methods, self, name);
 }
+#endif
 
 static struct smfiDesc description = {  /* Set some reasonable defaults */
   "pythonfilter",
@@ -1604,14 +1643,23 @@ static PyMethodDef milter_methods[] = {
 };
 
 static PyTypeObject milter_ContextType = {
+#if PY_MAJOR_VERSION >= 3
+  PyVarObject_HEAD_INIT(&PyType_Type,0)
+  "milter.Context",
+#else
   PyObject_HEAD_INIT(&PyType_Type)
   0,
   "milterContext",
+#endif
   sizeof(milter_ContextObject),
   0,
         milter_Context_dealloc,            /* tp_dealloc */
         0,               /* tp_print */
+#if PY_MAJOR_VERSION >= 3
+        0,           /* tp_getattr */
+#else
         milter_Context_getattr,           /* tp_getattr */
+#endif
         0,			/* tp_setattr */
         0,                                      /* tp_compare */
         0,                 /* tp_repr */
@@ -1625,6 +1673,15 @@ static PyTypeObject milter_ContextType = {
         0,                                      /* tp_setattro */
         0,                                      /* tp_as_buffer */
         Py_TPFLAGS_DEFAULT,                     /* tp_flags */
+#if PY_MAJOR_VERSION >= 3
+        NULL,   /* Documentation string */
+        0,      /* call function for all accessible objects */
+        0,      /* delete references to contained objects */
+        0,      /* rich comparisons */
+        0,      /* weak reference enabler */
+        0, 0,   /* Iterators */
+        context_methods, /* Attribute descriptor and subclassing stuff */
+#endif
 };
 
 static const char milter_documentation[] =
@@ -1634,17 +1691,45 @@ Libmilter is currently marked FFR, and needs to be explicitly installed.\n\
 See <sendmailsource>/libmilter/README for details on setting it up.\n";
 
 static void setitem(PyObject *d,const char *name,long val) {
-  PyObject *v = PyInt_FromLong(val);
+#if PY_MAJOR_VERSION >= 3
+  PyObject *v = PyLong_FromLong(val);
   PyDict_SetItemString(d,name,v);
   Py_DECREF(v);
 }
+ 
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "milter",           /* m_name */
+    milter_documentation,/* m_doc */
+    -1,                  /* m_size */
+    milter_methods,      /* m_methods */
+    NULL,                /* m_reload */
+    NULL,                /* m_traverse */
+    NULL,                /* m_clear */
+    NULL,                /* m_free */
+};
 
+PyMODINIT_FUNC PyInit_milter(void) {
+    PyObject *m, *d;
+ 
+   if (PyType_Ready(&milter_ContextType) < 0)
+          return NULL;
+
+   m = PyModule_Create(&moduledef);
+   if (m == NULL) return NULL;
+#else
+  PyObject *v = PyInt_FromLong(val);
+  PyDict_SetItemString(d,name,v);
+  Py_DECREF(v);
+}
+ 
 void
 initmilter(void) {
    PyObject *m, *d;
 
    m = Py_InitModule4("milter", milter_methods, milter_documentation,
-		      (PyObject*)NULL, PYTHON_API_VERSION);
+	              (PyObject*)NULL, PYTHON_API_VERSION);
+#endif
    d = PyModule_GetDict(m);
    MilterError = PyErr_NewException("milter.error", NULL, NULL);
    PyDict_SetItemString(d,"error", MilterError);
@@ -1710,4 +1795,7 @@ initmilter(void) {
    setitem(d,"DISCARD",  SMFIS_DISCARD);
    setitem(d,"ACCEPT",  SMFIS_ACCEPT);
    setitem(d,"TEMPFAIL",  SMFIS_TEMPFAIL);
+#if PY_MAJOR_VERSION >= 3
+   return m;
+#endif
 }