Skip to content

Remove Python 2 support #358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,14 @@ addons:
# Note: when updating Python versions, also change setup.py and tox.ini
matrix:
include:
- python: 2.7
env:
- TOXENV=py27
- WITH_GCOV=1
- python: 3.4
env:
- TOXENV=py34
- WITH_GCOV=1
- python: 3.5
env:
- TOXENV=py35
- WITH_GCOV=1
- python: 3.6
env:
- TOXENV=py36
- WITH_GCOV=1
- python: pypy
- python: pypy3
env:
- TOXENV=pypy
- TOXENV=pypy3
- CFLAGS_std="-std=c99"
- python: 3.7
env:
- TOXENV=py37
Expand All @@ -53,10 +42,6 @@ matrix:
- WITH_GCOV=1
dist: xenial
sudo: true
- python: 2.7
env:
- TOXENV=py2-nosasltls
- WITH_GCOV=1
- python: 3.6
env:
- TOXENV=py3-nosasltls
Expand All @@ -68,7 +53,7 @@ matrix:
env: TOXENV=doc
allow_failures:
- env:
- TOXENV=pypy
- TOXENV=pypy3

env:
global:
Expand All @@ -87,4 +72,3 @@ install:
- pip install tox-travis tox codecov

script: CFLAGS="$CFLAGS_warnings $CFLAGS_std" tox

138 changes: 20 additions & 118 deletions Doc/bytes_mode.rst
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
.. _text-bytes:
.. _bytes_mode:

Bytes/text management
=====================

Python 3 introduces a hard distinction between *text* (``str``) – sequences of
characters (formally, *Unicode codepoints*) – and ``bytes`` – sequences of
8-bit values used to encode *any* kind of data for storage or transmission.

Python 2 has the same distinction between ``str`` (bytes) and
``unicode`` (text).
However, values can be implicitly converted between these types as needed,
e.g. when comparing or writing to disk or the network.
The implicit encoding and decoding can be a source of subtle bugs when not
designed and tested adequately.

In python-ldap 2.x (for Python 2), bytes were used for all fields,
including those guaranteed to be text.

From version 3.0, python-ldap uses text where appropriate.
On Python 2, the :ref:`bytes mode <bytes_mode>` setting influences how text is
handled.


What's text, and what's bytes
-----------------------------

The LDAP protocol states that some fields (distinguished names, relative
distinguished names, attribute names, queries) be encoded in UTF-8.
In python-ldap, these are represented as text (``str`` on Python 3,
``unicode`` on Python 2).
In python-ldap, these are represented as text (``str`` on Python 3).

Attribute *values*, on the other hand, **MAY**
contain any type of data, including text.
Expand All @@ -38,102 +16,26 @@ Thus, attribute values are *always* treated as ``bytes``.
Encoding/decoding to other formats – text, images, etc. – is left to the caller.


.. _bytes_mode:

The bytes mode
--------------

In Python 3, text values are represented as ``str``, the Unicode text type.

In Python 2, the behavior of python-ldap 3.0 is influenced by a ``bytes_mode``
argument to :func:`ldap.initialize`:

``bytes_mode=True`` (backwards compatible):
Text values are represented as bytes (``str``) encoded using UTF-8.

``bytes_mode=False`` (future compatible):
Text values are represented as ``unicode``.

If not given explicitly, python-ldap will default to ``bytes_mode=True``,
but if a ``unicode`` value is supplied to it, it will warn and use that value.

Backwards-compatible behavior is not scheduled for removal until Python 2
itself reaches end of life.


Errors, warnings, and automatic encoding
----------------------------------------

While the type of values *returned* from python-ldap is always given by
``bytes_mode``, for Python 2 the behavior for “wrong-type” values *passed in*
can be controlled by the ``bytes_strictness`` argument to
:func:`ldap.initialize`:
Historical note
---------------

``bytes_strictness='error'`` (default if ``bytes_mode`` is specified):
A ``TypeError`` is raised.

``bytes_strictness='warn'`` (default when ``bytes_mode`` is not given explicitly):
A warning is raised, and the value is encoded/decoded
using the UTF-8 encoding.

The warnings are of type :class:`~ldap.LDAPBytesWarning`, which
is a subclass of :class:`BytesWarning` designed to be easily
:ref:`filtered out <filter-bytes-warning>` if needed.

``bytes_strictness='silent'``:
The value is automatically encoded/decoded using the UTF-8 encoding.

On Python 3, ``bytes_strictness`` is ignored and a ``TypeError`` is always
raised.

When setting ``bytes_strictness``, an explicit value for ``bytes_mode`` needs
to be given as well.


Porting recommendations
-----------------------

Since end of life of Python 2 is coming in a few years, projects are strongly
urged to make their code compatible with Python 3. General instructions for
this are provided :ref:`in Python documentation <pyporting-howto>` and in the
`Conservative porting guide`_.

.. _Conservative porting guide: https://portingguide.readthedocs.io/en/latest/


When porting from python-ldap 2.x, users are advised to update their code
to set ``bytes_mode=False``, and fix any resulting failures.

The typical usage is as follows.
Note that only the result's *values* are of the ``bytes`` type:

.. code-block:: pycon

