Skip to content

Commit db98910

Browse files
committed
Use stack walking for LDAPBytesWarning also in initialize()
1 parent a3723bc commit db98910

File tree

4 files changed

+66
-57
lines changed

4 files changed

+66
-57
lines changed

Lib/ldap/functions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
# Tracing is only supported in debugging mode
2828
import traceback
2929

30+
# See _raise_byteswarning in ldapobject.py
31+
_LDAP_WARN_SKIP_FRAME = True
32+
3033

3134
def _ldap_function_call(lock,func,*args,**kwargs):
3235
"""

Lib/ldap/ldapobject.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,26 @@ class LDAPBytesWarning(BytesWarning):
4646
"""python-ldap bytes mode warning
4747
"""
4848

49+
def _raise_byteswarning(message):
50+
"""Raise LDAPBytesWarning
51+
"""
52+
53+
# Call stacks that raise the warning tend to be complicated, so
54+
# getting a useful stacklevel is tricky.
55+
# We walk stack frames, ignoring functions in uninteresting files,
56+
# based on the _LDAP_WARN_SKIP_FRAME marker in globals().
57+
stacklevel = 2
58+
try:
59+
getframe = sys._getframe
60+
except AttributeError:
61+
pass
62+
else:
63+
frame = sys._getframe(stacklevel)
64+
while frame and frame.f_globals.get('_LDAP_WARN_SKIP_FRAME'):
65+
stacklevel += 1
66+
frame = frame.f_back
67+
warnings.warn(message, LDAPBytesWarning, stacklevel=stacklevel+1)
68+
4969

5070
class NO_UNIQUE_ENTRY(ldap.NO_SUCH_OBJECT):
5171
"""
@@ -90,13 +110,10 @@ def __init__(
90110
# By default, raise a TypeError when receiving invalid args
91111
self.bytes_mode_hardfail = True
92112
if bytes_mode is None and PY2:
93-
warnings.warn(
113+
_raise_byteswarning(
94114
"Under Python 2, python-ldap uses bytes by default. "
95115
"This will be removed in Python 3 (no bytes for DN/RDN/field names). "
96-
"Please call initialize(..., bytes_mode=False) explicitly.",
97-
LDAPBytesWarning,
98-
stacklevel=2,
99-
)
116+
"Please call initialize(..., bytes_mode=False) explicitly.")
100117
bytes_mode = True
101118
# Disable hard failure when running in backwards compatibility mode.
102119
self.bytes_mode_hardfail = False
@@ -129,28 +146,10 @@ def _bytesify_input(self, value):
129146
if self.bytes_mode_hardfail:
130147
raise TypeError("All provided fields *must* be bytes when bytes mode is on; got %r" % (value,))
131148
else:
132-
# Raise LDAPBytesWarning.
133-
# Call stacks with _bytesify_input tend to be complicated, so
134-
# getting a useful stacklevel is tricky.
135-
# We walk stack frames, ignoring all functions in this file
136-
# and in the _ldap extension, based on a marker in globals().
137-
stacklevel = 0
138-
try:
139-
getframe = sys._getframe
140-
except AttributeError:
141-
pass
142-
else:
143-
frame = sys._getframe(stacklevel)
144-
# walk up the stacks until we leave the file
145-
while frame and frame.f_globals.get('_LDAP_WARN_SKIP_FRAME'):
146-
stacklevel += 1
147-
frame = frame.f_back
148-
warnings.warn(
149-
"Received non-bytes value %r with default (disabled) bytes mode; please choose an explicit "
150-
"option for bytes_mode on your LDAP connection" % (value,),
151-
LDAPBytesWarning,
152-
stacklevel=stacklevel+1,
153-
)
149+
_raise_byteswarning(
150+
"Received non-bytes value %r with default (disabled) bytes mode; "
151+
"please choose an explicit "
152+
"option for bytes_mode on your LDAP connection" % (value,))
154153
return value.encode('utf-8')
155154
else:
156155
if not isinstance(value, text_type):

Modules/ldapmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ PyObject* init_ldap_module(void)
7373
LDAPinit_control(d);
7474

7575
/* Marker for LDAPBytesWarning stack walking
76-
* see SimpleLDAPObject._bytesify_input in ldapobject.py
76+
* See _raise_byteswarning in ldapobject.py
7777
*/
7878
if (PyModule_AddIntConstant(m, "_LDAP_WARN_SKIP_FRAME", 1) != 0) {
7979
return NULL;

Tests/t_ldapobject.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,25 @@ def catch_byteswarnings(self, *args, **kwargs):
361361
warnings.simplefilter('always', ldap.LDAPBytesWarning)
362362
yield conn, w
363363

364+
def _check_byteswarning(self, warning, expected_message):
365+
self.assertIs(warning.category, ldap.LDAPBytesWarning)
366+
self.assertIn(expected_message, text_type(warning.message))
367+
368+
def _normalize(filename):
369+
# Python 2 likes to report the ".pyc" file in warnings,
370+
# tracebacks or __file__.
371+
# Use the corresponding ".py" in that case.
372+
if filename.endswith('.pyc'):
373+
return filename[:-1]
374+
return filename
375+
376+
# Assert warning points to a line marked CORRECT LINE in this file
377+
self.assertEquals(_normalize(warning.filename), _normalize(__file__))
378+
self.assertIn(
379+
'CORRECT LINE',
380+
linecache.getline(warning.filename, warning.lineno)
381+
)
382+
364383
def _test_byteswarning_level_search(self, methodname):
365384
with self.catch_byteswarnings(explicit=False) as (conn, w):
366385
method = getattr(conn, methodname)
@@ -374,43 +393,31 @@ def _test_byteswarning_level_search(self, methodname):
374393

375394
self.assertEqual(len(w), 2, w)
376395

377-
def _normalize(filename):
378-
# Python 2 likes to report the ".pyc" file in warnings,
379-
# tracebacks or __file__.
380-
# Use the corresponding ".py" in that case.
381-
if filename.endswith('.pyc'):
382-
return filename[:-1]
383-
return filename
384-
385-
self.assertIs(w[0].category, ldap.LDAPBytesWarning)
386-
self.assertIn(
387-
u"Received non-bytes value u'(cn=Foo*)'",
388-
text_type(w[0].message)
389-
)
390-
self.assertEqual(_normalize(w[1].filename), _normalize(__file__))
391-
self.assertEqual(_normalize(w[0].filename), _normalize(__file__))
392-
self.assertIn(
393-
'CORRECT LINE',
394-
linecache.getline(w[0].filename, w[0].lineno)
395-
)
396+
self._check_byteswarning(
397+
w[0], u"Received non-bytes value u'(cn=Foo*)'")
396398

397-
self.assertIs(w[1].category, ldap.LDAPBytesWarning)
398-
self.assertIn(
399-
u"Received non-bytes value u'*'",
400-
text_type(w[1].message)
401-
)
402-
self.assertIn(_normalize(w[1].filename), _normalize(__file__))
403-
self.assertIn(
404-
'CORRECT LINE',
405-
linecache.getline(w[1].filename, w[1].lineno)
406-
)
399+
self._check_byteswarning(
400+
w[1], u"Received non-bytes value u'*'")
407401

408402
@unittest.skipUnless(PY2, "no bytes_mode under Py3")
409403
def test_byteswarning_level_search(self):
410404
self._test_byteswarning_level_search('search_s')
411405
self._test_byteswarning_level_search('search_st')
412406
self._test_byteswarning_level_search('search_ext_s')
413407

408+
@unittest.skipUnless(PY2, "no bytes_mode under Py3")
409+
def test_byteswarning_initialize(self):
410+
with warnings.catch_warnings(record=True) as w:
411+
warnings.resetwarnings()
412+
warnings.simplefilter('always', ldap.LDAPBytesWarning)
413+
bytes_uri = self.server.ldap_uri.decode('utf-8')
414+
self.ldap_object_class(bytes_uri) # CORRECT LINE
415+
416+
self.assertEqual(len(w), 1, w)
417+
418+
self._check_byteswarning(
419+
w[0], u"Under Python 2, python-ldap uses bytes by default.")
420+
414421

415422
class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject):
416423
"""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy