| 289 | | systemPluginFile = """ |
| 290 | | from zope.interface import classProvides |
| 291 | | from twisted.plugin import IPlugin, ITestPlugin |
| 292 | | |
| 293 | | class system(object): |
| 294 | | classProvides(IPlugin, ITestPlugin) |
| 295 | | """ |
| 296 | | |
| 297 | | devPluginFile = """ |
| 298 | | from zope.interface import classProvides |
| 299 | | from twisted.plugin import IPlugin, ITestPlugin |
| 300 | | |
| 301 | | class system(object): |
| 302 | | classProvides(IPlugin, ITestPlugin) |
| 303 | | |
| 304 | | class dev(object): |
| 305 | | classProvides(IPlugin, ITestPlugin) |
| 306 | | """ |
| 307 | | |
| 308 | | appPluginFile = """ |
| 309 | | from zope.interface import classProvides |
| 310 | | from twisted.plugin import IPlugin, ITestPlugin |
| 311 | | |
| 312 | | class app(object): |
| 313 | | classProvides(IPlugin, ITestPlugin) |
| 314 | | """ |
| 315 | | |
| 316 | | onePluginFile = """ |
| 317 | | from zope.interface import classProvides |
| 318 | | from twisted.plugin import IPlugin, ITestPlugin |
| 319 | | |
| 320 | | class one(object): |
| 321 | | classProvides(IPlugin, ITestPlugin) |
| 322 | | """ |
| 323 | | |
| 324 | | twoPluginFile = """ |
| 325 | | from zope.interface import classProvides |
| 326 | | from twisted.plugin import IPlugin, ITestPlugin |
| 327 | | |
| 328 | | class two(object): |
| 329 | | classProvides(IPlugin, ITestPlugin) |
| 330 | | """ |
| 331 | | |
| 332 | | def _createPluginDummy(entrypath, pluginContent, real=True): |
| | 290 | def pluginFileContents(name): |
| | 291 | return ( |
| | 292 | "from zope.interface import classProvides\n" |
| | 293 | "from twisted.plugin import IPlugin, ITestPlugin\n" |
| | 294 | "\n" |
| | 295 | "class %s(object):\n" |
| | 296 | " classProvides(IPlugin, ITestPlugin)\n") % (name,) |
| | 297 | |
| | 298 | |
| | 299 | def _createPluginDummy(entrypath, pluginContent, real, pluginModule): |
| 374 | | self.systemPackage = _createPluginDummy(self.systemPath, |
| 375 | | systemPluginFile) |
| 376 | | self.devPackage = _createPluginDummy(self.devPath, devPluginFile) |
| 377 | | self.appPackage = _createPluginDummy(self.appPath, appPluginFile, False) |
| | 338 | self.systemPackage = _createPluginDummy( |
| | 339 | self.systemPath, pluginFileContents('system'), |
| | 340 | True, 'plugindummy_builtin') |
| | 341 | self.devPackage = _createPluginDummy( |
| | 342 | self.devPath, pluginFileContents('dev'), |
| | 343 | True, 'plugindummy_builtin') |
| | 344 | self.appPackage = _createPluginDummy( |
| | 345 | self.appPath, pluginFileContents('app'), |
| | 346 | False, 'plugindummy_app') |
| | 494 | |
| | 495 | |
| | 496 | |
| | 497 | class AdjacentPackageTests(unittest.TestCase): |
| | 498 | """ |
| | 499 | Tests for the behavior of the plugin system when there are multiple |
| | 500 | installed copies of the package containing the plugins being loaded. |
| | 501 | """ |
| | 502 | def setUp(self): |
| | 503 | """ |
| | 504 | Save the elements of C{sys.path} and the items of C{sys.modules}. |
| | 505 | """ |
| | 506 | self.originalPath = sys.path[:] |
| | 507 | self.savedModules = sys.modules.copy() |
| | 508 | |
| | 509 | |
| | 510 | def tearDown(self): |
| | 511 | """ |
| | 512 | Restore C{sys.path} and C{sys.modules} to their original values. |
| | 513 | """ |
| | 514 | sys.path[:] = self.originalPath |
| | 515 | sys.modules.clear() |
| | 516 | sys.modules.update(self.savedModules) |
| | 517 | |
| | 518 | |
| | 519 | def createDummyPackage(self, root, name, pluginName): |
| | 520 | """ |
| | 521 | Create a directory containing a Python package named I{dummy} with a |
| | 522 | I{plugins} subpackage. |
| | 523 | |
| | 524 | @type root: L{FilePath} |
| | 525 | @param root: The directory in which to create the hierarchy. |
| | 526 | |
| | 527 | @type name: C{str} |
| | 528 | @param name: The name of the directory to create which will contain |
| | 529 | the package. |
| | 530 | |
| | 531 | @type pluginName: C{str} |
| | 532 | @param pluginName: The name of a module to create in the |
| | 533 | I{dummy.plugins} package. |
| | 534 | |
| | 535 | @rtype: L{FilePath} |
| | 536 | @return: The directory which was created to contain the I{dummy} |
| | 537 | package. |
| | 538 | """ |
| | 539 | directory = root.child(name) |
| | 540 | package = directory.child('dummy') |
| | 541 | package.makedirs() |
| | 542 | package.child('__init__.py').setContent('') |
| | 543 | plugins = package.child('plugins') |
| | 544 | plugins.makedirs() |
| | 545 | plugins.child('__init__.py').setContent(pluginInitFile) |
| | 546 | pluginModule = plugins.child(pluginName + '.py') |
| | 547 | pluginModule.setContent(pluginFileContents(name)) |
| | 548 | return directory |
| | 549 | |
| | 550 | |
| | 551 | def test_hiddenPackageSamePluginModuleNameObscured(self): |
| | 552 | """ |
| | 553 | Only plugins from the first package in sys.path should be returned by |
| | 554 | getPlugins in the case where there are two Python packages by the same |
| | 555 | name installed, each with a plugin module by a single name. |
| | 556 | """ |
| | 557 | root = FilePath(self.mktemp()) |
| | 558 | root.makedirs() |
| | 559 | |
| | 560 | firstDirectory = self.createDummyPackage(root, 'first', 'someplugin') |
| | 561 | secondDirectory = self.createDummyPackage(root, 'second', 'someplugin') |
| | 562 | |
| | 563 | sys.path.append(firstDirectory.path) |
| | 564 | sys.path.append(secondDirectory.path) |
| | 565 | |
| | 566 | import dummy.plugins |
| | 567 | |
| | 568 | plugins = list(plugin.getPlugins(plugin.ITestPlugin, dummy.plugins)) |
| | 569 | self.assertEqual(['first'], [p.__name__ for p in plugins]) |
| | 570 | |
| | 571 | |
| | 572 | def test_hiddenPackageDifferentPluginModuleNameObscured(self): |
| | 573 | """ |
| | 574 | Plugins from the first package in sys.path should be returned by |
| | 575 | getPlugins in the case where there are two Python packages by the same |
| | 576 | name installed, each with a plugin module by a different name. |
| | 577 | """ |
| | 578 | root = FilePath(self.mktemp()) |
| | 579 | root.makedirs() |
| | 580 | |
| | 581 | firstDirectory = self.createDummyPackage(root, 'first', 'thisplugin') |
| | 582 | secondDirectory = self.createDummyPackage(root, 'second', 'thatplugin') |
| | 583 | |
| | 584 | sys.path.append(firstDirectory.path) |
| | 585 | sys.path.append(secondDirectory.path) |
| | 586 | |
| | 587 | import dummy.plugins |
| | 588 | |
| | 589 | plugins = list(plugin.getPlugins(plugin.ITestPlugin, dummy.plugins)) |
| | 590 | self.assertEqual(['first'], [p.__name__ for p in plugins]) |
| | 591 | |
| | 592 | |
| | 593 | |
| | 594 | class PackagePathTests(unittest.TestCase): |
| | 595 | """ |
| | 596 | Tests for L{plugin.pluginPackagePaths} which constructs search paths for |
| | 597 | plugin packages. |
| | 598 | """ |
| | 599 | def setUp(self): |
| | 600 | """ |
| | 601 | Save the elements of C{sys.path}. |
| | 602 | """ |
| | 603 | self.originalPath = sys.path[:] |
| | 604 | |
| | 605 | |
| | 606 | def tearDown(self): |
| | 607 | """ |
| | 608 | Restore C{sys.path} to its original value. |
| | 609 | """ |
| | 610 | sys.path[:] = self.originalPath |
| | 611 | |
| | 612 | |
| | 613 | def test_pluginDirectories(self): |
| | 614 | """ |
| | 615 | L{plugin.pluginPackagePaths} should return a list containing each |
| | 616 | directory in C{sys.path} with a suffix based on the supplied package |
| | 617 | name. |
| | 618 | """ |
| | 619 | foo = FilePath('foo') |
| | 620 | bar = FilePath('bar') |
| | 621 | sys.path = [foo.path, bar.path] |
| | 622 | self.assertEqual( |
| | 623 | plugin.pluginPackagePaths('dummy.plugins'), |
| | 624 | [foo.child('dummy').child('plugins').path, |
| | 625 | bar.child('dummy').child('plugins').path]) |
| | 626 | |
| | 627 | |
| | 628 | def test_pluginPackagesExcluded(self): |
| | 629 | """ |
| | 630 | L{plugin.pluginPackagePaths} should exclude directories which are |
| | 631 | Python packages. The only allowed plugin package (the only one |
| | 632 | associated with a I{dummy} package which Python will allow to be |
| | 633 | imported) will already be known to the caller of |
| | 634 | L{plugin.pluginPackagePaths} and will most commonly already be in |
| | 635 | the C{__path__} they are about to mutate. |
| | 636 | """ |
| | 637 | root = FilePath(self.mktemp()) |
| | 638 | foo = root.child('foo').child('dummy').child('plugins') |
| | 639 | foo.makedirs() |
| | 640 | foo.child('__init__.py').setContent('') |
| | 641 | sys.path = [root.child('foo').path, root.child('bar').path] |
| | 642 | self.assertEqual( |
| | 643 | plugin.pluginPackagePaths('dummy.plugins'), |
| | 644 | [root.child('bar').child('dummy').child('plugins').path]) |