>>> import ldap
>>> con = ldap.initialize('ldap://localhost:389', bytes_mode=False)
>>> con.simple_bind_s(u'login', u'secret_password')
>>> results = con.search_s(u'ou=people,dc=example,dc=org', ldap.SCOPE_SUBTREE, u"(cn=Raphaël)")
>>> results
[
("cn=Raphaël,ou=people,dc=example,dc=org", {
'cn': [b'Rapha\xc3\xabl'],
'sn': [b'Barrois'],
}),
]


.. _filter-bytes-warning:

Filtering warnings
------------------
Python 3 introduced a hard distinction between *text* (``str``) – sequences of
characters (formally, *Unicode codepoints*) – and ``bytes`` – sequences of
8-bit values used to encode *any* kind of data for storage or transmission.

The bytes mode warnings can be filtered out and ignored with a
simple filter.
Python 2 had the same distinction between ``str`` (bytes) and
``unicode`` (text).
However, values could be implicitly converted between these types as needed,
e.g. when comparing or writing to disk or the network.
The implicit encoding and decoding can be a source of subtle bugs when not
designed and tested adequately.

.. code-block:: python
In python-ldap 2.x (for Python 2), bytes were used for all fields,
including those guaranteed to be text.

import warnings
import ldap
From version 3.0 to 3.3, python-ldap uses text where appropriate.
On Python 2, special ``bytes_mode`` and ``bytes_strictness`` settings
influenced how text was handled.

if hasattr(ldap, 'LDAPBytesWarning'):
warnings.simplefilter('ignore', ldap.LDAPBytesWarning)
From version 3.3 on, only Python 3 is supported. The “bytes mode” settings
are deprecated and do nothing.
2 changes: 1 addition & 1 deletion Doc/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Usage
.. _pyldap: https://pypi.org/project/pyldap/


**Q**: Does it work with Python 2.6? (1.5|2.0|2.1|2.2|2.3|2.4|2.5)?
**Q**: Does it work with Python 2.7? (1.5|2.0|2.1|2.2|2.3|2.4|2.5|2.6|2.7)?

**A**: No. Old versions of python-ldap are still available from PyPI, though.

Expand Down
22 changes: 15 additions & 7 deletions Doc/reference/ldap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Functions

This module defines the following functions:

.. py:function:: initialize(uri [, trace_level=0 [, trace_file=sys.stdout [, trace_stack_limit=None, [bytes_mode=None, [bytes_strictness=None, [fileno=None]]]]]]) -> LDAPObject object
.. py:function:: initialize(uri [, trace_level=0 [, trace_file=sys.stdout [, trace_stack_limit=None, [fileno=None]]]]) -> LDAPObject object

Initializes a new connection object for accessing the given LDAP server,
and return an :class:`~ldap.ldapobject.LDAPObject` used to perform operations
Expand Down Expand Up @@ -63,10 +63,6 @@ This module defines the following functions:
*trace_file* specifies a file-like object as target of the debug log and
*trace_stack_limit* specifies the stack limit of tracebacks in debug log.

The *bytes_mode* and *bytes_strictness* arguments specify text/bytes
behavior under Python 2.
See :ref:`text-bytes` for a complete documentation.

Possible values for *trace_level* are
:py:const:`0` for no logging,
:py:const:`1` for only logging the method calls with arguments,
Expand All @@ -78,6 +74,10 @@ This module defines the following functions:
Any additional keyword arguments are passed to ``LDAPObject``.
It is also fine to instantiate a ``LDAPObject`` (or a subclass) directly.

The function additionally takes *bytes_mode* and *bytes_strictness* keyword
arguments, which are deprecated and ignored. See :ref:`bytes_mode` for
details.

.. seealso::

:rfc:`4516` - Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator
Expand All @@ -86,6 +86,10 @@ This module defines the following functions:

The *fileno* argument was added.

.. deprecated:: 3.4

*bytes_mode* and *bytes_strictness* arguments are deprecated.


.. py:function:: get_option(option) -> int|string

Expand Down Expand Up @@ -730,12 +734,16 @@ Warnings

.. py:class:: LDAPBytesWarning

Raised when bytes/text mismatch in non-strict bytes mode.
This warning is deprecated. python-ldap no longer raises it.

See :ref:`bytes_mode` for details.
It used to be raised under Python 2 when bytes/text mismatch in non-strict
bytes mode. See :ref:`bytes_mode` for details.

.. versionadded:: 3.0.0

.. versionchanged:: 3.4.0

Deprecated.

.. _ldap-objects:

Expand Down
3 changes: 1 addition & 2 deletions Doc/reference/ldapurl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

This module parses and generates LDAP URLs. It is implemented in pure Python
and does not rely on any non-standard modules. Therefore it can be used stand-
alone without the rest of the python-ldap package. Compatibility note: This
module has been solely tested on Python 2.x and above.
alone without the rest of the python-ldap package.

.. seealso::

Expand Down
2 changes: 0 additions & 2 deletions Doc/sample_workflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ python-ldap won't affect the rest of your system::

$ python3 -m venv __venv__

(For Python 2, install `virtualenv`_ and use it instead of ``python3 -m venv``.)

.. _git: https://git-scm.com/
.. _virtualenv: https://virtualenv.pypa.io/en/stable/

Expand Down
2 changes: 1 addition & 1 deletion Lib/ldap/cidict.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"""
import warnings

from ldap.compat import MutableMapping
from collections.abc import MutableMapping
from ldap import __version__


Expand Down
Loading