From d6efc9424328b42a3c7aeae758bab35bc7df5014 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:09:42 -0400 Subject: Introduce a new monkey module to encapsulate the monkeypatching. --- setuptools/monkey.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 setuptools/monkey.py (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py new file mode 100644 index 00000000..b6baf49d --- /dev/null +++ b/setuptools/monkey.py @@ -0,0 +1,22 @@ +""" +Monkey patching of distutils. +""" + + +__all__ = [] +"everything is private" + + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls -- cgit v1.2.3 From 57a5c05e6f460260f1339dce37407c724ad4c5e8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:28:08 -0400 Subject: Move monkeypatching in package module into monkey. --- setuptools/monkey.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index b6baf49d..189fa4e0 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -2,6 +2,11 @@ Monkey patching of distutils. """ +import sys +import distutils.filelist + +import setuptools + __all__ = [] "everything is private" @@ -20,3 +25,36 @@ def _get_unpatched(cls): "distutils has already been patched by %r" % cls ) return cls + + +def patch_all(): + # we can't patch distutils.cmd, alas + distutils.core.Command = setuptools.Command + + has_issue_12885 = ( + sys.version_info < (3, 4, 6) + or + (3, 5) < sys.version_info <= (3, 5, 3) + or + (3, 6) < sys.version_info + ) + + if has_issue_12885: + # fix findall bug in distutils (http://bugs.python.org/issue12885) + distutils.filelist.findall = setuptools.findall + + needs_warehouse = ( + sys.version_info < (2, 7, 13) + or + (3, 0) < sys.version_info < (3, 3, 7) + or + (3, 4) < sys.version_info < (3, 4, 6) + or + (3, 5) < sys.version_info <= (3, 5, 3) + or + (3, 6) < sys.version_info + ) + + if needs_warehouse: + warehouse = 'https://upload.pypi.org/legacy/' + distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse -- cgit v1.2.3 From 443cabec148460b3a688923df1a63f689d1164c7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:30:10 -0400 Subject: Remove private prefix from monkey as monkey module explicitly declares that all functions are private. --- setuptools/monkey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 189fa4e0..1961dfba 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -12,7 +12,7 @@ __all__ = [] "everything is private" -def _get_unpatched(cls): +def get_unpatched(cls): """Protect against re-patching the distutils if reloaded Also ensures that no other distutils extension monkeypatched the distutils -- cgit v1.2.3 From cd22ba427f9b201d6bc48586ddf4595312b9e19e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:50:27 -0400 Subject: Move (much of?) the rest of the monkey patching into the monkey module --- setuptools/monkey.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 1961dfba..a2cac0e0 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -58,3 +58,44 @@ def patch_all(): if needs_warehouse: warehouse = 'https://upload.pypi.org/legacy/' distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse + + _patch_distribution_metadata_write_pkg_file() + _patch_distribution_metadata_write_pkg_info() + + # Install Distribution throughout the distutils + for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = setuptools.dist.Distribution + + # Install the patched Extension + distutils.core.Extension = setuptools.extension.Extension + distutils.extension.Extension = setuptools.extension.Extension + if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = ( + setuptools.extension.Extension + ) + + # patch MSVC + __import__('setuptools.msvc') + setuptools.msvc.patch_for_specialized_compiler() + + +def _patch_distribution_metadata_write_pkg_file(): + """Patch write_pkg_file to also write Requires-Python/Requires-External""" + distutils.dist.DistributionMetadata.write_pkg_file = ( + setuptools.dist.write_pkg_file + ) + + +def _patch_distribution_metadata_write_pkg_info(): + """ + Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local + encoding to save the pkg_info. Monkey-patch its write_pkg_info method to + correct this undesirable behavior. + """ + environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) + if not environment_local: + return + + distutils.dist.DistributionMetadata.write_pkg_info = ( + setuptools.dist.write_pkg_info + ) -- cgit v1.2.3 From 3c5cf653dbc0ab10d08738ee4e4db26710903466 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 20:29:14 -0400 Subject: Move msvc patch logic into monkey module. --- setuptools/monkey.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index a2cac0e0..6d341b43 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -4,6 +4,7 @@ Monkey patching of distutils. import sys import distutils.filelist +import platform import setuptools @@ -74,9 +75,7 @@ def patch_all(): setuptools.extension.Extension ) - # patch MSVC - __import__('setuptools.msvc') - setuptools.msvc.patch_for_specialized_compiler() + patch_for_msvc_specialized_compiler() def _patch_distribution_metadata_write_pkg_file(): @@ -99,3 +98,57 @@ def _patch_distribution_metadata_write_pkg_info(): distutils.dist.DistributionMetadata.write_pkg_info = ( setuptools.dist.write_pkg_info ) + + +unpatched = dict() + + +def patch_for_msvc_specialized_compiler(): + """ + Patch functions in distutils to use standalone Microsoft Visual C++ + compilers. + """ + try: + # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) + import distutils.msvc9compiler as msvc9compiler + except ImportError: + pass + + try: + # Distutil file for MSVC++ 14.0 and upper (Python 3.5+) + import distutils._msvccompiler as msvc14compiler + except ImportError: + pass + + if platform.system() != 'Windows': + # Compilers only availables on Microsoft Windows + return + + unpatched = __import__('setuptools.msvc').msvc.unpatched + + if unpatched: + # Already patched + return + + try: + # Patch distutils.msvc9compiler + unpatched['msvc9_find_vcvarsall'] = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = msvc9_find_vcvarsall + unpatched['msvc9_query_vcvarsall'] = msvc9compiler.query_vcvarsall + msvc9compiler.query_vcvarsall = msvc9_query_vcvarsall + except NameError: + pass + + try: + # Patch distutils._msvccompiler._get_vc_env + unpatched['msvc14_get_vc_env'] = msvc14compiler._get_vc_env + msvc14compiler._get_vc_env = msvc14_get_vc_env + except NameError: + pass + + try: + # Patch distutils._msvccompiler.gen_lib_options for Numpy + unpatched['msvc14_gen_lib_options'] = msvc14compiler.gen_lib_options + msvc14compiler.gen_lib_options = msvc14_gen_lib_options + except NameError: + pass -- cgit v1.2.3 From 9915760daaffca6d14444f83929a8cf25bdf1f7b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 20:38:39 -0400 Subject: Use unpatched locally --- setuptools/monkey.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 6d341b43..3e4f49e1 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -124,8 +124,6 @@ def patch_for_msvc_specialized_compiler(): # Compilers only availables on Microsoft Windows return - unpatched = __import__('setuptools.msvc').msvc.unpatched - if unpatched: # Already patched return -- cgit v1.2.3 From 20a2f628283a0af476020cc394e4dca1dcdeaadd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:27:32 -0400 Subject: Add Deprecation warning for _get_unpatched. --- setuptools/monkey.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 3e4f49e1..5a0cf43b 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -10,7 +10,10 @@ import setuptools __all__ = [] -"everything is private" +""" +Everything is private. Contact the project team +if you think you need this functionality. +""" def get_unpatched(cls): -- cgit v1.2.3 From 602dc92bd696ad292b4ab44638a01d0035d560ab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 11:24:34 -0400 Subject: Fix msvc monkeypatching, revealed by Appveyor tests. Fixes #778. --- setuptools/monkey.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 5a0cf43b..63891e74 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -7,6 +7,7 @@ import distutils.filelist import platform import setuptools +from . import msvc __all__ = [] @@ -134,22 +135,22 @@ def patch_for_msvc_specialized_compiler(): try: # Patch distutils.msvc9compiler unpatched['msvc9_find_vcvarsall'] = msvc9compiler.find_vcvarsall - msvc9compiler.find_vcvarsall = msvc9_find_vcvarsall + msvc9compiler.find_vcvarsall = msvc.msvc9_find_vcvarsall unpatched['msvc9_query_vcvarsall'] = msvc9compiler.query_vcvarsall - msvc9compiler.query_vcvarsall = msvc9_query_vcvarsall + msvc9compiler.query_vcvarsall = msvc.msvc9_query_vcvarsall except NameError: pass try: # Patch distutils._msvccompiler._get_vc_env unpatched['msvc14_get_vc_env'] = msvc14compiler._get_vc_env - msvc14compiler._get_vc_env = msvc14_get_vc_env + msvc14compiler._get_vc_env = msvc.msvc14_get_vc_env except NameError: pass try: # Patch distutils._msvccompiler.gen_lib_options for Numpy unpatched['msvc14_gen_lib_options'] = msvc14compiler.gen_lib_options - msvc14compiler.gen_lib_options = msvc14_gen_lib_options + msvc14compiler.gen_lib_options = msvc.msvc14_gen_lib_options except NameError: pass -- cgit v1.2.3 From a6524a5c0d40aedbaa06eb41c70990c44a1dfd15 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Fri, 9 Sep 2016 11:50:41 -0400 Subject: Move msvc import to avoid a circular import --- setuptools/monkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 63891e74..33083831 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -7,7 +7,6 @@ import distutils.filelist import platform import setuptools -from . import msvc __all__ = [] @@ -112,6 +111,8 @@ def patch_for_msvc_specialized_compiler(): Patch functions in distutils to use standalone Microsoft Visual C++ compilers. """ + from . import msvc + try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) import distutils.msvc9compiler as msvc9compiler -- cgit v1.2.3 From b6f2fee975c570d2beadb9007e6302411f91ab4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 12:39:43 -0400 Subject: Consolidate function patching and resolution of unpatched function, aligning pattern with the patched classes. --- setuptools/monkey.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 33083831..24739d97 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -6,6 +6,8 @@ import sys import distutils.filelist import platform +from .py26compat import import_module + import setuptools @@ -103,7 +105,20 @@ def _patch_distribution_metadata_write_pkg_info(): ) -unpatched = dict() +def patch_func(replacement, original): + # first set the 'unpatched' attribute on the replacement to + # point to the original. + vars(replacement).setdefault('unpatched', original) + + # next resolve the module in which the original func resides + target_mod = import_module(original.__module__) + + # finally replace the function in the original module + setattr(target_mod, original.__name__, replacement) + + +def get_unpatched_func(candidate): + return getattr(candidate, 'unpatched') def patch_for_msvc_specialized_compiler(): @@ -129,29 +144,21 @@ def patch_for_msvc_specialized_compiler(): # Compilers only availables on Microsoft Windows return - if unpatched: - # Already patched - return - try: # Patch distutils.msvc9compiler - unpatched['msvc9_find_vcvarsall'] = msvc9compiler.find_vcvarsall - msvc9compiler.find_vcvarsall = msvc.msvc9_find_vcvarsall - unpatched['msvc9_query_vcvarsall'] = msvc9compiler.query_vcvarsall - msvc9compiler.query_vcvarsall = msvc.msvc9_query_vcvarsall + patch_func(msvc.msvc9_find_vcvarsall, msvc9compiler.find_vcvarsall) + patch_func(msvc.msvc9_query_vcvarsall, msvc9compiler.query_vcvarsall) except NameError: pass try: # Patch distutils._msvccompiler._get_vc_env - unpatched['msvc14_get_vc_env'] = msvc14compiler._get_vc_env - msvc14compiler._get_vc_env = msvc.msvc14_get_vc_env + patch_func(msvc.msvc14_get_vc_env, msvc14compiler._get_vc_env) except NameError: pass try: # Patch distutils._msvccompiler.gen_lib_options for Numpy - unpatched['msvc14_gen_lib_options'] = msvc14compiler.gen_lib_options - msvc14compiler.gen_lib_options = msvc.msvc14_gen_lib_options + patch_func(msvc.msvc14_gen_lib_options, msvc14compiler.gen_lib_options) except NameError: pass -- cgit v1.2.3 From b7b9cb23f217095e79c618c0e3196712d2d9a285 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 12:45:21 -0400 Subject: Use programmatic import and add comment explaining purpose. --- setuptools/monkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 24739d97..5f098986 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -126,7 +126,8 @@ def patch_for_msvc_specialized_compiler(): Patch functions in distutils to use standalone Microsoft Visual C++ compilers. """ - from . import msvc + # import late to avoid circular imports on Python < 3.5 + msvc = import_module('setuptools.msvc') try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) -- cgit v1.2.3 From 7756f651df47dc870e886c1b13c5b48068c2dd5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 12:54:34 -0400 Subject: Allow get_unpatched to be called to get unpatched version of a class or function, further harmonizing the interfaces. --- setuptools/monkey.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 5f098986..7a23641c 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -5,6 +5,7 @@ Monkey patching of distutils. import sys import distutils.filelist import platform +import types from .py26compat import import_module @@ -18,7 +19,16 @@ if you think you need this functionality. """ -def get_unpatched(cls): +def get_unpatched(item): + lookup = ( + get_unpatched_class if isinstance(item, type) else + get_unpatched_function if isinstance(item, types.FunctionType) else + lambda item: None + ) + return lookup(item) + + +def get_unpatched_class(cls): """Protect against re-patching the distutils if reloaded Also ensures that no other distutils extension monkeypatched the distutils @@ -117,7 +127,7 @@ def patch_func(replacement, original): setattr(target_mod, original.__name__, replacement) -def get_unpatched_func(candidate): +def get_unpatched_function(candidate): return getattr(candidate, 'unpatched') -- cgit v1.2.3 From c49d0a044c0483f956902068a87f732b38cbbf6e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 12:55:28 -0400 Subject: Extract a variable for nicer indentation. --- setuptools/monkey.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 7a23641c..c9a52c61 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -37,9 +37,8 @@ def get_unpatched_class(cls): while cls.__module__.startswith('setuptools'): cls, = cls.__bases__ if not cls.__module__.startswith('distutils'): - raise AssertionError( - "distutils has already been patched by %r" % cls - ) + msg = "distutils has already been patched by %r" % cls + raise AssertionError(msg) return cls -- cgit v1.2.3 From b700b3611d39d28a64973ec6abd006fc75af5df0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 13:04:26 -0400 Subject: Account for the class might be old style on Python 2. --- setuptools/monkey.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index c9a52c61..c4289762 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -8,6 +8,7 @@ import platform import types from .py26compat import import_module +from setuptools.extern import six import setuptools @@ -21,7 +22,7 @@ if you think you need this functionality. def get_unpatched(item): lookup = ( - get_unpatched_class if isinstance(item, type) else + get_unpatched_class if isinstance(item, six.class_types) else get_unpatched_function if isinstance(item, types.FunctionType) else lambda item: None ) -- cgit v1.2.3 From 44a670456c81f844cad1d5aa713cd304ed80fc09 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 13:34:36 -0500 Subject: Patch MSVC functions by name. Fixes #790. --- setuptools/monkey.py | 64 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'setuptools/monkey.py') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index c4289762..43b97b4d 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -6,6 +6,7 @@ import sys import distutils.filelist import platform import types +import functools from .py26compat import import_module from setuptools.extern import six @@ -115,16 +116,21 @@ def _patch_distribution_metadata_write_pkg_info(): ) -def patch_func(replacement, original): - # first set the 'unpatched' attribute on the replacement to +def patch_func(replacement, target_mod, func_name): + """ + Patch func_name in target_mod with replacement + + Important - original must be resolved by name to avoid + patching an already patched function. + """ + original = getattr(target_mod, func_name) + + # set the 'unpatched' attribute on the replacement to # point to the original. vars(replacement).setdefault('unpatched', original) - # next resolve the module in which the original func resides - target_mod = import_module(original.__module__) - - # finally replace the function in the original module - setattr(target_mod, original.__name__, replacement) + # replace the function in the original module + setattr(target_mod, func_name, replacement) def get_unpatched_function(candidate): @@ -139,37 +145,43 @@ def patch_for_msvc_specialized_compiler(): # import late to avoid circular imports on Python < 3.5 msvc = import_module('setuptools.msvc') - try: - # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) - import distutils.msvc9compiler as msvc9compiler - except ImportError: - pass - - try: - # Distutil file for MSVC++ 14.0 and upper (Python 3.5+) - import distutils._msvccompiler as msvc14compiler - except ImportError: - pass - if platform.system() != 'Windows': # Compilers only availables on Microsoft Windows return + def patch_params(mod_name, func_name): + """ + Prepare the parameters for patch_func to patch indicated function. + """ + repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' + repl_name = repl_prefix + func_name.lstrip('_') + repl = getattr(msvc, repl_name) + mod = import_module(mod_name) + if not hasattr(mod, func_name): + raise ImportError(func_name) + return repl, mod, func_name + + # Python 2.7 to 3.4 + msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') + + # Python 3.5+ + msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') + try: # Patch distutils.msvc9compiler - patch_func(msvc.msvc9_find_vcvarsall, msvc9compiler.find_vcvarsall) - patch_func(msvc.msvc9_query_vcvarsall, msvc9compiler.query_vcvarsall) - except NameError: + patch_func(*msvc9('find_vcvarsall')) + patch_func(*msvc9('query_vcvarsall')) + except ImportError: pass try: # Patch distutils._msvccompiler._get_vc_env - patch_func(msvc.msvc14_get_vc_env, msvc14compiler._get_vc_env) - except NameError: + patch_func(*msvc14('_get_vc_env')) + except ImportError: pass try: # Patch distutils._msvccompiler.gen_lib_options for Numpy - patch_func(msvc.msvc14_gen_lib_options, msvc14compiler.gen_lib_options) - except NameError: + patch_func(*msvc14('gen_lib_options')) + except ImportError: pass -- cgit v1.2.3