diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 37843f31..2f835d76 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,7 +26,9 @@ jobs:
- "3.10"
- "3.11"
- "3.12"
+ - "3.13"
- "pypy3.9"
+ - "pypy3.10"
image:
- "ubuntu-22.04"
include:
@@ -43,7 +45,7 @@ jobs:
- name: Disable AppArmor
run: sudo aa-disable /usr/sbin/slapd
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
diff --git a/.github/workflows/tox-fedora.yml b/.github/workflows/tox-fedora.yml
index b86303fe..4c4c18f0 100644
--- a/.github/workflows/tox-fedora.yml
+++ b/.github/workflows/tox-fedora.yml
@@ -9,7 +9,7 @@ jobs:
tox_test:
name: Tox env "${{matrix.tox_env}}" on Fedora
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Run Tox tests
uses: fedora-python/tox-github-action@main
with:
@@ -17,7 +17,7 @@ jobs:
dnf_install: >
@c-development openldap-devel python3-devel
openldap-servers openldap-clients lcov clang-analyzer valgrind
- enchant
+ enchant python3-setuptools
strategy:
matrix:
tox_env:
@@ -28,6 +28,7 @@ jobs:
- py310
- py311
- py312
+ - py313
- c90-py36
- c90-py37
- py3-nosasltls
diff --git a/CHANGES b/CHANGES
index 500fa1e7..0491b6ef 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,20 @@
+Released 3.4.4 2022-11-17
+
+Fixes:
+* Reconnect race condition in ReconnectLDAPObject is now fixed
+* Socket ownership is now claimed once we've passed it to libldap
+* LDAP_set_option string formats are now compatible with Python 3.12
+
+Doc/
+* Security Policy was created
+* Broken article links are fixed now
+* Bring Conscious Language improvements
+
+Infrastructure:
+* Add testing and document support for Python 3.10, 3.11, and 3.12
+
+
+----------------------------------------------------------------
Released 3.4.3 2022-09-15
This is a minor release to bring back the removed OPT_X_TLS option.
diff --git a/Doc/contributing.rst b/Doc/contributing.rst
index bbaab491..de63a2e3 100644
--- a/Doc/contributing.rst
+++ b/Doc/contributing.rst
@@ -19,7 +19,7 @@ Communication
Always keep in mind that python-ldap is developed and maintained by volunteers.
We're happy to share our work, and to work with you to make the library better,
-but (until you pay someone), there's obligation to provide assistance.
+but (until you pay someone), there's no obligation to provide assistance.
So, keep it friendly, respectful, and supportive!
@@ -72,9 +72,6 @@ If you're used to open-source Python development with Git, here's the gist:
.. _the bug tracker: https://github.com/python-ldap/python-ldap/issues
.. _tox: https://tox.readthedocs.io/en/latest/
-Or, if you prefer to avoid closed-source services:
-
-* ``git clone https://pagure.io/python-ldap``
* Send bug reports and patches to the mailing list.
* Run tests with `tox`_; ignore Python interpreters you don't have locally.
* Read the documentation directly at `Read the Docs`_.
@@ -203,8 +200,6 @@ remember:
* Consider making the summary line suitable for the CHANGES document,
and starting it with a prefix like ``Lib:`` or ``Tests:``.
-* Push to Pagure as well.
-
If you have good reason to break the “rules”, go ahead and break them,
but mention why.
@@ -224,7 +219,7 @@ If you are tasked with releasing python-ldap, remember to:
* Run ``python setup.py sdist``, and smoke-test the resulting package
(install in a clean virtual environment, import ``ldap``).
* Create GPG-signed Git tag: ``git tag -s python-ldap-{version}``.
- Push it to GitHub and Pagure.
+ Push it to GitHub.
* Release the ``sdist`` on PyPI.
* Announce the release on the mailing list.
Mention the Git hash.
diff --git a/Doc/installing.rst b/Doc/installing.rst
index e4518c11..03e7a295 100644
--- a/Doc/installing.rst
+++ b/Doc/installing.rst
@@ -63,8 +63,8 @@ to get up to date information which versions are available.
Windows
-------
-Unofficial packages for Windows are available on
-`Christoph Gohlke's page `_.
+Unofficial binary builds for Windows are provided by Christoph Gohlke, available at
+`python-ldap-build `_.
`FreeBSD `_
@@ -143,10 +143,15 @@ Packages for building::
Debian
------
+Packages for building::
+
+ # apt-get install build-essential ldap-utils \
+ libldap2-dev libsasl2-dev
+
Packages for building and testing::
- # apt-get install build-essential python3-dev \
- libldap2-dev libsasl2-dev slapd ldap-utils tox \
+ # apt-get install build-essential ldap-utils \
+ libldap2-dev libsasl2-dev slapd python3-dev tox \
lcov valgrind
.. note::
diff --git a/Doc/spelling_wordlist.txt b/Doc/spelling_wordlist.txt
index e6c2aedd..e2150d9a 100644
--- a/Doc/spelling_wordlist.txt
+++ b/Doc/spelling_wordlist.txt
@@ -25,6 +25,7 @@ changeNumber
changesOnly
changeType
changeTypes
+Christoph
cidict
clientctrls
conf
@@ -56,6 +57,7 @@ filterstr
filterStr
formatOID
func
+Gohlke
GPG
Heimdal
hostport
@@ -98,7 +100,6 @@ oc
oid
oids
OpenLDAP
-Pagure
postalAddress
pre
previousDN
diff --git a/Lib/ldap/pkginfo.py b/Lib/ldap/pkginfo.py
index 026e9101..18ead66c 100644
--- a/Lib/ldap/pkginfo.py
+++ b/Lib/ldap/pkginfo.py
@@ -1,6 +1,6 @@
"""
meta attributes for packaging which does not import any dependencies
"""
-__version__ = '3.4.3'
+__version__ = '3.4.4'
__author__ = 'python-ldap project'
__license__ = 'Python style'
diff --git a/Lib/ldap/syncrepl.py b/Lib/ldap/syncrepl.py
index 1708b468..fd0c1285 100644
--- a/Lib/ldap/syncrepl.py
+++ b/Lib/ldap/syncrepl.py
@@ -12,6 +12,7 @@
from ldap.pkginfo import __version__, __author__, __license__
from ldap.controls import RequestControl, ResponseControl, KNOWN_RESPONSE_CONTROLS
+from ldap import RES_SEARCH_RESULT, RES_SEARCH_ENTRY, RES_INTERMEDIATE
__all__ = [
'SyncreplConsumer',
@@ -407,7 +408,7 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
all=0,
)
- if type == 101:
+ if type == RES_SEARCH_RESULT:
# search result. This marks the end of a refreshOnly session.
# look for a SyncDone control, save the cookie, and if necessary
# delete non-present entries.
@@ -420,7 +421,7 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
return False
- elif type == 100:
+ elif type == RES_SEARCH_ENTRY:
# search entry with associated SyncState control
for m in msg:
dn, attrs, ctrls = m
@@ -439,7 +440,7 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
self.syncrepl_set_cookie(c.cookie)
break
- elif type == 121:
+ elif type == RES_INTERMEDIATE:
# Intermediate message. If it is a SyncInfoMessage, parse it
for m in msg:
rname, resp, ctrls = m
diff --git a/Lib/ldapurl.py b/Lib/ldapurl.py
index 964076d3..b4dfd890 100644
--- a/Lib/ldapurl.py
+++ b/Lib/ldapurl.py
@@ -4,7 +4,7 @@
See https://www.python-ldap.org/ for details.
"""
-__version__ = '3.4.3'
+__version__ = '3.4.4'
__all__ = [
# constants
diff --git a/Lib/ldif.py b/Lib/ldif.py
index ae1d643d..fa41321c 100644
--- a/Lib/ldif.py
+++ b/Lib/ldif.py
@@ -3,7 +3,7 @@
See https://www.python-ldap.org/ for details.
"""
-__version__ = '3.4.3'
+__version__ = '3.4.4'
__all__ = [
# constants
diff --git a/Lib/slapdtest/__init__.py b/Lib/slapdtest/__init__.py
index 7ab7d2bd..7c410180 100644
--- a/Lib/slapdtest/__init__.py
+++ b/Lib/slapdtest/__init__.py
@@ -4,7 +4,7 @@
See https://www.python-ldap.org/ for details.
"""
-__version__ = '3.4.3'
+__version__ = '3.4.4'
from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler
from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls
diff --git a/Makefile b/Makefile
index 577ba883..2b52ddf5 100644
--- a/Makefile
+++ b/Makefile
@@ -89,7 +89,8 @@ valgrind: build $(PYTHON_SUPP)
autoformat: indent black
indent:
- indent Modules/*.c Modules/*.h
+ indent Modules/*.c
+ indent -npsl Modules/pythonldap.h
rm -f Modules/*.c~ Modules/*.h~
black:
diff --git a/Modules/LDAPObject.c b/Modules/LDAPObject.c
index da18d575..71fac73e 100644
--- a/Modules/LDAPObject.c
+++ b/Modules/LDAPObject.c
@@ -1,16 +1,10 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
+#include "pythonldap.h"
#include "patchlevel.h"
#include
#include
-#include "constants.h"
-#include "LDAPObject.h"
-#include "ldapcontrol.h"
-#include "message.h"
-#include "berval.h"
-#include "options.h"
#ifdef HAVE_SASL
#include
@@ -276,13 +270,8 @@ attrs_from_List(PyObject *attrlist, char ***attrsp)
if (attrlist == Py_None) {
/* None means a NULL attrlist */
-#if PY_MAJOR_VERSION == 2
- }
- else if (PyBytes_Check(attrlist)) {
-#else
}
else if (PyUnicode_Check(attrlist)) {
-#endif
/* caught by John Benninghoff */
LDAPerror_TypeError
("attrs_from_List(): expected *list* of strings, not a string",
@@ -293,11 +282,7 @@ attrs_from_List(PyObject *attrlist, char ***attrsp)
PyObject *item = NULL;
Py_ssize_t i, len, strlen;
-#if PY_MAJOR_VERSION >= 3
const char *str;
-#else
- char *str;
-#endif
seq = PySequence_Fast(attrlist, "expected list of strings or None");
if (seq == NULL)
@@ -315,24 +300,12 @@ attrs_from_List(PyObject *attrlist, char ***attrsp)
item = PySequence_Fast_GET_ITEM(seq, i);
if (item == NULL)
goto error;
-#if PY_MAJOR_VERSION == 2
- /* Encoded in Python to UTF-8 */
- if (!PyBytes_Check(item)) {
- LDAPerror_TypeError
- ("attrs_from_List(): expected bytes in list", item);
- goto error;
- }
- if (PyBytes_AsStringAndSize(item, &str, &strlen) == -1) {
- goto error;
- }
-#else
if (!PyUnicode_Check(item)) {
LDAPerror_TypeError
("attrs_from_List(): expected string in list", item);
goto error;
}
str = PyUnicode_AsUTF8AndSize(item, &strlen);
-#endif
/* Make a copy. PyBytes_AsString* / PyUnicode_AsUTF8* return
* internal values that must be treated like const char. Python
* 3.7 actually returns a const char.
@@ -521,7 +494,7 @@ l_ldap_add_ext(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_simple_bind */
@@ -572,7 +545,7 @@ l_ldap_simple_bind(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
#ifdef HAVE_SASL
@@ -730,7 +703,7 @@ l_ldap_sasl_bind_s(LDAPObject *self, PyObject *args)
}
else if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(ldaperror);
+ return PyLong_FromLong(ldaperror);
}
static PyObject *
@@ -757,15 +730,9 @@ l_ldap_sasl_interactive_bind_s(LDAPObject *self, PyObject *args)
* unsigned int, we need to use the "I" flag if we're running Python 2.3+ and a
* "i" otherwise.
*/
-#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 3)
- if (!PyArg_ParseTuple
- (args, "sOOOi:sasl_interactive_bind_s", &who, &SASLObject,
- &serverctrls, &clientctrls, &sasl_flags))
-#else
if (!PyArg_ParseTuple
(args, "sOOOI:sasl_interactive_bind_s", &who, &SASLObject,
&serverctrls, &clientctrls, &sasl_flags))
-#endif
return NULL;
if (not_valid(self))
@@ -809,7 +776,7 @@ l_ldap_sasl_interactive_bind_s(LDAPObject *self, PyObject *args)
if (msgid != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
#endif
@@ -858,7 +825,7 @@ l_ldap_cancel(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
#endif
@@ -912,7 +879,7 @@ l_ldap_compare_ext(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_delete_ext */
@@ -958,7 +925,7 @@ l_ldap_delete_ext(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_modify_ext */
@@ -1015,7 +982,7 @@ l_ldap_modify_ext(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_rename */
@@ -1065,7 +1032,7 @@ l_ldap_rename(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_result4 */
@@ -1281,7 +1248,7 @@ l_ldap_search_ext(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_whoami_s (available since OpenLDAP 2.1.13) */
@@ -1451,7 +1418,7 @@ l_ldap_passwd(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* ldap_extended_operation */
@@ -1502,7 +1469,7 @@ l_ldap_extended_operation(LDAPObject *self, PyObject *args)
if (ldaperror != LDAP_SUCCESS)
return LDAPerror(self->ldap);
- return PyInt_FromLong(msgid);
+ return PyLong_FromLong(msgid);
}
/* methods */
diff --git a/Modules/LDAPObject.h b/Modules/LDAPObject.h
deleted file mode 100644
index 4af0b382..00000000
--- a/Modules/LDAPObject.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_LDAPObject
-#define __h_LDAPObject
-
-#include "common.h"
-
-typedef struct {
- PyObject_HEAD LDAP *ldap;
- PyThreadState *_save; /* for thread saving on referrals */
- int valid;
-} LDAPObject;
-
-extern PyTypeObject LDAP_Type;
-
-#define LDAPObject_Check(v) (Py_TYPE(v) == &LDAP_Type)
-
-extern LDAPObject *newLDAPObject(LDAP *);
-
-/* macros to allow thread saving in the context of an LDAP connection */
-
-#define LDAP_BEGIN_ALLOW_THREADS( l ) \
- { \
- LDAPObject *lo = (l); \
- if (lo->_save != NULL) \
- Py_FatalError( "saving thread twice?" ); \
- lo->_save = PyEval_SaveThread(); \
- }
-
-#define LDAP_END_ALLOW_THREADS( l ) \
- { \
- LDAPObject *lo = (l); \
- PyThreadState *_save = lo->_save; \
- lo->_save = NULL; \
- PyEval_RestoreThread( _save ); \
- }
-
-#endif /* __h_LDAPObject */
diff --git a/Modules/berval.c b/Modules/berval.c
index 6917baef..39cc98a8 100644
--- a/Modules/berval.c
+++ b/Modules/berval.c
@@ -1,7 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "berval.h"
+#include "pythonldap.h"
/*
* Copies out the data from a berval, and returns it as a new Python object,
diff --git a/Modules/berval.h b/Modules/berval.h
deleted file mode 100644
index 9c427240..00000000
--- a/Modules/berval.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_berval
-#define __h_berval
-
-#include "common.h"
-
-PyObject *LDAPberval_to_object(const struct berval *bv);
-PyObject *LDAPberval_to_unicode_object(const struct berval *bv);
-
-#endif /* __h_berval_ */
diff --git a/Modules/common.c b/Modules/common.c
index 9d7001c0..4cfee744 100644
--- a/Modules/common.c
+++ b/Modules/common.c
@@ -1,7 +1,7 @@
/* Miscellaneous common routines
* See https://www.python-ldap.org/ for details. */
-#include "common.h"
+#include "pythonldap.h"
/* dynamically add the methods into the module dictionary d */
diff --git a/Modules/common.h b/Modules/common.h
deleted file mode 100644
index bc554c85..00000000
--- a/Modules/common.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* common utility macros
- * See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_common
-#define __h_common
-
-#define PY_SSIZE_T_CLEAN
-
-#include "Python.h"
-
-#if defined(HAVE_CONFIG_H)
-#include "config.h"
-#endif
-
-#include
-#include
-#include
-
-#if LDAP_VENDOR_VERSION < 20400
-#error Current python-ldap requires OpenLDAP 2.4.x
-#endif
-
-#if LDAP_VENDOR_VERSION >= 20448
- /* openldap.h with ldap_init_fd() was introduced in 2.4.48
- * see https://bugs.openldap.org/show_bug.cgi?id=8671
- */
-#define HAVE_LDAP_INIT_FD 1
-#include
-#elif (defined(__APPLE__) && (LDAP_VENDOR_VERSION == 20428))
-/* macOS system libldap 2.4.28 does not have ldap_init_fd symbol */
-#undef HAVE_LDAP_INIT_FD
-#else
- /* ldap_init_fd() has been around for a very long time
- * SSSD has been defining the function for a while, so it's probably OK.
- */
-#define HAVE_LDAP_INIT_FD 1
-#define LDAP_PROTO_TCP 1
-#define LDAP_PROTO_UDP 2
-#define LDAP_PROTO_IPC 3
-extern int ldap_init_fd(ber_socket_t fd, int proto, LDAP_CONST char *url,
- LDAP **ldp);
-#endif
-
-#if defined(MS_WINDOWS)
-#include
-#else /* unix */
-#include
-#include
-#include
-#endif
-
-#include
-#define streq( a, b ) \
- ( (*(a)==*(b)) && 0==strcmp(a,b) )
-
-extern PyObject *LDAPerror_TypeError(const char *, PyObject *);
-
-void LDAPadd_methods(PyObject *d, PyMethodDef *methods);
-
-#define PyNone_Check(o) ((o) == Py_None)
-
-/* Py2/3 compatibility */
-#if PY_VERSION_HEX >= 0x03000000
-/* In Python 3, alias PyInt to PyLong */
-#define PyInt_FromLong PyLong_FromLong
-#endif
-
-#endif /* __h_common_ */
diff --git a/Modules/constants.c b/Modules/constants.c
index 8d6f63b0..f0a0da94 100644
--- a/Modules/constants.c
+++ b/Modules/constants.c
@@ -1,9 +1,7 @@
/* constants defined for LDAP
* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "constants.h"
-#include "ldapcontrol.h"
+#include "pythonldap.h"
/* the base exception class */
@@ -107,20 +105,20 @@ LDAPraise_for_message(LDAP *l, LDAPMessage *m)
}
if (msgtype > 0) {
- pyresult = PyInt_FromLong(msgtype);
+ pyresult = PyLong_FromLong(msgtype);
if (pyresult)
PyDict_SetItemString(info, "msgtype", pyresult);
Py_XDECREF(pyresult);
}
if (msgid >= 0) {
- pyresult = PyInt_FromLong(msgid);
+ pyresult = PyLong_FromLong(msgid);
if (pyresult)
PyDict_SetItemString(info, "msgid", pyresult);
Py_XDECREF(pyresult);
}
- pyresult = PyInt_FromLong(errnum);
+ pyresult = PyLong_FromLong(errnum);
if (pyresult)
PyDict_SetItemString(info, "result", pyresult);
Py_XDECREF(pyresult);
@@ -131,7 +129,7 @@ LDAPraise_for_message(LDAP *l, LDAPMessage *m)
Py_XDECREF(str);
if (myerrno != 0) {
- pyerrno = PyInt_FromLong(myerrno);
+ pyerrno = PyLong_FromLong(myerrno);
if (pyerrno)
PyDict_SetItemString(info, "errno", pyerrno);
Py_XDECREF(pyerrno);
diff --git a/Modules/constants.h b/Modules/constants.h
deleted file mode 100644
index 7b9ce53e..00000000
--- a/Modules/constants.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_constants_
-#define __h_constants_
-
-#include "common.h"
-
-extern int LDAPinit_constants(PyObject *m);
-extern PyObject *LDAPconstant(int);
-
-extern PyObject *LDAPexception_class;
-extern PyObject *LDAPerror(LDAP *);
-extern PyObject *LDAPraise_for_message(LDAP *, LDAPMessage *m);
-PyObject *LDAPerr(int errnum);
-
-#ifndef LDAP_CONTROL_PAGE_OID
-#define LDAP_CONTROL_PAGE_OID "1.2.840.113556.1.4.319"
-#endif /* !LDAP_CONTROL_PAGE_OID */
-
-#ifndef LDAP_CONTROL_VALUESRETURNFILTER
-#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3" /* RFC 3876 */
-#endif /* !LDAP_CONTROL_VALUESRETURNFILTER */
-
-#endif /* __h_constants_ */
diff --git a/Modules/functions.c b/Modules/functions.c
index b811708f..f7d9cf37 100644
--- a/Modules/functions.c
+++ b/Modules/functions.c
@@ -1,11 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "functions.h"
-#include "LDAPObject.h"
-#include "berval.h"
-#include "constants.h"
-#include "options.h"
+#include "pythonldap.h"
/* ldap_initialize */
diff --git a/Modules/functions.h b/Modules/functions.h
deleted file mode 100644
index 2aef9740..00000000
--- a/Modules/functions.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_functions_
-#define __h_functions_
-
-#include "common.h"
-extern void LDAPinit_functions(PyObject *);
-
-#endif /* __h_functions_ */
diff --git a/Modules/ldapcontrol.c b/Modules/ldapcontrol.c
index e287e9a3..4a37b614 100644
--- a/Modules/ldapcontrol.c
+++ b/Modules/ldapcontrol.c
@@ -1,10 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "LDAPObject.h"
-#include "ldapcontrol.h"
-#include "berval.h"
-#include "constants.h"
+#include "pythonldap.h"
/* Prints to stdout the contents of an array of LDAPControl objects */
diff --git a/Modules/ldapcontrol.h b/Modules/ldapcontrol.h
deleted file mode 100644
index 74cae423..00000000
--- a/Modules/ldapcontrol.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_ldapcontrol
-#define __h_ldapcontrol
-
-#include "common.h"
-
-void LDAPinit_control(PyObject *d);
-void LDAPControl_List_DEL(LDAPControl **);
-int LDAPControls_from_object(PyObject *, LDAPControl ***);
-PyObject *LDAPControls_to_List(LDAPControl **ldcs);
-
-#endif /* __h_ldapcontrol */
diff --git a/Modules/ldapmodule.c b/Modules/ldapmodule.c
index 34d5a24c..cb3f58fb 100644
--- a/Modules/ldapmodule.c
+++ b/Modules/ldapmodule.c
@@ -1,17 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "constants.h"
-#include "functions.h"
-#include "ldapcontrol.h"
-
-#include "LDAPObject.h"
-
-#if PY_MAJOR_VERSION >= 3
-PyMODINIT_FUNC PyInit__ldap(void);
-#else
-PyMODINIT_FUNC init_ldap(void);
-#endif
+#include "pythonldap.h"
#define _STR(x) #x
#define STR(x) _STR(x)
@@ -33,27 +22,24 @@ static PyMethodDef methods[] = {
{NULL, NULL}
};
+static struct PyModuleDef ldap_moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_ldap", /* m_name */
+ "", /* m_doc */
+ -1, /* m_size */
+ methods, /* m_methods */
+};
+
/* module initialisation */
-/* Common initialization code */
-PyObject *
-init_ldap_module(void)
+PyMODINIT_FUNC
+PyInit__ldap()
{
PyObject *m, *d;
/* Create the module and add the functions */
-#if PY_MAJOR_VERSION >= 3
- static struct PyModuleDef ldap_moduledef = {
- PyModuleDef_HEAD_INIT,
- "_ldap", /* m_name */
- "", /* m_doc */
- -1, /* m_size */
- methods, /* m_methods */
- };
m = PyModule_Create(&ldap_moduledef);
-#else
- m = Py_InitModule("_ldap", methods);
-#endif
+
/* Initialize LDAP class */
if (PyType_Ready(&LDAP_Type) < 0) {
Py_DECREF(m);
@@ -78,17 +64,3 @@ init_ldap_module(void)
return m;
}
-
-#if PY_MAJOR_VERSION < 3
-PyMODINIT_FUNC
-init_ldap()
-{
- init_ldap_module();
-}
-#else
-PyMODINIT_FUNC
-PyInit__ldap()
-{
- return init_ldap_module();
-}
-#endif
diff --git a/Modules/message.c b/Modules/message.c
index 22aa313c..f1403237 100644
--- a/Modules/message.c
+++ b/Modules/message.c
@@ -1,10 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "message.h"
-#include "berval.h"
-#include "ldapcontrol.h"
-#include "constants.h"
+#include "pythonldap.h"
/*
* Converts an LDAP message into a Python structure.
diff --git a/Modules/message.h b/Modules/message.h
deleted file mode 100644
index ed73f32c..00000000
--- a/Modules/message.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-#ifndef __h_message
-#define __h_message
-
-#include "common.h"
-
-extern PyObject *LDAPmessage_to_python(LDAP *ld, LDAPMessage *m, int add_ctrls,
- int add_intermediates);
-
-#endif /* __h_message_ */
diff --git a/Modules/options.c b/Modules/options.c
index a621f81a..4577b075 100644
--- a/Modules/options.c
+++ b/Modules/options.c
@@ -1,11 +1,6 @@
/* See https://www.python-ldap.org/ for details. */
-#include "common.h"
-#include "constants.h"
-#include "LDAPObject.h"
-#include "ldapcontrol.h"
-#include "options.h"
-#include "berval.h"
+#include "pythonldap.h"
void
set_timeval_from_double(struct timeval *tv, double d)
@@ -373,7 +368,7 @@ LDAP_get_option(LDAPObject *self, int option)
res = LDAP_int_get_option(self, option, &intval);
if (res != LDAP_OPT_SUCCESS)
return option_error(res, "ldap_get_option");
- return PyInt_FromLong(intval);
+ return PyLong_FromLong(intval);
#ifdef LDAP_OPT_TCP_USER_TIMEOUT
case LDAP_OPT_TCP_USER_TIMEOUT:
diff --git a/Modules/options.h b/Modules/options.h
deleted file mode 100644
index fd6a5ce2..00000000
--- a/Modules/options.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* See https://www.python-ldap.org/ for details. */
-
-int LDAP_optionval_by_name(const char *name);
-int LDAP_set_option(LDAPObject *self, int option, PyObject *value);
-PyObject *LDAP_get_option(LDAPObject *self, int option);
-
-void set_timeval_from_double(struct timeval *tv, double d);
diff --git a/Modules/pythonldap.h b/Modules/pythonldap.h
new file mode 100644
index 00000000..7703af5e
--- /dev/null
+++ b/Modules/pythonldap.h
@@ -0,0 +1,131 @@
+/* common utility macros
+ * See https://www.python-ldap.org/ for details. */
+
+#ifndef pythonldap_h
+#define pythonldap_h
+
+/* *** common *** */
+#define PY_SSIZE_T_CLEAN
+
+#include "Python.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include
+#include
+#include
+
+#if LDAP_VENDOR_VERSION < 20400
+#error Current python-ldap requires OpenLDAP 2.4.x
+#endif
+
+#if LDAP_VENDOR_VERSION >= 20448
+ /* openldap.h with ldap_init_fd() was introduced in 2.4.48
+ * see https://bugs.openldap.org/show_bug.cgi?id=8671
+ */
+#define HAVE_LDAP_INIT_FD 1
+#include
+#elif (defined(__APPLE__) && (LDAP_VENDOR_VERSION == 20428))
+/* macOS system libldap 2.4.28 does not have ldap_init_fd symbol */
+#undef HAVE_LDAP_INIT_FD
+#else
+ /* ldap_init_fd() has been around for a very long time
+ * SSSD has been defining the function for a while, so it's probably OK.
+ */
+#define HAVE_LDAP_INIT_FD 1
+#define LDAP_PROTO_TCP 1
+#define LDAP_PROTO_UDP 2
+#define LDAP_PROTO_IPC 3
+LDAP_F(int) ldap_init_fd(ber_socket_t fd, int proto, LDAP_CONST char *url,
+ LDAP **ldp);
+#endif
+
+#if defined(MS_WINDOWS)
+#include
+#else /* unix */
+#include
+#include
+#include
+#endif
+
+#define PYLDAP_FUNC(rtype) rtype
+#define PYLDAP_DATA(rtype) extern rtype
+
+PYLDAP_FUNC(PyObject *) LDAPerror_TypeError(const char *, PyObject *);
+
+PYLDAP_FUNC(void) LDAPadd_methods(PyObject *d, PyMethodDef *methods);
+
+#define PyNone_Check(o) ((o) == Py_None)
+
+/* *** berval *** */
+PYLDAP_FUNC(PyObject *) LDAPberval_to_object(const struct berval *bv);
+PYLDAP_FUNC(PyObject *) LDAPberval_to_unicode_object(const struct berval *bv);
+
+/* *** constants *** */
+PYLDAP_FUNC(int) LDAPinit_constants(PyObject *m);
+
+PYLDAP_DATA(PyObject *) LDAPexception_class;
+PYLDAP_FUNC(PyObject *) LDAPerror(LDAP *);
+PYLDAP_FUNC(PyObject *) LDAPraise_for_message(LDAP *, LDAPMessage *m);
+PYLDAP_FUNC(PyObject *) LDAPerr(int errnum);
+
+#ifndef LDAP_CONTROL_PAGE_OID
+#define LDAP_CONTROL_PAGE_OID "1.2.840.113556.1.4.319"
+#endif /* !LDAP_CONTROL_PAGE_OID */
+
+#ifndef LDAP_CONTROL_VALUESRETURNFILTER
+#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3" /* RFC 3876 */
+#endif /* !LDAP_CONTROL_VALUESRETURNFILTER */
+
+/* *** functions *** */
+PYLDAP_FUNC(void) LDAPinit_functions(PyObject *);
+
+/* *** ldapcontrol *** */
+PYLDAP_FUNC(void) LDAPinit_control(PyObject *d);
+PYLDAP_FUNC(void) LDAPControl_List_DEL(LDAPControl **);
+PYLDAP_FUNC(int) LDAPControls_from_object(PyObject *, LDAPControl ***);
+PYLDAP_FUNC(PyObject *) LDAPControls_to_List(LDAPControl **ldcs);
+
+/* *** ldapobject *** */
+typedef struct {
+ PyObject_HEAD LDAP *ldap;
+ PyThreadState *_save; /* for thread saving on referrals */
+ int valid;
+} LDAPObject;
+
+PYLDAP_DATA(PyTypeObject) LDAP_Type;
+PYLDAP_FUNC(LDAPObject *) newLDAPObject(LDAP *);
+
+/* macros to allow thread saving in the context of an LDAP connection */
+
+#define LDAP_BEGIN_ALLOW_THREADS( l ) \
+ { \
+ LDAPObject *lo = (l); \
+ if (lo->_save != NULL) \
+ Py_FatalError( "saving thread twice?" ); \
+ lo->_save = PyEval_SaveThread(); \
+ }
+
+#define LDAP_END_ALLOW_THREADS( l ) \
+ { \
+ LDAPObject *lo = (l); \
+ PyThreadState *_save = lo->_save; \
+ lo->_save = NULL; \
+ PyEval_RestoreThread( _save ); \
+ }
+
+/* *** messages *** */
+PYLDAP_FUNC(PyObject *)
+LDAPmessage_to_python(LDAP *ld, LDAPMessage *m, int add_ctrls,
+ int add_intermediates);
+
+/* *** options *** */
+PYLDAP_FUNC(int) LDAP_optionval_by_name(const char *name);
+PYLDAP_FUNC(int) LDAP_set_option(LDAPObject *self, int option,
+ PyObject *value);
+PYLDAP_FUNC(PyObject *) LDAP_get_option(LDAPObject *self, int option);
+PYLDAP_FUNC(void) set_timeval_from_double(struct timeval *tv, double d);
+
+#endif /* pythonldap_h */
diff --git a/setup.py b/setup.py
index 6da3f491..8e7963a1 100644
--- a/setup.py
+++ b/setup.py
@@ -93,6 +93,7 @@ class OpenLDAP2:
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
+ 'Programming Language :: Python :: 3.13',
# Note: when updating Python versions, also change tox.ini and .github/workflows/*
'Topic :: Database',
@@ -117,15 +118,8 @@ class OpenLDAP2:
'Modules/berval.c',
],
depends = [
- 'Modules/LDAPObject.h',
- 'Modules/berval.h',
- 'Modules/common.h',
+ 'Modules/pythonldap.h',
'Modules/constants_generated.h',
- 'Modules/constants.h',
- 'Modules/functions.h',
- 'Modules/ldapcontrol.h',
- 'Modules/message.h',
- 'Modules/options.h',
],
libraries = LDAP_CLASS.libs,
include_dirs = ['Modules'] + LDAP_CLASS.include_dirs,
diff --git a/tox.ini b/tox.ini
index beade024..22752067 100644
--- a/tox.ini
+++ b/tox.ini
@@ -17,10 +17,12 @@ python =
3.10: py310
3.11: py311
3.12: py312
+ 3.13: py313
pypy3.9: pypy3.9
+ pypy3.10: pypy3.10
[testenv]
-deps =
+deps = setuptools
passenv = WITH_GCOV
# - Enable BytesWarning
# - Turn all warnings into exceptions.
@@ -98,6 +100,7 @@ deps =
markdown
sphinx
sphinxcontrib-spelling
+ setuptools
commands =
{envpython} setup.py check --restructuredtext --metadata --strict
{envpython} -m markdown README -f {envtmpdir}/README.html