You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_reflect.py 25KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test cases for the L{twisted.python.reflect} module.
  5. """
  6. from __future__ import division, absolute_import
  7. import os
  8. import weakref
  9. from collections import deque
  10. from twisted.python.compat import _PY3
  11. from twisted.trial import unittest
  12. from twisted.trial.unittest import SynchronousTestCase as TestCase
  13. from twisted.python import reflect
  14. from twisted.python.reflect import (
  15. accumulateMethods, prefixedMethods, prefixedMethodNames,
  16. addMethodNamesToDict, fullyQualifiedName)
  17. class Base(object):
  18. """
  19. A no-op class which can be used to verify the behavior of
  20. method-discovering APIs.
  21. """
  22. def method(self):
  23. """
  24. A no-op method which can be discovered.
  25. """
  26. class Sub(Base):
  27. """
  28. A subclass of a class with a method which can be discovered.
  29. """
  30. class Separate(object):
  31. """
  32. A no-op class with methods with differing prefixes.
  33. """
  34. def good_method(self):
  35. """
  36. A no-op method which a matching prefix to be discovered.
  37. """
  38. def bad_method(self):
  39. """
  40. A no-op method with a mismatched prefix to not be discovered.
  41. """
  42. class AccumulateMethodsTests(TestCase):
  43. """
  44. Tests for L{accumulateMethods} which finds methods on a class hierarchy and
  45. adds them to a dictionary.
  46. """
  47. def test_ownClass(self):
  48. """
  49. If x is and instance of Base and Base defines a method named method,
  50. L{accumulateMethods} adds an item to the given dictionary with
  51. C{"method"} as the key and a bound method object for Base.method value.
  52. """
  53. x = Base()
  54. output = {}
  55. accumulateMethods(x, output)
  56. self.assertEqual({"method": x.method}, output)
  57. def test_baseClass(self):
  58. """
  59. If x is an instance of Sub and Sub is a subclass of Base and Base
  60. defines a method named method, L{accumulateMethods} adds an item to the
  61. given dictionary with C{"method"} as the key and a bound method object
  62. for Base.method as the value.
  63. """
  64. x = Sub()
  65. output = {}
  66. accumulateMethods(x, output)
  67. self.assertEqual({"method": x.method}, output)
  68. def test_prefix(self):
  69. """
  70. If a prefix is given, L{accumulateMethods} limits its results to
  71. methods beginning with that prefix. Keys in the resulting dictionary
  72. also have the prefix removed from them.
  73. """
  74. x = Separate()
  75. output = {}
  76. accumulateMethods(x, output, 'good_')
  77. self.assertEqual({'method': x.good_method}, output)
  78. class PrefixedMethodsTests(TestCase):
  79. """
  80. Tests for L{prefixedMethods} which finds methods on a class hierarchy and
  81. adds them to a dictionary.
  82. """
  83. def test_onlyObject(self):
  84. """
  85. L{prefixedMethods} returns a list of the methods discovered on an
  86. object.
  87. """
  88. x = Base()
  89. output = prefixedMethods(x)
  90. self.assertEqual([x.method], output)
  91. def test_prefix(self):
  92. """
  93. If a prefix is given, L{prefixedMethods} returns only methods named
  94. with that prefix.
  95. """
  96. x = Separate()
  97. output = prefixedMethods(x, 'good_')
  98. self.assertEqual([x.good_method], output)
  99. class PrefixedMethodNamesTests(TestCase):
  100. """
  101. Tests for L{prefixedMethodNames}.
  102. """
  103. def test_method(self):
  104. """
  105. L{prefixedMethodNames} returns a list including methods with the given
  106. prefix defined on the class passed to it.
  107. """
  108. self.assertEqual(["method"], prefixedMethodNames(Separate, "good_"))
  109. def test_inheritedMethod(self):
  110. """
  111. L{prefixedMethodNames} returns a list included methods with the given
  112. prefix defined on base classes of the class passed to it.
  113. """
  114. class Child(Separate):
  115. pass
  116. self.assertEqual(["method"], prefixedMethodNames(Child, "good_"))
  117. class AddMethodNamesToDictTests(TestCase):
  118. """
  119. Tests for L{addMethodNamesToDict}.
  120. """
  121. def test_baseClass(self):
  122. """
  123. If C{baseClass} is passed to L{addMethodNamesToDict}, only methods which
  124. are a subclass of C{baseClass} are added to the result dictionary.
  125. """
  126. class Alternate(object):
  127. pass
  128. class Child(Separate, Alternate):
  129. def good_alternate(self):
  130. pass
  131. result = {}
  132. addMethodNamesToDict(Child, result, 'good_', Alternate)
  133. self.assertEqual({'alternate': 1}, result)
  134. class Summer(object):
  135. """
  136. A class we look up as part of the LookupsTests.
  137. """
  138. def reallySet(self):
  139. """
  140. Do something.
  141. """
  142. class LookupsTests(TestCase):
  143. """
  144. Tests for L{namedClass}, L{namedModule}, and L{namedAny}.
  145. """
  146. def test_namedClassLookup(self):
  147. """
  148. L{namedClass} should return the class object for the name it is passed.
  149. """
  150. self.assertIs(
  151. reflect.namedClass("twisted.test.test_reflect.Summer"),
  152. Summer)
  153. def test_namedModuleLookup(self):
  154. """
  155. L{namedModule} should return the module object for the name it is
  156. passed.
  157. """
  158. from twisted.python import monkey
  159. self.assertIs(
  160. reflect.namedModule("twisted.python.monkey"), monkey)
  161. def test_namedAnyPackageLookup(self):
  162. """
  163. L{namedAny} should return the package object for the name it is passed.
  164. """
  165. import twisted.python
  166. self.assertIs(
  167. reflect.namedAny("twisted.python"), twisted.python)
  168. def test_namedAnyModuleLookup(self):
  169. """
  170. L{namedAny} should return the module object for the name it is passed.
  171. """
  172. from twisted.python import monkey
  173. self.assertIs(
  174. reflect.namedAny("twisted.python.monkey"), monkey)
  175. def test_namedAnyClassLookup(self):
  176. """
  177. L{namedAny} should return the class object for the name it is passed.
  178. """
  179. self.assertIs(
  180. reflect.namedAny("twisted.test.test_reflect.Summer"),
  181. Summer)
  182. def test_namedAnyAttributeLookup(self):
  183. """
  184. L{namedAny} should return the object an attribute of a non-module,
  185. non-package object is bound to for the name it is passed.
  186. """
  187. # Note - not assertIs because unbound method lookup creates a new
  188. # object every time. This is a foolishness of Python's object
  189. # implementation, not a bug in Twisted.
  190. self.assertEqual(
  191. reflect.namedAny(
  192. "twisted.test.test_reflect.Summer.reallySet"),
  193. Summer.reallySet)
  194. def test_namedAnySecondAttributeLookup(self):
  195. """
  196. L{namedAny} should return the object an attribute of an object which
  197. itself was an attribute of a non-module, non-package object is bound to
  198. for the name it is passed.
  199. """
  200. self.assertIs(
  201. reflect.namedAny(
  202. "twisted.test.test_reflect."
  203. "Summer.reallySet.__doc__"),
  204. Summer.reallySet.__doc__)
  205. def test_importExceptions(self):
  206. """
  207. Exceptions raised by modules which L{namedAny} causes to be imported
  208. should pass through L{namedAny} to the caller.
  209. """
  210. self.assertRaises(
  211. ZeroDivisionError,
  212. reflect.namedAny, "twisted.test.reflect_helper_ZDE")
  213. # Make sure that there is post-failed-import cleanup
  214. self.assertRaises(
  215. ZeroDivisionError,
  216. reflect.namedAny, "twisted.test.reflect_helper_ZDE")
  217. self.assertRaises(
  218. ValueError,
  219. reflect.namedAny, "twisted.test.reflect_helper_VE")
  220. # Modules which themselves raise ImportError when imported should
  221. # result in an ImportError
  222. self.assertRaises(
  223. ImportError,
  224. reflect.namedAny, "twisted.test.reflect_helper_IE")
  225. def test_attributeExceptions(self):
  226. """
  227. If segments on the end of a fully-qualified Python name represents
  228. attributes which aren't actually present on the object represented by
  229. the earlier segments, L{namedAny} should raise an L{AttributeError}.
  230. """
  231. self.assertRaises(
  232. AttributeError,
  233. reflect.namedAny, "twisted.nosuchmoduleintheworld")
  234. # ImportError behaves somewhat differently between "import
  235. # extant.nonextant" and "import extant.nonextant.nonextant", so test
  236. # the latter as well.
  237. self.assertRaises(
  238. AttributeError,
  239. reflect.namedAny, "twisted.nosuch.modulein.theworld")
  240. self.assertRaises(
  241. AttributeError,
  242. reflect.namedAny,
  243. "twisted.test.test_reflect.Summer.nosuchattribute")
  244. def test_invalidNames(self):
  245. """
  246. Passing a name which isn't a fully-qualified Python name to L{namedAny}
  247. should result in one of the following exceptions:
  248. - L{InvalidName}: the name is not a dot-separated list of Python
  249. objects
  250. - L{ObjectNotFound}: the object doesn't exist
  251. - L{ModuleNotFound}: the object doesn't exist and there is only one
  252. component in the name
  253. """
  254. err = self.assertRaises(reflect.ModuleNotFound, reflect.namedAny,
  255. 'nosuchmoduleintheworld')
  256. self.assertEqual(str(err), "No module named 'nosuchmoduleintheworld'")
  257. # This is a dot-separated list, but it isn't valid!
  258. err = self.assertRaises(reflect.ObjectNotFound, reflect.namedAny,
  259. "@#$@(#.!@(#!@#")
  260. self.assertEqual(str(err), "'@#$@(#.!@(#!@#' does not name an object")
  261. err = self.assertRaises(reflect.ObjectNotFound, reflect.namedAny,
  262. "tcelfer.nohtyp.detsiwt")
  263. self.assertEqual(
  264. str(err),
  265. "'tcelfer.nohtyp.detsiwt' does not name an object")
  266. err = self.assertRaises(reflect.InvalidName, reflect.namedAny, '')
  267. self.assertEqual(str(err), 'Empty module name')
  268. for invalidName in ['.twisted', 'twisted.', 'twisted..python']:
  269. err = self.assertRaises(
  270. reflect.InvalidName, reflect.namedAny, invalidName)
  271. self.assertEqual(
  272. str(err),
  273. "name must be a string giving a '.'-separated list of Python "
  274. "identifiers, not %r" % (invalidName,))
  275. def test_requireModuleImportError(self):
  276. """
  277. When module import fails with ImportError it returns the specified
  278. default value.
  279. """
  280. for name in ['nosuchmtopodule', 'no.such.module']:
  281. default = object()
  282. result = reflect.requireModule(name, default=default)
  283. self.assertIs(result, default)
  284. def test_requireModuleDefaultNone(self):
  285. """
  286. When module import fails it returns L{None} by default.
  287. """
  288. result = reflect.requireModule('no.such.module')
  289. self.assertIsNone(result)
  290. def test_requireModuleRequestedImport(self):
  291. """
  292. When module import succeed it returns the module and not the default
  293. value.
  294. """
  295. from twisted.python import monkey
  296. default = object()
  297. self.assertIs(
  298. reflect.requireModule('twisted.python.monkey', default=default),
  299. monkey,
  300. )
  301. class Breakable(object):
  302. breakRepr = False
  303. breakStr = False
  304. def __str__(self):
  305. if self.breakStr:
  306. raise RuntimeError("str!")
  307. else:
  308. return '<Breakable>'
  309. def __repr__(self):
  310. if self.breakRepr:
  311. raise RuntimeError("repr!")
  312. else:
  313. return 'Breakable()'
  314. class BrokenType(Breakable, type):
  315. breakName = False
  316. def get___name__(self):
  317. if self.breakName:
  318. raise RuntimeError("no name")
  319. return 'BrokenType'
  320. __name__ = property(get___name__)
  321. BTBase = BrokenType('BTBase', (Breakable,),
  322. {"breakRepr": True,
  323. "breakStr": True})
  324. class NoClassAttr(Breakable):
  325. __class__ = property(lambda x: x.not_class)
  326. class SafeReprTests(TestCase):
  327. """
  328. Tests for L{reflect.safe_repr} function.
  329. """
  330. def test_workingRepr(self):
  331. """
  332. L{reflect.safe_repr} produces the same output as C{repr} on a working
  333. object.
  334. """
  335. xs = ([1, 2, 3], b'a')
  336. self.assertEqual(list(map(reflect.safe_repr, xs)), list(map(repr, xs)))
  337. def test_brokenRepr(self):
  338. """
  339. L{reflect.safe_repr} returns a string with class name, address, and
  340. traceback when the repr call failed.
  341. """
  342. b = Breakable()
  343. b.breakRepr = True
  344. bRepr = reflect.safe_repr(b)
  345. self.assertIn("Breakable instance at 0x", bRepr)
  346. # Check that the file is in the repr, but without the extension as it
  347. # can be .py/.pyc
  348. self.assertIn(os.path.splitext(__file__)[0], bRepr)
  349. self.assertIn("RuntimeError: repr!", bRepr)
  350. def test_brokenStr(self):
  351. """
  352. L{reflect.safe_repr} isn't affected by a broken C{__str__} method.
  353. """
  354. b = Breakable()
  355. b.breakStr = True
  356. self.assertEqual(reflect.safe_repr(b), repr(b))
  357. def test_brokenClassRepr(self):
  358. class X(BTBase):
  359. breakRepr = True
  360. reflect.safe_repr(X)
  361. reflect.safe_repr(X())
  362. def test_brokenReprIncludesID(self):
  363. """
  364. C{id} is used to print the ID of the object in case of an error.
  365. L{safe_repr} includes a traceback after a newline, so we only check
  366. against the first line of the repr.
  367. """
  368. class X(BTBase):
  369. breakRepr = True
  370. xRepr = reflect.safe_repr(X)
  371. xReprExpected = ('<BrokenType instance at 0x%x with repr error:'
  372. % (id(X),))
  373. self.assertEqual(xReprExpected, xRepr.split('\n')[0])
  374. def test_brokenClassStr(self):
  375. class X(BTBase):
  376. breakStr = True
  377. reflect.safe_repr(X)
  378. reflect.safe_repr(X())
  379. def test_brokenClassAttribute(self):
  380. """
  381. If an object raises an exception when accessing its C{__class__}
  382. attribute, L{reflect.safe_repr} uses C{type} to retrieve the class
  383. object.
  384. """
  385. b = NoClassAttr()
  386. b.breakRepr = True
  387. bRepr = reflect.safe_repr(b)
  388. self.assertIn("NoClassAttr instance at 0x", bRepr)
  389. self.assertIn(os.path.splitext(__file__)[0], bRepr)
  390. self.assertIn("RuntimeError: repr!", bRepr)
  391. def test_brokenClassNameAttribute(self):
  392. """
  393. If a class raises an exception when accessing its C{__name__} attribute
  394. B{and} when calling its C{__str__} implementation, L{reflect.safe_repr}
  395. returns 'BROKEN CLASS' instead of the class name.
  396. """
  397. class X(BTBase):
  398. breakName = True
  399. xRepr = reflect.safe_repr(X())
  400. self.assertIn("<BROKEN CLASS AT 0x", xRepr)
  401. self.assertIn(os.path.splitext(__file__)[0], xRepr)
  402. self.assertIn("RuntimeError: repr!", xRepr)
  403. class SafeStrTests(TestCase):
  404. """
  405. Tests for L{reflect.safe_str} function.
  406. """
  407. def test_workingStr(self):
  408. x = [1, 2, 3]
  409. self.assertEqual(reflect.safe_str(x), str(x))
  410. def test_brokenStr(self):
  411. b = Breakable()
  412. b.breakStr = True
  413. reflect.safe_str(b)
  414. def test_workingAscii(self):
  415. """
  416. L{safe_str} for C{str} with ascii-only data should return the
  417. value unchanged.
  418. """
  419. x = 'a'
  420. self.assertEqual(reflect.safe_str(x), 'a')
  421. def test_workingUtf8_2(self):
  422. """
  423. L{safe_str} for C{str} with utf-8 encoded data should return the
  424. value unchanged.
  425. """
  426. x = b't\xc3\xbcst'
  427. self.assertEqual(reflect.safe_str(x), x)
  428. def test_workingUtf8_3(self):
  429. """
  430. L{safe_str} for C{bytes} with utf-8 encoded data should return
  431. the value decoded into C{str}.
  432. """
  433. x = b't\xc3\xbcst'
  434. self.assertEqual(reflect.safe_str(x), x.decode('utf-8'))
  435. if _PY3:
  436. # TODO: after something like python.compat.nativeUtf8String is
  437. # introduced, use that one for assertEqual. Then we can combine
  438. # test_workingUtf8_* tests into one without needing _PY3.
  439. # nativeUtf8String is needed for Python 3 anyway.
  440. test_workingUtf8_2.skip = ("Skip Python 2 specific test for utf-8 str")
  441. else:
  442. test_workingUtf8_3.skip = (
  443. "Skip Python 3 specific test for utf-8 bytes")
  444. def test_brokenUtf8(self):
  445. """
  446. Use str() for non-utf8 bytes: "b'non-utf8'"
  447. """
  448. x = b'\xff'
  449. xStr = reflect.safe_str(x)
  450. self.assertEqual(xStr, str(x))
  451. def test_brokenRepr(self):
  452. b = Breakable()
  453. b.breakRepr = True
  454. reflect.safe_str(b)
  455. def test_brokenClassStr(self):
  456. class X(BTBase):
  457. breakStr = True
  458. reflect.safe_str(X)
  459. reflect.safe_str(X())
  460. def test_brokenClassRepr(self):
  461. class X(BTBase):
  462. breakRepr = True
  463. reflect.safe_str(X)
  464. reflect.safe_str(X())
  465. def test_brokenClassAttribute(self):
  466. """
  467. If an object raises an exception when accessing its C{__class__}
  468. attribute, L{reflect.safe_str} uses C{type} to retrieve the class
  469. object.
  470. """
  471. b = NoClassAttr()
  472. b.breakStr = True
  473. bStr = reflect.safe_str(b)
  474. self.assertIn("NoClassAttr instance at 0x", bStr)
  475. self.assertIn(os.path.splitext(__file__)[0], bStr)
  476. self.assertIn("RuntimeError: str!", bStr)
  477. def test_brokenClassNameAttribute(self):
  478. """
  479. If a class raises an exception when accessing its C{__name__} attribute
  480. B{and} when calling its C{__str__} implementation, L{reflect.safe_str}
  481. returns 'BROKEN CLASS' instead of the class name.
  482. """
  483. class X(BTBase):
  484. breakName = True
  485. xStr = reflect.safe_str(X())
  486. self.assertIn("<BROKEN CLASS AT 0x", xStr)
  487. self.assertIn(os.path.splitext(__file__)[0], xStr)
  488. self.assertIn("RuntimeError: str!", xStr)
  489. class FilenameToModuleTests(TestCase):
  490. """
  491. Test L{filenameToModuleName} detection.
  492. """
  493. def setUp(self):
  494. self.path = os.path.join(self.mktemp(), "fakepackage", "test")
  495. os.makedirs(self.path)
  496. with open(os.path.join(self.path, "__init__.py"), "w") as f:
  497. f.write("")
  498. with open(os.path.join(os.path.dirname(self.path), "__init__.py"),
  499. "w") as f:
  500. f.write("")
  501. def test_directory(self):
  502. """
  503. L{filenameToModuleName} returns the correct module (a package) given a
  504. directory.
  505. """
  506. module = reflect.filenameToModuleName(self.path)
  507. self.assertEqual(module, 'fakepackage.test')
  508. module = reflect.filenameToModuleName(self.path + os.path.sep)
  509. self.assertEqual(module, 'fakepackage.test')
  510. def test_file(self):
  511. """
  512. L{filenameToModuleName} returns the correct module given the path to
  513. its file.
  514. """
  515. module = reflect.filenameToModuleName(
  516. os.path.join(self.path, 'test_reflect.py'))
  517. self.assertEqual(module, 'fakepackage.test.test_reflect')
  518. def test_bytes(self):
  519. """
  520. L{filenameToModuleName} returns the correct module given a C{bytes}
  521. path to its file.
  522. """
  523. module = reflect.filenameToModuleName(
  524. os.path.join(self.path.encode("utf-8"), b'test_reflect.py'))
  525. # Module names are always native string:
  526. self.assertEqual(module, 'fakepackage.test.test_reflect')
  527. class FullyQualifiedNameTests(TestCase):
  528. """
  529. Test for L{fullyQualifiedName}.
  530. """
  531. def _checkFullyQualifiedName(self, obj, expected):
  532. """
  533. Helper to check that fully qualified name of C{obj} results to
  534. C{expected}.
  535. """
  536. self.assertEqual(fullyQualifiedName(obj), expected)
  537. def test_package(self):
  538. """
  539. L{fullyQualifiedName} returns the full name of a package and a
  540. subpackage.
  541. """
  542. import twisted
  543. self._checkFullyQualifiedName(twisted, 'twisted')
  544. import twisted.python
  545. self._checkFullyQualifiedName(twisted.python, 'twisted.python')
  546. def test_module(self):
  547. """
  548. L{fullyQualifiedName} returns the name of a module inside a package.
  549. """
  550. import twisted.python.compat
  551. self._checkFullyQualifiedName(
  552. twisted.python.compat, 'twisted.python.compat')
  553. def test_class(self):
  554. """
  555. L{fullyQualifiedName} returns the name of a class and its module.
  556. """
  557. self._checkFullyQualifiedName(
  558. FullyQualifiedNameTests,
  559. '%s.FullyQualifiedNameTests' % (__name__,))
  560. def test_function(self):
  561. """
  562. L{fullyQualifiedName} returns the name of a function inside its module.
  563. """
  564. self._checkFullyQualifiedName(
  565. fullyQualifiedName, "twisted.python.reflect.fullyQualifiedName")
  566. def test_boundMethod(self):
  567. """
  568. L{fullyQualifiedName} returns the name of a bound method inside its
  569. class and its module.
  570. """
  571. self._checkFullyQualifiedName(
  572. self.test_boundMethod,
  573. "%s.%s.test_boundMethod" % (__name__, self.__class__.__name__))
  574. def test_unboundMethod(self):
  575. """
  576. L{fullyQualifiedName} returns the name of an unbound method inside its
  577. class and its module.
  578. """
  579. self._checkFullyQualifiedName(
  580. self.__class__.test_unboundMethod,
  581. "%s.%s.test_unboundMethod" % (__name__, self.__class__.__name__))
  582. class ObjectGrepTests(unittest.TestCase):
  583. if _PY3:
  584. # This is to be removed when fixing #6986
  585. skip = "twisted.python.reflect.objgrep hasn't been ported to Python 3"
  586. def test_dictionary(self):
  587. """
  588. Test references search through a dictionary, as a key or as a value.
  589. """
  590. o = object()
  591. d1 = {None: o}
  592. d2 = {o: None}
  593. self.assertIn("[None]", reflect.objgrep(d1, o, reflect.isSame))
  594. self.assertIn("{None}", reflect.objgrep(d2, o, reflect.isSame))
  595. def test_list(self):
  596. """
  597. Test references search through a list.
  598. """
  599. o = object()
  600. L = [None, o]
  601. self.assertIn("[1]", reflect.objgrep(L, o, reflect.isSame))
  602. def test_tuple(self):
  603. """
  604. Test references search through a tuple.
  605. """
  606. o = object()
  607. T = (o, None)
  608. self.assertIn("[0]", reflect.objgrep(T, o, reflect.isSame))
  609. def test_instance(self):
  610. """
  611. Test references search through an object attribute.
  612. """
  613. class Dummy:
  614. pass
  615. o = object()
  616. d = Dummy()
  617. d.o = o
  618. self.assertIn(".o", reflect.objgrep(d, o, reflect.isSame))
  619. def test_weakref(self):
  620. """
  621. Test references search through a weakref object.
  622. """
  623. class Dummy:
  624. pass
  625. o = Dummy()
  626. w1 = weakref.ref(o)
  627. self.assertIn("()", reflect.objgrep(w1, o, reflect.isSame))
  628. def test_boundMethod(self):
  629. """
  630. Test references search through method special attributes.
  631. """
  632. class Dummy:
  633. def dummy(self):
  634. pass
  635. o = Dummy()
  636. m = o.dummy
  637. self.assertIn(".__self__",
  638. reflect.objgrep(m, m.__self__, reflect.isSame))
  639. self.assertIn(".__self__.__class__",
  640. reflect.objgrep(m, m.__self__.__class__, reflect.isSame))
  641. self.assertIn(".__func__",
  642. reflect.objgrep(m, m.__func__, reflect.isSame))
  643. def test_everything(self):
  644. """
  645. Test references search using complex set of objects.
  646. """
  647. class Dummy:
  648. def method(self):
  649. pass
  650. o = Dummy()
  651. D1 = {(): "baz", None: "Quux", o: "Foosh"}
  652. L = [None, (), D1, 3]
  653. T = (L, {}, Dummy())
  654. D2 = {0: "foo", 1: "bar", 2: T}
  655. i = Dummy()
  656. i.attr = D2
  657. m = i.method
  658. w = weakref.ref(m)
  659. self.assertIn("().__self__.attr[2][0][2]{'Foosh'}",
  660. reflect.objgrep(w, o, reflect.isSame))
  661. def test_depthLimit(self):
  662. """
  663. Test the depth of references search.
  664. """
  665. a = []
  666. b = [a]
  667. c = [a, b]
  668. d = [a, c]
  669. self.assertEqual(['[0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=1))
  670. self.assertEqual(['[0]', '[1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=2))
  671. self.assertEqual(['[0]', '[1][0]', '[1][1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=3))
  672. def test_deque(self):
  673. """
  674. Test references search through a deque object.
  675. """
  676. o = object()
  677. D = deque()
  678. D.append(None)
  679. D.append(o)
  680. self.assertIn("[1]", reflect.objgrep(D, o, reflect.isSame))
  681. class GetClassTests(unittest.TestCase):
  682. if _PY3:
  683. oldClassNames = ['type']
  684. else:
  685. oldClassNames = ['class', 'classobj']
  686. def test_old(self):
  687. class OldClass:
  688. pass
  689. old = OldClass()
  690. self.assertIn(reflect.getClass(OldClass).__name__, self.oldClassNames)
  691. self.assertEqual(reflect.getClass(old).__name__, 'OldClass')
  692. def test_new(self):
  693. class NewClass(object):
  694. pass
  695. new = NewClass()
  696. self.assertEqual(reflect.getClass(NewClass).__name__, 'type')
  697. self.assertEqual(reflect.getClass(new).__name__, 'NewClass')