1
# Copyright (c) Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Test cases for the L{twisted.python.reflect} module.
8
from __future__ import division, absolute_import
12
from collections import deque
14
from twisted.python.compat import _PY3
15
from twisted.trial import unittest
16
from twisted.trial.unittest import SynchronousTestCase as TestCase
17
from twisted.python import reflect
18
from twisted.python.reflect import (
19
accumulateMethods, prefixedMethods, prefixedMethodNames,
20
addMethodNamesToDict, fullyQualifiedName)
21
from twisted.python.versions import Version
26
A no-op class which can be used to verify the behavior of
27
method-discovering APIs.
32
A no-op method which can be discovered.
39
A subclass of a class with a method which can be discovered.
44
class Separate(object):
46
A no-op class with methods with differing prefixes.
49
def good_method(self):
51
A no-op method which a matching prefix to be discovered.
57
A no-op method with a mismatched prefix to not be discovered.
62
class AccumulateMethodsTests(TestCase):
64
Tests for L{accumulateMethods} which finds methods on a class hierarchy and
65
adds them to a dictionary.
68
def test_ownClass(self):
70
If x is and instance of Base and Base defines a method named method,
71
L{accumulateMethods} adds an item to the given dictionary with
72
C{"method"} as the key and a bound method object for Base.method value.
76
accumulateMethods(x, output)
77
self.assertEqual({"method": x.method}, output)
80
def test_baseClass(self):
82
If x is an instance of Sub and Sub is a subclass of Base and Base
83
defines a method named method, L{accumulateMethods} adds an item to the
84
given dictionary with C{"method"} as the key and a bound method object
85
for Base.method as the value.
89
accumulateMethods(x, output)
90
self.assertEqual({"method": x.method}, output)
93
def test_prefix(self):
95
If a prefix is given, L{accumulateMethods} limits its results to
96
methods beginning with that prefix. Keys in the resulting dictionary
97
also have the prefix removed from them.
101
accumulateMethods(x, output, 'good_')
102
self.assertEqual({'method': x.good_method}, output)
106
class PrefixedMethodsTests(TestCase):
108
Tests for L{prefixedMethods} which finds methods on a class hierarchy and
109
adds them to a dictionary.
112
def test_onlyObject(self):
114
L{prefixedMethods} returns a list of the methods discovered on an
118
output = prefixedMethods(x)
119
self.assertEqual([x.method], output)
122
def test_prefix(self):
124
If a prefix is given, L{prefixedMethods} returns only methods named
128
output = prefixedMethods(x, 'good_')
129
self.assertEqual([x.good_method], output)
133
class PrefixedMethodNamesTests(TestCase):
135
Tests for L{prefixedMethodNames}.
137
def test_method(self):
139
L{prefixedMethodNames} returns a list including methods with the given
140
prefix defined on the class passed to it.
142
self.assertEqual(["method"], prefixedMethodNames(Separate, "good_"))
145
def test_inheritedMethod(self):
147
L{prefixedMethodNames} returns a list included methods with the given
148
prefix defined on base classes of the class passed to it.
150
class Child(Separate):
152
self.assertEqual(["method"], prefixedMethodNames(Child, "good_"))
156
class AddMethodNamesToDictTests(TestCase):
158
Tests for L{addMethodNamesToDict}.
160
def test_baseClass(self):
162
If C{baseClass} is passed to L{addMethodNamesToDict}, only methods which
163
are a subclass of C{baseClass} are added to the result dictionary.
165
class Alternate(object):
168
class Child(Separate, Alternate):
169
def good_alternate(self):
173
addMethodNamesToDict(Child, result, 'good_', Alternate)
174
self.assertEqual({'alternate': 1}, result)
178
class Summer(object):
180
A class we look up as part of the LookupsTests.
190
class LookupsTests(TestCase):
192
Tests for L{namedClass}, L{namedModule}, and L{namedAny}.
195
def test_namedClassLookup(self):
197
L{namedClass} should return the class object for the name it is passed.
199
self.assertIdentical(
200
reflect.namedClass("twisted.test.test_reflect.Summer"),
204
def test_namedModuleLookup(self):
206
L{namedModule} should return the module object for the name it is
209
from twisted.python import monkey
210
self.assertIdentical(
211
reflect.namedModule("twisted.python.monkey"), monkey)
214
def test_namedAnyPackageLookup(self):
216
L{namedAny} should return the package object for the name it is passed.
218
import twisted.python
219
self.assertIdentical(
220
reflect.namedAny("twisted.python"), twisted.python)
223
def test_namedAnyModuleLookup(self):
225
L{namedAny} should return the module object for the name it is passed.
227
from twisted.python import monkey
228
self.assertIdentical(
229
reflect.namedAny("twisted.python.monkey"), monkey)
232
def test_namedAnyClassLookup(self):
234
L{namedAny} should return the class object for the name it is passed.
236
self.assertIdentical(
237
reflect.namedAny("twisted.test.test_reflect.Summer"),
241
def test_namedAnyAttributeLookup(self):
243
L{namedAny} should return the object an attribute of a non-module,
244
non-package object is bound to for the name it is passed.
246
# Note - not assertIs because unbound method lookup creates a new
247
# object every time. This is a foolishness of Python's object
248
# implementation, not a bug in Twisted.
251
"twisted.test.test_reflect.Summer.reallySet"),
255
def test_namedAnySecondAttributeLookup(self):
257
L{namedAny} should return the object an attribute of an object which
258
itself was an attribute of a non-module, non-package object is bound to
259
for the name it is passed.
261
self.assertIdentical(
263
"twisted.test.test_reflect."
264
"Summer.reallySet.__doc__"),
265
Summer.reallySet.__doc__)
268
def test_importExceptions(self):
270
Exceptions raised by modules which L{namedAny} causes to be imported
271
should pass through L{namedAny} to the caller.
275
reflect.namedAny, "twisted.test.reflect_helper_ZDE")
276
# Make sure that there is post-failed-import cleanup
279
reflect.namedAny, "twisted.test.reflect_helper_ZDE")
282
reflect.namedAny, "twisted.test.reflect_helper_VE")
283
# Modules which themselves raise ImportError when imported should
284
# result in an ImportError
287
reflect.namedAny, "twisted.test.reflect_helper_IE")
290
def test_attributeExceptions(self):
292
If segments on the end of a fully-qualified Python name represents
293
attributes which aren't actually present on the object represented by
294
the earlier segments, L{namedAny} should raise an L{AttributeError}.
298
reflect.namedAny, "twisted.nosuchmoduleintheworld")
299
# ImportError behaves somewhat differently between "import
300
# extant.nonextant" and "import extant.nonextant.nonextant", so test
301
# the latter as well.
304
reflect.namedAny, "twisted.nosuch.modulein.theworld")
308
"twisted.test.test_reflect.Summer.nosuchattribute")
311
def test_invalidNames(self):
313
Passing a name which isn't a fully-qualified Python name to L{namedAny}
314
should result in one of the following exceptions:
315
- L{InvalidName}: the name is not a dot-separated list of Python
317
- L{ObjectNotFound}: the object doesn't exist
318
- L{ModuleNotFound}: the object doesn't exist and there is only one
319
component in the name
321
err = self.assertRaises(reflect.ModuleNotFound, reflect.namedAny,
322
'nosuchmoduleintheworld')
323
self.assertEqual(str(err), "No module named 'nosuchmoduleintheworld'")
325
# This is a dot-separated list, but it isn't valid!
326
err = self.assertRaises(reflect.ObjectNotFound, reflect.namedAny,
328
self.assertEqual(str(err), "'@#$@(#.!@(#!@#' does not name an object")
330
err = self.assertRaises(reflect.ObjectNotFound, reflect.namedAny,
331
"tcelfer.nohtyp.detsiwt")
334
"'tcelfer.nohtyp.detsiwt' does not name an object")
336
err = self.assertRaises(reflect.InvalidName, reflect.namedAny, '')
337
self.assertEqual(str(err), 'Empty module name')
339
for invalidName in ['.twisted', 'twisted.', 'twisted..python']:
340
err = self.assertRaises(
341
reflect.InvalidName, reflect.namedAny, invalidName)
344
"name must be a string giving a '.'-separated list of Python "
345
"identifiers, not %r" % (invalidName,))
348
def test_requireModuleImportError(self):
350
When module import fails with ImportError it returns the specified
353
for name in ['nosuchmtopodule', 'no.such.module']:
356
result = reflect.requireModule(name, default=default)
358
self.assertIs(result, default)
361
def test_requireModuleDefaultNone(self):
363
When module import fails it returns C{None} by default.
365
result = reflect.requireModule('no.such.module')
367
self.assertIs(None, result)
370
def test_requireModuleRequestedImport(self):
372
When module import succeed it returns the module and not the default
375
from twisted.python import monkey
379
reflect.requireModule('twisted.python.monkey', default=default),
385
class Breakable(object):
392
raise RuntimeError("str!")
399
raise RuntimeError("repr!")
405
class BrokenType(Breakable, type):
408
def get___name__(self):
410
raise RuntimeError("no name")
412
__name__ = property(get___name__)
416
BTBase = BrokenType('BTBase', (Breakable,),
422
class NoClassAttr(Breakable):
423
__class__ = property(lambda x: x.not_class)
427
class SafeReprTests(TestCase):
429
Tests for L{reflect.safe_repr} function.
432
def test_workingRepr(self):
434
L{reflect.safe_repr} produces the same output as C{repr} on a working
437
xs = ([1, 2, 3], b'a')
438
self.assertEqual(list(map(reflect.safe_repr, xs)), list(map(repr, xs)))
441
def test_brokenRepr(self):
443
L{reflect.safe_repr} returns a string with class name, address, and
444
traceback when the repr call failed.
448
bRepr = reflect.safe_repr(b)
449
self.assertIn("Breakable instance at 0x", bRepr)
450
# Check that the file is in the repr, but without the extension as it
452
self.assertIn(os.path.splitext(__file__)[0], bRepr)
453
self.assertIn("RuntimeError: repr!", bRepr)
456
def test_brokenStr(self):
458
L{reflect.safe_repr} isn't affected by a broken C{__str__} method.
462
self.assertEqual(reflect.safe_repr(b), repr(b))
465
def test_brokenClassRepr(self):
469
reflect.safe_repr(X())
472
def test_brokenReprIncludesID(self):
474
C{id} is used to print the ID of the object in case of an error.
476
L{safe_repr} includes a traceback after a newline, so we only check
477
against the first line of the repr.
482
xRepr = reflect.safe_repr(X)
483
xReprExpected = ('<BrokenType instance at 0x%x with repr error:'
485
self.assertEqual(xReprExpected, xRepr.split('\n')[0])
488
def test_brokenClassStr(self):
492
reflect.safe_repr(X())
495
def test_brokenClassAttribute(self):
497
If an object raises an exception when accessing its C{__class__}
498
attribute, L{reflect.safe_repr} uses C{type} to retrieve the class
503
bRepr = reflect.safe_repr(b)
504
self.assertIn("NoClassAttr instance at 0x", bRepr)
505
self.assertIn(os.path.splitext(__file__)[0], bRepr)
506
self.assertIn("RuntimeError: repr!", bRepr)
509
def test_brokenClassNameAttribute(self):
511
If a class raises an exception when accessing its C{__name__} attribute
512
B{and} when calling its C{__str__} implementation, L{reflect.safe_repr}
513
returns 'BROKEN CLASS' instead of the class name.
517
xRepr = reflect.safe_repr(X())
518
self.assertIn("<BROKEN CLASS AT 0x", xRepr)
519
self.assertIn(os.path.splitext(__file__)[0], xRepr)
520
self.assertIn("RuntimeError: repr!", xRepr)
524
class SafeStrTests(TestCase):
526
Tests for L{reflect.safe_str} function.
529
def test_workingStr(self):
531
self.assertEqual(reflect.safe_str(x), str(x))
534
def test_brokenStr(self):
540
def test_workingAscii(self):
542
L{safe_str} for C{str} with ascii-only data should return the
546
self.assertEqual(reflect.safe_str(x), 'a')
549
def test_workingUtf8_2(self):
551
L{safe_str} for C{str} with utf-8 encoded data should return the
555
self.assertEqual(reflect.safe_str(x), x)
558
def test_workingUtf8_3(self):
560
L{safe_str} for C{bytes} with utf-8 encoded data should return
561
the value decoded into C{str}.
564
self.assertEqual(reflect.safe_str(x), x.decode('utf-8'))
567
# TODO: after something like python.compat.nativeUtf8String is
568
# introduced, use that one for assertEqual. Then we can combine
569
# test_workingUtf8_* tests into one without needing _PY3.
570
# nativeUtf8String is needed for Python 3 anyway.
571
test_workingUtf8_2.skip = ("Skip Python 2 specific test for utf-8 str")
573
test_workingUtf8_3.skip = (
574
"Skip Python 3 specific test for utf-8 bytes")
577
def test_brokenUtf8(self):
579
Use str() for non-utf8 bytes: "b'non-utf8'"
582
xStr = reflect.safe_str(x)
583
self.assertEqual(xStr, str(x))
586
def test_brokenRepr(self):
592
def test_brokenClassStr(self):
596
reflect.safe_str(X())
599
def test_brokenClassRepr(self):
603
reflect.safe_str(X())
606
def test_brokenClassAttribute(self):
608
If an object raises an exception when accessing its C{__class__}
609
attribute, L{reflect.safe_str} uses C{type} to retrieve the class
614
bStr = reflect.safe_str(b)
615
self.assertIn("NoClassAttr instance at 0x", bStr)
616
self.assertIn(os.path.splitext(__file__)[0], bStr)
617
self.assertIn("RuntimeError: str!", bStr)
620
def test_brokenClassNameAttribute(self):
622
If a class raises an exception when accessing its C{__name__} attribute
623
B{and} when calling its C{__str__} implementation, L{reflect.safe_str}
624
returns 'BROKEN CLASS' instead of the class name.
628
xStr = reflect.safe_str(X())
629
self.assertIn("<BROKEN CLASS AT 0x", xStr)
630
self.assertIn(os.path.splitext(__file__)[0], xStr)
631
self.assertIn("RuntimeError: str!", xStr)
635
class FilenameToModuleTests(TestCase):
637
Test L{filenameToModuleName} detection.
641
self.path = os.path.join(self.mktemp(), "fakepackage", "test")
642
os.makedirs(self.path)
643
with open(os.path.join(self.path, "__init__.py"), "w") as f:
645
with open(os.path.join(os.path.dirname(self.path), "__init__.py"),
650
def test_directory(self):
652
L{filenameToModuleName} returns the correct module (a package) given a
655
module = reflect.filenameToModuleName(self.path)
656
self.assertEqual(module, 'fakepackage.test')
657
module = reflect.filenameToModuleName(self.path + os.path.sep)
658
self.assertEqual(module, 'fakepackage.test')
663
L{filenameToModuleName} returns the correct module given the path to
666
module = reflect.filenameToModuleName(
667
os.path.join(self.path, 'test_reflect.py'))
668
self.assertEqual(module, 'fakepackage.test.test_reflect')
671
def test_bytes(self):
673
L{filenameToModuleName} returns the correct module given a C{bytes}
676
module = reflect.filenameToModuleName(
677
os.path.join(self.path.encode("utf-8"), b'test_reflect.py'))
678
# Module names are always native string:
679
self.assertEqual(module, 'fakepackage.test.test_reflect')
683
class FullyQualifiedNameTests(TestCase):
685
Test for L{fullyQualifiedName}.
688
def _checkFullyQualifiedName(self, obj, expected):
690
Helper to check that fully qualified name of C{obj} results to
693
self.assertEqual(fullyQualifiedName(obj), expected)
696
def test_package(self):
698
L{fullyQualifiedName} returns the full name of a package and a
702
self._checkFullyQualifiedName(twisted, 'twisted')
703
import twisted.python
704
self._checkFullyQualifiedName(twisted.python, 'twisted.python')
707
def test_module(self):
709
L{fullyQualifiedName} returns the name of a module inside a package.
711
import twisted.python.compat
712
self._checkFullyQualifiedName(
713
twisted.python.compat, 'twisted.python.compat')
716
def test_class(self):
718
L{fullyQualifiedName} returns the name of a class and its module.
720
self._checkFullyQualifiedName(
721
FullyQualifiedNameTests,
722
'%s.FullyQualifiedNameTests' % (__name__,))
725
def test_function(self):
727
L{fullyQualifiedName} returns the name of a function inside its module.
729
self._checkFullyQualifiedName(
730
fullyQualifiedName, "twisted.python.reflect.fullyQualifiedName")
733
def test_boundMethod(self):
735
L{fullyQualifiedName} returns the name of a bound method inside its
736
class and its module.
738
self._checkFullyQualifiedName(
739
self.test_boundMethod,
740
"%s.%s.test_boundMethod" % (__name__, self.__class__.__name__))
743
def test_unboundMethod(self):
745
L{fullyQualifiedName} returns the name of an unbound method inside its
746
class and its module.
748
self._checkFullyQualifiedName(
749
self.__class__.test_unboundMethod,
750
"%s.%s.test_unboundMethod" % (__name__, self.__class__.__name__))
753
class ObjectGrepTests(unittest.TestCase):
755
# This is to be removed when fixing #6986
756
skip = "twisted.python.reflect.objgrep hasn't been ported to Python 3"
759
def test_dictionary(self):
761
Test references search through a dictionnary, as a key or as a value.
767
self.assertIn("[None]", reflect.objgrep(d1, o, reflect.isSame))
768
self.assertIn("{None}", reflect.objgrep(d2, o, reflect.isSame))
772
Test references search through a list.
777
self.assertIn("[1]", reflect.objgrep(L, o, reflect.isSame))
779
def test_tuple(self):
781
Test references search through a tuple.
786
self.assertIn("[0]", reflect.objgrep(T, o, reflect.isSame))
788
def test_instance(self):
790
Test references search through an object attribute.
798
self.assertIn(".o", reflect.objgrep(d, o, reflect.isSame))
800
def test_weakref(self):
802
Test references search through a weakref object.
809
self.assertIn("()", reflect.objgrep(w1, o, reflect.isSame))
811
def test_boundMethod(self):
813
Test references search through method special attributes.
821
self.assertIn(".__self__",
822
reflect.objgrep(m, m.__self__, reflect.isSame))
823
self.assertIn(".__self__.__class__",
824
reflect.objgrep(m, m.__self__.__class__, reflect.isSame))
825
self.assertIn(".__func__",
826
reflect.objgrep(m, m.__func__, reflect.isSame))
828
def test_everything(self):
830
Test references search using complex set of objects.
837
D1 = {(): "baz", None: "Quux", o: "Foosh"}
838
L = [None, (), D1, 3]
840
D2 = {0: "foo", 1: "bar", 2: T}
846
self.assertIn("().__self__.attr[2][0][2]{'Foosh'}",
847
reflect.objgrep(w, o, reflect.isSame))
849
def test_depthLimit(self):
851
Test the depth of references search.
858
self.assertEqual(['[0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=1))
859
self.assertEqual(['[0]', '[1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=2))
860
self.assertEqual(['[0]', '[1][0]', '[1][1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=3))
863
def test_deque(self):
865
Test references search through a deque object.
872
self.assertIn("[1]", reflect.objgrep(D, o, reflect.isSame))
875
class GetClassTests(unittest.TestCase):
877
oldClassNames = ['type']
879
oldClassNames = ['class', 'classobj']
885
self.assertIn(reflect.getClass(OldClass).__name__, self.oldClassNames)
886
self.assertEqual(reflect.getClass(old).__name__, 'OldClass')
889
class NewClass(object):
892
self.assertEqual(reflect.getClass(NewClass).__name__, 'type')
893
self.assertEqual(reflect.getClass(new).__name__, 'NewClass')
897
# The functions tested below are deprecated but still used by external
898
# projects like Nevow 0.10. They are not going to be ported to Python 3
899
# (hence the condition above) and will be removed as soon as no project used
900
# by Twisted will depend on these functions. Also, have a look at the
901
# comments related to those functions in twisted.python.reflect.
902
class DeprecationTests(unittest.TestCase):
904
Test deprecations in twisted.python.reflect
907
def test_allYourBase(self):
909
Test deprecation of L{reflect.allYourBase}. See #5481 for removal.
912
(Version("Twisted", 11, 0, 0), "inspect.getmro"),
913
reflect.allYourBase, DeprecationTests)
916
def test_accumulateBases(self):
918
Test deprecation of L{reflect.accumulateBases}. See #5481 for removal.
922
(Version("Twisted", 11, 0, 0), "inspect.getmro"),
923
reflect.accumulateBases, DeprecationTests, l, None)
926
def test_getcurrent(self):
928
Test deprecation of L{reflect.getcurrent}.
935
Version("Twisted", 14, 0, 0),
936
reflect.getcurrent, C)
939
def test_isinst(self):
941
Test deprecation of L{reflect.isinst}.
945
(Version("Twisted", 14, 0, 0), "isinstance"),
946
reflect.isinst, object(), object)