|
1 | 1 | """ core implementation of testing process: init, session, runtest loop. """ |
2 | 2 | from __future__ import absolute_import, division, print_function |
3 | 3 |
|
| 4 | +import contextlib |
4 | 5 | import functools |
5 | 6 | import os |
| 7 | +import pkgutil |
6 | 8 | import six |
7 | 9 | import sys |
8 | 10 |
|
@@ -728,36 +730,57 @@ def _tryconvertpyarg(self, x): |
728 | 730 | """Convert a dotted module name to path. |
729 | 731 |
|
730 | 732 | """ |
731 | | - import pkgutil |
732 | | - |
733 | | - if six.PY2: # python 3.4+ uses importlib instead |
734 | | - def find_module_patched(self, fullname, path=None): |
735 | | - subname = fullname.split(".")[-1] |
736 | | - if subname != fullname and self.path is None: |
737 | | - return None |
738 | | - if self.path is None: |
739 | | - path = None |
740 | | - else: |
741 | | - path = [self.path] |
| 733 | + @contextlib.contextmanager |
| 734 | + def _patched_find_module(): |
| 735 | + """Patch bug in pkgutil.ImpImporter.find_module |
| 736 | +
|
| 737 | + When using pkgutil.find_loader on python<3.4 it removes symlinks |
| 738 | + from the path due to a call to os.path.realpath. This is not consistent |
| 739 | + with actually doing the import (in these versions, pkgutil and __import__ |
| 740 | + did not share the same underlying code). This can break conftest |
| 741 | + discovery for pytest where symlinks are involved. |
| 742 | +
|
| 743 | + The only supported python<3.4 by pytest is python 2.7. |
| 744 | + """ |
| 745 | + if six.PY2: # python 3.4+ uses importlib instead |
| 746 | + def find_module_patched(self, fullname, path=None): |
| 747 | + # Note: we ignore 'path' argument since it is only used via meta_path |
| 748 | + subname = fullname.split(".")[-1] |
| 749 | + if subname != fullname and self.path is None: |
| 750 | + return None |
| 751 | + if self.path is None: |
| 752 | + path = None |
| 753 | + else: |
| 754 | + # original: path = [os.path.realpath(self.path)] |
| 755 | + path = [self.path] |
| 756 | + try: |
| 757 | + file, filename, etc = pkgutil.imp.find_module(subname, |
| 758 | + path) |
| 759 | + except ImportError: |
| 760 | + return None |
| 761 | + return pkgutil.ImpLoader(fullname, file, filename, etc) |
| 762 | + |
| 763 | + old_find_module = pkgutil.ImpImporter.find_module |
| 764 | + pkgutil.ImpImporter.find_module = find_module_patched |
742 | 765 | try: |
743 | | - file, filename, etc = pkgutil.imp.find_module(subname, |
744 | | - path) |
745 | | - except ImportError: |
746 | | - return None |
747 | | - return pkgutil.ImpLoader(fullname, file, filename, etc) |
748 | | - |
749 | | - pkgutil.ImpImporter.find_module = find_module_patched |
| 766 | + yield |
| 767 | + finally: |
| 768 | + pkgutil.ImpImporter.find_module = old_find_module |
| 769 | + else: |
| 770 | + yield |
750 | 771 |
|
751 | 772 | try: |
752 | | - loader = pkgutil.find_loader(x) |
| 773 | + with _patched_find_module(): |
| 774 | + loader = pkgutil.find_loader(x) |
753 | 775 | except ImportError: |
754 | 776 | return x |
755 | 777 | if loader is None: |
756 | 778 | return x |
757 | 779 | # This method is sometimes invoked when AssertionRewritingHook, which |
758 | 780 | # does not define a get_filename method, is already in place: |
759 | 781 | try: |
760 | | - path = loader.get_filename(x) |
| 782 | + with _patched_find_module(): |
| 783 | + path = loader.get_filename(x) |
761 | 784 | except AttributeError: |
762 | 785 | # Retrieve path from AssertionRewritingHook: |
763 | 786 | path = loader.modules[x][0].co_filename |
|
0 commit comments